segment_pick Function

public function segment_pick(cam, a, b, pixels, s, depth, extra) result(hit)

Segment a-b (root frame) passes within the tolerance (plus extra pixels, e.g. half the line's width) of the cursor: the distance (pixels) of its closest point, the fraction s of the way from a to b of that point in the scene (perspective-correct), and its depth. The part behind the eye is cut off.

Arguments

Type IntentOptional Attributes Name
type(PickCamera), intent(in) :: cam

the camera and cursor of the pick

real(kind=wp), intent(in) :: a(3)

the segment's ends (root frame)

real(kind=wp), intent(in) :: b(3)

the segment's ends (root frame)

real(kind=wp), intent(out) :: pixels

distance from the cursor (framebuffer pixels)

real(kind=wp), intent(out) :: s

fraction of the way from a to b (0 to 1)

real(kind=wp), intent(out) :: depth

(scene units)

real(kind=wp), intent(in), optional :: extra

added to the tolerance (framebuffer pixels)

Return Value logical


Calls

proc~~segment_pick~~CallsGraph proc~segment_pick segment_pick proc~project project proc~segment_pick->proc~project

Called by

proc~~segment_pick~~CalledByGraph proc~segment_pick segment_pick proc~curve_pick curve_pick proc~curve_pick->proc~segment_pick proc~segment_artist_pick segment_artist_pick proc~segment_artist_pick->proc~segment_pick proc~vector_pick VectorHandle%vector_pick proc~vector_pick->proc~segment_pick proc~drawable_pick DrawableTrajectory%drawable_pick proc~drawable_pick->proc~curve_pick proc~drawable_pick->proc~segment_artist_pick

Source Code

    logical function segment_pick(cam, a, b, pixels, s, depth, extra) result(hit)
        type(PickCamera), intent(in) :: cam                 !! the camera and cursor of the pick
        real(wp), intent(in) :: a(3), b(3)                  !! the segment's ends (root frame)
        real(wp), intent(out) :: pixels                     !! distance from the cursor (framebuffer pixels)
        real(wp), intent(out) :: s                          !! fraction of the way from a to b (0 to 1)
        real(wp), intent(out) :: depth                      !! (scene units)
        real(wp), intent(in), optional :: extra             !! added to the tolerance (framebuffer pixels)
        real(wp) :: da, db, s0, s1, p0(3), p1(3), x0, y0, x1, y1, z0, z1, dx, dy, t, len2, u
        hit = .false.
        pixels = huge(1.0_wp)
        s = 0.0_wp
        depth = huge(1.0_wp)
        ! the part in front of the eye: s0 .. s1 of a-b
        s0 = 0.0_wp
        s1 = 1.0_wp
        if (cam%perspective) then
            da = dot_product(a - cam%eye, cam%forward)
            db = dot_product(b - cam%eye, cam%forward)
            if (da <= cam%near .and. db <= cam%near) return
            if (da <= cam%near) s0 = (cam%near - da) / (db - da) * 1.000001_wp
            if (db <= cam%near) s1 = (da - cam%near) / (da - db) * 0.999999_wp
        end if
        p0 = a + s0 * (b - a)
        p1 = a + s1 * (b - a)
        if (.not. project(cam, p0, x0, y0, z0)) return
        if (.not. project(cam, p1, x1, y1, z1)) return
        ! the closest point on the screen segment (the cursor is at 0, 0)
        dx = x1 - x0
        dy = y1 - y0
        len2 = dx * dx + dy * dy
        t = 0.0_wp
        if (len2 > 0.0_wp) t = max(0.0_wp, min(1.0_wp, -(x0 * dx + y0 * dy) / len2))
        pixels = hypot(x0 + t * dx, y0 + t * dy)
        ! back to the scene: linear on the screen is not linear in depth
        u = t
        if (cam%perspective) u = t * z0 / (t * z0 + (1.0_wp - t) * z1)
        s = s0 + u * (s1 - s0)
        depth = z0 + u * (z1 - z0)
        hit = pixels <= cam%tolerance
        if (present(extra)) hit = pixels <= cam%tolerance + extra
        hit = hit .and. depth <= cam%max_depth
    end function segment_pick