Commit Graph

22848 Commits

Author SHA1 Message Date
Dion Moult cd2e208b6a ifcparse: build the RocksDB inverse prefix in all_referencing_instances with rocksdb_key
Since #9508 the numeric key segments are fixed-width hex; the one
hand-built "v|" + std::to_string(id) prefix wouldn't have matched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH
2026-09-17 20:48:13 +10:00
Dion Moult f2230ed975 ifcparse: keep file::all_referencing_instances out of the Python wrapper
SWIG wraps every public method of file, so the std::function-taking
helper leaked into the wrapper and the stub validation tripped over it.
Only the compiled %extend helpers call it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH
2026-09-17 20:48:13 +10:00
Dion Moult 17092e97cd ifcparse: dispatch the inverse containment check for every storage backend
Review: the wrapper helpers visited the storage variant themselves and
threw for anything but in-memory storage. Move the dispatch into
file::all_referencing_instances(instance_id, pred), implemented for
both backends the way instances_by_reference is: the in-memory index
through inverse_index::all_sources, RocksDB through the same "v|<id>|"
prefix seek, stopping at the first source pred rejects. The SWIG
helpers only build the id set and call it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH
2026-09-17 20:48:13 +10:00
Dion Moult a819b8f1d4 ifcopenshell-python: decide remove_deep2 containment in C++ without materializing inverses
remove_deep2 decides whether a subelement may be purged by checking that
every instance referencing it lies inside the subgraph. It did so with
set(ifc_file.get_inverse(subelement)) - subgraph_set, which wraps every
referencing instance into a Python object first. For shared instances
that is enormous: a representation context is referenced once per
representation in the file, so purging one representation wrapped
~100k instances just to conclude "not exclusively mine". The same
question was asked once more for the start element against
also_consider, answered by traversing each considered element and
scanning the result with entity_instance.__eq__.

Add inverse_index::all_sources(id, pred), which visits the live records
referencing id and stops at the first source pred rejects, and two
wrapper helpers on file built on it: _all_inverses_within(e, ids) for
one instance, and _ids_referenced_only_within(ids) which returns the
subset of ids referenced only from within ids. remove_deep2 calls the
latter once per invocation, before the loop: clearing large aggregates
inside the loop only removes references whose source is inside the
subgraph, so "referenced only from inside" cannot change while it
runs. Both call sites are decision-identical to the old checks: the
set of surviving instances after root.remove_product on 300 products
of a 155 MB model is unchanged, and so is the element/api test suite.

root.remove_product per call, 300 products, on top of open-perf-v2:
see the pull request for the per-model table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH
2026-09-17 20:48:13 +10:00
Dion Moult d94215360f [AI-generated, unverified] ifcparse: make deleting and creating instances work on RocksDB-backed files (#9508)
* ifcparse: make deleting and creating instances work on RocksDB-backed files

file.remove() on a RocksDB-backed file segfaulted. Reducing it turned up
five gaps that each made editing such a file crash or silently do
nothing:

- process_deletion_inverse() decoded the v| inverse-record values as
  size_t while the serializer, register_inverse(), unregister_inverse()
  and instances_by_reference() use uint32_t, so std::find failed and
  vals.erase(end()) was undefined behaviour. It also took the DeleteRange
  end from an iterator that is invalid when the instance has no inverse
  records. Decode as uint32_t, remove every occurrence guarded on
  "found", and derive the range end from the prefix itself.
- attribute_value::size() ignored storage_model_, so every aggregate
  assignment on a RocksDB instance threw "Invalid variant index" from
  set_attribute_value(). Branch on the storage model like the sibling
  accessors and count the deserialized aggregate.
- rocks_db_file_storage::create() was a stub returning an empty handle,
  which anything creating an instance then dereferenced. Implement it
  after in_memory_file_storage::create().
- max_id_ is only initialised by the in-memory parse, so a RocksDB file
  would have handed out ids that overwrite existing instances.
  Implement the recalculate_id_counter() stub per backend and run it
  once before the first fresh_id() on RocksDB.
- byid_.erase() was a no-op: set_to_map_transformer::erase() and
  rocksdb_set_view::erase() were stubs. The deleted instance's attribute
  keys and cached handle survived, entity_names() still listed it and
  reopening the database threw. Erase deletes every key under the
  instance's prefix; the transformer forwards to it and takes an
  on-erase hook the storage uses to drop the cached handle.

root.remove_product on the first 200 products of a 61 MB model now
leaves the same surviving ids and inverse counts whether the file was
opened from SPF or converted to RocksDB.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH

* ifcparse: map argument_type to its stored type, size() as size_t

Review: argument_type enumerates the members of type_variant_parameter_pack
in order, so express that once as argument_storage_type_t<A> (pinned by
static_asserts) and let attribute_value::size() on RocksDB go through a
single aggregate_size_<A>() helper instead of spelling each vector type
out in the switch. size() now returns size_t; its only caller already
took size_t.

Also build the RocksDB DeleteRange upper bounds as prefix + ('|' + 1)
rather than a literal '}', which read as the {id} placeholder notation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH

* ifcparse: fixed-width hex ids in RocksDB keys, batched instance erasure

Review: recalculate_id_counter() scanned every i|<id>|_ record because
ids were written as decimal text, which doesn't sort numerically. Make
every numeric key segment fixed-width 16-digit lowercase hex, produced
and parsed by key_to_string()/key_from_string() in rocksdb_map_adapter.h,
with named builders (rocksdb_key::attribute, header_attribute,
type_record, inverse, inverse_prefix, type_list, upper_bound) that the
storage, entity_instance_data.cpp, read_schema() and the serializer use
instead of assembling "i|" + std::to_string(id) + ... by hand. Keys now
sort by id, an instance's records are contiguous in id order, and the
largest id is the last key under i|, which recalculate_id_counter()
seeks to. The two dormant to_string_fixed_width() helpers are gone.

This changes the on-disk layout; databases converted before this commit
have to be re-converted.

Also review: instance_cache_ eviction went through an on-erase hook on
set_to_map_transformer, a std::function call under a mutex per deleted
instance. Drop the hook; file::remove_entity() and unbatch() call
file::erase_instances_(ids), which on RocksDB is
rocks_db_file_storage::erase_instances(): one WriteBatch of DeleteRanges
and one lock for the whole batch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH

* ifcopenshell-python: test_rocks queries the fixed-width hex keys

The test looked up raw keys in the decimal layout ("i|139|5",
"t|<identity>|0"); numeric key segments are fixed-width hex now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNrXDmR88wKPCYwGE21SyH

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 20:47:33 +10:00
Dion Moult 336cb80394 ci: build macOS on macos-15
Homebrew no longer publishes bottles for the macos-14 runner, so
`brew install qt` in build_osx.yml compiles Qt and QtWebEngine from source
and fails in the dependency step before any IfcOpenShell code is reached.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 19:30:29 +10:00
Dion Moult 3c81e6e2e7 win: key the CGAL install dir on its version
6dbc4e28d raised the minimum CGAL to 5.4. The Windows dependency cache in
build-outputs is keyed on the install directory name and only ever adds to
it, so the unversioned `cgal` directory kept serving CGAL 5.2.3 and the
Windows build has failed at configure since:

  Could not find a configuration file for package "CGAL" that is compatible
  ... cgal/lib/cmake/CGAL/CGALConfig.cmake, version: 5.2.3

Name the directory `cgal-5.5.5`, as the TODO already suggested, and record
it in the deps cache like the other installers do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 19:30:29 +10:00
Dion Moult e389fb6145 cmake: give the wasm IfcParse its Boost headers back
ae2f27c3a replaced the global include_directories(${Boost_INCLUDE_DIRS})
with a Boost::headers target appended to Boost_LIBRARIES. The wasm branch of
IfcParse's link line never included Boost_LIBRARIES, so it lost the include
path and the Pyodide build has failed since with

  src/ifcparse/exception.h:25:10: fatal error: 'boost/lexical_cast.hpp' file not found

Nobody noticed because no Pyodide build had run since 2026-08-28.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 19:30:29 +10:00
Andrej730 998060b686 ifcparse: drop dllexport from instance_streamer class template
Fixes the following compilation error when building on MSVC with `USE_MMAP`.

```
parse.cpp(2202,23): error C2338: static_assert failed: 'Default instance_streamer requires a pushed sequential reader'
```

Apparently, when class has `IFC_PARSE_API`, compiler is very eager to instantiate all possible templates leading to this error. We already have explicit instantiation with export for the ones we actually use (e.g. `template class IFC_PARSE_API ifcopenshell::instance_streamer<file_reader<full_buffer_impl>>;`), so it should be no-op.
2026-09-16 18:07:51 +05:00
Andrej730 8c2ea9eb8b cmake: add minimum required eigen3 version 2026-09-16 18:07:51 +05:00
Andrej730 a5dec54719 cmake: add minimum required json version 2026-09-16 18:07:51 +05:00
Andrej730 4c22bd794c cmake: drop duplicated logic for finding gmp/mpfr
It also didn't propagate cgal includes for non-cmake configs.
So it means since roughly 0555523, when `CGAL_INCLUDE_DIR` was dropped from global includes, we only supported cmake configs discovery for cgal, so it's a good evidence to drop non-cmake config discovery in general.
2026-09-16 18:07:51 +05:00
Andrej730 6dbc4e28df cmake: add minimum required CGAL version 2026-09-16 18:07:51 +05:00
Andrej730 c2e0ae3bdf cmake: add minimum required boost version
Added 1.74 just to start somewhere (it's the on tested in CI currently)
2026-09-16 18:07:51 +05:00
Andrej730 3c1f44a42c cmake: require just 2 boost component by default program_options, regex
Tested locally that build succeeds with all other components being
header-only or unused by now.

`regex` is only needed for Boost <1.76, see
https://www.boost.org/releases/1.76.0/
2026-09-16 18:07:51 +05:00
Andrej730 776eea17da .clang-tidy: ignore identifier length 2026-09-16 18:07:51 +05:00
Andrej730 bf372a861a build-all: reuse common 2026-09-16 18:07:51 +05:00
Andrej730 f8541abcb5 ci: build-all to use cli args instead of envs 2026-09-16 18:07:51 +05:00
Andrej730 abeea643af build-all: --add-commit-sha cli arg 2026-09-16 18:07:51 +05:00
Andrej730 2bfaa4a196 build-all: --help to include default values 2026-09-16 18:07:51 +05:00
Andrej730 6d880a26c1 build-all: --build-cfg cli arg 2026-09-16 18:07:51 +05:00
Andrej730 490d888e17 build-all: --schemas cli arg 2026-09-16 18:07:51 +05:00
Andrej730 39b913d5ec build-all: --num_build_procs cli arg 2026-09-16 18:07:51 +05:00
Andrej730 18ed33d05d ci.yml: a bit more readable ci name
Instead of just "compile-and-test (ON)".
2026-09-16 18:07:51 +05:00
Andrej730 ae2f27c3a1 cmake: remove wide boost include
Replacing it with appending `Boost::headers` to `Boost_LIBRARIES` - this target is constructed both for Boost config and FindBoost and just references `Boost_INCLUDE_DIRS`, so it will automatically propagate to all `Boost_LIBRARIES` consumers. This is only needed for FindBoost users though.
2026-09-15 19:01:05 +05:00
Andrej730 7c43c12766 cmake: remove mention of Boost_LIBRARY_DIRS
Only used by ifcmax, which is mostly dead and can use `Boost_LIBRARIES`.
2026-09-15 19:01:05 +05:00
Andrej730 48042b4ff5 build-all-win: reuse boost for a small speedup 2026-09-15 19:01:05 +05:00
Andrej730 28a1ebdf89 Renormalize line endings in js files 2026-09-15 19:01:05 +05:00
Andrej730 cd11d82c03 .gitattributes: mark js and ts files as text 2026-09-15 19:01:05 +05:00
Andrej730 7b8e0b83c0 ci-lint: fix check-whitespace missing --check
ahh, forgot to add `--check`, so it was always passing
2026-09-15 19:01:05 +05:00
Andrej730 c29012d979 conda/clean-old-versions: bump anaconda-client
1.14.0 fixes the use of deprecated `pgk_resources` warning (https://github.com/anaconda/anaconda-client/releases/tag/1.14.0). Though bumping to the latest version.

Warning:
```
 /home/runner/micromamba/envs/test-env/lib/python3.11/site-packages/binstar_client/__init__.py:15: UserWarning:
  pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources
  package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
```
2026-09-15 12:02:52 +05:00
Andrej730 e7b961d269 ty: add conda 2026-09-15 12:02:52 +05:00
Andrej730 8a5878886e conda\clean-old-version: add script metadata
`bash -l` seems to be part of `setup-micromambda`
2026-09-15 12:02:52 +05:00
Andrej730 69e49422a9 ci-ifcopenshell-conda-cleaner: move script to a separate .py file 2026-09-15 12:02:52 +05:00
Andrej730 88b70b1163 cmake: drop unnecessary global cgal, gmp, mpfr includes
Since 0555523c9 we moved all those includes to `CGAL::CGAL` target and linked it directly when needed, so global include is redundant now.
2026-09-15 12:02:52 +05:00
Andrej730 0247ef13f4 cmake: fix uninitialized variables
`set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_CGAL)` already occurs in main cmake, so this command is redundant in `FindCGAL.cmake`.
2026-09-15 12:02:52 +05:00
Andrej730 76f668117a cmake: drop global INCLUDE_DIRECTORIES variable
It doesn't seem to be used anywhere and using this wide include might hide issues if some include actually doesn't reach some target.
2026-09-15 12:02:52 +05:00
Andrej730 5bc2cd058a cmake: remove unset IFCGEOM_SCHEMA_LIBRARIES, SCHEMA_AGNOSTIC_H_FILES
`IFCGEOM_SCHEMA_LIBRARIES` Is never set after we moved code to `src/ifcgeom/mapping` (b7a919925)
`SCHEMA_AGNOSTIC_H_FILES` is set but only inside `src/ifcgeom/mapping` and already handled there.
2026-09-15 12:02:52 +05:00
Andrej730 0607a7caa1 build scripts: autoswitch to release Python 3.15 2026-09-15 12:02:52 +05:00
Andrej730 5528d5e5a5 build-all-win: remove redundant python_versions list
A dead code since the commit when it was introduced (51bf4951b)
2026-09-15 12:02:52 +05:00
Andrej730 a6746ba531 run-cmake.py: --clean flag for removing build dir 2026-09-15 12:02:52 +05:00
Andrej730 ed6316b7f9 build-deps.py: add support for Python 3.15
We've added 3.15 to build-all-win, but `3.15.0` is not available on nuget yet, we need rc2 suffix for workflow to succeed.
2026-09-15 12:02:52 +05:00
Andrej730 e21dec4781 build-all: drop stale x86 comments (993ae7196) 2026-09-15 12:02:52 +05:00
Andrej730 c0a939be96 build-all: bump Python 3.15 to rc2 2026-09-15 12:02:52 +05:00
Bruno Perdigão 1da48061e3 snap: fix stale cut geometry after file reload bonsai-0.9.0-alpha2609142348 2026-09-14 20:47:55 -03:00
Bruno Perdigão edbce75f4e Unify polyline tool cleanup
Route all polyline tools through a single PolylineOperator.cleanup that tears down the decorators, polyline, and raycast cache, and uninstall ProductDecorator unconditionally since it is idempotent.

Generated with the assistance of an AI coding tool.
2026-09-14 20:47:55 -03:00
Bruno Perdigão a6d59bf61c snap: fix possible crash
- use draw handler to move the expensive synchronous GPU readback
out of the mouse-event loop.
- restore GPU state (depth, blend, face culling) around the offscreen
2026-09-14 20:47:55 -03:00
Bruno Perdigão 0f629d0d54 snap: add option to use GPU snapping. It is disabled by default 2026-09-14 20:47:55 -03:00
Bruno Perdigão 495455cb2e snap: small fix 2026-09-14 20:47:55 -03:00
Bruno Perdigão e9c7203c23 snap: black formatting 2026-09-14 20:47:55 -03:00