Skip to content

Commit 623ebbb

Browse files
committed
Completes 'fits' & 'overlap' method testing
1 parent d36561c commit 623ebbb

File tree

2 files changed

+247
-2
lines changed

2 files changed

+247
-2
lines changed

src/osut/osut.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,11 +3083,13 @@ def triads(pts=None, co=False) -> openstudio.Point3dVectorVector:
30833083
pts = uniques(pts)
30843084
if len(pts) < 2: return vv
30853085

3086-
for i1, pts in enumerate(pts):
3086+
for i1, p1 in enumerate(pts):
30873087
i2 = i1 + 1
30883088
if i2 == len(pts): i2 = 0
3089+
30893090
i3 = i2 + 1
30903091
if i3 == len(pts): i3 = 0
3092+
30913093
p2 = pts[i2]
30923094
p3 = pts[i3]
30933095

@@ -4128,7 +4130,7 @@ def cast(p1=None, p2=None, ray=None) -> openstudio.Point3dVector:
41284130

41294131
for pt in p1:
41304132
length = n.dot(pt - p0) / n.dot(ray.reverseVector())
4131-
face.append(pt) + scalar(ray, length)
4133+
face.append(pt + scalar(ray, length))
41324134

41334135
return face
41344136

tests/test_osut.py

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2704,6 +2704,249 @@ def test23_fits_overlaps(self):
27042704
self.assertAlmostEqual(dt1, 1, places=2)
27052705
self.assertAlmostEqual(dt2, 1, places=2)
27062706

2707+
# Alternative: first 'cast' vertically one polygon onto the other.
2708+
pl1 = openstudio.Plane(ceiling.vertices())
2709+
pl2 = openstudio.Plane(roof.vertices())
2710+
up = openstudio.Point3d(0, 0, 1) - openstudio.Point3d(0, 0, 0)
2711+
down = openstudio.Point3d(0, 0,-1) - openstudio.Point3d(0, 0, 0)
2712+
cast00 = osut.cast(roof, ceiling, down)
2713+
cast01 = osut.cast(roof, ceiling, up)
2714+
cast02 = osut.cast(ceiling, roof, up)
2715+
self.assertTrue(osut.areParallel(cast00, ceiling))
2716+
self.assertTrue(osut.areParallel(cast01, ceiling))
2717+
self.assertTrue(osut.areParallel(cast02, roof))
2718+
self.assertFalse(osut.areParallel(cast00, roof))
2719+
self.assertFalse(osut.areParallel(cast01, roof))
2720+
self.assertFalse(osut.areParallel(cast02, ceiling))
2721+
2722+
# As the cast ray is vertical, only the Z-axis coordinate changes.
2723+
for i, pt in enumerate(cast00):
2724+
self.assertTrue(pl1.pointOnPlane(pt))
2725+
self.assertAlmostEqual(pt.x(), roof.vertices()[i].x(), places=2)
2726+
self.assertAlmostEqual(pt.y(), roof.vertices()[i].y(), places=2)
2727+
2728+
# The direction of the cast ray doesn't matter (e.g. up or down).
2729+
for i, pt in enumerate(cast01):
2730+
self.assertTrue(pl1.pointOnPlane(pt))
2731+
self.assertAlmostEqual(pt.x(), cast00[i].x(), places=2)
2732+
self.assertAlmostEqual(pt.y(), cast00[i].y(), places=2)
2733+
2734+
# The sequence of arguments matters: 1st polygon is cast onto 2nd.
2735+
for i, pt in enumerate(cast02):
2736+
self.assertTrue(pl2.pointOnPlane(pt))
2737+
self.assertAlmostEqual(pt.x(), ceiling.vertices()[i].x())
2738+
self.assertAlmostEqual(pt.y(), ceiling.vertices()[i].y())
2739+
2740+
# Overlap between roof and vertically-cast ceiling onto roof plane.
2741+
olap02 = osut.overlap(roof, cast02)
2742+
self.assertEqual(len(olap02), 3) # not 5
2743+
self.assertTrue(osut.fits(olap02, roof))
2744+
2745+
for pt in olap02: self.assertTrue(pl2.pointOnPlane(pt))
2746+
2747+
vtx1 = openstudio.Point3dVector()
2748+
vtx1.append(openstudio.Point3d(17.69, 0.00, 0))
2749+
vtx1.append(openstudio.Point3d(13.46, 4.46, 0))
2750+
vtx1.append(openstudio.Point3d( 4.23, 4.46, 0))
2751+
vtx1.append(openstudio.Point3d( 0.00, 0.00, 0))
2752+
2753+
vtx2 = openstudio.Point3dVector()
2754+
vtx2.append(openstudio.Point3d( 8.85, 0.00, 0))
2755+
vtx2.append(openstudio.Point3d( 8.85, 4.46, 0))
2756+
vtx2.append(openstudio.Point3d( 4.23, 4.46, 0))
2757+
vtx2.append(openstudio.Point3d( 4.23, 0.00, 0))
2758+
2759+
self.assertTrue(osut.isPointAlongSegment(vtx2[1], [vtx1[1], vtx1[2]]))
2760+
self.assertTrue(osut.isPointAlongSegments(vtx2[1], vtx1))
2761+
self.assertTrue(osut.isPointWithinPolygon(vtx2[1], vtx1))
2762+
self.assertTrue(osut.fits(vtx2, vtx1))
2763+
2764+
# Bounded box test.
2765+
cast03 = osut.cast(ceiling, south, down)
2766+
self.assertTrue(osut.isRectangular(cast03))
2767+
olap03 = osut.overlap(south, cast03)
2768+
self.assertTrue(osut.areParallel(south, olap03))
2769+
self.assertFalse(osut.isRectangular(olap03))
2770+
box = osut.boundedBox(olap03)
2771+
self.assertTrue(osut.isRectangular(box))
2772+
self.assertTrue(osut.areParallel(olap03, box))
2773+
2774+
area1 = openstudio.getArea(olap03)
2775+
area2 = openstudio.getArea(box)
2776+
self.assertTrue(area1)
2777+
self.assertTrue(area2)
2778+
area1 = area1.get()
2779+
area2 = area2.get()
2780+
self.assertEqual(int(100 * area2 / area1), 68) # %
2781+
self.assertEqual(o.status(), 0)
2782+
2783+
del(model)
2784+
2785+
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- #
2786+
# Testing more complex cases, e.g. triangular windows, irregular 4-side
2787+
# windows, rough opening edges overlapping parent surface edges. These
2788+
# tests were initially part of the TBD Tests repository:
2789+
#
2790+
# github.com/rd2/tbd_tests
2791+
#
2792+
# ... yet have been upgraded and are now tested here.
2793+
model = openstudio.model.Model()
2794+
space = openstudio.model.Space(model)
2795+
space.setName("Space")
2796+
2797+
# Windows are SimpleGlazing constructions.
2798+
fen = openstudio.model.Construction(model)
2799+
glazing = openstudio.model.SimpleGlazing(model)
2800+
layers = openstudio.model.MaterialVector()
2801+
fen.setName("FD fen")
2802+
glazing.setName("FD glazing")
2803+
self.assertTrue(glazing.setUFactor(2.0))
2804+
layers.append(glazing)
2805+
self.assertTrue(fen.setLayers(layers))
2806+
2807+
# Frame & Divider object.
2808+
w000 = 0.000
2809+
w200 = 0.200 # 0mm to 200mm (wide!) around glazing
2810+
fd = openstudio.model.WindowPropertyFrameAndDivider(model)
2811+
fd.setName("FD")
2812+
self.assertTrue(fd.setFrameConductance(0.500))
2813+
self.assertTrue(fd.isFrameWidthDefaulted())
2814+
self.assertAlmostEqual(fd.frameWidth(), w000, places=2)
2815+
2816+
# A square base wall surface:
2817+
v0 = openstudio.Point3dVector()
2818+
v0.append(openstudio.Point3d( 0.00, 0.00, 10.00))
2819+
v0.append(openstudio.Point3d( 0.00, 0.00, 0.00))
2820+
v0.append(openstudio.Point3d(10.00, 0.00, 0.00))
2821+
v0.append(openstudio.Point3d(10.00, 0.00, 10.00))
2822+
2823+
# A first triangular window:
2824+
v1 = openstudio.Point3dVector()
2825+
v1.append(openstudio.Point3d( 2.00, 0.00, 8.00))
2826+
v1.append(openstudio.Point3d( 1.00, 0.00, 6.00))
2827+
v1.append(openstudio.Point3d( 4.00, 0.00, 9.00))
2828+
2829+
# A larger, irregular window:
2830+
v2 = openstudio.Point3dVector()
2831+
v2.append(openstudio.Point3d( 7.00, 0.00, 4.00))
2832+
v2.append(openstudio.Point3d( 4.00, 0.00, 1.00))
2833+
v2.append(openstudio.Point3d( 8.00, 0.00, 2.00))
2834+
v2.append(openstudio.Point3d( 9.00, 0.00, 3.00))
2835+
2836+
# A final triangular window, near the wall's upper right corner:
2837+
v3 = openstudio.Point3dVector()
2838+
v3.append(openstudio.Point3d( 9.00, 0.00, 9.80))
2839+
v3.append(openstudio.Point3d( 9.80, 0.00, 9.00))
2840+
v3.append(openstudio.Point3d( 9.80, 0.00, 9.80))
2841+
2842+
w0 = openstudio.model.Surface(v0, model)
2843+
w1 = openstudio.model.SubSurface(v1, model)
2844+
w2 = openstudio.model.SubSurface(v2, model)
2845+
w3 = openstudio.model.SubSurface(v3, model)
2846+
w0.setName("w0")
2847+
w1.setName("w1")
2848+
w2.setName("w2")
2849+
w3.setName("w3")
2850+
self.assertTrue(w0.setSpace(space))
2851+
sub_gross = 0
2852+
2853+
for w in [w1, w2, w3]:
2854+
self.assertTrue(w.setSubSurfaceType("FixedWindow"))
2855+
self.assertTrue(w.setSurface(w0))
2856+
self.assertTrue(w.setConstruction(fen))
2857+
self.assertTrue(w.uFactor())
2858+
self.assertAlmostEqual(w.uFactor().get(), 2.0, places=1)
2859+
self.assertTrue(w.allowWindowPropertyFrameAndDivider())
2860+
self.assertTrue(w.setWindowPropertyFrameAndDivider(fd))
2861+
width = w.windowPropertyFrameAndDivider().get().frameWidth()
2862+
self.assertAlmostEqual(width, w000, places=2)
2863+
2864+
sub_gross += w.grossArea()
2865+
2866+
self.assertAlmostEqual(w1.grossArea(), 1.50, places=2)
2867+
self.assertAlmostEqual(w2.grossArea(), 6.00, places=2)
2868+
self.assertAlmostEqual(w3.grossArea(), 0.32, places=2)
2869+
self.assertAlmostEqual(w0.grossArea(), 100.00, places=2)
2870+
self.assertAlmostEqual(w1.netArea(), w1.grossArea(), places=2)
2871+
self.assertAlmostEqual(w2.netArea(), w2.grossArea(), places=2)
2872+
self.assertAlmostEqual(w3.netArea(), w3.grossArea(), places=2)
2873+
self.assertAlmostEqual(w0.netArea(), w0.grossArea()-sub_gross, places=2)
2874+
2875+
# Applying 2 sets of alterations:
2876+
# - WITHOUT, then WITH Frame & Dividers (F&D)
2877+
# - 3 successive 20° rotations around:
2878+
angle = math.pi / 9
2879+
origin = openstudio.Point3d(0, 0, 0)
2880+
east = openstudio.Point3d(1, 0, 0) - origin
2881+
up = openstudio.Point3d(0, 0, 1) - origin
2882+
north = openstudio.Point3d(0, 1, 0) - origin
2883+
2884+
for i in range(4): # successive rotations
2885+
if i != 0:
2886+
if i == 1: r = openstudio.createRotation(origin, east, angle)
2887+
if i == 2: r = openstudio.createRotation(origin, up, angle)
2888+
if i == 3: r = openstudio.createRotation(origin, north, angle)
2889+
self.assertTrue(w0.setVertices(r.inverse() * w0.vertices()))
2890+
self.assertTrue(w1.setVertices(r.inverse() * w1.vertices()))
2891+
self.assertTrue(w2.setVertices(r.inverse() * w2.vertices()))
2892+
self.assertTrue(w3.setVertices(r.inverse() * w3.vertices()))
2893+
2894+
for j in range(2): # F&D
2895+
if j == 0:
2896+
wx = w000
2897+
if i != 0: fd.resetFrameWidth()
2898+
else:
2899+
wx = w200
2900+
self.assertTrue(fd.setFrameWidth(wx))
2901+
2902+
for w in [w1, w2, w3]:
2903+
wfd = w.windowPropertyFrameAndDivider().get()
2904+
width = wfd.frameWidth()
2905+
self.assertAlmostEqual(width, wx, places=2)
2906+
2907+
# F&D widths offset window vertices.
2908+
w1o = osut.offset(w1.vertices(), wx, 300)
2909+
w2o = osut.offset(w2.vertices(), wx, 300)
2910+
w3o = osut.offset(w3.vertices(), wx, 300)
2911+
2912+
w1o_m2 = openstudio.getArea(w1o)
2913+
w2o_m2 = openstudio.getArea(w2o)
2914+
w3o_m2 = openstudio.getArea(w3o)
2915+
self.assertTrue(w1o_m2)
2916+
self.assertTrue(w2o_m2)
2917+
self.assertTrue(w3o_m2)
2918+
w1o_m2 = w1o_m2.get()
2919+
w2o_m2 = w2o_m2.get()
2920+
w3o_m2 = w3o_m2.get()
2921+
2922+
if j == 0:
2923+
# w1 == 1.50m2; w2 == 6.00 m2; w3 == 0.32m2
2924+
self.assertAlmostEqual(w1o_m2, w1.grossArea(), places=2)
2925+
self.assertAlmostEqual(w2o_m2, w2.grossArea(), places=2)
2926+
self.assertAlmostEqual(w3o_m2, w3.grossArea(), places=2)
2927+
else:
2928+
self.assertAlmostEqual(w1o_m2, 3.75, places=2)
2929+
self.assertAlmostEqual(w2o_m2, 8.64, places=2)
2930+
self.assertAlmostEqual(w3o_m2, 1.10, places=2)
2931+
2932+
# All windows entirely fit within the wall (without F&D).
2933+
for w in [w1, w2, w3]: self.assertTrue(osut.fits(w, w0, True))
2934+
2935+
# All windows fit within the wall (with F&D).
2936+
for w in [w1o, w2o]: self.assertTrue(osut.fits(w, w0))
2937+
2938+
# If F&D frame width == 200mm, w3o aligns along the wall top &
2939+
# side, so not entirely within wall polygon.
2940+
self.assertTrue(osut.fits(w3, w0, True))
2941+
self.assertTrue(osut.fits(w3o, w0))
2942+
if j == 0: self.assertTrue(osut.fits(w3o, w0, True))
2943+
if j != 0: self.assertFalse(osut.fits(w3o, w0, True))
2944+
2945+
# None of the windows conflict with each other.
2946+
self.assertFalse(osut.overlapping(w1o, w2o))
2947+
self.assertFalse(osut.overlapping(w1o, w3o))
2948+
self.assertFalse(osut.overlapping(w2o, w3o))
2949+
27072950
del(model)
27082951
self.assertEqual(o.clean(), DBG)
27092952

0 commit comments

Comments
 (0)