As we're not currently bundling dlls for all other external dependencies.
It seems `--shared`in CI previously worked sort of by accident - since it was relying on the cached build outputs that were previously built statically.
Caused errors like so:
```
Traceback (most recent call last):
File "/__w/IfcOpenShell/IfcOpenShell/build/Linux/x86_64/install/python-3.13.6/lib/python3.13/site-packages/ifcopenshell/__init__.py", line 87, in <module>
from . import ifcopenshell_wrapper
File "/__w/IfcOpenShell/IfcOpenShell/build/Linux/x86_64/install/python-3.13.6/lib/python3.13/site-packages/ifcopenshell/ifcopenshell_wrapper.py", line 10, in <module>
from . import _ifcopenshell_wrapper
ImportError: libifcopenshell.geometry.writer.so: cannot open shared object file: No such file or directory
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<string>", line 1, in <module>
import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/__w/IfcOpenShell/IfcOpenShell/build/Linux/x86_64/install/python-3.13.6/lib/python3.13/site-packages/ifcopenshell/__init__.py", line 89, in <module>
raise ImportError("IfcOpenShell not built for '%s' (%s)" % (python_distribution, e)) from e
ImportError: IfcOpenShell not built for 'linux/64bit/python3.13' (libifcopenshell.geometry.writer.so: cannot open shared object file: No such file or directory)
```
Level of Effort activities were skipped outright. They are now imported as
IfcTask with PredefinedType ATTENDANCE, the nearest thing IFC has to support
work that spans what it hangs off. Their dates stay derivable: the SS/FS and FF
predecessors P6 computes the span from are ordinary relationships and are
written out like any other. Skipping them also dropped 970 relationships that
happened to touch one.
User defined fields and activity codes now land as P6_UDF and
P6_ActivityCodes property sets. Separate sets because they are separate
concepts in P6, and not IfcClassificationReference for the codes because they
are orthogonal facets rather than a hierarchy. A code's description goes on
IfcProperty.Description so the short value and the readable one both survive.
UDF types come from the root declarations, since that is the only place the
data type is known, and are written sparsely.
Task times are now transcribed rather than recalculated. edit_task_time exists
to keep a schedule self-consistent while somebody edits it, so it snaps dates
off non-working days and derives durations across the calendar: reasonable for
an editor, wrong for a transcription where P6 has already run the critical path
and its answer is authoritative. On one 4,181 activity programme it moved 3,534
start dates and disagreed with P6's own duration on 3,206 activities. Actual
dates, early and late dates, float and completion are now carried too, none of
which were written before.
WBS children are created in P6's SequenceNumber order rather than document
order. IfcRelNests keeps an ordered list, so this is all it takes for a reader
to recover the breakdown as the planner arranged it, and sorting any other way
shows a programme nobody recognises.
Also: the project name was read without the namespace map, so every schedule
came out called "Unnamed".
I previously incorrectly believed that sequence types are mutually exclusive. E.g. you can have max 1 relationship between two tasks. Now after looking at more schedules I realise it's logically allowed to have more than one sequence relationship. For example simultaneous SS + FF.
The recipe loaded the model into Blender purely to reach its geometry
engine, so it could only run inside Bonsai and needed a filepath instead
of a file. It is now a plain BasePatcher.
The three fixes Revit needs are unchanged. To lower each space onto its
storey we take the storey elevation from util.placement and the space
placement from util.shape, instead of from Blender object matrices. To
convert to an extruded area solid we triangulate the space with
ifcopenshell.geom, union its downwards facing triangles with shapely to
get the footprint and its voids, and rebuild the body with
util.shape_builder.
Unioning every downwards facing face, rather than only the vertices
sitting at z=0, means stepped and clipped spaces keep their full
footprint, columns poking through a room become profile voids, and
disjoint footprints extrude as one item each. Spaces that cannot be
patched are logged and skipped rather than aborting the run, and a
storey above the top of a space falls back to the space's own height
instead of asking for a negative extrusion depth.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Background colour gains a meaningful alpha. Below 1 the viewport composites
over whatever the host has stacked behind it rather than painting a colour
of its own; at 0 it clears to nothing, so the model draws over another 3D
view, a map, or ordinary page content. There is no depth interaction -- the
layer behind is strictly behind and cannot occlude the model.
Honouring the alpha needs a premultiplied surface. configureSurface asks for
one only when the platform advertises it, the same way it picks a present
mode, and falls back to Auto otherwise -- this machine's Vulkan surface
offers opaque compositing only, so hardcoding premultiplied would have
requested a mode it does not support. The clear follows that choice: scaled
by alpha when premultiplied, held at 1 when not, since scaling would
otherwise just darken the colour for an alpha nobody reads.
ViewportWindow::setBackgroundColor now forwards to core rather than writing
through a reference proxy, dropping one more member from the friend
declaration it is meant to shrink. The screenshot QImage is tagged
premultiplied to match what the main pass leaves in the buffer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The renderer tints the selection blue, which says nothing about an object
that is already blue. Add a halo drawn just outside the selected objects
instead: a fixed colour against the background, so it reads whatever the
element is painted.
The mask pass reuses the main shader module and pipeline layout and shares
the main depth buffer read-only, so the halo follows the selection as
visible -- an occluded object contributes nothing. A separable dilation
widens the mask into inner and outer rings, composited after the edge pass
so the edge multiply does not darken it.
On by default; IfcViewer.setSelectionOutline(false) gets the tint alone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- to avoid some diffs on regeneration
- some fixtures had problems with the header, so `fail` for them wasn't
really testing the intended case, because small issues in the header
would make fixture fail regardless
Linux "basic dependencies" omitted libeigen3-dev even though
ifcgeom requires Eigen3 (find_package Eigen3 REQUIRED) and the
cmake snippet already passes -DEIGEN_DIR=/usr/include/eigen3.
macOS Homebrew line already installs eigen.
Closes#6903
Generated with the assistance of an AI coding tool.
test_file's parametrize list was filtered with `sys.argv[1] in
os.path.basename(fn)`, reading the raw process argv instead of a
pytest-native option. Under a bare `pytest` invocation sys.argv[1] is
pytest's own first CLI token, never a match, so the 138-fixture EXPRESS
rule corpus in test/fixtures/rules collapses to an empty parametrize and
pytest reports it as a single skipped test rather than an error. Under
CI's actual invocation (pytest -p no:pytest-blender -n $NPROCS test ...)
sys.argv[1] is "-p", which happens to substring-match 47 of the 138
fixtures, so CI has been silently running a coincidental 34% slice of
the corpus with no signal anything was wrong.
Replaced the module-level list comprehension with a pytest_generate_tests
hook plus a --rule CLI option (added via a new test/conftest.py). This
runs the full corpus by default under any pytest invocation, still
allows filtering to one rule for local debugging via --rule, and no
longer collides with pytest's own argv.
Verified all 138 fixtures collect and pass under the fixed harness
(63 fail- fixtures each raise a violation, 75 pass- fixtures raise none).
Generated with the assistance of an AI coding tool.