Skip to content

Commit 9f41e35

Browse files
committed
First try at 'overlaps' - buggy for now
1 parent 5ba1ace commit 9f41e35

File tree

2 files changed

+166
-6
lines changed

2 files changed

+166
-6
lines changed

src/osut/osut.py

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3610,11 +3610,11 @@ def poly(pts=None, vx=False, uq=False, co=False, tt=False, sq="no") -> openstudi
36103610
if not pln.pointOnPlane(pt): return oslg.empty("plane", mth, CN.ERR, v)
36113611

36123612
t = openstudio.Transformation.alignFace(pts)
3613-
at = t.inverse() * pts
3613+
at = list(t.inverse() * pts)
36143614
at.reverse()
36153615

36163616
if isinstance(tt, cl):
3617-
att = tt.inverse() * pts
3617+
att = list(tt.inverse() * pts)
36183618
att.reverse()
36193619

36203620
if areSame(at, att):
@@ -3628,7 +3628,7 @@ def poly(pts=None, vx=False, uq=False, co=False, tt=False, sq="no") -> openstudi
36283628
t = openstudio.Transformation.alignFace(att)
36293629

36303630
if t:
3631-
a = t.inverse() * att
3631+
a = list(t.inverse() * att)
36323632
a.reverse()
36333633
else:
36343634
a = att
@@ -3717,13 +3717,13 @@ def isPointWithinPolygon(p0=None, s=[], entirely=False) -> bool:
37173717
s = poly(s, False, True, True)
37183718
if not s: return oslg.empty("polygon", mth, CN.DBG, False)
37193719

3720-
n = OpenStudio.getOutwardNormal(s)
3720+
n = openstudio.getOutwardNormal(s)
37213721
if not n: return oslg.invalid("plane/normal", mth, 2, CN.DBG, False)
37223722

37233723
n = n.get()
37243724
pl = openstudio.Plane(s[0], n)
37253725
if not pl.pointOnPlane(p0): return False
3726-
if not isinstance(entireley, bool): entirely = False
3726+
if not isinstance(entirely, bool): entirely = False
37273727

37283728
segs = segments(s)
37293729

@@ -3931,6 +3931,166 @@ def isSquare(pts=None) -> bool:
39313931
return True
39323932

39333933

3934+
def fits(p1=None, p2=None, entirely=False) -> bool:
3935+
"""Determines whether a 1st OpenStudio polygon (p1) fits within a 2nd
3936+
polygon (p2). Vertex sequencing of both polygons must be counterclockwise.
3937+
If option 'entirely' is True, then the method returns False if a 'p1' point
3938+
lies along any of the 'p2' polygon edges, or is very near any of its
3939+
vertices.
3940+
3941+
Args:
3942+
p1 (openstudio.Point3d):
3943+
1st OpenStudio vector of 3D points.
3944+
p2 (openstudio.Point3d):
3945+
2nd OpenStudio vector of 3D points.
3946+
entirely (bool):
3947+
Whether point should be neatly within polygon limits.
3948+
3949+
Returns:
3950+
bool: Whether 1st polygon fits within the 2nd polygon.
3951+
False: If invalid input (see logs).
3952+
3953+
"""
3954+
pts = []
3955+
p1 = poly(p1)
3956+
p2 = poly(p2)
3957+
if not p1: return False
3958+
if not p2: return False
3959+
3960+
for p0 in p1:
3961+
if not isPointWithinPolygon(p0, p2): return False
3962+
3963+
# Although p2 points may lie ALONG p1, none may lie entirely WITHIN p1.
3964+
for p0 in p2:
3965+
if isPointWithinPolygon(p0, p1): return False
3966+
3967+
# p1 segment mid-points must not lie OUTSIDE of p2.
3968+
for sg in segments(p1):
3969+
mp = midpoint(sg[0], sg[1])
3970+
if not isPointWithinPolygon(mp, p2): return False
3971+
3972+
if not isinstance(entirely, bool): entirely = False
3973+
if not entirely: return True
3974+
3975+
for p0 in p1:
3976+
if not isPointWithinPolygon(p0, p2, entirely): return False
3977+
3978+
return True
3979+
3980+
3981+
def overlaps(p1=None, p2=None, flat=False) -> bool:
3982+
"""Returns intersection of overlapping polygons, empty if non intersecting.
3983+
If the optional 3rd argument is left as False, the 2nd polygon may only
3984+
overlap if it shares the 3D plane equation of the 1st one. If the 3rd
3985+
argument is instead set to True, then the 2nd polygon is first 'cast' onto
3986+
the 3D plane of the 1st one; the method therefore returns (as overlap) the
3987+
intersection of a 'projection' of the 2nd polygon onto the 1st one. The
3988+
method returns the smallest of the 2 polygons if either fits within the
3989+
larger one.
3990+
3991+
Args:
3992+
p1 (openstudio.Point3d):
3993+
1st OpenStudio vector of 3D points.
3994+
p2 (openstudio.Point3d):
3995+
2nd OpenStudio vector of 3D points.
3996+
flat (bool):
3997+
Whether to first project the 2nd set onto the 1st set plane.
3998+
3999+
Returns:
4000+
openstudio.Point3dVector: Largest intersection (see logs if empty).
4001+
4002+
"""
4003+
mth = "osut.overlap"
4004+
face = openstudio.Point3dVector()
4005+
p01 = poly(p1)
4006+
p02 = poly(p2)
4007+
if not p01: return oslg.empty("points 1", mth, CN.DBG, face)
4008+
if not p02: return oslg.empty("points 2", mth, CN.DBG, face)
4009+
if fits(p01, p02): return p01
4010+
if fits(p02, p01): return p02
4011+
4012+
if not isinstance(flat, bool): flat = False
4013+
4014+
if shareXYZ(p01, "z"):
4015+
t = None
4016+
a1 = list(p01)
4017+
a2 = list(p02)
4018+
cw1 = isClockwise(p01)
4019+
if cw1: a1.reverse()
4020+
if flat: a2 = flatten(a2)
4021+
4022+
if not shareXYZ(a2, "z"):
4023+
return invalid("points 2", mth, 2, CN.DBG, face)
4024+
4025+
cw2 = isClockwise(a2)
4026+
if cw2: a2.reverse()
4027+
else:
4028+
t = openstudio.Transformation.alignFace(p01)
4029+
a1 = t.inverse() * p01
4030+
a2 = t.inverse() * p02
4031+
if flat: a2 = list(flatten(a2))
4032+
4033+
if not shareXYZ(a2, "z"):
4034+
return invalid("points 2", mth, 2, CN.DBG, face)
4035+
4036+
cw2 = isClockwise(a2)
4037+
if cw2: a2.reverse()
4038+
4039+
# Return either (transformed) polygon if one fits into the other.
4040+
p1t = p01
4041+
4042+
if t:
4043+
if not cw2: a2.reverse()
4044+
p2t = to_p3Dv(t * a2)
4045+
else:
4046+
if cw1:
4047+
if cw2: a2.reverse()
4048+
p2t = to_p3Dv(a2)
4049+
else:
4050+
if not cw2: a2.reverse()
4051+
p2t = to_p3Dv(a2)
4052+
4053+
if fits(a1, a2): return p1t
4054+
if fits(a2, a1): return p2t
4055+
4056+
area1 = openstudio.getArea(a1)
4057+
area2 = openstudio.getArea(a2)
4058+
4059+
if not area1: return oslg.empty("points 1 area", mth, CN.ERR, face)
4060+
if not area2: return oslg.empty("points 2 area", mth, CN.ERR, face)
4061+
4062+
area1 = area1.get()
4063+
area2 = area2.get()
4064+
a1.reverse()
4065+
a2.reverse()
4066+
union = openstudio.join(a1, a2, CN.TOL2)
4067+
if not union: return face
4068+
4069+
union = union.get()
4070+
area = OpenStudio.getArea(union)
4071+
if not area: return face
4072+
4073+
area = area.get()
4074+
delta = area1 + area2 - area
4075+
4076+
if area > CN.TOL:
4077+
if round(area, 2) == round(area1, 2): return face
4078+
if round(area, 2) == round(area2, 2): return face
4079+
if round(delta, 2) == 0: return face
4080+
4081+
res = openstudio.intersect(a1, a2, CN.TOL)
4082+
if not res: return face
4083+
4084+
res = res.get()
4085+
res1 = res.polygon1()
4086+
if not res1: return face
4087+
4088+
res1.reverse()
4089+
if t: res1 = t * res1
4090+
4091+
return to_p3Dv(res1)
4092+
4093+
39344094
def facets(spaces=[], boundary="all", type="all", sides=[]) -> list:
39354095
"""Returns an array of OpenStudio space surfaces or subsurfaces that match
39364096
criteria, e.g. exterior, north-east facing walls in hotel "lobby". Note

tests/test_osut.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2444,7 +2444,7 @@ def test23_fits_overlaps(self):
24442444
self.assertTrue(res1_m2)
24452445
res1_m2 = res1_m2.get()
24462446
self.assertAlmostEqual(res1_m2, delta, places=2)
2447-
# self.assertTrue(mod1.doesOverlap(p1a, p2a))
2447+
# self.assertTrue(osut.overlaps(p1a, p2a))
24482448
# self.assertEqual(o.status(), 0)
24492449

24502450
def test24_triangulation(self):

0 commit comments

Comments
 (0)