Skip to content

Commit 204be8c

Browse files
Merge pull request #241 from robbievanleeuwen/align_to_points
Geometry.align_center() -> Ability to align geometries to a point
2 parents 359cc43 + 6d3ffc9 commit 204be8c

File tree

3 files changed

+66
-14
lines changed

3 files changed

+66
-14
lines changed

sectionproperties/pre/geometry.py

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22
from ntpath import join
3-
from typing import List, Optional, Union, Tuple, Any
3+
from typing import List, Optional, Union, Tuple, List, Any
44

55
import copy
66
import math
@@ -479,31 +479,47 @@ def align_to(
479479

480480
return new_geom
481481

482-
def align_center(self, align_to: Optional[Geometry] = None):
482+
def align_center(
483+
self, align_to: Optional[Union[Geometry, Tuple[float, float]]] = None
484+
):
483485
"""
484486
Returns a new Geometry object, translated in both x and y, so that the
485-
center-point of the new object's centroid will be aligned with
486-
centroid of the object in 'align_to'. If 'align_to' is None then the new
487-
object will be aligned with it's centroid at the origin.
487+
the new object's centroid will be aligned with the centroid of the object
488+
in 'align_to'. If 'align_to' is an x, y coordinate, then the centroid will
489+
be aligned to the coordinate. If 'align_to' is None then the new
490+
object will be aligned with its centroid at the origin.
488491
489492
:param align_to: Another Geometry to align to or None (default is None)
490-
:type align_to: Optional[:class:`~sectionproperties.pre.geometry.Geometry`]
493+
:type align_to: Optional[Union[:class:`~sectionproperties.pre.geometry.Geometry`, Tuple[float, float]]]
491494
492495
:return: Geometry object translated to new alignment
493496
:rtype: :class:`~sectionproperties.pre.geometry.Geometry`
494497
"""
495498
cx, cy = list(self.geom.centroid.coords)[0]
496-
# Suggested by Agent 6-6-6: Hard-rounding of cx and cy allows
499+
# Suggested by @Agent6-6-6: Hard-rounding of cx and cy allows
497500
# for greater precision in placing geometry with its centroid
498501
# near [0, 0]. True [0, 0] placement will not be possible due
499502
# to floating point errors.
500503
if align_to is None:
501504
shift_x, shift_y = round(-cx, self.tol), round(-cy, self.tol)
502505

503-
else:
506+
elif isinstance(align_to, Geometry):
504507
align_cx, align_cy = list(align_to.geom.centroid.coords)[0]
505508
shift_x = round(align_cx - cx, self.tol)
506509
shift_y = round(align_cy - cy, self.tol)
510+
511+
else:
512+
try:
513+
point_x, point_y = align_to
514+
shift_x = round(point_x - cx, self.tol)
515+
shift_y = round(point_y - cy, self.tol)
516+
except (
517+
TypeError,
518+
ValueError,
519+
): # align_to not subscriptable, incorrect length, etc.
520+
raise ValueError(
521+
f"align_to must be either a Geometry object or an x, y coordinate, not {align_to}."
522+
)
507523
new_geom = self.shift_section(x_offset=shift_x, y_offset=shift_y)
508524
return new_geom
509525

@@ -1555,20 +1571,23 @@ def mirror_section(
15551571
new_geom = CompoundGeometry(geoms_acc)
15561572
return new_geom
15571573

1558-
def align_center(self, align_to: Optional[Geometry] = None):
1574+
def align_center(
1575+
self, align_to: Optional[Union[Geometry, Tuple[float, float]]] = None
1576+
):
15591577
"""
15601578
Returns a new CompoundGeometry object, translated in both x and y, so that the
15611579
center-point of the new object's material-weighted centroid will be aligned with
1562-
centroid of the object in 'align_to'. If 'align_to' is None then the new
1563-
object will be aligned with it's centroid at the origin.
1580+
centroid of the object in 'align_to'. If 'align_to' is an x, y coordinate, then
1581+
the centroid will be aligned to the coordinate. If 'align_to' is None then the new
1582+
object will be aligned with its centroid at the origin.
15641583
15651584
Note: The material-weighted centroid refers to when individual geometries within
15661585
the CompoundGeometry object have been assigned differing materials. The centroid
15671586
of the compound geometry is calculated by using the E modulus of each
15681587
geometry's assigned material.
15691588
1570-
:param align_to: Another Geometry to align to or None (default is None)
1571-
:type align_to: Optional[:class:`~sectionproperties.pre.geometry.Geometry`]
1589+
:param align_to: Another Geometry to align to, an xy coordinate, or None (default is None)
1590+
:type align_to: Optional[Union[:class:`~sectionproperties.pre.geometry.Geometry`, Tuple[float, float]]]
15721591
15731592
:return: Geometry object translated to new alignment
15741593
:rtype: :class:`~sectionproperties.pre.geometry.Geometry`
@@ -1597,10 +1616,21 @@ def align_center(self, align_to: Optional[Geometry] = None):
15971616
round(-weighted_cy, self.tol),
15981617
)
15991618

1600-
else:
1619+
elif isinstance(Geometry):
16011620
align_cx, align_cy = list(align_to.geom.centroid.coords)[0]
16021621
shift_x = round(align_cx - weighted_cx, self.tol)
16031622
shift_y = round(align_cy - weighted_cy, self.tol)
1623+
1624+
else:
1625+
try:
1626+
point_x, point_y = align_to
1627+
shift_x = round(point_x - weighted_cx, self.tol)
1628+
shift_y = round(point_y - weighted_cy, self.tol)
1629+
except (TypeError, ValueError):
1630+
raise ValueError(
1631+
f"align_to must be either a Geometry object or an x, y coordinate, not {align_to}."
1632+
)
1633+
16041634
new_geom = self.shift_section(x_offset=shift_x, y_offset=shift_y)
16051635
return new_geom
16061636

sectionproperties/tests/test_sections.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,3 +532,24 @@ def test_warping_disjoint_warning():
532532
sec.calculate_geometric_properties()
533533
with pytest.warns(UserWarning):
534534
sec.calculate_warping_properties()
535+
536+
537+
def test_align_center():
538+
rect = rectangular_section(d=200, b=70)
539+
circ = circular_section(d=200, n=30)
540+
rect = rect.rotate_section(-45, rot_point=[0, 0])
541+
rect_point = rect.points[1]
542+
circ = circ.align_center(rect_point)
543+
circ_x, circ_y = circ.calculate_centroid()
544+
assert pytest.approx(circ_x) == 49.497474683057995
545+
assert pytest.approx(circ_y) == -49.49747468305799
546+
547+
circ = circ.align_center()
548+
circ_x, circ_y = circ.calculate_centroid()
549+
assert pytest.approx(circ_x) == 0
550+
assert pytest.approx(circ_y) == 0
551+
552+
circ = circ.align_center(rect)
553+
circ_x, circ_y = circ.calculate_centroid()
554+
assert pytest.approx(circ_x) == 95.45941546018399
555+
assert pytest.approx(circ_y) == 45.961940777125974

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ rhino =
5151

5252
test =
5353
rhino-shapley-interop>=0.0.4
54+
pytest
5455
pytest_check

0 commit comments

Comments
 (0)