Data types#
papermodels.datatypes.geometry_graph#
The GeometryGraph: a directed load-path graph built from marked-up drawings.
Nodes are Element tags and edges point
from a supported element to the element that supports it, so load flows in
edge direction from the collectors (rank 0) down to the foundations.
- papermodels.datatypes.geometry_graph.TRANSFER_LINES_CANNOT_INTERSECT_WITH_LINEAR_POLYGONS(e, inter)[source]#
Intersection rule: returns False when a transfer (rank > 0) line meets a polygon with a “linear” reaction type, in either direction. Used as a predicate to drop intersections that should not form load paths.
- Parameters:
e (Element) – The Element being checked.
inter (Intersection) – An Intersection on ‘e’.
- class papermodels.datatypes.geometry_graph.GeometryGraph(process_gravity_frame=True, cantilever_abs_tol=0.2, suppress_warnings=False)[source]#
Bases:
DiGraphA class to represent a connected geometry system in a graph. Inherits from networkx.DiGraph and adds a .node_hash attribute for storing a hash of all the nodes.
Can be used to generate a GeometryGraph.
The node_hash is how changes to the graph nodes can be tracked.
- Parameters:
- property collector_elements#
the elements that collect load directly from area loads.
- Type:
Tags of rank-0 elements with no predecessors
- property transfer_elements#
Tags of elements that receive load from at least one other element.
- property orphaned_elements#
lines with fewer than two supports and polygons with no supports.
- Type:
Tags of elements that are not adequately supported
- property contiguous_elements#
Tags of all elements that are not orphaned.
- classmethod from_elements(elements, process_gravity_frame=True, cantilever_abs_tol=0.2, intersection_rules=[<function TRANSFER_LINES_CANNOT_INTERSECT_WITH_LINEAR_POLYGONS>], suppress_warnings=False)[source]#
Returns a GeometryGraph (networkx.DiGraph) based upon the intersections and correspondents of the ‘elements’.
- build_geometry_model()[source]#
Builds (or rebuilds) self.geometry_model from the graph’s current element geometry. It must run after the gravity-frame processing, which moves transfer-element geometry (aligning to centroids, trimming cantilevers).
The model’s node registry is seeded with every canonical intersection coordinate already stored on the elements, so crossings computed later against the model (e.g. by a joist array) reuse those exact coordinates.
- Return type:
- align_frames_to_centroids()[source]#
Aligns the ends of frame elements so that they start and end on the centroids of posts and walls (centerlines).
- remove_excess_correspondent_load_paths()[source]#
Removes edges from the graph for the following conditions:
A Polygon node that has more than one “correspondent” edge. The “correspondent”
edge leading to a node with rank 0 is prioritized. If no node with rank 0 is present, the edge leading to the node with the larger overlap ratio is prioritized. This prevents a correspondent load above from transferring to both its “end load” and to a correspondent below at the same time. The load path from above should terminate at the “end load” and the end load should transfer to whatever it intersects with.
A Polygon node, with a “point” reaction type, that has an “intersection” edge
and one or more “correspondent” edges. If an “intersection” edge is present, then the “correspondent” edges will be removed. This represents the condition of platform-framing where a post will land on the floor framing, and transfer through it to the supporting post below and the floor framing should receive a “crushing” load from the posts above and below without the posts directly transferring to each other.
A LineString node that has intersections_below with other LineStrings and the
intersecting_region (Point) is within a Polygon that is also an intersection_below. This represents the condition where a frame element connects to another frame element, fully transfering to the other frame element, at the same location where the supporting frame element is transferring to a column. This rule is intended to prevent both frame elements transferring their load to a column in addition to the supported frame element transferring load to the supporting frame element. The correct load path should be
FB0.1 -> FB0.2 -> columninstead ofFB0.1 -> columnwithFB0.1 -> FB0.2 -> columnalso.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 can be omitted.
- add_intersection_indexes_below()[source]#
Assigns each element’s supports a local reaction index and transfer extents.
Walks the graph in topological order and rewrites each element’s ‘intersections_below’ and ‘correspondents_below’ so that every support records its reaction index on this element. Orphaned line elements are skipped with a warning (unless warnings are suppressed).
- add_intersection_indexes_above()[source]#
Records, on each transfer element, which reaction of each element above it delivers load to it.
Must run after
add_intersection_indexes_below(), whose reaction indexes it reads.
- assign_collector_behaviour(element_constructor, filter_function=None, *args, **kwargs)[source]#
Returns a list of Element to be assigned to element.subelements for elements that have element_type == “collector”.
- Parameters:
element_constructor (callable) –
A callable class that will create a new Element populated with a trib_area attribute and possibly other modified attributes. The new Element will replace the existing element at that node.
The class must have a .use_subelements attribute that must be set at time of initialization so that this function can query it and determine whether the resulting elements should be created as subelements on this element.
The signature of the __call__ method of the class should be one of either:
def __call__(element: Element, [*args, **kwargs]) -> Element-ordef __call__(element: Element, [*args, **kwargs]) -> list[Element]A constructor function that returns a list[Element] should be used with as_subelements = True so that the returned list of Element will be assigned to the .subelements attribute of the Element
filter_function (callable | None) –
A callable with the following function signature:
def filter_function(element: Element) -> boolAll elements that return True from the filter_function will have the element_constructor function called on them. Any functions that return False will have no collector behaviour assigned.
You can use the papermodels.datatypes.element.create_element_filter to readily create such a filter function.
If None, then all elements will be assigned the collector behaviour.
*args – These are passed through to the ‘element_constructor’ function
**kwargs – These are passed through to the ‘element_constructor’ function
- Return type:
- classmethod from_dxf_file(dxf_filepath, legend_table, page_idx=0, scale=Decimal('1'), polygonize_layers=None, debug=False, progress=False, process_gravity_frame=True, show_skipped=False, suppress_warnings=False)[source]#
Returns a GeometryGraph built from the geometric entities (LINE, LWPOLYLINE, INSERT) contained within ‘dxf_filepath’.
- Parameters:
legend_table (dict | Path | str) – A dict (or a path to a JSON file) that maps layer names to annotation text properties
scale (Decimal) – An optional scale to be applied to the annotations. If not provided, the units of the annotations will be in PDF points where 1 point == 1 /72 inch
debug (bool) – When True, will provide verbose documentation of the annotation parsing process to assist in reviewing errors and geometry inconsistencies.
progress (bool) – When True, a progress bar will be displayed
process_gravity_frame (bool) – Processes the geometry for a gravity frame by fully resolving in-plane connectivity
show_skipped (bool) – Shows the skipped annotations that occured during pdf.load_pdf_annotations
suppress_warnings (bool) – Do not show user warnings during post-processing
page_idx (int)
- unassigned_collectors()[source]#
Returns a list of str that represents collector nodes who do not currently have collector behaviour assigned meaning that they do not have a populated .trib_area attribute.
- classmethod from_pdf_file(pdf_filepath, legend_identifier='legend', scale=None, cantilever_abs_tol=0.2, debug=False, progress=False, process_gravity_frame=True, save_tagged_pdf_file=False, tag_pdf_file_mode='append', show_skipped=False, show_unimplemented=False, suppress_warnings=False)[source]#
Returns a GeometryGraph built from that annotations in the provided PDF file at ‘pdf_filepath’.
The provided annotations are parsed into four different categories:
Legend entries - All legend entries must contain the ‘legend_identifier’
as the first piece of text in their text property. Legend entries need only appear on ONE page of the PDF document.
Structural elements - All legend entries for structural elements must have
the legend identifier, a “Type” field (e.g. “Type: <value>”), and a “Rank” field (e.g. “Rank: <integer>”)
Area load elements - All legend entries for area load elements must have
an “Occupancy” field (e.g. “Occupancy: <value>”)
Trib area elements - All legend entries for trib area elements must have
a “Type” field with a value of “trib” (e.g. “Type: trib”)
Origin elements - All origin elements (max. 1 per page) must have the
word “origin” as their text element. THE WORD “origin” CANNOT BE USED AS PART OF ANY OTHER LEGEND ENTRY (e.g. in the Type, Rank, or Occupancy fields)
- Parameters:
annotations – the list of Annotations
legend_identifier (str) – the str used in the text attribute of the PDF annotation to indicates a given geometry is part of the legend.
scale (Decimal | None) – An optional scale to be applied to the annotations. If not provided, the units of the annotations will be in PDF points where 1 point == 1 /72 inch
debug (bool) – When True, will provide verbose documentation of the annotation parsing process to assist in reviewing errors and geometry inconsistencies.
progress (bool) – When True, a progress bar will be displayed
process_gravity_frame (bool) – Processes the geometry for a gravity frame by fully resolving in-plane connectivity
show_skipped (bool) – Shows the skipped annotations that occured during pdf.load_pdf_annotations
show_unimplemented (bool) – Shows the annotations that were read but are not implemented in the parser yet.
suppress_warnings (bool) – Do not show user warnings during post-processing
cantilever_abs_tol (float | None)
save_tagged_pdf_file (bool)
tag_pdf_file_mode (str)
- classmethod from_annotations(annotations, legend_identifier='legend', scale=None, cantilever_abs_tol=0.02, debug=False, progress=False, process_gravity_frame=False, suppress_warnings=False)[source]#
Returns a GeometryGraph built from the provided annotations.
The provided annotations are parsed into four different categories:
Legend entries - All legend entries must contain the ‘legend_identifier’
as the first piece of text in their text property. Legend entries need only appear on ONE page of the PDF document.
Structural elements - All legend entries for structural elements must have
the legend identifier, a “Type” field (e.g. “Type: <value>”), and a “Rank” field (e.g. “Rank: <integer>”)
Area load elements - All legend entries for area load elements must have
an “Occupancy” field (e.g. “Occupancy: <value>”)
Trib area elements - All legend entries for trib area elements must have
a “Type” field with a value of “trib” (e.g. “Type: trib”)
Origin elements - All origin elements (max. 1 per page) must have the
word “origin” as their text element. THE WORD “origin” CANNOT BE USED AS PART OF ANY OTHER LEGEND ENTRY (e.g. in the Type, Rank, or Occupancy fields)
- Parameters:
annotations (list[Annotation]) – the list of Annotations
legend_identifier (str) – the str used in the text attribute of the PDF annotation to indicates a given geometry is part of the legend.
scale (Decimal | None) – An optional scale to be applied to the annotations. If not provided, the units of the annotations will be in PDF points where 1 point == 1 /72 inch
debug (bool) – When True, will provide verbose documentation of the annotation parsing process to assist in reviewing errors and geometry inconsistencies.
progress (bool) – When True, a progress bar will be displayed
process_gravity_frame (bool) – Processes the geometry for a gravity frame by fully resolving in-plane connectivity
suppress_warnings (bool) – Do not show user warnings when performing gravity frame post-processing
cantilever_abs_tol (float | None)
- plot_connectivity(filepath=None)[source]#
Using GraphViz, this function plots the connectivity, a representation of a load path, between members where each member is represented as a node in the graph and each connection is represented by a directional edge (arrow).
If ‘filepath’ is provided, then the resulting SVG image of the graph will be saved to disk.
If ‘filepath’ is None, then display of the SVG will be attempted through IPython.display.
If the designer is not within a Jupyter-like environment, then the SVG string will be returned.
Requires GraphViz to be independently installed. Installation instructions here: https://graphviz.org/download/
- plot_annotations(page_idx, figsize=(8, 8), dpi=150)[source]#
Plots all annotations in self.parsed_annotations that are on the ‘page_idx’
- plot_elements(plane_id, figsize=(8, 8), dpi=150, plot_trib_areas=False, plot_extent_polygons=False, plot_subelement_tags=False, plot_elems_by_tag=None)[source]#
Plots all elements in the graph that are on ‘page_idx’
- create_loaded_elements()[source]#
Returns a list of LoadedElement, each with ‘loading_areas’ applied.
# TODO: Is there a way to include trib areas? A dict of trib areas where the key is the Element.geometry # and the value is the trib area Polygon? Perhaps a way to specify a buffer value (or a left/right) value # to generate one from thg Element.geometry and some integers?
# HERE: Need to find a way to add raw load annotations to the graph so that they cann # automatically sort themselves by plane_id so that the right loads go to the right Elements
- Return type:
- export_tagged_pdf(export_path=None, mode='append')[source]#
Returns None. Generates a copy of the PDF file at self.pdf_path with the element tags added to the text field of each annotation which represents a structural element (e.g. “tag: FB0.1”)
- Parameters:
- Return type:
None
appended to the end of the existing annotation using a new line character as a separator.
- papermodels.datatypes.geometry_graph.get_local_coords(start_coord, dependent_intersections)[source]#
Returns (distance, tag) tuples giving the distance from ‘start_coord’ to each of the ‘dependent_intersections’.
- papermodels.datatypes.geometry_graph.get_dependent_intersections(element, graph_dependents)[source]#
Returns the intersections below ‘element’ whose other element is one of the tags in ‘graph_dependents’.
- Parameters:
- Return type:
- papermodels.datatypes.geometry_graph.get_dependent_correspondents(element, graph_dependents)[source]#
Returns the correspondents below ‘element’ whose other element is one of the tags in ‘graph_dependents’.
- Parameters:
- Return type:
- papermodels.datatypes.geometry_graph.correlate_extents(element_annots, extent_annots)[source]#
Returns a copy of ‘element_annots’ with value dicts that have been updated to include an ‘extent_polygon’ for any element_annots that have been drawn with an extent line or polygon.
- Parameters:
element_annots (dict[Annotation, dict])
extent_annots (dict[Annotation, dict])
- Return type:
- papermodels.datatypes.geometry_graph.correlate_containers(element_annots, container_annots, suppress_warnings=False)[source]#
Returns a copy of ‘element_annots’ in which each joist prototype (a rank-0 LineString) drawn inside a joist container polygon has that polygon stored under ‘joist_container’.
A prototype belongs to the container holding most of its length (on the same page). Warns when a container holds no prototype or more than one (a container describes one array), and when a prototype has both an extent line and a container (the container is used).
- Parameters:
element_annots (dict[Annotation, dict])
container_annots (dict[Annotation, dict])
suppress_warnings (bool)
- Return type:
papermodels.datatypes.element#
Structural elements and the routines that connect them.
An Element is a single 2D geometry (a line for beams and joists, a
polygon for walls, columns and other bearing elements) together with its
intersections on the same plane and its correspondents on adjacent planes. A
LoadedElement adds applied loads and can be exported as a structural
model (TOML or JSON) for downstream analysis.
- class papermodels.datatypes.element.Intersection(intersecting_region, other_geometry, other_tag, other_overlap=None, other_index=None, other_reaction_type='point', other_extents=None)[source]#
Bases:
NamedTupleA class to represent an intersection of geometries
- Parameters:
intersecting_region (Point | LineString)
other_geometry (LineString | Polygon)
other_tag (str)
other_overlap (LineString | Polygon | None)
other_index (int | None)
other_reaction_type (str)
other_extents (tuple | None)
- intersecting_region: Point | LineString#
Alias for field number 0
- other_geometry: LineString | Polygon#
Alias for field number 1
- other_overlap: LineString | Polygon | None#
Alias for field number 3
- class papermodels.datatypes.element.Correspondent(overlap_ratio, other_geometry, other_tag, other_rank, other_reaction_type='point', other_extents=None)[source]#
Bases:
NamedTupleA class to represent the correspondence of two Polygons
- Parameters:
- class papermodels.datatypes.element.Element(geometry, tag=None, rank=None, intersections_above=None, intersections_below=None, correspondents_above=None, correspondents_below=None, plane_id=None, element_type=None, subelements=None, trib_area=None, reaction_type='point', kwargs=None, extent_line=None, joist_container=None)[source]#
Bases:
objectA class to generically represent a connected 2D geometry within a 3D “geometry graph”.
The 2D geometry can exist independent of an interconnected 3D graph by not having any ‘intersections’ or ‘correspondents’. The existence of ‘intersections’ and/or ‘correspondents’ indicates that the 2D geometry is part of a graph.
‘intersections’ describe interconnectivity on the same 2D plane. ‘correspondents’ describe interconnectivity between adjacent 2D planes,
whether above or below.
- Parameters:
geometry (LineString | Polygon) – Union[LineString, Polygon], The geometry for the Element
tag (str | int | None) – str | int, An optional unique name or integer ID for the Element.
intersections_above/below – a dict whose keys are the ‘tag’ of an intersecting Element and the values are the Point within self.geometry where the intersection occurs. _above represents geometries that occur “above” the element in the directed geoemtry graph and while _below represents those “below”.
correspondents_above/below – a dict whose keys are the ‘tag’ of a corresponding geometry on an adjacent 2D plane and the values are the corresponding Geometry on the adjacent plane. _above represents geometries that occur “above” the element in the directed geoemtry graph and while _below represents those “below”.
plane_id (str | int | None) – Optional[str | int] = None, An optional unique identifier for the 2D plane that this Element resides on
element_type (str | None) – One of {“collector”, “transfer”} or None. Assigned within the GeometryGraph.
subelements (list[Element]) – list[Element] or None. Assigned within the GeometryGraph.
trib_area (Polygon | None) – Optional[Polygon] or None. # Not sure if adding this here is the right thing to do. Currently in use for the creation of collector subelements and for storing their trib areas.
kwargs (dict | None) – Optional[dict]. Additional kwargs that are defined on the annotation are passed-through to the Element instance and stored here.
rank (int | None)
reaction_type (str)
extent_line (LineString | None)
joist_container (Polygon | None)
- geometry: LineString | Polygon#
- extent_line: LineString | None = None#
- property extent_polygon#
The region swept by the joist prototype along its extent line (in the prototype’s own frame, so it is correct for any orientation).
- property array_region#
the joist container if one was drawn, else the extent polygon, else None.
- Type:
The region a joist array spans
- classmethod from_geometries(elem_geom, elem_tag, intersections_above=None, intersections_below=None, correspondents_above=None, correspondents_below=None, plane_id=None)[source]#
Generates an Element from provided geometries
- Parameters:
elem_geom (LineString | Polygon)
intersections_above (dict[str | int, LineString | Polygon] | None)
intersections_below (dict[str | int, LineString | Polygon] | None)
correspondents_above (dict[str | int, LineString | Polygon] | None)
correspondents_below (dict[str | int, LineString | Polygon] | None)
- classmethod from_parsed_annotations(parsed_annotations, trib_annotations=None, correspond_with_like_only=True)[source]#
Returns a list of Element generated from the annotations in ‘annots’ according to the element types described in the ‘legend’. If an annotation is not described in the legend then it will not be included in the result list of Elements.
- Parameters:
parsed_annotations (dict[Annotation, dict])
trib_annotations (dict[Annotation, dict] | None)
correspond_with_like_only (bool)
- Return type:
- property supporting_geometries#
those it intersects below on its own plane, followed by those it corresponds with on the plane below.
- Type:
The geometries of the elements that support this Element
- get_collector_extents(relative=True)[source]#
Returns a dict keyed by .tag attributes in self.intersections_below and with values representing the (start_x, end_x) locations where the collector prototype would spread over the other_geometry. The (start_x, end_x) locations refer to ordinates on other_geometry, not on the self.
If ‘relative’ == False, then the values returned are absolute Point objects.
- get_transfer_extents()[source]#
Returns a tuple of str, extents_dict
e.g. {“FB0.1”: (2.5, 6.5, 0.3, 4.3)}
Where the first two values describe the extents of the _transferred_ elemnt and the last two values describe the extents of the _transferring_ element.
For the polygon element that has a linear reaction type. This element could have more than one intersection below if it overlaps multiple beams, for example. It can also have correspondents that need to have a portion of teh linear load applied to it.
- get_ordered_support_geoms(by='geometry')[source]#
Returns the support geoms of self in x-positive order
- Parameters:
by (str) – {“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
- Return type:
- papermodels.datatypes.element.prioritize_correspondents(correspondents, family)[source]#
Filters ‘correspondents’ such that:
Only one correspondent exists in the list
That correspondent is of the same “family”
The rank is either 0 or the same as ‘rank’
The purpose of this filtering is to ensure that the top of a vertical element only corresponds with one element below it, either as a continuation of the same type of element (e.g. a column continuing down the structure) or as the point load that results from transferring out the bottom of the element to some other element (e.g. the bottom of a column transferring out to a beam).
- Parameters:
correspondents (list[Correspondent])
family (str)
- Return type:
- class papermodels.datatypes.element.LoadedElement(geometry, tag=None, rank=None, intersections_above=None, intersections_below=None, correspondents_above=None, correspondents_below=None, plane_id=None, element_type=None, subelements=None, trib_area=None, reaction_type='point', kwargs=None, extent_line=None, joist_container=None, loading_geoms=None, applied_loading_areas=None)[source]#
Bases:
ElementAn Element with loads applied to it, ready to be exported as a structural model.
- Parameters:
loading_geoms (LoadingGeometry | None) – A list of load_distribution LoadingGeometry objects (area loads with their occupancy and load components) that may apply to this Element.
applied_loading_areas (list[tuple[Polygon, Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]]] | None) – A list of (Polygon, LoadingGeometry) tuples for the loading areas that intersect this Element’s trib area. The Polygon is the intersecting region. Computed during initialization; the designer is not expected to populate this parameter.
geometry (LineString | Polygon)
rank (int | None)
element_type (str | None)
trib_area (Polygon | None)
reaction_type (str)
kwargs (dict | None)
extent_line (LineString | None)
joist_container (Polygon | None)
Typically created via
LoadedElement.from_element_with_loads()orpapermodels.datatypes.geometry_graph.GeometryGraph.create_loaded_elements().- applied_loading_areas: list[tuple[Polygon, Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]]] | None = None#
- dump_analysis_model()[source]#
Returns the structured beam data dict to go to analysis model
- Return type:
- static create_point_load(transfer_location, magnitude, transfer_source, transfer_reaction_index, direction='gravity')[source]#
Returns a dict describing a point load transferred onto this element.
- Parameters:
transfer_location (str) – The location of the load along the element.
magnitude (float) – The magnitude of the load.
transfer_source (str) – The tag of the element the load is transferred from.
transfer_reaction_index (int) – The index of the reaction on the source element that produces this load.
direction (str) – The load direction. Default “gravity”.
- static create_distributed_load(start_location, start_magnitude, end_location, end_magnitude, transfer_source, transfer_reaction_index, transfer_source_start_extent, transfer_source_end_extent, occupancy, load_components, applied_area, direction)[source]#
Returns a dict describing a linearly varying distributed load on this element.
- Parameters:
start_location (float) – The start and end of the load along the element.
end_location (float) – The start and end of the load along the element.
start_magnitude (float) – The load intensity at the start and end.
end_magnitude (float) – The load intensity at the start and end.
transfer_source (str) – The tag of the element (or loading area) the load comes from.
transfer_reaction_index (int) – The index of the reaction on the source element.
transfer_source_start_extent (float) – The extents of the load on the source element.
transfer_source_end_extent (float) – The extents of the load on the source element.
occupancy (str) – The occupancy of the originating loading area.
load_components (dict) – The load components (e.g. dead, live) of the loading area.
applied_area (float) – The loaded area that produces this load.
direction (str) – The load direction.
- dump_json(fp, precision=3)[source]#
Writes .model(precision) to the open text file ‘fp’ as indented JSON.
- classmethod from_element_with_loads(elem, loading_geoms, trib_area=None, predecessors=None, successors=None)[source]#
Returns a LoadedElement. Validates the intersections and correspondents against the supplied ‘predecessors’ and ‘successors’ from the graph. Intersections and correspondents that do not exist in the ‘predecessors’ or ‘successors’ are excluded.
- papermodels.datatypes.element.create_element_filter(page_idxs=None, tags=None, element_types=None, user_defined=None, exclude_tags=None, exclude_element_types=None, exclude_user_defined=None)[source]#
Returns a function with this signature:
func(element: Element) -> bool
The returned function returns True if the supplied element matches the criteria defined in this function.
- Parameters:
page_idxs (list[int] | None) – A list of ints. Any collector elements on those page indexes will be included.
tags (list[str] | None) – A list of joist tags to be included by the filter
element_types (list[str] | None) – A list of element types that exist in the graph. In this context, an “element_type” refers to the tag prefix and not the designation of “collector” or “transfer”. The element will return True if the element matches ANY of the element_types listed (e.g. [‘FB’, ‘SJ’, ‘CT’])
user_defined (dict[str, str] | None) – A dict of values assigned manually to an element. If ALL the dict of values provided matches the kwarg values in an element, it will be included.
exclude_tags (list[str] | None) – A list of tags that will be excluded from the filter
exclude_element_types (list[str] | None) – A list of element types which will be excluded
exclude_user_defined (dict[str, str] | None) – If ANY of the key/values in this dict match the kwarg values in an element, it will be excluded.
- Return type:
callable
- papermodels.datatypes.element.get_geometry_intersections(tagged_annotations)[source]#
Returns a dictionary of
- Parameters:
tagged_annotations (dict[Annotation, dict])
- Return type:
- papermodels.datatypes.element.check_eligible_polygon_intersection(i_tag, j_tag)[source]#
Returns True if ‘i_tag’ and ‘j_tag’ indicate the polygon elements are of the same ‘family’
- Return type:
- papermodels.datatypes.element.check_eligible_collector_extent_polygon_intersection(j_geomtype, j_reaction_type)[source]#
Returns True if the “j” annotation is eligible to receive collector reactions i.e. is not a column/post
- papermodels.datatypes.element.get_geometry_correspondents(tagged_annotations)[source]#
Returns a copy of ‘tagged_annotations’ with a ‘correspondents’ field added to that attribute’s dictionary of each Annotation key.
- Parameters:
tagged_annotations (dict[Annotation, dict])
- Return type:
- papermodels.datatypes.element.trim_cantilevers(element, abs_tol=0.02)[source]#
Mutates the geometry in node elements so that any cantilevers which are below self.cantilever_abs_tol are removed from the geometry and the geometry spans exactly from support to support.
- papermodels.datatypes.element.align_frames_to_centroids(element)[source]#
Mutates the geometry in node elements so that any cantilevers which are below self.cantilever_abs_tol are removed from the geometry and the geometry spans exactly from support to support.
- Parameters:
element (Element)
- papermodels.datatypes.element.filter_correspondents(tagged_annotations)[source]#
Applies the filter from prioritize_correspondents and back-propagates the filtered correspodents to the correspondents_above:
- Parameters:
tagged_annotations (dict[Annotation, dict])
- Return type:
- papermodels.datatypes.element.annotations_by_page(annots, ascending=False)[source]#
Returns ‘annots’ in a dictionary keyed by page number
- Parameters:
annots (dict[Annotation, dict])
- Return type:
dict[int, dict[Annotation, dict]]
- papermodels.datatypes.element.get_elements_by_page(elements)[source]#
Returns ‘elements’ sorted by page
papermodels.datatypes.joist_models#
Joist models: turn one drawn joist prototype into the joists of an array.
JoistArrayModel is the joist model; it replaces the former
CollectorTribModel (removed), whose whole-spread trib area could not
represent support gaps, variable spans or joist containers.
- class papermodels.datatypes.joist_models.JoistArrayModel(element, spacing=1, initial_offset=0.0, joist_at_start=True, joist_at_end=True, cantilever_tolerance=0.1, geometry_model=None, min_bearing=0.001, span_jump_tol=None, suppress_warnings=False)[source]#
Bases:
objectModels a spread of joists from one drawn joist prototype.
The array is laid out by the station engine in
papermodels.geometry.joist_array(see its module docstring), in one of three modes:container — the prototype lies in a
joist_containerpolygon; eachjoist is the container cut along its station line, so cantilevers and backspans can both vary.
extent — the prototype has an extent line; joists run between the
outer supports at each station with the prototype’s constant cantilevers.
plain — neither; the array runs over the station range where all of
the prototype’s supports exist, with constant cantilevers.
Joists that would land where supports are missing are moved to the nearest valid station (or dropped, with a warning); trib areas tile the array region.
Supports are the parent element’s graph supports (
intersections_below), read from the sharedGeometryModel(wall centerlines, beam lines). Every joist/support crossing is a canonical node of the model’sNodeRegistry, and the generated joists are written back into the model.- Parameters:
- use_subelements = True#
- property joist_geoms: list[LineString]#
The LineString geometry of each generated joist, in station order.
papermodels.datatypes.geometry_model#
Node canonicalization for the geometry model (Phase 1 prototype).
This module implements the two structures described in
docs/node_canonicalization_design.md:
NodeRegistry— canonical point identities. Every computed intersectionpoint is inserted via
NodeRegistry.get_or_create(), which snaps to an existing node withinnode_abs_tol(an absolute, real-world tolerance) if one exists, otherwise registers a new node. The registry uses an incremental quantized-coordinate hash (get_or_createis O(1) amortized) and probes the 9-cell neighborhood so points straddling a bucket boundary still snap. This is the “incremental now, batch later” decision (design §9): no scipy dependency; the canonical coordinate is “first point wins”.GeometryModel— the single source of truth over the geometry. It owns thegeometries (no copies), their ranks and planes, a
shapely.STRtreefor the broad phase, theNodeRegistry, and an incidence map (which geometries meet at each node). “Intersecting geometries share the same node location” becomes true by construction: each physical crossing is evaluated exactly once and its identity is shared thereafter.
Scope (Phase 1): this is a prototype that lives behind the existing API — it
is not yet wired into GeometryGraph. It governs same-plane intersections
(the node registry’s job). Cross-plane correspondence stays ratio-based in
the existing layer (design §5.5) and is intentionally not modelled here.
Walls (linear-reaction polygons) contribute their centerline to the index and node registry (design §5.4) while their polygon is retained for future correspondence use.
- class papermodels.datatypes.geometry_model.NodeRegistry(node_abs_tol=0.001)[source]#
Bases:
objectCanonical point identities under an absolute snapping tolerance.
Points inserted within
node_abs_tolof an existing node snap to it (the nearest one); otherwise a new node id is created. The canonical coordinate is that of the first point registered for the node (“first point wins”).- Parameters:
node_abs_tol (float)
- get_or_create(point)[source]#
Return the id of the canonical node within
node_abs_tolofpoint, creating a new node (atpoint) if none exists.The 9-cell neighborhood is probed: two points within
node_abs_tolcan differ by at mostnode_abs_tolin each axis, so their quantized cells differ by at most one cell in each axis. This is what catches points straddling a bucket boundary (design §9).- Return type:
- class papermodels.datatypes.geometry_model.Incidence(geom_id, rank, role)[source]#
Bases:
NamedTupleA geometry that meets at a node, tagged by its rank and its role there.
- class papermodels.datatypes.geometry_model.GeometryModel(node_abs_tol=0.001, nodes=None)[source]#
Bases:
objectSingle source of truth over the geometry (design §5.1).
Owns geometries (no copies), ranks, planes, an
STRtreebroad-phase index, aNodeRegistryof canonical node identities, and an incidence map keyed byNodeId. Same-plane crossings are computed once, at build time, and their node identities are shared by every incident geometry.- Parameters:
node_abs_tol (float)
nodes (Optional[NodeRegistry])
- noding_report: NodingReport | None#
- classmethod from_elements(elements, node_abs_tol=0.001, noding_abs_tol=None, noding_max_passes=10, suppress_warnings=False, nodes=None, seed_points=())[source]#
Build a model from
Element-like objects. Each must exposetag,geometry,rank,plane_idandreaction_type.If
noding_abs_tolis given, Phase 2 tolerance extend/trim noding runs over the index geometries before the crossing build (design §6/§7): line endpoints withinnoding_abs_tolof nearby geometry are snapped onto it so the arrangement is topologically clean when crossings are canonicalized. Wall centerlines are held fixed (valid snap targets but never moved).noding_abs_tolis separate fromnode_abs_tol(§9).nodesshares an existing registry.seed_pointsare registered before any crossing is built; because the registry is “first point wins”, crossings computed here then reuse those exact coordinates (e.g. the canonical intersection regions already stored on the elements).- Parameters:
- Return type:
- add_geometry(gid, geom, rank, plane, reaction_type='point', crossings=None, parent=None)[source]#
Write a generated geometry (e.g. a joist produced by a joist array) into the model.
crossingsmaps each canonical node the new geometry meets (already obtained fromself.nodes) to the geometry it meets there; incidence is recorded for both sides. Crossings are not recomputed: the caller constructed the geometry from those nodes.parentrecords which geometry this one was generated from (seeremove_generated()).
- remove_geometry(gid)[source]#
Remove a geometry and its incidence (nodes stay: they are identities).
- Parameters:
gid (str)
- Return type:
None
- remove_generated(parent)[source]#
Remove every geometry previously generated from
parent.- Parameters:
parent (str)
- Return type:
None
- source_geometry(gid)[source]#
The element’s own geometry: the polygon for walls, else the index geometry.
- Parameters:
gid (str)
- Return type:
BaseGeometry
- is_bearing_support(gid)[source]#
True if a joist can bear on
gid: a line (beam) or a linear-reaction polygon (wall, indexed by its centerline). Point-reaction polygons (columns/posts) are not joist supports.
- query_supports(region, plane, rank)[source]#
Geometry ids that can support a member of
rankonplaneand whose index geometry (wall centerline / beam line) intersectsregion.Returned in model insertion order so results are deterministic.
- incident_geoms(node_id)[source]#
Returns the incidences (geometries touching the node) recorded for ‘node_id’, or an empty list.
- topology_signature()[source]#
A jitter-invariant summary of the built arrangement (design §10).
Comparing two signatures answers “same node set, same edge set, same incidence structure?” independent of the exact floating-point coordinates, which is the acceptance criterion for the whole effort.
- Return type:
papermodels.datatypes.annotation#
The Annotation data type, a format-independent record of a single drawing markup.
- class papermodels.datatypes.annotation.Annotation(page, object_type, text, vertices, line_color, fill_color, line_type, line_weight, line_opacity, fill_opacity, matrix, local_id)[source]#
Bases:
objectA single drawing markup, independent of the file format it was read from.
- Parameters:
page (int) – The page index the annotation appears on.
object_type (str) – The markup type, e.g. “Line”, “PolyLine”, “Polygon”, “Square”.
text (str) – The annotation’s text content. For DXF sources this is the layer name.
vertices (list[Decimal]) – Flat list of coordinates, [x0, y0, x1, y1, …], in PDF points.
line_color (tuple[Decimal]) – RGB line color, each component in [0, 1].
fill_color (tuple[Decimal]) – RGB fill color, each component in [0, 1].
line_type (Any) – The dash pattern of the line, or None for a solid line.
line_weight (Decimal) – Line weight in points.
line_opacity (Decimal) – Line opacity in [0, 1].
fill_opacity (Decimal) – Fill opacity in [0, 1].
matrix (list[Decimal]) – The six-term affine transformation matrix of the annotation.
local_id (int) – Index of the annotation within its source page.
Two annotations compare equal when all of their geometric and visual properties match; ‘local_id’ is not compared. This is what allows a legend entry to be matched to the markups that share its appearance.
papermodels.datatypes.exceptions#
Exceptions raised while parsing markups and building geometry.
- exception papermodels.datatypes.exceptions.LegendError[source]#
Bases:
ExceptionRaised when legend entries are missing, malformed or ambiguous.