IfcSurfaceFeature (e.g. road markings) adheres to a host element through
IfcRelAdheresToElement, a [1:1] cardinality hierarchical relationship in the
same family as aggregation, containment and nesting since IFC4.3. The spatial
traversal never followed it, so surface features had no resolvable parent or
container: on import they landed in the Unsorted collection instead of the
host's spatial collection, and were dropped entirely in DECOMPOSITION filter
mode.
Add get_adhered_element (feature to host) to the get_parent resolver chain and
walk HasSurfaceFeatures in get_decomposition, plus a get_surface_features helper
mirroring get_parts/get_contained. With get_parent resolving adherence,
get_container now returns the host's spatial container, so tool.Collector places
surface features under the host. Also follow HasSurfaceFeatures in the Bonsai
DECOMPOSITION filter path so they load in that mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
IfcGeometricRepresentationContext.Precision is typed as a plain IfcReal
but is interpreted in the project length unit, so the IfcLengthMeasure
traversal in convert_file_length_units never touched it. A model
converted from mm to m kept a Precision of e.g. 0.01 (fine in mm, huge
in m), which breaks downstream geometry interpretation such as
IfcConvert boolean cleanup.
Subcontexts derive Precision from their parent, so only root contexts
are scaled.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_info_2 advertises the same signature as get_info but raised a bare
AssertionError for anything the C++ fast path does not implement --
including its own default arguments (recursive=False).
Use the fast path when recursive=True, return_type=dict and ignore=()
hold, and delegate to the pure Python get_info otherwise. As noted in
the issue, without recursion there is no meaningful performance gain to
lose by delegating.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test_memusage_partial_open was silently skipped in CI (psutil was
never installed there). Add psutil so it actually runs, and run the
RSS measurement in a subprocess so the fixture file isn't already in
the page cache from earlier tests, which was making both deltas read
as zero.
Generated with the assistance of an AI coding tool.
The class-pairing validation added in 10ee5aef4f rejected every typed
annotation with "IfcTypeProduct cannot type IfcAnnotation ... (allowed
occurrence classes: <none>)".
The check derived allowed occurrence classes solely from the
buildingSMART implementer-agreement map, which has no entry for the
abstract IfcTypeProduct that Bonsai uses for annotation types (IFC4 has
no IfcAnnotationType). The intended occurrence class is declared in the
type's ApplicableOccurrence attribute (e.g. "IfcAnnotation/TEXT"), the
schema-defined mechanism for exactly this purpose.
Augment the allow-list with the ApplicableOccurrence class, but only
when its leading token resolves to a real entity in the schema so
free-form text is not trusted blindly. Genuine mismatches (e.g.
IfcWallType -> IfcWindow) are still rejected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
regenerate_wall_representation returns None for walls without an
IfcMaterialLayerSet (the only mode it knows how to rebuild). Feeding
None to switch_representation crashes deep in resolve_representation
on .Items. Document the None return on the API side and bail in
tool.Model.recreate_wall when it hits.
Generated with the assistance of an AI coding tool.
Schema-illegal IfcDoor->IfcWallType pairings parse cleanly but propagate
into operators that fan out by type and eventually crash the wrapper.
Block the pairing at its source: API guard in ifcopenshell.api.type.
assign_type, per-object partition in BIM_OT_assign_type + DuplicateType,
new tool.Type.is_relating_type_compatible helper, AST forward-compat
guard. Files in the wild are still loaded unchanged.
Generated with the assistance of an AI coding tool.
Adds the IFC-library primitives the ifcpatch Migrate recipe needs for a
defensive IFC4 / IFC4X3 -> IFC2X3 downgrade without each caller
reinventing the wheel.
In ifcopenshell.util.schema:
- Migrator(fallback_element_to_proxy=False) opt-in: when True, IFC4-only
IfcElement subclasses (IfcLamp, IfcPipeSegment, IfcGeographicElement,
...) migrate to IfcBuildingElementProxy instead of raising. Default
preserves the strict failure-on-unmappable contract for existing
callers (classification API, etc.).
- geometry_classes_introduced_after(target, source) derives the
IfcRepresentationItem subclasses present in `source` but absent in
`target` directly from the loaded schemas. Cached per pair. Replaces
hand-curated class lists that drift with each IFC update.
ifc4_only_geometry_classes() retained as an alias.
- generate_default_value synthesises a unit IfcAxis2Placement2D /
IfcAxis2Placement3D when downgrading entities whose Position became
required in the target schema (IfcIShapeProfileDef and friends in
IFC2X3).
- Enum-mismatch detection upgraded from string-matched RuntimeError to a
structural check via ifcopenshell.util.attribute.get_enum_items so
upgrade paths still surface real bugs loudly.
In ifcopenshell.util.shape_builder:
- polygonal_face_set_to_faceted_brep converts IfcPolygonalFaceSet /
IfcTriangulatedFaceSet (IFC4-only) directly to IfcFacetedBrep,
preserving topology including IfcIndexedPolygonalFaceWithVoids inner
bounds. Validates inputs at the boundary.
- arc_to_polyline_points approximates a circular arc through three
points with a chord polyline of configurable subdivisions. Tolerates
floating-point noise on planar Z. Raises on non-planar or invalid
inputs.
Test coverage: 47 unit tests across schema + shape_builder lanes
covering each helper directly (no transitive-only coverage), including
regression pins for the IFC4X3-prefix ordering invariant in
get_fallback_schema and the strict-default Migrator contract.
Generated with the assistance of an AI coding tool.
IfcTriangulatedFaceSet/IfcPolygonalFaceSet were introduced in
Fix#7992: IFC4 and do not exist in IFC2X3. Previously, requesting an
IfcTessellatedFaceSet representation in an IFC2X3 file silently
fell back to a faceted brep after unassigning material sets.
Add a guard in the update_representation operator (user-facing
error) and in the add_representation API (ValueError) so the
unsupported request is caught instead of failing silently.
Generated with the assistance of an AI coding tool.
When validate_type selected a preferred_item from remaining_items
(e.g. the sole IfcBooleanResult in a representation), it left that
item in the list. The subsequent Items filter removed every item,
leaving Items=[] and causing guess_type to return
"MappedRepresentation" — silently corrupting the representation.
Also removes temporary debug print statements added during
investigation of the wall-to-slab extension workflow.
Generated with the assistance of an AI coding tool.
When a `+`-separated filter group returns no results, `FacetTransformer.facet_list`
was skipping the reset of `has_additive_facet_in_current_list` because the reset
was inside the `if self.elements:` guard. The stale flag caused the next group's
`add_default_elements()` to bail out early, leaving its element set empty and
silently dropping every subsequent group from the result.
Move the flag reset outside the guard so it always fires regardless of whether
the group produced any results.
add_railing_representation now factors into two parts:
* compute_wall_mounted_handrail_geometry returns a pure-geometry
WallMountedHandrailGeometry dataclass (handrail polyline + support
list + terminal caps), no IFC mutation.
* add_railing_representation wraps that dataclass into an
IfcShapeRepresentation as before.
Downstream consumers that want the same math without round-tripping
through an IFC file (Blender gizmo previews, viewport drafts) now
drive compute_X directly. Future add_X_representation work in the
geometry API is encouraged to follow the same shape — a sibling
compute_X function + thin IFC wrapper.
The railing_type parameter is dropped from the signature — only
WALL_MOUNTED_HANDRAIL was ever supported, so the kwarg was dead.
The Bonsai railing-modifier caller is updated in the same commit
to stop passing it; without that update Bonsai's
finish_editing_railing_path raises TypeError on the first edit.
RailingSupport and WallMountedHandrailGeometry use @dataclass(slots=True)
— they're constructed N-per-cap during arc sampling, so the per-instance
overhead matters.
Public symbols (RailingSupport, TERMINAL_TYPE,
WallMountedHandrailGeometry, compute_wall_mounted_handrail_geometry,
add_railing_representation) re-exported from ifcopenshell.api.geometry.
New test/api/geometry/test_add_railing_representation.py covers the
compute/wrap contract.
Generated with the assistance of an AI coding tool.
Drops the module-local ``mm()`` helper in favour of the centralised
``ifcopenshell.util.unit.mm_to_m`` (added earlier in this PR). The
``as mm`` import alias preserves the existing call sites' readability.
Generated with the assistance of an AI coding tool.
Drops the module-local ``mm()`` helper in favour of the centralised
``ifcopenshell.util.unit.mm_to_m`` (added earlier in this PR). The
``as mm`` import alias preserves the existing call sites' readability.
Generated with the assistance of an AI coding tool.
ShapeBuilder gains module-level NP_X / NP_Y / NP_Z / NP_XY / NP_XZ /
NP_YZ / NP_YX axis-index constants. Downstream geometry builders had
been redefining local copies for indexing np.ndarray vectors of shape
(3,) or (N, 3); centralising removes the duplication.
mep_transition_length and mep_transition_calculate verbose default
flipped from True to False. The prints are diagnostic-only output;
True-by-default spammed the console on every transition computation,
which fires per-fitting on IFC load.
Generated with the assistance of an AI coding tool.
Centralises the millimetre-to-metre conversion shortcut that
add_door_representation and add_window_representation each defined
locally. Subsequent commits in this PR switch both call sites to
import this from util.unit, removing the duplicate definitions.
Generated with the assistance of an AI coding tool.