Commit Graph

20398 Commits

Author SHA1 Message Date
Andrej730 e2561ffa3b black, sort imports 2026-08-14 10:17:02 +05:00
Dion Moult 4b87ab5d0d Fix warnings in test suite due to undeclared wall pytest marker 2026-08-14 06:44:11 +10:00
Thomas Krijnen 8cc36f0d4d Add link dependency on native build to resole example failure 2026-08-13 05:13:35 +02:00
Dion Moult 13cc190849 Update georef tests to not hardcode the results of vert[0] used in auto origin detection.
Because vert[0] can change based on kernel output, we now assert that 1)
origins are on a vert, any vert, and 2) both blender coords and map
coords are what we expect. I manually visually verified all tests
against Blender 5.1 + stable 0.8.5 to check that actual behaviour hasn't
changed, only tests need updating.
2026-08-13 11:20:26 +10:00
Dion Moult b71354ce19 Fix assigning a plain material to an occurrence as a layer set
Assigning a material to an occurrence with a set material type has raised
"IfcMaterial cannot be assiged as a IfcMaterialLayerSetUsage" since the
default changed to assigning usages to occurrences. The type is upgraded to a
usage but the material is passed on unchanged, and material.assign_material
only accepts a material for a usage when that material is already the set,
whereas the Object Materials dropdown gives us a plain IfcMaterial. Pass
nothing in that case and let the API make the set, as it does when asked for
a usage with no material.

Look the set up past the usage afterwards, so the material the user picked is
added to it. get_material returns the usage, which is not a material set, so
neither branch of the repair below matched and the picked material was
dropped, leaving the set empty.

This is a stopgap and is commented as such: the real problem is that
assign_material builds sets with no items in them and ignores the material it
was given, which is not valid IFC and leaves callers patching up after it.

Also register "I evaluate expression" as a Then step. It has only ever been a
Given and a When, so the last line of the scenario covering this could never
run; it is the only Then of its kind in the suite.

test/bim goes from 16 failures to 15, with none introduced.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 10:28:15 +10:00
Dion Moult 17042f6f80 ifc4d: make ScheduleIfcGenerator's boilerplate file actually work
create_boilerplate_ifc sets self.file and self.work_plan and returns
nothing, but create_ifc assigned its result back over self.file, so any
caller that did not supply a file got None and crashed on the next
create_entity. Call it for its side effects, as csv2ifc and csv4d2ifc
already do.

That alone only moved the failure along: the boilerplate builds a file and a
work plan but no IfcProject, and add_work_calendar looks for an IfcContext.
Create one, matching csv4d2ifc's copy of the same method, which has both
lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 21:30:27 +10:00
Dion Moult beb0db89e5 ifc4d: rewrite the MS Project importer onto ScheduleIfcGenerator
msp2ifc parsed the XML and built the IFC itself, so a programme read
differently depending on whether it came out of MS Project or P6. It now
parses only, and hands the parsed programme to ScheduleIfcGenerator the way
p62ifc does. Calendars, statuses, task times and resources are therefore the
shared ones, and a reader no longer has to know which tool planned the
schedule.

Three things MS Project does differently needed handling rather than sharing.

It has no work breakdown structure: there is one flat task list and an
OutlineLevel column, and a task with anything indented under it is a summary
whose dates are rolled up rather than planned. Those become IfcTasks without
an IfcTaskTime, as a P6 WBS node does. Summaries and leaves also interleave,
and a planner expects a summary to stay where they put it, so the tree is
walked in export order instead of through create_tasks, which sorts nodes
ahead of activities. And a link may hang off a summary, which P6 cannot do,
so create_rel_sequences resolves both ends against summaries too --
IfcRelSequence relates two IfcProcesses and does not require a time on
either.

Calendar handling flattens what MS Project stores as differences against a
base calendar, since IfcWorkCalendar has no such notion, and reads holidays
from whichever of the two spellings the export uses rather than both.
Recurring exceptions are skipped, because the recurrence is not readable from
the export and guessing wrong silently moves every date computed from the
calendar.

In common.py the UDF and activity-code property set names become class
attributes. They keep their P6 names by default, but MS Project's extended
attributes are not P6 user-defined fields and now land in
MSP_ExtendedAttribute rather than under a name that says P6. IsMilestone
likewise prefers a source that states it outright -- MS Project has a
Milestone flag -- and falls back to the zero-duration test, which is all P6
gives us, so the other importers are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 21:24:56 +10:00
Dion Moult 572f718007 Use tool.Blender to get selected objects 2026-08-11 17:06:37 +10:00
Dion Moult 6bab0603e6 by_type now returns tuple - update annotations and fix failing tests 2026-08-11 17:06:37 +10:00
Dion Moult b408e64e5e Fix failing test due to declaration() instead of declaration 2026-08-11 17:06:37 +10:00
Dion Moult f580f7255f Type elements are hidden after assignment by default now. So rewrite tests to either use non-types or explicitly select types. 2026-08-11 17:06:37 +10:00
Dion Moult b252cd25f8 Give the web viewer a federation: false origin and per-model transforms
Models now resolve to global coordinates, which alone would make things worse:
composed per-instance transforms are float32, and around six million metres
that quantises at roughly half a metre. So the first model to load also sets a
false origin, derived from where its geometry actually sits, unless a host has
set one itself.

WebFederation owns the concepts an .ifcfed carries — a federation unit, a false
origin, a per-model transform and display name — without the file format. The
desktop Federation class is a document model whose sources are local filesystem
paths, which mean nothing in a browser; a host page that wants .ifcfed can parse
the JSON and drive these calls.

Models are keyed by the JS source id rather than the session model id. The
source id exists the moment a File or URL is registered, whereas the session id
is minted inside the async range-read chain, so keying on it lets a transform be
set before the model has streamed and applied when it arrives — the model never
visibly jumps. loadSidecarMetadataWeb gained a completion callback to carry that
id back out, and addFile/addUrl now return the source id and fire onModelLoaded,
where before they were fire-and-forget with no handle and no completion signal.

The embedded sample bypasses the source registry, so it is bound separately;
otherwise the guess never runs for a page that only ever shows the sample.

georef-a and georef-b are the regression fixture: two boxes whose different map
conversions resolve to the same real-world point, so a viewer that applies them
draws one box's worth of scene and one that ignores them spans 707 m. They carry
two meshes each because reorderSidecarByMorton bails out below two and then
writes no chunk table, and a sidecar without one cannot stream over byte ranges.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:06:37 +10:00
Dion Moult 935562142e Apply a model's coordinate operation when its sidecar loads
.ifcview has carried the model's CoordinateOperation since v11 and the
streaming reader has always parsed it, but applyCachedModel ignored it. The
matrix only ever reached the scene because BonsaiViewer pushes it after every
load via setModelCoordinateOperation. Nothing does that on web, so every model
rendered in its local coordinates and two federated models with differing map
conversions came out misaligned.

Seed the matrix and the unit scales from the sidecar, and recompose the model
afterwards. Seeding alone is not enough: the instance transforms in a sidecar
are baked with identity federation matrices, and applyCachedModel uploads them
as-is. The recompose also fixes a second case that had nothing to do with
georeferencing — a model loaded while a federated false origin was already in
force kept its unshifted transforms.

ModelGpuData gains the unit scales because composeModelTransformation needs
them to lift a transform's anchor point into metres, and on a sidecar-only load
there is no IFC to read them back from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:06:37 +10:00
Dion Moult d1d0fb4636 Move the federation transform math into IfcViewerCore
The value types and compose helpers in Federation.h were already Qt-free —
Eigen and std::string — but sat in the Qt half of the viewer, so the web build
could not reach them. Split them into FederationMath and add it to
IfcViewerCore, which the Emscripten build links.

What stays behind is what genuinely needs the dependencies: computeModelGeoref
reads an ifcopenshell::file, and the Federation class is a QObject that
persists .ifcfed. Federation.h includes the new header, so no caller changes.

FederationMath needs convert() to resolve a federation unit name to metres and
x_axis_to_angle_deg() to read grid north off a coordinate operation, hence the
helpers_math dependency added in the previous commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:06:37 +10:00
Dion Moult e0b226f4ca Split the schema-free half of the unit and geolocation helpers out
unit.h and geolocation.h both include ../ifcparse/express.h for the entity
walking they do, which puts the whole module out of reach of anything that
cannot link IfcParse. Most of what a viewer wants from them needs no IFC at
all: the unit conversion tables, and the Helmert parameters-to-matrix math.

Move those into unit_convert and geolocation_transform, and build them as a
new helpers_math target that `helpers` re-exports PUBLIC, so existing callers
keep working through the unchanged unit.h / geolocation.h includes. The new
target has no IfcParse or Qt dependency and so builds under Emscripten, where
the rest of this directory cannot.

One target rather than compiling the sources into each consumer: the glob in
this directory would otherwise put them in libhelpers.a as well, leaving two
copies of the same objects in any link that pulls both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:06:37 +10:00
Dion Moult 79bd3563de Refill the web chunk fetch pipeline from each load completion
Queued chunk loads waited for the next render frame to start, so streaming
advanced at frame cadence rather than as fast as the in-flight cap allowed.
driveStreamingLoads now queues whatever it could not start and every load
completion drains that queue, decoupling fetching from the render loop.

pumpWebChunkLoads is deliberately defined outside the __EMSCRIPTEN__ block
that holds the rest of the byte-range streaming code: driveStreamingLoads
calls it unconditionally and ViewportCore.h declares it unconditionally, so
desktop needs a definition to link against. The body guards itself instead
and compiles to a no-op off the web, where loads are not asynchronous.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:06:37 +10:00
Andrej730 4e887e1c59 Reapply 253918c
Fixes pyodide build. Reverted in af58eaf by accident?
2026-08-10 20:54:36 +05:00
Andrej730 77dc679a6e pytest: fix warnings from using non-collections 2026-08-10 18:39:07 +05:00
Andrej730 109bd58384 ruff: use readable rule names in ignores 2026-08-10 17:06:56 +05:00
Andrej730 b6dccce12a ruff: use readable rule names in selectors 2026-08-10 17:06:56 +05:00
Andrej730 daa7d98b3f ruff: remove unused noqa
Most of them are actually correct, but they're not enforced in general on the repo, so using them blocks us from flagging `unused-noqa` for rules that we actually do use.
2026-08-10 17:06:56 +05:00
Andrej730 321760cea4 bcf: bump required Python version to 3.10
3.9 is EOL
2026-08-10 16:54:37 +05:00
Andrej730 7b9615f4e5 ruff: fix unsorted-dunder-all 2026-08-10 16:54:37 +05:00
Andrej730 ff22a9d1f3 ruff: fix deprecated-import 2026-08-10 16:54:37 +05:00
Andrej730 055f64fa9b ruff: fix quoted-annotation 2026-08-10 16:54:37 +05:00
Andrej730 7370d07db1 ruff: sort imports 2026-08-10 16:54:37 +05:00
Andrej730 717d6aa2af ruff: fix pyprojects using select instead of extend-select by mistake 2026-08-10 16:33:01 +05:00
Andrej730 785936000a ruff: sort imports 2026-08-10 13:35:22 +05:00
Thomas Krijnen 83fc219a8a publish-cpp-api-docs.yml 2026-08-10 06:15:49 +02:00
Thomas Krijnen 7a1dcd07c8 Handle version postfixes 2026-08-10 05:19:17 +02:00
Thomas Krijnen b63137e859 Merge remote-tracking branch 'origin/v0.8.0' into ifcviewer-wgpu 2026-08-10 03:31:02 +02:00
Thomas Krijnen 3d15500976 run black 2026-08-09 14:17:35 +02:00
Thomas Krijnen 64aed6a766 try fix stub 2026-08-09 14:11:49 +02:00
Thomas Krijnen a08eed7ac9 swig ignore ifcopenshell::detail::performance_scope 2026-08-09 14:04:04 +02:00
Thomas Krijnen 076f46cfeb Further propagate logger so that test succeeds 2026-08-09 14:00:42 +02:00
Thomas Krijnen a353edb9e0 check_call() so that init errors surface earlier 2026-08-09 13:59:44 +02:00
Thomas Krijnen 30fb379e32 Remove cwd from import in case you're running tests like I do 2026-08-09 13:16:24 +02:00
Thomas Krijnen dbea3f0362 Reapply skip type bitmap after field reordering changes 2026-08-09 12:56:15 +02:00
Thomas Krijnen b5eca83357 Adapt for namespaces changes 2026-08-09 12:48:35 +02:00
Thomas Krijnen e5aaf7c602 Adapt for namespaces changes 2026-08-09 12:45:00 +02:00
Thomas Krijnen 9e53d0dcc9 Don't bind to reference in order not to overwrite entity instance storage in case of IfcPropertySetDefinitionSet 2026-08-09 12:42:35 +02:00
Thomas Krijnen 17c4d8faff Skip unavailable shape-stat kernels
Generated with the assistance of an AI coding tool.
2026-08-09 11:51:06 +02:00
Thomas Krijnen c9c7edd4d6 Require SWIG 4.1 in CMake
Generated with the assistance of an AI coding tool.
2026-08-09 10:58:05 +02:00
Thomas Krijnen e9fffc221b Silence final compiler warnings
Generated with the assistance of an AI coding tool.
2026-08-09 10:03:41 +02:00
Thomas Krijnen a441757080 Use underscore plugin artifact names
Generated with the assistance of an AI coding tool.
2026-08-09 09:52:17 +02:00
Thomas Krijnen c818f48a47 Own completed iterator results uniquely
Generated with the assistance of an AI coding tool.
2026-08-09 09:14:41 +02:00
Thomas Krijnen 28c9c1d34d Silence remaining compiler warnings
Generated with the assistance of an AI coding tool.
2026-08-09 09:04:28 +02:00
Robert Sigmundsson f05dd4aea5 Fix #9278. calculate_unit_scale raises the SI prefix to the length exponent for prefixed SQUARE_METRE/CUBIC_METRE units.
An SI prefix attaches to the base unit symbol and the prefixed symbol is
raised to the power as a whole: DECI CUBIC_METRE is dm3 = a litre = 1e-3 m3,
not 0.1 m3. The scale factor previously applied the prefix multiplier
linearly for all IfcSIUnits, inflating volumes x100 and areas x10 for such
declarations (produced e.g. by MagiCAD for Revit MEP exports).

Following the reviewer note in #9278, the exponent is taken from the
derived attribute IfcSIUnit.Dimensions rather than from substring matching
on the unit name: the multiplier is raised to LengthExponent only when the
unit's dimensions are a pure power of length, so prefixed derived units
(KILO PASCAL, MEGA NEWTON) and non-length units (KILO GRAM) correctly keep
the linear multiplier. This matches the exponent handling already present
in convert() and named_dimensions in the same module.

Adds regression tests for prefixed AREAUNIT/VOLUMEUNIT and for the
linear-prefix behaviour of PRESSUREUNIT/MASSUNIT.
2026-08-09 08:41:15 +02:00
Thomas Krijnen dcfc22e29e Transfer iterator result ownership
Generated with the assistance of an AI coding tool.
2026-08-09 04:54:51 +02:00
Thomas Krijnen be3c2ee770 Expose geometry types in snake case
Generated with the assistance of an AI coding tool.
2026-08-09 04:44:18 +02:00