@@ -118,16 +118,17 @@ def projectVector(self, point, onDirection):
118118 """Returns point projected onto this region along onDirection."""
119119 pass
120120
121+ @abstractmethod
122+ def closestPointTo (self , target ):
123+ """Returns the closest point to target (also a point) that is contained in the region"""
124+ pass
125+
121126 @property
122127 @abstractmethod
123128 def AABB (self ):
124129 """Axis-aligned bounding box for this `Region`."""
125130 pass
126131
127- @cached_property
128- def midpoint (self ):
129- return Vector (* self .AABB [0 ]) + Vector (* self .AABB [1 ]) / 2
130-
131132 ## Overridable Methods ##
132133 # The following methods can be overriden to get better performance or if the region
133134 # has dependencies (in the case of sampleGiven).
@@ -267,6 +268,10 @@ def __repr__(self):
267268 s += f" { self .name } "
268269 return s + f" at { hex (id (self ))} >"
269270
271+ @cached_property
272+ def midpoint (self ):
273+ return Vector (* self .AABB [0 ]) + Vector (* self .AABB [1 ]) / 2
274+
270275
271276class PointInRegionDistribution (VectorDistribution ):
272277 """Uniform distribution over points in a Region"""
@@ -335,6 +340,9 @@ def distanceTo(self, point):
335340 def projectVector (self , point , onDirection ):
336341 return point
337342
343+ def closestPointTo (self , target ):
344+ return toVector (target )
345+
338346 @property
339347 def AABB (self ):
340348 raise TypeError ("AllRegion does not have a well defined AABB" )
@@ -387,6 +395,9 @@ def distanceTo(self, point):
387395 def projectVector (self , point , onDirection ):
388396 raise RejectionException ("Projecting vector onto empty Region" )
389397
398+ def closestPointTo (self , target ):
399+ raise RejectionException ("Finding closest point in empty Region" )
400+
390401 @property
391402 def AABB (self ):
392403 raise TypeError ("EmptyRegion does not have a well defined AABB" )
@@ -474,6 +485,9 @@ def projectVector(self, point, onDirection):
474485 f'{ type (self ).__name__ } does not yet support projection using "on"'
475486 )
476487
488+ def closestPointTo (self , target ):
489+ raise NotImplementedError
490+
477491 @property
478492 def AABB (self ):
479493 raise NotImplementedError
@@ -587,6 +601,12 @@ def projectVector(self, point, onDirection):
587601 f'{ type (self ).__name__ } does not yet support projection using "on"'
588602 )
589603
604+ def closestPointTo (self , target ):
605+ target = toVector (target )
606+ candidate_points = [region .closestPointTo (target ) for region in self .regions ]
607+ candidate_points .sort (key = lambda pt : pt .distanceTo (target ))
608+ return candidate_points [0 ]
609+
590610 @property
591611 def AABB (self ):
592612 raise NotImplementedError
@@ -688,6 +708,9 @@ def projectVector(self, point, onDirection):
688708 f'{ type (self ).__name__ } does not yet support projection using "on"'
689709 )
690710
711+ def closestPointTo (self , target ):
712+ raise NotImplementedError
713+
691714 @property
692715 def AABB (self ):
693716 raise NotImplementedError
@@ -1010,6 +1033,10 @@ def projectVector(self, point, onDirection):
10101033
10111034 return Vector (* closest_point )
10121035
1036+ @distributionFunction
1037+ def closestPointTo (self , target ):
1038+ return toVector (trimesh .proximity .closest_point (self .mesh , target ))
1039+
10131040 @cached_property
10141041 @distributionFunction
10151042 def circumcircle (self ):
@@ -1769,6 +1796,15 @@ def distanceTo(self, point):
17691796
17701797 return abs (dist )
17711798
1799+ @distributionFunction
1800+ def closestPointTo (self , target ):
1801+ target = toVector (target )
1802+
1803+ if self .containsPoint (target ):
1804+ return target
1805+
1806+ return super ().closestPointTo (target )
1807+
17721808 @distributionFunction
17731809 def minimumDistanceTo (self , other ):
17741810 """Get the minimum distance between this region and another.
@@ -2315,6 +2351,9 @@ def distanceTo(self, point):
23152351 def projectVector (self , point , onDirection ):
23162352 raise NotImplementedError
23172353
2354+ def closestPointTo (self , target ):
2355+ raise NotImplementedError
2356+
23182357 def uniformPointInner (self ):
23192358 # First generate a point uniformly in a box with dimensions
23202359 # equal to scale, centered at the origin.
@@ -2605,6 +2644,12 @@ def projectVector(self, point, onDirection):
26052644 f'{ type (self ).__name__ } does not yet support projection using "on"'
26062645 )
26072646
2647+ @distributionFunction
2648+ def closestPointTo (self , target ):
2649+ target = toVector (target )
2650+ pt_2d = toVector (shapely .ops .nearest_points (self .polygons , target )[0 ])
2651+ return Vector (pt_2d .x , pt_2d .y , target .z )
2652+
26082653 @property
26092654 def AABB (self ):
26102655 raise NotImplementedError
@@ -2844,6 +2889,9 @@ def defaultOrientation(self, point):
28442889 def projectVector (self , point , onDirection ):
28452890 raise NotImplementedError
28462891
2892+ def closestPointTo (self , target ):
2893+ raise NotImplementedError
2894+
28472895 @cached_property
28482896 def AABB (self ):
28492897 return (
@@ -3140,6 +3188,12 @@ def distanceTo(self, point):
31403188 dist2D = shapely .distance (self .polygons , makeShapelyPoint (point ))
31413189 return math .hypot (dist2D , point [2 ] - self .z )
31423190
3191+ @distributionFunction
3192+ def closestPointTo (self , target ):
3193+ target = toVector (target )
3194+ pt_2d = toVector (shapely .ops .nearest_points (self .polygons , target )[0 ])
3195+ return Vector (pt_2d .x , pt_2d .y , self .z )
3196+
31433197 @cached_property
31443198 @distributionFunction
31453199 def inradius (self ):
@@ -3731,6 +3785,10 @@ def distanceTo(self, point) -> float:
37313785 dist2D = self .lineString .distance (makeShapelyPoint (point ))
37323786 return math .hypot (dist2D , point .z )
37333787
3788+ @distributionMethod
3789+ def closestPointTo (self , target ):
3790+ return toVector (shapely .ops .nearest_points (self .lineString , target )[0 ])
3791+
37343792 def projectVector (self , point , onDirection ):
37353793 raise TypeError ('PolylineRegion does not support projection using "on"' )
37363794
@@ -3958,6 +4016,12 @@ def distanceTo(self, point):
39584016 distance , _ = self .kdTree .query (point )
39594017 return distance
39604018
4019+ @distributionMethod
4020+ def closestPointTo (self , target ):
4021+ point = toVector (point ).coordinates
4022+ _ , neighbor_i = self .kdTree .query (point )
4023+ return toVector (self .points [neighbor_i ])
4024+
39614025 def projectVector (self , point , onDirection ):
39624026 raise TypeError ('PointSetRegion does not support projection using "on"' )
39634027
@@ -4082,6 +4146,9 @@ def containsRegionInner(self, reg, tolerance):
40824146 def projectVector (self , point , onDirection ):
40834147 raise TypeError ('GridRegion does not support projection using "on"' )
40844148
4149+ def closestPointTo (self , target ):
4150+ raise NotImplementedError
4151+
40854152
40864153###################################################################################################
40874154# View Regions
0 commit comments