From 723037361eeaa02b2b2860e7f70a71b547c07ee9 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 17 Jan 2020 13:59:22 -0500 Subject: [PATCH] fix(facade_parameter): Get facade parameters to respond to scaling This commit fixes [this bug here](https://github.com/ladybug-tools/dragonfly-core/issues/22). --- dragonfly/room2d.py | 14 +++++++++ dragonfly/shadingparameter.py | 54 ++++++++++++++++++++++++++++++++++ dragonfly/windowparameter.py | 33 +++++++++++++++++++++ tests/room2d_test.py | 8 ++++- tests/shadingparameter_test.py | 36 +++++++++++++++++++++++ tests/windowparameter_test.py | 21 +++++++++++++ 6 files changed, 165 insertions(+), 1 deletion(-) diff --git a/dragonfly/room2d.py b/dragonfly/room2d.py index c12b675d..d162c1a8 100644 --- a/dragonfly/room2d.py +++ b/dragonfly/room2d.py @@ -647,14 +647,28 @@ def reflect(self, plane): def scale(self, factor, origin=None): """Scale this Room2D by a factor from an origin point. + Note that this will scale both the Room2D geometry and the WindowParameters + and FacadeParameters assigned to this Room2D. + Args: factor: A number representing how much the object should be scaled. origin: A ladybug_geometry Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ + # scale the Room2D geometry self._floor_geometry = self._floor_geometry.scale(factor, origin) self._floor_to_ceiling_height = self._floor_to_ceiling_height * factor + # scale the window parameters + for i, win_par in enumerate(self._window_parameters): + if win_par is not None: + self._window_parameters[i] = win_par.scale(factor) + + # scale the shading parameters + for i, shd_par in enumerate(self._shading_parameters): + if shd_par is not None: + self._shading_parameters[i] = shd_par.scale(factor) + def check_horizontal(self, tolerance, raise_exception=True): """Check whether the Room2D's floor geometry is horiztonal within a tolerance. diff --git a/dragonfly/shadingparameter.py b/dragonfly/shadingparameter.py index cf9aadbd..6ea66eb9 100644 --- a/dragonfly/shadingparameter.py +++ b/dragonfly/shadingparameter.py @@ -20,6 +20,16 @@ def add_shading_to_face(self, face): """Add Shades to a Honeybee Face using these Window Parameters.""" pass + def scale(self, factor): + """Get a scaled version of these ShadingParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return self + @classmethod def from_dict(cls, data): """Create ShadingParameterBase from a dictionary. @@ -84,6 +94,16 @@ def add_shading_to_face(self, face, tolerance): for ap in face.apertures: ap.extruded_border(self.depth) + def scale(self, factor): + """Get a scaled version of these ShadingParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return ExtrudedBorder(self.depth * factor) + @classmethod def from_dict(cls, data): """Create ExtrudedBorder from a dictionary. @@ -165,6 +185,16 @@ def add_shading_to_face(self, face, tolerance): """ face.overhang(self.depth, self.angle, False, tolerance) + def scale(self, factor): + """Get a scaled version of these ShadingParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return Overhang(self.depth * factor, self.angle) + @classmethod def from_dict(cls, data): """Create Overhang from a dictionary. @@ -359,6 +389,18 @@ def add_shading_to_face(self, face, tolerance): self.distance, self.depth, self.offset, self.angle, self.contour_vector, self.flip_start_side, False, tolerance) + def scale(self, factor): + """Get a scaled version of these ShadingParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return LouversByDistance( + self.distance * factor, self.depth * factor, self.offset * factor, + self.angle, self.contour_vector, self.flip_start_side) + @classmethod def from_dict(cls, data): """Create LouversByDistance from a dictionary. @@ -470,6 +512,18 @@ def add_shading_to_face(self, face, tolerance): self.louver_count, self.depth, self.offset, self.angle, self.contour_vector, self.flip_start_side, False, tolerance) + def scale(self, factor): + """Get a scaled version of these ShadingParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return LouversByCount( + self.louver_count, self.depth * factor, self.offset * factor, + self.angle, self.contour_vector, self.flip_start_side) + @classmethod def from_dict(cls, data): """Create LouversByCount from a dictionary. diff --git a/dragonfly/windowparameter.py b/dragonfly/windowparameter.py index 407025d0..7012dc84 100644 --- a/dragonfly/windowparameter.py +++ b/dragonfly/windowparameter.py @@ -26,6 +26,16 @@ def add_window_to_face(self, face, tolerance=0): """Add Apertures to a Honeybee Face using these Window Parameters.""" pass + def scale(self, factor): + """Get a scaled version of these WindowParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return self + @classmethod def from_dict(cls, data): """Create WindowParameterBase from a dictionary. @@ -140,6 +150,17 @@ def add_window_to_face(self, face, tolerance=0): final_names = (adj_ap_name,) + names face.apertures[0].boundary_condition = Surface(final_names, True) + def scale(self, factor): + """Get a scaled version of these WindowParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return SingleWindow( + self.width * factor, self.height * factor, self.sill_height * factor) + @classmethod def from_dict(cls, data): """Create SingleWindow from a dictionary. @@ -359,6 +380,18 @@ def add_window_to_face(self, face, tolerance=0): final_names = (adj_ap_name,) + names ap.boundary_condition = Surface(final_names, True) + def scale(self, factor): + """Get a scaled version of these WindowParameters. + + This method is called within the scale methods of the Room2D. + + Args: + factor: A number representing how much the object should be scaled. + """ + return RepeatingWindowRatio( + self.window_ratio, self.window_height * factor, self.sill_height * factor, + self.horizontal_separation * factor, self.vertical_separation * factor) + @classmethod def from_dict(cls, data): """Create RepeatingWindowRatio from a dictionary. diff --git a/tests/room2d_test.py b/tests/room2d_test.py index 6b9252f8..b5a1fd59 100644 --- a/tests/room2d_test.py +++ b/tests/room2d_test.py @@ -2,7 +2,7 @@ import pytest from dragonfly.room2d import Room2D -from dragonfly.windowparameter import SimpleWindowRatio +from dragonfly.windowparameter import SimpleWindowRatio, SingleWindow from dragonfly.shadingparameter import Overhang from honeybee.boundarycondition import Outdoors, Ground, Surface @@ -275,6 +275,8 @@ def test_scale(): pts = (Point3D(1, 1, 2), Point3D(2, 1, 2), Point3D(2, 2, 2), Point3D(1, 2, 2)) plane_1 = Plane(Vector3D(0, 0, 1), Point3D(0, 0, 0)) room = Room2D('Square Shoebox', Face3D(pts, plane_1), 3) + room.set_outdoor_window_parameters(SingleWindow(1, 1, 1)) + room.set_outdoor_shading_parameters(Overhang(1)) new_r = room.duplicate() new_r.scale(2) @@ -284,6 +286,10 @@ def test_scale(): assert new_r.floor_geometry[3] == Point3D(2, 4, 4) assert new_r.floor_area == room.floor_area * 2 ** 2 assert new_r.volume == room.volume * 2 ** 3 + assert new_r.window_parameters[0].width == 2 + assert new_r.window_parameters[0].height == 2 + assert new_r.window_parameters[0].sill_height == 2 + assert new_r.shading_parameters[0].depth == 2 def test_rotate_xy(): diff --git a/tests/shadingparameter_test.py b/tests/shadingparameter_test.py index 77ba1ee8..f009b344 100644 --- a/tests/shadingparameter_test.py +++ b/tests/shadingparameter_test.py @@ -32,6 +32,14 @@ def test_extruded_border_equality(): assert simple_border != simple_border_alt +def test_extruded_border_scale(): + """Test the scale method.""" + simple_border = ExtrudedBorder(0.3) + + new_simple_border = simple_border.scale(2) + assert new_simple_border.depth == 0.6 + + def test_extruded_border_dict_methods(): """Test the to/from dict methods.""" simple_border = ExtrudedBorder(0.3) @@ -83,6 +91,15 @@ def test_overhang_equality(): assert simple_awning != simple_awning_alt +def test_overhang_scale(): + """Test the scale method.""" + simple_awning = Overhang(2, 10) + + new_simple_awning = simple_awning.scale(2) + assert new_simple_awning.depth == 4 + assert new_simple_awning.angle == 10 + + def test_overhang_dict_methods(): """Test the to/from dict methods.""" simple_awning = Overhang(2, 10) @@ -132,6 +149,16 @@ def test_louvers_by_distance_equality(): assert louvers != louvers_alt +def test_louvers_by_distance_scale(): + """Test the scale method.""" + louvers = LouversByDistance(0.5, 0.3, 1, 30) + + new_louvers = louvers.scale(2) + assert new_louvers.distance == 1 + assert new_louvers.depth == 0.6 + assert new_louvers.offset == 2 + + def test_louvers_by_distance_dict_methods(): """Test the to/from dict methods.""" louvers = LouversByDistance(0.5, 0.3, 1, 30) @@ -181,6 +208,15 @@ def test_louvers_by_count_equality(): assert louvers != louvers_alt +def test_louvers_by_distance_scale(): + """Test the scale method.""" + louvers = LouversByCount(3, 0.3, 1, 30) + + new_louvers = louvers.scale(2) + assert new_louvers.depth == 0.6 + assert new_louvers.offset == 2 + + def test_louvers_by_count_dict_methods(): """Test the to/from dict methods.""" louvers = LouversByCount(3, 0.3, 1, 30) diff --git a/tests/windowparameter_test.py b/tests/windowparameter_test.py index a921a08f..2a76c43b 100644 --- a/tests/windowparameter_test.py +++ b/tests/windowparameter_test.py @@ -43,6 +43,16 @@ def test_single_window_dict_methods(): assert glz_dict == new_simple_window.to_dict() +def test_single_window_scale(): + """Test the scale method.""" + simple_window = SingleWindow(5, 2, 0.8) + + new_simple_window = simple_window.scale(2) + assert new_simple_window.width == 10 + assert new_simple_window.height == 4 + assert new_simple_window.sill_height == 1.6 + + def test_single_window_add_window_to_face(): """Test the add_window_to_face method.""" simple_window = SingleWindow(5, 2, 0.8) @@ -127,6 +137,17 @@ def test_repeating_window_ratio_equality(): assert ashrae_base != ashrae_base_alt +def test_repeating_window_scale(): + """Test the scale method.""" + ashrae_base = RepeatingWindowRatio(0.4, 2, 0.8, 3) + + new_ashrae_base = ashrae_base.scale(2) + assert new_ashrae_base.window_ratio == ashrae_base.window_ratio + assert new_ashrae_base.window_height == 4 + assert new_ashrae_base.sill_height == 1.6 + assert new_ashrae_base.horizontal_separation == 6 + + def test_repeating_window_ratio_dict_methods(): """Test the to/from dict methods.""" ashrae_base = RepeatingWindowRatio(0.4, 2, 0.8, 3)