/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ /****************************************************************************/ #ifndef __VCG_DISTANCE3 #define __VCG_DISTANCE3 #include #include namespace vcg { /* * Computes the minimum distance between a 3D box and a point * @param[in] p The input point * @param[in] b The input bounding box * @param[out] dist The distance */ template void PointBoxDistance(const Point3 &p, const Box3 &b, ScalarType& dist) { ///if fall inside return distance to a face if (b.IsIn(p)) { const ScalarType dx = std::min(b.max.X()-p.X(), p.X()-b.min.X()); const ScalarType dy = std::min(b.max.Y()-p.Y(), p.Y()-b.min.Y()); const ScalarType dz = std::min(b.max.Z()-p.Z(), p.Z()-b.min.Z()); dist= std::min(dx, std::min(dy, dz)); } { ScalarType sq_dist = ScalarType(0); for (int i=0; i<3; ++i) { ScalarType delta = ScalarType(0); if (p[i] < b.min[i]) delta = p[i] - b.min[i]; else if (p[i] > b.max[i]) delta = p[i] - b.max[i]; sq_dist += delta * delta; } dist= math::Sqrt(sq_dist); } } /* * Computes the minimum distance between a sphere and a point * @param[in] p The input point * @param[in] sphere The input sphere * @param[out] dist The distance */ template void SpherePointDistance(const Sphere3 &sphere, const Point3 &p, ScalarType& dist) { dist = Distance(p, sphere.Center()) - sphere.Radius(); if(dist < 0) dist = 0; } /* * Computes the minimum distance between a 3D box and a point * @param[in] sphere0 The input sphere * @param[in] sphere1 The input sphere * @param[out] dist The distance */ template void SphereSphereDistance(const Sphere3 &sphere0, const Sphere3 &sphere1, ScalarType& dist) { dist = (sphere1.Center()-sphere0.Center()).Norm() - sphere0.Radius() - sphere1.Radius(); if(dist < 0) dist = 0; return dist; } /* * Computes the minimum squared distance between a between a point and a line * @param[in] l The input line * @param[in] p The input point * @param[out] closest The closest point * @param[out] dist The squared distance */ template void LinePointSquaredDistance(const Line3 &l,const Point3 &p, Point3 &closest,ScalarType &dist) { closest=l.P(l.Projection(p)); dist= (closest - p).SquaredNorm(); } /* * Computes the minimum distance between a between a point and a line * @param[in] l The input line * @param[in] p The input point * @param[out] closest The closest point * @param[out] dist The distance */ template void LinePointDistance(const Line3 &l,const Point3 &p, Point3 &closest,ScalarType &dist) { LinePointSquaredDistance(l,p,closest,dist); dist=sqrt(dist); } /* * Computes the minimum distance between two lines * @param[in] mLine0 The input line0 * @param[in] mLine1 The input line1 * @param[out] parallel true if the two lines are parallel * @param[mClosestPoint0] the closest point on line0 * @param[mClosestPoint1] the closest point on line1 */ template void LineLineDistance(const vcg::Line3 &mLine0, const vcg::Line3 &mLine1, bool ¶llel, ScalarType &dist, vcg::Point3 &mClosestPoint0, vcg::Point3 &mClosestPoint1) { const ScalarType loc_EPSILON=ScalarType(0.000000001); typedef typename vcg::Point3 CoordType; CoordType diff = mLine0.Origin() - mLine1.Origin(); ScalarType a01 = -mLine0.Direction()* mLine1.Direction(); ScalarType b0 = diff *(mLine0.Direction()); ScalarType c = diff.SquaredNorm(); ScalarType det = fabs((ScalarType)1 - a01*a01); ScalarType b1, s0, s1, sqrDist; if (det >=loc_EPSILON) { // Lines are not parallel. b1 = -diff*(mLine1.Direction()); ScalarType invDet = ((ScalarType)1)/det; s0 = (a01*b1 - b0)*invDet; s1 = (a01*b0 - b1)*invDet; sqrDist = s0*(s0 + a01*s1 + ((ScalarType)2)*b0) + s1*(a01*s0 + s1 + ((ScalarType)2)*b1) + c; parallel=false; } else { // Lines are parallel, select any closest pair of points. s0 = -b0; s1 = (ScalarType)0; sqrDist = b0*s0 + c; parallel=true; } ///find the two closest points mClosestPoint0 = mLine0.Origin() + mLine0.Direction()*s0; mClosestPoint1 = mLine1.Origin() + mLine1.Direction()*s1; /*mLine0Parameter = s0; mLine1Parameter = s1;*/ // Account for numerical round-off errors. if (sqrDist < (ScalarType)0) { sqrDist = (ScalarType)0; } dist=sqrt(sqrDist); } /* * Computes the minimum distance between a segment and a point * @param[in] segment The input segment * @param[in] p The input point * @param[in] clos The closest point * @param[in] sqr_dist The squared distance */ template void SegmentPointSquaredDistance( Segment3 s, const Point3 & p, Point3< ScalarType > &clos, ScalarType &sqr_dist) { ///create a line vcg::Line3 l; l.Set(s.P0(),s.P1()-s.P0()); l.Normalize(); ///take the closest point on a Line vcg::LinePointSquaredDistance(l,p,clos,sqr_dist); ///quick test with a BB vcg::Box3 b; b.Add(s.P0()); b.Add(s.P1()); ///if the point is in the BB since it is ALSO along the line ///means that it is also along the SEGMENT if (b.IsIn(clos)) return; else { ///check the extremes ScalarType d0=(s.P0()-p).SquaredNorm(); ScalarType d1=(s.P1()-p).SquaredNorm(); if (d0 void SegmentPointDistance( Segment3 s, const Point3 & p, Point3< ScalarType > &clos, ScalarType &dist) { SegmentPointSquaredDistance(s,p,clos,dist); dist=sqrt(dist); } /* * Computes the minimum distance between two segments * @param[in] s0 The input segment0 * @param[in] s1 The input segment1 * @param[out] parallel true if the two segments are parallel * @param[out] dist the distance * @param[closest0] the closest point on segment0 * @param[closest1] the closest point on segment1 */ template void SegmentSegmentDistance(const vcg::Segment3 &s0, const vcg::Segment3 &s1, ScalarType &dist, bool ¶llel, vcg::Point3 &closest0, vcg::Point3 &closest1) { typedef typename vcg::Point3 CoordType; vcg::Line3 l0,l1; ///construct two lines l0.SetOrigin(s0.P0()); l0.SetDirection((s0.P1()-s0.P0()).Normalize()); l1.SetOrigin(s1.P0()); l1.SetDirection((s1.P1()-s1.P0()).Normalize()); ///then find closest point ScalarType line_dist; CoordType closest_test0,closest_test1; LineLineDistance(l0,l1,parallel,line_dist,closest_test0,closest_test1); ///special case if the two lines are parallel if (parallel) { ///find the minimum distance between extremes to segments ScalarType dist_test; CoordType clos_test; //CoordType to_test[4]={s1.P0(),s1.P1(),s0.P0(),s1.P1()}; ///find combination of distances between all extremes and segments SegmentPointSquaredDistance(s0,s1.P0(),clos_test,dist); closest0=clos_test; closest1=s1.P0(); ///and find the minimum updating coherently the closest points SegmentPointSquaredDistance(s0,s1.P1(),clos_test,dist_test); if (dist_test void TrianglePointDistance(const vcg::Triangle3 &t, const typename vcg::Point3 & q, ScalarType & dist, typename vcg::Point3 & closest ) { typedef typename vcg::Point3 CoordType; CoordType clos[3]; ScalarType distv[3]; CoordType clos_proj; ScalarType distproj; ///find distance on the plane vcg::Plane3 plane; plane.Init(t.P(0),t.P(1),t.P(2)); clos_proj=plane.Projection(q); ///control if inside/outside CoordType n=(t.P(1)-t.P(0))^(t.P(2)-t.P(0)); CoordType n0=(t.P(0)-clos_proj)^(t.P(1)-clos_proj); CoordType n1=(t.P(1)-clos_proj)^(t.P(2)-clos_proj); CoordType n2=(t.P(2)-clos_proj)^(t.P(0)-clos_proj); distproj=(clos_proj-q).Norm(); if (((n*n0)>=0)&&((n*n1)>=0)&&((n*n2)>=0)) { closest=clos_proj; dist=distproj; return; } //distance from the edges vcg::Segment3 e0=vcg::Segment3(t.P(0),t.P(1)); vcg::Segment3 e1=vcg::Segment3(t.P(1),t.P(2)); vcg::Segment3 e2=vcg::Segment3(t.P(2),t.P(0)); SegmentPointDistance(e0,q,clos[0],distv[0]); SegmentPointDistance(e1,q,clos[1],distv[1]); SegmentPointDistance(e2,q,clos[2],distv[2]); /*clos[0]=ClosestPoint( e0, q); clos[1]=ClosestPoint( e1, q); clos[2]=ClosestPoint( e2, q); */ //distv[0]=(clos[0]-q).Norm(); //distv[1]=(clos[1]-q).Norm(); //distv[2]=(clos[2]-q).Norm(); int min=0; ///find minimum distance for (int i=1;i<3;i++) { if (distv[i] void TriangleSegmentDistance(const vcg::Triangle3 &t, const vcg::Segment3 &s, ScalarType & dist) { dist=std::numeric_limits::max(); ///test the intersection ScalarType a,b; typedef typename vcg::Point3 CoordType; bool intersect=IntersectionSegmentTriangle >(s,t,a,b); if (intersect) { dist=0; return; } ///project endpoints and see if they fall into the triangle vcg::Plane3 pl3; pl3.Init(t.P(0),t.P(1),t.P(2)); CoordType pj0=pl3.Projection(s.P(0)); CoordType pj1=pl3.Projection(s.P(1)); ///take distances ScalarType dpj0=(pj0-s.P(0)).Norm(); ScalarType dpj1=(pj1-s.P(1)).Norm(); ///test if they fall inside the triangle CoordType bary0,bary1; bool Inside0=vcg::InterpolationParameters(t,pj0,bary0); bool Inside1=vcg::InterpolationParameters(t,pj1,bary1); if (Inside0&&Inside1) { dist=std::min(dpj0,dpj1); return; } ///initialize with the sdistance if only once falls into if (Inside0) dist=dpj0; if (Inside1) dist=dpj1; ///then test segment-to segment distance with edges of the triangle for (int i=0;i<3;i++) { vcg::Segment3 edge=vcg::Segment3(t.P0(i),t.P0((i+1)%3)); ScalarType test_dist; CoordType clos1,clos2; bool parallel; vcg::SegmentSegmentDistance(s,edge,test_dist,parallel,clos1,clos2); if (test_dist void TriangleTriangleDistance(const vcg::Triangle3 &t0, const vcg::Triangle3 &t1, ScalarType &dist) { const ScalarType loc_EPSILON=(vcg::DoubleArea(t0)+vcg::DoubleArea(t1))*(ScalarType)0.0000001; dist=std::numeric_limits::max(); ///test each segment of t1 with t0 ///keeping the minimum distance for (int i=0;i<3;i++) { vcg::Segment3 edge=vcg::Segment3(t0.P0(i),t0.P0((i+1)%3)); ScalarType test_dist; vcg::TriangleSegmentDistance(t1,edge,test_dist); if (test_dist edge=vcg::Segment3(t1.P0(i),t1.P0((i+1)%3)); ScalarType test_dist; vcg::TriangleSegmentDistance(t0,edge,test_dist); if (test_dist