assign_type started passing should_map_representations to api type.assign_type,
but the two core tests still predicted the old call. Prophecy serializes kwargs
into the prediction key, so both failed with "Interface 'bonsai.core.tool.Ifc'
was not called with run".
Both cases use a plain string stand-in for the element, which has no material,
so _is_per_instance_profile returns False and the flag is True.
test/core is green again: 385 passed.
Generated with the assistance of an AI coding tool.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
assign_type passes should_map_representations=False for a per-instance profile
occurrence so the new type's shared representation is not mapped over its own
body. That flag gates more than the representation: in api type.assign_type it
also guards map_material_usages, the step that re-points an occurrence's
IfcMaterialProfileSetUsage at the new type's material set.
The occurrence therefore kept a usage whose ForProfileSet still referenced the
OLD type's IfcMaterialProfileSet. get_material(should_skip_usage=True) follows
that pointer, so the material panel showed, and edited, the old type's profile.
Duplicating a profile-based type and then changing the copy's profile silently
rewrote the original's profile instead, taking every other occurrence of the
original with it.
Re-point the usage here when the mapping is suppressed. Reusing
material.assign_material rather than setting ForProfileSet by hand matters: its
update_representation_profile rewrites SweptArea to the new type's profile but
never touches the extrusion depth, so the per-instance length this branch exists
to protect still survives. restore_material_usage_attributes runs afterwards, so
recorded usage attributes land on the new usage.
Traced in Blender against a duplicated IfcFurnitureType: before the fix the
profile edit reported the source type's IfcMaterialProfile, after it the copy's,
while the occurrence's representation entity ids were unchanged across the
retype.
Generated with the assistance of an AI coding tool.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lets an occurrence's extrusion length be either driven by the type
(typed/shared, e.g. Revit-mapped mullions) or owned by the occurrence
(per-instance, editable), and lets the user switch between the two.
- bim.make_profile_length_per_instance: un-maps a shared mapped body into a
per-instance SweptSolid (shared IfcProfileDef kept; the old mapped body is
orphaned, not deleted, to avoid cascading to sibling instances), gives the
occurrence its own IfcMaterialProfileSetUsage (so the Length UI appears), and
normalizes the placement so the extrusion runs local Z 0->depth -- Revit puts
the object and extrusion origins on opposite ends, which made extend_profile
flip the origin.
- bim.make_profile_length_type_driven: the inverse -- re-maps the occurrence
onto the type's shared representation, promoting the occurrence's body onto
the type first if the type has no RepresentationMap.
- A "Per-instance Length" checkbox in the Type panel (a get/set property that
reads the current mode straight from the IFC, so there is no stored state to
desync).
- New occurrences of a mapped/typed profile type default to per-instance.
- recreate_profile now skips mapped bodies (reloading the mesh instead of
regenerating), so assigning a length-driven type keeps the occurrence typed
rather than un-mapping it mid-assign and emptying sibling instances via the
shared-representation removal cascade.
- assign_type preserves a per-instance occurrence's own geometry/length
(should_map_representations=False) instead of converting it to typed.
Design/working notes under docs/dev-notes/profile-length-per-instance.md.
Closes#8657Closes#8656
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
reimport_element_representations' change_data already handles an empty ->
mesh transition (recreate_object_with_data when the datatypes differ), but the
leftover-data cleanup then called has_data_users(old_data) with old_data=None,
crashing with "'NoneType' object has no attribute 'users'". Guard the cleanup
so an object with no prior mesh reloads cleanly.
Closes#8655
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds bim.make_profile_length_per_instance, which converts profile
occurrences whose length is "typed" (shared via the type's mapped
RepresentationMap) into per-instance editable extrusions.
Per selected occurrence it:
- swaps the mapped body for its own SweptSolid copy (geometry/length
preserved exactly, shared IfcProfileDef kept), leaving the old mapped
IfcShapeRepresentation as a harmless orphan rather than deleting it (a
delete would either cascade to sibling instances via the shared
RepresentationMap or leave Bonsai's Blender-side links dangling);
- gives it its own IfcMaterialProfileSetUsage, since occurrences that only
inherit the type's IfcMaterialProfileSet report get_usage_type()==None and
never get the per-instance Length UI.
Sibling instances and the shared MappingSource/MappedRepresentation are left
untouched. The operator is idempotent: re-running repairs partial conversions
(e.g. adds a missing usage to an already-un-mapped occurrence).
A notice + "Make Length Per-Instance" button in the Type panel surfaces the
conversion when the active occurrence is still typed-length.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Error was:
```
configure: error: could not find a working compiler, see config.log for details
```
config.log:
```
conftest.c: In function 'f':
conftest.c:12:48: error: too many arguments to function 'g'; expected 0, have 6
12 | for(i=0;i<1;i++){if(e(got,got,9,d[i].n)==0)h();g(i,d[i].src,d[i].n,got,d[i].want,9);if(d[i].n)h();}}
| ^ ~
```
Last reference to this file was dropped in 7ae685dbf, though the ref was
pointing to `/patches/opencollada/pr622.patch`, so IIUC
`patches/pr622.patch` was never used.
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.
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
Because uv was always trying to install when starting a venv in `ifcopenshell` folder, though they might be already available globally. And also they were listed twice - in pyproject and in the ci-lint.yml, now there's a single source of truth.
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.
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>
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>
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>