Commit Graph

4186 Commits

Author SHA1 Message Date
Petru Conduraru ff798bc989 fix(selector): round() should not crash on non-numeric values (#6776)
FormatTransformer.round() called Decimal() directly on the input value,
which raises decimal.InvalidOperation when the value is a non-numeric
string (a text property, or a value carrying a unit suffix like "12.5 m").
In a spreadsheet export this crashed the entire operation as soon as one
element carried such a value.

Now round() catches InvalidOperation and returns the value unchanged, the
same graceful-fallback convention used by add(). Numeric rounding is
unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 2eea7728d2)
2026-07-25 23:15:23 +10:00
Petru Conduraru 007573cea5 Support block comments in selector filter syntax (#5023)
The filter_elements selector grammar had no way to comment out part of a
query, so users had to delete and retype text to temporarily toggle a
facet. Add a /* ... */ block comment terminal that is ignored by the
lexer, and tolerate a trailing "+" so that commenting out the final
operand (e.g. "IfcWall + /* IfcSlab */") parses cleanly. Comments may
span multiple lines; a /* sequence inside a quoted string is not treated
as a comment. Only the filter grammar is affected, not get_element or
format which use "/" for regex and division.

Adds a regression test and documents the syntax.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 5c11946470)
2026-07-25 23:15:23 +10:00
Ryan Schultz d39b1c56f8 Docs: clarify immediate vs. any-depth spatial selectors
The location and parent filters both match at any depth in the spatial
hierarchy, which surprises users who want only the elements immediately
under a given container. Document that the parent query key resolves the
direct parent only (e.g. query:"parent.Name"="My Site"), add a matching
filter example, and note the immediacy on the parent value key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 0b7e25a3ef)
2026-07-25 23:15:23 +10:00
Petru Conduraru c57da082a5 Make CGAL circle-segments 0-default deflection-driven (rework #8368)
Address maintainer request on #8368: instead of a deflection floor on top
of a fixed CircleSegments count, use one mode or the other. When
CircleSegments == 0 (the new default) the CGAL kernel derives the conic
segment count from MesherLinearDeflection, matching the deflection based
meshing OpenCascade already does and fixing #8051. When CircleSegments is
non zero it is used directly as a fixed, radius independent count.

CircleSegments is only read by the CGAL kernel; OpenCascade meshes by
deflection and never reads it, so the new default has no effect there.

Update the setting description and the ifcconvert / geometry-settings docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 0d70812641)
2026-07-25 23:15:22 +10:00
Stephen Boddy 1132c3c385 Fix lint drift introduced by merging v0.8.0 into lint-pass
- add_stationing_referent.py: black reformat (new drift from v0.8.0).
- update_fallback_position.py: v0.8.0's changes to this file made the
  ifcopenshell.util.unit import (added in an earlier commit here) unused;
  removed per ruff.

(cherry picked from commit c4605f2a8f)
2026-07-25 23:15:22 +10:00
Stephen Boddy 7036ea5c33 Fix ty-ios type-check errors (ifcopenshell-python side)
poe ty's sequence only reaches ty-ios once ty-bonsai passes, so these
never surfaced until now:

- util/alignment.py: drop the stale `include_referent=False` kwarg from
  add_zero_length_segment() - that parameter was removed from the function's
  signature in 45ea5eb07 but this caller in a different file was missed,
  leaving a latent TypeError if this code path is ever exercised.
- ifcopenshell_wrapper.pyi: add the optional trailing `logger` parameter to
  parse_ifcxml/open/construct_iterator*, matching the real SWIG signatures
  in src/ifcwrap/*.i (all declare `Logger& logger = Logger::Root()`) that
  the hand-maintained stub never picked up.
- ifcopenshell/__init__.py: remove a stale `ty: ignore[unknown-argument]`
  comment that ty confirms is no longer suppressing anything.
- assign_cost_item_quantity.py: OPERATORS mixes 2-arg binary operators with
  the 1-arg `operator.neg` (for ast.USub), but FormulaEvaluator has no
  visit_UnaryOp so USub can never reach this lookup via visit_BinOp.
  Suppressed at the call site rather than touching the dict, since this
  looks like scaffolding for unary-minus support rather than dead code.
- Explicit submodule imports (ifcopenshell.geom / api.alignment / util.unit
  / api.aggregate / api.context / api.spatial) added where accessed but
  only reachable by accident of import order.

(cherry picked from commit d5e890bccd)
2026-07-25 23:15:22 +10:00
Richard Brice a674dc5641 Fixes bug with fallback position introduced in 206cd6bb
(cherry picked from commit ade03b171a)
2026-07-25 23:15:22 +10:00
Richard Brice 9fc01de468 Stationing referent can optionally be located relative to the basis_curve (default) or the alignment curve
(cherry picked from commit b5c1b81ede)
2026-07-25 23:15:22 +10:00
Richard Brice 26c6281cc7 Locates positioning referent on the alignment curve, not the basis curve
(cherry picked from commit 47a20f0c7c)
2026-07-25 23:15:22 +10:00
Andrej730 9001cca078 ifcwrap: exclude internal geometry pointers
Still available as `int(xxx.this)`.
2026-07-24 21:51:21 +05:00
Andrej730 018695a2a9 file.ctor: use swig shadowing 2026-07-24 21:50:49 +05:00
Andrej730 2b7f55c1a0 stub: fixes after data model changes 2026-07-24 21:50:49 +05:00
Andrej730 9a62bf3c11 stub: updates after plugins were introduced 2026-07-24 21:50:49 +05:00
Andrej730 310eaedc8e stub: group plugin search paths methods 2026-07-24 21:50:49 +05:00
Andrej730 3c381b0d00 stub: add updated get_info_cpp 2026-07-24 21:50:49 +05:00
Andrej730 4dd39ee918 downstream: stub: drop abstract_arrangement (158756e921)
And also gnore delete_same_facet_edge_pairs as it's more of an interanl API.
2026-07-24 21:50:49 +05:00
Andrej730 af32884731 downstream: stub: add logger_or_root arg type 2026-07-24 21:50:49 +05:00
Andrej730 a20c7484e5 downstream: stub: add missing arrange_polygon_settings (158756e921) 2026-07-24 21:50:49 +05:00
Andrej730 c91d6d54bc new_IfcBaseClass: use ref for safety 2026-07-24 21:50:49 +05:00
Andrej730 a954170927 downstream: ifcwrap: exclude interal geometry pointers
Still available as `int(xxx.this)`.
2026-07-24 21:50:49 +05:00
Andrej730 6fb5a619df downsteram: IfcSchema: provide arg names for register_schema, schema_by_name 2026-07-24 21:50:49 +05:00
Andrej730 849123acee downstream: ifcwrap: hide guess_file_type from Python as unused 2026-07-24 21:50:49 +05:00
Andrej730 9544641e41 downstream: stub: sync added/removed symbols 2026-07-24 21:50:49 +05:00
Andrej730 ec558dc57e stub: add logger_or_root arg type 2026-07-24 18:37:25 +05:00
Andrej730 2db5658386 ifcwrap: hide guess_file_type from Python as unused 2026-07-24 18:22:59 +05:00
Andrej730 4a20b67038 IfcSchema: provide arg names for register_schema, schema_by_name 2026-07-24 16:48:51 +05:00
dependabot[bot] a85d5cc990 Bump lxml from 4.9.1 to 6.1.0 in /src/ifcopenshell-python
Bumps [lxml](https://github.com/lxml/lxml) from 4.9.1 to 6.1.0.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.9.1...lxml-6.1.0)

---
updated-dependencies:
- dependency-name: lxml
  dependency-version: 6.1.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-24 15:04:15 +05:00
Petru Conduraru 21122c0d28 Resolve nested complex quantity paths in the selector (#2041)
get_element_value could not reach the members of an IfcPhysicalComplexQuantity
(or IfcComplexProperty) by their natural path. util.element expands a complex
quantity into a dict whose nested members live under a "properties" sub-dict,
but the selector's dict navigation only looked at the top level, so
"Qto_Custom.Layer1.Width" returned None and IfcCsv exported nothing for it.
Only the internal "Qto_Custom.Layer1.properties.Width" path worked.

When a key is not a direct member of the value dict, descend into its
"properties" sub-dict so nested quantities/properties resolve with the
natural "Set.Complex.Nested" path. Direct keys still take priority, so the
explicit ".properties." path stays backward compatible and the regex branch
is untouched.

Verified: Qto_Custom.Layer1.Width -> 0.1 and Layer1.Height -> 2.5 (were
None), the sibling simple NetArea still resolves, the legacy .properties.
path still works, and IfcCsv now exports the nested value. test_selector.py:
38 passed (adds test_selecting_a_nested_complex_quantity).

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:31:38 +02:00
Dion Moult 466df653b7 docs: add installation pages for bonsaiviewer and ifcviewer
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:15:09 +10:00
Petru Conduraru 82f73c29ea style.assign_representation_styles: fix crash on IfcPresentationStyleAssignment #7883
When replacing a style on an item whose previous IfcStyledItem wraps its styles
in the deprecated IfcPresentationStyleAssignment, and the assignment is not
being reused (use_style_assignment is False, e.g. an IFC4 file authored by
AVEVA E3D), the else branch called remove_same_type_styles(style_assignment)
with style_assignment still None, raising
AttributeError: 'NoneType' object has no attribute 'Styles'. Operate on style_,
the assignment found in the current iteration, instead of the accumulator.
Verified red-green with a minimal IFC4 file using IfcPresentationStyleAssignment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:46:29 +10:00
Petru Conduraru 209c44db83 Selector: negate list comparisons as an aggregate #8129
compare() recursed into list values passing the negated comparison through,
so != meant "at least one item differs" and both = and != matched the same
elements on any multi-valued property (e.g. an enumerated property with two
values selected). Strip the negation for the per-item comparison and negate
the aggregate instead, so != means "no item equals" and stays the complement
of =. The same applies to !*=.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:24:03 +10:00
Petru Conduraru 3d7d1ff4f3 ci: drop the ColumnPSetsOfSets.ifc fixture change, conflicts upstream
Per aothms's review comment: this file's schema was already changed
independently on v0.8.0 since this branch was created, so this PR's own
edit conflicts with it. Reverting to the current upstream version of the
fixture; the bsdd.py rate-limiting fix is untouched.
2026-07-24 14:28:18 +10:00
Petru Conduraru 8c434b7167 ifcopenshell.util.element: dedupe SET-typed attributes in replace_attribute
replace_attribute() rewrites references inside aggregate attributes via
element.walk(), but never checked whether the replacement value was
already present elsewhere in the same aggregate. For an EXPRESS SET
(e.g. IfcProject.RepresentationContexts, IfcRelAggregates.RelatedObjects)
this can leave the same reference listed twice, which is invalid IFC.
LIST and BAG aggregates legitimately allow duplicates, so a blanket dedup
would be wrong; only SET-typed attributes are deduplicated, determined at
runtime from the schema declaration (IfcOpenShell#8706 review comment).

The SET/LIST/BAG check is cached per (schema, class, attribute index), and
the dedup pass itself only runs when a cheap linear pre-check finds the
replacement value already present in the aggregate, so the common case
(no duplicate produced) pays only that pre-check, not a hash-set rebuild.
Benchmarked against a 23MB (431k entities) and a 104MB (2.4M entities) IFC
model against a large SET attribute: worst case adds well under 1ms per
call; the realistic case (merging duplicate contexts, matching the PR
#8706 scenario) shows no measurable regression.

Fixes the root cause flagged in IfcOpenShell#8706 (Moult), obviating the
need for MergeDuplicateContexts' own manual aggregate-dedup pass for that
scenario.

Generated with the assistance of an AI coding tool.
2026-07-24 14:21:31 +10:00
Petru Conduraru 8fb8966094 docs: cover reading properties and quantities from an element and its type in C++ getting started
Fixes issue #3910's documentation gap. IsDefinedBy() returns
IfcRelDefinesByProperties relationship objects, not the property set
itself, and RelatingPropertyDefinition() must be used to reach the
IfcPropertySet or IfcElementQuantity. Properties can also come from an
element's type via IsTypedBy() -> RelatingType() -> HasPropertySets(),
a path that is easy to miss because it works differently. Adds a
worked, beginner-commented, compilable example covering both paths.

Generated with the assistance of an AI coding tool.
2026-07-24 06:14:34 +02:00
Petru Conduraru e23142d01a Fix #7331: derive cost item quantities from IfcSpace base quantities
assign_cost_item_quantity skipped every IfcSpatialElement, which also
swallowed IfcSpace. Spaces are legitimate quantifiable objects, so their
Qto_SpaceBaseQuantities (for example GrossFloorArea) were never picked up
and count based cost items fell back to 0. Keep skipping spatial
containers (site, building, storey) but allow IfcSpace.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 19:58:44 +02:00
carlopav 305f8c6003 cost: don't leave copied cost items in the copied schedule (#8851)
copy_cost_item appends the copy to the inverse relationships of the
original cost item, which for a root cost item includes the source
schedule's IfcRelAssignsToControl. copy_cost_schedule then assigned that
same cost item to the new schedule as well, so the copies showed up in
both schedules and deleting them from one removed them from the other.

Unassign the copy from the source schedule before assigning it to the
new one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:04:38 +02: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
Andrej730 1e4b4b5557 test_getting_elements_by_profile: fix test 2026-07-22 19:01:50 +05:00
Andrej730 5786d1e4bd test_global_id_updated: fix expected message (a07f56db6) 2026-07-22 19:01:50 +05:00
Andrej730 1bc5134578 ifcopenshell: fix stale reference to _file 2026-07-22 19:01:50 +05:00
Andrej730 298e4cfd8d instance_streamer: migrate to snake case 2026-07-22 19:01:50 +05:00
Andrej730 e98a3a5c2c test_create_shape: fix use of renamed OutputFormat 2026-07-22 19:01:50 +05:00
Andrej730 ab38f4f7f6 Consider new geometry settings 2026-07-22 19:01:50 +05:00
Andrej730 01cfbe6948 Consider new serializer settings 2026-07-22 19:01:50 +05:00
Andrej730 872efac4d2 parse_ifcxml: drop the use of the removed method 2026-07-22 19:01:50 +05:00
Andrej730 32f2dfd294 downstream: logger: reuse logger_or_root, dedupe optional-logger-arg pattern 2026-07-22 19:01:50 +05:00
Andrej730 d45174066f downstream: logger: use Logger* instead of Logger& to propagate signature using swig 2026-07-22 19:01:50 +05:00
Andrej730 e50bace056 stub: drop serializer classes
Superseded by generic `create_geometry_serializer`
2026-07-22 19:01:50 +05:00
Andrej730 5f0f4669f2 Fix using logger.Root instead of logger.root 2026-07-22 19:01:50 +05:00
Andrej730 c4e411c735 Drop use of removed entity_instance.wrapped_data 2026-07-22 19:01:50 +05:00