From 5ae390420f305a2ef2440307fe5deaf6f2d2d223 Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Sun, 21 Aug 2022 14:58:00 -0700 Subject: [PATCH 1/4] Feat: Added ability for Geometry.align_center() and CompoundGeometry.align_center() to align to a x, y coordinate; Tests: Added tests for Geometry.align_center() --- sectionproperties/pre/geometry.py | 38 ++++++++++++++++++++---- sectionproperties/tests/test_sections.py | 21 +++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/sectionproperties/pre/geometry.py b/sectionproperties/pre/geometry.py index 99318241..a19f7e3c 100644 --- a/sectionproperties/pre/geometry.py +++ b/sectionproperties/pre/geometry.py @@ -493,17 +493,30 @@ def align_center(self, align_to: Optional[Geometry] = None): :rtype: :class:`~sectionproperties.pre.geometry.Geometry` """ cx, cy = list(self.geom.centroid.coords)[0] - # Suggested by Agent 6-6-6: Hard-rounding of cx and cy allows + # Suggested by @Agent6-6-6: Hard-rounding of cx and cy allows # for greater precision in placing geometry with its centroid # near [0, 0]. True [0, 0] placement will not be possible due # to floating point errors. if align_to is None: shift_x, shift_y = round(-cx, self.tol), round(-cy, self.tol) - else: + elif isinstance(align_to, Geometry): align_cx, align_cy = list(align_to.geom.centroid.coords)[0] shift_x = round(align_cx - cx, self.tol) shift_y = round(align_cy - cy, self.tol) + + else: + try: + point_x, point_y = align_to + shift_x = round(point_x - cx, self.tol) + shift_y = round(point_y - cy, self.tol) + except ( + TypeError, + ValueError, + ): # align_to not subscriptable, incorrect length, etc. + raise ValueError( + f"align_to must be either a Geometry object or an x, y coordinate, not {align_to}." + ) new_geom = self.shift_section(x_offset=shift_x, y_offset=shift_y) return new_geom @@ -1555,7 +1568,9 @@ def mirror_section( new_geom = CompoundGeometry(geoms_acc) return new_geom - def align_center(self, align_to: Optional[Geometry] = None): + def align_center( + self, align_to: Optional[Union[Geometry, List[float, float]]] = None + ): """ Returns a new CompoundGeometry object, translated in both x and y, so that the center-point of the new object's material-weighted centroid will be aligned with @@ -1567,8 +1582,8 @@ def align_center(self, align_to: Optional[Geometry] = None): of the compound geometry is calculated by using the E modulus of each geometry's assigned material. - :param align_to: Another Geometry to align to or None (default is None) - :type align_to: Optional[:class:`~sectionproperties.pre.geometry.Geometry`] + :param align_to: Another Geometry to align to, an xy coordinate, or None (default is None) + :type align_to: Optional[:class:`~sectionproperties.pre.geometry.Geometry`, List[float, float]] :return: Geometry object translated to new alignment :rtype: :class:`~sectionproperties.pre.geometry.Geometry` @@ -1597,10 +1612,21 @@ def align_center(self, align_to: Optional[Geometry] = None): round(-weighted_cy, self.tol), ) - else: + elif isinstance(Geometry): align_cx, align_cy = list(align_to.geom.centroid.coords)[0] shift_x = round(align_cx - weighted_cx, self.tol) shift_y = round(align_cy - weighted_cy, self.tol) + + else: + try: + point_x, point_y = align_to + shift_x = round(point_x - weighted_cx, self.tol) + shift_y = round(point_y - weighted_cy, self.tol) + except (TypeError, ValueError): + raise ValueError( + f"align_to must be either a Geometry object or an x, y coordinate, not {align_to}." + ) + new_geom = self.shift_section(x_offset=shift_x, y_offset=shift_y) return new_geom diff --git a/sectionproperties/tests/test_sections.py b/sectionproperties/tests/test_sections.py index 02ad843d..a61484d3 100644 --- a/sectionproperties/tests/test_sections.py +++ b/sectionproperties/tests/test_sections.py @@ -532,3 +532,24 @@ def test_warping_disjoint_warning(): sec.calculate_geometric_properties() with pytest.warns(UserWarning): sec.calculate_warping_properties() + + +def test_align_center(): + rect = rectangular_section(d=200, b=70) + circ = circular_section(d=200, n=30) + rect = rect.rotate_section(-45, rot_point=[0, 0]) + rect_point = rect.points[1] + circ = circ.align_center(rect_point) + circ_x, circ_y = circ.calculate_centroid() + assert pytest.approx(circ_x) == 49.497474683057995 + assert pytest.approx(circ_y) == -49.49747468305799 + + circ = circ.align_center() + circ_x, circ_y = circ.calculate_centroid() + assert pytest.approx(circ_x) == 0 + assert pytest.approx(circ_y) == 0 + + circ = circ.align_center(rect) + circ_x, circ_y = circ.calculate_centroid() + assert pytest.approx(circ_x) == 95.45941546018399 + assert pytest.approx(circ_y) == 45.961940777125974 From 456d3f38ca287940114a3bbc8510293cc501c753 Mon Sep 17 00:00:00 2001 From: robbievanleeuwen Date: Tue, 23 Aug 2022 10:27:09 +1000 Subject: [PATCH 2/4] Add pytest dependency to test package --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 202f108f..38440504 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,4 +51,5 @@ rhino = test = rhino-shapley-interop>=0.0.4 + pytest pytest_check From 022ec50275a2754911225e4ab4c0a40b978f5a2b Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Mon, 22 Aug 2022 22:02:06 -0700 Subject: [PATCH 3/4] Updated doc strings for both Geometry.align_center and CompoundGeometry.align_center --- sectionproperties/pre/geometry.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sectionproperties/pre/geometry.py b/sectionproperties/pre/geometry.py index a19f7e3c..781218c2 100644 --- a/sectionproperties/pre/geometry.py +++ b/sectionproperties/pre/geometry.py @@ -1,6 +1,6 @@ from __future__ import annotations from ntpath import join -from typing import List, Optional, Union, Tuple, Any +from typing import List, Optional, Union, Tuple, List, Any import copy import math @@ -479,15 +479,16 @@ def align_to( return new_geom - def align_center(self, align_to: Optional[Geometry] = None): + def align_center(self, align_to: Optional[Union[Geometry, Tuple[float, float]]] = None): """ Returns a new Geometry object, translated in both x and y, so that the - center-point of the new object's centroid will be aligned with - centroid of the object in 'align_to'. If 'align_to' is None then the new - object will be aligned with it's centroid at the origin. + the new object's centroid will be aligned with the centroid of the object + in 'align_to'. If 'align_to' is an x, y coordinate, then the centroid will + be aligned to the coordinate. If 'align_to' is None then the new + object will be aligned with its centroid at the origin. :param align_to: Another Geometry to align to or None (default is None) - :type align_to: Optional[:class:`~sectionproperties.pre.geometry.Geometry`] + :type align_to: Optional[Union[:class:`~sectionproperties.pre.geometry.Geometry`, Tuple[float, float]]] :return: Geometry object translated to new alignment :rtype: :class:`~sectionproperties.pre.geometry.Geometry` @@ -1569,13 +1570,14 @@ def mirror_section( return new_geom def align_center( - self, align_to: Optional[Union[Geometry, List[float, float]]] = None + self, align_to: Optional[Union[Geometry, Tuple[float, float]]] = None ): """ Returns a new CompoundGeometry object, translated in both x and y, so that the center-point of the new object's material-weighted centroid will be aligned with - centroid of the object in 'align_to'. If 'align_to' is None then the new - object will be aligned with it's centroid at the origin. + centroid of the object in 'align_to'. If 'align_to' is an x, y coordinate, then + the centroid will be aligned to the coordinate. If 'align_to' is None then the new + object will be aligned with its centroid at the origin. Note: The material-weighted centroid refers to when individual geometries within the CompoundGeometry object have been assigned differing materials. The centroid @@ -1583,7 +1585,7 @@ def align_center( geometry's assigned material. :param align_to: Another Geometry to align to, an xy coordinate, or None (default is None) - :type align_to: Optional[:class:`~sectionproperties.pre.geometry.Geometry`, List[float, float]] + :type align_to: Optional[Union[:class:`~sectionproperties.pre.geometry.Geometry`, Tuple[float, float]]] :return: Geometry object translated to new alignment :rtype: :class:`~sectionproperties.pre.geometry.Geometry` From 6d3ffc97bb3bdbf3db7f4753bb9bbb67f874a0c3 Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Mon, 22 Aug 2022 22:03:26 -0700 Subject: [PATCH 4/4] Blackify --- sectionproperties/pre/geometry.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sectionproperties/pre/geometry.py b/sectionproperties/pre/geometry.py index 781218c2..731a1c9d 100644 --- a/sectionproperties/pre/geometry.py +++ b/sectionproperties/pre/geometry.py @@ -479,7 +479,9 @@ def align_to( return new_geom - def align_center(self, align_to: Optional[Union[Geometry, Tuple[float, float]]] = None): + def align_center( + self, align_to: Optional[Union[Geometry, Tuple[float, float]]] = None + ): """ Returns a new Geometry object, translated in both x and y, so that the the new object's centroid will be aligned with the centroid of the object @@ -1575,7 +1577,7 @@ def align_center( """ Returns a new CompoundGeometry object, translated in both x and y, so that the center-point of the new object's material-weighted centroid will be aligned with - centroid of the object in 'align_to'. If 'align_to' is an x, y coordinate, then + centroid of the object in 'align_to'. If 'align_to' is an x, y coordinate, then the centroid will be aligned to the coordinate. If 'align_to' is None then the new object will be aligned with its centroid at the origin.