Commit Graph

19153 Commits

Author SHA1 Message Date
Petru Conduraru 09313eac40 Bonsai: tolerate an already-removed mesh/curve/camera data-block in delete_data (#5793)
tool.Geometry.delete_data() only guarded against TypeError (passing the
wrong bpy.data collection). If the data-block had already been freed
elsewhere between the earlier has_data_users() check and this deferred
cleanup call (observed after a busy sequence of switch_representation
calls followed by remove_representation), bpy.data.meshes.remove()
raises an uncaught ReferenceError, which aborts the whole IFC operator
transaction with a traceback matching the one reported in #5793.

Catch ReferenceError too and treat an already-removed data-block as a
no-op, since the desired end state (the data being gone) is already met.

Generated with the assistance of an AI coding tool.
2026-07-16 14:41:22 +03:00
Andrej730 821cf7b671 ifcparse: replace std::to_chars to fix mac build (ee2b357d7) 2026-07-16 11:57:37 +05:00
Andrej730 8718db63da pyproject: flip ty rules to error-by-default, review all new rules added since version bump 2026-07-15 19:20:17 +05:00
Andrej730 71c6950a59 ifcclash: fix use of undefined clash["position"]
It's an artifact from the old hppfcl clasher dropped in 18c38b312
2026-07-15 19:20:17 +05:00
Andrej730 5a8aa0a659 bsdd: raise informative HTTPError
Previously we were just passing `.json()` which allowed too many request error slip in to later occur as missing attributes on the dictionaries.
2026-07-15 19:20:17 +05:00
Andrej730 f4526d152f bsdd: warn about include_class_properties deprecation
See https://github.com/buildingSMART/bSDD/issues/149
2026-07-15 19:20:17 +05:00
Andrej730 a7a7edfd27 bsdd: fix test_get_class_relations
`classRelations` doesn't exist on `ClassPropertiesContractV1`, probably was just a typo.
2026-07-15 19:20:17 +05:00
Andrej730 78712ead98 misc: more readable poll error for import_quick_favorites 2026-07-14 19:57:31 +05:00
Andrej730 6efb8a4373 misc: add Blender 5.2 offset for Quick Favorites user_menus 2026-07-14 19:57:31 +05:00
Andrej730 9e25c12b16 surveyor: drop never used dead code
Surveyor test was failing because `get_z_rotation` and `set_z_rotation` were not implemented.
The code was added in 230cbe1fd8, but it was never used.
2026-07-14 19:57:31 +05:00
Andrej730 0968d06780 Deduplicate code by reusing tool.document 2026-07-14 19:57:31 +05:00
Andrej730 3a8619726b core.drawing: deduplicate code, fix test
Core test was trying to access actual ifc data (`ifc.get().by_type("IfcGroup")` and was failing.
2026-07-14 19:57:31 +05:00
Andrej730 1b1da821f1 file.get_inverse: document with_attribute_indices overload 2026-07-14 19:29:34 +05:00
Andrej730 d772b24bd6 geometry.add_boolean: fix typo in the class name
🫣🫣
2026-07-14 19:29:34 +05:00
Andrej730 549f81a76e ios pyproject: add networkx stubs as dev dependency 2026-07-14 19:29:34 +05:00
Andrej730 e5c7206a37 express: fix use of non-existent ifcexpressparser
`express.bnf` arg wasn't handled since d506ad77b
`ifcexpressparser` waa moved inside `ifcopenshell-python` awhile ago too
2026-07-14 19:29:34 +05:00
Andrej730 5b968d5c75 pyparsing: fix using deprecated aliases
Deprecated since pyparsing 3.0 and produce runtime warnings. New function work exactly the same, except their name is pep8 compatible.
2026-07-14 19:29:34 +05:00
Andrej730 6075187720 express_parser: fix non-idempotent results 2026-07-14 18:42:12 +05:00
Andrej730 92528d84cd express: clean up trailing spaces 2026-07-14 18:42:12 +05:00
Andrej730 3955718145 express: update transpiled express rules using latest Python's AST
AST parser has changed a bit and there are some minor differences in the .py output. Updating files just to avoid seeing these diffs when rerunning rule compiler.
2026-07-14 18:42:12 +05:00
Andrej730 501246cd0b rule_compiler: fix error running on Python 3.14
Example error:
```
    ast.Str(s=node.attr),
    ^^^^^^^
AttributeError: module 'ast' has no attribute 'Str'
```

`ast.Str` was deprecated since 3.8 and was removed in 3.14, see https://docs.python.org/3/whatsnew/3.14.html#id9
2026-07-14 18:42:12 +05:00
Andrej730 fb8b2ee878 bonsai pyproject: move pytest deps to requirements-dev.txt 2026-07-14 18:42:05 +05:00
Andrej730 8a00ce84cc geom/main.py: fix ty complaint 2026-07-14 14:56:06 +05:00
Andrej730 4032bbbd17 entity_instance.py: fix oveloads signatures (f93d79dc)
Without `/` overload implies that it also accepts kw args, while the implementation signature doesn't support them.
2026-07-14 14:56:06 +05:00
Andrej730 3e9ef82448 maintenance.rst: move pyver matrix to bundled Python version section 2026-07-14 14:56:06 +05:00
Andrej730 56ed79792e bonsai tests: fix test_failed_to_load_returns_only_base_keys (fdb2947) 2026-07-14 14:56:06 +05:00
Andrej730 9c91727402 express: drop Python 2 fallbacks 2026-07-14 14:56:06 +05:00
Andrej730 d30c25010c dev_environment.py: detect Python 3.13 on any Blender 5.1+ 2026-07-14 12:18:00 +05:00
Andrej730 97d1a6e488 dev_environment.py: add shebang and make executable 2026-07-14 12:12:54 +05:00
Petru Conduraru 780739719f Bonsai docs: fix version switcher scheme mismatch (http vs https)
versionURLs in brand.html used http:// while the docs sites are
served over https://, so currentURL.includes(url) never matched and
the <select> never reflected/switched to Unstable. Fixes #8023.

Generated with the assistance of an AI coding tool.
2026-07-13 20:44:19 +10:00
Petru Conduraru 4a717ca7ff Fix ci-bonsai-daily: reconnect Cost/IfcGit tool interfaces (TestImplementsTool)
Two TestImplementsTool failures on v0.8.0:

- test_cost.py: Cost could not be instantiated because
  core.tool.Cost declared abstract get_direct_cost_item_products, which
  tool.cost.Cost never implements. The method is dead (zero call sites;
  get_cost_item_products(is_deep=False) already covers the 'direct'
  case), so remove the abstract declaration.
- test_ifcgit.py: tool.ifcgit.IfcGit was not declared as a subclass of
  its core.tool.IfcGit interface (unlike every sibling tool class), so
  the isinstance check failed. Add the base class (and the
  bonsai.core.tool import it needs). All 50 interface methods are
  already implemented on the concrete class.

No behaviour change. Verified in headless Blender: isinstance(Cost(), core.tool.Cost) and isinstance(IfcGit(), core.tool.IfcGit) both True (were TypeError / False); repo abstract-vs-impl diff confirms all IfcGit abstracts are implemented.

This change was made with the assistance of an AI tool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 20:43:25 +10:00
Petru Conduraru 5a831e3d21 Fix ci-lint: black-format selector.py
black (the version CI's psf/black@stable resolves to) flags three spots
in util/selector.py: the chained .replace() in FormatTransformer.number,
the suppress_zero_inches kwarg in format_length, and the long
`elif key in (...) and hasattr(...)` placement-key tuple in
set_element_value. Reformat all three to black's multi-line style.
Formatting only, no behavioural change (all keys preserved).

This change was made with the assistance of an AI tool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 20:42:25 +10:00
Petru Conduraru d30286225c Bonsai: deterministic annotation order in generated drawing SVGs (#6608)
generate_annotation built the annotation list from a set union and sorted it by
ZIndex and TEXT-ness only. Annotations that tied on that key kept set iteration
order, which follows entity hash (step id plus the process memory address), so
the order of tied annotations (for example a label and its background fill)
shuffled between Blender restarts and flipped their draw order.

Add the stable IFC step id as a final tiebreaker so the order is total and
session independent. Behavior preserving, no z-layer semantics changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:41:41 +10:00
Bruno Postle 65695fb878 ifcedit: fix Optional[entity_instance] coercion crash on native JSON values
coerce_value assumed value_str was always a CLI string, but ifcmcp
passes JSON-decoded native types (int, None) straight through. Guard
the Union/Optional "none" check so it only calls .lower() on strings,
and handle native None explicitly.
2026-07-13 08:55:43 +01:00
Bruno Postle ab15750747 ifcedit: include IfcSpace in default QTO element scope
IfcSpace is not a subtype of IfcElement, so quantify.run_quantify()'s
default selector silently skipped all spaces, reporting
elements_quantified: 0 with no error or warning.

Generated with the assistance of an AI coding tool.
2026-07-13 09:45:25 +01:00
Petru Conduraru 694a44e638 ifc4d: tolerate activities without a CalendarObjectId in P6 import (#5617)
Importing a Primavera P6 XML crashed with
`AttributeError: 'NoneType' object has no attribute 'text'` in
P62Ifc.parse_activity_xml, which read
activity.find("pr:CalendarObjectId").text unconditionally. CalendarObjectId
is optional on a P6 Activity; when omitted, the activity inherits the
project's ActivityDefaultCalendarObjectId.

Capture the project default in parse_xml and fall back to it when an
activity has no CalendarObjectId (`calendar_id or self.default_calendar_id`).

Verified on the reporter's attached file (20241021 Cronograma.xml): 3 of 14
activities lack a CalendarObjectId and reproduced the exact crash on
v0.8.0; after the fix parse_xml completes and those activities resolve to
the project default calendar "2" (a valid calendar in the file). An
activity with an explicit CalendarObjectId keeps its own value.

Fixes the P6 re-import crash reported in #5617 (that issue tracks several
Gantt items; this addresses the import AttributeError).

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:15:36 +10:00
Petru Conduraru a3950ac191 util.element: read property sets inside an IfcPropertySetDefinitionSet (#6330)
get_pset and get_psets assumed RelatingPropertyDefinition is a single property
definition and read definition.Name directly. When it is an
IfcPropertySetDefinitionSet (a defined type wrapping a list of property set
definitions) that attribute access raised AttributeError, so an element whose
psets are grouped in a set returned none of them.

Unpack IfcPropertySetDefinitionSet into its members in both loops and process
each one. Single property definitions and the psets_only and qtos_only filters
are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:06:18 +10:00
Petru Conduraru 6f90badda8 Fix ci-bonsai-daily: renumber stale STEP ids in BDD feature fixtures
Several BDD scenarios hardcode absolute representation-item object names
whose trailing number is the IFC STEP line id
(f"Item/{item.is_a()}/{item.id()}"). Those ids drift when file-creation
order changes; a recent shift moved all of them by a uniform -4, so the
scenarios failed with "Item/.../NN does not exist".

The failing step (the_object_name_exists in test_feature.py) dumps the
full bpy.data.objects listing on failure, so the correct current ids are
recoverable directly from the CI log (run 29208793599, tested commit
36e21e882f, an ancestor of HEAD with only a .gitignore commit between).
Renumber to match:
  IfcExtrudedAreaSolid/77->73, IfcPolygonalFaceSet/76->72,
  IfcVertexPoint/69->65, IfcEdge/72->68, IfcFace/74->70.

Verified against the CI failure dump (a local build produces different
ids, so this is validated by CI's own object listing rather than a local
run). boolean.feature also hardcodes IfcHalfSpaceSolid/90 and panel text
[91] downstream of the failing assertion, which CI never reached and so
never dumped; left as-is to avoid guessing - they will print a fresh dump
next run for a follow-up if still stale.

This change was made with the assistance of an AI tool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:49:33 +10:00
Stephen Boddy d0eca6fa90 Fix segfault closing autosave recovery dialog
Reported: Blender segfaults when clicking Cancel on the "newer
autosave found" recovery popup shown by LoadProject at startup.

Root cause: LoadProject.execute()/invoke() triggered the recovery
popup via bpy.ops.bim.load_autosaved_recovery_popup("INVOKE_DEFAULT",
...) and returned that call's result ({'RUNNING_MODAL'}) as their own
return value, without LoadProject itself ever calling
modal_handler_add(). Blender's window manager takes a RUNNING_MODAL
return as a promise the operator registered its own modal handler;
since it hadn't, the WM's operator bookkeeping was left corrupted -
silently, since this is heap/state corruption rather than an
immediate crash. It only surfaced later, when the real modal operator
(the popup) closed and the WM reconciled its modal stack, which lines
up with the crash occurring specifically on dialog close regardless
of which button was pressed. check_autosave_recovery() now returns a
plain bool and fires the popup fire-and-forget; LoadProject reports
its own honest {"FINISHED"}.

Also hardened, as defense in depth: LoadAutosavedRecoveryPopup's
execute()/cancel() call back into bim.load_project(...), which (with
should_start_fresh_session) calls wm.read_homefile() and tears down
the window manager/screens. Doing that synchronously from inside this
popup's own execute()/cancel() - itself invoked from deep inside
Blender's modal handling for the popup's button click - risks the
same class of use-after-free as the timer bug fixed in the previous
commit. The reload is now deferred by one timer tick so it runs after
the popup's modal handling has fully unwound, and the deferred
callback closes over plain values rather than `self`, since the
operator instance may not survive past cancel()/execute() returning.

This defer-only change was tried and tested first, on the (incorrect)
assumption it was the root cause: it produced a byte-for-byte
identical crash backtrace on retest, which is what pointed at the
RUNNING_MODAL bug above as the actual cause - the defer change alone
was insufficient because the corruption happens when the popup is
first shown, not when it's closed.

Generated with the assistance of an AI coding tool.
2026-07-12 21:52:09 +01:00
Stephen Boddy 6306ce0f80 Fix autosave timer self-unregister crash risk
The periodic autosave timer called reset_timer() at the end of its
own callback, which unregistered the timer that was still executing
(itself). Blender frees the timer's internal registry entry on that
manual unregister, then frees it again when the callback returns
None - a double free that corrupts the heap and can crash Blender
later, once the corrupted memory is reused.

Reschedule by returning the next interval from the callback instead,
which is the safe, documented way to repeat a bpy.app.timers
callback. External reset_timer() calls (from SaveProject,
LoadProject, AutosavePrompt) are unaffected since they run from a
separate call stack (UI events), not from inside the timer.

Found while investigating a segfault reported when cancelling the
autosave recovery popup; not itself the cause of that crash (see the
following commit), but the same reentrant-unregister pattern and a
real, independent latent bug in the periodic reminder path.

Generated with the assistance of an AI coding tool.
2026-07-12 21:51:56 +01:00
Petru Conduraru 980988f208 Bonsai: fix KeyError in format_distance for kilometre and mile units #8255
The project-unit to Blender-unit mapping in format_distance only knew
FOOT/INCH/METRE/DECIMETRE/CENTIMETRE/MILLIMETRE, so creating a project
with Kilometers or Miles in the New Project Wizard crashed with
KeyError: 'KILOMETRE' (or 'MILE') as soon as the spatial tree formatted
an elevation. Add the missing Blender-supported units (kilometre, mile,
micrometre) and fall through gracefully for anything else (for example
HECTOMETRE) so unknown units use the adaptive formatting branch instead
of raising.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 22:51:37 +10:00
Petru Conduraru d4805387ef Selector: add rotation_x/y/z value keys #6262
Expose the Euler rotation of an element's placement in degrees through
get_element_value, alongside the existing x/y/z and easting/northing/
elevation keys. This makes element rotation exportable through ifccsv,
e.g. for placing oriented symbols in GIS.

Adopts the approach agreed in the review of the stale PR #6272 by
@TZwielehner: reuse util.shape_builder.np_matrix_to_euler and do the
degree conversion inside get_element_value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 22:49:43 +10:00
Petru Conduraru 69a4be68e8 Bonsai: fall back to adaptive units for unsupported SI prefixes #8074
Project loading set scene length_unit to f"{Prefix}METERS", but Blender's
enum only defines KILOMETERS, CENTIMETERS, MILLIMETERS and MICROMETERS.
A model with a DECIMETRE (or HECTO/DECA/etc.) length unit therefore raised
on the enum assignment and the file failed to open. Guard with the set of
supported values and fall back to ADAPTIVE display for the rest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 22:38:10 +10:00
Petru Conduraru 06da416b8f docs: remove TODO placeholder sections from the create-model quickstart #8208
The quickstart ended with three empty sections whose bodies were only
"TODO" (placing occurrences, changing locations, modeling a building),
which read as a dead end on docs.bonsaibim.org. The page now ends on the
completed save-and-view flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 22:37:34 +10:00
Petru Conduraru 21ae78fbc2 resource.assign_resource: fix typo in duplicate guard #8203
The guard that avoids re-assigning the same object to the same resource
tested is_a("IfclRelAssignsToResource") (stray "l"), so it never matched.
A repeat assignment therefore fell through and appended the related object
to RelatedObjects a second time. Corrected to "IfcRelAssignsToResource".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:36:26 +10:00
Petru Conduraru 0a8ae14789 fix(ifcdiff): check attributes by default so PredefinedType changes are caught (#8214)
IfcDiff defaulted to relationships=["geometry"], so a plain diff only ever
compared geometry. Attribute-only edits on an element that kept its GlobalId
(a modified or removed PredefinedType, a renamed element, etc.) were silently
missed. The CLI made this worse: --relationships did not list "attributes" or
"geometry" as valid values, so there was no documented way to enable it.

The default is now ["attributes", "geometry"], so a plain `ifcdiff old new`
reports attribute changes alongside geometry changes. The CLI help and the
IfcDiff docstring now document all valid relationship values.

Added a regression test covering a PredefinedType change detected with the
default configuration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 22:29:31 +10:00
Petru Conduraru 2eea7728d2 fix(selector): round() should not crash on non-numeric values (#6776)
FormatTransformer.round() called Decimal() directly on the input value,
which raises decimal.InvalidOperation when the value is a non-numeric
string (a text property, or a value carrying a unit suffix like "12.5 m").
In a spreadsheet export this crashed the entire operation as soon as one
element carried such a value.

Now round() catches InvalidOperation and returns the value unchanged, the
same graceful-fallback convention used by add(). Numeric rounding is
unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 22:28:38 +10:00
Petru Conduraru 6b3cc54afc ifcfm: convert COBie Coordinate space points to project units (#5926)
In the cobie24 Coordinate sheet, Floor rows use get_local_placement, whose values
are in the project length unit, but Space rows come from ifcopenshell.geom
create_shape, whose vertices are in SI metres, and the space branch never scaled
them back. So on a non metre model (for example millimetres) the Coordinate sheet
mixed units a thousandfold apart and disagreed with the Facility sheet's declared
LinearUnits.

Scale the space bounding box by the project unit scale so the whole Coordinate
sheet is consistent. A metre model is unchanged since the scale is 1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:27:07 +10:00
Petru Conduraru 5c11946470 Support block comments in selector filter syntax (#5023)
The filter_elements selector grammar had no way to comment out part of a
query, so users had to delete and retype text to temporarily toggle a
facet. Add a /* ... */ block comment terminal that is ignored by the
lexer, and tolerate a trailing "+" so that commenting out the final
operand (e.g. "IfcWall + /* IfcSlab */") parses cleanly. Comments may
span multiple lines; a /* sequence inside a quoted string is not treated
as a comment. Only the filter grammar is affected, not get_element or
format which use "/" for regex and division.

Adds a regression test and documents the syntax.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:23:30 +10:00
Ryan Schultz 0b7e25a3ef Docs: clarify immediate vs. any-depth spatial selectors
The location and parent filters both match at any depth in the spatial
hierarchy, which surprises users who want only the elements immediately
under a given container. Document that the parent query key resolves the
direct parent only (e.g. query:"parent.Name"="My Site"), add a matching
filter example, and note the immediacy on the parent value key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 16:44:22 -05:00