Commit Graph

21784 Commits

Author SHA1 Message Date
Andrej730 16c1d2ece3 downstream: 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-22 18:36:08 +05:00
Andrej730 036c74e901 downstream: dev_environment.py: detect Python 3.13 on any Blender 5.1+ 2026-07-22 17:59:38 +05:00
Andrej730 4972556eb9 downstream: ColumnPSetsOfSets.ifc: restore original schema
It seems it was switched to ifc2x3 by accident.
Related - a7738ee 6c590bf00
2026-07-22 17:26:30 +05:00
Andrej730 8f5b744cea downstream: util.schema: fix geometry_classes_introduced_after using wrong IFC4X3 schema
It was passing `IFC4X3` directly to `schema_by_name` which is expecting
schema identifier (e.g. IFC4X3_ADD2, not IFC4X3 allowed by `IFC_SCHEMA`
- IFC4X3 is one of the IFC4X3 iterations while it was in development,
not the final one).

Noticed by tests failing:
FAILED
test/util/test_schema.py::TestGeometryClassesIntroducedAfter::test_ifc4x3_to_ifc2x3_is_superset_of_ifc4_to_ifc2x3
- RuntimeError: No schema named IFC4X3
FAILED
test/util/test_schema.py::TestGeometryClassesIntroducedAfter::test_ifc4_to_ifc4x3_is_empty
- RuntimeError: No schema named IFC4X3
2026-07-22 16:26:49 +05:00
Andrej730 20b1d98178 downstream: express: drop Python 2 fallbacks 2026-07-22 14:52:28 +05:00
Andrej730 c5634d5160 downstream: 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-22 14:52:28 +05:00
Andrej730 89663b716a downstream: 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-22 14:52:28 +05:00
Andrej730 9da1b9e02b downstream: express_parser: fix non-idempotent results 2026-07-22 14:52:28 +05:00
Andrej730 94a7ca6ac5 downstream: express: clean up trailing spaces 2026-07-22 14:52:28 +05:00
Andrej730 90c395bde4 downstream: 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-22 14:52:28 +05:00
Andrej730 9e6179e671 downstream: 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-22 14:52:28 +05:00
Stephen Boddy a16ea85610 downstream: Remove unused imports flagged by ruff
Fixes 23 unused-import violations, mostly in the alignment API module.
2026-07-22 12:32:05 +05:00
Andrej730 38c1f32fc5 downstream: pyproject: add more packages to dev-setup 2026-07-22 12:32:05 +05:00
Andrej730 584e1b2994 downstream: pyproject: add dev-setup poe task to setup environment for ide 2026-07-22 12:32:05 +05:00
Andrej730 d5f7f616f0 downstream: bonsai pyproject: move pytest deps to requirements-dev.txt 2026-07-22 12:32:05 +05:00
Andrej730 79aa36b751 downstream: pyproject: Move tool deps from to requirements-tools.txt
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.
2026-07-22 12:30:35 +05:00
Petru Conduraru 248c7e28c9 Remove QtViewer remnants
Per aothms's request on #8605: QtViewer is being superseded by the new
Bonsai Viewer, so its remains are deleted here (src/qtviewer, its
BUILD_QTVIEWER cmake option and add_subdirectory, and its references in
ci.yml's path filter, .gitignore, the conda recipe's license table, and
README's library table).

src/ifcopenshell-python/ifcopenshell/geom/app.py's qtViewer3d is
unrelated (pythonocc-core's own OCC.Display widget class, a name
coincidence) and is untouched.

Generated with the assistance of an AI coding tool.
2026-07-20 10:46:20 +10:00
Petru Conduraru f23db9440f ifcparse: widen all integer attribute types to int64_t for consistency
Follow-up to the scalar-only fix in #8754, per aothms's direct request on
that PR ("Please do make all int types consistent") and his own original
2023 design intent on issue #3058 ("make all integers (incl. schema
namespaces) an int64_t"). Widens the remaining inconsistent spots now that
compatibility isn't a constraint on this v0.9 branch:

- Integer aggregates (IfcTriangulatedFaceSet.CoordIndex and similar
  List<int> attributes), including the SWIG to_vec_int/to_vec_vec_int
  helpers, which previously silently truncated via static_cast<int> on the
  Python-set path - the same bug class as the original scalar issue.
- The schema code generator (express/mapping.py's integer type mapping),
  and all 12 generated schema header/source pairs regenerated to match, so
  every schema-typed getter/setter (e.g. IfcOwnerHistory::CreationDate) is
  int64_t end to end, not just the dynamic attribute-value path.

Instance/reference identifiers (STEP #123 ids) are deliberately left at
32-bit: they're a file-local index into internal maps, not an EXPRESS
domain value an application chooses, and no realistic STEP file has
billions of entities. The lexer's Token_IDENTIFIER parsing still funnels
through a 32-bit int for this reason - flagged as a known, low-risk gap
rather than fixed, since fixing it would mean touching indexing/hashing
code for no realistic benefit.

Verified: original PR's round-trip tests extended with aggregate cases
(IfcTriangulatedFaceSet.CoordIndex, InnerCoordIndices) at 64-bit boundary
values, in memory and through STEP text, IFC2X3 and IFC4. A standalone C++
program exercising the generated schema API directly (Ifc4::IfcOwnerHistory
::setCreationDate/CreationDate, IfcTriangulatedFaceSet::setCoordIndex/
CoordIndex) confirms int64_t end to end, bypassing SWIG. Full build
(BUILD_IFCGEOM, WITH_OPENCASCADE, BUILD_IFCPYTHON, IFC2X3+IFC4) clean.
test/util/test_attribute.py and test_file.py pass unchanged.

This contribution was produced with the assistance of an AI coding tool.
2026-07-19 13:54:16 +02:00
Petru Conduraru d5076bded3 ifcparse: store integer attribute values as int64_t to allow out-of-range timestamps
Setting an IfcInteger/IfcTimeStamp typed attribute (e.g. IfcOwnerHistory.CreationDate)
outside the signed 32-bit range corrupted the value instead of raising, since the
Python wrapper's set_attribute_value_py() truncated it with a plain static_cast<int>
before handing it to the C++ storage. Unix timestamps before 1901-12-13 or after
2038-01-19 silently wrapped around (e.g. 3000000000 became -1294967296) rather than
being rejected or stored correctly. Fixes #3058, equivalent to PR #8683 but ported to
this branch's rewritten ifcparse (snake_case files, variant_array/instance_data
storage, SWIG PyObject-based attribute setter) instead of the old IfcEntityInstanceData
sources, which no longer exist here.

The scalar slot of the attribute variant (Argument_INT) becomes int64_t. Integer
aggregates (Argument_AGGREGATE_OF_INT, e.g. CoordIndex) and instance/reference
identifiers stay 32-bit, since neither is the value that overflows here; this narrow
scope is kept on its own technical merits (aggregates and identifiers were never the
source of the bug, and widening them would be a much larger, riskier change for no
benefit) even though aothms said compatibility isn't a concern on this v0.9-track
branch. express::Base::set_attribute_value promotes the schema-generated int to
int64_t at a single choke point, so the generated setters keep compiling unchanged.
The STEP lexer, writer, and SWIG wrapper (set_attribute_value_py, pythonize) are all
widened together, since widening only the Python-facing setter would have silently
wrapped the value on file write instead of raising.

Verified in a build (IFC2X3 and IFC4, BUILD_IFCGEOM off, no kernels): pre-1901,
post-2038, both 32-bit boundaries, and a 9e12 value all round trip exactly both in
memory and through STEP text serialization (write then reopen). A value outside the
64-bit range now raises a clean exception instead of corrupting data. Ordinary
in-range integers and integer aggregates (e.g. IfcTriangulatedFaceSet.CoordIndex) are
unaffected. The existing util/test_attribute.py and test_file.py suites pass
unchanged; test_entity_instance.py has 5 pre-existing failures unrelated to this
change (confirmed identical on an unfixed build of this branch, caused by a missing
get_info_2 binding and _patch_swig_comparisons never being implemented here).

Generated with the assistance of an AI coding tool.
2026-07-19 13:54:16 +02:00
Thomas Krijnen d8799d799e Fixes for 4.3 compilation 2026-07-10 17:05:35 +02:00
Thomas Krijnen 8c9c3cde28 Merge remote-tracking branch 'origin/v0.8.0' into ifcviewer-wgpu 2026-07-10 16:32:51 +02:00
Thomas Krijnen 4f69855dc0 ContextPriorities 2026-07-10 16:31:35 +02:00
Thomas Krijnen 542856fb30 Merge branch 'ifcviewer-wgpu' of https://github.com/IfcOpenShell/IfcOpenShell into ifcviewer-wgpu 2026-07-10 08:44:21 +02:00
Thomas Krijnen 241b276de0 MaxVoidsPerElement 2026-07-10 08:44:14 +02:00
Dion Moult 402591e71c ci: restore --shared on the Rocky builds (undo datamodel-merge regression)
The datamodel-v1.0 merge (cf05bbd1b) overwrote build_rocky.yml with a
version that switched python3 -> uv run but dropped the --shared flag that
a91b1da28 ("Reduce Rocky package size") had added. build_osx.yml kept it
(ddee88bed).

Without --shared, nix/build-all.py builds IfcOpenShell as static libs, so
each of the ~40 plug-in .so files (schemas x8, kernels, mappings,
serializers, writers) statically embeds a full copy of libIfcParse +
libIfcGeom. The data-model rewrite made those base libs much larger, so the
duplication ballooned the Linux packages (~2-3x). With --shared the plug-ins
dynamically reference the shared libIfcParse/libIfcGeom instead. Restores the
same size reduction macOS already has.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:53:32 +10:00
Dion Moult a63bb999c1 models: rename a model's display name from the panel context menu
Adds a 'Rename' action to the model (non-group) context menu, mirroring
renameGroup: prompts via QInputDialog, trims, and calls
setModelDisplayName + notifyFederationChanged. The Federation already emits
modelChanged, so the panel item text updates in place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:33:37 +10:00
Dion Moult 21ba06cd94 ci: install FLTK's X11/pango/cairo + static libstdc++ for the connector link
With dbus-devel added, the connector's Rust code compiles fully and reaches
the final link, which fails: the bundled FLTK GUI toolkit needs the X11
extension, pango and cairo shared libs, plus libsupc++.a. ld reports every
unresolved -l at once, so this is the complete set:
  -lXext -lXinerama -lXcursor -lXrender -lXfixes -lXft
  -lpango-1.0 -lpangoxft-1.0 -lpangocairo-1.0 -lcairo -lsupc++
The X/pango/cairo -devel packages are in AppStream; libstdc++-static
(libsupc++.a) is in CRB, so enable it for the transaction. GitHub's ubuntu
runners ship all of this, which is why the dedicated connector workflow
never needed it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:49:46 +10:00
Dion Moult 0908a5b5a3 ci: install dbus-devel for the Rust connector's keyring backend
cargo build of bonsaiviewer-autodesk pulls dbus-secret-service (the Linux
OS-keyring backend for credential storage) -> dbus -> libdbus-sys, whose
build.rs needs dbus-1.pc via pkg-config. GitHub's ubuntu runners ship
libdbus-1-dev, so the dedicated connector workflow never needed it; the
minimal Rocky container doesn't. Add dbus-devel to both Rocky jobs
(pkg-config is already present). This was the last step after a fully
successful C++ build + cargo compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:31:46 +10:00
Dion Moult b72daf1330 ci(arm): set PATH in the Rocky 10 container so run steps find sh
The community rockylinux/rockylinux:10 image omits PATH from its image
config, unlike the old Docker Official arm64v8/rockylinux:9. GitHub Actions
derives each run step's PATH from that config, so with no PATH the shell
exec (docker exec ... sh -e {0}) fails with exit 127, 'exec: sh: not found'
— it broke before any build logic ran. Restore a standard PATH via the
container env; the runner still layers GITHUB_PATH additions (uv, cargo)
on top.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:21:42 +10:00
Dion Moult 014baab708 ci: provision Rocky jobs to build+package BonsaiViewer & Rust connector
Two gaps left when BonsaiViewer and its Rust connector were newly added to
the Rocky CI jobs (May–Jun), neither previously exercised there:

1. Rust: the autodesk connector was rewritten from a PyInstaller Python
   app to a Rust crate, so packaging/build.py now runs 'cargo build
   --release'. Neither Rocky workflow installed a toolchain. Add rustup
   (stable, matching the dedicated dtolnay/rust-toolchain@stable workflow)
   to both x86 and ARM.

2. ARM glibc: aqt's official Qt6 ARM binaries link glibc 2.38, which Rocky
   9 (glibc 2.34) can't load — moc fails, breaking IfcViewer_autogen. Move
   the ARM job to Rocky 10 (glibc 2.39). The legacy arm64v8/rockylinux
   image stopped at 9, so use rockylinux/rockylinux:10 (multi-arch, has
   arm64). Rocky 10 defaults to Python 3.12 and drops python3.11, so the
   script-runner references move python3.11 -> python3 (system Python only
   runs helper scripts; ifcopenshell is built against uv's Python). Bump
   the ccache key to rockylinux10. x86 stays on Rocky 9 to keep its lower
   glibc floor for end users.

The rockylinux9-arm64 build-outputs deps branch is kept as-is: Rocky 9
deps are forward-compatible on Rocky 10, and no rocky10 branch exists yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:07:02 +10:00
Dion Moult e9e944f77a ifcviewer: include MetalSurface_mac.h before its use (fix macOS build)
createWgpuSurface() calls wgpu_macos_attach_metal_layer() in the Q_OS_MAC
branch at the top of the file, but the only #include of MetalSurface_mac.h
sat ~450 lines below the call site, so macOS builds failed with 'use of
undeclared identifier'. The header self-guards on __APPLE__, so move the
include up into the early platform block next to <Windows.h>; the lone
call site is the sole consumer, so the late include was dead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 08:51:54 +10:00
Richard Brice 206cd6bbe1 Alignment API update for station and positioning referents. Fixes bug with fallback position. bonsai-0.8.6-alpha2607092110 2026-07-09 14:10:33 -07:00
Thomas Krijnen 552576fcc3 Merge branch 'ifcviewer-wgpu' of https://github.com/IfcOpenShell/IfcOpenShell into ifcviewer-wgpu 2026-07-09 22:02:39 +02:00
Thomas Krijnen 561a23cfbc After-merge clean-ups 2026-07-09 22:01:21 +02:00
Dion Moult d3b12d0307 ifcwrap: ignore spf_header set_file_* setters in SWIG (fix Windows wrapper)
The data-model branch's spf_header::set_file_description/name/schema take a
const shared_pointer_type& (an internal instance_data* storage handle). SWIG
wraps them and emits the alias unqualified into the global-scope wrapper,
which MSVC rejects (C2065 'shared_pointer_type': undeclared identifier). The
matching getters are already %ignore'd and re-exposed via %extend; the raw
setters are not a usable Python API, so ignore them the same way.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:32:11 +10:00
Dion Moult b3f83f67ca ifcgeomserver: test iterator->next() via operator bool (fix ambiguous !=)
Iterator::next() now returns express::Base (data-model branch). Comparing
it against 0 is ambiguous: 0 converts to Base via the pointer ctor while
Base converts to int via operator bool, so both operator!=(int,int) and
Base::operator!= are candidates. Use an explicit truthiness test — an
empty Base signals end-of-iteration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:28:41 +10:00
Dion Moult 1684513109 ifcparse: parse doubles via C-locale strtod_l on macOS (fix Apple build)
parse_num_ used std::from_chars for both integers and doubles, but the
floating-point from_chars overload is =deleted in Apple clang's libc++, so
the macOS build failed to compile (parse.cpp:136, instantiated for double).

Split parse_num_ with `if constexpr`: integers keep std::from_chars
everywhere; on macOS, doubles parse via strtod_l with a cached "C" locale
(locale-independent, restoring the pre-charconv Apple path). libstdc++ and
the MSVC STL have working float from_chars and are left unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 22:00:41 +10:00
Dion Moult ecee648b44 ci: install aqtinstall into the uv run env (fix Linux Qt6 install)
build-all.py's install_qt6 runs `sys.executable -m aqt`, but the build now
runs under `uv run`, whose isolated env never got aqtinstall — it was pip
installed into the system Python. `uv run --with typing_extensions --with
aqtinstall` puts them where the script actually executes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 21:51:49 +10:00
Thomas Krijnen 7fc2d9a998 Merge remote-tracking branch 'origin/v0.8.0' into ifcviewer-wgpu 2026-07-09 13:21:39 +02:00
Dion Moult 79d8408684 models: consume .rdbview bundles (extract at load time)
A .rdbview is a zip of model.rdb/ (the lossy IFC data DB — the rdb
serializer skips IfcRepresentationItem) + model.ifcview (baked geometry).
The viewer could produce them but not open them.

- extractRdbview(): unzip a .rdbview (QZipReader) into a session temp dir
  keyed by a hash of path+mtime+size (reused on re-open), returning the
  extracted model.rdb. The producer's layout means sidecarPath(model.rdb)
  resolves the sibling model.ifcview automatically, so it then loads exactly
  like any pure .rdb: geometry from the sidecar, data from the .rdb via
  ifcopenshell::file(FT_AUTODETECT). No SceneLoader/engine changes.
- detail::loadModels() resolves each source path through it before
  queueModels (both fresh-open and project reload go through here), so the
  Federation persists the .rdbview while the loader gets the extracted .rdb.
- cleanupRdbviewCache() clears stale extractions at startup.
- .rdbview is offered under "Add Geometry" (the file picker; "Add IFC
  Database" is a directory picker), not "Add IFC File" — it's a lossy viewer
  bundle, not a source IFC.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 20:36:51 +10:00
Dion Moult db884047e1 ifcviewer: don't block the UI while baking the .ifcview at 100%
Opening a fresh .ifc streams geometry to the GPU, then bakes the .ifcview
cache. That bake — reorder + per-chunk zstd (level 19) — ran synchronously
in SceneLoader::onStreamerFinished, which is a QueuedConnection slot on the
main thread, so it froze the UI right as the progress bar hit 100% (≈15s of
zstd for a 130 MB-geometry model).

- Move the compress + writeSidecar onto a background thread. The geometry is
  already resident and the sidecar is only a cache for the next open, so the
  viewport is interactive the instant streaming finishes; the write is joined
  before the next write and in the destructor.
- Parallelise the per-chunk zstd across hardware_concurrency threads (compress
  all chunks, then write serially to keep contiguous offsets) so the
  background write also finishes quickly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 18:32:37 +10:00
Dion Moult 9ccbcc2216 viewport: wire up the backface-culling setting
The "Backface Culling" checkbox persisted a value and reflected it, but
nothing consumed AppSettings::backfaceCulling — the opaque pipeline
hardcoded cullMode = Back, so toggling had no effect.

Build a second opaque pipeline (cullMode None) alongside the culled one and
pick between them per-frame from a backface_culling_ flag; setBackfaceCulling
flips the flag and requests a redraw (no rebuild). ViewportWindow forwards
it, and MainWindow applies the persisted value at startup and re-applies on
change — same wiring as the nav preset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:23:48 +10:00
Dion Moult ed21dd7ecc web: MODULARIZE build + embedded JS-integration example + selection callback
Restructure the web viewer so the wasm is a reusable module and add a
second example that drives it from ordinary page DOM.

Build:
- Emit IfcViewerWeb.js (a `createIfcViewer` factory, MODULARIZE) + .wasm
  instead of a single baked page (dropped --shell-file); copy the static
  example pages next to it at build time.
- Unbreak the web build: CameraMath.h / ViewportCore.cpp used
  boost::math::constants::pi just for pi, pulling all of boost/math into a
  header shared with the Emscripten build (no Boost in its sysroot). Replace
  with a constexpr kPiF — identical value, no dependency, desktop unaffected.

JS integration (web/ifcviewer.js):
- A small helper wraps the factory: boots the viewer on a canvas, runs the
  RAF loop from onRuntimeInitialized (NOT a post-await .then, which stalls
  Dawn-web's device callback and leaves the device half-initialised), and
  exposes addFile/addUrl, clearScene, model list/progress, and onSelect(...).
- ViewportCore/main_web emit each pick to JS via Module.__ifcvOnSelect
  (object id + IFC GlobalId + model index; empty on deselect); onSelect also
  dispatches an 'ifcviewer:select' DOM event.
- Fix input coords for a non-fullscreen canvas: mousemove/mouseup are
  window-targeted, so convert their coords to canvas-relative via the canvas
  client-rect origin (marquee + box-pick were offset when embedded).

Examples:
- IfcViewerWeb.html: the fullscreen viewer (same DOM/behaviour as before,
  now loading the module) — the Playwright smoke suite still targets it.
- embedded.html: a sized viewer with DOM outside it to add models (file or
  URL), list loaded models with streaming progress, and show the model +
  GlobalId of the clicked object. Starts empty (drops the wasm's embedded
  sample, which the fullscreen page/tests still use).
- index.html links both.

Federation note: the web viewer already streams multiple models into one
scene (a byte-source per file/URL); it doesn't need the desktop Federation
document for this. Verified: 11/11 web smoke tests pass; embedded example
loads models, reports the picked model + GUID, and the marquee aligns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:46:29 +10:00
Dion Moult b2ecfab86e style: theme input/tab-bar, align header height with body rows
- Theme QInputDialog (the New Group / Rename Group popup) so it follows
  the dark theme instead of rendering light.
- Theme the generic QTabBar that QMainWindow creates for tabbed docks
  (previously bright white). The app's own #appTabBar keeps its look via
  more specific selectors.
- Reduce QHeaderView::section vertical padding (7px -> 4px) so table/tree
  header rows match the body row height throughout the UI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 13:17:55 +10:00
Dion Moult 93fcdc9a8d viewport: don't clobber the persisted nav preset at startup
initWgpu() runs on the first exposeEvent, after MainWindow has already
applied the nav preset saved in Settings. It then unconditionally
re-applied "blender" whenever WGPU_NAV_PRESET was unset, silently
overriding the user's saved choice — so the applied navigation didn't
match what Settings showed.

Only apply the preset from WGPU_NAV_PRESET when that env override is
actually set; otherwise leave the current preset (MainWindow's persisted
choice, or the blender default). The startup log now reports the effective
orbit/pan bindings rather than a hardcoded name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 13:17:55 +10:00
Dion Moult 6e86072d5a viewport: select a section plane, highlight it, delete the selected one
Previously Del always removed the most recently added section plane. Now a
plane can be picked and deleted individually:

- ViewportCore tracks a selected plane index, kept valid as planes are
  added (the new one becomes selected), removed, or cleared.
- Clicking a gizmo with the section tool active selects that plane.
- The section gizmo geometry is baked white and coloured via its per-plane
  tint, so the selected plane draws in a bright amber highlight while the
  rest stay red (unchanged look).
- Del/Backspace removes the selected plane, falling back to the most recent
  one when nothing is selected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 12:48:38 +10:00
Dion Moult 12002ace86 properties: filter psets/quantities by name, property, or value
The filter field now actually filters. Typing shows only the sets whose
name matches, or that contain a matching property name/value — and when
it's a property/value match, only the matching rows are kept (neighbouring
rows are dropped). Matching is case-insensitive.

Set widgets live in a per-section container that's rebuilt from the raw
data on each keystroke, so filtering never recreates the filter field (its
focus and cursor are preserved). Placeholder reads "No properties/
quantities" with no data, "No matching properties/quantities" when the
filter excludes everything.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 12:21:11 +10:00
Gorgious56 9ae79b42dd Merge pull request #8398 from Gorgious56/batch-array-duplicate-helper
Batch array duplicate helper
bonsai-0.8.6-alpha2607081328
2026-07-08 15:27:53 +02:00
Gorgious56 c01433cb6c Bonsai: spec typed test doubles for Blender + dataclass mocks
Convert bare Mock() to Mock(spec=bpy.types.Object) for Blender-object
stand-ins in TestRecalculateWallsWithNewConnections, TestMEPActionGuards,
and TestRecreateAggregateIteratesAllNew so typos on the Blender API
fail loudly instead of silently returning a MagicMock.

Replace the ad-hoc Mock() ConnectionRecord stand-in in
TestRecreateConnectionsZipsPairs with a real ConnectionRecord instance,
which pins field names at construction and catches drift if the
dataclass fields ever get renamed.

IFC entity mocks remain bare Mock() intentionally: entity_instance
attributes are schema-driven at runtime rather than defined statically
on the class, so spec= would refuse the .GlobalId / .HasFillings /
.ConnectedTo attribute writes the tests need.

Relates to #8088.

Generated with the assistance of an AI coding tool.
2026-07-08 15:02:21 +02:00
Gorgious56 da50d22ed5 Bonsai: route array-regen selection through tool.Blender utilities
Rewrite tool.Array.select_only_parent as a thin call to
tool.Blender.select_and_activate_single_object; drop the ad-hoc
per-child deselect loop and the unused parent_element parameter.

Replace the tail parent_obj.select_set(True) in _regenerate_array_body
with tool.Blender.set_object_selection, which wraps select_set in the
hidden-object try/except the utility already owns.

Relates to #8088.

Generated with the assistance of an AI coding tool.
2026-07-08 14:53:29 +02:00