The x-angle transformation for LAYER3 slabs assumed SweptArea
is always IfcArbitraryClosedProfileDef (which has OuterCurve),
but composite profiles use IfcCompositeProfileDef instead.
Apply the coord scaling to each sub-profile individually.
Generated with the assistance of an AI coding tool.
generate_section_reference_points had no handler for
MODEL_VIEW target view, causing it to silently return
None. Add MODEL_VIEW branch that clips the section line
to XY camera bounds while preserving the Z coordinate
for correct 3D placement.
Generated with the assistance of an AI coding tool.
Guard the int() cast on CardinalPoint in
BIM_OT_edit_assigned_material so a None value (no cardinal
point set) no longer raises a TypeError.
Generated with the assistance of an AI coding tool.
Extracted from the font file like so:
python3 -c "
from fontTools.ttLib import TTFont
tt = TTFont('src/bonsai/bonsai/bim/data/fonts/OpenGost Type B TT.ttf')
for record in tt['name'].names:
if record.nameID == 13:
print(record.toUnicode())
"
IFC2X3 representations have no HasShapeAspects inverse; opening the
Geometry & Materials subpanel on an IFC2X3 object raised AttributeError
and left the items list empty. Wrap the access with a getattr default
so pre-IFC4 schemas return an empty iterable, and pin the contract with
an AST forward-compat guard that scans bim/, tool/, and core/ for any
future direct .HasShapeAspects access.
Closes#8157
Generated with the assistance of an AI coding tool.
Port gizmos-8088's railing gizmo block to v0.8.0:
- _RailingEditMixin (PathPreservingEditMixin specialisation) +
EnableEditingRailing / CancelEditingRailing / FinishEditingRailing
edit triad
- CycleRailingType (2-value type cycler) + ToggleRailingUseManualSupports
one-shot + EditRailingTerminalType
- FlipRailingPathOrder + EnableEditingRailingPath /
CancelEditingRailingPath / FinishEditingRailingPath path-edit
operators (mutually exclusive with the schematic frame)
- GizmoRailingSchematic (BaseSchematicGizmoGroup specialisation) —
axonometric schematic frame with per-attribute dimension gizmos
for FRAMELESS_PANEL + WALL_MOUNTED_HANDRAIL railing types;
hover-on-attr highlights the schematic edges tagged with the
matching feature
Tests: test_railing_lifecycle.py (280 LOC) +
test_railing_schematic.py (272 LOC).
Drops the per-feature GizmoPreferences{Door,Window,Stair,Wall,Roof,
Railing} PropertyGroups that the source commit added to bim/ui.py
— that finer-grained per-attribute toggle model was deliberately
collapsed to flat per-feature bools in the PR5b prefs sweep, and
GizmoRailingSchematic gates on the flat ``prefs.gizmos.railing``
bool via ``gizmo_pref_name`` so no functionality is lost.
Generated with the assistance of an AI coding tool.
The check `len(bytedata) == n * 2` was wrong: float64 is 8 bytes per
element, not 2. Legacy float64 checksums fell through to the float32
reader and produced a (2n,)-shaped array, breaking is_moved() and
is_camera_moved() with `ValueError: operands could not be broadcast`
on .blend files saved by Blender <5.0.
Adds a parametrized regression test covering both n=3 (translation)
and n=9 (rotation) for both dtypes.
Generated with the assistance of an AI coding tool.
GizmoWallEdition.position_gizmos used props.anchor_x / props.length
for the in-range check (split icon visibility) and perpendicular
gizmo placement. Those props mirror IFC and are re-primed by
_maybe_resync_wall_props_from_ifc — any operator path that skips
the re-sync leaves the perpendicular gizmo clamped to the previous
wall extent, so the icon parks at the old wall end instead of the
cursor's orthogonal projection. Visible after a wall mutation as
the perpendicular icon landing way off the cursor in top-down view.
Switch to the mesh bbox along local X. recreate_wall rebuilds the
mesh to match the current IFC body on every wall mutation, so
bound_box is authoritative without an explicit props sync.
Generated with the assistance of an AI coding tool.
MEP elements imported as tessellation / brep (no IfcExtrudedAreaSolid
or IfcSweptDiskSolid in their body representation) can't be
parametrically edited — the gizmos offer affordances the geometry
kernel has no path to honour. tool.System.has_parametric_body
inspects the Model/Body/MODEL_VIEW representation and returns True
only when at least one item resolves to one of the two
profile-sweep primitives.
The gate is wired into:
- GizmoMEPActions.is_eligible_object (the action icon group)
- _active_is_flow_segment / _active_is_bend_fitting visibility
predicates the icon row consults per-icon
- GizmoPipeSegmentEdition / GizmoDuctSegmentEdition is_element_type
tool.Parametric.is_pipe_segment / is_duct_segment stay IFC-class-only
so their truth-table contract test keeps reading a single concern.
Generated with the assistance of an AI coding tool.
Pure-math parallelism check (value ≡ 0 mod π within VTX_PRECISION)
that lived as a module-private helper in mep.py belongs next to
tool.Cad.is_x — same comparator family, no MEP-specific knowledge.
Other features with rotation-difference checks (wall fillet, roof
slope, railing terminus) now have a sanctioned spelling.
Generated with the assistance of an AI coding tool.
Four standalone test files pinning contracts the production code
already honours:
- test_mep_actions_cache.py: GizmoMEPActions visibility-predicate
cache evicts on selection or generation change.
- test_mep_bend_preview_cache.py: bend decorator polyline cache
re-uses within a generation and rebuilds on generation bump.
- test_mep_distribution_fit_smoke.py: bim.fit_flow_segments
round-trips a 3-segment polyline without raising.
- test_preview_cancel_ops_forward_compat.py: AST scan ensures every
preview Enable* operator has a paired Cancel* operator with the
matching prop reset.
Generated with the assistance of an AI coding tool.
Two small refactors:
- apply_transform_modal_draw_gate(group, context) replaces the
three-line _is_transform_modal_active + _hide_all_non_modal_gizmos
pair that BillboardingGizmoGroupMixin, BaseParametricGizmoGroup
and BaseSchematicGizmoGroup all repeat in draw_prepare.
- decorator.py renames _stroke_lines_alpha to a public-scope
draw_polyline_segments and drops the no-longer-private companion
docstring reference; the function is now usable by sibling
decorators that draw polyline overlays.
Plus a few one-liner tweaks in tool/model.py and opening.py
following the helper rename.
Generated with the assistance of an AI coding tool.
Two hot paths the gizmo polls fire every viewport event memoise
their result against tool.Parametric.get_geom_generation():
- tool.Blender.Modifier.any_selected_array_child caches the
per-selection scan against the selection identity-set + the
IFC generation token so a stable selection during a drag
doesn't re-walk every selected object's BBIM_Array pset every
frame.
- bim/module/model/wall.py grows a pair-predicate + connection
cache that the wall topology gizmos hit; both keyed on
(pair_uids, predicate_kind, generation) so a wall split or
axis edit invalidates correctly via the generation bump.
Behavioural contract is unchanged — stale entries are evicted
on generation bump; cache miss returns the same value the
un-cached path returned.
Generated with the assistance of an AI coding tool.
bim/module/model/conftest.py exposes the autouse _require_real_bpy
skip-guard, four make_* factories (obj / element / context /
ifc_file), and a patched_tool context-manager factory that wires
the half-dozen tool.* boundary patches every gizmo + decorator
test was repeating.
Existing test files in the directory drop their local copies of
_require_real_bpy and adopt the patched_tool / make_* fixtures
where the call site simplifies — test_mep_port_operators.py is
the biggest beneficiary (−89 LOC).
No production behaviour change.
Generated with the assistance of an AI coding tool.
Three concerns bundled by file boundary (all in mep.py):
- Extract bend preview operators + GizmoBendPreview into a focused
mep_bend_preview.py module; preview_base.py grows the shared helper
set both bend and other previews now consume; classes tuple in
model/__init__.py updated to register the new module.
- Surface ERROR reports on five silent CANCELLED returns in
MEPUnjoinAtPort / MEPRemoveTerminalFitting / MEPUnjoinPair so a
degenerate IFC file ("fitting has no Blender object", "connected
port leads nowhere") shows up in the popup instead of looking like
a no-op.
- DRY: _resolve_active_mep_segment + _require_port_state factor the
segment-id-or-active-object resolve + port-state guard out of every
port operator's prologue; _wire_anchored_icon_targets pulls the
GizmoMEPActions setup() body into an exercise-without-MRO helper so
the wiring-contract tests can hit it without instantiating the
GizmoGroup.
Drops the now-unused preview_base import that the extraction left
behind.
Generated with the assistance of an AI coding tool.
The opening preview's outline used a single-batch two-pass scheme that
dimmed the occluded back pass via alpha=0.25. The visible front pass also
inherited the source decorator color's modest alpha, so the outline read
as subtle on both sides.
Replace with a CAD hidden-line convention: solid full-alpha front pass on
the visible side, world-space dashed back pass on the occluded side. Both
passes use POLYLINE_UNIFORM_COLOR so depth and line-weight paths match.
The dashed batch is built once per object epoch by a new pure helper
tool.Blender.build_dashed_line_segments (pre-segments edges into world-
space dash chunks), then cached via the existing batch-cache mechanism
under "<uid>_dashed".
The solid front pass is rendered at a slightly wider line width than the
dashed back pass so its halo overpowers Blender's WIRE-display overlay
bias at outline pixels — without the asymmetry the wire's anti-z-fight
forward bias makes the LESS_EQUAL comparison narrowly fail and the
dashed pass wins on visible edges too.
Generated with the assistance of an AI coding tool.
MEPConnectElements took obj1_name/obj2_name (Blender object names),
which break when objects are renamed or replicated by array
duplication. Switch to obj1_guid/obj2_guid resolved via
ifc_file.by_guid, with by_guid RuntimeError surfaced as an operator
error rather than a stack trace. DrawPolylineProfile (the sole
in-tree caller) updates to pass GlobalIds.
Generated with the assistance of an AI coding tool.
Pins the geometry contracts the hand-meshed bend body relies on
while IfcSweptDiskSolid round-trip is broken upstream (#8106):
- profile cross-section sampling: circle returns 16 evenly-spaced
points starting at (radius, 0); rectangle returns the four
canonical corners; anything else returns None so the rep swap
is skipped rather than meshed against the wrong section
- parallel-transport framing keeps the cross-section continuous
around L-shaped corners — pinned via start / end ring planes
- initial_basis override seeds the first ring with the source
segment's local +X / +Y axes, fixing the asymmetric-rectangle
twist the world-Z seed produces
Generated with the assistance of an AI coding tool.
Pins which IFC mutation each port operator commits and which
inputs each refuses with CANCELLED:
- MEPUnjoinAtPort removes the fitting + reconnects the two free
ports; refuses if the named port is free or terminal
- MEPRemoveTerminalFitting deletes the terminal element + leaves
the segment's port free; refuses on bridged fittings
- SelectMEPPathMembers walks IfcRelConnectsPorts in both
directions from the active segment and selects every fitting /
segment reachable through the port graph
Boundary mocks for tool.Ifc, tool.System and MEPGenerator stand
in for the IFC fixture; tests assert against the recorded
ifcopenshell.api.* calls.
Generated with the assistance of an AI coding tool.
Pins two regressions the live MEP gizmo group can hit:
- per-icon setup() must write `position` (and `mode` on open-lock
icons) onto every target_set_operator result; the test stands in
for the AttributeError on bim.mep_add_obstruction that surfaced
when a field was dropped from the operator declaration
- each visibility_condition lambda must stay total against None /
non-IFC inputs, since a single raising predicate silently disables
every sibling icon in the group
Generated with the assistance of an AI coding tool.
ProfileDecorator.draw_faces (used by the roof path-edit overlay) and
SystemDecorator.draw_faces called bmesh.ops.triangulate on the live
bmesh — both mutated the input and produced ear-clip fans that rendered
as visible streaks across n-gon roof faces at alpha 0.1. The opening
DecorationsHandler edit-mode branch had a separate bug: it computed
triangles from obj.data.calc_loop_triangles() while iterating the
edit-mode bmesh, so any topology added mid-edit desynced the indices.
Centralise the correct draw path on tool.Blender.draw_bmesh_face_tris
(wraps bm.calc_loop_triangles, non-mutating, beauty triangulator) and
route all three call-sites through it. A forward-compat AST guard walks
every *Decorator / DecorationsHandler class under bim/module/ and pins
the no-bmesh.ops.triangulate rule against future regressions.
Generated with the assistance of an AI coding tool.
A user clicking the pen icon on a typed-product occurrence whose body
representation is mapped from its type would silently mutate every
sibling occurrence's geometry. Add a confirmation dialog at the pen-icon
dispatcher (the single chokepoint every feature routes through) showing
the sibling count, with a session-scoped suppress checkbox.
The check is read-only: tool.Model.get_sibling_occurrence_count wraps
tool.Geometry.get_elements_by_representation against the resolved body
rep and subtracts self + type. A forward-compat AST guard pins the
dispatcher monopoly so any future feature that binds pen_gizmo directly
to a feature-specific enable op fails the test before merge.
Generated with the assistance of an AI coding tool.