@@ -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