Skip to content

Commit 5d36fe8

Browse files
committed
New closestPointTo function
1 parent 8ab487a commit 5d36fe8

3 files changed

Lines changed: 81 additions & 7 deletions

File tree

src/scenic/core/regions.py

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

271276
class 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

src/scenic/core/vectors.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import numpy
1818
from scipy.spatial.transform import Rotation
19-
import shapely.geometry
19+
import shapely
2020

2121
from scenic.core.distributions import (
2222
Distribution,
@@ -450,7 +450,9 @@ def toVector(self) -> Vector:
450450

451451
@staticmethod
452452
def _canCoerceType(ty):
453-
return issubclass(ty, (tuple, list, numpy.ndarray)) or hasattr(ty, "toVector")
453+
return issubclass(ty, (tuple, list, numpy.ndarray, shapely.Point)) or hasattr(
454+
ty, "toVector"
455+
)
454456

455457
@staticmethod
456458
def _coerce(thing) -> Vector:
@@ -461,6 +463,8 @@ def _coerce(thing) -> Vector:
461463
"expected 2D/3D vector, got " f"{type(thing).__name__} of length {l}"
462464
)
463465
return Vector(*thing)
466+
elif isinstance(thing, shapely.Point):
467+
return Vector(*thing.coords[0])
464468
else:
465469
return thing.toVector()
466470

src/scenic/core/workspaces.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ def distanceTo(self, point):
117117
return self.region.distanceTo(point)
118118

119119
def projectVector(self, point, onDirection):
120-
raise self.region.projectVector(point, onDirection)
120+
return self.region.projectVector(point, onDirection)
121+
122+
def closestPointTo(self, target):
123+
return self.region.closestPointTo(target)
121124

122125
@property
123126
def AABB(self):

0 commit comments

Comments
 (0)