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.
| Type | Intent | Optional | 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) |
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