RemoveAnnotationFromDrawing read context.active_object, so taking several
annotations off a drawing meant selecting and clicking them one at a time,
even though AddAnnotationToDrawing already works across the whole selection.
It now mirrors the add operator: every selected IfcAnnotation that is actually
on this drawing is removed, drawing annotations are skipped, and the outcome is
reported as removed and skipped counts. The skipped count covers annotations
that must stay on at least one drawing, which the old code could only report
for the active object.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A manual IfcAnnotation can now be shared across several drawings as one
element: editing it once updates it everywhere, and it renders into each
drawing's SVG. Assignment is native IFC - the annotation becomes a member
of multiple drawing IfcGroups - so SVG generation picks it up per-drawing
with no writer changes.
Auto-generated annotations (grid/section/elevation/level tags, via
is_auto_annotation) are excluded since they are regenerated per drawing;
manual annotations, dimensions included, are shareable.
- core.add_annotation_to_drawing / remove_annotation_from_drawing
- tool.Drawing.get_annotation_drawings (plural)
- collector links a shared annotation into every assigned drawing collection
- import_annotations_in_group re-collects already-imported shared annotations
- bim.add_annotation_to_drawing / bim.remove_annotation_from_drawing operators
- BIM_PT_annotation_drawings panel (add/remove drawings on an annotation)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move is_manual_reference UI block to before _DIMENSION_TYPES to match the
position established by PR #7965's conflict resolution already present in
the build. The prior merge fix (66fcad84e3) placed it after _DIMENSION_TYPES,
causing a 3-way merge conflict when the build's #7965 has it before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both PR #7798 (ManualDrawingReference) and PR #8083 (parametric_dimensions)
independently added properties after entry #33 in Psets_BBIM_Annotation.ifc
and new BoolProperty declarations after `type_name` in prop.py and workspace.py
UI rows at the same locations.
Resolution: keep #8083's IDs (#34-#40) unchanged, renumber #7798's
IsManualDrawingReference and IsDocumentReference entries to #41 and #42,
update EPset_Annotation reference list accordingly, keep both UI rows,
and combine hotkey_S_A to use #8083's parametric-dimension routing
with #7798's "INVOKE_DEFAULT" argument for add_annotation.
Vertical faces perpendicular to the camera cannot be hit by raycast, so
the dimension snap tool missed them entirely. Fix by checking nearby
objects whose 3D bbox contains the floor hit point and running
mode-appropriate candidate lookup on each:
- FACE mode: _snap_on_coplanar_faces finds vertical mesh faces with a
bottom edge at the hovered Z, projects the cursor onto the face plane,
and returns a FACE candidate (blue outline + face snap point).
- LAYER mode: get_layer_snap_candidates now runs on nearby bbox objects
the same way VERTEX/EDGE mode already did, using the shared
_snap_cand_multi_cache (cleared on TAB mode switch).
- Remove clear_snap_objs() from PolylineOperator.invoke — BVH cache now
persists across invocations; per-entry staleness is checked in
create_snap_obj via matrix_world equality + vertex count, eliminating
the ~11 s full rebuild on every Shift+A press.
- Add _init_snapping_points() hook to PolylineOperator; DrawParametricDimension
overrides it with a cheap plane-intersection placeholder, deferring full
BVH detection to the first MOUSEMOVE.
- Cache matrix_world in SnapObj and replace O(N_vertices) validation loop
with O(1) matrix equality + single sample vertex check, cutting per-call
create_snap_obj cost from 22-600 ms to <0.2 ms on cache hits.
- Use scene-level BVH pierce-through in SetDimensionAnchor._compute_candidates
instead of per-object ray_cast loop (O(log N) vs O(N_objects)).
- Guard PolylineDecorator snap_mouse_point access against empty collection
to prevent IndexError before first MOUSEMOVE populates the property.
- Wrap closest_point_on_mesh in try/except RuntimeError in
_update_snap_draw_data for annotation objects with no internal mesh data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DrawParametricDimension: TAB cycles snap mode FACE→LAYER→EDGE→VERTEX during
placement; consumes both PRESS and RELEASE when not in input mode to avoid
conflict with polyline Cycle Input
- DrawParametricDimension: IFC-native snap candidate overrides polyline cursor
position in LAYER/EDGE/VERTEX modes; FACE mode shows polygon outline; reuses
_snap_draw_data / _draw_snap_indicator_global infrastructure from SetDimensionAnchor
- DrawParametricDimension: LAYER_BOUNDARY support in _update_perp_constraint,
deriving normal from LayerSetDirection (AXIS1/2/3)
- ClickNearestDimensionAnchor: scan all visible annotations instead of only
selected ones — view3d.select deselects the dimension before this operator
runs, so pre-selection check caused dots to never activate
- AnnotationTool keymap: bim.click_nearest_dimension_anchor placed before
view3d.select so it fires first when the annotation tool is active
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ClickNearestDimensionAnchor: scan all selected objects instead of active
object so clicking a green dot doesn't lose to the underlying IFC geometry
- GizmoAnchorHandle: remove draw_select entirely (any entry in the select
buffer causes Blender's gizmo system to consume clicks); keep purely visual
- Scale anchor dots to scale_basis = 0.2
- Fix ReferenceError in decoration.py draw loop after undo by catching
ReferenceError and resetting DecoratorData.is_loaded
- SetDimensionAnchor: inherit tool.Ifc.Operator so IFC pset writes are
tracked for undo; finish the modal after each face write so each anchor
gets its own undo step
- Fix ReferenceError in _modal after undo when annotation RNA is freed
- handler.py: add regenerate_dims_for_layer; call it from
EditMaterialSetItem._execute so dimensions update when layer thickness changes
- regenerate_dimension.py: fix ForcePerpendicularToFace for LAYER_BOUNDARY
anchors by deriving the thickness-axis normal from LayerSetDirection
(AXIS2→Y, AXIS1→X, AXIS3→Z) instead of requiring a stored normal_local
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GizmoAnchorHandle + DimensionAnchorWidget: colored dot gizmos at each
dimension curve vertex (green=anchored, orange=free); color changes to
blue while SetDimensionAnchor is in PICK_FACE mode for that vertex
- ClickNearestDimensionAnchor (LMB keymap): Python proximity operator that
fires SetDimensionAnchor pre-targeted at the nearest anchor dot within
120px, returning PASS_THROUGH for misses so normal viewport clicks are
unaffected
- SetDimensionAnchor: added anchor_index prop to enter PICK_FACE directly;
set_active_anchor called at all phase transitions (invoke, vertex-pick,
face-pick, alt-click free, ESC/RMB) so gizmo color tracks state correctly
- handler._sync_dimension_anchors_to_curve: proximity-based anchor sync
when curve vertex count changes in Edit Mode (subdivide / delete)
- depsgraph_update_post_handler: regenerates dimensions when referenced
elements move; also handles annotation curve edits directly
- Remove standalone Set Anchor button from annotation tool UI (replaced by
clicking a gizmo dot)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BBIM_Dimension.LinePosition (IfcLengthMeasure): holds the dimension line at
a fixed global coordinate along cross(world_Z, dim_direction), independent of
geometry movement
- regenerate_dimension applies LinePosition only when ForcePerpendicularToFace is
also set (the two are semantically coupled); anchor["pt"] always stores the true
surface hit so the measured length is unaffected
- BIMAnnotationProperties.line_position uses get/set callbacks instead of an update
callback to avoid the 'Writing to ID classes in this context is not allowed' error
that fires when Blender draws the tool header
- DimensionLinePositionWidget (BIM_GGT_dimension_line_position): gizmo group with
two opposing GizmoCone handles at the curve midpoint; poll requires
ForcePerpendicularToFace so the handles only appear when the feature is active
- UI: line_position field and gizmo are hidden when ForcePerpendicularToFace is off
- Psets_BBIM_Annotation.ifc: #40 LinePosition template added to BBIM_Dimension
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
workspace.py:
- Move "Set Dimension Anchor" and "Regenerate" buttons from the properties
panel (ui.py) into draw_edit_object_interface in the annotation tool,
visible whenever a selected object is a dimension-type IfcAnnotation
- Change force_perpendicular_to_face from a push-button (toggle=True) to
a standard checkbox for clearer on/off state
ui.py:
- Remove the "Parametric Dimension" section (now lives in the tool header)
prop.py:
- Add _update_force_perpendicular update callback: when the checkbox is
toggled, iterates all selected dimension annotations, writes the new
ForcePerpendicularToFace value to each BBIM_Dimension pset, and calls
regenerate_dimension so the constraint is applied immediately
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SetDimensionAnchor — hover-select-then-confirm:
- Cursor highlights candidate IFC elements (orange Blender selection outline)
before committing; Tab cycles through overlapping/coplanar candidates
- _compute_candidates: ray-cast all IFC mesh objects; falls back to 2D
bounding-box proximity (5 cm tolerance) for plan-view picks where the
ray misses the mesh by sub-mm amounts
- _write_anchor: after anchoring a face, immediately calls
regenerate_dimension with placement_override (Blender matrix_world)
and _update_blender_curve so the curve vertex moves to the resolved point
DrawParametricDimension — ForcePerpendicularToFace live snap constraint:
- Reads force_perpendicular_to_face toggle from annotation props on invoke
- After anchor[0] is placed on a FACE, _update_perp_constraint extracts
the face normal and stores it as the constraint axis
- _apply_perp_constraint runs every modal tick after handle_snap_selection,
projecting the current snap point onto pt[0] + t*normal
- On finalize, _create_dimension_from_polyline writes ForcePerpendicularToFace
to the BBIM_Dimension pset and calls regenerate_dimension to snap the
stored curve to the constraint before the operator exits
regenerate_dimension.py:
- ForcePerpendicularToFace block: after resolving all anchors, projects
vertices 1…n onto the line through pt[0] along anchor[0]'s face normal
- _get_anchor_face_normal_world: reads normal_local from anchor fingerprint,
calls _rotate_local_to_world with placement_override; falls back to stored
world-space normal
resolve_anchor.py:
- _rotate_local_to_world: transforms an element-local direction vector to
world space using the element's placement or placement_override matrix
pset/operator.py:
- EditPset._execute: after editing a BBIM_Dimension pset on an IfcAnnotation,
auto-calls regenerate_dimension + _update_blender_curve so changes to
anchors/ForcePerpendicularToFace are reflected immediately in the viewport
prop.py / workspace.py:
- Added force_perpendicular_to_face BoolProperty to BIMAnnotationProperties
- UI toggle shown in annotation tool header for DIMENSION/RADIUS/DIAMETER/
ANGLE/PLAN_LEVEL/SECTION_LEVEL types
Psets_BBIM_Annotation.ifc:
- Added ForcePerpendicularToFace property template (#39) to BBIM_Dimension
- Extended BBIM_Dimension applicability to ANGLE, PLAN_LEVEL, SECTION_LEVEL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends parametric dimension support with a modal polyline operator that uses
Bonsai's existing snap infrastructure (same as walls/slabs) for placing anchor
points. Shift+A in the Annotation tool now routes dimension types through this
operator instead of the generic add_annotation path.
- DrawParametricDimension: PolylineOperator subclass; each confirmed snap point
is converted to a BBIM_DimensionTarget anchor via _snap_to_anchor, which reads
face_index from the snap dict for face hits and falls back to closest_point_on_mesh
for vertex/edge hits
- handle_inserting_polyline override tracks anchor list in sync with polyline
points (insert on count increase, pop on BACKSPACE)
- hotkey_S_A dispatches to bim.draw_parametric_dimension for DIMENSION/RADIUS/
DIAMETER/ANGLE/PLAN_LEVEL/SECTION_LEVEL types; all other types keep existing path
- depsgraph_update_post_handler extended to also watch is_updated_geometry so
dimensions auto-regenerate when a referenced mesh is edited in Edit Mode; the
affected element's tessellation is evicted from _dim_shape_cache so resolve_anchor
re-tessellates from the updated IFC representation on the next pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New modal operator (bim.set_dimension_anchor) anchors dimension vertices
to IFC element faces. Anchors are stored as JSON in a BBIM_DimensionTarget
pset on the IfcAnnotation and resolved via tessellation at regeneration time.
- resolve_anchor.py / regenerate_dimension.py: new ifcopenshell API modules
- bim.set_dimension_anchor: 2-phase Object Mode modal (pick vertex → pick face)
- bim.regenerate_dimensions: recomputes all parametric dimensions
- Auto-regeneration via depsgraph_update_post when referenced elements move
- placement_override reads Blender matrix_world for G-moved elements
- Plan-view annotations flattened to annotation plane (Z=0 in local space)
- IfcIndexedPolyCurve.Segments rebuilt to handle n-point chains correctly
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())
"
The https://github.com/IfcOpenShell/website repo already has bonsai-docs.yml workflow that does the same thing - builds Bonsai docs from the main repo and deploys to bonsaibim_org_docs, so this workflow is redundant and confusing.
Extends manual drawing reference annotations (elevation and section) to
also support external SVG references imported via bim.add_reference.
- Add "Is a Reference" checkbox to the annotation tool sidebar, shown
when Elevation or Section is the active type; checking it and pressing
Add opens a dialog to optionally link the tag to a Bonsai drawing or
an external SVG reference
- The MANUAL_DRAWING_REFERENCE dropdown type is retained for backwards
compatibility; selecting it shows a style picker (Elevation/Section)
and the same linking dialog
- External-reference annotations are flagged with IsDocumentReference
in EPset_Annotation and linked to their IfcDocumentInformation via
IfcRelAssociatesDocument; drawing-reference annotations continue to
use IfcRelAssignsToProduct as before
- SVG export resolves the correct reference/sheet IDs for both link
types via get_reference_and_sheet_id_from_annotation
- Add IsDocumentReference to the EPset_Annotation pset template
Don't leave a broken repo if ifcmerge is misinstalled.
Fix bug where only local branches could be merged.
Fix gitch where merge commits were not considered relevant.
Add update callbacks to the relating_object and related_object
PointerProperties so that selecting an object via the eyedropper
in BIM_PT_aggregate immediately calls aggregate_assign_object
and closes the editing panel, removing the need to click the
checkmark button manually.
Generated with the assistance of an AI coding tool.
Adds `remove_coplanar_boundary_lines()` to operator.py (Bonsai uses this
path, not draw.py's main()). After `merge_linework_and_add_metadata()`
assigns material CSS classes, this post-processes the SVG to delete
projection line segments that appear in two or more adjacent, coplanar
elements with the same material and presentation style.
Key design decisions:
- Material identity: compared via sorted IFC material ID tuples from
`get_materials()`, not CSS class names — avoids false matches between
unrelated `material-null` elements.
- Presentation style identity: compared via IFC IfcPresentationStyle IDs
from `StyledByItem` on geometry representation items — handles elements
with no material but distinct visual styles.
- Physical adjacency: confirmed by a 3D shared-vertex test (tol=0.01 m)
after a quick AABB guard, rejecting elements whose 2D projections
overlap but sit at different depths.
- Coplanarity: determined by the dominant (largest-area) face normal of
each Blender mesh object — area-weighted averages are unreliable for
slabs whose equal top/bottom faces cancel out. Folded walls sharing an
edge but meeting at an angle are correctly rejected (normal dot ≪ 1.0).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Anthropic prompt caching (cache_control on system prompt and
tools) to reduce repeated token costs by ~90%
- Truncate large tool results in conversation history (2000 char cap)
to prevent context bloat from ifc_tree/ifc_select responses
- Add sliding window (40 messages) on conversation history, trimming
at user message boundaries to avoid breaking tool-call sequences
- Default "New IFC" button to IFC4X3 schema instead of IFC4
- Constrain ifc_new schema parameter with enum to prevent invalid
schema strings like "IFC4X3ADD2"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parse ifcmerge JSON output and display a per-conflict breakdown in the
panel when merge fails. Ctrl+click on the Merge button previews
conflicts without committing. Add SelectConflictEntity operator to
select and frame the conflicting object in the 3D viewport.
Generated with the assistance of an AI coding tool.
A degenerate edge (zero-length segment) caused an early `return`
of a tuple instead of continuing the loop, resulting in a
TypeError when snap.py iterated the result and tried to assign
`point["group"]` on a float.
Generated with the assistance of an AI coding tool.
Add a provider selector (OpenAI / Anthropic) to the ifcchat web UI,
allowing users to use their Anthropic API key with Claude models
instead of only OpenAI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a remote branch tip is checked out (resulting in detached HEAD),
the new-branch name field is now pre-filled with the local equivalent
of the remote branch name (generating a unique suffix if that name is
already taken), so the commit button is immediately usable.
See #7580
Generated with the assistance of an AI coding tool.
This version has some functional differences:
- Structured JSON error message instead of free text (on STDOUT not STDERR)
- New --prioritise-local flag to control which side wins in merge conflicts (not used by Bonsai yet)
- IfcLocalPlacement conflicts now auto-resolve instead of failing the merge (partial solution to #6885)
- Float values are normalised when comparing entities (workaround for #7696)
Include bonsai_git_branch in get_debug_info(). For dev environments
using the GitPython-based update_commit_data() path, the branch is
read from repo.active_branch.name. For built extensions, a 7777777
placeholder is replaced at build time via the Makefile, matching the
existing pattern for bonsai_commit_hash and bonsai_commit_date.
Generated with the assistance of an AI coding tool.
Move all business logic into bonsai core and tool. Performance fixes to
minimise file IO, various minor bug fixes and tests.
Generated with the assistance of an AI coding tool.
In order to fix error of the type:
| point, _ = cls.intersect_edge_region_border(
| ^^^^^^^^
| TypeError: cannot unpack non-iterable NoneType object
a tuple is expected.
Two bugs introduced in 31b571322:
- SnapObj assumed obj.data is always a Mesh; non-mesh
objects (empties, lights, etc.) have obj.data = None,
causing an AttributeError on obj.data.edges.
- view3d_utils was used but never imported.
Generated with the assistance of an AI coding tool.
Add --format ids to the ifcquery.rst format description and a new
"Scripting with ifcedit" section showing composition examples. Add
the foreach subcommand to ifcedit.rst with usage examples.
Add ifcquery, ifcedit and ifcmcp to the README contents table, the
Sphinx docs toctree and introduction utilities table. Add new .rst
pages for each package documenting subcommands, installation, usage,
and parameter types. Fix plot and render CLI examples in ifcquery
README to use -o/--out-format flags. Update ifcmcp README to use the
installed ifcmcp command rather than python3 -m ifcmcp.
Generated with the assistance of an AI coding tool.
These three packages were added to src/ but lacked the Makefile needed
by common.mk to build distribution wheels, and the GitHub Actions
workflow to publish them to PyPI.
Adds make dist / make test / make qa targets and ci-*-pypi.yaml
workflows matching the pattern used by ifcpatch, ifcclash, etc.
IfcRelConnectsPathElements has an optional ConnectionGeometry attribute for
recording the geometric cut-plane between adjacent elements, but there was
no way to set it via the API.
Generated with the assistance of an AI coding tool.
assign_product creates IfcRelAssignsToProduct linking a structural member to
a physical building element. assign_to_building creates IfcRelServicesBuildings
linking a structural analysis model to a building. add_topology_representation
creates IfcTopologyRepresentation for structural elements, inferring the
representation type from the item class.
Generated with the assistance of an AI coding tool.
Both attributes are required by the IFC schema but were not settable via
the API function. Add physical_or_virtual and internal_or_external parameters
with "NOTDEFINED" defaults for backward compatibility. Update Bonsai boundary
panel to expose both fields in the editor.
Generated with the assistance of an AI coding tool.
Add --format ids to ifcquery to output step IDs suitable for piping into
ifcedit parameters. Add ifcedit foreach to apply an operation to every
element in a query result. Extend clash and relations output so --format ids
extracts all involved element IDs, enabling one-liners like clash detection
piped directly into render.
Generated with the assistance of an AI coding tool.
Enhances the performance of mouse intersection checks for wireframe objects.
Details:
- Calculated the intersection with the mouse in 2D pixels first.
- Converted objects to a BVH Tree to reduce the number of edges checked against the mouse position.
mcp is an optional dependency so that the embedded API (embedded.py) can
be used from Pyodide without pulling in pydantic-core and the rest of the
MCP protocol stack, which may not be available in all WASM environments.
Fix#7034
bpy.ops.bim.extend_to_underside doesn't exist - the correct operator
name is bim.extend_walls_to_underside. The AttributeError killed the
entire panel draw, hiding mirror, align, aggregation, and QTO buttons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If a file had an IfcProjectedCRS without an IfcCoordinateOperation (or
vice versa), add_georeferencing would return early without creating the
missing entity. This caused edit_georeferencing to crash with IndexError.
Now detects the inconsistent state, cleans up, and recreates both.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ifcmcp is a new Model Context Protocol server that wraps ifcquery and ifcedit, holding an IFC model in memory across tool calls. It is the preferred way to interact with IFC models from AI assistants and MCP-compatible clients.
Setup:
claude mcp add --transport stdio ifc -- python3 -m ifcmcp
Session tools: ifc_load, ifc_save
Query tools: ifc_summary, ifc_tree, ifc_info, ifc_select, ifc_relations, ifc_clash, ifc_validate, ifc_schedule, ifc_cost, ifc_schema, ifc_contexts, ifc_materials, ifc_plot, ifc_render, ifc_shape, ifc_shape_list, ifc_shape_docs
Edit discovery: ifc_list, ifc_docs
Edit execution: ifc_edit, ifc_quantify
The model stays in memory between calls - ifc_edit does not auto-save; call ifc_save explicitly when done.
Depends on both ifcquery and ifcedit
Generated with the assistance of an AI coding tool.
ifcedit is a new command-line tool for executing ifcopenshell.api mutations from the shell. It wraps the entire API surface — any function callable via ifcopenshell.api can be invoked without writing Python.
Subcommands:
list [module] — list all API modules, or functions within a module
docs <module.function> — full documentation (params, types, descriptions)
run <file> <module.function> [--param value ...] — execute a mutation; overwrites input file by default, or use -o <output> to write elsewhere; --dry-run validates without executing
quantify list — list available QTO rules
quantify run <file> <rule> — run quantity take-off, writing IfcElementQuantity psets back to the file
Parameter coercion: entity references can be passed as step IDs (strings); lists, dicts, booleans, and None are handled automatically.
Usage:
python3 -m ifcedit run model.ifc root.remove_product --product 42
python3 -m ifcedit docs geometry.edit_object_placement
Generated with the assistance of an AI coding tool.
ifcquery is a new command-line tool for querying and inspecting IFC models. All output is JSON.
Subcommands:
summary — schema version, entity counts, project metadata
tree — full spatial hierarchy (Project → Site → Building → Storeys → Spaces → Elements)
info <id> — deep inspection of any entity by step ID (attributes, psets, placement matrix, type, material)
select <query> — filter elements using ifcopenshell selector syntax
relations <id> — relationships for an element; --traverse up walks to IfcProject
clash <id> — geometric intersection and clearance detection
validate — schema/constraint validation; --rules adds EXPRESS checks
schedule — work schedules with nested task trees
cost — cost schedules with nested cost item trees
schema <class> — IFC class documentation from the model's schema version
plot — SVG plan drawing
render — 3D geometry rendering
contexts — geometric representation contexts
materials — material assignments
Usage:
python3 -m ifcquery <file.ifc> <subcommand> [args]
Generated with the assistance of an AI coding tool.
* Add geometry.clip_solid API
* Add geometry.clip_solid_bounded API
* Add geometry.copy_representation API
Deep-copies the named representation from a source element to a target
element.
Generated with the assistance of an AI coding tool.
* Doc clarification for api.sequence.assign_process
* Doc clarification for api.geometry.edit_object_placement
* Doc clarification for api.feature.remove_feature
* Doc clarification for api.geometry.add_wall_representation clippings normal
* regenerate_wall_representation: document BBIM_Boolean preservation requirement
Generated with the assistance of an AI coding tool.
* shape_builder: complete docstrings and return type annotations
* shape_builder: warn about mixed item types in get_representation
* shape_builder: fix half_space_solid agreement_flag docstring
Generated with the assistance of an AI coding tool.
Add tests for all four covering generation operators: flooring/ceiling
from walls and flooring/ceiling from cursor. Previously only flooring
from walls was tested.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The covering tool used bmesh as an intermediate and relied on
type.assign_type post-listeners (removed in 44a52863a) to generate
the IfcExtrudedAreaSolid body. With those listeners gone, coverings
had no body representation and assign_swept_area_outer_curve crashed.
Build covering representations from scratch using ShapeBuilder, reading
the extrusion depth from the type's IfcMaterialLayerSet. Also replace
bpy.ops.bim.assign_class with bonsai.core.root.assign_class using
should_add_representation=False, consistent with the space fix.
Refactored shared coordinate-conversion and extrusion-building logic
into get_2d_vertices_from_polygon and set_extrusion_representation_from_polygon,
used by both space and covering code paths. Removed all bmesh-dependent
dead code from the spatial tool.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Space regeneration was only updating the Blender mesh and marking the
object as edited, but the IFC representation was never synced on save.
Replace the bmesh-based approach with ShapeBuilder to write geometry
directly to IFC as an IfcExtrudedAreaSolid, then reload via
switch_representation. This applies to both new space creation and
existing space regeneration.
Also changes assign_ifcspace_class_to_obj to call
bonsai.core.root.assign_class directly with
should_add_representation=False instead of bpy.ops.bim.assign_class.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The nest assign_object API now removes existing spatial containment and
aggregate relationships before creating the nest, matching the behavior
documented in its docstring and consistent with aggregate.assign_object.
Fix#7248
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Walk up the full hierarchy via get_parent() in can_nest() and
can_aggregate() to reject assignments that would create a cycle.
Also reject self-assignment.
Fix#7248
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After assigning an IFC class and undoing, msgbus subscriptions registered
with the old Python object wrapper survived (PERSISTENT flag) but could
not be cleared because: (1) rollback_link_element looked up objects by
their post-link name which no longer exists after undo, and (2) the
per-object clear_by_owner calls in rebuild_element_maps used new Python
wrappers that didn't match the old subscription owners.
Fix by using a dedicated stable object (object_subscription_owner) as
the msgbus owner for all per-object subscriptions, allowing
rebuild_element_maps to clear all stale subscriptions in one call
regardless of Python wrapper identity changes during undo/redo.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `is not ...` was being captured by the walrus assignment due to
missing parentheses, causing the condition to always evaluate incorrectly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
remove_representation_item now returns early if removing the item would
leave Items empty. edit_text_literals returns early on empty attributes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
add_boolean was removing second operands from ALL IfcShapeRepresentations
that referenced them, which could corrupt unrelated shapes and leave
representations with empty Items (bug #7803).
The API no longer modifies Items — callers manage this explicitly.
validate_type and Bonsai's AddBoolean operator now handle their own
item removal scoped to the correct representation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
remove_deep is deprecated and can silently delete elements still in use.
remove_deep2 requires zero inverses before removal, making it safer.
Also fixes a double-removal bug in remove_grid_axis and prevents
removing the last prop template from a pset template.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests using custom first/last tread runs were not setting
custom_tread_lock=False, so the custom values were silently ignored
since 8f7cf76d9 introduced the lock gate in the calculation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SyntaxWarning: invalid escape sequence '\/' at line 312.
`\/` in a plain string is treated as `/` by accident; replaced with raw string r"..." to be explicit.
- `function_item`, `tags` added in df7318973
- MakeVolume added in c385b93, ignore as all other conversion settings
- moved `SeparateZUpNode` ignore to the other geom serializer settings
Mentioned in https://github.com/IfcOpenShell/IfcOpenShell/issues/7667#issuecomment-4076645173
Traceback:
```
Traceback (most recent call last):
File "\bonsai\bim\module\patch\prop.py", line 55, in get_ifcpatch_recipes
docs = ifcpatch.extract_docs(f, "Patcher", "__init__", ("src", "file", "logger", "args"))
File "\ifcpatch\__init__.py", line 168, in extract_docs
spec.loader.exec_module(submodule)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "<frozen importlib._bootstrap_external>", line 1027, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "\ifcpatch/recipes/FixRevit2025TINs.py", line 31, in <module>
class Patcher:
...<509 lines>...
return co / self.unit_scale
File "\ifcpatch/recipes/FixRevit2025TINs.py", line 168, in Patcher
def create_edges(self, obj: bpy.types.Object) -> None:
^^^
NameError: name 'bpy' is not defined
File "\bonsai\bim\module\patch\prop.py", line 43, in get_ifcpatch_recipes
```
To fix warnings below:
```
<python-input-1>:1: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
```
Replace plain enum dropdowns with prop_with_search in the AddAnnotation
and AssignManualDrawingReference operator dialogs, making it easier to
locate a target drawing when many drawings exist in the project.
Generated with the assistance of an AI coding tool.
Use get_default_annotation_matrix() for manual SECTION annotations so
the object is placed in the camera's annotation plane with the correct
rotation, matching how auto-generated section annotations are created.
Without this, annotations placed in elevation or section camera views
had identity rotation, causing the IFC representation to be in the wrong
coordinate system and failing to tessellate.
Also guard draw_edit_object_interface against non-IFC active objects to
prevent AssertionError during toolbar redraws, and skip IFC item edit
mode for ELEVATION/SECTION annotation types on placement.
The elevation tag's local -Z axis is intentionally parallel to its
drawing camera's view direction, making screen-space projection of that
axis always degenerate (zero XY delta). Fall through to the tag's local
+X axis, which lies in the camera plane and rotates correctly as the
user adjusts the tag's orientation. Also fix a zero-length vector crash
in svgwriter when the same degenerate case occurs during SVG export.
Adds MANUAL_DRAWING_REFERENCE to the annotation type dropdown.
Selecting it shows a dialog to choose elevation or section and
optionally assign a target drawing before placement. Tags are
protected from regeneration via EPset_Annotation.IsManualDrawingReference.
Generated with the assistance of an AI coding tool.
Adds operator to link a manual drawing reference tag to a target
drawing via IfcRelAssignsToProduct, with a pre-populated dialog
and immediate Properties panel refresh on confirm.
Generated with the assistance of an AI coding tool.
Adds operator to place manual elevation/section drawing reference
tags that survive drawing regeneration. Includes core function,
tool methods, type-selection dialog, default horizontal rotation
for elevation tags, and SVG null guard for unassigned references.
Generated with the assistance of an AI coding tool.
Introduces a boolean pset property that marks an ELEVATION or
SECTION annotation as manually placed, exempting it from
automatic deletion or regeneration during drawing sync.
Generated with the assistance of an AI coding tool.
Closes#7787: Previously bim.assign_selected_as_product required exactly
2 objects. With multiple annotations referencing the same
product, users had to repeat the operation once per
annotation. Now any number of IfcAnnotations can be selected
alongside a single product object and all are assigned in
one operation and one undo step.
Generated with the assistance of an AI coding tool.
* buildingSMART Data Dictionary module: added textfield to change data dictionary url
* moved change of bsdd baseurl change to addon settings
* Receiving Psets from other dictionary sources has been made available by dynamizing the identifier_url using the client baseurl
* Remove unnecessary blank lines in prop.py
* Remove unused import of bsdd module
* Fix#7712 - global alignment controls now affects all literals
* Fix#7760 - goodbye 3x3 box alignment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous logic always skipped the first point. Instead, it should only
skip when actually closing a loop (i.e. >= 3 points).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Since we added more data to debug info in fcf5614 Fatal Error itself started to fail and was never displayed due some props being inaccessible during load, should be fixed now.
Possible error that were fixed:
```
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 328, in <module>
print(format_debug_info(get_debug_info()))
~~~~~~~~~~~~~~^^
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 117, in get_debug_info
if bpy.data.is_saved:
^^^^^^^^^^^^^^^^^
AttributeError: '_RestrictData' object has no attribute 'is_saved'
Traceback (most recent call last):
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 366, in draw
info = get_debug_info()
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 152, in get_debug_info
bim_props = tool.Blender.get_bim_props()
^^^^
NameError: name 'tool' is not defined. Did you mean: 'bool'?
Traceback (most recent call last):
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 366, in draw
info = get_debug_info()
File "\Blender\5.1\extensions\raw_githubusercontent_com\bonsai\__init__.py", line 141, in get_debug_info
import bonsai.tool as tool
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\__init__.py", line 355, in <module>
print(format_debug_info(get_debug_info()))
~~~~~~~~~~~~~~^^
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\__init__.py", line 141, in get_debug_info
import bonsai.tool as tool
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\tool\__init__.py", line 23, in <module>
from bonsai.tool.attribute import Attribute
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\tool\attribute.py", line 31, in <module>
import bonsai.bim.helper as helper
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\__init__.py", line 28, in <module>
from . import handler, operator, prop, ui
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\handler.py", line 36, in <module>
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\module\aggregate\__init__.py", line 21, in <module>
from . import operator, prop, ui
File "\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\bim\module\aggregate\operator.py", line 32, in <module>
class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
^^^^^^^^
AttributeError: partially initialized module 'bonsai.tool' from '\Blender\5.1\extensions\.local\lib\python3.13\site-packages\bonsai\tool\__init__.py' has no attribute 'Ifc' (most likely due to a circular import)
```
Blender doesn't have it's own quick favorites manager and working with them can be not very flexible - you can add them in context menu and remove them from Quick Favorites menu. But you can't reorder them, you can't rename them and you can't even add a new button to favorites if it's not added by some addon in the UI.
Have been stumbling upon this for awhile and decided to create an experimental manager UI for this. Things it can do:
- help user create a button with any operator in Blender and properties they prefer to then save it Quick Favorites. Which seems can be very useful in Bonsai, since you can create separate buttons for all kinds of selectors expressions, class assignment or other operators.
- it can import quick favorites from user's actual current quick favorites, so they can just modify them a bit, reorder, rename and then add them again.
- Since quick favorites are not exposed to Python API in Blender, we're using a very hacky way to retrieve them from Blender and don't provide our own buttons for adding and removing quick favorites, as it may be dangerous and even more hacky in implementation. So the workflow for user is to either generate some buttons and add them to quick favorites using Manager or to import it's own quick favorites, then change them how they like, then remove quick favorites using usual quick favorites menu and then add new button one by one.
Small demo - https://files.catbox.moe/vyffp6.mp4
Refactored methods for accessing objects in linked models and added a simple operator to select object in linked model by providing guid.
A quick demo - https://files.catbox.moe/sjjw37.mp4
* Fix#7718: Fix FallDecorator label calculation for all slope annotation types
- Fix wrong dict key type in decoration.py: DecoratorData.data["fall"] is
keyed by obj.name (str) but was looked up with obj (Object), causing
object_type to always be None
- Apply obj.matrix_world transform to spline points before computing rise/run
in both decoration.py and svgwriter.py; local coordinates have Z=0 for flat
annotations, world coordinates correctly reflect elevation change
- Use hypotenuse (segment_length) instead of run as the denominator for
SLOPE_FRACTION label display
Generated with the assistance of an AI coding tool.
* docs: add BonsaiPR bleeding edge installation section
Add new section to installation.rst documenting the BonsaiPR
community build, including why it exists, how the automated
PR-merging system works, installation steps with automated
updates, manual installation, and the PR workflow for
contributors.
Generated with the assistance of an AI coding tool.
* whoops
Linking flags were missing for `MODULE` type libraries, example warning: `IfcPythonPYTHON_wrap.obj : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance`
Which is useful when debugging and calling tools.ps1 directly - less thing to modify to make it work.
Also replaced `exit 0` with `return`, so it would be possible to reuse functions inside `tools.ps1`
To avoid running in a situation when some builds are using one tag and some are using another and then unstable repo script fails to find builds for some platforms.
Objects with hide_select=True could not be selected during
isolation, causing hide_view_set to incorrectly hide them.
Objects with hide_viewport=True had their H-key hide state
modified as a side effect of hide_view_clear/hide_view_set.
Both are now left unaffected by bim.activate_drawing.
Generated with the assistance of an AI coding tool.
Use EPset_Parametric.LayerSetDirection exclusively to
determine horizontal vs vertical layer rendering in type
thumbnails, rather than hardcoding IfcSlabType checks.
Also fix line drawing to use the is_horizontal flag
consistently.
Generated with the assistance of an AI coding tool.
Fix Sphinx docs: replace csv-table with list-table for formatting functions
The documentation table of formatting/query functions was not rendering
because `.. csv-table::` requires strict RFC4180 CSV escaping. The table
contains nested quotes, inch marks (e.g. `3' - 0"`), backticks, and code
examples, which cause the CSV parser in docutils to treat rows as malformed
and drop the entire directive.
Replaced the directive with `.. list-table::`, which parses reStructuredText
instead of CSV and safely supports inline code, quotes, and multi-line cells.
Also moved the examples text outside the directive block and ensured a blank
line after the table so Sphinx does not interpret following paragraphs as
table rows.
No content changes — documentation now renders correctly.
Generated with the assistance of an AI coding tool.
* Enhance AddReferenceImage operator to use file browser instead of independent popup dialogue
* Fix dimensions assertion in TestAddReferenceImage
* Remove error in return in _execute (it is not execute)
* Add IFC2X3 support to AddReferenceImage
* Adde unit="LENGTH" to the x/y properties (every length dimension everywhere in the UI is in project length units. No need to say it explicitly)
* Manually create the texture always, not just for IFC2X3
* Add poll method to AddReferenceImage operator to check for loaded IFC project
* Refactor AddReferenceImage to add representation manually following pattern in root/operator.py's bim.add_element
* Improve File explorer options between new and select from existing project Ifc Reference Images
* Refactor get_existing_reference_images to use selector for filtering image annotations
* No extra args needed after should_add_representation is False
* Doing clean=True deletes everything
* Don't manually add geometry and materials, don't call bpy.ops. Only create IFC data, then use preexisting loading functions to create geometry.
* Black formatting, also now we can start to remove this operator as it becomes obsolete
* Consolidate duplicate UV generation into Loader.load_generated_uv_map
Replace 3 identical XY-UV baking blocks (create_object IMAGE,
bm_add_image_plane, ImageScalingTool) with a single reusable
classmethod in tool.Loader.
* Fix IFC4 texture display in Solid viewport Texture mode
IFC4 IfcTextureCoordinateGenerator Mode=COORD is used, load_texture_maps
falls back to load_generated_uv_map to bake XY-UV data onto the mesh.
* Fix IFC2X3 texture display
* This looks wrong
* Remove legacy override image feature, because we now have a proper styles and texture manager
* Remove legacy override existing image element, because we now have a dedicated styles texture manager
* Remove unnecessary roundtrip to bmesh and mesh
---------
Co-authored-by: Dion Moult <dion@thinkmoult.com>
Guidelines for external contributors using AI coding tools,
covering licensing, AI disclosure requirements, PR scope,
commit style, code formatting, and testing expectations.
Generated with the assistance of an AI coding tool.
Previously, there was a dance between invoke, execute, and draw. This
can probably be resolved, but is a high-risk for undo bugs. This
simplifies the logic flow to just a traditional _invoke -> _execute.
I add a new feature test to at least make sure it does something, and
this also fixes the segfault in tool tests as it no longer requires the
launching of the file browser.
In my previous commit, I mistakenly believed that there was an API change from
snap_angle_increment to snap_angle_increment_3d
But since the feature was introduced in blender 4.2 the setting is called
snap_angle_increment_3d.
Previously it was implemented inline. This now implements it as a
tool.Blender function with tests. Also the previous tests didn't
actually run and weren't actually testing any tools despite being in a
tool tests.
IfcSectionedSurface and IfcSectionedSolidHorizontal both of CrossSectionPositions attributes which are lists of IfcAxis2PlacementLinear. The implementation of each class used its own bespoke mapping of IfcAxis2PlacementLinear, which were identical to each other and slightly different than IfcAxis2PlacementLinear. Now the two sectioned classes use the one and only mapping for IfcAxis2PlacementLinear
* Linked IFC projects enhancement (multiple links to same project file)
- Implement link management system using UUIDs as identifiers to support multiple links to the same IFC file
- Add georeferencing compatibility detection and UI display (NONE, NOT_COMPATIBLE, PARTIAL_COMPATIBLE, FULL_COMPATIBLE)
- Support for duplicate link creation with Shift+D shortcut and automatic position offset
- Add false origin and project north calculation from 3D cursor for MANUAL mode
- Only store one cache per file, regardless of the amount of links
- Prevent duplicate links based on filepath and position comparison
- Improve error handling for missing files and loading failures
- Update tests
* Remove duplicate georef UI
I try to avoid duplicate UI (especially for one that can be as
sophisticated as georef - e.g. missing is WCS) as it means double the
code, double the tests, potential user confusion. BTW the note about
vertical datum isn't quite accurate as it may be included in the CRS
definition so vertical datum is optional.
* Remove depsgraph_update_post handler for update_link_ui_on_transform as per core developer feedback
* Move get_projected_crs to geolocation module
* Refactor get_projected_crs to simplify as per core developer feedback
* Remove unused import of bonsai.tool from project module
* Use IfcDocumentInformation per linked file and IfcDocumentReference for locaiton information
* Refactor SaveBlendMetadataFile operator to remove try-except blocks and remove linked projects collections since they are recreated by bonsai
* Cleanup removing empty collection instances for linked models in metadata.blend file and call determine_georeferencing_compatibility on link reload
* Add locking mechanism for linked models and update UI to reflect lock status
* Update logic that track IFC to execute_ifc_duplicate_operator instead of having it in execute() which does not track IFC undo/redo
* Refactor link handling to use get_link_empty_handle and set_link_empty_handle methods which in turn use the standard blender-ifc integrations patters (tool.Ifc.get_object(doc_reference) and tool.Ifc.link(doc_reference, empty_handle)
* remove operator.DuplicateLink and move it to tool.Project.duplicate_link()
* Refactor link handling to use sequential identifiers (no need for STEP ID DocRef)
* Refactor IFC linking logic to handle cases without a parent IFC file loaded. Firts link flase origin becomes parent origin
* Lock should not affect selection.
This makes it consistent with grid / spatial lock, and also toggle
selectability is already implemented.
* Remove unnecessary check for loaded library as Blender seems to do this internally already
* Rename util to get_crs because in IFC4X3 you can also have geographic CRS not just projected
* Remove unnecessary call to determine_georeferencing_compatibility
This function is already always called prior to calculate_link_position
so shouldn't be called here. It's also a very expensive function: as it
currently stands, just to link a single IFC, ifcopenshell.open() is
called 3 times. This reduces it to 2.
* Store CRS as metadata for linked models, and compare metadata when indicating georeferencing compatibility
Previously, to check georeferencing compatibility, ifcopenshell.open()
was used. When linking large models, this adds considerable time and
memory usage. This instead captures the georef as standard metadata in
our .cache.json. This now reduces the ifcopenshell.open() calls back
down to only 1 as necessary (see previous commit).
* Use link index instead of link name to fetch link collection item
Link name runs into issues with name uniqueness. This is why you created
a function for "get next link ID". After this refactoring, we can no
longer worry about uniqueness and that function may be removed.
* Simplify reloadlink into just unload and reload (with cache disabled)
This function should not be responsible for editing any data.
* Remove unnecessary get_next_link_id as names no longer need uniqueness
This now frees up the name variable to track a more meaningful, human
name like IfcDocumentInformation's Name attribute.
* Rewrite get / set link_empty_handle to just use the link directly
This prevents needless logic to fetch the link and also removes issues
related to duplicate names.
* Temporarily remove logic in prop callback
Right now, pretty much all the logic is done in a prop callback. In
general logic in prop callbacks should be minimised, since it's hard to
test and easily triggered as a domino effect of another change, and may
also impact undo/redo.
* Remove code that unnecessarily removes cache
This code removes cache, which means any project unlinking an IFC auto
clears the cache for any other project which doesn't make sense, and
also breaks the ability to readd it quickly.
* Rewrite link, unlink, load, and unload IFC
There were a few issues tackled here:
- Operators that change any IFC data must use tool.Ifc.Operator and
_execute, otherwise undo/redo will break. That's one of the risks of
using prop callbacks, as it is not explicit when an IFC edit happens.
- The usage of IfcDocumentReference was not correct. The Location
should store the URL, _not_ the position. The position should be in the
Identification attribute.
- The URL was stored in IfcDocumentInformation location, which does not
work in IFC2X3. There are a few changes here to make it IFC2X3
compatible.
- Generally move logic in operators, not prop callback.
* Remove restriction around manual mode.
Users should be able to use manual mode if they want.
* Restore AUTOMATIC mode to identical behaviour to file open
This is the first step to reusing cache files agnostic of the host.
* Revert tests for a fresh start for updating tests
* Revert "test_feature - clean up .ifc.cache. files after test was executed"
This reverts commit 99ae768ddf.
* Update tests and reimplement calculations for matrix of empty handle
Previously, the empty would always be placed at the origin, unless a
"position" offset was present. This is a problem, because the "position"
is simply a local offset relative to the Blender cache! If the cache was
regenerated, the offsets would be outdated. Also, the cache appeared in
different locations depending on the false origin mode, so the offset
would mean different things to different people.
Instead, a more robust method is:
1. When you link a file, a Blender cache is generated. The Blender
origin of this cache is arbitrary! It depends on the user's false origin
mode and is purely a Blender session specific thing.
2. When you load a link, a link is _always_ loaded into the correct
location with regards to IFC global coordinates. All math is done from
the perspective of IFC.
3. If you choose to transform (move / rotate / scale!?) this link from
its correct location, that gets recorded as a 4x4 transformation matrix.
Note: I haven't implemented this properly yet.
Tests all pass, with a minor modification to the new behaviour that
false origin mode now won't affect the location it ends up in, only the
generation of the cache.
* Remove arbitrary convention around display name
Not needed anymore now that A/M/D is a detail and not significant on
actual coordinates, and also that the UUID is no longer needed.
* Simplify implementation of loading linked models when opening an IFC
* Move link matrix calculation from operator to tool for reuse
* Implement editing link location and calculation of transformation matrix
I changed my mind on the is_locked thing, since it isn't clear to the
user that locking need to be done to save changes.
* Remove old is_locked, prop update callback no longer needed (dedicated operator instead), remove old calculation code
* Simplify code related to placed_as_per_georef
* For now, simple skip for duplicate / delete
IMO duplicate / delete / move a link are very rare and explicit
operations.
* Update tests
* Remove host_model coordinate data as cache is no longer host model dependent
* Move icons outside list because there are too many
* Minor tweaks
---------
Co-authored-by: Dion Moult <dionmoult@gmail.com>
Co-authored-by: Dion Moult <dion@thinkmoult.com>
IfcOpenshell read file, and write file without changes. This round trip introduces truncation noise. It should not hurt to increase the precision to keep this clean.
Replace hardcoded 5-degree angle snapping with Blender's
snap_angle_increment setting in create_wall_from_2_points()
and create_profile_from_2_points().
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace hardcoded 5-degree angle snapping with Blender's
snap_angle_increment setting in handle_lock_axis() for:
- Initial angle rounding when locking axis (A key)
- Angle rounding and increments on Shift+Wheel scroll
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace hardcoded 5-degree angle snapping with Blender's
snap_angle_increment setting in calculate_distance_and_angle().
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This function retrieves the angle snap increment from Blender's
tool_settings.snap_angle_increment property, which was added in
Blender 4.2. This allows users to configure the angle snap value
through Blender's native UI instead of using hardcoded values.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| [ifcconvert](https://docs.ifcopenshell.org/ifcconvert.html) | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcconvert/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcconvert&expanded=true)
| [ifccsv](https://docs.ifcopenshell.org/ifccsv.html) | Library and CLI app to export and import schedules from IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifccsv/) |
| [ifcedit](https://docs.ifcopenshell.org/ifcedit.html) | CLI wrapper for ifcopenshell.api IFC model mutation functions | LGPL-3.0-or-later | [](https://pypi.org/project/ifcedit/) |
| [ifcfm](https://docs.ifcopenshell.org/ifcfm.html) | Extract IFC data for FM handover requirements | LGPL-3.0-or-later | [](https://pypi.org/project/ifcfm/) |
| [ifcmax](https://docs.ifcopenshell.org/ifcmax.html) | Historic extension for IFC support in 3DS Max | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcmax.html)
| [ifcmcp](https://docs.ifcopenshell.org/ifcmcp.html) | MCP server for querying and editing IFC building models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcopenshell-mcp/) |
#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','Classes separated by spaces that end up in classes for this element in svg. Can be used to specify the text font size: small - 1.8mm; regular - 2.5mm; large - 3.5mm; header - 5mm; title - 7mm. By default regular size is used.',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#25=IFCSIMPLEPROPERTYTEMPLATE('1rL2AbQsXD8RbpoWH5pYOV',$,'ShowDescriptionOnly','Hide the measurement values and show only annotation description',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#26=IFCSIMPLEPROPERTYTEMPLATE('0SVyOfB0rC2xNfdRYf3XvY',$,'SuppressZeroInches','Suppress 0 inch values in dimension annotation text (for example: 12'' - 0" -> 12'')',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#27=IFCSIMPLEPROPERTYTEMPLATE('2bUmj458PBqPAtUoI3MXsb',$,'TextPrefix','Text to add before annotation measurement value',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#34=IFCSIMPLEPROPERTYTEMPLATE('1Kx4Pm9nR8vBwZqTs2uYeL',$,'Separator','Characters placed between multiple dimension values when CustomUnit has more than one unit selected (default: '' / '')',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#35=IFCSIMPLEPROPERTYTEMPLATE('3Nf6Qs1mT0pWxBuCvDyEzA',$,'SuppressZeroFeet','Suppress 0 feet in dimension annotation text (for example: 0'' - 3 1/2" -> 3 1/2")',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#36=IFCSIMPLEPROPERTYTEMPLATE('2Rg7Hn5jK4mLpNqOsVwXtY',$,'IsOrdinate','Show accumulated distance from the first vertex instead of individual segment lengths',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#37=IFCSIMPLEPROPERTYTEMPLATE('1XpRnKoT2sGuW7vYcZaMqb',$,'Anchors','JSON array of parametric anchor descriptors — one per polyline vertex. Each entry: {"guid": str|null, "type": "FACE"|"CIRCLE_CENTER"|"WORLD", "addr": {...}, "hint": [x,y,z]|null, "pt": [x,y,z]}',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#38=IFCSIMPLEPROPERTYTEMPLATE('2YqSmLoU3tHvX8wZdaNrjc',$,'MeasureAxis','Axis along which distances are projected: X | Y | Z | TRUE | PERPENDICULAR',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#39=IFCSIMPLEPROPERTYTEMPLATE('3Ny31Go6T5Z9fh8j4yQC0p',$,'ForcePerpendicularToFace','When enabled the polyline is constrained to follow the face normal of the first anchor vertex so the dimension measures straight-line distance perpendicular to that face',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#40=IFCSIMPLEPROPERTYTEMPLATE('1LoNpKqR3sTuVwXyZaBcDe',$,'LinePosition','Absolute world-space coordinate (metres) of the dimension line along the horizontal offset axis (perpendicular to the dimension direction). When set, the dimension line is held at this fixed global position even if the measured geometry moves. When absent the line sits at the anchor points.',.P_SINGLEVALUE.,'IfcLengthMeasure',$,$,$,$,$,.READWRITE.);
#41=IFCSIMPLEPROPERTYTEMPLATE('0FauxIsAnnotFaux0001aB',$,'IsManualDrawingReference','Marks this annotation as a manually placed drawing reference, exempt from automatic drawing regeneration.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#42=IFCSIMPLEPROPERTYTEMPLATE('0FauxIsDocRefFaux001aB',$,'IsDocumentReference','Marks this annotation as pointing to an external document reference (not a Bonsai drawing camera).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
# Properties have many different data types. We won't use all of them in this
# demo module, but this is a list for your reference.
frombpy.propsimport(
BoolProperty,
CollectionProperty,
EnumProperty,
FloatProperty,
FloatVectorProperty,
IntProperty,
PointerProperty,
StringProperty,
)
frombpy.typesimportPropertyGroup
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.