diff --git a/src/papermodels/datatypes/element.py b/src/papermodels/datatypes/element.py index e426be8..ad6b4a5 100644 --- a/src/papermodels/datatypes/element.py +++ b/src/papermodels/datatypes/element.py @@ -284,16 +284,13 @@ def get_collector_extents(self, relative: bool = True) -> dict[str, tuple]: self.trib_area, extent_polygon=self.extent_polygon, ) - except (geom_ops.GeometryError, AssertionError, ValueError) as e: - print( + except (geom_ops.GeometryError, AssertionError, ValueError, TypeError): + support_intersections = ( 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 + geometry = (f"{self.geometry.wkt=}") + tag = (f"{self.tag=}") + raise geom_ops.GeometryError(f"Debug information:{tag=}\n{geometry=}\n{support_intersections=}") tagged_extents = {} for idx, extent in enumerate(extents): support_geom = ordered_support_geoms[idx] @@ -730,7 +727,6 @@ 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" @@ -1374,7 +1370,12 @@ def trim_cantilevers(element: Element, abs_tol: Optional[float] = 0.02): start_point = cantilevers["A_intersection"] if (cantilevers["B"] == 0.0) and (cantilevers["B"] != cantilevers["B_orig"]): end_point = cantilevers["B_intersection"] - new_geometry = LineString([start_point, end_point]) # type: ignore + try: + new_geometry = LineString([start_point, end_point]) # type: ignore + except TypeError: + raise geom_ops.GeometryError( + f"{element=}" + ) new_element.geometry = new_geometry intersection_checks = [ new_geometry.intersects(ib.other_geometry) diff --git a/src/papermodels/datatypes/geometry_graph.py b/src/papermodels/datatypes/geometry_graph.py index 30110ee..04183ad 100644 --- a/src/papermodels/datatypes/geometry_graph.py +++ b/src/papermodels/datatypes/geometry_graph.py @@ -261,6 +261,11 @@ def remove_excess_correspondent_load_paths(self): frame element transferring load to the supporting frame element. The correct load path should be |FB0.1 -> FB0.2 -> column| instead of |FB0.1 -> column| with |FB0.1 -> FB0.2 -> column| also. + 4. A Polygon node, with "linear" reaction type, that is intersecting with LineString + elements that run perpendicular to it. This can occur if a beam is drawn to + transfer out a wall from above but that same beam has other beams framing into + it perpendicular. We do not want the wall to transfer out to these other beams + at the intersection points. Modifications to the implementation of this function can adjust how load paths are conceptually created. For example, to implement baloon framing, the second rule @@ -381,10 +386,29 @@ def remove_excess_correspondent_load_paths(self): if ( element.geometry.geom_type == "LineString" and intersection_points_in_polygon_below + ): - for edge in intersection_points_in_polygon_below: + inters_below_set = set(intersection_points_in_polygon_below) + for edge in inters_below_set: self.remove_edge(*edge) + # Rule 4 + if ( + element.geometry.geom_type == "Polygon" + and element.reaction_type == "linear" + and "intersection" in edge_properties + ): + center_line = geom.get_rectangle_centerline( + element.geometry + ) + for idx, dep in enumerate(dependents): + dep_geom = self.nodes[dep]["element"].geometry + if dep_geom.geom_type == "LineString": + is_roughly_parallel = geom.check_2d_linestring_parallel(center_line, dep_geom, tol=0.01) + if not is_roughly_parallel: + self.remove_edge(element.tag, dep) + + def add_intersection_indexes_below(self): sorted_nodes = nx.topological_sort(self) orphaned_nodes = self.orphaned_elements diff --git a/src/papermodels/datatypes/joist_models.py b/src/papermodels/datatypes/joist_models.py index 7954650..7c1dedc 100644 --- a/src/papermodels/datatypes/joist_models.py +++ b/src/papermodels/datatypes/joist_models.py @@ -621,6 +621,11 @@ def generate_joist_geom(self, index: int): intersecting_supports, grid_size=1e-3 ) ordered_intersections = geom_ops.order_nodes_positive(support_locs) + if len(ordered_intersections) < 2: + return None + raise geom_ops.GeometryError( + f"Joist prototype {self.element.tag} is not intersecting correctly." + ) support_a_loc, support_b_loc = ( ordered_intersections[0], ordered_intersections[-1], diff --git a/src/papermodels/paper/annotations.py b/src/papermodels/paper/annotations.py index eadfb2a..e6b1d0a 100644 --- a/src/papermodels/paper/annotations.py +++ b/src/papermodels/paper/annotations.py @@ -103,6 +103,8 @@ def parse_annotations( annot_attrs["geometry"] = annot_geom annot_attrs["page_label"] = annot.page annot_attrs["tag"] = existing_annot_tag + if annot_geom is None: + raise ValueError(f"{annot=}") for annot_key, annot_attr in annot_attributes.items(): annot_attrs[annot_key] = str_to_int( annot_attr.split("<")[0] diff --git a/src/papermodels/paper/pdf.py b/src/papermodels/paper/pdf.py index 8f90225..2fb8de1 100644 --- a/src/papermodels/paper/pdf.py +++ b/src/papermodels/paper/pdf.py @@ -144,8 +144,25 @@ def pike_annotation_to_pm_annotation( annot_type = str(annot["/Subj"]) else: return None - if annot_type.lower() in ("polygon", "polyline", "circle", "ellipse"): + if annot_type.lower() in ("polygon", "polyline"): vertices = tuple(annot.get("/Vertices", tuple())) + elif annot_type.lower() in ("circle", "ellipse"): + annot_type = "Polygon" + rect = tuple(annot.get("/Rect", tuple())) + x1, y1, x2, y2 = rect + h = (x1 + x2) / 2 + k = (y1 + y2) / 2 + a = (x2 - x1) / 2 + b = (y2 - y1) / 2 + n = 16 + x_vertices = float(h) + float(a) * np.cos(np.linspace(0, 2*np.pi, n)) + y_vertices = float(k) + float(b) * np.sin(np.linspace(0, 2*np.pi, n)) + vertices = [] + for idx, x_vertex in enumerate(x_vertices): + vertices.append(Decimal(x_vertex)) + y_vertex = y_vertices[idx] + vertices.append(Decimal(y_vertex)) + elif annot_type.lower() in ( "rectangle", "square", @@ -243,21 +260,29 @@ def parse_content_stream(stream: str) -> dict[str, list]: operators will only have one entry). """ commands = {} - operand_with_operator = re.compile(r"[0-9.\s]+[a-zA-Z]+") - operators = re.compile(f"[a-zA-Z]+") - operands = re.compile(r"[\d.]+") + operand_with_operator = re.compile(r"([\d+\s*|\d+\.\d+\s*]*)([A-Za-z]{1,2})\s*") + operators_pattern = re.compile(r"[a-zA-Z]+") + operands_pattern = re.compile(r"[\d.]+") + floats_pattern = re.compile(r"^\d+\.\d+$") + integers_pattern = re.compile(r"^\d+$") matches = operand_with_operator.findall(stream) for match in matches: - operator = operators.findall(match)[0] - operand = operands.findall(match) + # operator = operators.findall(match)[0] + # operand = operands.findall(match) + operands, operator = match numerical_operands = [] - for element in operand: - if "." in element: + operands_matches = operands_pattern.findall(operands) + for element in operands_matches: + element = element.strip() + float_match = floats_pattern.search(element) + integer_match = integers_pattern.search(element) + if float_match is not None: elem = Decimal(element) - else: + numerical_operands.append(elem) + elif integer_match is not None: elem = int(element) - numerical_operands.append(elem) + numerical_operands.append(elem) commands.update({operator: numerical_operands}) return commands