Drag the grabbed handle to the cursor's ray (origin, unit dir; root frame): its time where the path is nearest the ray (see the module documentation). False (not moved) if it isn't grabbed or the path is all behind the eye.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(TimeHandle), | intent(inout) | :: | self | |||
| real(kind=wp), | intent(in) | :: | origin(3) |
the cursor's ray (root frame) |
||
| real(kind=wp), | intent(in) | :: | dir(3) |
the cursor's ray (root frame) |
logical function time_drag(self, origin, dir) result(moved) class(TimeHandle), intent(inout) :: self real(wp), intent(in) :: origin(3), dir(3) !! the cursor's ray (root frame) real(wp) :: parent(4,4), miss(path_samples), best, t_best real(wp) :: t_min(path_samples), m_min(path_samples) !! the local minima refined: their times and misses integer :: k, n_min logical :: found moved = .false. if (.not. (self%dragged_ .and. allocated(self%sample_t) .and. associated(self%path_))) return parent = matmul(self%world_matrix(), rigid_inverse(self%local_matrix())) ! (the path's coordinates to the root) do k = 1, path_samples miss(k) = ray_miss(self%sample_p(:, k)) end do if (minval(miss) >= huge(1.0_wp)) return ! each local minimum of the samples refined; of those nearly as near as ! the nearest (a path passing the same place again: orbits), the one ! nearest in time to the handle's n_min = 0 do k = 1, path_samples if (.not. is_minimum(k)) cycle n_min = n_min + 1 call refine(k, t_min(n_min), m_min(n_min)) end do if (n_min == 0) return best = minval(m_min(1:n_min)) found = .false. t_best = self%time_ do k = 1, n_min if (m_min(k) > 1.5_wp * best + 1.0e-4_wp) cycle if (found .and. abs(t_min(k) - self%time_) >= abs(t_best - self%time_)) cycle t_best = t_min(k) found = .true. end do if (.not. found) return call self%set_time(t_best) moved = .true. contains !> How far point p (the path's coordinates) is from the ray, as seen !> from its origin (the tangent of the angle; huge() behind it). real(wp) function ray_miss(p) real(wp), intent(in) :: p(3) !! the point (the path's coordinates) real(wp) :: v(3), along v = matmul(parent(1:3, 1:3), p) + parent(1:3, 4) - origin along = dot_product(v, dir) ray_miss = huge(1.0_wp) if (along > 0.0_wp) ray_miss = norm2(v - along * dir) / along end function ray_miss !> Sample k is a local minimum of the samples' misses (in front of the eye). logical function is_minimum(j) integer, intent(in) :: j !! the sample is_minimum = miss(j) < huge(1.0_wp) if (j > 1) is_minimum = is_minimum .and. miss(j - 1) >= miss(j) if (j < path_samples) is_minimum = is_minimum .and. miss(j + 1) > miss(j) end function is_minimum !> The time of the least miss between sample j's neighbours (golden-section !> search), and that miss. subroutine refine(j, t, m) integer, intent(in) :: j !! the sample real(wp), intent(out) :: t !! the time found real(wp), intent(out) :: m !! its miss real(wp), parameter :: golden = 0.5_wp * (sqrt(5.0_wp) - 1.0_wp) real(wp) :: a, b, c, d, fc, fd integer :: it a = self%sample_t(max(1, j - 1)) b = self%sample_t(min(path_samples, j + 1)) c = b - golden * (b - a) d = a + golden * (b - a) fc = ray_miss(self%path_%position(c)) fd = ray_miss(self%path_%position(d)) do it = 1, 40 if (fc < fd) then b = d d = c fd = fc c = b - golden * (b - a) fc = ray_miss(self%path_%position(c)) else a = c c = d fc = fd d = a + golden * (b - a) fd = ray_miss(self%path_%position(d)) end if end do t = 0.5_wp * (a + b) m = min(fc, fd) end subroutine refine end function time_drag