Commit Graph

20927 Commits

Author SHA1 Message Date
Ryan Schultz 705f7cc109 Add LAYER3 (slab) mirror geometry support
- Bypass type-based mirroring for LAYER3 elements: slab geometry lives
  on the instance (IfcExtrudedAreaSolid), not the type's RepresentationMaps,
  so the old path was a no-op that silently reassigned an empty mirrored type
- Handle IfcBooleanResult in invert_general_object by recursing into
  FirstOperand/SecondOperand before calling ShapeBuilder.mirror, which
  only handles leaf geometry types
- Mirror IfcOpeningElement placements in slab-local coordinate space:
  working in world space caused a double-offset because edit_object_placement
  stores a relative matrix, so when the depsgraph later synced the slab's
  Blender move to IFC the opening shifted again by the full slab displacement;
  mirroring the relative offset directly avoids this regardless of sync timing
2026-06-22 13:12:09 -05:00
Ryan Schultz c98de235e5 Add Z-axis mirror support
Extend mirror_axes from a 2-tuple to a 3-tuple so the Z component
is no longer silently dropped. Truncate to 2D only at the
ShapeBuilder.mirror call site (which is 2D-only). Add 180° Y-axis
rotation compensation for the Z-flip case, analogous to the existing
Z-axis rotation compensation for Y-flip.

Generated with the assistance of an AI coding tool.
2026-06-22 13:12:09 -05:00
Ryan Schultz e4ca42001a Fix mirror axis mismatch when ref object is rotated
The geometry inversion was hardcoded to the object's local X axis
regardless of the mirror reference orientation. When the reference
was rotated (e.g. a wall at 90°), the inversion axis and the
position reflection were perpendicular, producing a rotation artefact.

Fix by computing the mirror plane normal in the object's local space
and passing it as the inversion axis for untyped elements. For typed
elements, the cached inverted type is always X-flipped; a 180° Z
rotation is applied to the object when a Y-flip is needed instead
(flip_Y = Rotate_Z_180 ∘ flip_X).

Generated with the assistance of an AI coding tool.
2026-06-22 13:12:09 -05:00
Ryan Schultz 916c7596a7 - Shift+click or Shift+Ctrl+F duplicates and mirrors, keeping original
- Show Mirror Geometry button for any active IFC element, not just
  flippable types

Generated with the assistance of an AI coding tool.
2026-06-22 13:12:09 -05:00
Ryan Schultz ab977f23c8 - Use active object as mirror plane when other objects are selected,
falling back to in-place X-axis flip when only one object is selected
- Show Mirror Geometry button for any active IFC element, not just
  flippable types

Generated with the assistance of an AI coding tool.
2026-06-22 13:12:08 -05:00
Ryan Schultz 720d97c784 Fix NoneType error and missing argument in mirror_geometry operator
- Guard against `RepresentationMaps` being `None` for type products without geometry
- Fix `assign_type()` call missing `tool.Model` argument

Generated with the assistance of an AI coding tool.
2026-06-22 13:12:08 -05:00
Robin Quint 2ec3ecd535 Mirroring should now support IfcBoundingBox 2026-06-22 13:12:08 -05:00
Robin Quint 428a780181 Applied formatting 2026-06-22 13:12:08 -05:00
Robin Quint 8b621089a8 Mirrored objects now stay in place correctly. Editing doors no longer results in switching representations. 2026-06-22 13:11:53 -05:00
Robin Quint 900fdfc51c Added tool.Blender.Modifier.has_mirrored_type and set_mirrored_type. 2026-06-22 13:08:49 -05:00
Robin Quint bd5102b4bb Geometry for arbitrary objects is now synced for mirrored objects 2026-06-22 13:08:04 -05:00
Robin Quint e6ad9c63c5 Inverted type name is now xxx.Mirror 2026-06-22 13:07:19 -05:00
Robin Quint f20f65a312 fixed directly flipping a product type throwing an exception 2026-06-22 13:07:19 -05:00
Robin Quint 8c50cfc906 changed hotkey to ctrl + f, added icon 2026-06-22 13:07:19 -05:00
Robin Quint aa08eb0ece Added bim.mirror_geometry() operator for truly mirroring products / types 2026-06-22 13:04:26 -05:00
Robin Quint 981b52ed45 Added ChangeSwingDirection operator for parametric doors 2026-06-22 13:00:47 -05:00
Robin Quint 66ca622718 Made door / window preview both simpler and more intuitive 2026-06-22 13:00:10 -05:00
Robin Quint c6782b54b2 Fixed preview for rotated walls 2026-06-22 12:59:23 -05:00
Robin Quint ab6cc3423b Fixed door / window preview and made preview of other objects not rotate with wall 2026-06-22 12:59:23 -05:00
Robin Quint ef1de5fa79 Flip tool now moves windows / doors to the other side of a wall 2026-06-22 12:57:39 -05:00
Robin Quint f3e3129a63 Doors are now placed on the side of the wall that was snapped to 2026-06-22 12:57:09 -05:00
Bruno Postle d0b5e0a92f bonsai: fix failing test_copy_with_new_geometry_copied_from_the_old
Move has_material_styles into the Root tool so it can be mocked in core
unit tests; the private function was calling ifcopenshell.util.element
directly, bypassing the tool layer and crashing the test.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Bruno Postle 7c7943bb0a bsdd: retry on HTTP 429 rate-limit responses in Client.get()
The bSDD API rate-limits aggressively; without retry logic the client
silently returns the 429 JSON dict, causing KeyError when callers
access expected response keys. Client.get() now retries up to 5 times,
sleeping for the Retry-After header value (defaulting to 5s).

Remove the manual time.sleep() calls from the test module now that the
client handles rate limiting automatically.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Bruno Postle a61bb485c6 bsdd: fix tests for rate limiting, wrong API call, and assertion bugs
Cache all API responses at module level with sleeps between calls to
avoid hitting the bSDD rate limit. Fix test_get_class_relations to
call get_class_relations() instead of get_class_properties(uri, True).
Fix "X" and "Y" in [...] assertions which only checked "Y".

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Bruno Postle 233c6498a6 CI: and ifc5d test needs xlsxwriter 2026-06-22 12:56:23 -05:00
Bruno Postle 278f924b1f ifc5d: fix two csv2ifc bugs found by round-trip test
ItemIsASum and Quantities are exporter columns that were missing from
MAIN_CSV_HEADER_COLUMNS, causing them to be misidentified as numeric cost
value categories on re-import. Also initialise rate_cost_schedule to None
before the search loop to avoid UnboundLocalError when no match is found.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Bruno Postle 7f62758061 tests: mathutils tests need python 3.12+ 2026-06-22 12:56:23 -05:00
Bruno Postle c630668c24 CI: An ifc5d test needs odfpy 2026-06-22 12:56:23 -05:00
Bruno Postle d36ac63734 ci: add test coverage for ifc5d, ifcquery, ifcedit, ifcmcp
Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Bruno Postle 3c18964eb1 ifcpatch: fix MergeProjects context deduplication and remove stale stub
Contexts were deleted with remove_deep2 into a single set, but
file.add() inflates inverse counts causing phantom references. Split
into coord_ops (0 real inverses, remove_deep2 recurses to clean
IfcProjectedCRS) and contexts (sorted parent-first so phantoms don't
become dangling references). Remove stale MakeVolume stub from
ifcopenshell_wrapper.pyi.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Ryan Schultz 6441c39c4e Fix #6392: when duplicating a window/door/etc, the associated IfcOpenElement duplicates as well.
Also when `bim.append_library_element` it copies the IfcOpenElement, as well.
2026-06-22 12:56:23 -05:00
Ryan Schultz 8cd797eeec Fix extruded void mirror in LAYER2 walls
IfcExtrudedAreaSolid opening geometry must be transformed by the
wall-local Householder H applied numerically to opening-local coords
(T_geom = H), not the opening-local Householder R^T H R.  The
correct Position-local transform is placement_mat.T @ H @ placement_mat,
reusing H already computed for the placement conjugation.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Ryan Schultz 60a0411e96 Add LAYER2 wall mirror support
Three changes:

1. Mirror LAYER2 (wall) geometry correctly: exclude LAYER2 from the
   assign_inverted_type path (which only flips the type geometry and
   leaves the instance body unchanged) and route it through
   invert_representation like LAYER3 slabs.  For Y-axis flips also
   invert DirectionSense and negate OffsetFromReferenceLine on the
   IfcMaterialLayerSetUsage so regenerated walls stay correct.

2. Surface bim.mirror_geometry in the right-click context menu and
   Object menu so it is reachable without activating the BIM Tool.

3. Guard bpy.ops.bim.extend_to_underside.__doc__ with try/except so a
   missing operator (branch without that feature) does not crash the
   entire tool-header draw and hide all other buttons.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Ryan Schultz b6476b764e Fix void mirroring on assign_type path
Two bugs fixed in TrueMirrorElements for tessellated elements with
IfcExtrudedAreaSolid voids on the assign_inverted_type path:

1. Sync element IFC placement before computing opening positions so the
   geometry engine uses the correct element-local frame when applying
   boolean voids (fixes void appearing at wrong element-local offset).

2. Replace builder.mirror for IfcExtrudedAreaSolid with direct coordinate
   negation to avoid IFC entity aliasing that corrupted profile vertices
   in the translate→mirror→translate sequence.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:23 -05:00
Ryan Schultz 1a15cfef70 Fix BRep and opening element mirroring
Three fixes in TrueMirrorElements / _apply_opening_mirror:

1. IfcFacetedBrep support: implement mirroring in mirror_item by
   negating vertex coordinates along the flipped axes and reversing
   face winding for an odd number of flipped axes (restores outward
   normals).

2. Routing: add type_has_reps guard to is_assign_type_path so
   elements whose IfcTypeProduct carries no RepresentationMaps fall
   through to direct geometry mirroring instead of a no-op type swap.

3. Opening placement: use H@R@H conjugation (not direct Householder
   on columns) for the rotation part of _apply_opening_mirror.
   Direct Householder yields det=-1; IFC's Y=Z×X normalization then
   introduces a spurious 180°Z rotation. Conjugation keeps det=+1
   and produces the correct mirrored rotation. Also call
   builder.mirror() on each opening representation item so local
   vertex data is mirrored alongside the placement frame.

Generated with the assistance of an AI coding tool.
AI effort: 8/10
2026-06-22 12:56:23 -05:00
Ryan Schultz 3782bbe790 Fix IfcOpeningElement mirror on LAYER3 slabs
Three-part fix in _apply_opening_mirror:

1. Rotation: replace direct Householder on rotation columns with
   conjugation H@R@H. Direct Householder gives det=-1; IFC's
   Y=Z×X normalization then introduces a spurious 180°Z rotation
   (R_z(π−θ) instead of the correct R_z(−θ)). Conjugation keeps
   det=+1 and produces the correct mirrored rotation for all θ.

2. Geometry: call builder.mirror() on each representation item of
   the opening so that local vertices/profiles are also mirrored.
   edit_object_placement only moves the placement frame; without
   this step the void shape is the original, not its mirror image.

3. Ordering (existing): opening update runs before
   invert_representation so the internal reload_representation
   sees correct positions.

Generated with the assistance of an AI coding tool.
AI effort: 8/10
2026-06-22 12:56:23 -05:00
Ryan Schultz 95e11a56bf Add LAYER3 (slab) mirror geometry support
- Bypass type-based mirroring for LAYER3 elements: slab geometry lives
  on the instance (IfcExtrudedAreaSolid), not the type's RepresentationMaps,
  so the old path was a no-op that silently reassigned an empty mirrored type
- Handle IfcBooleanResult in invert_general_object by recursing into
  FirstOperand/SecondOperand before calling ShapeBuilder.mirror, which
  only handles leaf geometry types
- Mirror IfcOpeningElement placements in slab-local coordinate space:
  working in world space caused a double-offset because edit_object_placement
  stores a relative matrix, so when the depsgraph later synced the slab's
  Blender move to IFC the opening shifted again by the full slab displacement;
  mirroring the relative offset directly avoids this regardless of sync timing
2026-06-22 12:56:22 -05:00
Ryan Schultz d45fc0601d copy_representation: preserve MappedRepresentation structure for IfcTypeProduct
Incorporates the per-map copy logic from build-0.8.5 that preserves nested
MappedRepresentation structure instead of blindly using copy_deep, while
retaining HasShapeAspects and StyledByItem handling from this branch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 12:56:22 -05:00
Ryan Schultz 2f7ed0ea52 Add Z-axis mirror support
Extend mirror_axes from a 2-tuple to a 3-tuple so the Z component
is no longer silently dropped. Truncate to 2D only at the
ShapeBuilder.mirror call site (which is 2D-only). Add 180° Y-axis
rotation compensation for the Z-flip case, analogous to the existing
Z-axis rotation compensation for Y-flip.

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:22 -05:00
Ryan Schultz 76ff5c67e0 Fix mirror axis mismatch when ref object is rotated
The geometry inversion was hardcoded to the object's local X axis
regardless of the mirror reference orientation. When the reference
was rotated (e.g. a wall at 90°), the inversion axis and the
position reflection were perpendicular, producing a rotation artefact.

Fix by computing the mirror plane normal in the object's local space
and passing it as the inversion axis for untyped elements. For typed
elements, the cached inverted type is always X-flipped; a 180° Z
rotation is applied to the object when a Y-flip is needed instead
(flip_Y = Rotate_Z_180 ∘ flip_X).

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:22 -05:00
Ryan Schultz ef779e2259 - Shift+click or Shift+Ctrl+F duplicates and mirrors, keeping original
- Show Mirror Geometry button for any active IFC element, not just
  flippable types

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:22 -05:00
Ryan Schultz 53a275501e - Use active object as mirror plane when other objects are selected,
falling back to in-place X-axis flip when only one object is selected
- Show Mirror Geometry button for any active IFC element, not just
  flippable types

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:22 -05:00
Ryan Schultz 888cbf7a63 Fix NoneType error and missing argument in mirror_geometry operator
- Guard against `RepresentationMaps` being `None` for type products without geometry
- Fix `assign_type()` call missing `tool.Model` argument

Generated with the assistance of an AI coding tool.
2026-06-22 12:56:22 -05:00
Robin Quint 84902d9130 Mirroring should now support IfcBoundingBox 2026-06-22 12:56:22 -05:00
Robin Quint dcb22acd3c Fixed double mirroring when two IfcPolygonalFaceSets reuse the same coordinate list entity 2026-06-22 12:56:22 -05:00
Robin Quint f1754ca523 Fixed missing length unit conversion in FilledOpeningGenerator 2026-06-22 12:56:22 -05:00
Robin Quint f576c58c05 Applied formatting 2026-06-22 12:56:22 -05:00
Robin Quint 01a5e457a2 copy_representation now properly copies explicitly assigned IfcStyledItems 2026-06-22 12:56:22 -05:00
Robin Quint 0b7f798c03 Mirrored objects now stay in place correctly. Editing doors no longer results in switching representations. 2026-06-22 12:56:22 -05:00
Robin Quint be62fd494e Simplified shape aspect duplication in copy_representation() 2026-06-22 12:56:22 -05:00