Commit Graph

22341 Commits

Author SHA1 Message Date
Stephen Boddy 9dd071f9a0 Sync ifcopenshell_wrapper.pyi with sync_stub.py
Ran the new sync_stub.py against a real local build: adds
context.delete_same_facet_edge_pairs (present on the compiled wrapper,
missing from the stub) and drops the module-level logger_or_root
(present in the stub, no longer exists on the wrapper at all).

Nothing else changes - no license header rewrite, no docstring loss,
none of the 14 hand-curated named-parameter constructor/function
signatures touched, unlike the wholesale regeneration this replaces.

Generated with the assistance of an AI coding tool.

(cherry picked from commit b61f809731)
2026-07-25 23:16:38 +10:00
Stephen Boddy b61484f3a6 Add sync_stub.py, a minimal-diff stub syncer
generate_stub.py (this branch's earlier commit) regenerates
ifcopenshell_wrapper.pyi wholesale from the compiled wrapper: it
reliably fixes real drift, but it also discards everything that isn't
mechanically recoverable from the wrapper alone - the license header,
docstrings, and hand-curated named-parameter signatures for
SWIG-overloaded constructors/functions (SWIG itself always emits
generic `*args` for those, so a regenerator can't tell a deliberate
curation from real drift and just overwrites it).

sync_stub.py takes the smaller-blast-radius approach: it only adds
top-level symbols/class members that are genuinely missing, and only
removes ones that are genuinely gone, cross-checking against
validate_stub.py's own full canonicalisation (via the newly-exposed
get_names_tree()) so it never mistakes a property()/staticmethod()-
wrapped member for something absent just because its own narrower
parser skips that form. Anything that exists on both sides under the
same name but with a different signature - exactly where curation
lives - is left untouched and reported for a human to review instead
of guessed at.

Verified against a real local build: applying it to the current
ifcopenshell_wrapper.pyi produces a small, targeted diff (add one
missing method, drop one stale function) with the license header,
docstrings, and all 14 curated constructor/function signatures
preserved byte-for-byte, versus generate_stub.py's ~1000-line
wholesale rewrite for the same underlying fix.

Generated with the assistance of an AI coding tool.

(cherry picked from commit 948ffce7e9)
2026-07-25 23:16:38 +10:00
Petru Conduraru da4127f33b Fix pythonocc-core viewer compatibility in geom.occ_utils and geom.app (#1037, #1098)
set_shape_transparency() called AIS_InteractiveContext.SetTransparency(),
whose argument count is inconsistent across pythonocc-core versions
(reported as a TypeError in #1037). Set transparency directly on the AIS
object instead, the same stable pattern already used elsewhere in this
file (display_shape() calls ais.SetTransparency() directly, never through
the Context), then call Context.UpdateCurrentViewer() to refresh.

app.py's viewer used a "SetSelectionPriority(counter)"/"SelectionPriority()"
pair as an ad hoc unique key to map a displayed AIS object back to its IFC
product. On modern pythonocc-core this crashed with AttributeError because
.GetObject() (needed to unwrap the old handle-based API) no longer exists
on AIS objects (#1098, PR #1113 partially patched one of the two call
sites but left the one in HandleSelection unguarded).

Live pythonocc-core 7.9.3 testing showed the GetObject() guard alone is
not sufficient: SetSelectionPriority/SelectionPriority themselves have
been removed from AIS_InteractiveObject entirely in modern OCCT (only
AIS_Trihedron keeps a same-named but unrelated method for datum parts),
so gating the .GetObject() call with the existing USE_OCCT_HANDLE flag
would still crash the first time a shape is selected. Verified live that
AIS objects retain correct __eq__/__hash__ (matching the underlying OCCT
instance) across separate SWIG wrapper instances, so ais_to_product is
now keyed directly by the AIS object itself, removing the dependency on
the removed OCCT API and the GetObject()/handle distinction altogether.

Verified live against pythonocc-core 7.9.3 (conda-forge) using real
AIS_Shape objects obtained from ifcopenshell.geom.occ_utils.display_shape()
and a real IFC file: reproduced both the original TypeError (#1037) and
AttributeError (#1098), confirmed both fixes resolve them, and confirmed
the ais_to_product dict lookup round trips correctly through a real
Context.Select()/SelectedInteractive() call. Could not exercise the full
Qt-embedded viewer.finished()/HandleSelection() flow end to end because
this pythonocc-core build segfaults natively when creating a second GL
context inside a Qt widget on this macOS host, a pre-existing environment
issue unrelated to this diff (reproduces identically with unpatched code,
before any touched line executes).

AI-generated, reviewed and tested by Petru Conduraru.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 6603c8459a)
2026-07-25 23:16:38 +10:00
Petru Conduraru 0191ac63dc ifcwrap: keep geometry's owning element alive to fix silent data corruption (#1124)
create_shape() returns a Python-owned Element (SWIG_POINTER_OWN in the
boost::variant out typemap). Its .geometry property calls Element::geometry(),
which returns a reference into the element's boost::shared_ptr<Representation>
_geometry member. SWIG wraps that reference as a non-owning pointer, so the
returned Triangulation/BRep/Serialization proxy does not keep the element alive.

When a caller keeps only .geometry (e.g. create_shape(s, e).geometry) and drops
the parent element, Python garbage-collects the element, destroying its
shared_ptr and freeing the underlying representation. Subsequent reads of
verts/faces then return freed memory: empty or implausible float/int garbage,
non-deterministically depending on GC and allocator timing. This is silent data
corruption, not a crash, and has bitten users since 2020.

Fix: in the TriangulationElement/SerializedElement/BRepElement pythoncode, wrap
the geometry getter so the returned geometry stores a backreference to its
owning element (result._parent = self). This makes the parent's lifetime at
least as long as the geometry's, automatically and transparently, so no caller
has to remember to hold the element. This is aothms's suggested backreference,
applied generically in the binding rather than left as a workaround.

Reproduced deterministically (washBasin fixture): before, verts len 0 vs 133500
across repeated GC-pressure runs; after, 133500 every run for all three element
types. test_create_shape passes; no regressions.

Note: tree.select_ray()'s ray_intersection_result (2024 follow-up in #1124) is a
separate ownership mechanism (std::vector element reference + std::array member
pointer) and is left as follow-up scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 824c1fc280)
2026-07-25 23:16:38 +10:00
carlopav b5fa31d762 drawing: compute cut/fill intersection once per CutDecorator object
recalculate_cut() and recalculate_fill() each ran is_intersecting_camera(),
which builds a bmesh and scans every vertex. When a redraw recalculated both
(camera moved, cache miss, or the object selected) that was two full
intersection tests per object per frame for the same answer.

Compute it once in decorate() and pass it to both, and skip the test
entirely when neither recalculation is needed. Never more tests than before,
identical result since the camera can't move within a frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 705af7ba3a)
2026-07-25 23:16:38 +10:00
carlopav 7de625b3d3 drawing: evaluate camera movement once per CutDecorator redraw
is_camera_moved() runs eval()/numpy over the camera matrix and, as a side
effect, refreshes the stored checksum the first time it returns True. It was
called up to twice per object inside decorate(), so on a frame where the
camera actually moved the first call updated the checksum and every later
call - the fill check on the same object, and both checks on all remaining
objects - then saw an already-current checksum and returned False. Only the
first object's cut got recalculated; its fill and every other element stayed
stale until something else invalidated the cache.

Evaluate it once at the top of __call__ and reuse the flag. This halves the
per-object eval overhead on the common path (viewport navigation with the
camera object stationary) and, when the camera does move, correctly
recalculates the cut and fill for every intersecting element instead of just
the first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 074fc26e8f)
2026-07-25 23:16:38 +10:00
Ryan Schultz c07f9e2c45 Bonsai: preserve occurrence geometry/material/styles when deleting a type
Deleting a type used to strip its occurrences: any that displayed the
type's mapped representation lost their geometry, and inherited material
and presentation styles were dropped too.

The no-SHIFT "Delete Type" path now bakes each occurrence's geometry,
styles, and inherited material onto the occurrence before the type is
removed:
- Refactor UnassignType's unmap logic into a reusable
  UnassignType.unassign_and_unmap(), and extend it to re-attach styled
  items (copy_deep only follows forward refs, so IfcStyledItem is lost)
  and bake down any inherited (non-owned) material.
- Add RemoveType._detach_type_material_set(): unhook the type's
  IfcMaterialLayerSet/ProfileSet association cascade-free before deletion,
  so remove_product's aggressive unassign_material never fires and the
  occurrences' layer/profile-set usages survive intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit a90064929b)
2026-07-25 23:16:38 +10:00
Ryan Schultz a45acf65e5 Bonsai: add Delete Type button to Type Attributes panel
Adds a trash button in BIM_PT_type_attributes that deletes the relating
type via bim.remove_type. SHIFT+Click also deletes every occurrence of
the type in the project, behind a confirmation dialog showing the count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 397f13e71c)
2026-07-25 23:16:38 +10:00
Petru Conduraru 6dba261e1b Optimise IfcPatch recipe: make toposort backend configurable
aothms asked for the toposort dependency ordering used by the dedup
walk to try igraph's C-backed topological_sorting() first, since it
should shave off additional time on top of the non-recursive
get_info fix. Falls back to the pure python toposort package with a
warning if igraph is not installed.

Generated with the assistance of an AI coding tool.

(cherry picked from commit 7ebdd046b6)
2026-07-25 23:16:38 +10:00
Petru Conduraru 4299968b6e Fix #1043. Optimise IfcPatch recipe: avoid redundant recursive get_info
The 2020 profiling in issue #1043 found the Optimise recipe's dedup
loop spent almost all of its time in entity_instance.get_info(recursive=True):
because the topological sort already guarantees every referenced entity
is folded before the entity that references it, recomputing each
already-folded subtree's canonical value from scratch for every parent
that points to it is wasted work. Confirmed this is still exactly the
bottleneck in the current codebase, unchanged since 2020 (get_info's
recursive path still walks the whole subtree on every call).

Applied aothms's suggested fix from the issue thread: canonicalize each
entity with a non-recursive get_info, and for referenced entities substitute
the already-computed identity of their folded replacement (looked up in
instance_mapping) instead of re-expanding the subtree. Also limited the
toposort dependency graph to direct references (max_levels=1), since a
topological sort only needs direct edges, not the full transitive closure
traverse() was computing for every entity.

Benchmarked before and after on real IFC test fixtures and a larger
synthetic file with heavily shared geometry (thousands of walls sharing
a handful of profile/point subtrees, mirroring the sharing pattern
described in the issue):

- test/input/geometrygym_great_court_roof.ifc (56989 entities): 9.9s -> 1.7s
- test/input/acad2010_objects.ifc (16296 entities): 3.7s -> 0.4s
- synthetic 120083-entity fixture with heavy geometry sharing: 19.2s -> 3.4s

Verified correctness by comparing the full canonical (recursive get_info)
multiset of the optimized output between the old and new implementation on
all three fixtures: identical results, same fold counts.

Added test_Optimise.py covering the core scenario from the issue: entities
built from separate, value-identical non-rooted subtrees fold to a shared
instance, while entities with distinct values do not.

Generated with the assistance of an AI coding tool.

(cherry picked from commit 57cfd9d1fd)
2026-07-25 23:16:38 +10:00
Petru Conduraru 634a7add85 ifcgeom: build the swept-area directrix from the offset curve far from origin (#4848)
IfcSurfaceCurveSweptAreaSolid regressed in 0.8 for geometry far from the
origin (for example parapets on a georeferenced building), which went
missing or glitched.

The kernel offsets the directrix toward the origin when it is far away
(mean.norm() > 1e2), storing the offset copy in a local curve variable and
setting applied_temporary_offset so the finished solid is translated back by
+mean. But the wire was still built from scs->curve, the un-offset original,
so the offset never took effect and the result was translated by +mean from
its correct location. Build the wire from curve instead. When no offset is
applied curve aliases scs->curve, so near-origin geometry is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit e333c1c100)
2026-07-25 23:16:38 +10:00
Bruno Postle 57d9c47c6c Fix null-pointer derefs in reference resolution
Two related bugs in read_from_stream's reference-resolution
loop, both reachable from malformed input:

- has_attribute_value<IfcBaseClass*> only checks the stored
  slot's type, not that it's non-null (e.g. an explicit $
  value), so the following get_attribute_value() call could
  return null and inst->declaration() crashed on it.
- byid_[ref] default-inserts (and returns) a null pointer
  when the owning instance id isn't present, which was then
  dereferenced unconditionally via ->data().

Added regression tests using the two minimized crash inputs
that found these.

Generated with the assistance of an AI coding tool.

(cherry picked from commit 56121ca061)
2026-07-25 23:16:38 +10:00
Bartok 141cf95568 docs(readme): use https for IfcOpenShell website link
(cherry picked from commit 7b613a0bcc)
2026-07-25 23:16:38 +10:00
Andrej730 6858cccdcc settings_mixin.build_parser: fix ty == "bool" typo, should be an assignment
(cherry picked from commit f744753726)
2026-07-25 23:16:38 +10:00
Andrej730 982f5e802c assign_cost_item_quantity: fix indendation and missing values (de65e50)
`values` dictionary was missing and variables were never collected to it, so `FormulaEvaluator(values)` was always resulting in missing variable error.

(cherry picked from commit 2e21fc5a98)
2026-07-25 23:16:38 +10:00
Andrej730 b1f7e44175 assign_cost_item_quantity: annotate
(cherry picked from commit ca9bbbc4a7)
2026-07-25 23:16:38 +10:00
Andrej730 1a2e6872be edit_true_north: handle unsetting case when TrueNorth is already None
(cherry picked from commit 47dc1a6c68)
2026-07-25 23:16:38 +10:00
Stephen Boddy c17ea80827 Remove stale ty lint ignore directive
(cherry picked from commit 489084c7be)
2026-07-25 23:16:38 +10:00
Stephen Boddy bb908f3dfa Route boolean-op kernel logging through the injected logger
Ports #96e2efebc onto wgpu. wgpu's boolean_utils logged via the global
::logger::root() singleton (which IfcConvert never wires to --log-file),
so boolean-op messages were effectively dropped. Thread the caller's
injected logger through instead:
- boolean_settings gains `::logger* logger` + `log()` accessor (falls
  back to ::logger::root()); boolean_operation logs via settings.log()
- eliminate_narrow_operands / boolean_subtraction_2d_using_builder take a
  `::logger& logger = ::logger::root()` param; boolean_operation passes
  settings.log() into them
- OpenCascadeKernel / boolean_result set bst.logger = &logger_ and log
  via logger_ (were ::logger::root())
Adapted from v0.8.0's Logger/Logger::Root() to wgpu's ::logger/::logger::root().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:16:38 +10:00
Stephen Boddy 60b4f6191c Allow process/resource type assignment via Type-suffix convention
The class-pairing validation added in 10ee5aef4f rejects any type
assignment whose class isn't in the buildingSMART implementer
agreement map. That map only covers physical product occurrence/type
pairs (IfcWallType -> IfcWall, etc); IfcTypeProcess and IfcTypeResource
subtypes such as IfcTaskType, IfcProcedureType and the resource types
have no entry, so previously-valid assignments like
IfcTaskType -> IfcTask were rejected with "allowed occurrence
classes: <none>".

These classes still follow the schema's universal Type-suffix naming
convention, so derive the pairing the same way the existing
ApplicableOccurrence fallback does: strip "Type" from the relating
type's class name and accept it only if the schema actually declares
that entity. This can only add pairings implied by the type's own
class name, so it cannot loosen the existing rejection of genuine
mismatches (e.g. IfcWallType -> IfcWindow).

Generated with the assistance of an AI coding tool.

(cherry picked from commit d188e3beaf)
2026-07-25 23:16:38 +10:00
Dion Moult a341ad29f3 port: SVG edge classification (#3668) onto wgpu [worklist #114-120]
Ports the 7-commit v0.8.0 SVG edge-classification feature (f0970b90b +
6 follow-ups) onto wgpu's heavily-diverged serializer. Reconstructed
block-by-block rather than merged, because both sides rewrote
SvgSerializer (v0.8.0 +419, wgpu +778) and git's conflict alignment was
misleading.

Key wgpu adaptations reasoned per block:
- IfcUtil::IfcBaseEntity* (pointer identity) -> express::Base (value),
  incl. as a std::map key in draw_hlr (express::Base has operator<);
  nullptr fallback -> express::Base{}
- boost::optional -> std::optional (css_class, dash_array)
- hlr_calc::result_type pair -> 3-tuple (adds per-edge class label)
- draw_hlr restructured with a group_by_product map: ONE path_object per
  product so multiple class buckets share a group and per-path classes
  survive Bonsai's merge (NOT naive per-item threading, which fragments
  groups -- caught during visual verification)
- settings wired into wgpu's apply_settings() (ctor-called), NOT the
  feature's added ready() call which wgpu already solved differently
- logger_.Warning -> logger().warning in write(geometry_data)

Verified end-to-end via IfcConvert on a curved-geometry stress scene
(Suzanne/Torus/Sphere/Cube/Cone/...): edges classify into outline/sharp/
crease/boundary/flush with correct per-product grouping and CSS styling.
Requires the model's storey to carry an Elevation and --svg-project to
trigger the HLR projection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:16:38 +10:00
Ryan Schultz 43fc405b58 Bonsai: allow cross-family class reassignment for spatial elements with geometry (#8665)
The Reassign Class operator refused to reassign an element to a different
IFC product family unless it was an IfcElement <-> IfcElementType swap, so a
piece of geometry mistakenly hosted on IfcSite could not be turned into
IfcFurniture even though root.reassign_class handles it fine.

Loosen the guard: only block the case that actually matters - a spatial
element (IfcSpatialElement / IfcSpatialStructureElement for IFC2X3) with no
geometry, which would be a real containment-hierarchy container rather than
a stray modelled object. Everything else reassigns freely.

Closes #8664

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit b5a0f1fc74)
2026-07-25 23:16:38 +10:00
Petru Conduraru dbbbc54f19 Bonsai: make 'has openings' representation error actionable (#8108)
When converting a wall representation to a parametric extrusion via the
Representation Utilities buttons, an element that has openings would report
"has openings - representation cannot be updated" and stop, without telling
the user there is an ALT+click path that bakes the openings into the new
representation. Point the message at that path so the error is actionable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 25441bd816)
2026-07-25 23:16:38 +10:00
Petru Conduraru f76ee61812 Bonsai: place auto-generated opening boundaries at their real position (#8237) (#8311)
* Bonsai: place auto-generated opening boundaries at their real position #8237

auto_generate_boundaries (single-space mode) built each opening/filling boundary
from the opening's LOCAL geometry (get_vertices) but first did
mat.translation = (0, 0, 0) on its placement matrix. Because the vertices are
local, that placement translation is exactly what carries the opening to its
real location, so zeroing it collapsed every window/door boundary onto the
origin. This is why the auto path misplaced window boundaries while the
single-element path (create_element_boundary) placed them correctly, as
@MDHering observed with the two modes. Keep the full placement matrix.

Verified on the reporter's file: the opening's real placement is (0.1, 1.5, 1.0);
a vertex went from (0.6, 0, 0) under the old code to (0.7, 1.5, 1.0) with the fix,
i.e. moved by exactly the (0.1, 1.5, 1.0) that was being discarded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Remove superfluous comment from #8237 fix

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: CyrilWaechter <cyril@biminsight.ch>
(cherry picked from commit 65811ac7c9)
2026-07-25 23:16:38 +10:00
Andrej730 a55ce7b1fb test-package: drop stale comment
This information is already documented in maintanence.rst.

(cherry picked from commit d9d1824886)
2026-07-25 23:16:38 +10:00
Andrej730 6997046743 Bump build 3e7b739 -> 821cf7b
Just to test everything is working with the changes from the last month.

(cherry picked from commit 16e5f18553)
2026-07-25 23:16:38 +10:00
Andrej730 a62238bcaf test-package: assert BUILD_COMMIT is a 7-char short SHA
(cherry picked from commit b7a9b7bc5a)
2026-07-25 23:16:38 +10:00
Andrej730 ddbd3adf40 test-package: verify build URLs with HEAD requests instead of scraping listing page
(cherry picked from commit e14397058d)
2026-07-25 23:16:38 +10:00
Andrej730 90dfd2926e stub: add missing entity.inverse_attributes
(cherry picked from commit 9123d8c183)
2026-07-25 23:15:23 +10:00
Andrej730 725162823c ci-lint: run ty-bonsai and ty-ios as separate steps
So if one fails, it wouldn't block another.
Noticed by Stephen in d5e890bccd

(cherry picked from commit ffd939508c)
2026-07-25 23:15:23 +10:00
Andrej730 a0556c1124 build-all: ensure all patches are present
Also changed type to just `list[str]` to keep it simple.

(cherry picked from commit 816eba5145)
2026-07-25 23:15:23 +10:00
Andrej730 641becb4e0 build-all: drop unused opencollada pr622 patch
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.

(cherry picked from commit c013b9aca7)
2026-07-25 23:15:23 +10:00
Andrej730 647f53163c build-all: drop unused occt patch
Introduced in e21277e80, reference removed
in 683cadeb7 when occt was bumped to 7.3.0 and switched to git-tag based
download.

(cherry picked from commit 24e454ce0c)
2026-07-25 23:15:23 +10:00
Andrej730 c3c119d8e0 pyproject: add nix script to ty check
(cherry picked from commit cb497b37f7)
2026-07-25 23:15:23 +10:00
Andrej730 4f429f60f9 ifcclash: fix use of undefined clash["position"]
It's an artifact from the old hppfcl clasher dropped in 18c38b312

(cherry picked from commit 71c6950a59)
2026-07-25 23:15:23 +10:00
Andrej730 30c9956fac bsdd: fix test_get_class_relations
`classRelations` doesn't exist on `ClassPropertiesContractV1`, probably was just a typo.

(cherry picked from commit a7a7edfd27)
2026-07-25 23:15:23 +10:00
Andrej730 6935c631d4 build-all: fix note about the schemas built by default
(cherry picked from commit 5273569b08)
2026-07-25 23:15:23 +10:00
Andrej730 39d6459511 misc: more readable poll error for import_quick_favorites
(cherry picked from commit 78712ead98)
2026-07-25 23:15:23 +10:00
Andrej730 8f12a15678 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.

(cherry picked from commit 9e25c12b16)
2026-07-25 23:15:23 +10:00
Andrej730 c30c1d3f69 Deduplicate code by reusing tool.document
(cherry picked from commit 0968d06780)
2026-07-25 23:15:23 +10:00
Andrej730 136ae52c8e file.get_inverse: document with_attribute_indices overload
(cherry picked from commit 1b1da821f1)
2026-07-25 23:15:23 +10:00
Andrej730 b30bd5b4f2 geometry.add_boolean: fix typo in the class name
🫣🫣

(cherry picked from commit d772b24bd6)
2026-07-25 23:15:23 +10:00
Andrej730 672b0f81f4 ios pyproject: add networkx stubs as dev dependency
(cherry picked from commit 549f81a76e)
2026-07-25 23:15:23 +10:00
Andrej730 2f4bcc17a6 geom/main.py: fix ty complaint
(cherry picked from commit 8a00ce84cc)
2026-07-25 23:15:23 +10:00
Dion Moult 98cfc7873c port: fix XmlSerializer property_set_sets type (aggregate_of -> auto)
My resolution of #25 (e38993909) over-took v0.8.0's explicit
aggregate_of<...>::ptr type, which the rewrite renamed. Every other
get_related call in this file uses auto (it's a deduced-return template);
wgpu's HEAD already used auto here. Only the SCHEMA_HAS_ macro typo fix
was actually needed. Revert the type to auto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:15:23 +10:00
Andrej730 94b95527ea maintenance.rst: move pyver matrix to bundled Python version section
(cherry picked from commit 3e9ef82448)
2026-07-25 23:15:23 +10:00
Andrej730 4c896954bb ci-bonsai-daily: Use Blender 5.2 for tests
(cherry picked from commit a5c77fd096)
2026-07-25 23:15:23 +10:00
Andrej730 2a14e2e29c pyproject: support formatting with ruff
Since it's black-compatible drop-in replacement and they can be used
almost interchangeably.

(cherry picked from commit d183961280)
2026-07-25 23:15:23 +10:00
Andrej730 a6a6c20a18 dev_environment.py: add shebang and make executable
(cherry picked from commit 97d1a6e488)
2026-07-25 23:15:23 +10:00
Petru Conduraru 73687e041e 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.

(cherry picked from commit 780739719f)
2026-07-25 23:15:23 +10:00