Compute the point of the triangle closest to the given point
.
This function computes the point of the triangle with the smallest distance to the provided point. Input parameters:
me
- the triangle to checkpoint
- the point for which the closest neighbor point within the
triangle is to be foundOutput parameters:
closest
- point of the triangle which is the closest to the given
point
closekind
- there are three different kinds of closest point:
- it may be inside the triangle
- it may be on one of the edges
- it may be one of the three vertices
A fourth case arises with degenerate triangles for which
no proper distance can be computed, if this is set, the
other values have no proper values assigned to them.
This parameter returns which kind of closest point we have.distance
- the smallest distance between point
and the triangle (me
)normal
- depending on the closekind
this returns:
- the normal vector on the triangle pointing from closest
to point (has the length distance
) if closest is withing
the triangle
- the unit normal vector to the side of point
if closest
is on an edge or vertex of the triangleType | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
type(tem_triangle_type), | intent(in) | :: | me | |||
real(kind=rk), | intent(in) | :: | point(3) | |||
real(kind=rk), | intent(out) | :: | closest(3) | |||
integer, | intent(out) | :: | closekind | |||
real(kind=rk), | intent(out) | :: | distance | |||
real(kind=rk), | intent(out) | :: | normal(3) |
subroutine tem_triangle_normal_proximity(me, point, closest, closekind, &
& distance, normal )
! ---------------------------------------------------------------------- !
type(tem_triangle_type), intent(in) :: me
real(kind=rk), intent(in) :: point(3)
real(kind=rk), intent(out) :: closest(3)
integer, intent(out) :: closekind
real(kind=rk), intent(out) :: distance
real(kind=rk), intent(out) :: normal(3)
! ---------------------------------------------------------------------- !
integer :: min_edge, min_vertex
real(kind=rk) :: edge(3,3) ! The three edges of the triangle
real(kind=rk) :: edgelen_squared(3) ! Squared length the edges
real(kind=rk) :: edgescale(3)
real(kind=rk) :: edgeprod1_2 ! Dot Product of first two edges
real(kind=rk) :: tri2p(3) ! Vector pointing from the triangles first vertex
! to the given point.
real(kind=rk) :: plane_point(3) ! Point projected onto plane of triangle
real(kind=rk) :: edge_point(3,3) ! Projection of point onto the three edges
real(kind=rk) :: plane_vector(3) ! Vector from first node to projected point
real(kind=rk) :: tri_prod(3) ! Point projections onto edges
real(kind=rk) :: tri_coord(2) ! Point in coordinates of first and second
! edge
real(kind=rk) :: vertex_distance(3) ! Distances to each of the three
! vertices
real(kind=rk) :: edge_distance(3) ! Distances to each of the three edges
real(kind=rk) :: normlen_squared
real(kind=rk) :: normlen
real(kind=rk) :: det
! ---------------------------------------------------------------------- !
closekind = tem_triangle_close_invalid
closest = 0.0_rk
distance = huge(distance)
! First: project the point onto the plane of the triangle
! if the projected point is inside the triangle the projected point
! is the `closest`.
edge(:,1) = me%nodes(:,2) - me%nodes(:,1)
edge(:,2) = me%nodes(:,3) - me%nodes(:,1)
edge(:,3) = me%nodes(:,3) - me%nodes(:,2)
normal = cross_product3D(edge(:,1), edge(:,2))
normlen_squared = normal(1)**2 + normal(2)**2 + normal(3)**2
validtri: if (normlen_squared > epsilon(normlen_squared)) then
normlen = sqrt(normlen_squared)
normal = normal / normlen
tri2p = point - me%nodes(:,1)
edgelen_squared(1) = dot_product(edge(:,1), edge(:,1))
edgelen_squared(2) = dot_product(edge(:,2), edge(:,2))
edgeprod1_2 = dot_product(edge(:,1), edge(:,2))
plane_point = point - dot_product(tri2p, normal)
plane_vector = plane_point - me%nodes(:,1)
tri_prod(1) = dot_product(plane_vector, edge(:,1))
tri_prod(2) = dot_product(plane_vector, edge(:,2))
det = edgeprod1_2**2 - (edgelen_squared(1)*edgelen_squared(2))
tri_coord(1) = ( (edgeprod1_2 * tri_prod(2)) &
& - (edgelen_squared(2) * tri_prod(1)) )
tri_coord(2) = ( (edgeprod1_2 * tri_prod(1)) &
& - (edgelen_squared(1) * tri_prod(2)) )
if (tri_coord(1) > tiny(0.0_rk) .and. tri_coord(2) > tiny(0.0_rk) &
& .and. (tri_coord(1) + tri_coord(2)) < 1.0_rk ) then
! The projected point lies within the triangle
closekind = tem_triangle_close_face
closest = plane_point
normal = point - plane_point
distance = sqrt(dot_product(normal, normal))
else
edgelen_squared(3) = dot_product(edge(:,3), edge(:,3))
tri_prod(3) = dot_product(plane_point - me%nodes(:,2), edge(:,3))
! The projected point lies outside the triangle (or on its boundary).
! Thus we check for the closest point on the boundary
! (vertices and edges).
vertex_distance(1) = sqrt(sum((point - me%nodes(:,1))**2))
vertex_distance(2) = sqrt(sum((point - me%nodes(:,2))**2))
vertex_distance(3) = sqrt(sum((point - me%nodes(:,3))**2))
min_vertex = minloc(vertex_distance, 1)
edgescale = tri_prod / edgelen_squared
! Limit the points to the length of the edge
edgescale = min(edgescale, 1.0_rk)
edgescale = max(edgescale, 0.0_rk)
edge_point(:,1) = me%nodes(:,1) + edgescale(1) * edge(:,1)
edge_point(:,2) = me%nodes(:,1) + edgescale(2) * edge(:,2)
edge_point(:,3) = me%nodes(:,2) + edgescale(3) * edge(:,3)
edge_distance(1) = sqrt(sum((point - edge_point(:,1))**2))
edge_distance(2) = sqrt(sum((point - edge_point(:,2))**2))
edge_distance(3) = sqrt(sum((point - edge_point(:,3))**2))
min_edge = minloc(vertex_distance, 1)
closekind = tem_triangle_close_edge
closest = edge_point(:, min_edge)
distance = edge_distance(min_edge)
if (vertex_distance(min_vertex) < distance + epsilon(distance)) then
closekind = tem_triangle_close_vertex
closest = me%nodes(:, min_vertex)
distance = vertex_distance(min_vertex)
end if
if (dot_product(normal, point-closest) < 0.0_rk) then
! flip the normal to point to the side of the given point
normal = -normal
end if
end if
end if validtri
end subroutine tem_triangle_normal_proximity