Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions structuralcodes/geometry/_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -824,28 +824,43 @@ def __init__(
geometries, materials
)
self.point_geometries = []
# useful for representation in svg
geoms_representation = [g.polygon for g in self.geometries]
self.geom = MultiPolygon(geoms_representation)
# self.geom (svg representation) is built lazily, see the geom
# property.
self._geom = None
return
if isinstance(geometries, list):
self.geometries, self.point_geometries = _process_geometries_list(
geometries
)
# useful for representation in svg
geoms_representation = [g.polygon for g in self.geometries]
geoms_representation += [
pg.point.buffer(pg.diameter / 2)
for pg in self.point_geometries
]
self.geom = MultiPolygon(geoms_representation)
# self.geom (svg representation) is built lazily, see the geom
# property.
self._geom = None
self._reinforced_concrete = None

# we can add here static methods like
# from_dxf
# from_ascii
# ...

@property
def geom(self) -> MultiPolygon:
"""MultiPolygon used for the SVG representation, built lazily.

Point geometries are represented as buffered circles. This is only
needed for display, so it is computed on first access rather than in
__init__ -- building it eagerly buffers every point geometry on every
construction (including every rotate()), which dominated the section
calculations.
"""
if self._geom is None:
geoms_representation = [g.polygon for g in self.geometries]
geoms_representation += [
pg.point.buffer(pg.diameter / 2)
for pg in self.point_geometries
]
self._geom = MultiPolygon(geoms_representation)
return self._geom

def _repr_svg_(self) -> str:
"""Returns the svg representation."""
return str(self.geom._repr_svg_())
Expand Down
36 changes: 36 additions & 0 deletions tests/test_geometry/test_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,42 @@ def test_compound_geometry():
)


def test_compound_geometry_lazy_geom():
"""The svg ``geom`` MultiPolygon is built lazily and cached.

``geom`` is only needed for the SVG representation, so it is computed on
first access rather than in ``__init__``. It must still contain one polygon
per surface geometry plus a buffered circle per point geometry, and a
transformed copy must recompute its own ``geom``.
"""
C25 = ConcreteMC2010(25)
steel = ReinforcementMC2010(fyk=450, Es=210000, ftk=450, epsuk=0.03)

poly = Polygon(((0, 0), (200, 0), (200, 400), (0, 400)))
geo = SurfaceGeometry(poly, C25)
geo = add_reinforcement_line(geo, (40, 40), (160, 40), 20, steel, n=4)
assert len(geo.geometries) == 1
assert len(geo.point_geometries) == 4

# geom is not built until first access
assert geo._geom is None

# one polygon per surface geometry + one buffered circle per point geometry
n_expected = len(geo.geometries) + len(geo.point_geometries)
assert isinstance(geo.geom, MultiPolygon)
assert len(geo.geom.geoms) == n_expected

# the result is cached: repeated access returns the same object
assert geo._geom is not None
assert geo.geom is geo.geom

# a transformed copy starts unbuilt and recomputes its own geom
geo_r = geo.rotate(np.pi / 2)
assert geo_r._geom is None
assert isinstance(geo_r.geom, MultiPolygon)
assert len(geo_r.geom.geoms) == n_expected


def test_add_geometries():
"""Test addition for different geometries."""
polys = []
Expand Down
Loading