Commit Graph

150 Commits

Author SHA1 Message Date
Dion Moult 19a1d88970 Fix all ty diagnostics on ifcviewer-wgpu (ci-lint ty-ios + ty-bonsai)
This branch carried v0.8.0's strict `[tool.ty.rules] all = "error"` config but
not the source fixes that were made upstream to satisfy it, so both ci-lint ty
gates were failing: `poe ty-ios` reported 256 diagnostics and `poe ty-bonsai`
258. Both are now clean.

Most fixes are ported from v0.8.0 and follow two idioms: initialise a name
before a conditional that may not bind it (plus an `assert` where the invariant
is real but not provable), and close an exhaustive `if`/`elif` chain with
`else: assert False, <discriminant>`.

The branch's own newer accessors are preserved throughout - `.file`,
`.declaration`, `file.types()`, `get_max_id()` are kept rather than reverted to
`wrapped_data.*`, and non-ty upstream changes (notably the in-progress geometry
cache removal) are deliberately not pulled in.

Notable fixes that are not straight ports:

* ifcopenshell_wrapper.pyi: `entity_instance.file` was declared as
  `def file(self) -> file`, where the property name shadows the `class file`
  below it, so the annotation resolved to `Unknown`. Every `element.file` in
  the codebase was therefore unchecked. Qualifying it to `ifcopenshell.file`
  restores `.schema` to its Literal union and surfaces no new diagnostics.

* model/wall.py: a duplicated merge fragment in the void-straddle path ran an
  always-true `if void_straddles:` that read `new_opening` from the mutually
  exclusive branch (stale value, or NameError on the first iteration), followed
  by an unreachable duplicate `elif`. Removing it makes the file match v0.8.0.

* light/operator.py: upstream's own fix unpacks three targets from two values
  and raises ValueError unconditionally; corrected to `None, None, None`.

* assign_system.py, validate.py, geom/main.py: walrus-in-genexp is valid at
  runtime (PEP 572 binds in the containing scope) but ty does not model it;
  rewritten as explicit loops, matching upstream.

Verified: poe ty-ios, poe ty-bonsai, ruff check src/ nix/, black --check .,
and compileall -W error at py3.10 (ifcopenshell-python) and py3.11 (bonsai).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 19:14:39 +10:00
Dion Moult 291d7d8441 black . 2026-07-26 18:03:09 +10:00
Petru Conduraru e308ecaeb4 ifc5d: match cost schedule export columns to the Bonsai cost panel (#6251)
Stefano's final ask on #6251 was specific: the ODS/XLSX export should
show exactly what the cost panel shows, ID (Identification), Name,
Quantity, Value, Total Cost, no more, no less. The previous fix in
this PR removed the internal bookkeeping columns but still exported
Description, Unit and a per-category cost breakdown (Labor Cost,
Material Cost, etc), none of which appear in the panel.

Presentation formats (.ods/.xlsx) now use an explicit allow-list of
columns instead of a block-list of internal ones, and relabel headers
to match the panel's own wording (ID / Value / Total Cost). The .csv
format is unchanged: csv2ifc still reads back the extra bookkeeping
columns for the import round trip, which is why it keeps them.

Also add a "Download CSV" button to the browser costing view
(Generate spreadsheet browser), which previously only offered a
clipboard-based Copy Selected. It reuses the already-rendered table
(respecting the user's column visibility settings) and triggers a
real file download, dropping only the UI-only Actions column.

AI-generated with Claude Code; reviewed and tested by Petru Conduraru.

(cherry picked from commit 1df738d968)
2026-07-25 23:18:33 +10:00
Petru Conduraru 1d1bb2276d ifc5d: professional grade ODS/XLSX cost schedule export #6251
Three defects reported against the Costing tab export:

1. XLSX export crashed with ModuleNotFoundError: xlsxwriter was never
   bundled with Bonsai. Port the writer to openpyxl, which ifccsv
   already uses and Bonsai already ships, so it works out of the box.
2. Every ODS cell was written as a string (numbers as text), and the
   formula branch was dead code: it compared against 'Total Price' /
   'Rate Subtotal' while the headers are 'TotalPrice' / 'RateSubtotal'.
   Numeric columns are now typed float cells and TotalPrice becomes a
   real formula: Quantity*RateSubtotal on leaf items, SUM over the
   direct children's TotalPrice cells on sum items.
3. Internal bookkeeping columns (Id, ItemIsASum, Hierarchy, Index,
   Quantities) leaked into the presentation formats. ODS/XLSX now hide
   them; CSV keeps them since csv2ifc consumes them for the round trip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 98c28a1f30)
2026-07-25 23:18:33 +10:00
Petru Conduraru 33f1aa8104 Bonsai: compute earthworks base quantities in ifc5d take-off #6325
The ifcopenshell take-off engine left every Qto_EarthworksFillBaseQuantities
value null, so Bonsai added the qset with no numbers on IFC4X3 models. Map the
geometrically derivable quantities using the slab axis convention: Length on
local X, Width on local Y, Depth on local Z, and the net solid volume as the
compacted (Fill) or undisturbed (Cut) volume. LooseVolume and Weight stay
unmapped because they need soil bulking and density factors absent from
geometry. Bring IfcEarthworksCut to parity with the Blender engine and wire
IfcReinforcedSoil on both engines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit d506f5df1e)
2026-07-25 23:18:32 +10:00
Petru Conduraru 3f42e02cb4 Fix #8570: populate ifc5d IfcOpenShell QTO formulas for IfcSpace
The headless "IfcOpenShell" calculator had all Qto_SpaceBaseQuantities
formulas set to null for IfcSpace in both IFC4QtoBaseQuantities.json and
IFC4X3QtoBaseQuantities.json, so qto.py's `if not formula: continue`
skipped every quantity, no geometry task was queued, and spaces never
appeared in results (elements_quantified: 0). The Blender calculator
already computes these; they were just never ported to the
ifcopenshell.util.shape-backed calculator.

Map the eight computable quantities to existing util.shape functions,
mirroring the Blender calculator semantics (no new shape.py code):
GrossFloorArea=gross_get_footprint_area, NetFloorArea=net_get_footprint_area,
GrossCeilingArea=gross_get_top_area, NetCeilingArea=net_get_top_area,
GrossPerimeter=gross_get_footprint_perimeter, GrossVolume=gross_get_volume,
NetVolume=net_get_volume, Height=net_get_z.

Left null (matching the Blender ruleset, not guessed): GrossWallArea,
NetWallArea, NetPerimeter (Blender stub), and FinishFloor/CeilingHeight
(Blender derives these from sibling IfcCovering decomposition geometry,
which this per-element calculator architecture can't reach).

Verified on IFC4 (4x3 space extruded 2.5m): before -> {} / elements_quantified 0;
after -> GrossFloorArea 12, GrossPerimeter 14, Height 2.5, GrossVolume 30,
etc. - all exact matches to the extrusion. IFC4X3 formulas are identical
and the formula->function resolution is schema-agnostic.

Scope: fixes the IfcSpace case (the issue title). The 12 other all-null
classes noted in the issue (IfcDoor, IfcSite, IfcRailing, ...) are left as
follow-up.

This change was made with the assistance of an AI tool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 6085894433)
2026-07-25 23:18:32 +10:00
Petru Conduraru 87dd026f94 Fix IfcCovering Qto_CoveringBaseQuantities mismatch between calculators (#6728)
The ifc5d IfcOpenShell (geometry-based) Qto engine computed
Qto_CoveringBaseQuantities using axis-agnostic heuristics:

- GrossArea/NetArea: gross_get_max_side_area / net_get_max_side_area,
  the largest of the X/Y/Z projected side areas.
- Width: gross_get_min_xyz, the smallest of the X/Y/Z dimensions.

The Blender Qto engine instead already used
EPset_Parametric.LayerSetDirection (AXIS2 for wall-like coverings,
AXIS3 for floor/ceiling-like coverings) to pick the correct axis via
get_covering_gross_area/get_covering_net_area/get_covering_width in
bonsai/bim/module/qto/calculator.py.

For any covering whose length isn't the largest dimension (e.g. a
short wall-covering strip, or a small covering patch), the two
engines' heuristics can pick different faces/axes entirely, giving
different Width/Area values for the same element - this is what was
reported in #6728.

Fix: give the IfcOpenShell engine the same layer-set-direction
awareness. Added IfcOpenShell.get_covering_parametric_axis/
get_covering_area/get_covering_width (dispatched as internal
functions, like the existing get_weight/get_segment_length), and
wired gross_get_covering_area/net_get_covering_area/
gross_get_covering_width into the IfcCovering rules in
IFC4QtoBaseQuantities.json and IFC4X3QtoBaseQuantities.json.

The AXIS2 area/width formulas (get_side_area, net_get_y) intentionally
match the simpler formulas already used for Qto_WallBaseQuantities in
this same rule set (net_get_side_area/net_get_y), rather than
replicating the Blender engine's more elaborate get_lateral_area/
get_width (min(X,Y)) helpers, consistent with how the two engines
already diverge for regular walls without being considered a bug.

Verified with a standalone script driving ifc5d.qto.IfcOpenShell
directly against synthetic AXIS2/AXIS3 IfcCovering geometry: for
typical proportions old and new formulas agree, and for
disproportionate coverings (thin dimension not the smallest/largest)
the old formulas picked the wrong axis while the new ones correctly
track the covering's LayerSetDirection, matching the Blender engine.
Did not verify through the full Blender/Bonsai UI, as it would have
required registering the addon in the machine's shared Blender
profile, which is unsafe while other agents may have it loaded.

Generated with the assistance of an AI coding tool.

(cherry picked from commit c61ebe3876)
2026-07-25 23:18:32 +10:00
Petru Conduraru c5f25405a2 ifc5d: wire up GrossFootprintArea/NetFootprintArea for IfcWall QTO
Root cause: the IfcOpenShell-geometry-engine calculator ruleset
(IFC4QtoBaseQuantities.json and IFC4X3QtoBaseQuantities.json) left
IfcWall's GrossFootprintArea/NetFootprintArea mapped to null, so
these two quantities were silently omitted from Qto_WallBaseQuantities
whenever that ruleset was used. The generic gross_get_footprint_area
and net_get_footprint_area formulas already exist and are already
wired up for IfcSlab in the same files, so this was a missing mapping,
not a missing implementation.

Fixes #7029.

Generated with the assistance of an AI coding tool.

(cherry picked from commit f3cb7aea61)
2026-07-25 23:18:32 +10:00
Petru Conduraru 0d8813348c Fix IfcFooting Qto_FootingBaseQuantities axis mapping per predefined type #4783
Footings are authored two ways with different local axis conventions. Beam-like
footings (STRIP_FOOTING, FOOTING_BEAM) are a profile extruded along local Z, so
Length is local Z and the cross section sits on local X (Width, horizontal) and
local Y (Height, vertical). Slab-like footings (PAD_FOOTING, PILE_CAP) have their
footprint on local X/Y and their thickness (Height) on local Z.

The engine rule set is keyed per IfcFooting and cannot branch on predefined type,
so the previous static rule (Height=net_get_z, Length=net_get_max_xy, Width=null)
swapped Length and Height for beam-like footings and never emitted Width.

Add predefined-type-aware get_footing_length/width/height to the IfcOpenShell and
Blender calculators, and point the IfcFooting rule at them in all four IFC4/IFC4X3
ios/Blender rule files.

Confirmed by authoring footings through the real Bonsai generators and measuring
world-axis orientation: a beam-like footing with a 0.3 wide by 0.6 tall cross
section and 6.0 run reports Length 6.0, Width 0.3, Height 0.6, with the 0.3
physically horizontal and 0.6 physically vertical; a 2.0x1.5x0.3 pad reports
Length 2.0, Width 1.5, Height 0.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 4ceadd8f10)
2026-07-25 23:18:32 +10:00
Petru Conduraru be56984e12 ifc5d: measure openings in their real orientation on both take-off engines
See #6835. Qto_OpeningElementBaseQuantities came out axis-scrambled for
openings authored in a Z-up local frame (X along the voided wall, Y
through it, Z vertical), which is how Bonsai authors every wall opening:

- The IfcOpenShell engine mapped Height to the local Y extent and Depth
  to the local Z extent, so a 0.9 x 2.0 door opening with Bonsai's
  default 1.2m void depth reported Height 1.2 and Depth 2.0, and Area
  (max side area) picked the through-wall side, 2.4 instead of 1.8.
  This matches the wrong Height=1.2/Area=1.2 screenshots reported for a
  1x1 window opening in #6835.
- The Blender engine mapped opening Width to get_length, which returns
  the longest bounding box edge, i.e. the opening height for typical
  door openings (the same defect 4adaf0d fixed for IfcDoor Width), and
  get_opening_depth used min(x, y), which returns the opening width
  whenever the width is smaller than the void depth.

The IfcOpenShell engine now has opening-aware internal calculators
(get_opening_width/height/depth/area) that detect horizontal (slab
style) openings with the same heuristic as the Blender calculator, so
slab opening depths keep reporting the slab thickness. The Blender
ruleset uses get_x for opening Width, and get_opening_depth measures the
through-element Y extent for vertical openings.

Door and window quantities themselves are addressed separately: the
Blender engine door Width was fixed in 4adaf0d, and the remaining
door/window defects (door not quantified on the IfcOpenShell engine,
inflated areas) are fixed by the attribute-based calculators in #8389.

Generated with the assistance of an AI coding tool.

(cherry picked from commit efac8a0ec0)
2026-07-25 23:18:32 +10:00
Andrej730 3536bcf46d entity_instance: switch to use of declaration as a property
Fixes most of the bonsai tests.
2026-07-22 19:01:51 +05:00
Thomas Krijnen 7fc2d9a998 Merge remote-tracking branch 'origin/v0.8.0' into ifcviewer-wgpu 2026-07-09 13:21:39 +02:00
Bruno Postle a0ce930994 ifc5d: fix two csv2ifc bugs found by round-trip test
ItemIsASum and Quantities are exporter columns that were missing from
MAIN_CSV_HEADER_COLUMNS, causing them to be misidentified as numeric cost
value categories on re-import. Also initialise rate_cost_schedule to None
before the search loop to avoid UnboundLocalError when no match is found.

Generated with the assistance of an AI coding tool.
2026-07-04 16:08:43 +01:00
Massimo Fabbro 714105b9fd See #6570. Tests for import cost schedule from csv and minor fix 2026-07-02 08:40:58 +02:00
Massimo Fabbro f0b5ab860f See #6570. Formula column minor improvements and documentation 2026-07-02 08:40:58 +02:00
Massimo Fabbro 528964ca56 See #6570. Formula column for ifc5d import from csv
Now it's possible to specify the Formula column in the csv in order to calculate cost item quantities
2026-07-02 08:40:58 +02:00
carlopav 4d3bff4e3a feat(ifc5d): include quantity Formula in serialised Quantities
IfcQuantity* carries an optional Formula (IfcLabel) documenting how a
quantity was derived. Export it alongside each quantity so it survives
in the Quantities column.

The per-quantity entry shape grows from [name, value] to
[name, value, formula], which stays backward compatible for positional
consumers reading index 0/1. Formula is read with a schema-safe getattr
(it does not exist on IfcPhysicalComplexQuantity, nor in IFC2X3) and is
coalesced to "" when absent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-16 13:34:46 +02:00
carlopav 074021de70 fix(ifc5d): escape quantity names when serialising Quantities to JSON
serialise_cost_quantities built the "Quantities" JSON string by manual
concatenation, inserting quantity.Name and the related element's Name
without any escaping. A name containing a double quote, backslash or
newline produced invalid JSON, breaking any downstream parser (e.g. a
Typst json.decode consumer reporting "failed to parse JSON"). It also
crashed with a TypeError when a name was None (str += None).

Build a Python list and serialise it with json.dumps instead, keeping
the exact same [[name, value], ...] output shape, the element-name
prefix and the unsupported-type behaviour. None names are coalesced to
"" and quantity values are defensively coerced to float.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-16 06:35:06 +02:00
carlopav 11154baa39 IfcCostSchedule PDF export with typst: fix bugs
Fixed a bug when a summary cost has no sum applied.
Added Currency in table header.
Cleanup.
Added guards for end summary.
2026-06-11 18:34:37 +02:00
Massimo Fabbro 00f6241417 See #6853. Minor fix for IfcDoor with IFC4x3 quantity calculation with blender engine 2026-06-11 18:25:47 +02:00
carlopav 3f680f5c21 IfcCostSchedule PDF export with typst: fix bugs
Fixed a bug when a summary cost has no sum applied.
Added Currency in table header.
Cleanup.
Added guards for end summary.
2026-05-29 18:38:16 +02:00
Massimo Fabbro 4adaf0d61f See #6853. Minor fix for IfcDoor with IFC4x3 quantity calculation with blender engine 2026-04-20 17:55:49 +02:00
Thomas Krijnen 91ae631c7d Merge remote-tracking branch 'origin/v0.8.0' into datamodel-v1.0 2026-04-18 20:15:28 +02:00
Andrej730 953c5fddc6 bonsai - sort imports 2026-01-26 17:13:18 +05:00
Andrej730 72f8218fcd ruff - check for more unused imports 2026-01-26 17:10:02 +05:00
Thomas Krijnen b66b04b001 Remove usage of .wrapped_item and some other fixes 2026-01-10 11:01:18 +01:00
Andrej730 13be6ccd45 Sort imports 2025-12-19 18:53:14 +05:00
Massimo Fabbro 87283874a1 Add simple examples for ifc5d import from csv 2025-11-22 09:02:53 +01:00
Andrej730 9491096485 black . 2025-11-13 19:13:27 +05:00
kaplus e8eb5e4d0a Add continue statement to skip duplicate calculations
Skip general path to avoid duplicate calculations.
2025-11-12 13:14:38 +01:00
Andrej730 24bee01f01 black . 2025-11-03 18:33:34 +05:00
Ryan Schultz 7329c485ea Fix quantify() regression where by_type queries and grouped iteration could apply QTOs to all entities in the model instead of the intended element subset. 2025-10-31 10:03:52 -05:00
Bruno Postle 90bf56d9ba Fix ifc5d documentation
Documentation claimed that ODS/XLSX output was to a given filename, but
the code actually writes files with generated names in a folder.

(also create the folder if necessary for convenience)
2025-10-24 22:19:01 +01:00
Massimo Fabbro ac5a44a962 See #6570. Now it's possible to specify Description in csv import
Now it is possible to specify the column Description in the csv file in order to specify also the cost item description
2025-10-05 17:49:23 +02:00
Thomas Krijnen b5b64d6c4a ifc5d.quantify: lookup entity names directly; index into dict rather than iterate in simple cases 2025-09-15 18:29:58 +02:00
Andrej730 10e805b3f1 black . 2025-09-10 15:34:06 +05:00
Massimo Fabbro e28979ceb7 See #6570. Better column name for import csv.
Change column name for import rate from csv from Rate Cost Schedule to RateSchedule
2025-09-10 06:50:50 +02:00
Andrej730 a9c0c1f4ba control.unassign_control to support batching 2025-09-09 15:42:32 +05:00
Andrej730 abfb56350d control.assign_control to support batching 2025-09-09 15:42:32 +05:00
Massimo Fabbro 5400712a7a Black 2025-09-06 07:20:48 +02:00
Massimo Fabbro 1dea8ca37b See #6570.Keep ifc5d independent from Bonsai 2025-09-06 07:20:48 +02:00
Massimo Fabbro 25c0ed6759 Black 2025-09-04 16:33:03 +02:00
Massimo Fabbro f75d629990 See #6570. Now it is possible to define the rate in import csv
In order to assign the rate in the csv import, use the columns 'Rate cost schedule' and 'RateID'
2025-09-04 16:33:03 +02:00
carlopav e00a865bce Various fixes to IfcCostSchedule PDF export (#6983)
* IfcCostSchedue PDF export: changed Output type from off to auto

* Fixed currency value

* Fix summary total if no nested structure present
2025-08-04 08:46:20 +00:00
carlopav dcf8329b04 Pdf ifc cost schedule export improvements (#6884)
* Export_IfcCostSchedule_to_PDF_improvements

* IfcCostSchedule CSV export: Added ItemIsASum column

New column in the ifc export that tracks if IfcCostItem is a sum, also added a new static method to the IfcDataGetter class.

* IfcCostSchedule CSV export: Added cost quantities column

Cost quantities are a serialsed list containing the name of the quantity and the quantity value.

* IfcCostScheduel PDF export: add options to fine tune export

New options include nested_structure_depth, should_print_cover, should_print_description, should_print_rates, should_print_summary, should_print_cost_ids.

Also pass project currency to typst (still not used).

Added footer with "proudly created with IfcOpenShell".

Updated Cover with formatting and IfcCostSchedule Description
2025-07-21 11:34:00 +05:00
Andrej730 0f3b570800 Fix get_segment_length bug with non-parametrized profiles (f9c4b6a)
`settings` wasn't defined, so `create_shape` was always resulting in an error. Also, `create_shape` is returning `Triangulation` in this kind of cases, so there's no `.geometry`.

Also removed IfcCircleProfileDef if-check since it's covered by IfcParameterizedProfileDef.
2025-07-15 17:47:37 +05:00
carlopav 5f5c0b41d5 Basic infrastructure for IfcCostSchedule PDF export with typst (#6860) 2025-07-03 13:18:03 +00:00
Andrej 02d359d0e6 Fix Ruff UP035 (also deprecated annotation symbols) 2025-06-09 18:03:19 +05:00
Massimo Fabbro eade620581 Add calculating functions for Qto_CourseBaseQuantities for IfcCourse elements 2025-05-31 14:57:46 +02:00
Andrej 1ab288de0b ifc5d - fix <py3.11 compatibility issue (6499ac4) 2025-05-29 19:14:43 +05:00