@@ -583,11 +583,16 @@ contains
583583
584584 end function f_model_is_inside
585585
586- !> This procedure, given a cell center will determine if a point exists instide a surface
587- !! @param ntrs Number of triangles in the model
588- !! @param pid Patch ID od this model
586+ !> This procedure determines if a point is inside a surface using
587+ !! the generalized winding number (Jacobson et al., SIGGRAPH 2013 ).
588+ !! The winding number is the sum of signed solid angles subtended
589+ !! by each triangle, normalized by 4 * pi. Returns ~1.0 inside,
590+ !! ~0.0 outside. Unlike ray casting, this is robust to small
591+ !! triangles and vertex winding order.
592+ !! @param ntrs Number of triangles in the model.
593+ !! @param pid Patch ID of this model.
589594 !! @param point Point to test.
590- !! @return fraction The perfentage of candidate rays cast indicate that we are inside the model
595+ !! @return fraction Winding number (~ 1.0 inside, ~ 0.0 outside).
591596 function f_model_is_inside_flat (ntrs , pid , point ) result(fraction)
592597
593598 $:GPU_ROUTINE(parallelism= ' [seq]' )
@@ -597,43 +602,44 @@ contains
597602 real (wp), dimension (1 :3 ), intent (in ) :: point
598603
599604 real (wp) :: fraction
600- type(t_ray) :: ray
601- type(t_triangle) :: tri
602- integer :: i, j, k, q, nInOrOut, nHits
603605
604- ! cast 26 rays from the point and count the number at leave the boundary
605- nInOrOut = 0
606- do i = - 1 , 1
607- do j = - 1 , 1
608- do k = - 1 , 1
609- if (i /= 0 .or. j /= 0 .or. k /= 0 ) then
610- ! We cannot get inersections if the ray is exactly in line with triangle plane
611- if (p == 0 .and. k == 0 ) cycle
612-
613- ! generate the ray
614- ray%o = point
615- ray%d(:) = [real (i, wp), real (j, wp), real (k, wp)]
616- ray%d = ray%d/ sqrt (real (abs (i) + abs (j) + abs (k), wp))
617-
618- ! count the number of intersections
619- nHits = 0
620- do q = 1 , ntrs
621- tri%v(:, :) = gpu_trs_v(:, :, q, pid)
622- nHits = nHits + f_intersects_triangle(ray, tri)
623- end do
624- ! if the ray intersected an odd number of times, we must be inside
625- nInOrOut = nInOrOut + mod (nHits, 2 )
626- end if
627- end do
628- end do
606+ real (wp) :: r1(3 ), r2(3 ), r3(3 )
607+ real (wp) :: r1_mag, r2_mag, r3_mag
608+ real (wp) :: numerator, denominator
609+ integer :: q
610+
611+ fraction = 0.0_wp
612+
613+ do q = 1 , ntrs
614+ r1 = gpu_trs_v(1 , :, q, pid) - point
615+ r2 = gpu_trs_v(2 , :, q, pid) - point
616+ r3 = gpu_trs_v(3 , :, q, pid) - point
617+
618+ r1_mag = sqrt (dot_product (r1, r1))
619+ r2_mag = sqrt (dot_product (r2, r2))
620+ r3_mag = sqrt (dot_product (r3, r3))
621+
622+ ! Van Oosterom- Strackee formula:
623+ ! tan (Omega/ 2 ) = numerator / denominator
624+ ! numerator = scalar triple product r1 . (r2 x r3)
625+ numerator = r1(1 )* (r2(2 )* r3(3 ) - r2(3 )* r3(2 )) &
626+ + r1(2 )* (r2(3 )* r3(1 ) - r2(1 )* r3(3 )) &
627+ + r1(3 )* (r2(1 )* r3(2 ) - r2(2 )* r3(1 ))
628+
629+ denominator = r1_mag* r2_mag* r3_mag &
630+ + dot_product (r1, r2)* r3_mag &
631+ + dot_product (r2, r3)* r1_mag &
632+ + dot_product (r3, r1)* r2_mag
633+
634+ ! Solid angle = 2 * atan2 (num, den).
635+ ! atan2 (0 ,0 ) = 0 per IEEE 754 , so degenerate triangles
636+ ! contribute nothing without special casing.
637+ fraction = fraction + atan2 (numerator, denominator)
629638 end do
630639
631- if (p == 0 ) then
632- ! in 2D , we skipped 8 rays
633- fraction = real (nInOrOut)/ 18._wp
634- else
635- fraction = real (nInOrOut)/ 26._wp
636- end if
640+ ! Winding number = total solid angle / (4 * pi)
641+ ! Each triangle contributes 2 * atan2, so sum / (2 * pi)
642+ fraction = fraction/ (2.0_wp * acos (- 1.0_wp ))
637643
638644 end function f_model_is_inside_flat
639645
0 commit comments