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
80 changes: 55 additions & 25 deletions src/papermodels/datatypes/element.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from copy import deepcopy
from dataclasses import dataclass
from typing import Optional, Union, NamedTuple
import load_distribution as ld
import numpy as np
import numpy.typing as npt
from shapely import Point, LineString, Polygon, GeometryCollection
Expand All @@ -11,7 +12,6 @@
tag_parsed_annotations,
)
from ..geometry import geom_ops
import load_distribution as ld
import parse
import math
import tomli_w
Expand Down Expand Up @@ -267,28 +267,16 @@ def get_collector_extents(self, relative: bool = True) -> dict[str, tuple]:
tagged_extents.update({ib.other_tag: (support_start, support_end)})
# When we have joist prototypes that have been drawn for all locations
else:
support_tags_by_geom = {
geom_ops.clean_polygon_supports([ib.other_geometry], self.geometry)[
0
]: ib.other_tag
cleaned_support_geoms_by_tag = {
ib.other_tag: geom_ops.clean_polygon_supports(
[ib.other_geometry], self.geometry
)[0]
for ib in self.intersections_below
}
support_geoms = list(support_tags_by_geom.keys())
cleaned_supports_map = {}
for idx, poly_support_geom in enumerate(support_geoms):
clean_support_geom = support_geoms[idx]
cleaned_supports_map.update({clean_support_geom: poly_support_geom})
assert all(
[self.geometry.intersects(support) for support in support_geoms]
)
try:
ordered_support_geoms = geom_ops.sort_supports(
self.geometry, support_geoms
)
except (AssertionError, ValueError):
raise geom_ops.GeometryError(
f"Element {self.tag} appears to have only one support intersection."
)
ordered_tags = self.get_ordered_support_geoms(by="tag")
ordered_support_geoms = [
cleaned_support_geoms_by_tag[tag] for tag in ordered_tags
]
try:
extents = geom_ops.get_joist_extents(
self.geometry,
Expand All @@ -297,14 +285,20 @@ def get_collector_extents(self, relative: bool = True) -> dict[str, tuple]:
extent_polygon=self.extent_polygon,
)
except (geom_ops.GeometryError, AssertionError, ValueError) as e:
raise AssertionError(
f"No intersection within joist extents: {self.tag=}"
print(
f"{GeometryCollection(ordered_support_geoms).intersection(self.geometry).wkt=}"
)
print(f"{self.geometry.wkt=}")
# raise AssertionError(
# f"No intersection within joist extents: {self.tag=}"
# )
print(f"{self.tag=}")
raise e
tagged_extents = {}
for idx, extent in enumerate(extents):
support_geom = ordered_support_geoms[idx]
support_start, support_end = geom_ops.get_start_end_nodes(support_geom)
support_tag = support_tags_by_geom[support_geom]
support_tag = ordered_tags[idx]
extent_start = extent[0].distance(support_start)
extent_end = extent[1].distance(support_start)
if relative:
Expand Down Expand Up @@ -424,6 +418,41 @@ def get_transfer_extents(self) -> tuple[str, dict]:

return intersection_extents | correspondent_extents

def get_ordered_support_geoms(
self, by: str = "geometry"
) -> list[LineString | Polygon]:
"""
Returns the support geoms of self in x-positive order

'by': {"geometry", "tag", "index"}:
when 'geometry', returns the ordered shapely geometry
when 'tag', returns the ordered tags
when 'index', returns the ordered index positions of the original list
"""
sort_keys = []
for idx, intersection_below in enumerate(self.intersections_below):
intersection_below: Intersection
geometry = intersection_below.other_geometry
tag = intersection_below.other_tag
inter_region = intersection_below.intersecting_region
if inter_region.geom_type == "Point":
sort_keys.append((inter_region, geometry, tag, idx))
elif inter_region.geom_type in ("Polygon", "LineString"):
sort_keys.append((inter_region.centroid, geometry, tag, idx))
ordered_support_tups = sorted(
sorted(sort_keys, key=lambda x: x[0].coords[0][1]),
key=lambda x: x[0].coords[0][0],
)
# ordered_support_tups = sorted(sort_keys, key=lambda x: x[0].coords[0])
if by == "geometry":
return [tup[1] for tup in ordered_support_tups]
elif by == "tag":
return [tup[2] for tup in ordered_support_tups]
elif by == "index":
return [tup[3] for tup in ordered_support_tups]
else:
return [tup[1] for tup in ordered_support_tups]


def prioritize_correspondents(
correspondents: list[Correspondent], family: str
Expand Down Expand Up @@ -701,6 +730,7 @@ def _get_transfer_loads(self, precision: int):
transfer_type = intersection_above.other_reaction_type
source_member = intersection_above.other_tag
reaction_idx = intersection_above.other_index
# print(transfer_type, source_member, reaction_idx)
if reaction_idx is None:
raise ValueError(
"The .other_index attribute within the .intersections_above list"
Expand Down Expand Up @@ -1314,7 +1344,7 @@ def trim_cantilevers(element: Element, abs_tol: Optional[float] = 0.02):
if geometry.geom_type == "LineString" and element.extent_line is None:
orig_support_geoms = [ib.other_geometry for ib in element.intersections_below]
support_geoms = geom_ops.clean_polygon_supports(orig_support_geoms, geometry)
support_geoms = geom_ops.sort_supports(geometry, support_geoms)
support_geoms = element.get_ordered_support_geoms(by="geometry")
ordered_geom = LineString(
geom_ops.order_nodes_positive(
[Point(geometry.coords[0]), Point(geometry.coords[-1])]
Expand Down
33 changes: 27 additions & 6 deletions src/papermodels/datatypes/geometry_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def __init__(
self.raw_annotations = None
self.legend_entries = None
self.pdf_path = None
self.omitted = {}
self.cantilever_abs_tol: Optional[float] = cantilever_abs_tol

@property
Expand Down Expand Up @@ -381,6 +382,7 @@ def remove_excess_correspondent_load_paths(self):
def add_intersection_indexes_below(self):
sorted_nodes = nx.topological_sort(self)
orphaned_nodes = self.orphaned_elements
self.omitted = {} # Used when generated collectors only have one support
for node in sorted_nodes:
if node in orphaned_nodes:
continue
Expand Down Expand Up @@ -445,21 +447,31 @@ def add_intersection_indexes_below(self):
start_coord, sub_dependent_intersections
)
sub_id = subelem.tag
subextents = subelem.get_collector_extents()
try:
subextents = subelem.get_collector_extents()
except geom.GeometryError:
self.omitted.update({sub_id: subelem})
continue

sub_sorted_below_ints = sorted(
sub_local_coords, key=lambda x: x[0]
)
if len(sub_sorted_below_ints) < 2:
raise ValueError(
f"It seems that this element only has one support: {node}"
)
self.omitted.update({sub_id: subelem})
continue

# raise ValueError(
# f"It seems that this element only has one support: {sub_id}"
# )
_, sub_other_tags_below = zip(*sub_sorted_below_ints)
sub_updated_intersections_below = []
for sub_intersection in subelem.intersections_below:
sub_other_tag = sub_intersection.other_tag
sub_local_index = sub_other_tags_below.index(
sub_other_tag
)
if sub_intersection.other_tag not in subextents:
continue
new_sub_intersection = Intersection(
sub_intersection.intersecting_region,
self.nodes[sub_other_tag]["element"].geometry,
Expand Down Expand Up @@ -516,6 +528,15 @@ def add_intersection_indexes_below(self):
element.intersections_below = updated_intersections_below
element.intersections_below = updated_intersections_below
self.nodes[node]["element"] = element
for omit_tag, omit_elem in self.omitted.items():
filtered_subelements = []
elem = omit_tag.split("-")[0]
for subelem in self.nodes[elem]["element"].subelements:
if subelem.tag == omit_tag:
continue
else:
filtered_subelements.append(subelem)
self.nodes[elem]["element"].subelements = filtered_subelements

def add_intersection_indexes_above(self):
sorted_nodes = nx.topological_sort(self)
Expand Down Expand Up @@ -1101,7 +1122,7 @@ def plot_elements(
dpi: int = 150,
plot_trib_areas: bool = False,
plot_extent_polygons: bool = False,
plot_tags: bool = False,
plot_subelement_tags: bool = False,
plot_elems_by_tag: Optional[list[str]] = None,
):
"""
Expand All @@ -1115,7 +1136,7 @@ def plot_elements(
dpi,
plot_trib_areas=plot_trib_areas,
plot_extent_polygons=plot_extent_polygons,
plot_tags=plot_tags,
plot_subelement_tags=plot_subelement_tags,
plot_elems_by_tag=plot_elems_by_tag,
)

Expand Down
94 changes: 28 additions & 66 deletions src/papermodels/datatypes/joist_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,9 @@ def __init__(
)
self.element = element
self.extent_polygon = element.extent_polygon
self.joist_supports = {}
self._joist_supports = {}
for ib in element.intersections_below:
tag = ib.other_tag
if (
not ib.other_geometry.intersects(self.joist_prototype)
and not self.extent_polygon
Expand All @@ -418,33 +419,17 @@ def __init__(
support = geom_ops.clean_polygon_supports(
[ib.other_geometry], self.joist_prototype, self.extent_polygon
)
self.joist_supports.update({support[0]: ib.other_reaction_type})
self._joist_supports.update({tag: (support[0], ib.other_reaction_type)})
else:
support = ib.other_geometry
self.joist_supports.update({support: ib.other_reaction_type})

# TODO: FIX THIS HACK THAT SEEMS TO ALTER THE DIRECTION OF SOME CANTILEVERS
if self.extent_polygon:
support_centroids = [geom.centroid for geom in self.joist_supports.keys()]
ordered_centroids = geom_ops.order_nodes_positive(support_centroids)
ordered_supports = []
for point in ordered_centroids:
for support in self.joist_supports.keys():
if point == support.centroid:
ordered_supports.append(support)
break
self._supports = ordered_supports
else:
try:
self._supports = geom_ops.sort_supports(
self.joist_prototype, self.joist_supports.keys()
)
except (AssertionError, ValueError):
raise geom_ops.GeometryError(
f"Element {self.element.tag} appears to have only one support intersection:\n{self.element.intersections_below}"
)

self.joist_support_tags = [ib.other_tag for ib in element.intersections_below]
self._joist_supports.update({tag: (support, ib.other_reaction_type)})
ordered_support_tags = self.element.get_ordered_support_geoms(by="tag")
self.joist_supports = {
self._joist_supports[tag][0]: self._joist_supports[tag][1]
for tag in ordered_support_tags
}
self._supports = list(self.joist_supports.keys())
self.joist_support_tags = self.element.get_ordered_support_geoms(by="tag")
self.id = element.tag
self.plane_id = element.plane_id
self.elem_kwargs = element.kwargs
Expand Down Expand Up @@ -632,33 +617,29 @@ def generate_joist_geom(self, index: int):
for support in self._supports
if support.intersects(ray_a | ray_b)
]
try:
sorted_supports = geom_ops.sort_supports(
ray_a | ray_b, intersecting_supports
)
except AssertionError:
# This condition is hit when a particular joist geometry cannot be
# supported upon the supplied supports. It is skipped and a gap in
# the joist array will be result. The solution is for the designer
# to adjust their support conditions so the joist will not be
# skipped.
warnings.warn(
f"No geometry generated on {self.element.tag} for joist index={index}. Designer to "
"review support conditions and make required adjustments."
)
return None
support_a_loc = ray_a.intersection(sorted_supports[0], grid_size=1e-3)
support_b_loc = ray_b.intersection(sorted_supports[-1], grid_size=1e-3)
support_locs = (ray_a | ray_b).intersection(
intersecting_supports, grid_size=1e-3
)
ordered_intersections = geom_ops.order_nodes_positive(support_locs)
support_a_loc, support_b_loc = (
ordered_intersections[0],
ordered_intersections[-1],
)

end_a = support_a_loc
end_b = support_b_loc
# These clauses req'd to deal with floating point error possible
# on the end joists (occurs after performing project_node)
elif index == 0:
end_a = support_a_loc = self._extents[0][0]
end_b = support_b_loc = self._extents[-1][0]
# stand-in values for so that the variable intersecting_supports exists
intersecting_supports = [0, 1]
elif index == len(self.joist_locations) - 1:
end_a = support_a_loc = self._extents[0][1]
end_b = support_b_loc = self._extents[-1][1]
# stand-in values for so that the variable intersecting_supports exists
intersecting_supports = [0, 1]

cant_a = self._cantilevers["A"]
cant_b = self._cantilevers["B"]
Expand All @@ -671,29 +652,10 @@ def generate_joist_geom(self, index: int):
support_b_loc, self.vector_parallel, self._cantilevers["B"]
)
joist_geom = set_precision(LineString([end_a, end_b]), grid_size=1e-3)
# With diagonal supports, it is possible the the new geom sliiiightly misses the supports. WTF. Why?
# This is a hack to fix that...TODO: BETTER SOLUTION FOR THIS HACK?
if not all([joist_geom.intersects(support) for support in self._supports]):
end_a = geom_ops.project_node(
end_a, -self.vector_parallel, self._cantilever_tolerance / 10
)
end_b = geom_ops.project_node(
end_b, self.vector_parallel, self._cantilever_tolerance / 10
)
joist_geom = LineString([end_a, end_b])
# TODO:
# This will superficially break one of the tests. Do I want to add this in?

# if not all([joist_geom.intersects(support) for support in self._supports]):
# print(self.element.tag)
# end_a = geom_ops.project_node(
# end_a, -self.vector_parallel, self._cantilever_tolerance / 10
# )
# end_b = geom_ops.project_node(
# end_b, self.vector_parallel, self._cantilever_tolerance / 10
# )
# joist_geom = LineString([end_a, end_b])
if joist_geom.length <= self._cantilever_tolerance:
if (
joist_geom.length <= self._cantilever_tolerance
or len(intersecting_supports) < 2
):
return None

return joist_geom
Expand Down
Loading