Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 10 additions & 23 deletions src/osut/osut.py
Original file line number Diff line number Diff line change
Expand Up @@ -3205,7 +3205,8 @@ def uniques(pts=None, n=0) -> openstudio.Point3dVector:
try:
n = int(n)
except:
return oslg.mismatch("n unique points", n, int, mth, CN.DBG, v)
oslg.mismatch("n points", n, int, mth, CN.DBG)
n = 0
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 3 OSut functions that take in n as argument:

  • uniques(pts, n=0):
  • collinear(pts, n=0)
  • noncollinears(pts, n=0)

... where n is an (optional) user-requested number of points to return. For instance, a user may request the first 3 points of a hexagon (6 points) to determine a plane equation.

Unfortunately, all three functions did not share the same validation checks and treatment of n - an inconsistency and source of confusion. Furthermore, DEBUG or ERROR messages shouldn't be logged when users incorrectly guess in advance what the admissible range of points may be. So these changes:

  • reduce the volume a bit
  • harmonize the treatment of n across all three methods
  • introduces stress tests


for pt in pts:
if not holds(v, pt): v.append(pt)
Expand Down Expand Up @@ -3659,24 +3660,19 @@ def nonCollinears(pts=None, n=0) -> openstudio.Point3dVector:
Requested number of non-collinears (0 returns all).

Returns:
openstudio.Point3dVector: non-collinears (see logs if empty).
openstudio.Point3dVector: non-collinears (see logs).

"""
mth = "osut.nonCollinears"
v = openstudio.Point3dVector()
a = []
pts = uniques(pts)
if len(pts) < 3: return pts

try:
n = int(n)
except:
oslg.mismatch("n non-collinears", n, int, mth, CN.DBG, v)

if n > len(pts):
return oslg.invalid("+n non-collinears", mth, 0, CN.ERR, v)
elif n < 0 and abs(n) > len(pts):
return oslg.invalid("-n non-collinears", mth, 0, CN.ERR, v)
oslg.mismatch("n points", n, int, mth, CN.DBG)
n = 0

# Evaluate cross product of vectors of 3x sequential points.
for i2, p2 in enumerate(pts):
Expand All @@ -3698,9 +3694,7 @@ def nonCollinears(pts=None, n=0) -> openstudio.Point3dVector:
a.rotate(1)
a = list(a)

if n > len(a): return p3Dv(a)
if n < 0 and abs(n) > len(a): return p3Dv(a)

if abs(n) > len(a): n = 0
if n > 0: a = a[0:n]
if n < 0: a = a[n:]

Expand All @@ -3718,34 +3712,27 @@ def collinears(pts=None, n=0) -> openstudio.Point3dVector:
Requested number of collinears (0 returns all).

Returns:
openstudio.Point3dVector: collinears (see logs if empty).
openstudio.Point3dVector: collinears (see logs).

"""
mth = "osut.collinears"
v = openstudio.Point3dVector()
a = []
pts = uniques(pts)
if len(pts) < 3: return pts

try:
n = int(n)
except:
oslg.mismatch("n collinears", n, int, mth, CN.DBG, v)

if n > len(pts):
return oslg.invalid("+n collinears", mth, 0, CN.ERR, v)
elif n < 0 and abs(n) > len(pts):
return oslg.invalid("-n collinears", mth, 0, CN.ERR, v)
oslg.mismatch("n points", n, int, mth, CN.DBG)
n = 0

ncolls = nonCollinears(pts)
if not ncolls: return pts

for pt in pts:
if pt not in ncolls: a.append(pt)

if n > len(a): return p3Dv(a)
if n < 0 and abs(n) > len(a): return p3Dv(a)

if abs(n) > len(a): n = 0
if n > 0: a = a[0:n]
if n < 0: a = a[n:]

Expand Down
88 changes: 76 additions & 12 deletions tests/test_osut.py
Original file line number Diff line number Diff line change
Expand Up @@ -3308,48 +3308,112 @@ def test25_segments_triads_orientation(self):
p7 = openstudio.Point3d(14, 20, -5)
p8 = openstudio.Point3d(-9, -9, -5)

# Stress tests.
m1 = "Invalid '+n collinears' (osut.collinears)"
m2 = "Invalid '-n collinears' (osut.collinears)"
# Stress test 'uniques'.
m0 = "'n points' str? expecting int (osut.uniques)"

# Invalid case.
uniks = osut.uniques([p0, p1, p2, p3], "osut")
self.assertTrue(isinstance(uniks, openstudio.Point3dVector))
self.assertEqual(len(uniks), 4)
self.assertTrue(o.is_debug())
self.assertEqual(len(o.logs()), 1)
self.assertEqual(o.logs()[0]["message"], m0)
self.assertEqual(o.clean(), DBG)

# Valid, basic case.
uniks = osut.uniques([p0, p1, p2, p3])
self.assertTrue(isinstance(uniks, openstudio.Point3dVector))
self.assertEqual(len(uniks), 4)
self.assertEqual(o.status(), 0)

uniks = osut.uniques([p0, p1, p2, p3], 0)
self.assertTrue(isinstance(uniks, openstudio.Point3dVector))
self.assertEqual(len(uniks), 4)
self.assertEqual(o.status(), 0)

# Valid, first 3 points.
uniks = osut.uniques([p0, p1, p2, p3], 3)
self.assertTrue(isinstance(uniks, openstudio.Point3dVector))
self.assertEqual(len(uniks), 3)
self.assertEqual(o.status(), 0)

# Valid, last 3 points.
uniks = osut.uniques([p0, p1, p2, p3], -3)
self.assertTrue(isinstance(uniks, openstudio.Point3dVector))
self.assertEqual(len(uniks), 3)
self.assertEqual(o.status(), 0)

# Valid, n = 5: returns original 4 uniques points.
uniks = osut.uniques([p0, p1, p2, p3], 5)
self.assertTrue(isinstance(uniks, openstudio.Point3dVector))
self.assertEqual(len(uniks), 4)
self.assertEqual(o.status(), 0)

# Valid, n = -5: returns original 4 uniques points.
uniks = osut.uniques([p0, p1, p2, p3], -5)
self.assertTrue(isinstance(uniks, openstudio.Point3dVector))
self.assertEqual(len(uniks), 4)
self.assertEqual(o.status(), 0)

# Stress tests collinears.
m0 = "'n points' str? expecting int (osut.collinears)"

# Invalid case - raise DEBUG message, yet returns valid collinears.
collinears = osut.collinears([p0, p1, p3, p8], "osut")
self.assertTrue(isinstance(collinears, openstudio.Point3dVector))
self.assertEqual(len(collinears), 1)
self.assertTrue(osut.areSame(collinears[0], p0))
self.assertTrue(o.is_debug())
self.assertEqual(len(o.logs()), 1)
self.assertEqual(o.logs()[0]["message"], m0)
self.assertEqual(o.clean(), DBG)

# Valid, basic case
collinears = osut.collinears([p0, p1, p3, p8])
self.assertEqual(len(collinears), 1)
self.assertTrue(osut.areSame(collinears[0], p0))

collinears = osut.collinears([p0, p1, p3, p8], 0)
self.assertEqual(len(collinears), 1)
self.assertTrue(osut.areSame(collinears[0], p0))

collinears = osut.collinears([p0, p1, p2, p3, p8])
self.assertEqual(len(collinears), 2)
self.assertTrue(osut.areSame(collinears[0], p0))
self.assertTrue(osut.areSame(collinears[1], p1))

# Only 2 collinears, so request for first 3 is ignored.
collinears = osut.collinears([p0, p1, p2, p3, p8], 3)
self.assertEqual(len(collinears), 2)
self.assertTrue(osut.areSame(collinears[0], p0))
self.assertTrue(osut.areSame(collinears[1], p1))

# First collinear (out of 2).
collinears = osut.collinears([p0, p1, p2, p3, p8], 1)
self.assertEqual(len(collinears), 1)
self.assertTrue(osut.areSame(collinears[0], p0))

# Last collinear (out of 2).
collinears = osut.collinears([p0, p1, p2, p3, p8], -1)
self.assertEqual(len(collinears), 1)
self.assertTrue(osut.areSame(collinears[0], p1))

# First two vs last two: same result.
collinears = osut.collinears([p0, p1, p2, p3, p8], -2)
self.assertEqual(len(collinears), 2)
self.assertTrue(osut.areSame(collinears[0], p0))
self.assertTrue(osut.areSame(collinears[1], p1))

# Ignore n request when abs(n) > number of actual collinears.
collinears = osut.collinears([p0, p1, p2, p3, p8], 6)
self.assertTrue(o.is_error())
self.assertEqual(len(o.logs()), 1)
self.assertEqual(o.logs()[0]["message"], m1)
self.assertEqual(o.clean(), DBG)
self.assertEqual(len(collinears), 2)
self.assertTrue(osut.areSame(collinears[0], p0))
self.assertTrue(osut.areSame(collinears[1], p1))

collinears = osut.collinears([p0, p1, p2, p3, p8], -6)
self.assertTrue(o.is_error())
self.assertEqual(len(o.logs()), 1)
self.assertEqual(o.logs()[0]["message"], m2)
self.assertEqual(o.clean(), DBG)
self.assertEqual(len(collinears), 2)
self.assertTrue(osut.areSame(collinears[0], p0))
self.assertTrue(osut.areSame(collinears[1], p1))

# CASE a1: 2x end-to-end line segments (returns matching endpoints).
self.assertTrue(osut.doesLineIntersect([p0, p1], [p1, p2]))
Expand Down Expand Up @@ -3659,7 +3723,7 @@ def test26_ulc_blc(self):
# [70, 0, 0]
# [70, 45, 0]
# [ 0, 45, 0]

def test27_polygon_attributes(self):
o = osut.oslg
self.assertEqual(o.status(), 0)
Expand Down