Compare commits

...

37 Commits

Author SHA1 Message Date
Petru Conduraru 3d15f581d1 util.doc: fix the same truncation in pset and property descriptions
The property-set and property extraction sites had the identical
first-paragraph truncation: descriptions ending at a colon lost their
bulleted lists and follow-up paragraphs. Route all four sites (IFC2X3 +
IFC4, pset-level and property-level including recursive children) through
extract_full_description, and make the existing HISTORY: changelog split
case-insensitive, since 21 pset docs use "History:" which only becomes
reachable once the walk passes the first paragraph.

Data refreshed from the same local buildingSMART doc sources with the
merge-only-description discipline: IFC4 376 descriptions completed
(34 pset + 342 property/child), IFC2X3 147 (20 + 127), zero artifact
regressions, all other fields byte-identical.

Example: Pset_DamperOccurrence.SizingMethod previously ended at
"...nominally or with exact measurements:" and now includes
"NOMINAL: Nominal sizing method. EXACT: Exact sizing method."

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 15:17:55 +03:00
Petru Conduraru 5977587f03 Trim a dangling remark fragment from IfcStructuralItem description
Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 15:14:04 +03:00
Petru Conduraru 6490e3ffa9 Cut lazy HISTORY remark leaks from extracted descriptions
Three IFC2X3 type descriptions leaked HISTORY remark text that is not
wrapped in a blockquote in the source markdown: a literal "> HISTORY ..."
tail inside the definition paragraph (IfcEnergyMeasure,
IfcHeatFluxDensityMeasure) and an inline "HISTORY: ..." sentence
(IfcMetricValueSelect). Teach extract_full_description to skip leading
HISTORY paragraphs and cut at inline/lazy remark markers, and clean the
three affected data entries (all other descriptions unchanged).

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 15:13:18 +03:00
Petru Conduraru 5a7ab0ca68 util.doc: stop truncating entity descriptions at the first paragraph (#4624)
get_entity_doc returned incomplete descriptions for many entities and
types, e.g. IFC4 IfcAlarmType ended mid-sentence at "The set of shared
information may include:". The DocExtractor took only the FIRST <p> of the
markdown-derived HTML (BeautifulSoup .find("p").text), silently dropping
any bulleted list embedded in the definition and every paragraph after it.

Add DocExtractor.extract_full_description, which walks all top-level
<p>/<ul>/<ol> elements in document order (list items rendered as "- item"),
stops before any <blockquote> (HISTORY/NOTE remarks), and strips inline
kramdown attribute markers. The four entity/type extraction sites now use
it; the property-set extraction sites are left untouched to keep this
change scoped.

The shipped schema JSON data is refreshed from the buildingSMART IFC doc
sources (IFC4.0.2.1 and Ifc2.3.0.1): 1491 entity/type descriptions are
completed. Only top-level description fields change; attribute
descriptions and all other fields are byte-identical to the previous data,
and 11 regenerated descriptions containing extraction artifacts were kept
at their previous text.

Verified: IfcAlarmType now includes the full "may include" list and the
closing paragraph; IfcBeamType, IfcWindow and IFC2X3 IfcWallStandardCase
spot-checked complete with no HISTORY leakage.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:32:40 +03:00
Ryan Schultz 0b7e25a3ef 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>
2026-07-11 16:44:22 -05:00
Ryan Schultz d16c283aef Add bulk-load of selected drawings' annotations (#8525)
SHIFT+CTRL+CLICK on Activate Drawing now imports the
annotations of all selected drawings without switching
the active view or camera, then selects their cameras with
the first as active. SHIFT+CTRL+ALT+CLICK also selects the
loaded annotation objects. The drawing camera is imported
when missing so annotations land in the correct collection.
Loading is idempotent.

Generated with the assistance of an AI coding tool.
2026-07-11 15:54:20 -05:00
Petru Conduraru a0f493b471 IfcConvert: report an error when the output file cannot be opened (#438)
Converting to a path whose directory does not exist (or is not writable)
failed silently: the serializer's ready() check correctly returned false,
but IfcConvert deleted the temp file and returned EXIT_FAILURE without any
message, so the user saw no reason for the failure.

Log a SYS error naming the output file before returning, matching the
existing "Unable to open output file" reporting used elsewhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:28:51 +02:00
Petru Conduraru e389939092 serializers: expand IfcPropertySetDefinitionSet in XML output (#6330)
Property sets contained in an IfcPropertySetDefinitionSet were exported as
an empty element in XML. The XmlSerializer already had a block to expand
such a set into its member property sets, but it was gated behind
#ifdef SCHEMAS_HAS_IfcPropertySetDefinitionSet while the schema generator
emits SCHEMA_HAS_IfcPropertySetDefinitionSet (singular). The plural spelling
is defined nowhere, so the block was dead code and a RelatingPropertyDefinition
holding a set produced nothing.

Correct the macro name so the set is expanded and its property sets are
serialized. The parse layer already reads these nested sets (they are
reachable from util.element), so this only completes the XML path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:26:49 +02:00
Petru Conduraru 380675e214 ifcparse: strip XML-illegal control characters in escape_xml (#2043, #3074)
escape_xml escaped the five XML metacharacters but passed control
characters (0x00 to 0x1F other than tab, newline and carriage return)
through unchanged. Those bytes are illegal in XML 1.0 and cannot be
represented even as numeric character references, so any IFC string
containing them produced non-well-formed XML and SVG output.

Strip those illegal control characters before escaping. Bytes belonging to
a valid UTF-8 multibyte sequence are always >= 0x80, so filtering on the low
control range leaves real text intact. This is the shared helper used by the
SVG serializer text and attribute sites (audited: all route through it) and
by the XML/Collada paths, so both reports are resolved at one place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:20:47 +02:00
Petru Conduraru 3e55c5126c ifcgeom: honour PnIndex in triangulated and polygonal face sets (#3434)
IfcTriangulatedFaceSet and IfcPolygonalFaceSet used CoordIndex values to
index Coordinates.CoordList directly, ignoring the optional PnIndex
attribute. When PnIndex is present it remaps point references, so a
CoordIndex value i must resolve as CoordList[PnIndex[i-1]-1] (both 1-based).
Without the indirection any model carrying a PnIndex was built from the wrong
points.

Add a resolve() helper in both mappings that applies the PnIndex indirection
when present and is a plain bounds-checked lookup otherwise, with bounds
checks at both index levels. When PnIndex is absent the behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:19:06 +02:00
Petru Conduraru 7e3d2f936d build: do not request the header-only Boost.System component (build against Boost 1.70+)
Boost.System has been header-only since Boost 1.69 and its compiled stub
library was removed in newer Boost, so listing system in the requested
find_package components makes configuration fail on Boost 1.70 and up (for
example Boost 1.90 errors with "Could not find boost_system"). Boost.System
is still pulled in transitively by thread / iostreams where it is needed, so
drop it from the explicit component list.

Verified: with this change IfcOpenShell configures and builds IfcConvert
cleanly against Homebrew Boost 1.90 and OpenCASCADE 7.9.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:16:35 +02:00
Petru Conduraru 0d70812641 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>
2026-07-11 15:12:53 +02:00
Petru Conduraru dd9fa65629 Fix cgal kernel under-tessellating large-radius arcs (#8051)
The CGAL kernels (cgal and cgal-simple) allocate arc segments as a
fraction of the full circle via CircleSegments, ignoring the radius.
A large-radius arc that spans a small angle therefore collapsed to a
single chord, turning curved curtain-wall mullions straight while the
OpenCascade kernel (which meshes by deflection) kept them curved.

evaluate_conic now also enforces a deflection-based floor on the number
of segments, keeping the chord deviation within mesher-linear-deflection,
matching OpenCascade. Small circles are unchanged (CircleSegments floor
still dominates); only large-radius curves get denser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:12:53 +02:00
Petru Conduraru eb7324e7fc IfcConvert: add --fail-on-error to exit non-zero when conversion logs errors (#1118)
IfcConvert returned a success exit code even when geometry conversion logged
errors and silently dropped elements (for example a failed TopoDS::Shell build
under layerset slicing produced valid looking output with most objects
missing), so CI and scripts could not detect a partial conversion.

Add an opt-in --fail-on-error flag that makes IfcConvert exit non-zero when any
error was logged during processing, reusing the existing MaxSeverity based
failure check already used for --validate. The default exit behaviour is
unchanged, so pipelines that tolerate individual element failures are
unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:10:03 +02:00
Petru Conduraru 061bb90d50 Warn when a face inner boundary intersects another boundary (#527)
A face whose inner boundary crosses the outer boundary (or another inner
boundary) is invalid per the schema. Open Cascade silently heals or drops
such a face, so the intended hole is lost or the face is corrupted with no
diagnostic at all (the 2018 report saw a dropped face; on the current line
the face survives as wrong geometry, still silently).

After the wires are collected, if a face has inner boundaries, measure the
BRepExtrema distance between each inner wire and every earlier wire. Two
non intersecting loops have strictly positive distance, so a distance at
or below the modelling precision means the boundaries touch or cross; emit
a warning (GEO 402) naming the offending face. This is diagnostic only, no
geometry change.

The message is emitted via the kernel logger() rather than Logger::Root():
IfcConvert configures a local Logger and worker logs merge into it, while
Logger::Root() is a separate unconfigured singleton whose messages are
discarded (a latent issue affecting some existing GEO messages too).

Verified on OCC 7.9.2 with synthesized IFC4 faces: an inner triangle
crossing the outer edge, and one straddling the bottom edge, each emit one
GEO 402; a valid 4x4 hole emits none and triangulates identically (area
84.0), in both sequential and multithreaded runs. Pure inner self
intersection and full containment are distinct classes and intentionally
left untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 14:39:03 +02:00
Petru Conduraru a8d0ef3437 Add AI-generated marker to IfcAsymmetricIShapeProfileDef.cpp
Comply with AGENTS.md: new AI-generated files must carry a top-of-file
comment indicating AI assistance.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 14:24:10 +02:00
Petru Conduraru 438c0955f2 Map IfcAsymmetricIShapeProfileDef standalone in IFC4+ (#1367)
In IFC2X3 IfcAsymmetricIShapeProfileDef is a subtype of
IfcIShapeProfileDef, so the IfcIShapeProfileDef mapping dispatched it by
inheritance. From IFC4 onwards it is a standalone subtype of
IfcParameterizedProfileDef, so nothing mapped it and the extruded solid
came out empty (GEO326, 0 verts).

Add a dedicated map_impl that builds the twelve-point asymmetric section
(independent bottom/top flange widths, thicknesses, fillet/edge radii and
flange slopes), plus a guarded BIND. Both are wrapped in
SCHEMA_IfcAsymmetricIShapeProfileDef_HAS_BottomFlangeWidth, which is only
defined where the type is standalone, so IFC2X3 keeps its existing
subtype route unchanged.

Verified on OCC 7.9.2: an IFC4 asymmetric extrusion goes from 0 verts to
a correct 72-vert solid (bottom flange wider than top); IFC2X3 output is
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 14:24:10 +02:00
Stephen Boddy b9deb9c63d Git ignores CLAUDE.local.md file
This allows a file that will be automatically picked up by Claude. It can either
be a copy of a CLAUDE.md, or a one line file pointing to a shared common file. i.e.

@~/.claude/conventions-ifcopenshell.md
2026-07-11 13:10:14 +01:00
sboddy e14b3ec8a0 Merge pull request #8243 from sboddy/feature-5753-autosave
Feature #5753 - Autosave for ifc files

Merging because it could be a life saver. It is hidden behind an option and is off by default.

- Provides the option have an autosave file created periodically (duration in prefs).
- Can be set to save immediately or a dialog prompt to save, but can be dismissed.
- Removes the autosave when Blender quits cleanly.
- If the autosave file exists at startup, it will prompt which file to load.

_Every_ AI had a hand in this, but I have reviewed, understood and tested it. AI Credits go to:
Cursor, Grok, Copilot, and Claude.
2026-07-11 11:10:59 +01:00
Stephen Boddy c0d2c2ea24 Fix upstream ci-lint failures on this branch
- autosave.py: black formatting (blank line) and ruff's
  collections.abc.Callable import fix.
- project/__init__.py, tool/__init__.py: ruff import-sort fixes. The
  autosave import in tool/__init__.py is deliberately kept last (must
  come after tool.drawing, per its existing comment) via `# isort: skip`
  rather than letting ruff move it, which would reintroduce that bug.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 10:50:00 +01:00
Stephen Boddy 0ce6e94352 Make autosave recovery prompt properly modal
The recovery popup used invoke_popup, which is dismissed the instant
the mouse leaves its bounds - closing the prompt without loading
either file, and with no visible feedback that anything happened.

Switches to invoke_props_dialog, which blocks the rest of the UI and
is only dismissed by an explicit action. Since Blender always renders
both a fixed "Cancel" button and one labelled by confirm_text on that
dialog type, the prompt is reframed as a direct Yes/Cancel question
("Do you want to load the autosaved version instead?") instead of
adding separate Load Original/Load Autosave buttons on top of those.

Folds the load logic directly into the popup's execute()/cancel(), so
the now-redundant LoadAutosavedRecovery operator is removed.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 10:50:00 +01:00
Stephen Boddy be55400ec6 Remove stale autosave file on clean Blender quit
Previously the autosaved copy was only ever overwritten, never removed,
so a deliberate quit (whether the user saved or chose "don't save")
still nagged with a recovery prompt on next startup.

Registers an atexit cleanup that removes the active IFC's autosave
file(s) on a graceful interpreter shutdown. atexit never runs on an
actual crash, so a genuine crash still leaves the recovery file in
place as before.

The cleanup reads a cached plain-string path kept up to date by
reset_timer(), rather than looking it up live via bpy.context - by
the time atexit fires, Blender's C++ side is torn down far enough
that even a read-only bpy.context.scene access aborts the process
(std::bad_optional_access) instead of raising a catchable exception.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 10:50:00 +01:00
Stephen Boddy 6f1737bb58 Feature #5753 - Autosave for ifc files
Implemented as described in #5753, with two options:
- A nag dialog with save or cancel options.
- An autosaved file.

Settings are in preference to activate the feature (default: off), the period before prompting/saving,
and choosing between the two methods.

Prevent the autosave file being added to the recent files list when the user opens the original, but selects to open the autosaved version.

black/ruff

This commit was created using AI assistance. Cursor for the initial code, then Grok and I fixing all the errors
that Cursor made. Finally Copilot did a code review.

I have reviewed and tested the code, and I understand it, and it works and does not introduce any obvious bugs.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Grok
Co-authored-by: Cursor
2026-07-11 10:39:48 +01:00
sboddy 256d5a63f1 Merge pull request #8495 from sboddy/lint-pass
Fix ci-lint failures: black formatting, ruff unused imports, ty type errors
2026-07-10 22:47:46 +01:00
Stephen Boddy c4605f2a8f 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.
2026-07-10 22:21:23 +01:00
Stephen Boddy 4a62ffe9ca Merge remote-tracking branch 'origin/lint-pass' into lint-pass 2026-07-10 22:20:29 +01:00
Stephen Boddy d5e890bccd 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.
2026-07-10 22:19:56 +01:00
sboddy bba11aa619 Merge branch 'v0.8.0' into lint-pass 2026-07-10 21:53:44 +01:00
Stephen Boddy 9f848a73e1 Fix remaining ty type-check errors in tool.py, product.py, railing.py
- tool.py: drop the `-> int` annotation on the Parametric interface's
  get_geom_generation stub; its `pass` body implicitly returns None, which
  ty can't reconcile with the runtime @interface/@abstractmethod rewriting
  it never sees statically. Matches the file's other stubs (-> None).
- railing.py: qualify the "BIMRailingProperties" string annotations as
  "prop.BIMRailingProperties" on the two functions using it, since the bare
  name was never imported into this module's namespace.
- product.py: suppress ty's missing-argument errors on
  copy_z_rotation_to_selected's Surveyor.get_z_rotation/set_z_rotation
  calls with targeted ty: ignore comments. The function is unused and its
  two dependencies were never implemented on the concrete Surveyor tool;
  left as-is rather than deleted or implemented.
2026-07-10 21:45:31 +01:00
Stephen Boddy 4fb8af2278 Fix ty type-check errors: missing imports and unresolved names
- gizmos.py: TYPE_CHECKING-guard `import bmesh` for the string-literal
  annotation in build_schematic_mesh; suppress the still-unresolved
  gizmo_textures import in TexturedQuadGizmoMixin (WIP dependency, not dead
  code).
- model/__init__.py: register the `decorator` submodule, which unregister()
  already calls (would have raised NameError on addon disable).
- mep.py / tool/model.py: add explicit imports for bonsai.core.geometry and
  bonsai.core.model, previously only reachable by accident of import order.
- Test files: add explicit ifcopenshell.api.pset / ifcopenshell.util.element
  submodule imports used but not imported.
2026-07-10 21:27:10 +01:00
Stephen Boddy 78653a1708 Remove unused imports flagged by ruff
Fixes 23 unused-import violations, mostly in the alignment API module.
2026-07-10 20:42:49 +01:00
Stephen Boddy 216092150a Apply black formatting to fix CI lint-formatting drift
20 files had fallen out of sync with the project's black version;
running `black .` brings them back in line with no logic changes.
2026-07-10 20:42:18 +01:00
Richard Brice ade03b171a Fixes bug with fallback position introduced in 206cd6bb 2026-07-10 09:54:03 -07:00
Richard Brice b5c1b81ede Stationing referent can optionally be located relative to the basis_curve (default) or the alignment curve 2026-07-10 09:46:11 -07:00
Richard Brice 47a20f0c7c Locates positioning referent on the alignment curve, not the basis curve 2026-07-10 09:45:38 -07:00
Richard Brice 52d894298e Fixes double unit conversion when convert-back-units are used 2026-07-10 17:09:51 +02:00
Richard Brice 206cd6bbe1 Alignment API update for station and positioning referents. Fixes bug with fallback position. 2026-07-09 14:10:33 -07:00
81 changed files with 3435 additions and 2253 deletions
+1
View File
@@ -127,6 +127,7 @@ src/ifcopenshell-python/ifcopenshell/express/*.exp.cache.dat
# temp files from AI coding tools
*.claude
CLAUDE.local.md
*.py.tmp*
*.json.tmp*
+5 -1
View File
@@ -314,8 +314,12 @@ if(WASM_BUILD)
else()
# @todo review this, shouldn't this be all possible header-only now?
# ... or rewritten using C++17 features?
# Boost.System has been header-only since 1.69 and its compiled stub library
# was dropped in newer Boost, so requesting it as a component makes
# find_package fail on Boost 1.70 and up (for example Boost 1.90). It is
# still pulled in transitively by thread / iostreams where needed, so do not
# request it explicitly.
set(BOOST_COMPONENTS
system
program_options
regex
thread
+2
View File
@@ -320,9 +320,11 @@ def loadIfcStore(scene: bpy.types.Scene) -> None:
IfcStore.purge()
refresh_ui_data()
if not tool.Ifc.get():
tool.Autosave.cancel_timer()
return
tool.Ifc.schema()
IfcStore.relink_all_objects()
tool.Autosave.reset_timer()
@persistent
+18 -3
View File
@@ -82,7 +82,15 @@ import math
from collections.abc import Callable, Iterator
from dataclasses import dataclass
from enum import Enum
from typing import Any, ClassVar, Literal, Optional, Protocol, runtime_checkable
from typing import (
TYPE_CHECKING,
Any,
ClassVar,
Literal,
Optional,
Protocol,
runtime_checkable,
)
import blf
import bpy
@@ -105,6 +113,9 @@ from mathutils.kdtree import KDTree
import bonsai.tool as tool
from bonsai.bim.module.drawing.shaders import ExtrusionGuidesShader
if TYPE_CHECKING:
import bmesh
SNAP_POINT_SIZE = 10.0
SNAP_POINT_COLOR = (1.0, 0.5, 0.0, 1.0)
SNAP_MAX_RADIUS = 50.0
@@ -2035,7 +2046,9 @@ class TexturedQuadGizmoMixin(StaticTrisGizmoMixin):
def setup(self) -> None:
super().setup()
from bonsai.bim.module.drawing import gizmo_textures
from bonsai.bim.module.drawing import (
gizmo_textures, # ty: ignore[unresolved-import]
)
self._quad_batch = batch_for_shader(
gizmo_textures.get_shader(),
@@ -2044,7 +2057,9 @@ class TexturedQuadGizmoMixin(StaticTrisGizmoMixin):
)
def draw(self, context: bpy.types.Context) -> None:
from bonsai.bim.module.drawing import gizmo_textures
from bonsai.bim.module.drawing import (
gizmo_textures, # ty: ignore[unresolved-import]
)
texture = gizmo_textures.get_icon_texture(self.icon_name)
if texture is None:
@@ -981,7 +981,9 @@ class CreateDrawing(bpy.types.Operator):
# Specifically for PLAN_VIEW and REFLECTED_PLAN_VIEW, any Plan context is also prioritised.
contexts = self.get_linework_contexts(ifc, target_view)
self.serialize_contexts_elements(ifc, tree, contexts, "body", drawing_elements, target_view, link_matrix)
self.serialize_contexts_elements(ifc, tree, contexts, "annotation", drawing_elements, target_view, link_matrix)
self.serialize_contexts_elements(
ifc, tree, contexts, "annotation", drawing_elements, target_view, link_matrix
)
if tool.Ifc.get() == ifc and self.camera_element not in drawing_elements:
with profile("Camera element"):
@@ -2341,7 +2343,9 @@ class ActivateDrawingBase(tool.Ifc.Operator):
"Activates the selected drawing view.\n\n"
+ "ALT+CLICK to keep the viewport position.\n\n"
+ "SHIFT+CLICK to load a quick preview of the drawing view.\n\n"
+ "SHIFT+CTRL+CLICK to load the annotations of all selected drawings without switching views"
+ "SHIFT+CTRL+CLICK to load the annotations of all selected drawings without switching views, "
+ "then select their cameras (the first selected drawing's camera becomes active).\n\n"
+ "SHIFT+CTRL+ALT+CLICK to do the same but also select the annotations, not just the cameras"
)
drawing: bpy.props.IntProperty()
@@ -2363,16 +2367,25 @@ class ActivateDrawingBase(tool.Ifc.Operator):
default=False,
options={"SKIP_SAVE"},
)
include_annotations_in_selection: bpy.props.BoolProperty(
name="Include Annotations In Selection",
description="Also select the loaded annotation objects, not just the drawing cameras.",
default=False,
options={"SKIP_SAVE"},
)
if TYPE_CHECKING:
drawing: int
should_view_from_camera: bool
use_quick_preview: bool
load_selected_annotations: bool
include_annotations_in_selection: bool
def invoke(self, context, event) -> set["rna_enums.OperatorReturnItems"]:
if event.type == "LEFTMOUSE" and event.shift and event.ctrl:
self.load_selected_annotations = True
if event.alt:
self.include_annotations_in_selection = True
return self.execute(context)
if event.type == "LEFTMOUSE" and event.alt:
self.should_view_from_camera = False
@@ -2387,15 +2400,34 @@ class ActivateDrawingBase(tool.Ifc.Operator):
bpy.ops.bim.load_drawings()
if self.load_selected_annotations:
objs_to_select = []
active_camera = None
for d in props.drawings:
if not (d.is_drawing and d.is_selected):
continue
selected_drawing = tool.Ifc.get().by_id(d.ifc_definition_id)
# Importing the camera (if missing) ensures the drawing's
# collection exists so the annotations get collected into it.
if not tool.Ifc.get_object(selected_drawing):
tool.Drawing.import_drawing(selected_drawing)
tool.Drawing.import_annotations_in_group(tool.Drawing.get_drawing_group(selected_drawing))
if not (camera := tool.Ifc.get_object(selected_drawing)):
camera = tool.Drawing.import_drawing(selected_drawing)
group = tool.Drawing.get_drawing_group(selected_drawing)
tool.Drawing.import_annotations_in_group(group)
if active_camera is None:
active_camera = camera
objs_to_select.append(camera)
if self.include_annotations_in_selection:
for element in tool.Drawing.get_group_elements(group) or []:
if element.is_a("IfcAnnotation") and element.ObjectType != "DRAWING":
if annotation_obj := tool.Ifc.get_object(element):
objs_to_select.append(annotation_obj)
# Select the checked drawings' objects, with the first drawing's camera as active.
bpy.ops.object.select_all(action="DESELECT")
for obj in objs_to_select:
obj.select_set(True)
if active_camera is not None:
context.view_layer.objects.active = active_camera
return {"FINISHED"}
drawing = tool.Ifc.get().by_id(self.drawing)
@@ -2484,7 +2516,9 @@ class ActivateDrawing(bpy.types.Operator, ActivateDrawingBase):
"Activates the selected drawing view.\n\n"
+ "ALT+CLICK to keep the viewport position.\n\n"
+ "SHIFT+CLICK to load a quick preview of the drawing view.\n\n"
+ "SHIFT+CTRL+CLICK to load the annotations of all selected drawings without switching views"
+ "SHIFT+CTRL+CLICK to load the annotations of all selected drawings without switching views, "
+ "then select their cameras (the first selected drawing's camera becomes active).\n\n"
+ "SHIFT+CTRL+ALT+CLICK to do the same but also select the annotations, not just the cameras"
)
@@ -27,6 +27,7 @@ import bonsai.tool as tool
from . import (
array,
covering,
decorator,
door,
external,
grid,
@@ -38,6 +38,7 @@ import numpy as np
from ifcopenshell.util.shape_builder import ShapeBuilder
from mathutils import Matrix, Vector
import bonsai.core.geometry
import bonsai.core.root
import bonsai.tool as tool
from bonsai.bim.module.drawing import gizmos as gizmo
@@ -138,7 +138,7 @@ def update_bbim_railing_pset(element: ifcopenshell.entity_instance, railing_data
def generate_wall_mounted_handrail_preview(
obj: bpy.types.Object,
props: "BIMRailingProperties",
props: "prop.BIMRailingProperties",
path_data: dict[str, Any],
si_conversion: float,
) -> None:
@@ -860,7 +860,9 @@ class GizmoRailingSchematic(bpy.types.GizmoGroup, gizmo.BaseSchematicGizmoGroup)
terminal_world = anchor + billboard_rot @ view_rotation @ terminal_local
self.terminal_gizmo.matrix_basis = gizmo.billboarded_at(terminal_world, billboard_rot, 0.18)
def update_editing_gizmos(self, context: bpy.types.Context, mw: "Matrix", props: "BIMRailingProperties") -> None:
def update_editing_gizmos(
self, context: bpy.types.Context, mw: "Matrix", props: "prop.BIMRailingProperties"
) -> None:
"""Hide the pen gizmo while polyline path-edit is active; reposition the cycle icon.
The base class shows the pen gizmo whenever ``is_editing`` is False,
@@ -18,6 +18,8 @@
import bpy
import bonsai.tool as tool
from . import decorator, gizmo, operator, prop, ui, workspace
classes = (
@@ -58,6 +60,8 @@ classes = (
operator.LinkIfc,
operator.LoadBlendMetadataAndIFC,
operator.LoadLink,
operator.AutosavePrompt,
operator.LoadAutosavedRecoveryPopup,
operator.LoadLinkedProject,
operator.LoadProject,
operator.LoadProjectElements,
@@ -136,6 +140,7 @@ def register():
def unregister():
if not bpy.app.background:
bpy.utils.unregister_tool(workspace.ExploreTool)
tool.Autosave.cancel_timer()
del bpy.types.Scene.BIMProjectProperties
del bpy.types.Scene.MeasureToolSettings
bpy.app.handlers.load_post.remove(decorator.toggle_decorations_on_load)
@@ -985,8 +985,10 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
),
default=False,
)
skip_autosave_recovery: bpy.props.BoolProperty(default=False, options={"HIDDEN", "SKIP_SAVE"})
use_detailed_tooltip: bpy.props.BoolProperty(default=False, options={"HIDDEN"})
filename_ext = ".ifc"
skip_recent: bpy.props.BoolProperty(default=False, options={"HIDDEN", "SKIP_SAVE"})
if TYPE_CHECKING:
filepath: str
@@ -995,6 +997,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
use_relative_path: bool
should_start_fresh_session: bool
import_without_ifc_data: bool
skip_autosave_recovery: bool
use_detailed_tooltip: bool
@classmethod
@@ -1041,7 +1044,26 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
return tooltip
def check_autosave_recovery(self, context: bpy.types.Context) -> set["rna_enums.OperatorReturnItems"] | None:
if self.skip_autosave_recovery:
return None
autosaved_filepath = tool.Autosave.get_newer_autosaved_path(self.get_filepath_abs())
if not autosaved_filepath:
return None
return bpy.ops.bim.load_autosaved_recovery_popup(
"INVOKE_DEFAULT",
original_filepath=str(self.get_filepath_abs()),
autosaved_filepath=autosaved_filepath,
is_advanced=self.is_advanced,
use_relative_path=self.use_relative_path,
should_start_fresh_session=self.should_start_fresh_session,
import_without_ifc_data=self.import_without_ifc_data,
)
def execute(self, context):
if recovery := self.check_autosave_recovery(context):
return recovery
if (
tool.Blender.get_addon_preferences().save_metadata_blend_file
and self.should_start_fresh_session
@@ -1136,7 +1158,8 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
props.should_save_metadata_for_this_file = metadata_doc is not None
tool.Blender.register_toolbar()
tool.Project.add_recent_ifc_project(self.get_filepath_abs())
if not self.skip_recent:
tool.Project.add_recent_ifc_project(self.get_filepath_abs())
if self.is_advanced:
pass
@@ -1149,10 +1172,13 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
except:
bonsai.last_error = traceback.format_exc()
raise
tool.Autosave.reset_timer()
return {"FINISHED"}
def invoke(self, context, event):
if self.filepath:
if recovery := self.check_autosave_recovery(context):
return recovery
return self.execute(context)
return ImportHelper.invoke(self, context, event)
@@ -1947,6 +1973,7 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
skip_recent: bpy.props.BoolProperty(default=False, options={"HIDDEN", "SKIP_SAVE"})
if TYPE_CHECKING:
filter_glob: str
@@ -2007,6 +2034,18 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
return {"FINISHED"}
def _execute(self, context):
project_props = tool.Project.get_project_props()
project_props.use_relative_project_path = self.use_relative_path
# Fallback if filepath is not set
if not getattr(self, "filepath", None) or self.filepath.strip() in ("", ".ifc"):
props = tool.Blender.get_bim_props()
if props.ifc_file:
self.filepath = str(tool.Blender.ensure_blender_path_is_abs(Path(props.ifc_file)))
else:
self.report({"ERROR"}, "No filepath available for saving.")
return {"CANCELLED"}
committed, failed_commits = tool.Parametric.commit_pending_edits()
# Previews are session-transient — discard rather than commit. Sibling
# gizmo polls gate on each preview's is_active flag, and a stuck flag
@@ -2069,7 +2108,8 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
settings.logger.info("Export finished in {:.2f} seconds".format(time.time() - start))
print("Export finished in {:.2f} seconds".format(time.time() - start))
# New project created in Bonsai should be in recent projects too.
tool.Project.add_recent_ifc_project(Path(output_file))
if not self.skip_recent:
tool.Project.add_recent_ifc_project(Path(output_file))
props = tool.Project.get_project_props()
if props.use_relative_project_path and bpy.data.is_saved:
output_file = os.path.relpath(output_file, bpy.path.abspath("//"))
@@ -2103,6 +2143,7 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
)
bonsai.bim.handler.refresh_ui_data()
tool.Autosave.reset_timer()
@classmethod
def description(cls, context, properties):
@@ -2111,6 +2152,97 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
return "Save the IFC file. Will save both .IFC/.BLEND files if synced together"
class LoadAutosavedRecoveryPopup(bpy.types.Operator):
bl_idname = "bim.load_autosaved_recovery_popup"
bl_label = "Recover Autosaved File"
bl_options = {"REGISTER", "UNDO"}
original_filepath: bpy.props.StringProperty(options={"SKIP_SAVE"})
autosaved_filepath: bpy.props.StringProperty(options={"SKIP_SAVE"})
is_advanced: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
use_relative_path: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
should_start_fresh_session: bpy.props.BoolProperty(default=True, options={"SKIP_SAVE"})
import_without_ifc_data: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
def draw(self, context):
layout = self.layout
layout.label(text="A newer autosaved copy was found:", icon="INFO")
layout.label(text=os.path.basename(self.autosaved_filepath))
layout.separator()
layout.label(text="Do you want to load the autosaved version instead?")
layout.label(text="(Cancel will load the original)")
def invoke(self, context, event):
# invoke_props_dialog is modal - unlike invoke_popup/popup_menu, it
# isn't dismissed by the mouse simply leaving its bounds. It always
# renders both a fixed "Cancel" button and this confirm_text one, so
# the question is framed as Yes/Cancel rather than adding separate
# Load buttons on top.
return context.window_manager.invoke_props_dialog(
self, width=420, title="Recover Autosaved File", confirm_text="Yes"
)
def _load(self, filepath: str, skip_recent: bool) -> set["rna_enums.OperatorReturnItems"]:
return bpy.ops.bim.load_project(
filepath=filepath,
skip_autosave_recovery=True, # Prevent infinite loop
is_advanced=self.is_advanced,
use_relative_path=self.use_relative_path,
should_start_fresh_session=self.should_start_fresh_session,
import_without_ifc_data=self.import_without_ifc_data,
skip_recent=skip_recent,
)
def execute(self, context):
result = self._load(self.autosaved_filepath, skip_recent=True)
# Re-point tracking at the original path so future saves write back
# to it, not "_autosaved.ifc".
tool.Ifc.set_path(self.original_filepath)
return result
def cancel(self, context):
# Also reached via Escape or a click outside the dialog, not just Cancel.
self._load(self.original_filepath, skip_recent=False)
class AutosavePrompt(bpy.types.Operator):
bl_idname = "bim.autosave_prompt"
bl_label = "Autosave Reminder"
bl_options = set()
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(
self, width=400, confirm_text="Save", title="Autosave Reminder"
)
def draw(self, context):
layout = self.layout
layout.label(text="The autosave timer has expired.", icon="INFO")
layout.label(text="Would you like to save your IFC project now?")
def execute(self, context):
# Get current IFC path
props = tool.Blender.get_bim_props()
current_ifc_path = props.ifc_file
if not current_ifc_path:
self.report({"WARNING"}, "No IFC file path set. Please save manually.")
tool.Autosave.reset_timer()
return {"CANCELLED"}
# Call save_project with explicit filepath using EXEC_DEFAULT
result = bpy.ops.bim.save_project(
"EXEC_DEFAULT", filepath=current_ifc_path, should_save_as=False, skip_recent=True
)
tool.Autosave.reset_timer()
return result
def cancel(self, context):
tool.Autosave.reset_timer()
return {"CANCELLED"}
class LoadLinkedProject(bpy.types.Operator, ImportHelper):
bl_idname = "bim.load_linked_project"
bl_label = "Load Project For Viewing Only"
+46
View File
@@ -577,6 +577,43 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
should_disable_undo_on_save: BoolProperty(
name="Disable Undo When Saving (Faster saves, no undo for you!)", default=False
)
def update_autosave_settings(self, context: bpy.types.Context) -> None:
if self.autosave_enabled:
tool.Autosave.reset_timer()
else:
tool.Autosave.cancel_timer()
autosave_enabled: BoolProperty(
name="Enable IFC Autosave Timer",
description="Periodically remind you to save or automatically create a backup copy of the IFC file",
default=False,
update=update_autosave_settings,
)
autosave_interval_minutes: bpy.props.IntProperty(
name="Autosave Interval (Minutes)",
description="Time between autosave reminders or backups. The timer resets whenever you open or save a project",
default=10,
min=1,
max=1440,
update=update_autosave_settings,
)
autosave_mode: bpy.props.EnumProperty(
name="Autosave Mode",
items=[
(
"PROMPT",
"Prompt to Save",
"Show a dialog offering to save the IFC project when the timer expires",
),
(
"BACKUP",
"Automatic Backup",
"Save a backup copy as filename_autosaved.ifc when the timer expires",
),
],
default="PROMPT",
)
should_stream: BoolProperty(name="Stream Data From IFC-SPF (Only for advanced users)", default=False)
should_always_cache: BoolProperty(
name="Always Cache Geometry",
@@ -689,6 +726,9 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
bsdd_load_test_dictionaries: bool
bsdd_baseurl: str
should_disable_undo_on_save: bool
autosave_enabled: bool
autosave_interval_minutes: int
autosave_mode: Literal["PROMPT", "BACKUP"]
should_stream: bool
should_always_cache: bool
occurrence_name_style: Literal["CLASS", "TYPE", "CUSTOM"]
@@ -837,6 +877,12 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
def draw_other_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
layout.prop(self, "opening_focus_opacity")
layout.prop(self, "should_disable_undo_on_save")
layout.separator()
layout.label(text="Autosave:")
layout.prop(self, "autosave_enabled")
if self.autosave_enabled:
layout.prop(self, "autosave_interval_minutes")
layout.prop(self, "autosave_mode")
layout.prop(self, "should_stream")
layout.prop(self, "should_always_cache")
layout.label(text="bSDD:")
+17 -13
View File
@@ -302,23 +302,25 @@ def add_drawing(
context=drawing.get_body_context(),
ifc_representation_class=None,
)
drawings_parent_group = None
for group in ifc.get().by_type("IfcGroup"):
if group.Name == "DRAWINGS" and group.ObjectType == "DRAWINGS":
drawings_parent_group = group
break
if not drawings_parent_group:
drawings_parent_group = ifc.run("group.add_group")
ifc.run("group.edit_group", group=drawings_parent_group, attributes={"Name": "DRAWINGS", "ObjectType": "DRAWINGS"})
ifc.run(
"group.edit_group", group=drawings_parent_group, attributes={"Name": "DRAWINGS", "ObjectType": "DRAWINGS"}
)
group = ifc.run("group.add_group")
ifc.run("group.edit_group", group=group, attributes={"Name": drawing_name, "ObjectType": "DRAWING"})
ifc.run("group.assign_group", group=group, products=[element])
ifc.run("group.assign_group", group=drawings_parent_group, products=[group])
collector.assign(camera)
pset = ifc.run("pset.add_pset", product=element, name="EPset_Drawing")
if drawing.get_unit_system() == "METRIC":
@@ -355,7 +357,7 @@ def add_drawing(
if document.Name == "DRAWINGS" and document.Scope == "DRAWINGS":
drawings_parent_document = document
break
if not drawings_parent_document:
drawings_parent_document = ifc.run("document.add_information")
if ifc.get_schema() == "IFC2X3":
@@ -363,7 +365,7 @@ def add_drawing(
else:
attributes = {"Identification": "DRAWINGS", "Name": "DRAWINGS", "Scope": "DRAWINGS"}
ifc.run("document.edit_information", information=drawings_parent_document, attributes=attributes)
information = ifc.run("document.add_information", parent=drawings_parent_document)
uri = drawing.get_default_drawing_path(drawing_name)
reference = ifc.run("document.add_reference", information=information)
@@ -392,17 +394,19 @@ def duplicate_drawing(
drawing_tool.set_name(new_drawing, drawing_name)
group = drawing_tool.get_drawing_group(new_drawing)
ifc.run("group.unassign_group", group=group, products=[new_drawing])
drawings_parent_group = None
for parent_group in ifc.get().by_type("IfcGroup"):
if parent_group.Name == "DRAWINGS" and parent_group.ObjectType == "DRAWINGS":
drawings_parent_group = parent_group
break
if not drawings_parent_group:
drawings_parent_group = ifc.run("group.add_group")
ifc.run("group.edit_group", group=drawings_parent_group, attributes={"Name": "DRAWINGS", "ObjectType": "DRAWINGS"})
ifc.run(
"group.edit_group", group=drawings_parent_group, attributes={"Name": "DRAWINGS", "ObjectType": "DRAWINGS"}
)
new_group = ifc.run("group.add_group")
ifc.run("group.edit_group", group=new_group, attributes={"Name": drawing_name, "ObjectType": "DRAWING"})
ifc.run("group.assign_group", group=new_group, products=[new_drawing])
@@ -427,7 +431,7 @@ def duplicate_drawing(
if document.Name == "DRAWINGS" and document.Scope == "DRAWINGS":
drawings_parent_document = document
break
if not drawings_parent_document:
drawings_parent_document = ifc.run("document.add_information")
if ifc.get_schema() == "IFC2X3":
+4 -3
View File
@@ -50,14 +50,15 @@ def copy_z_rotation_to_selected(
flip: bool = False,
) -> int:
"""Apply ``active``'s Z-Euler rotation to each target."""
source_z = surveyor.get_z_rotation(active)
source_z = surveyor.get_z_rotation(active) # ty: ignore[missing-argument]
if flip:
source_z += math.pi
rotated = 0
for obj in targets:
if abs(_z_rotation_diff(surveyor.get_z_rotation(obj), source_z)) < Z_ROTATION_ALIGNMENT_TOLERANCE:
target_z = surveyor.get_z_rotation(obj) # ty: ignore[missing-argument]
if abs(_z_rotation_diff(target_z, source_z)) < Z_ROTATION_ALIGNMENT_TOLERANCE:
continue
surveyor.set_z_rotation(obj, source_z)
surveyor.set_z_rotation(obj, source_z) # ty: ignore[missing-argument]
rotated += 1
if ifc.get_entity(obj) is not None:
bonsai.core.geometry.edit_object_placement(ifc, geometry, surveyor, obj=obj)
+1 -1
View File
@@ -804,7 +804,7 @@ class Profile:
@interface
class Parametric:
def get_geom_generation(cls) -> int: pass
def get_geom_generation(cls): pass
def refresh_post_commit(cls, operator) -> None: pass
+3
View File
@@ -80,3 +80,6 @@ from bonsai.tool.type import Type
from bonsai.tool.unit import Unit
from bonsai.tool.wall import Wall
from bonsai.tool.web import Web
# Have to move after import of tool.drawing
from bonsai.tool.autosave import Autosave # isort: skip
+188
View File
@@ -0,0 +1,188 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2026
#
# This file is part of Bonsai.
#
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Bonsai is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
#
# This file was generated with the assistance of an AI coding tool.
from __future__ import annotations
import atexit
import logging
import os
from collections.abc import Callable
from pathlib import Path
from typing import Union
import bpy
import bonsai.tool as tool
from bonsai.bim import export_ifc
from bonsai.bim.module.model import preview_base
AUTOSAVING_SUFFIX = "_autosaving.ifc"
AUTOSAVED_SUFFIX = "_autosaved.ifc"
_timer_callback: Union[Callable[[], None], None] = None
# See cleanup_stale_autosave() for why this is a cached plain string rather
# than looked up live.
_active_ifc_path_cache: Union[str, None] = None
class Autosave:
@classmethod
def get_paths(cls, ifc_path: Union[str, Path]) -> tuple[Path, Path, Path]:
path = Path(ifc_path)
stem = path.stem if path.suffix.lower() == ".ifc" else path.name
parent = path.parent
main_path = path if path.suffix.lower() == ".ifc" else parent / f"{stem}.ifc"
autosaving_path = parent / f"{stem}{AUTOSAVING_SUFFIX}"
autosaved_path = parent / f"{stem}{AUTOSAVED_SUFFIX}"
return main_path, autosaving_path, autosaved_path
@classmethod
def get_active_ifc_path(cls) -> Union[Path, None]:
props = tool.Blender.get_bim_props()
if not props.ifc_file:
return None
path = tool.Blender.ensure_blender_path_is_abs(Path(props.ifc_file))
if path.suffix.lower() != ".ifc":
return None
return path
@classmethod
def _update_active_ifc_path_cache(cls) -> None:
global _active_ifc_path_cache
ifc_path = cls.get_active_ifc_path()
_active_ifc_path_cache = ifc_path.as_posix() if ifc_path is not None else None
@classmethod
def is_enabled(cls) -> bool:
return bool(tool.Blender.get_addon_preferences().autosave_enabled)
@classmethod
def get_interval_seconds(cls) -> float:
minutes = tool.Blender.get_addon_preferences().autosave_interval_minutes
return max(1.0, float(minutes) * 60.0)
@classmethod
def is_eligible(cls) -> bool:
return cls.is_enabled() and tool.Ifc.get() is not None and cls.get_active_ifc_path() is not None
@classmethod
def cancel_timer(cls) -> None:
global _timer_callback
if _timer_callback is not None and bpy.app.timers.is_registered(_timer_callback):
bpy.app.timers.unregister(_timer_callback)
_timer_callback = None
@classmethod
def reset_timer(cls) -> None:
cls.cancel_timer()
cls._update_active_ifc_path_cache()
if not cls.is_eligible():
return
def on_timer() -> None:
cls._on_timer_expired()
return None
global _timer_callback
_timer_callback = on_timer
bpy.app.timers.register(on_timer, first_interval=cls.get_interval_seconds())
@classmethod
def _on_timer_expired(cls) -> None:
if not cls.is_eligible():
return
prefs = tool.Blender.get_addon_preferences()
bim_props = tool.Blender.get_bim_props()
if bim_props.is_dirty:
if prefs.autosave_mode == "PROMPT":
bpy.ops.bim.autosave_prompt("INVOKE_DEFAULT")
elif prefs.autosave_mode == "BACKUP":
try:
cls.perform_backup(bpy.context)
except Exception as error:
print(f"Bonsai: autosave backup failed: {error}")
cls.reset_timer()
@classmethod
def perform_backup(cls, context: bpy.types.Context) -> None:
ifc_path = cls.get_active_ifc_path()
if ifc_path is None:
return
_, autosaving_path, autosaved_path = cls.get_paths(ifc_path)
autosaving_path.parent.mkdir(parents=True, exist_ok=True)
tool.Parametric.commit_pending_edits()
preview_base.discard_pending_previews(context.scene)
logger = logging.getLogger("ExportIFC")
output_file = autosaving_path.as_posix().replace("\\", "/")
settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
export_ifc.IfcExporter(settings).export()
try:
os.replace(autosaving_path, autosaved_path)
except OSError:
if autosaving_path.is_file():
autosaving_path.unlink(missing_ok=True)
raise
@classmethod
def get_newer_autosaved_path(cls, ifc_path: Union[str, Path]) -> Union[str, None]:
path = Path(ifc_path)
if path.suffix.lower() != ".ifc" or not path.is_file():
return None
_, _, autosaved_path = cls.get_paths(path)
if not autosaved_path.is_file():
return None
if autosaved_path.stat().st_mtime > path.stat().st_mtime:
return autosaved_path.as_posix().replace("\\", "/")
return None
@classmethod
def cleanup_stale_autosave(cls) -> None:
"""Remove the active IFC's autosave file(s) on a graceful shutdown.
Registered via `atexit`, which only runs on a normal interpreter
shutdown - never on an actual crash. So a deliberate quit (whether
the user saved or chose "don't save") clears the recovery file and
won't prompt on next startup, while a genuine crash leaves it in
place for recovery, since no atexit callbacks fire then.
Deliberately reads only `_active_ifc_path_cache` - a plain string
kept up to date by `reset_timer()` - rather than touching `bpy` here.
By the time `atexit` fires, Blender's own C++ side is torn down far
enough that even reading `bpy.context.scene` aborts the process
(std::bad_optional_access) instead of raising a catchable exception.
"""
if _active_ifc_path_cache is None:
return
try:
_, autosaving_path, autosaved_path = cls.get_paths(_active_ifc_path_cache)
autosaving_path.unlink(missing_ok=True)
autosaved_path.unlink(missing_ok=True)
except Exception:
pass
atexit.register(Autosave.cleanup_stale_autosave)
+1
View File
@@ -59,6 +59,7 @@ from ifcopenshell.util.shape_builder import ShapeBuilder, np_to_3d
from mathutils import Matrix, Vector
import bonsai.core.geometry
import bonsai.core.model
import bonsai.core.tool
import bonsai.tool as tool
from bonsai.bim import import_ifc
@@ -35,6 +35,7 @@ from unittest.mock import Mock, patch
import bpy
import ifcopenshell
import ifcopenshell.api.pset
import pytest
import bonsai.tool as tool
@@ -37,6 +37,8 @@ from unittest.mock import patch
import bpy
import ifcopenshell
import ifcopenshell.api.pset
import ifcopenshell.util.element
import pytest
import bonsai.tool as tool
@@ -146,7 +146,9 @@ def test_fit_flow_segments_with_single_segment_dispatches_obstruction():
mep.tool.Model, "get_flow_segment_profile", return_value=segment_profile
), patch.object(mep.MEPAddObstruction, "_execute", return_value=None) as obstruction, patch.object(
mep.MEPAddBend, "_execute", return_value=None
) as bend, patch.object(mep.MEPAddTransition, "_execute", return_value=None) as transition:
) as bend, patch.object(
mep.MEPAddTransition, "_execute", return_value=None
) as transition:
mep.FitFlowSegments._execute(op, context=context)
assert obstruction.call_count == 1
@@ -178,7 +180,9 @@ def test_fit_flow_segments_refuses_mixed_pipe_and_duct():
mep.tool.Model, "get_flow_segment_profile", return_value=profile
), patch.object(mep.MEPAddObstruction, "_execute", return_value=None) as obstruction, patch.object(
mep.MEPAddBend, "_execute", return_value=None
) as bend, patch.object(mep.MEPAddTransition, "_execute", return_value=None) as transition:
) as bend, patch.object(
mep.MEPAddTransition, "_execute", return_value=None
) as transition:
mep.FitFlowSegments._execute(op, context=context)
obstruction.assert_not_called()
@@ -173,8 +173,9 @@ def test_gizmo_group_class_wiring(gizmo_cls_name, bl_idname, is_element_predicat
predicate = getattr(tool.Parametric, is_element_predicate)
fake_element = Mock()
fake_element.is_a.return_value = True
with patch.object(tool.Parametric, is_element_predicate, side_effect=predicate) as p, patch.object(
tool.System, "has_parametric_body", return_value=True
with (
patch.object(tool.Parametric, is_element_predicate, side_effect=predicate) as p,
patch.object(tool.System, "has_parametric_body", return_value=True),
):
cls.is_element_type(fake_element)
assert p.called, f"{gizmo_cls_name}.is_element_type did not delegate to Parametric.{is_element_predicate}"
@@ -0,0 +1,68 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2026
#
# This file is part of Bonsai.
#
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Bonsai is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
#
# This file was generated with the assistance of an AI coding tool.
import os
import time
from pathlib import Path
import pytest
from bonsai.tool.autosave import AUTOSAVED_SUFFIX, AUTOSAVING_SUFFIX, Autosave
pytestmark = pytest.mark.project
class TestAutosavePaths:
def test_get_paths_for_ifc_file(self):
main_path, autosaving_path, autosaved_path = Autosave.get_paths("/tmp/myfile.ifc")
assert main_path == Path("/tmp/myfile.ifc")
assert autosaving_path == Path(f"/tmp/myfile{AUTOSAVING_SUFFIX}")
assert autosaved_path == Path(f"/tmp/myfile{AUTOSAVED_SUFFIX}")
def test_get_newer_autosaved_path_when_missing(self, tmp_path):
ifc_path = tmp_path / "myfile.ifc"
ifc_path.write_text("ifc")
assert Autosave.get_newer_autosaved_path(ifc_path) is None
def test_get_newer_autosaved_path_when_older(self, tmp_path):
ifc_path = tmp_path / "myfile.ifc"
autosaved_path = tmp_path / f"myfile{AUTOSAVED_SUFFIX}"
ifc_path.write_text("ifc")
autosaved_path.write_text("autosaved")
past = time.time() - 10
os.utime(ifc_path, (past, past))
os.utime(autosaved_path, (time.time(), time.time()))
assert Autosave.get_newer_autosaved_path(ifc_path) == autosaved_path.as_posix()
def test_get_newer_autosaved_path_when_not_newer(self, tmp_path):
ifc_path = tmp_path / "myfile.ifc"
autosaved_path = tmp_path / f"myfile{AUTOSAVED_SUFFIX}"
ifc_path.write_text("ifc")
autosaved_path.write_text("autosaved")
now = time.time()
os.utime(ifc_path, (now, now))
past = now - 10
os.utime(autosaved_path, (past, past))
assert Autosave.get_newer_autosaved_path(ifc_path) is None
def test_get_newer_autosaved_path_ignores_non_ifc(self, tmp_path):
path = tmp_path / "myfile.ifczip"
path.write_text("zip")
assert Autosave.get_newer_autosaved_path(path) is None
@@ -139,6 +139,5 @@ def test_every_cancel_ops_entry_has_a_real_preview_propertygroup() -> None:
orphaned = [attr for attr, _op in preview_base.PREVIEW_CANCEL_OPS if attr not in declared_attrs]
assert not orphaned, (
"PREVIEW_CANCEL_OPS contains entries whose PointerProperty child no longer "
f"exists on {UMBRELLA_CLASS}. Drop the stale tuple(s):\n "
+ "\n ".join(orphaned)
f"exists on {UMBRELLA_CLASS}. Drop the stale tuple(s):\n " + "\n ".join(orphaned)
)
+1
View File
@@ -24,6 +24,7 @@ import time
import bpy
import ifcopenshell
import ifcopenshell.util.element
import pytest
from bonsai import tool as tool
+1
View File
@@ -23,6 +23,7 @@ import bpy
import ifcopenshell
import ifcopenshell.api.geometry
import ifcopenshell.api.material
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.api.style
import ifcopenshell.api.type
+5 -3
View File
@@ -57,7 +57,8 @@ class CsvHeader(TypedDict):
# Formula
Formula: NotRequired[str]
#QuantityClass: NotRequired[str]
# QuantityClass: NotRequired[str]
# Currently we assume that if column is not part of the main header,
# then it is a cost value category. So here we list any additional column
@@ -97,7 +98,8 @@ class CostItem(TypedDict):
Query: Union[str, None]
Formula: Union[str, None]
#QuantityClass: Union[str, None]
# QuantityClass: Union[str, None]
class Csv2Ifc:
# Inputs.
@@ -420,7 +422,7 @@ class Csv2Ifc:
products=results,
formula=cost_item["Formula"],
ifc_class=ifc_quantity_class,
)
)
self.create_cost_items(cost_item["children"], cost_item["ifc"])
+11
View File
@@ -252,6 +252,10 @@ int main(int argc, char** argv) {
("stderr-progress", "output progress to stderr stream")
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
("no-progress", "suppress possible progress bar type of prints that use carriage return")
("fail-on-error", "return a non-zero exit code when one or more errors were logged during "
"geometry conversion (e.g. an element failed to convert). By default IfcConvert exits "
"successfully as long as an output file could be written, even if some elements were "
"silently dropped. Enable this flag so scripts and CI can detect partial conversions.")
("log-format", po::value<std::string>(&log_format), "log format: plain or json")
("log-file", new po::typed_value<path_t, char_t>(&log_file), "redirect log output to file");
@@ -449,6 +453,7 @@ int main(int argc, char** argv) {
const bool mmap = vmap.count("mmap") != 0;
const bool no_progress = vmap.count("no-progress") != 0;
const bool fail_on_error = vmap.count("fail-on-error") != 0;
const bool quiet = vmap.count("quiet") != 0;
const bool stderr_progress = vmap.count("stderr-progress") != 0;
@@ -885,6 +890,7 @@ int main(int argc, char** argv) {
}
if (!serializer->ready()) {
logger.Error("SYS", 25, "Unable to open output file '" + IfcUtil::path::to_utf8(output_filename) + "' for writing; check that the directory exists and is writable");
IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename));
write_log(!quiet);
return EXIT_FAILURE;
@@ -1220,6 +1226,11 @@ int main(int argc, char** argv) {
successful = false;
}
if (fail_on_error && logger.MaxSeverity() >= Logger::LOG_ERROR) {
logger.Error("SYS", 26, "Errors encountered during processing, failing due to --fail-on-error.");
successful = false;
}
if (logger.Verbosity() == Logger::LOG_PERF) {
logger.PrintPerformanceStats();
}
+2 -2
View File
@@ -361,8 +361,8 @@ namespace ifcopenshell {
struct CircleSegments : public SettingBase<CircleSegments, int> {
static constexpr const char* const name = "circle-segments";
static constexpr const char* const description = "Number of segments to approximate full circles in CGAL kernel.";
static constexpr int defaultvalue = 16;
static constexpr const char* const description = "Number of segments to approximate full circles in the CGAL kernel. When 0 (the default) the segment count is derived from mesher-linear-deflection instead, so curves stay within the deflection tolerance regardless of radius.";
static constexpr int defaultvalue = 0;
};
struct CgalSmoothAngleDegrees : public SettingBase<CgalSmoothAngleDegrees, double> {
+35 -1
View File
@@ -391,6 +391,11 @@ namespace {
}
};
// Representative radius used to size the polygonal approximation of a conic.
// For an ellipse the larger semi-axis is the conservative choice.
inline double conic_radius(const taxonomy::circle::ptr& c) { return c->radius; }
inline double conic_radius(const taxonomy::ellipse::ptr& e) { return e->radius > e->radius2 ? e->radius : e->radius2; }
struct cgal_curve_creation_visitor {
Settings& settings_;
parameter_range param;
@@ -425,7 +430,36 @@ namespace {
if (b <= a) {
b += 2 * M_PI;
}
int num_segments = (int)std::ceil(std::fabs(a - b) / (2 * M_PI) * settings_.get<settings::CircleSegments>().get());
const double span = std::fabs(a - b);
// CircleSegments controls how conics (circles, ellipses, arcs) are approximated
// in the CGAL kernel. Two modes, one or the other:
// - CircleSegments == 0 (the default): the segment count is derived from
// MesherLinearDeflection, so the chord deviation stays within the mesher's
// linear deflection regardless of radius. This matches the deflection based
// meshing the OpenCascade kernel already does and fixes issue #8051, where
// large radius arcs (curved curtain wall mullions) collapsed to straight chords
// because a fixed segment count is radius agnostic.
// - CircleSegments > 0: it is used directly as the number of segments for a full
// circle, giving deterministic, radius independent output.
int num_segments;
const int circle_segments = settings_.get<settings::CircleSegments>().get();
if (circle_segments > 0) {
num_segments = (int)std::ceil(span / (2 * M_PI) * circle_segments);
} else {
const double radius = conic_radius(t);
const double deflection = settings_.get<settings::MesherLinearDeflection>().get();
if (deflection > 0. && radius > deflection) {
const double max_segment_angle = 2.0 * std::acos(1.0 - deflection / radius);
num_segments = (int)std::ceil(span / max_segment_angle);
} else {
// Radius within the deflection tolerance (or no deflection set): a chord per
// quarter turn already keeps the deviation within tolerance.
num_segments = (int)std::ceil(span / (M_PI / 2.));
}
}
if (num_segments < 1) {
num_segments = 1;
}
double du = (b - a) / num_segments;
taxonomy::point3 P;
// @nb for loop is not inclusive of the both end points
+22
View File
@@ -31,6 +31,7 @@
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <BRep_Tool.hxx>
#include <BRepExtrema_DistShapeShape.hxx>
#include <Standard_Macro.hxx>
#include <TopoDS_Shape.hxx>
@@ -356,6 +357,27 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re
return false;
}
// #527: A face whose inner boundary intersects the outer boundary (or
// another inner boundary) is invalid per the schema. Open Cascade heals or
// drops such a face silently, so the intended hole is lost with no
// diagnostic. The distance between two non-intersecting loops is strictly
// positive; a distance at (or below) the modelling precision means the
// boundaries touch or cross. Emit a clear warning so the invalid input is
// not silently lost. wires() is ordered outer-first, inner-bounds after.
if (fd.wires().size() > 1) {
const auto& fwires = fd.wires();
bool reported = false;
for (size_t i = 1; i < fwires.size() && !reported; ++i) {
for (size_t j = 0; j < i && !reported; ++j) {
BRepExtrema_DistShapeShape dss(fwires[i], fwires[j]);
if (dss.IsDone() && dss.Value() < precision_) {
logger().Warning("GEO", 402, "Face inner boundary intersects another face boundary", face->instance);
reported = true;
}
}
}
}
if (fd.surface().IsNull()) {
// Use the first wire to find a plane manually for polygonal wires
const TopoDS_Wire& wire = fd.wires().front();
@@ -0,0 +1,93 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
#include "../profile_helper.h"
// In IFC2X3 IfcAsymmetricIShapeProfileDef is a subtype of IfcIShapeProfileDef and is
// therefore dispatched (and handled) by the IfcIShapeProfileDef mapping. From IFC4
// onwards it is a standalone subtype of IfcParameterizedProfileDef with its own
// Bottom*/Top* attributes, so nothing mapped it and the extrusion came out empty.
// The presence of the standalone BottomFlangeWidth attribute is the discriminator:
// it is only defined in the schemas where the type is standalone (IFC4 / IFC4X3).
#ifdef SCHEMA_IfcAsymmetricIShapeProfileDef_HAS_BottomFlangeWidth
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcAsymmetricIShapeProfileDef* inst) {
// Bottom flange (half width), overall depth (half), web (half thickness).
const double xb = inst->BottomFlangeWidth() / 2.0 * length_unit_;
const double xt = inst->TopFlangeWidth() / 2.0 * length_unit_;
const double y = inst->OverallDepth() / 2.0 * length_unit_;
const double d1 = inst->WebThickness() / 2.0 * length_unit_;
// Bottom flange thickness; top flange thickness defaults to the bottom one.
const double ftb = inst->BottomFlangeThickness() * length_unit_;
const double ftt = inst->TopFlangeThickness().get_value_or(inst->BottomFlangeThickness()) * length_unit_;
// Optional fillet radii (web/flange transition) and flange edge radii.
const double fb = inst->BottomFlangeFilletRadius().get_value_or(0.) * length_unit_;
const double ft_top = inst->TopFlangeFilletRadius().get_value_or(0.) * length_unit_;
const double feb = inst->BottomFlangeEdgeRadius().get_value_or(0.) * length_unit_;
const double fet = inst->TopFlangeEdgeRadius().get_value_or(0.) * length_unit_;
// Optional flange slopes: the inner edge of the flange rises towards the web.
const double bottomSlope = inst->BottomFlangeSlope().get_value_or(0.) * angle_unit_;
const double topSlope = inst->TopFlangeSlope().get_value_or(0.) * angle_unit_;
const double dyb = (xb - d1) * tan(bottomSlope);
const double dyt = (xt - d1) * tan(topSlope);
const double tol = settings_.get<settings::Precision>().get();
if (xb < tol || xt < tol || y < tol || d1 < tol || ftb < tol || ftt < tol) {
logger_.Message(Logger::LOG_NOTICE, "GEO", 264, "Skipping zero sized profile:", inst);
return nullptr;
}
taxonomy::matrix4::ptr m4;
bool has_position = true;
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
has_position = !!inst->Position();
#endif
if (has_position) {
m4 = taxonomy::cast<taxonomy::matrix4>(map(inst->Position()));
}
// Twelve corner points, running counter-clockwise from the bottom-left, with the
// bottom flange (xb) possibly wider than the top flange (xt). Fillet/edge radii are
// attached to the corner they round, matching the symmetric IfcIShapeProfileDef.
return profile_helper(m4, {
{{-xb,-y}},
{{xb,-y}},
{{xb,-y + ftb}, {feb}},
{{d1,-y + ftb + dyb},{fb} },
{{d1,y - ftt - dyt},{ft_top} },
{{xt,y - ftt}, {fet}},
{{xt,y}},
{{-xt,y}},
{{-xt,y - ftt}, {fet}},
{{-d1,y - ftt - dyt},{ft_top} },
{{-d1,-y + ftb + dyb},{fb} },
{{-xb,-y + ftb}, {feb}}
});
}
#endif
+22 -11
View File
@@ -39,8 +39,25 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolygonalFaceSet* inst) {
int max_index = (int)points.size();
// When the optional PnIndex is present, CoordIndex values do not index into
// CoordList directly but into PnIndex, which in turn remaps to CoordList.
// Both index levels are 1-based per the IFC specification.
auto pn_index = inst->PnIndex();
auto resolve = [&](int idx) -> const taxonomy::point3::ptr& {
if (pn_index) {
if (idx < 1 || idx > (int)pn_index->size()) {
throw IfcParse::IfcException("IfcPolygonalFaceSet PnIndex out of bounds for index " + boost::lexical_cast<std::string>(idx));
}
idx = (*pn_index)[idx - 1];
}
if (idx < 1 || idx > max_index) {
throw IfcParse::IfcException("IfcPolygonalFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(idx));
}
return points[idx - 1];
};
auto shell = taxonomy::make<taxonomy::shell>();
for (auto& f : *polygonal_faces) {
auto fa = taxonomy::make<taxonomy::face>();
shell->children.push_back(fa);
@@ -52,17 +69,14 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolygonalFaceSet* inst) {
auto indices = f->CoordIndex();
taxonomy::point3::ptr previous;
for (std::vector<int>::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) {
if (*jt < 1 || *jt > max_index) {
throw IfcParse::IfcException("IfcPolygonalFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
}
auto current = points[(*jt) - 1];
auto current = resolve(*jt);
if (jt != indices.begin()) {
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
}
previous = current;
}
if (!indices.empty()) {
auto current = points[indices.front() - 1];
auto current = resolve(indices.front());
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
}
}
@@ -77,17 +91,14 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolygonalFaceSet* inst) {
loop->external = false;
for (std::vector<int>::const_iterator jt = li.begin(); jt != li.end(); ++jt) {
if (*jt < 1 || *jt > max_index) {
throw IfcParse::IfcException("IfcPolygonalFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
}
auto current = points[(*jt) - 1];
auto current = resolve(*jt);
if (jt != li.begin()) {
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
}
previous = current;
}
if (!li.empty()) {
auto current = points[li.front() - 1];
auto current = resolve(li.front());
loop->children.push_back(taxonomy::make<taxonomy::edge>(previous, current));
}
}
+18 -4
View File
@@ -39,6 +39,23 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcTriangulatedFaceSet* inst) {
int max_index = (int)points.size();
// When the optional PnIndex is present, CoordIndex values do not index into
// CoordList directly but into PnIndex, which in turn remaps to CoordList.
// Both index levels are 1-based per the IFC specification.
auto pn_index = inst->PnIndex();
auto resolve = [&](int idx) -> const taxonomy::point3::ptr& {
if (pn_index) {
if (idx < 1 || idx > (int)pn_index->size()) {
throw IfcParse::IfcException("IfcTriangulatedFaceSet PnIndex out of bounds for index " + boost::lexical_cast<std::string>(idx));
}
idx = (*pn_index)[idx - 1];
}
if (idx < 1 || idx > max_index) {
throw IfcParse::IfcException("IfcTriangulatedFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(idx));
}
return points[idx - 1];
};
auto shell = taxonomy::make<taxonomy::shell>();
for (auto& indices : indices_list) {
@@ -51,10 +68,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcTriangulatedFaceSet* inst) {
loop->external = true;
taxonomy::point3::ptr first, previous;
for (std::vector<int>::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) {
if (*jt < 1 || *jt > max_index) {
throw IfcParse::IfcException("IfcTriangulatedFaceSet index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
}
const taxonomy::point3::ptr& current = points[(*jt) - 1];
const taxonomy::point3::ptr& current = resolve(*jt);
if (jt == indices.begin()) {
first = current;
} else {
+5 -1
View File
@@ -89,7 +89,11 @@ BIND(IfcRectangleHollowProfileDef);
BIND(IfcRectangleProfileDef);
BIND(IfcTrapeziumProfileDef);
BIND(IfcCShapeProfileDef);
// IfcAsymmetricIShapeProfileDef included
// In IFC2X3 IfcAsymmetricIShapeProfileDef is a subtype of IfcIShapeProfileDef and is
// mapped by it; from IFC4 onwards it is a standalone type and needs its own binding.
#ifdef SCHEMA_IfcAsymmetricIShapeProfileDef_HAS_BottomFlangeWidth
BIND(IfcAsymmetricIShapeProfileDef);
#endif
BIND(IfcIShapeProfileDef);
BIND(IfcLShapeProfileDef);
BIND(IfcTShapeProfileDef);
@@ -311,8 +311,12 @@ CLI Manual
output.
--force-space-transparency arg Overrides transparency of spaces in
geometry output.
--circle-segments arg (= 16) Number of segments to approximate full
circles in CGAL kernel.
--circle-segments arg (= 0) Number of segments to approximate full
circles in the CGAL kernel. When 0 (the
default) the segment count is derived from
mesher-linear-deflection instead, so curves
stay within the deflection tolerance
regardless of radius.
--cgal-smooth-angle-degrees arg (= -1)
Angle in degrees under which adjacent
facets will have averaged vertex
@@ -72,6 +72,8 @@ Filtering is typically used to select any IFC element or type.
"``IfcPump, location=""Level 3""``", "Locations bubble up the hierarchy. So if a pump is in a space and that space is on Level 3, then you can say ""all pumps on level 3"" which will include that pump in the space."
"``IfcElement, query:""parent.Name""=""My Site""``", "Only elements *immediately* under ""My Site"" in the spatial hierarchy. Unlike the ``location`` and ``parent`` filters, which both match at any depth, the ``parent`` query key resolves the direct parent only, so nested storeys (and their contents) are excluded."
The filter elements syntax works by specifying one or more groups of filters
separated by a ``+`` character. Each filter group will return a set of filtered
elements, and these are unioned together.
@@ -111,6 +113,15 @@ will search through all IfcTypeProducts and IfcProducts in the IFC project.
"Parent", "Filter", "``parent{{=}}{{value}}``", "``parent=Foo`` specifies the criteria that elements must be a direct or indirect child in the spatial hierarchy to an element with a ``Name`` attribute with a value of ``Foo``."
"Query", "Filter", "``query:{{keys}}{{=}}{{value}}``", "``query:types.count=0`` specifies the criteria that elements must have zero type occurrences. The query keys corresponds to the syntax used in the `Getting element values`_ section"
.. note::
The ``location`` and ``parent`` filters both match at **any depth** in the
spatial hierarchy. To match only elements *immediately* contained in (or
aggregated under) a spatial element, use the ``parent`` query key, which
resolves the direct parent only. For example,
``query:"parent.Name"="My Site"`` selects elements directly under ``My
Site`` but excludes anything nested inside its sub-storeys or spaces.
When you specify a filter with a ``{{=}}`` check, you can choose from one of
the following comparison checks:
@@ -191,7 +202,7 @@ Valid keys are:
"``storey``", "Gets the first IfcBuildingStorey spatial element that an element is contained in."
"``building``", "Gets the first IfcBuilding spatial element that an element is contained in."
"``site``", "Gets the first IfcSite spatial element that an element is contained in."
"``parent``", "Gets the parent element in the spatial hierarchy."
"``parent``", "Gets the **immediate** parent element in the spatial hierarchy (the direct spatial container, or the direct aggregate/nest/fill/void parent). Combine with ``.Name`` in a query filter to match only immediate children, e.g. ``query:""parent.Name""=""My Site""``."
"``classification``", "Gets the element's classification reference(s)"
"``group``", "Gets the element's group(s)"
"``system``", "Gets the element's system(s). This is a subset of group(s)."
@@ -228,10 +228,10 @@ circle-segments
+------+-----------------------+---------+
| Type | IfcConvert Option | Default |
+======+=======================+=========+
| INT | ``--circle-segments`` | 16 |
| INT | ``--circle-segments`` | 0 |
+------+-----------------------+---------+
Number of segments to approximate full circles in CGAL kernel.
Number of segments to approximate full circles in the CGAL kernel. When 0 (the default) the segment count is derived from mesher-linear-deflection instead, so curves stay within the deflection tolerance regardless of radius.
context-identifiers
^^^^^^^^^^^^^^^^^^^
@@ -231,7 +231,7 @@ def open(
kwargs = {"mmap": mmap}
if logger is not None:
kwargs["logger"] = logger
f = ifcopenshell_wrapper.open(str(path.absolute()), **kwargs) # ty: ignore[unknown-argument]
f = ifcopenshell_wrapper.open(str(path.absolute()), **kwargs)
else:
f = ifcopenshell_wrapper.open(str(path.absolute()), False, *((logger,) if logger is not None else ()))
return file(f)
@@ -49,6 +49,7 @@ Future versions of this API may support:
from ._get_segment_start_point_label import register_referent_name_callback
from .add_stationing_referent import add_stationing_referent
from .add_positioning_referent import add_positioning_referent
from .add_vertical_layout import add_vertical_layout
from .add_zero_length_segment import add_zero_length_segment
from .create import create
@@ -94,6 +95,7 @@ from .util import *
__all__ = [
"add_stationing_referent",
"add_positioning_referent",
"add_vertical_layout",
"add_zero_length_segment",
"create",
@@ -22,8 +22,6 @@ import numpy as np
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
import ifcopenshell.util.unit
from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._get_segment_endpoint import _get_segment_endpoint
from ifcopenshell.api.alignment._update_zero_length_segment_placement import _update_zero_length_segment_placement
@@ -22,28 +22,11 @@ import numpy as np
import ifcopenshell
import ifcopenshell.api.alignment
from ifcopenshell.api.alignment import _map_alignment_cant_segment
from ifcopenshell.api.alignment._update_zero_length_segment_placement import _update_zero_length_segment_placement
import ifcopenshell.api.nest
import ifcopenshell.api.pset
import ifcopenshell.geom
import ifcopenshell.util.alignment
import ifcopenshell.util.unit
from ifcopenshell import entity_instance, ifcopenshell_wrapper
from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._add_segment_to_curve import _add_segment_to_curve
from ifcopenshell.api.alignment._get_segment_endpoint import _get_segment_endpoint
from ifcopenshell.api.alignment._get_segment_start_point_label import (
_get_segment_start_point_label,
)
from ifcopenshell.api.alignment._map_alignment_cant_segment import (
_map_alignment_cant_segment,
)
from ifcopenshell.api.alignment._map_alignment_horizontal_segment import (
_map_alignment_horizontal_segment,
)
from ifcopenshell.api.alignment._map_alignment_vertical_segment import (
_map_alignment_vertical_segment,
)
def _add_segment_to_layout(
@@ -18,11 +18,7 @@
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.util.alignment
from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._get_segment_start_point_label import (
_get_segment_start_point_label,
)
def _add_zero_length_segment(file: ifcopenshell.file, layout: entity_instance) -> None:
@@ -18,6 +18,7 @@
import ifcopenshell.api.alignment
import ifcopenshell.geom
from ifcopenshell import entity_instance, ifcopenshell_wrapper
from ifcopenshell.api.alignment._map_alignment_segment import _map_alignment_segment
from typing import Union
@@ -0,0 +1,113 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2025 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api.alignment
from ifcopenshell.api.alignment.update_fallback_position import update_fallback_position
import ifcopenshell.api.pset
import ifcopenshell.guid
from ifcopenshell import entity_instance
def add_positioning_referent(
file: ifcopenshell.file,
name: str,
alignment: entity_instance,
distance_along: float,
station: float,
positioned_product: entity_instance,
) -> entity_instance:
"""
Semantically defines the position of a product along an alignment by adding an IfcReferent to the alignment that defines the stationing system.
:param alignment: the alignment to receive the referent
:param distance_along: distance along the alignment basis curve
:param station: station value
:param name: name to assign to IfcReferent.Name, typically a stringized version of the station value
:param positioned_product: the product whose position is informed by the referent
:return: referent
Example:
.. code:: python
alignment = model.by_type("IfcAlignment")[0]
pier = model.by_type("IfcBridgePart")[0]
ifcopenshell.api.alignment.add_positioning_referent(model,name="Pier 1 Sta 1+00",alignment=alignment,distance_along=0.0,station=100.0,positioned_product=pier)
"""
curve = ifcopenshell.api.alignment.get_curve(alignment)
object_placement = None
representation = None
if curve and curve.is_a("IfcCompositeCurve") and 0 < len(curve.Segments):
object_placement = file.createIfcLinearPlacement(
RelativePlacement=file.createIfcAxis2PlacementLinear(
Location=file.createIfcPointByDistanceExpression(
DistanceAlong=file.createIfcLengthMeasure(distance_along),
OffsetLateral=None,
OffsetVertical=None,
OffsetLongitudinal=None,
BasisCurve=curve,
)
),
)
update_fallback_position(file, object_placement)
else:
object_placement = file.createIfcLocalPlacement(
PlacementRelTo=None,
RelativePlacement=file.createIfcAxis2Placement2D(
Location=file.createIfcCartesianPoint(alignment.ObjectPlacement.RelativePlacement.Location.Coordinates)
),
)
# this commented out code is what you would do to add a geometric representation of the referent
# the example is a circle. a better way would be to pass a representation into the function
# representation = file.create_entity(
# name="IfcCircle",
# position=file.createIfcAxis2Placement2D(Location=file.createIfcCartesianPoint(Coordinates=(0.0, 0.0)),
# radius=1.0)
# )
# create referent for the station
referent = file.createIfcReferent(
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=None,
Name=name,
Description=None,
ObjectType=None,
ObjectPlacement=object_placement,
Representation=representation,
PredefinedType="POSITION",
)
pset_stationing = ifcopenshell.api.pset.add_pset(file, product=referent, name="Pset_Stationing")
ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties={"Station": station})
if len(referent.Positions) == 0:
rel_positions = file.createIfcRelPositions(
GlobalId=ifcopenshell.guid.new(),
RelatingPositioningElement=referent,
RelatedProducts=[
positioned_product,
],
)
else:
referent.Positions[0].RelatedProducts += (positioned_product,)
return referent
@@ -16,35 +16,35 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from typing import Optional
import ifcopenshell
import ifcopenshell.api.alignment
from ifcopenshell.api.alignment.update_fallback_position import update_fallback_position
import ifcopenshell.api.pset
import ifcopenshell.geom
import ifcopenshell.guid
import ifcopenshell.util.element
import ifcopenshell.util.unit
from ifcopenshell import entity_instance, ifcopenshell_wrapper
from ifcopenshell import entity_instance
def add_stationing_referent(
file: ifcopenshell.file,
name: str,
alignment: entity_instance,
distance_along: float,
station: float,
name: str,
positioned_product: entity_instance,
incoming_station: Optional[float] = None,
on_basis_curve: Optional[bool] = None,
) -> entity_instance:
"""
Adds an IfcReferent to the alignment with the Pset_Stationing property set.
Adds an IfcReferent to the alignment that defines the stationing system.
:param name: name to assign to IfcReferent.Name, typically a stringized version of the station value
:param alignment: the alignment to receive the referent
:param distance_along: distance along the alignment basis curve
:param station: station value
:param name: name to assign to IfcReferent.Name, typically a stringized version of the station value
:param positioned_product: the product whose position is informed by the referent
:param incoming_station: station value of the incoming segment, only set to specify a station equation
:param on_basis_curve: whether the referent is positioned on the basis curve or the alignment curve, if None the function will default to the basis curve
:return: referent
Example:
@@ -52,14 +52,21 @@ def add_stationing_referent(
.. code:: python
alignment = model.by_type("IfcAlignment")[0]
ifcopenshell.api.alignment.add_stationing_referent(model,alignment=alignment,distance_along=0.0,station=100.0)
ifcopenshell.api.alignment.add_stationing_referent(model,name="1+00.0",alignment=alignment,distance_along=0.0,station=100.0)
"""
basis_curve = ifcopenshell.api.alignment.get_basis_curve(alignment)
if on_basis_curve is None:
on_basis_curve = True
curve = (
ifcopenshell.api.alignment.get_basis_curve(alignment)
if on_basis_curve
else ifcopenshell.api.alignment.get_curve(alignment)
)
object_placement = None
representation = None
if basis_curve and basis_curve.is_a("IfcCompositeCurve") and 0 < len(basis_curve.Segments):
if curve and curve.is_a("IfcCompositeCurve") and 0 < len(curve.Segments):
object_placement = file.createIfcLinearPlacement(
RelativePlacement=file.createIfcAxis2PlacementLinear(
Location=file.createIfcPointByDistanceExpression(
@@ -67,7 +74,7 @@ def add_stationing_referent(
OffsetLateral=None,
OffsetVertical=None,
OffsetLongitudinal=None,
BasisCurve=basis_curve,
BasisCurve=curve,
)
),
)
@@ -100,8 +107,12 @@ def add_stationing_referent(
Representation=representation,
PredefinedType="STATION",
)
properties = {"Station": station}
if incoming_station is not None:
properties["IncomingStation"] = incoming_station
pset_stationing = ifcopenshell.api.pset.add_pset(file, product=referent, name="Pset_Stationing")
ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties={"Station": station})
ifcopenshell.api.pset.edit_pset(file, pset=pset_stationing, properties=properties)
nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
if nest is None:
@@ -115,15 +126,4 @@ def add_stationing_referent(
nest.RelatedObjects, key=lambda x: ifcopenshell.util.element.get_pset(x, name="Pset_Stationing", prop="Station")
)
if len(referent.Positions) == 0:
rel_positions = file.createIfcRelPositions(
GlobalId=ifcopenshell.guid.new(),
RelatingPositioningElement=referent,
RelatedProducts=[
positioned_product,
],
)
else:
referent.Positions[0].RelatedProducts += (positioned_product,)
return referent
@@ -51,18 +51,6 @@ def _move_vertical_layout_to_child_alignment(
# aggregate the child alignment to the parent alignment
ifcopenshell.api.aggregate.assign_object(file, products=[child_alignment], relating_object=parent_alignment)
# move all referents positioning segments of the vertical layout to the referent nest of the child alignment
child_referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, child_alignment)
parent_referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, parent_alignment)
for referent in parent_referent_nest.RelatedObjects:
for product in referent.Positions[0].RelatedProducts:
if product.is_a("IfcAlignmentSegment") and product.Nests[0].RelatingObject == vertical_layout:
# ifcopenshell.api.nest.change_nest(file,referent,child_alignment) - this doesn't work because referent is assigned to child_alignment.IsNestedBy[0].RelatedObjects
# and it needs to be assigned to child_alignment.IsNestedBy[1].RelatedObjects
# move the referent manually - unassign it and add it to the child alignment's referent nest
ifcopenshell.api.nest.unassign_object(file, [referent])
child_referent_nest.RelatedObjects += (referent,)
# if the parent alignment has a representation, move the Axis/Curve3D represention to the child alignment
base_curve = ifcopenshell.api.alignment.get_basis_curve(parent_alignment)
if base_curve:
@@ -23,18 +23,8 @@ import ifcopenshell.api.alignment
from ifcopenshell.api.alignment._get_segment_endpoint import _get_segment_endpoint
from ifcopenshell.api.alignment._update_zero_length_segment_placement import _update_zero_length_segment_placement
import ifcopenshell.api.nest
import ifcopenshell.ifcopenshell_wrapper as wrapper
import ifcopenshell.util.unit
from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._get_segment_start_point_label import (
_get_segment_start_point_label,
)
from ifcopenshell.api.alignment._map_alignment_horizontal_segment import (
_map_alignment_horizontal_segment,
)
from ifcopenshell.api.alignment._map_alignment_vertical_segment import (
_map_alignment_vertical_segment,
)
from ifcopenshell.api.alignment._update_curve_segment_transition_code import (
_update_curve_segment_transition_code,
)
@@ -87,9 +87,7 @@ def create(
_create_geometric_representation(file, alignment)
referent_name = ifcopenshell.util.alignment.station_as_string(file, start_station)
referent = ifcopenshell.api.alignment.add_stationing_referent(
file, alignment, 0.0, start_station, referent_name, alignment
)
referent = ifcopenshell.api.alignment.add_stationing_referent(file, referent_name, alignment, 0.0, start_station)
for layout in alignment_layouts:
_add_zero_length_segment(file, layout)
@@ -141,7 +141,7 @@ def create_as_polyline(
# define stationing
name = ifcopenshell.util.alignment.station_as_string(file, start_station)
referent = ifcopenshell.api.alignment.add_stationing_referent(file, alignment, 0.0, start_station, name, alignment)
referent = ifcopenshell.api.alignment.add_stationing_referent(file, name, alignment, 0.0, start_station)
# IFC 4.1.4.1.1 Alignment Aggregation To Project
project = file.by_type("IfcProject")[0]
@@ -21,9 +21,7 @@ from typing import Union
import numpy as np
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.geom
from ifcopenshell import entity_instance, ifcopenshell_wrapper
from ifcopenshell import entity_instance
from ifcopenshell.api.alignment._add_segment_to_layout import _add_segment_to_layout
@@ -16,23 +16,47 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.util.element
from ifcopenshell import entity_instance
def distance_along_from_station(file: ifcopenshell.file, alignment: entity_instance, station: float) -> float:
def _distance_along_of_referent(referent: entity_instance) -> float:
placement = referent.ObjectPlacement
if placement.is_a("IfcLinearPlacement"):
return placement.RelativePlacement.Location.DistanceAlong.wrappedValue
# IfcLocalPlacement fallback (e.g. semantic-only alignment, or the placement could not yet
# be expressed relative to a basis curve) carries no DistanceAlong; it is only ever used for
# the starting referent, at distance 0.0.
return 0.0
def distance_along_from_station(file: ifcopenshell.file, alignment: entity_instance, station: float) -> Optional[float]:
"""
Given a station, returns the distance along the horizontal alignment.
If the alignment does not have stationing defined with an IfcReferent, the start of the alignment is assumed
to be at station 0.0. That is, the station is the distance along.
.. note:: The current implementation does not account for station equations and assumes stationing is increasing along the alignment.
Station equations (where Pset_Stationing.IncomingStation is set on a referent) are taken into account.
For each STATION referent nested to the alignment, DistanceAlong (D) and the outgoing station (S, i.e.
Pset_Stationing.Station) are read off, sorted by DistanceAlong. The requested station is located within
the segment defined by the last referent whose outgoing station is less than or equal to it, and the
distance along is computed as D + (station - S) for that referent.
If the station falls within a gap introduced by a forward (gap) station equation - that is, it was skipped
over by the equation - there is no distance along that corresponds to it, and None is returned.
Note that an overlap (backward) station equation causes a range of stations to correspond to two distinct
distances along the alignment, one on either side of the equation. This implementation returns the distance
along in the segment following the equation (i.e. the outgoing side).
:param alignment: the alignment
:param station: station value
:return: distance along the horizontal alignment
:return: distance along the horizontal alignment, or None if the station falls inside a station equation gap
Example:
@@ -43,6 +67,36 @@ def distance_along_from_station(file: ifcopenshell.file, alignment: entity_insta
print(dist_along) # 100.00
"""
start_station = ifcopenshell.api.alignment.get_alignment_start_station(file, alignment)
dist_along = station - start_station
return dist_along
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
if referent_nest is None:
start_station = ifcopenshell.api.alignment.get_alignment_start_station(file, alignment)
return station - start_station
stations = [
(
_distance_along_of_referent(referent),
ifcopenshell.util.element.get_pset(referent, name="Pset_Stationing", prop="Station"),
)
for referent in referent_nest.RelatedObjects
]
stations.sort(key=lambda entry: entry[0])
index = None
for i, (distance_along, outgoing_station) in enumerate(stations):
if outgoing_station <= station:
index = i
if index is None:
# station precedes the alignment's starting station; extrapolate from the first referent
distance_along, outgoing_station = stations[0]
return distance_along + (station - outgoing_station)
distance_along, outgoing_station = stations[index]
if index + 1 < len(stations):
next_distance_along, _ = stations[index + 1]
if station - outgoing_station > next_distance_along - distance_along:
# the station was skipped over by a forward (gap) station equation
return None
return distance_along + (station - outgoing_station)
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from collections.abc import Sequence
from ifcopenshell import entity_instance
@@ -19,6 +19,7 @@
import numpy as np
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.util.placement
from ifcopenshell import entity_instance
@@ -117,7 +117,7 @@ def assign_cost_item_quantity(
"products": products or [],
"prop_name": prop_name,
"formula": formula,
"ifc_class" : ifc_class
"ifc_class": ifc_class,
}
return usecase.execute()
@@ -134,7 +134,7 @@ class Usecase:
continue
self.assign_cost_control(related_object=product, cost_item=self.settings["cost_item"])
if self.settings["formula"]:
tree = ast.parse(self.settings["formula"], mode = "eval")
tree = ast.parse(self.settings["formula"], mode="eval")
collector = VariableExtractor()
collector.visit(tree)
variables = collector.variables
@@ -144,10 +144,10 @@ class Usecase:
value = getter(product, variable)
if value is None:
print(
f"WARNING: Variable '{variable}' in product '{product.Name}' "
f"is missing (None). Check Pset/Qset or property name."
)
print(
f"WARNING: Variable '{variable}' in product '{product.Name}' "
f"is missing (None). Check Pset/Qset or property name."
)
elif value == 0:
print(
f"WARNING: Variable '{variable}' in product '{product.Name}' "
@@ -159,7 +159,9 @@ class Usecase:
new_quantity = None
for quantity in self.quantities:
if quantity.Formula == self.settings["formula"] and len(self.settings["products"]) == 1: #Todo improve it
if (
quantity.Formula == self.settings["formula"] and len(self.settings["products"]) == 1
): # Todo improve it
new_quantity = quantity
self.settings["ifc_class"] = quantity.is_a()
continue
@@ -184,23 +186,23 @@ class Usecase:
self.update_cost_item_count()
def get_value_from_pset(
self,
product:ifcopenshell.entity_instance,
v: str,
self,
product: ifcopenshell.entity_instance,
v: str,
) -> float:
pset_name = v.split(".")[0]
pset = ifcopenshell.util.element.get_pset(product, pset_name)
pset_property_name = v.split(".")[1]
return (pset or {}).get(pset_property_name,None)
return (pset or {}).get(pset_property_name, None)
def get_value_from_qset(
self,
product:ifcopenshell.entity_instance,
v: str,
self,
product: ifcopenshell.entity_instance,
v: str,
) -> float:
qtos = ifcopenshell.util.element.get_psets(product, qtos_only = True)
qtos = ifcopenshell.util.element.get_psets(product, qtos_only=True)
quantities = next(iter(qtos.values()), {})
return (quantities or {}).get(v,None)
return (quantities or {}).get(v, None)
def assign_cost_control(
self, related_object: ifcopenshell.entity_instance, cost_item: ifcopenshell.entity_instance
@@ -243,6 +245,7 @@ class Usecase:
count += 1
quantity[3] = count
OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
@@ -252,18 +255,20 @@ OPERATORS = {
ast.USub: operator.neg,
}
def build_full_name(node):
#used for variables with dots
# used for variables with dots
parts = []
while isinstance(node, ast.Attribute):
parts.append(node.attr)
node = node.value
parts.append(node.attr)
node = node.value
if isinstance(node, ast.Name):
parts.append(node.id)
return ".".join(reversed(parts))
class VariableExtractor(ast.NodeVisitor):
def __init__(self):
self.variables = set()
@@ -274,6 +279,7 @@ class VariableExtractor(ast.NodeVisitor):
def visit_Attribute(self, node):
self.variables.add(build_full_name(node))
class FormulaEvaluator(ast.NodeVisitor):
def __init__(self, values):
self.values = values
@@ -281,7 +287,7 @@ class FormulaEvaluator(ast.NodeVisitor):
def visit_BinOp(self, node):
left = self.visit(node.left)
right = self.visit(node.right)
return OPERATORS[type(node.op)](left, right)
return OPERATORS[type(node.op)](left, right) # ty: ignore[too-many-positional-arguments]
def visit_Name(self, node):
return self.values[node.id]
@@ -221,8 +221,7 @@ for id in to_emit:
statements.append("%s << %s" % (id, stmt))
if __name__ == "__main__":
print(
r"""
print(r"""
# This file is generated by IfcOpenShell ifcexpressparser bootstrap.py
from __future__ import annotations
@@ -261,6 +260,4 @@ if __name__ == "__main__":
mdl = importlib.import_module(output)
mdl.Generator(m).emit()
sys.stdout.write(m.schema.name)
"""
% ("\n ".join(statements))
)
""" % ("\n ".join(statements)))
@@ -695,6 +695,7 @@ codegen_rule("MOD", lambda context: "%")
codegen_rule("TRUE", lambda context: "True")
codegen_rule("FALSE", lambda context: "False")
def _dotted_name(node: ast.AST):
"""Return dotted name for Name/Attribute chains, else None."""
if isinstance(node, ast.Name):
@@ -704,6 +705,7 @@ def _dotted_name(node: ast.AST):
return f"{base}.{node.attr}" if base else node.attr
return None
class AttributeGetattrTransformer(ast.NodeTransformer):
def visit_Attribute(self, node):
parents = []
@@ -720,7 +722,7 @@ class AttributeGetattrTransformer(ast.NodeTransformer):
if isinstance(node.ctx, ast.Store):
return node
if _dotted_name(node) in ('ifcopenshell.create_entity', 'str.lower'):
if _dotted_name(node) in ("ifcopenshell.create_entity", "str.lower"):
return node
if node.attr.startswith("__"):
@@ -363,24 +363,18 @@ class EarlyBoundCodeWriter:
)
)
self.statements[self.statements.index("{factory_placeholder}")] = (
"""
self.statements[self.statements.index("{factory_placeholder}")] = """
class %(schema_name)s_instance_factory : public IfcParse::instance_factory {
virtual IfcUtil::IfcBaseClass* operator()(const IfcParse::declaration* decl, IfcEntityInstanceData&& data) const {
%(instance_mapping)s
}
};
"""
% locals()
)
""" % locals()
""
self.statements[self.statements.index("{string_pool_placeholder}")] = (
"""
self.statements[self.statements.index("{string_pool_placeholder}")] = """
const std::string strings[] = {%s};
"""
% ",".join(map(lambda s: '"%s"s' % s, self.strings))
)
""" % ",".join(map(lambda s: '"%s"s' % s, self.strings))
def __str__(self):
return "\n".join(self.statements)
@@ -145,8 +145,7 @@ class configuration:
config.set(
"snippets",
"print all wall ids",
self.config_encode(
"""
self.config_encode("""
###########################################################################
# A simple script that iterates over all walls in the current model #
# and prints their Globally unique IDs (GUIDS) to the console window #
@@ -154,15 +153,13 @@ class configuration:
for wall in model.by_type("IfcWall"):
print ("wall with global id: "+str(wall.GlobalId))
""".lstrip()
),
""".lstrip()),
)
config.set(
"snippets",
"print properties of current selection",
self.config_encode(
"""
self.config_encode("""
###########################################################################
# A simple script that iterates over all IfcPropertySets of the currently #
# selected object and prints them to the console #
@@ -180,8 +177,7 @@ if selection:
for prop in relDefinesByProperties.RelatingPropertyDefinition.HasProperties:
print ("{:<20} :{}".format(prop.Name,prop.NominalValue.wrappedValue))
print ("\\n")
""".lstrip()
),
""".lstrip()),
)
with open(conf_file, "w") as configfile:
config.write(configfile)
@@ -1697,10 +1697,16 @@ class uninitialized_tag: ...
def arrange_polygons(settings, polygons): ...
def clear_schemas(): ...
def construct_iterator(geometry_library, settings, file, num_threads): ...
def construct_iterator_with_include_exclude(geometry_library, settings, file, elems, include, num_threads): ...
def construct_iterator_with_include_exclude_globalid(geometry_library, settings, file, elems, include, num_threads): ...
def construct_iterator_with_include_exclude_id(geometry_library, settings, file, elems, include, num_threads): ...
def construct_iterator(geometry_library, settings, file, num_threads, logger=...): ...
def construct_iterator_with_include_exclude(
geometry_library, settings, file, elems, include, num_threads, logger=...
): ...
def construct_iterator_with_include_exclude_globalid(
geometry_library, settings, file, elems, include, num_threads, logger=...
): ...
def construct_iterator_with_include_exclude_id(
geometry_library, settings, file, elems, include, num_threads, logger=...
): ...
def convert_loop_to_function_item(loop): ...
def create_box(*args): ...
def create_epeck(*args): ...
@@ -1717,8 +1723,8 @@ def line_segments_to_polygons(s, eps, segments): ...
def map_shape(settings, instance): ...
def nary_union(sequence): ...
def new_IfcBaseClass(schema_identifier: str, name: str) -> entity_instance: ...
def open(fn: str, readonly: bool = False) -> file: ...
def parse_ifcxml(filename): ...
def open(fn: str, readonly: bool = False, logger=...) -> file: ...
def parse_ifcxml(filename, logger=...): ...
def polygons_to_svg(*args): ...
def read(data): ...
def register_schema(arg1): ...
@@ -56,7 +56,7 @@ def append_zero_length_segments(file: ifcopenshell.file) -> ifcopenshell.file:
for alignment in alignments:
layouts = ifcopenshell.api.alignment.get_alignment_layouts(alignment)
for layout in layouts:
ifcopenshell.api.alignment.add_zero_length_segment(patched_file, layout, include_referent=False)
ifcopenshell.api.alignment.add_zero_length_segment(patched_file, layout)
curve = ifcopenshell.api.alignment.get_layout_curve(layout)
if curve:
ifcopenshell.api.alignment.add_zero_length_segment(patched_file, curve)
@@ -355,8 +355,7 @@ def get_cost_rate(
class CostValueUnserialiser:
def parse(self, formula: str):
l = lark.Lark(
"""start: formula
l = lark.Lark("""start: formula
formula: operand (operator operand)*
operand: value | category "(" formula ")"
value: NUMBER?
@@ -393,8 +392,7 @@ class CostValueUnserialiser:
NEWLINE: (CR? LF)+
%ignore WS // Disregard spaces in text
"""
)
""")
start = l.parse(formula)
return self.get_formula(start.children[0])
@@ -265,6 +265,44 @@ class DocExtractor:
description = description.strip()
return description
def extract_full_description(self, html: str) -> str:
"""Extract the full definition text from markdown-derived HTML.
Entity/type documentation often introduces a bulleted list mid-definition
(e.g. "... may include:" followed by a `<ul>`), or continues with another
paragraph after it. Naively taking only the first `<p>` silently drops
that content (see #4624). Instead, walk all top-level paragraph/list
elements in document order, stopping before any `<blockquote>` (which in
these docs holds the HISTORY/NOTE remarks).
"""
soup = BeautifulSoup(html, features="lxml")
body = soup.body or soup
parts = []
for child in body.find_all(["p", "ul", "ol"], recursive=False):
if child.name in ("ul", "ol"):
items = [li.get_text() for li in child.find_all("li", recursive=False)]
parts.append(" ".join(f"- {item}" for item in items))
else:
part = child.get_text()
# Some IFC2X3 docs put the HISTORY remark in a plain leading
# paragraph rather than a blockquote; it is metadata, not part
# of the definition.
if re.match(r"\s*(HISTORY|IFC2x Edition)\b", part):
continue
parts.append(part)
text = " ".join(parts)
# A remark can also be a "lazy" blockquote continuation inside a
# paragraph (a literal "> HISTORY ..." tail that markdown does not
# turn into a <blockquote>) or an inline "HISTORY: ..." sentence; cut
# the definition there.
text = re.split(r"\s*(?:>\s*)?(?:HISTORY\s*:|>\s*HISTORY\b|(?:>\s*)?IFC2x Edition\b)", text)[0]
# strip inline kramdown/pandoc attribute-list markers that survive as literal
# text once we're no longer limited to the first paragraph, e.g.
# "{ .change-ifc2x4}", "{ .note}", "{: .extDef}".
text = re.sub(r"\{[^{}]*\}", "", text)
text = re.sub(r"\s+", " ", text).strip()
return text
def extract_ifc2x3(self):
print("Parsing data for Ifc2.3.0.1")
if not IFC2x3_DOCS_LOCATION.is_dir():
@@ -337,7 +375,7 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
entity_description = BeautifulSoup(html, features="lxml").find("p").text
entity_description = self.extract_full_description(html)
entity_description = entity_description.replace("\n", " ")
entity_description = entity_description.replace("\u00a0", " ")
@@ -442,9 +480,14 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
property_set_description = BeautifulSoup(html, features="lxml").find("p").text
property_set_description = self.extract_full_description(html)
property_set_description = property_set_description.replace("\n", " ")
property_set_description = property_set_description.split("HISTORY:", 1)[0]
# case-insensitive: some pset docs use "History:" instead of "HISTORY:",
# which only becomes reachable now that extract_full_description() walks
# past the first paragraph.
property_set_description = re.split(
r"HISTORY:", property_set_description, maxsplit=1, flags=re.IGNORECASE
)[0]
property_set_description = property_set_description.strip()
property_set_dict["description"] = property_set_description
else:
@@ -516,7 +559,7 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
description = BeautifulSoup(html, features="lxml").find("p").text
description = self.extract_full_description(html)
description = description.replace("\n", " ")
description = description.replace("\u00a0", " ")
property_dict["description"] = description
@@ -550,7 +593,7 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
type_description = BeautifulSoup(html, features="lxml").find("p").text
type_description = self.extract_full_description(html)
type_description = type_description.replace("\n", " ")
type_description = type_description.replace("\u00a0", " ")
type_description = type_description.replace("Definition from ISO/CD 10303-46:1992: ", "")
@@ -662,7 +705,7 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
entity_description = BeautifulSoup(html, features="lxml").find("p").text
entity_description = self.extract_full_description(html)
entity_description = entity_description.replace("\n", " ")
entity_description = entity_description.replace("\u00a0", " ")
entity_description = entity_description.replace("{ .extDef}", "")
@@ -769,9 +812,14 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
property_set_description = BeautifulSoup(html, features="lxml").find("p").text
property_set_description = self.extract_full_description(html)
property_set_description = property_set_description.replace("\n", " ")
property_set_description = property_set_description.split("HISTORY:", 1)[0]
# case-insensitive: some pset docs use "History:" instead of "HISTORY:",
# which only becomes reachable now that extract_full_description() walks
# past the first paragraph.
property_set_description = re.split(
r"HISTORY:", property_set_description, maxsplit=1, flags=re.IGNORECASE
)[0]
property_set_description = property_set_description.strip()
property_set_dict["description"] = property_set_description
else:
@@ -854,7 +902,7 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
description = BeautifulSoup(html, features="lxml").find("p").text
description = self.extract_full_description(html)
description = description.replace("\n", " ")
description = description.replace("\u00a0", " ")
property_dict["description"] = description
@@ -888,7 +936,7 @@ class DocExtractor:
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read().replace("{ .extDef}", ""))
type_description = BeautifulSoup(html, features="lxml").find("p").text
type_description = self.extract_full_description(html)
type_description = type_description.replace("\n", " ")
type_description = type_description.replace("\u00a0", " ")
type_description = type_description.replace("{ .extDef}", "")
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,10 @@
{
"IfcAbsorbedDoseMeasure": {
"description": "IfcAbsorbedDoseMeasure is a measure of the absorbed radioactivity dose.",
"description": "IfcAbsorbedDoseMeasure is a measure of the absorbed radioactivity dose. Usually measured in Gray (Gy, J/kg). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcabsorbeddosemeasure.htm"
},
"IfcAccelerationMeasure": {
"description": "IfcAccelerationMeasure is a measure of acceleration.",
"description": "IfcAccelerationMeasure is a measure of acceleration. Usually measured in m/s2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcaccelerationmeasure.htm"
},
"IfcActionRequestTypeEnum": {
@@ -48,7 +48,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcbuildingcontrolsdomain/lexical/ifcalarmtypeenum.htm"
},
"IfcAmountOfSubstanceMeasure": {
"description": "An amount of substance measure is the value for the quantity of a substance when compared with the number of atoms in 0.012 kg of carbon 12.",
"description": "An amount of substance measure is the value for the quantity of a substance when compared with the number of atoms in 0.012 kg of carbon 12. Usually measure in mole (mol). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcamountofsubstancemeasure.htm"
},
"IfcAnalysisModelTypeEnum": {
@@ -60,23 +60,23 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralanalysisdomain/lexical/ifcanalysistheorytypeenum.htm"
},
"IfcAngularVelocityMeasure": {
"description": "IfcAngularVelocityMeasure is a measure of the velocity of a body measured in terms of angle subtended per unit time.",
"description": "IfcAngularVelocityMeasure is a measure of the velocity of a body measured in terms of angle subtended per unit time. Usually measured in radians/s. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcangularvelocitymeasure.htm"
},
"IfcAppliedValueSelect": {
"description": "IfcAppliedValueSelect defines a value to be calculated within a formula.",
"description": "IfcAppliedValueSelect defines a value to be calculated within a formula. Types are used as follows: - IfcValue: A constant value using project default units. - IfcMeasureWithUnit: A constant value using specified units. - IfcReference: A value referenced on an object attribute. For cost values, the following guidance applies: - IfcMeasureWithUnit allows the specification of both the actual figure for the value together with the currency in which the value is represented. - Selecting IfcMonetaryMeasure allows the specification only of the value, the currency being as set by the global context. - Selecting IfcRatioMeasure assumes that the amount is a percentage or other REAL number. Note that if the amount is normally specified as -20%, then this figure will need to be converted to a multiplier of 0.8",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifccostresource/lexical/ifcappliedvalueselect.htm"
},
"IfcArcIndex": {
"description": "The IfcArcIndex describes a single circular arc segment within a poly curve by providing a list on indices. The first index is the start point of the circular arc, the second index is a point on arc, the third index is the end point of the circular arc. The three points shall not be co-linear.",
"description": "The IfcArcIndex describes a single circular arc segment within a poly curve by providing a list on indices. The first index is the start point of the circular arc, the second index is a point on arc, the third index is the end point of the circular arc. The three points shall not be co-linear. Informal Propositions: - The second index, resolving to a point on arc, shall resolve into a Cartesian point that has approximately the same distance to the start point and the end point of the circular arc. This is due to avoid numeric instability, if the point on arc is too close to either the start or the end point.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcgeometryresource/lexical/ifcarcindex.htm"
},
"IfcAreaDensityMeasure": {
"description": "IfcAreaDensityMeasure is a measure of the density of a two-dimensional object and is calculated as the mass per unit area.",
"description": "IfcAreaDensityMeasure is a measure of the density of a two-dimensional object and is calculated as the mass per unit area. Usually measured in kg/m2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcareadensitymeasure.htm"
},
"IfcAreaMeasure": {
"description": "An area measure is the value of the extent of a surface.",
"description": "An area measure is the value of the extent of a surface. Usually measured in square metre (m2). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcareameasure.htm"
},
"IfcArithmeticOperatorEnum": {
@@ -124,7 +124,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcboilertypeenum.htm"
},
"IfcBoolean": {
"description": "IfcBoolean is a defined data type of simple data type Boolean. It is required since a select type (IfcSimpleValue) cannot directly include simple types in its select list. A Boolean type can have value TRUE or FALSE.",
"description": "IfcBoolean is a defined data type of simple data type Boolean. It is required since a select type (IfcSimpleValue) cannot directly include simple types in its select list. A Boolean type can have value TRUE or FALSE. Type: BOOLEAN",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcboolean.htm"
},
"IfcBooleanOperand": {
@@ -136,7 +136,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcgeometricmodelresource/lexical/ifcbooleanoperator.htm"
},
"IfcBoxAlignment": {
"description": "The box alignment specifies the alignment of the text box relative to its position. The following string values shall be used:",
"description": "The box alignment specifies the alignment of the text box relative to its position. The following string values shall be used: - top-left - top-middle - top-right - middle-left - center - middle-right - bottom-left - bottom-middle - bottom-right Figure 1 illustrates alignment values. Figure 2 illustrates use of alignment values together with the placement and planar extent.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationdefinitionresource/lexical/ifcboxalignment.htm"
},
"IfcBuildingElementPartTypeEnum": {
@@ -172,11 +172,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifccablesegmenttypeenum.htm"
},
"IfcCardinalPointReference": {
"description": "An IfcCardinalPointReference is an index reference to significant points of a section profile. This index is used to describe the spatial relationship between the section of a member and a reference axis of the same member.",
"description": "An IfcCardinalPointReference is an index reference to significant points of a section profile. This index is used to describe the spatial relationship between the section of a member and a reference axis of the same member. Indexes 1...9 refer to points at the bounding box of a profile. Indexes 10...19 refer to points defined by geometric centroid (usually centre of gravity) and shear centre, and their combinations with bounding box coordinates. In particular, the following index values are specified in this specification: - bottom left - bottom centre - bottom right - mid-depth left - mid-depth centre - mid-depth right - top left - top centre - top right - geometric centroid - bottom in line with the geometric centroid - left in line with the geometric centroid - right in line with the geometric centroid - top in line with the geometric centroid - shear centre - bottom in line with the shear centre - left in line with the shear centre - right in line with the shear centre - top in line with the shear centre Other index values are possible but outside the scope of this specification. Figure 1 illustrates cardinal point values. Figure 2 illustrates an example extrusion shape with arbitrary profile (IfcArbitraryClosedProfileDef), aligned \"mid-depth right\" on the member axis. The line of sight follows the extrusion direction Z which points into the drawing plane of above illustration. Hence, \"left\" is in the positive X direction of the IfcProfileDef. \"Top\" is in the positive Y direction of the IfcProfileDef.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmaterialresource/lexical/ifccardinalpointreference.htm"
},
"IfcChangeActionEnum": {
"description": "IfcChangeActionEnum identifies the type of change that might have occurred to the object during the last session (for example, added, modified, deleted). This information is required in a partial model exchange scenario so that an application or model server will know how an object might have been affected by the previous application. Valid enumerations are:",
"description": "IfcChangeActionEnum identifies the type of change that might have occurred to the object during the last session (for example, added, modified, deleted). This information is required in a partial model exchange scenario so that an application or model server will know how an object might have been affected by the previous application. Valid enumerations are: Consider Application A will create an IFC dataset that it wants to publish to others for modification and have the ability to subsequently merge these changes back into the original model. Before publication, it may want to set the IfcChangeActionEnum to NOCHANGE to establish a baseline so that other application changes can be easily identified. Application B then receives this IFC dataset and adds a new object and sets IfcChangeActionEnum to ADDED with Application B defined as the OwningApplication. Application B then modifies an existing object and (re)defines the LastModifiedDate to the time of the modification, LastModifyingUser to the IfcPersonAndOrganization making the change, and sets the LastModifyingApplication to Application B. When Application A receives this modified dataset, it can determine which objects have been added and modified by Application B and either merge or reject these changes as necessary. Consequently, the intent is that an application only modifies the value of IfcChangeActionEnum when it does something to the object, with the further intent that a model server is responsible for clearing the IfcChangeActionEnum back to NOCHANGE when it is ready to be republished.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcutilityresource/lexical/ifcchangeactionenum.htm"
},
"IfcChillerTypeEnum": {
@@ -216,7 +216,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifccommunicationsappliancetypeenum.htm"
},
"IfcComplexNumber": {
"description": "IfcComplexNumber is a representation of a complex number expressed as an array with two elements. The first element (index 1) denotes the real component which is the numerical component of a complex number whose square roots can be calculated explicitly. The second element (index 2) denotes the imaginary component which is the numerical component of a complex number whose square roots cannot be determined other than through the provision of the square of the imaginary number j where j\\^2 = -1. Note that the imaginary component may be referred to as i in certain references.",
"description": "IfcComplexNumber is a representation of a complex number expressed as an array with two elements. The first element (index 1) denotes the real component which is the numerical component of a complex number whose square roots can be calculated explicitly. The second element (index 2) denotes the imaginary component which is the numerical component of a complex number whose square roots cannot be determined other than through the provision of the square of the imaginary number j where j\\^2 = -1. Note that the imaginary component may be referred to as i in certain references. Type: ARRAY [1:2] OF REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifccomplexnumber.htm"
},
"IfcComplexPropertyTemplateTypeEnum": {
@@ -224,7 +224,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifckernel/lexical/ifccomplexpropertytemplatetypeenum.htm"
},
"IfcCompoundPlaneAngleMeasure": {
"description": "IfcCompoundPlaneAngleMeasure is a compound measure of plane angle in degrees, minutes, seconds, and optionally millionth-seconds of arc.",
"description": "IfcCompoundPlaneAngleMeasure is a compound measure of plane angle in degrees, minutes, seconds, and optionally millionth-seconds of arc. Type: LIST [3:4] OF INTEGER Value restrictions - The first integer measure is the number of degrees and is generally not range-restricted. However, when IfcCompoundPlaneAngleMeasure is used to express geographic coordinates, only latitudes of [-90, 90] and longitudes of [-180, 180] are used in practice. - The second integer measure is the number of minutes and shall be in the range (-60, 60). - The third integer measure is the number of seconds and shall be in the range (-60, 60). - The optional fourth integer measure is the number of millionth-seconds and shall be in the range (-1 000 000, 1 000 000). Signedness All measure components have the same sign (positive or negative). It is therefore trivial to convert between floating point representation (decimal degrees) and compound representation regardless whether the angle is greater or smaller than zero. Example: Use in string representations When a compound plane angle measure is formatted for display or printout, the signs of the fractional components will usually be discarded because, to a human reader, the sign of the first component alone already indicates the sense of the angle: Another often encountered display format of latitudes and longitudes is to omit the signs and print N, S, E, W indicators instead, for example, 50&deg;58'33\"S . When stored as IfcCompoundPlaneAngleMeasure however, a compound plane angle measure is always signed, with same sign of all components.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifccompoundplaneanglemeasure.htm"
},
"IfcCompressorTypeEnum": {
@@ -236,7 +236,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifccondensertypeenum.htm"
},
"IfcConnectionTypeEnum": {
"description": "This enumeration defines the different ways how path based elements (such as IfcWallStandardCase) can connect, as shown in Figure 1.",
"description": "This enumeration defines the different ways how path based elements (such as IfcWallStandardCase) can connect, as shown in Figure 1. The enumerated items shall be used in the following combinations:",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcconnectiontypeenum.htm"
},
"IfcConstraintEnum": {
@@ -256,7 +256,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcconstructionmgmtdomain/lexical/ifcconstructionproductresourcetypeenum.htm"
},
"IfcContextDependentMeasure": {
"description": "The value of a physical quantity as defined within the exchange context.",
"description": "The value of a physical quantity as defined within the exchange context. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifccontextdependentmeasure.htm"
},
"IfcControllerTypeEnum": {
@@ -284,7 +284,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedmgmtelements/lexical/ifccostscheduletypeenum.htm"
},
"IfcCountMeasure": {
"description": "A count measure is the value of a count of items.",
"description": "A count measure is the value of a count of items. Type: NUMBER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifccountmeasure.htm"
},
"IfcCoveringTypeEnum": {
@@ -304,7 +304,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifccurtainwalltypeenum.htm"
},
"IfcCurvatureMeasure": {
"description": "IfcCurvatureMeasure is a measure for curvature, which is defined as the change of slope per length. This is typically a computed value in structural analysis. It is usually measured in rad/m.",
"description": "IfcCurvatureMeasure is a measure for curvature, which is defined as the change of slope per length. This is typically a computed value in structural analysis. It is usually measured in rad/m. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifccurvaturemeasure.htm"
},
"IfcCurveFontOrScaledCurveFontSelect": {
@@ -336,19 +336,19 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifcdataoriginenum.htm"
},
"IfcDate": {
"description": "The IfcData identifies a particular calender day, expressed by year, calender month and day in month. It is expressed by a string value following a particular lexical representation.",
"description": "The IfcData identifies a particular calender day, expressed by year, calender month and day in month. It is expressed by a string value following a particular lexical representation. The lexical representation for IfcDate is the YYYY-MM-DD, where YYYY represents the calendar year, MM the ordinal number of the calendar month, and DD the ordinal number of the day within the calendar month. No left truncation is allowed. An optional following time zone qualifier is allowed. To accommodate year values outside the range from 0001 to 9999, additional digits can be added to the left of this representation and a preceding \"-\" sign is allowed.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifcdate.htm"
},
"IfcDateTime": {
"description": "The IfcDataTime identifies a particular point in time, expressed by hours, minutes and optional seconds elapsed within a calender day, expressed by year, calender month and day in month. It is expressed by a string value following a particular lexical representation.",
"description": "The IfcDataTime identifies a particular point in time, expressed by hours, minutes and optional seconds elapsed within a calender day, expressed by year, calender month and day in month. It is expressed by a string value following a particular lexical representation. This lexical representation for IfcDataTime is YYYY-MM-DDThh:mm:ss where \"YYYY\" represent the year, \"MM\" the month and \"DD\" the day, preceded by an optional leading \"-\" sign to indicate a negative year number. If the sign is omitted, \"+\" is assumed. The letter \"T\" is the date/time separator and \"hh\", \"mm\", \"ss\" represent hour, minute and second respectively. Additional digits can be used to increase the precision of fractional seconds if desired i.e the format ss.ss... with any number of digits after the decimal point is supported. The fractional seconds part is optional; other parts of the lexical form are not optional. To accommodate year values greater than 9999 additional digits can be added to the left of this representation. Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden. The year 0000 is prohibited.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifcdatetime.htm"
},
"IfcDayInMonthNumber": {
"description": "IfcDayInMonthNumber is an integer that defines the position of the specified day in a month.",
"description": "IfcDayInMonthNumber is an integer that defines the position of the specified day in a month. Type: INTEGER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifcdayinmonthnumber.htm"
},
"IfcDayInWeekNumber": {
"description": "The IfcDayInWeekNumber is an integer that defines the position of the specified day in a week. The positions have the following meaning that assigns the ordinal day number in the week to the Calendar day name.",
"description": "The IfcDayInWeekNumber is an integer that defines the position of the specified day in a week. The positions have the following meaning that assigns the ordinal day number in the week to the Calendar day name. Ordinal day numbers map to calendar days as follows: - 1: Monday - 2: Tuesday - 3: Wednesday - 4: Thursday - 5: Friday - 6: Saturday - 7: Sunday",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifcdayinweeknumber.htm"
},
"IfcDefinitionSelect": {
@@ -356,7 +356,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifckernel/lexical/ifcdefinitionselect.htm"
},
"IfcDerivedMeasureValue": {
"description": "IfcDerivedMeasureValue is a select type for selecting between derived measure types.",
"description": "IfcDerivedMeasureValue is a select type for selecting between derived measure types. SELECT - IfcAbsorbedDoseMeasure - IfcAccelerationMeasure - IfcAngularVelocityMeasure - IfcAreaDensityMeasure - IfcCompoundPlaneAngleMeasure - IfcCurvatureMeasure - IfcDoseEquivalentMeasure - IfcDynamicViscosityMeasure - IfcElectricCapacitanceMeasure - IfcElectricChargeMeasure - IfcElectricConductanceMeasure - IfcElectricResistanceMeasure - IfcElectricVoltageMeasure - IfcEnergyMeasure - IfcForceMeasure - IfcFrequencyMeasure - IfcHeatFluxDensityMeasure - IfcHeatingValueMeasure - IfcIlluminanceMeasure - IfcInductanceMeasure - IfcIntegerCountRateMeasure - IfcIonConcentrationMeasure - IfcIsothermalMoistureCapacityMeasure - IfcKinematicViscosityMeasure - IfcLinearForceMeasure - IfcLinearMomentMeasure - IfcLinearStiffnessMeasure - IfcLinearVelocityMeasure - IfcLuminousFluxMeasure - IfcLuminousIntensityDistributionMeasure - IfcMagneticFluxDensityMeasure - IfcMagneticFluxMeasure - IfcMassDensityMeasure - IfcMassFlowRateMeasure - IfcMassPerLengthMeasure - IfcModulusOfElasticityMeasure - IfcModulusOfLinearSubgradeReactionMeasure - IfcModulusOfRotationalSubgradeReactionMeasure - IfcModulusOfSubgradeReactionMeasure - IfcMoistureDiffusivityMeasure - IfcMolecularWeightMeasure - IfcMomentOfInertiaMeasure - IfcMonetaryMeasure - IfcPHMeasure - IfcPlanarForceMeasure - IfcPowerMeasure - IfcPressureMeasure - IfcRadioActivityMeasure - IfcRotationalFrequencyMeasure - IfcRotationalMassMeasure - IfcRotationalStiffnessMeasure - IfcSectionModulusMeasure - IfcSectionalAreaIntegralMeasure - IfcShearModulusMeasure - IfcSoundPowerLevelMeasure - IfcSoundPowerMeasure - IfcSoundPressureLevelMeasure - IfcSoundPressureMeasure - IfcSpecificHeatCapacityMeasure - IfcTemperatureGradientMeasure - IfcTemperatureRateOfChangeMeasure - IfcThermalAdmittanceMeasure - IfcThermalConductivityMeasure - IfcThermalExpansionCoefficientMeasure - IfcThermalResistanceMeasure - IfcThermalTransmittanceMeasure - IfcTorqueMeasure - IfcVaporPermeabilityMeasure - IfcVolumetricFlowRateMeasure - IfcWarpingConstantMeasure - IfcWarpingMomentMeasure",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcderivedmeasurevalue.htm"
},
"IfcDerivedUnitEnum": {
@@ -364,7 +364,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcderivedunitenum.htm"
},
"IfcDescriptiveMeasure": {
"description": "A descriptive measure is a human interpretable definition of a quantifiable value. The mode of interpretation has to be established for the exchange context.",
"description": "A descriptive measure is a human interpretable definition of a quantifiable value. The mode of interpretation has to be established for the exchange context. Type: STRING",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcdescriptivemeasure.htm"
},
"IfcDimensionCount": {
@@ -388,7 +388,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgserviceelements/lexical/ifcdistributionporttypeenum.htm"
},
"IfcDistributionSystemEnum": {
"description": "This enumeration identifies different types of distribution systems. It is used to designate systems by their function as well as ports of devices within such systems to restrict connectivity to compatible connections.",
"description": "This enumeration identifies different types of distribution systems. It is used to designate systems by their function as well as ports of devices within such systems to restrict connectivity to compatible connections. Ports for cable carriers may be connected using IfcCableCarrierSegment and IfcCableCarrierFitting. Type objects for cable carrier segments and fittings (IfcCableCarrierSegmentType and IfcCableCarrierFittingType that are not specific to a particular system type may have ports with PredefinedType of NOTDEFINED which indicates that occurrences of such objects may connect to ports of any other cable-carrier based port. Valid enumerations for cable carriers are the same as that for cables, and may be asserted if ports of the contained cables are all of the same type.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgserviceelements/lexical/ifcdistributionsystemenum.htm"
},
"IfcDocumentConfidentialityEnum": {
@@ -404,11 +404,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcexternalreferenceresource/lexical/ifcdocumentstatusenum.htm"
},
"IfcDoorPanelOperationEnum": {
"description": "This enumeration defines the basic ways how individual door panels operate as shown in Figure 1.",
"description": "This enumeration defines the basic ways how individual door panels operate as shown in Figure 1. The opening direction of the door panels is given by the local placement of the IfcDoor. The positive y-axis determines the direction as shown in Figure 2.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcdoorpaneloperationenum.htm"
},
"IfcDoorPanelPositionEnum": {
"description": "This enumeration defines the basic ways to describe the location of a door panel within a door lining.",
"description": "This enumeration defines the basic ways to describe the location of a door panel within a door lining. Figure 1 shows the designation of a door panel with PanelPosition = LEFT and a door panel with PanelPosition = RIGHT within a door style with OperationType = DOUBLE_DOOR_SINGLE_SWING. The position is given as shown in the XZ plane of the local placement, looking into the direction of the positive Y axis. !(../../../../../../figures/ifcdoorpanelpositionenum-fig01.gif \"Figure 1 \u2014 Door panel positions\")",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcdoorpanelpositionenum.htm"
},
"IfcDoorStyleConstructionEnum": {
@@ -416,7 +416,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcdoorstyleconstructionenum.htm"
},
"IfcDoorStyleOperationEnum": {
"description": "This enumeration defines the basic ways to describe how doors operate as shown in Figure 1.",
"description": "This enumeration defines the basic ways to describe how doors operate as shown in Figure 1. NOTE - Figures are shown in the ground view. - Figures (symbolic representation) depend on the national building code. - These figures are only shown as illustrations, the actual representation in the ground view might differ. - Open to the outside is declared as open into the direction of the positive y-axis, determined by the ObjectPlacement at IfcDoor - The location of the panel relative to the wall thickness is defined by the ObjectPlacement at IfcDoor, and the IfcDoorLiningProperties.LiningOffset parameter.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcdoorstyleoperationenum.htm"
},
"IfcDoorTypeEnum": {
@@ -424,15 +424,15 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcdoortypeenum.htm"
},
"IfcDoorTypeOperationEnum": {
"description": "This enumeration defines the basic ways to describe how doors operate, as shown in Figure 1. It combines the partitioning of the door into a single or multiple door panels and the operation types of that panels.",
"description": "This enumeration defines the basic ways to describe how doors operate, as shown in Figure 1. It combines the partitioning of the door into a single or multiple door panels and the operation types of that panels. In the most common case of swinging doors the IfcDoorTypeOperationEnum defined the hinge side (left hing or right hung) and the opening direction (opening to the left, opening to the right). Whether the door opens inwards or outwards is determined by the local coordinate system of the IfcDoor, or IfcDoorStandardCase. NOTE - Figures are shown in the ground view. - Figures (symbolic representation) depend on the national building code. - These figures are only shown as illustrations, the actual representation in the ground view might differ. - Open to the outside is declared as open into the direction of the positive y-axis, determined by the ObjectPlacement at IfcDoor - The location of the panel relative to the wall thickness is defined by the ObjectPlacement at IfcDoor, and the IfcDoorLiningProperties.LiningOffset parameter.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcdoortypeoperationenum.htm"
},
"IfcDoseEquivalentMeasure": {
"description": "IfcDoseEquivalentMeasure is a measure of the radioactive dose equivalent.",
"description": "IfcDoseEquivalentMeasure is a measure of the radioactive dose equivalent. Usually measured in Sievert (Sv, J/kg). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcdoseequivalentmeasure.htm"
},
"IfcDuctFittingTypeEnum": {
"description": "This enumeration is used to identify the primary purpose of a duct fitting. This is a very basic categorization mechanism to generically identify the duct fitting type. Subcategories of duct fittings are not enumerated.",
"description": "This enumeration is used to identify the primary purpose of a duct fitting. This is a very basic categorization mechanism to generically identify the duct fitting type. Subcategories of duct fittings are not enumerated. Enumerated Item Definitions: - BEND: A fitting with typically two ports used to change the direction of flow between connected elements. - CONNECTOR: Connector fitting, typically used to join two ports together within a flow distribution system (e.g., a coupling used to join two duct segments). - ENTRY: Entry fitting, typically unconnected at one port and connected to a flow distribution system at the other (e.g., an outside air duct system intake opening). - EXIT: Exit fitting, typically unconnected at one port and connected to a flow distribution system at the other (e.g., an exhaust air discharge opening). - JUNCTION: A fitting with typically more than two ports used to redistribute flow among the ports and/or to change the direction of flow between connected elements (e.g, tee, cross, wye, etc.). - OBSTRUCTION: A fitting with typically two ports used to obstruct or restrict flow between the connected elements (e.g., screen, perforated plate, etc.). - TRANSITION: A fitting with typically two ports having different shapes or sizes. Can also be used to change the direction of flow between connected elements. - USERDEFINED: User-defined fitting. - NOTDEFINED: Undefined fitting.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcductfittingtypeenum.htm"
},
"IfcDuctSegmentTypeEnum": {
@@ -444,11 +444,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcductsilencertypeenum.htm"
},
"IfcDuration": {
"description": "The IfcDuration identifies a quantity of time (or a \"lenght\" of an event occurring in time).",
"description": "The IfcDuration identifies a quantity of time (or a \"lenght\" of an event occurring in time). This lexical representation for IfcDataTime is PnYnMnDTnHnMnS, where nY represents the number of years, nM the number of months, nD the number of days, 'T' is the date/time separator, nH the number of hours, nM the number of minutes and nS the number of seconds. The number of seconds can include decimal digits to arbitrary precision.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifcduration.htm"
},
"IfcDynamicViscosityMeasure": {
"description": "IfcDynamicViscosityMeasure is a measure of the viscous resistance of a medium.",
"description": "IfcDynamicViscosityMeasure is a measure of the viscous resistance of a medium. Usually measured in Pascal second (Pa s). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcdynamicviscositymeasure.htm"
},
"IfcElectricApplianceTypeEnum": {
@@ -456,19 +456,19 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifcelectricappliancetypeenum.htm"
},
"IfcElectricCapacitanceMeasure": {
"description": "IfcElectricCapacitanceMeasure is a measure of the electric capacitance.",
"description": "IfcElectricCapacitanceMeasure is a measure of the electric capacitance. Usually measured in Farad (F, C/V = A s/V). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcelectriccapacitancemeasure.htm"
},
"IfcElectricChargeMeasure": {
"description": "IfcElectricChargeMeasure is a measure of the electric charge.",
"description": "IfcElectricChargeMeasure is a measure of the electric charge. Usually measured in Coulomb (C, A s). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcelectricchargemeasure.htm"
},
"IfcElectricConductanceMeasure": {
"description": "IfcElectricConductanceMeasure is a measure of the electric conductance.",
"description": "IfcElectricConductanceMeasure is a measure of the electric conductance. Usually measured in Siemens (S, 1/Ohm = A/V). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcelectricconductancemeasure.htm"
},
"IfcElectricCurrentMeasure": {
"description": "The value for the movement of electrically charged particles.",
"description": "The value for the movement of electrically charged particles. Usually measured in Ampere (A). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcelectriccurrentmeasure.htm"
},
"IfcElectricDistributionBoardTypeEnum": {
@@ -488,7 +488,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifcelectricmotortypeenum.htm"
},
"IfcElectricResistanceMeasure": {
"description": "IfcElectricResistanceMeasure is a measure of the electric resistance.",
"description": "IfcElectricResistanceMeasure is a measure of the electric resistance. Usually measured in Ohm (V/A). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcelectricresistancemeasure.htm"
},
"IfcElectricTimeControlTypeEnum": {
@@ -496,7 +496,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifcelectrictimecontroltypeenum.htm"
},
"IfcElectricVoltageMeasure": {
"description": "IfcElectricVoltageMeasure is a measure of electromotive force.",
"description": "IfcElectricVoltageMeasure is a measure of electromotive force. Usually measured in Volts (V, W/A). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcelectricvoltagemeasure.htm"
},
"IfcElementAssemblyTypeEnum": {
@@ -508,7 +508,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcproductextension/lexical/ifcelementcompositionenum.htm"
},
"IfcEnergyMeasure": {
"description": "IfcEnergyMeasure is a measure of energy required or used.",
"description": "IfcEnergyMeasure is a measure of energy required or used. Usually measured in Joules, (J, Nm). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcenergymeasure.htm"
},
"IfcEngineTypeEnum": {
@@ -556,7 +556,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcplumbingfireprotectiondomain/lexical/ifcfiresuppressionterminaltypeenum.htm"
},
"IfcFlowDirectionEnum": {
"description": "This enumeration defines the flow direction at a distribution port.",
"description": "This enumeration defines the flow direction at a distribution port. - For pipe-based ports, the direction is the physical flow direction. - For duct-based ports, the direction is the physical flow direction.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgserviceelements/lexical/ifcflowdirectionenum.htm"
},
"IfcFlowInstrumentTypeEnum": {
@@ -568,15 +568,15 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcflowmetertypeenum.htm"
},
"IfcFontStyle": {
"description": "The IfcFontStyle type defines whether the normal, the italic or the oblique faces within a font family shall be used. Values are:",
"description": "The IfcFontStyle type defines whether the normal, the italic or the oblique faces within a font family shall be used. Values are: - normal - italic - oblique",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcfontstyle.htm"
},
"IfcFontVariant": {
"description": "The IfcFontVariant type defines whether the normal or the small-caps faces within a font family shall be used. Values are:",
"description": "The IfcFontVariant type defines whether the normal or the small-caps faces within a font family shall be used. Values are: - normal - small-caps",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcfontvariant.htm"
},
"IfcFontWeight": {
"description": "The IfcFontWeight type defines the weight of the font. Values are:",
"description": "The IfcFontWeight type defines the weight of the font. Values are: - normal - bold - 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcfontweight.htm"
},
"IfcFootingTypeEnum": {
@@ -584,11 +584,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralelementsdomain/lexical/ifcfootingtypeenum.htm"
},
"IfcForceMeasure": {
"description": "IfcForceMeasure is a measure of the force.",
"description": "IfcForceMeasure is a measure of the force. Usually measured in Newton (N, kg m/s2). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcforcemeasure.htm"
},
"IfcFrequencyMeasure": {
"description": "IfcFrequencyMeasure is a measure of the number of times that an item vibrates in unit time.",
"description": "IfcFrequencyMeasure is a measure of the number of times that an item vibrates in unit time. Usually measured in cycles/s or Hertz (Hz). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcfrequencymeasure.htm"
},
"IfcFurnitureTypeEnum": {
@@ -612,7 +612,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcrepresentationresource/lexical/ifcglobalorlocalenum.htm"
},
"IfcGloballyUniqueId": {
"description": "An IfcGloballyUniqueId holds an encoded string identifier that is used to uniquely identify an IFC object. An IfcGloballyUniqueId is a Globally Unique Identifier (GUID) which is an auto-generated 128-bit number. Since this identifier is required for all IFC object instances, it is desirable to compress it to reduce overhead. The encoding of the base 64 character set is shown below:",
"description": "An IfcGloballyUniqueId holds an encoded string identifier that is used to uniquely identify an IFC object. An IfcGloballyUniqueId is a Globally Unique Identifier (GUID) which is an auto-generated 128-bit number. Since this identifier is required for all IFC object instances, it is desirable to compress it to reduce overhead. The encoding of the base 64 character set is shown below: The resulting string is a fixed 22 character length string to be exchanged within the IFC exchange file structure.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcutilityresource/lexical/ifcgloballyuniqueid.htm"
},
"IfcGridPlacementDirectionSelect": {
@@ -624,7 +624,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcproductextension/lexical/ifcgridtypeenum.htm"
},
"IfcHatchLineDistanceSelect": {
"description": "The IfcHatchLineDistanceSelect is a selection between different ways to determine the distance and optionally the start point of hatch lines, either by an offset distance measure or by a vector.",
"description": "The IfcHatchLineDistanceSelect is a selection between different ways to determine the distance and optionally the start point of hatch lines, either by an offset distance measure or by a vector. The vector, if selected, acts as a one time repeat factor in the fill area style hatching for determining the origin of the repeated hatch line relative to the origin of the previous hatch line, Given the initial position of any hatch line, the one direction repeat factor determines two new positions according to the equation:",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifchatchlinedistanceselect.htm"
},
"IfcHeatExchangerTypeEnum": {
@@ -632,7 +632,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcheatexchangertypeenum.htm"
},
"IfcHeatFluxDensityMeasure": {
"description": "IfcHeatFluxDensityMeasure is a measure of the density of heat flux within a body.",
"description": "IfcHeatFluxDensityMeasure is a measure of the density of heat flux within a body. Usually measured in W/m2 (J/s m2). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcheatfluxdensitymeasure.htm"
},
"IfcHeatingValueMeasure": {
@@ -644,23 +644,23 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifchumidifiertypeenum.htm"
},
"IfcIdentifier": {
"description": "An identifier is an alphanumeric string which allows an individual thing to be identified. It may not provide natural-language meaning.",
"description": "An identifier is an alphanumeric string which allows an individual thing to be identified. It may not provide natural-language meaning. Type: STRING of up to 255 characters Value restrictions As a merely machine-readable string for identification purposes, an identifier is usually machine-generated and locale-independent (in contrast to human-readable labels, IfcLabel).",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcidentifier.htm"
},
"IfcIlluminanceMeasure": {
"description": "IfcIlluminanceMeasure is a measure of the illuminance.",
"description": "IfcIlluminanceMeasure is a measure of the illuminance. Usually measured in Lux (lx, Lumen/m2 = Candela Steradian/m2). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcilluminancemeasure.htm"
},
"IfcInductanceMeasure": {
"description": "IfcInductanceMeasure is a measure of the inductance.",
"description": "IfcInductanceMeasure is a measure of the inductance. Usually measure in Henry (H, Weber/A = V s/A). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcinductancemeasure.htm"
},
"IfcInteger": {
"description": "IfcInteger is a defined type of simple data type Integer. It is required since a select type (IfcSimpleValue) cannot include directly simple types in its select list.",
"description": "IfcInteger is a defined type of simple data type Integer. It is required since a select type (IfcSimpleValue) cannot include directly simple types in its select list. In principle, the domain of IfcInteger (being an Integer) is all integer numbers. Here the number of bits used for the IfcInteger representation is unconstrained, but in practice it is implementation specific. Type: INTEGER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcinteger.htm"
},
"IfcIntegerCountRateMeasure": {
"description": "IfcIntegerCountRateMeasure is a measure of the integer number of units flowing per unit time.",
"description": "IfcIntegerCountRateMeasure is a measure of the integer number of units flowing per unit time. Type: INTEGER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcintegercountratemeasure.htm"
},
"IfcInterceptorTypeEnum": {
@@ -680,7 +680,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcionconcentrationmeasure.htm"
},
"IfcIsothermalMoistureCapacityMeasure": {
"description": "IfcIsothermalMoistureCapacityMeasure is a measure of isothermal moisture capacity.",
"description": "IfcIsothermalMoistureCapacityMeasure is a measure of isothermal moisture capacity. Usually measured in m3/kg. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcisothermalmoisturecapacitymeasure.htm"
},
"IfcJunctionBoxTypeEnum": {
@@ -688,7 +688,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifcjunctionboxtypeenum.htm"
},
"IfcKinematicViscosityMeasure": {
"description": "IfcKinematicViscosityMeasure is a measure of the viscous resistance of a medium to a moving body.",
"description": "IfcKinematicViscosityMeasure is a measure of the viscous resistance of a medium to a moving body. Usually measured in m2/s. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifckinematicviscositymeasure.htm"
},
"IfcKnotType": {
@@ -696,7 +696,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcgeometryresource/lexical/ifcknottype.htm"
},
"IfcLabel": {
"description": "A label is the term by which something may be referred to. It is a string which represents the human-interpretable name of something and shall have a natural-language meaning.",
"description": "A label is the term by which something may be referred to. It is a string which represents the human-interpretable name of something and shall have a natural-language meaning. Type: STRING of up to 255 characters Value restrictions As a human-readable string for naming purposes, a label is usually human-specified and locale-dependent (in contrast to purely machine-readable identifiers, IfcIdentifier).",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifclabel.htm"
},
"IfcLaborResourceTypeEnum": {
@@ -708,7 +708,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifclamptypeenum.htm"
},
"IfcLanguageId": {
"description": "The IfcLanguageId identifies the language in which a natural language text is expressed. It uses a language tag to identify the language.",
"description": "The IfcLanguageId identifies the language in which a natural language text is expressed. It uses a language tag to identify the language. Type: IfcIdentifier",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcexternalreferenceresource/lexical/ifclanguageid.htm"
},
"IfcLayerSetDirectionEnum": {
@@ -720,7 +720,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationorganizationresource/lexical/ifclayereditem.htm"
},
"IfcLengthMeasure": {
"description": "An IfcLengthMeasure is the value of a distance.",
"description": "An IfcLengthMeasure is the value of a distance. Usually measured in millimeters (mm). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifclengthmeasure.htm"
},
"IfcLibrarySelect": {
@@ -732,7 +732,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationorganizationresource/lexical/ifclightdistributioncurveenum.htm"
},
"IfcLightDistributionDataSourceSelect": {
"description": "A goniometric light gets its intensity distribution function (how much light goes in any one direction) from one of two sources: (i) an industry-standard file, (ii) from distribution data passed directly via the IfcLightIntensityDistribution.",
"description": "A goniometric light gets its intensity distribution function (how much light goes in any one direction) from one of two sources: (i) an industry-standard file, (ii) from distribution data passed directly via the IfcLightIntensityDistribution. The light distribution provides the luminous intensity distribution according to some standardized light distribution curves. SELECT",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationorganizationresource/lexical/ifclightdistributiondatasourceselect.htm"
},
"IfcLightEmissionSourceEnum": {
@@ -748,19 +748,19 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcgeometryresource/lexical/ifclineindex.htm"
},
"IfcLinearForceMeasure": {
"description": "IfcLinearForceMeasure is a measure of linear force.",
"description": "IfcLinearForceMeasure is a measure of linear force. Usually measured in N/m. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifclinearforcemeasure.htm"
},
"IfcLinearMomentMeasure": {
"description": "IfcLinearMomentMeasure is a measure of linear moment.",
"description": "IfcLinearMomentMeasure is a measure of linear moment. Usually measured in Nm/m. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifclinearmomentmeasure.htm"
},
"IfcLinearStiffnessMeasure": {
"description": "IfcLinearStiffnessMeasure is a measure of linear stiffness.",
"description": "IfcLinearStiffnessMeasure is a measure of linear stiffness. Usually measured in N/m. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifclinearstiffnessmeasure.htm"
},
"IfcLinearVelocityMeasure": {
"description": "IfcLinearVelocityMeasure is a measure of the velocity of a body measured in terms of distance moved per unit time.",
"description": "IfcLinearVelocityMeasure is a measure of the velocity of a body measured in terms of distance moved per unit time. Usually measured in m/s. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifclinearvelocitymeasure.htm"
},
"IfcLoadGroupTypeEnum": {
@@ -768,51 +768,51 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralanalysisdomain/lexical/ifcloadgrouptypeenum.htm"
},
"IfcLogical": {
"description": "IfcLogical_IfcSimpleValue) cannot directly include simple types in its select list). Logical datatype can have values TRUE, FALSE or UNKNOWN._",
"description": "IfcLogical_IfcSimpleValue) cannot directly include simple types in its select list). Logical datatype can have values TRUE, FALSE or UNKNOWN._ Type: LOGICAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifclogical.htm"
},
"IfcLogicalOperatorEnum": {
"description": "IfcLogicalOperatorEnum is an enumeration that defines the logical operators that may be applied for the satisfaction of one or more operands (IfcConstraint) at a time.",
"description": "IfcLogicalOperatorEnum is an enumeration that defines the logical operators that may be applied for the satisfaction of one or more operands (IfcConstraint) at a time. Table 1 illustrates application of IfcLogicalOperatorEnum in a case of three operands, A, B and C, for each operator.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcconstraintresource/lexical/ifclogicaloperatorenum.htm"
},
"IfcLuminousFluxMeasure": {
"description": "IfcLuminousFluxMeasure is a measure of the luminous flux.",
"description": "IfcLuminousFluxMeasure is a measure of the luminous flux. Usually measured in Lumen (lm, Candela Steradian). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcluminousfluxmeasure.htm"
},
"IfcLuminousIntensityDistributionMeasure": {
"description": "IfcLuminousIntensityDistributionMeasure is a measure of the luminous intensity of a light source that changes according to the direction of the ray. It is normally based on some standardized distribution light distribution curves.",
"description": "IfcLuminousIntensityDistributionMeasure is a measure of the luminous intensity of a light source that changes according to the direction of the ray. It is normally based on some standardized distribution light distribution curves. Usually measured in Candela/Lumen (cd/lm) or (cd/klm). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcluminousintensitydistributionmeasure.htm"
},
"IfcLuminousIntensityMeasure": {
"description": "An IfcLuminousIntensityMeasure is the value for the brightness of a body.",
"description": "An IfcLuminousIntensityMeasure is the value for the brightness of a body. Usually measured in candela (cd). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcluminousintensitymeasure.htm"
},
"IfcMagneticFluxDensityMeasure": {
"description": "IfcMagneticFluxDensityMeasure is a measure of the magnetic flux density.",
"description": "IfcMagneticFluxDensityMeasure is a measure of the magnetic flux density. Usually measured in Tesla (T, Weber/m2 = V s/m2). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmagneticfluxdensitymeasure.htm"
},
"IfcMagneticFluxMeasure": {
"description": "IfcMagneticFluxMeasure is a measure of the magnetic flux.",
"description": "IfcMagneticFluxMeasure is a measure of the magnetic flux. Usually measured in Weber (Wb, V s). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmagneticfluxmeasure.htm"
},
"IfcMassDensityMeasure": {
"description": "IfcMassDensityMeasure is a measure of the density of a medium.",
"description": "IfcMassDensityMeasure is a measure of the density of a medium. Usually measured in kg/m3. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmassdensitymeasure.htm"
},
"IfcMassFlowRateMeasure": {
"description": "IfcMassFlowRateMeasure is a measure of the mass of a medium flowing per unit time.",
"description": "IfcMassFlowRateMeasure is a measure of the mass of a medium flowing per unit time. Usually measured in kg/s. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmassflowratemeasure.htm"
},
"IfcMassMeasure": {
"description": "An IfcMassMeasure is the value of the amount of matter that a body contains.",
"description": "An IfcMassMeasure is the value of the amount of matter that a body contains. Usually measured in kilograms (kg) or grams (g). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmassmeasure.htm"
},
"IfcMassPerLengthMeasure": {
"description": "IfcMassPerLengthMeasure is a measure for mass per length. For example for rolled steel profiles the weight of an imaginary beam is usually expressed by kg/m length for cost calculation and structural analysis purposes.",
"description": "IfcMassPerLengthMeasure is a measure for mass per length. For example for rolled steel profiles the weight of an imaginary beam is usually expressed by kg/m length for cost calculation and structural analysis purposes. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmassperlengthmeasure.htm"
},
"IfcMaterialSelect": {
"description": "IfcMaterialSelect provides selection of either a material definition or a material usage definition that can be assigned to an element, a resource or another entity within this specification.",
"description": "IfcMaterialSelect provides selection of either a material definition or a material usage definition that can be assigned to an element, a resource or another entity within this specification. - IfcMaterialDefinition IfcMaterial IfcMaterialLayer IfcMaterialLayerSet IfcMaterialProfile IfcMaterialProfileSet IfcMaterialConstituent IfcMaterialConstituentSet - IfcMaterialUsageDefinition IfcMaterialLayerSetUsage IfcMaterialProfileSetUsage - IfcMaterialList",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmaterialresource/lexical/ifcmaterialselect.htm"
},
"IfcMeasureValue": {
@@ -832,19 +832,19 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcmembertypeenum.htm"
},
"IfcMetricValueSelect": {
"description": "IfcMetricValueSelect is a select type that enables selection of the data type for the value component of an IfcMetric.",
"description": "IfcMetricValueSelect is a select type that enables selection of the data type for the value component of an IfcMetric. Types are used as follows: - IfcValue: A constant value using project default units. - IfcMeasureWithUnit: A constant value using specified units. - IfcAppliedValue: A value calculated from a formula. - IfcTable: A value retrieved from a table. - IfcTimeSeries: A value that varies over time. - IfcReference: A value referenced on an object attribute.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcconstraintresource/lexical/ifcmetricvalueselect.htm"
},
"IfcModulusOfElasticityMeasure": {
"description": "IfcModulusOfElasticityMeasure is a measure of modulus of elasticity.",
"description": "IfcModulusOfElasticityMeasure is a measure of modulus of elasticity. Usually measured in N/m2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmodulusofelasticitymeasure.htm"
},
"IfcModulusOfLinearSubgradeReactionMeasure": {
"description": "IfcModulusOfLinearSubgradeReactionMeasure is a measure for modulus of linear subgrade reaction, which expresses the elastic bedding of a linear structural element per length, such as for a beam. It is typically measured in N/m\\^2.",
"description": "IfcModulusOfLinearSubgradeReactionMeasure is a measure for modulus of linear subgrade reaction, which expresses the elastic bedding of a linear structural element per length, such as for a beam. It is typically measured in N/m\\^2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmodulusoflinearsubgradereactionmeasure.htm"
},
"IfcModulusOfRotationalSubgradeReactionMeasure": {
"description": "IfcModulusOfRotationalSubgradeReactionMeasure is a measure for modulus of rotational subgrade reaction, which expresses the rotational elastic bedding of a linear structural element per length, such as for a beam. It is typically measured in Nm/(m*rad).",
"description": "IfcModulusOfRotationalSubgradeReactionMeasure is a measure for modulus of rotational subgrade reaction, which expresses the rotational elastic bedding of a linear structural element per length, such as for a beam. It is typically measured in Nm/(m*rad). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmodulusofrotationalsubgradereactionmeasure.htm"
},
"IfcModulusOfRotationalSubgradeReactionSelect": {
@@ -852,7 +852,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralloadresource/lexical/ifcmodulusofrotationalsubgradereactionselect.htm"
},
"IfcModulusOfSubgradeReactionMeasure": {
"description": "IfcModulusOfSubgradeReactionMeasure is a geotechnical measure describing interaction between foundation structures and the soil. May also be known as bedding measure.",
"description": "IfcModulusOfSubgradeReactionMeasure is a geotechnical measure describing interaction between foundation structures and the soil. May also be known as bedding measure. Usually measured in N/m3. Type: REAL Figure 1 illustrates elastic support of a planar member. !(../../../../../../figures/ifcmodulusofsubgradereactionmeasure.gif \"Figure 1 \u2014 Modulus of subgrade reaction measure\")",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmodulusofsubgradereactionmeasure.htm"
},
"IfcModulusOfSubgradeReactionSelect": {
@@ -864,23 +864,23 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralloadresource/lexical/ifcmodulusoftranslationalsubgradereactionselect.htm"
},
"IfcMoistureDiffusivityMeasure": {
"description": "IfcMoistureDiffusivityMeasure is a measure of moisture diffusivity.",
"description": "IfcMoistureDiffusivityMeasure is a measure of moisture diffusivity. Usually measured in m3/s. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmoisturediffusivitymeasure.htm"
},
"IfcMolecularWeightMeasure": {
"description": "IfcMolecularWeightMeasure is a measure of molecular weight of material (typically gas).",
"description": "IfcMolecularWeightMeasure is a measure of molecular weight of material (typically gas). Usually measured in g/mole. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmolecularweightmeasure.htm"
},
"IfcMomentOfInertiaMeasure": {
"description": "IfcMomentOfInertiaMeasure is a measure of moment of inertia.",
"description": "IfcMomentOfInertiaMeasure is a measure of moment of inertia. Usually measured in m4. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmomentofinertiameasure.htm"
},
"IfcMonetaryMeasure": {
"description": "A monetary measure is the value of an amount of money without regard to its currency.",
"description": "A monetary measure is the value of an amount of money without regard to its currency. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcmonetarymeasure.htm"
},
"IfcMonthInYearNumber": {
"description": "IfcMonthInYearNumber is an integer that defines the position of the specified month in a year.",
"description": "IfcMonthInYearNumber is an integer that defines the position of the specified month in a year. Calendar month numbers map to calendar month names as follows: - 1: January - 2: February - 3: March - 4: April - 5: May - 6: June - 7: July - 8: August - 9: September - 10: October - 11: November - 12: December Type: INTEGER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifcmonthinyearnumber.htm"
},
"IfcMotorConnectionTypeEnum": {
@@ -888,11 +888,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifcmotorconnectiontypeenum.htm"
},
"IfcNonNegativeLengthMeasure": {
"description": "A non-negative length measure is a length measure that is greater than or equal to zero.",
"description": "A non-negative length measure is a length measure that is greater than or equal to zero. Type: IfcLengthMeasure",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcnonnegativelengthmeasure.htm"
},
"IfcNormalisedRatioMeasure": {
"description": "IfcNormalisedRatioMeasure is a dimensionless measure to express ratio values ranging from 0.0 to 1.0.",
"description": "IfcNormalisedRatioMeasure is a dimensionless measure to express ratio values ranging from 0.0 to 1.0. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcnormalisedratiomeasure.htm"
},
"IfcNullStyle": {
@@ -900,7 +900,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcnullstyle.htm"
},
"IfcNumericMeasure": {
"description": "An IfcNumericMeasure is the numeric value of a physical quantity.",
"description": "An IfcNumericMeasure is the numeric value of a physical quantity. Type: NUMBER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcnumericmeasure.htm"
},
"IfcObjectReferenceSelect": {
@@ -932,11 +932,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcphmeasure.htm"
},
"IfcParameterValue": {
"description": "An IfcParameterValue is the value which specifies the amount of a parameter in some parameter space.",
"description": "An IfcParameterValue is the value which specifies the amount of a parameter in some parameter space. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcparametervalue.htm"
},
"IfcPerformanceHistoryTypeEnum": {
"description": "This enumeration is used to identify the primary purpose of performance history. The IfcPerformanceHistoryTypeEnum contains the following:",
"description": "This enumeration is used to identify the primary purpose of performance history. The IfcPerformanceHistoryTypeEnum contains the following: - USERDEFINED: User-defined. - NOTDEFINED: Undefined.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifccontrolextension/lexical/ifcperformancehistorytypeenum.htm"
},
"IfcPermeableCoveringOperationEnum": {
@@ -968,11 +968,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcpipesegmenttypeenum.htm"
},
"IfcPlanarForceMeasure": {
"description": "IfcPlanarForceMeasure is a measure of force on an area.",
"description": "IfcPlanarForceMeasure is a measure of force on an area. Usually measured in N/m2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcplanarforcemeasure.htm"
},
"IfcPlaneAngleMeasure": {
"description": "An IfcPlaneAngleMeasure is the value of an angle in a plane.",
"description": "An IfcPlaneAngleMeasure is the value of an angle in a plane. Usually measured in radian (rad, m/m = 1), but also grads may be used. The grad unit has to be declared as a conversion based unit based on radian unit. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcplaneanglemeasure.htm"
},
"IfcPlateTypeEnum": {
@@ -984,23 +984,23 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcgeometricconstraintresource/lexical/ifcpointorvertexpoint.htm"
},
"IfcPositiveInteger": {
"description": "IfcPositiveInteger is a defined type based on simple data type Integer with the additional restriction to positive integers (excluding zero).",
"description": "IfcPositiveInteger is a defined type based on simple data type Integer with the additional restriction to positive integers (excluding zero). In principle, the domain of IfcInteger is all integer numbers larger than zero. Here the number of bits used for the IfcInteger representation is unconstrained, but in practice it is implementation specific. Type: INTEGER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcpositiveinteger.htm"
},
"IfcPositiveLengthMeasure": {
"description": "An IfcPositiveLengthMeasure is a length measure that is greater than zero.",
"description": "An IfcPositiveLengthMeasure is a length measure that is greater than zero. Type: IfcLengthMeasure",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcpositivelengthmeasure.htm"
},
"IfcPositivePlaneAngleMeasure": {
"description": "An IfcPositivePlaneAngleMeasure is a plane angle measure that is greater than zero.",
"description": "An IfcPositivePlaneAngleMeasure is a plane angle measure that is greater than zero. Type: IfcPlaneAngleMeasure",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcpositiveplaneanglemeasure.htm"
},
"IfcPositiveRatioMeasure": {
"description": "An IfcPositiveRatioMeasure is a ratio measure that is greater than zero.",
"description": "An IfcPositiveRatioMeasure is a ratio measure that is greater than zero. Type: IfcRatioMeasure",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcpositiveratiomeasure.htm"
},
"IfcPowerMeasure": {
"description": "IfcPowerMeasure is a measure of power required or used.",
"description": "IfcPowerMeasure is a measure of power required or used. Usually measured in Watts (W, J/s). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcpowermeasure.htm"
},
"IfcPreferredSurfaceCurveRepresentation": {
@@ -1016,7 +1016,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcpresentationstyleselect.htm"
},
"IfcPressureMeasure": {
"description": "IfcPressureMeasure is a measure of the quantity of a medium acting on a unit area.",
"description": "IfcPressureMeasure is a measure of the quantity of a medium acting on a unit area. Usually measured in Pascals (Pa, N/m2). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcpressuremeasure.htm"
},
"IfcProcedureTypeEnum": {
@@ -1044,7 +1044,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedmgmtelements/lexical/ifcprojectordertypeenum.htm"
},
"IfcProjectedOrTrueLengthEnum": {
"description": "This enumeration type is needed for load definition and is only considered if the load values are given as global actions and if they define linear or planar loads (that is, one- or two-dimensionally distributed loads).",
"description": "This enumeration type is needed for load definition and is only considered if the load values are given as global actions and if they define linear or planar loads (that is, one- or two-dimensionally distributed loads). Figure 1 illustrates the interpretation of a load definition depending on the enumeration types IfcGlobalOrLocalEnum and IfcProjectedOrTrueLengthEnum.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralanalysisdomain/lexical/ifcprojectedortruelengthenum.htm"
},
"IfcProjectionElementTypeEnum": {
@@ -1076,7 +1076,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcpumptypeenum.htm"
},
"IfcRadioActivityMeasure": {
"description": "IfcRadioActivityMeasure is a measure of activity of radionuclide.",
"description": "IfcRadioActivityMeasure is a measure of activity of radionuclide. Usually measured in Becquerel (Bq, 1/s). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcradioactivitymeasure.htm"
},
"IfcRailingTypeEnum": {
@@ -1088,15 +1088,15 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcrampflighttypeenum.htm"
},
"IfcRampTypeEnum": {
"description": "This enumeration defines the basic configuration of the ramp type in terms of the number and shape of ramp flights, as shown in Figure 1. The type also distinguished turns by landings. In addition the subdivision of the straight and changing direction ramps is included. The ramp configurations are given for ramps without and with one and two landings.",
"description": "This enumeration defines the basic configuration of the ramp type in terms of the number and shape of ramp flights, as shown in Figure 1. The type also distinguished turns by landings. In addition the subdivision of the straight and changing direction ramps is included. The ramp configurations are given for ramps without and with one and two landings. Ramps which are subdivided into more than two landings, or ramps with non-regular shapes are to be defined with type being USERDEFINED or NOTDEFINED.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcramptypeenum.htm"
},
"IfcRatioMeasure": {
"description": "An IfcRatioMeasure is the value of the relation between two physical quantities that are of the same kind.",
"description": "An IfcRatioMeasure is the value of the relation between two physical quantities that are of the same kind. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcratiomeasure.htm"
},
"IfcReal": {
"description": "IfcReal is a defined type of simple data type REAL. It is required since a select type (IfcSimpleValue), cannot directly include simple types in its select list.",
"description": "IfcReal is a defined type of simple data type REAL. It is required since a select type (IfcSimpleValue), cannot directly include simple types in its select list. In principle, the domain of IfcReal (being a Real) is all rational, irrational and scientific real numbers. Here the precision is unconstrained, but in practice it is implementation specific. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcreal.htm"
},
"IfcRecurrenceTypeEnum": {
@@ -1136,19 +1136,19 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcactorresource/lexical/ifcroleenum.htm"
},
"IfcRoofTypeEnum": {
"description": "This enumeration defines the basic configuration of the roof in terms of the different roof shapes, as illustrated in Figure 1.",
"description": "This enumeration defines the basic configuration of the roof in terms of the different roof shapes, as illustrated in Figure 1. Roofs which are subdivided into more than these basic shapes or roofs with non-regular shapes (free form roofs) have the type FREEFORM.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcrooftypeenum.htm"
},
"IfcRotationalFrequencyMeasure": {
"description": "IfcRotationalFrequencyMeasure is a measure of the number of cycles that an item revolves in unit time.",
"description": "IfcRotationalFrequencyMeasure is a measure of the number of cycles that an item revolves in unit time. Usually measured in cycles/s. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcrotationalfrequencymeasure.htm"
},
"IfcRotationalMassMeasure": {
"description": "The rotational mass measure denotes the inertia of a body with respect to angular acceleration.",
"description": "The rotational mass measure denotes the inertia of a body with respect to angular acceleration. It is usually measured in kg*m\\^2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcrotationalmassmeasure.htm"
},
"IfcRotationalStiffnessMeasure": {
"description": "IfcRotationalStiffnessMeasure is a measure of rotational stiffness.",
"description": "IfcRotationalStiffnessMeasure is a measure of rotational stiffness. Usually measured in Nm/rad. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcrotationalstiffnessmeasure.htm"
},
"IfcRotationalStiffnessSelect": {
@@ -1168,7 +1168,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcplumbingfireprotectiondomain/lexical/ifcsanitaryterminaltypeenum.htm"
},
"IfcSectionModulusMeasure": {
"description": "IfcSectionModulusMeasure is a measure for the resistance of a cross section against bending or torsional moment. It is usually measured in m\\^3.",
"description": "IfcSectionModulusMeasure is a measure for the resistance of a cross section against bending or torsional moment. It is usually measured in m\\^3. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsectionmodulusmeasure.htm"
},
"IfcSectionTypeEnum": {
@@ -1176,7 +1176,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcprofileresource/lexical/ifcsectiontypeenum.htm"
},
"IfcSectionalAreaIntegralMeasure": {
"description": "The sectional area integral measure is typically used in torsional analysis. It is usually measured in m\\^5.",
"description": "The sectional area integral measure is typically used in torsional analysis. It is usually measured in m\\^5. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsectionalareaintegralmeasure.htm"
},
"IfcSegmentIndexSelect": {
@@ -1196,7 +1196,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcshadingdevicetypeenum.htm"
},
"IfcShearModulusMeasure": {
"description": "IfcShearModulusMeasure is a measure of shear modulus.",
"description": "IfcShearModulusMeasure is a measure of shear modulus. Usually measured in N/m2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcshearmodulusmeasure.htm"
},
"IfcShell": {
@@ -1208,7 +1208,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifckernel/lexical/ifcsimplepropertytemplatetypeenum.htm"
},
"IfcSimpleValue": {
"description": "IfcSimpleValue is a select type for selecting between simple value types.",
"description": "IfcSimpleValue is a select type for selecting between simple value types. SELECT - IfcInteger: Defined type of simple type INTEGER. - IfcReal: Defined type of simple type REAL. - IfcBoolean: Defined type of simple type BOOLEAN. - IfcLogical: Defined type of simple type LOGICAL. - IfcIdentifier: Defined type of simple type STRING for identification purposes. - IfcLabel: Defined type of simple type STRING for naming purposes. - IfcText: Defined type of simple type STRING for descriptive purposes. - IfcDateTime: Defined type of simple type STRING to represent a date and time. - IfcDate: Defined type of simple type STRING to represent a date. - IfcTime: Defined type of simple type STRING to represent a time. - IfcDuration: Defined type of simple type STRING to represent a duration. - IfcTimeStamp: Defined type of simple type INTEGER to represent a point in time by seconds elapsed since 1970.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsimplevalue.htm"
},
"IfcSizeSelect": {
@@ -1224,7 +1224,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifcsolardevicetypeenum.htm"
},
"IfcSolidAngleMeasure": {
"description": "An IfcSolidAngleMeasure is the value of an angle in a solid.",
"description": "An IfcSolidAngleMeasure is the value of an angle in a solid. Usually measured in Steradians, (sr, m2/m2). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsolidanglemeasure.htm"
},
"IfcSolidOrShell": {
@@ -1232,23 +1232,23 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcgeometricconstraintresource/lexical/ifcsolidorshell.htm"
},
"IfcSoundPowerLevelMeasure": {
"description": "A sound power level measure is a measure of total radiated noise with units of decibels with a reference value of picowatts.",
"description": "A sound power level measure is a measure of total radiated noise with units of decibels with a reference value of picowatts. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsoundpowerlevelmeasure.htm"
},
"IfcSoundPowerMeasure": {
"description": "A sound power measure is a measure of total radiated noise with units of watts (sonic energy per time unit).",
"description": "A sound power measure is a measure of total radiated noise with units of watts (sonic energy per time unit). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsoundpowermeasure.htm"
},
"IfcSoundPressureLevelMeasure": {
"description": "A sound pressure level measure is a measure of the pressure fluctuations superimposed over the ambient pressure level with units of decibels with a reference value of micropascals.",
"description": "A sound pressure level measure is a measure of the pressure fluctuations superimposed over the ambient pressure level with units of decibels with a reference value of micropascals. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsoundpressurelevelmeasure.htm"
},
"IfcSoundPressureMeasure": {
"description": "A sound pressure measure is a measure of the pressure fluctuations superimposed over the ambient pressure level with units of pascals.",
"description": "A sound pressure measure is a measure of the pressure fluctuations superimposed over the ambient pressure level with units of pascals. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcsoundpressuremeasure.htm"
},
"IfcSpaceBoundarySelect": {
"description": "The IfcSpaceBoundarySelect selects either an internal space for internal or external space boundaries, or an external spatial element for external space boundaries at the outer envelop of the building.",
"description": "The IfcSpaceBoundarySelect selects either an internal space for internal or external space boundaries, or an external spatial element for external space boundaries at the outer envelop of the building. SELECT - IfcSpace, - IfcExternalSpatialElement",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcproductextension/lexical/ifcspaceboundaryselect.htm"
},
"IfcSpaceHeaterTypeEnum": {
@@ -1264,11 +1264,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcproductextension/lexical/ifcspatialzonetypeenum.htm"
},
"IfcSpecificHeatCapacityMeasure": {
"description": "IfcSpecificHeatCapacityMeasure defines the specific heat of material: The heat energy absorbed per temperature unit.",
"description": "IfcSpecificHeatCapacityMeasure defines the specific heat of material: The heat energy absorbed per temperature unit. Usually measured in J / kg Kelvin. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcspecificheatcapacitymeasure.htm"
},
"IfcSpecularExponent": {
"description": "The IfcSpecularExponent defines the datatype for exponent determining the sharpness of the 'reflection'. The reflection is made sharper with large values of the exponent, such as 10.0. Small values, such as 1.0, decrease the specular fall-off.",
"description": "The IfcSpecularExponent defines the datatype for exponent determining the sharpness of the 'reflection'. The reflection is made sharper with large values of the exponent, such as 10.0. Small values, such as 1.0, decrease the specular fall-off. IfcSpecularExponent is of type REAL.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcspecularexponent.htm"
},
"IfcSpecularHighlightSelect": {
@@ -1276,7 +1276,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcspecularhighlightselect.htm"
},
"IfcSpecularRoughness": {
"description": "The IfcSpecularRoughness defines the datatype for the reflection resulting from the roughness of a surface through the height of surface impurities where the specular highlight is made sharper with small values for the roughness, such as 0.1. Applies to \"glass\", \"metal\", \"mirror\" and \"plastic\" reflection models. Larger values, close to 1.0 decrease the specular fall-off.",
"description": "The IfcSpecularRoughness defines the datatype for the reflection resulting from the roughness of a surface through the height of surface impurities where the specular highlight is made sharper with small values for the roughness, such as 0.1. Applies to \"glass\", \"metal\", \"mirror\" and \"plastic\" reflection models. Larger values, close to 1.0 decrease the specular fall-off. IfcSpecularRoughness is of type REAL. It is constraint to values between (and including) 0 and 1.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcspecularroughness.htm"
},
"IfcStackTerminalTypeEnum": {
@@ -1288,11 +1288,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcstairflighttypeenum.htm"
},
"IfcStairTypeEnum": {
"description": "This enumeration defines the basic configuration of the stair type in terms of the number of stair flights and the number of landings, as illustrated in Figure 1. The type also distinguished turns by windings or by landings. In addition the subdivision of the straight and changing direction stairs is included. The stair configurations are given for stairs without and with one, two or three landings.",
"description": "This enumeration defines the basic configuration of the stair type in terms of the number of stair flights and the number of landings, as illustrated in Figure 1. The type also distinguished turns by windings or by landings. In addition the subdivision of the straight and changing direction stairs is included. The stair configurations are given for stairs without and with one, two or three landings. Stairs which are subdivided into more than three landings, or stairs with non-regular shapes are to be defined with type being USERDEFINED or NOTDEFINED.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcstairtypeenum.htm"
},
"IfcStateEnum": {
"description": "The IfcStateEnum enumeration identifies the state or accessibility of the object (for example, read/write, locked).",
"description": "The IfcStateEnum enumeration identifies the state or accessibility of the object (for example, read/write, locked). Valid enumerations are:",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcutilityresource/lexical/ifcstateenum.htm"
},
"IfcStructuralActivityAssignmentSelect": {
@@ -1316,7 +1316,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralanalysisdomain/lexical/ifcstructuralsurfacemembertypeenum.htm"
},
"IfcStyleAssignmentSelect": {
"description": "The style assignment select is a selection of two wasy of assigning presentation styles to an IfcStyledItem.",
"description": "The style assignment select is a selection of two wasy of assigning presentation styles to an IfcStyledItem. - by directly assigning presentation styles as subtypes of IfcPresentationStyle - by assigning presentation stypes via an intermediate collection entity IfcPresentationStyleAssignment",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifcstyleassignmentselect.htm"
},
"IfcSubContractResourceTypeEnum": {
@@ -1360,11 +1360,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcprocessextension/lexical/ifctasktypeenum.htm"
},
"IfcTemperatureGradientMeasure": {
"description": "The temperature gradient measures the difference of a temperature per length, as for instance used in an external wall or its layers. It is usually measured in K/m.",
"description": "The temperature gradient measures the difference of a temperature per length, as for instance used in an external wall or its layers. It is usually measured in K/m. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifctemperaturegradientmeasure.htm"
},
"IfcTemperatureRateOfChangeMeasure": {
"description": "The temperature rate of change measures the difference of a temperature per time (positive: rise, negative: fall), as for instance used with heat sensors. It is for example measured in K/s (Kelvin per second).",
"description": "The temperature rate of change measures the difference of a temperature per time (positive: rise, negative: fall), as for instance used with heat sensors. It is for example measured in K/s (Kelvin per second). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifctemperaturerateofchangemeasure.htm"
},
"IfcTendonAnchorTypeEnum": {
@@ -1376,15 +1376,15 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralelementsdomain/lexical/ifctendontypeenum.htm"
},
"IfcText": {
"description": "An IfcText is an alphanumeric string of characters which is intended to be read and understood by a human being. It is for information purposes only.",
"description": "An IfcText is an alphanumeric string of characters which is intended to be read and understood by a human being. It is for information purposes only. Type: STRING",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifctext.htm"
},
"IfcTextAlignment": {
"description": "The IfcTextAlignment describes how text is aligned within the element. Values are:",
"description": "The IfcTextAlignment describes how text is aligned within the element. Values are: - left - right - center - justify",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextalignment.htm"
},
"IfcTextDecoration": {
"description": "The IfcTextDecoration describes decorations that are added to the text of an element. Values are:",
"description": "The IfcTextDecoration describes decorations that are added to the text of an element. Values are: - none - underline - overline - line-through",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextdecoration.htm"
},
"IfcTextFontName": {
@@ -1400,39 +1400,39 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationdefinitionresource/lexical/ifctextpath.htm"
},
"IfcTextTransformation": {
"description": "The IfcTextTransformation describes how the cases of characters are handled. Values are:",
"description": "The IfcTextTransformation describes how the cases of characters are handled. Values are: - capitalize: uppercases the first character of each word - uppercase: uppercases all letters of the element - lowercase: lowercases all letters of the element - none",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcpresentationappearanceresource/lexical/ifctexttransformation.htm"
},
"IfcThermalAdmittanceMeasure": {
"description": "IfcThermalAdmittanceMeasure is the measure of the ability of a surface to smooth out temperature variations.",
"description": "IfcThermalAdmittanceMeasure is the measure of the ability of a surface to smooth out temperature variations. Usually measured in Watt / m2 Kelvin. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcthermaladmittancemeasure.htm"
},
"IfcThermalConductivityMeasure": {
"description": "IfcThermalConductivityMeasure is a measure of thermal conductivity.",
"description": "IfcThermalConductivityMeasure is a measure of thermal conductivity. Usually measured in Watt / m Kelvin. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcthermalconductivitymeasure.htm"
},
"IfcThermalExpansionCoefficientMeasure": {
"description": "IfcThermalExpansionCoeffientMeasure is a measure of the thermal expansion coefficient of a material, which expresses its elongation (as a ratio) per temperature difference. It is usually measured in 1/K. A positive elongation per (positive) rise of temperature is expressed by a positive value.",
"description": "IfcThermalExpansionCoeffientMeasure is a measure of the thermal expansion coefficient of a material, which expresses its elongation (as a ratio) per temperature difference. It is usually measured in 1/K. A positive elongation per (positive) rise of temperature is expressed by a positive value. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcthermalexpansioncoefficientmeasure.htm"
},
"IfcThermalResistanceMeasure": {
"description": "IfcThermalResistanceMeasure is a measure of the resistance offered by a body to the flow of energy.",
"description": "IfcThermalResistanceMeasure is a measure of the resistance offered by a body to the flow of energy. Usually measured in m2 Kelvin/Watt.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcthermalresistancemeasure.htm"
},
"IfcThermalTransmittanceMeasure": {
"description": "IfcThermalTransmittanceMeasure is a measure of the rate at which energy is transmitted through a body.",
"description": "IfcThermalTransmittanceMeasure is a measure of the rate at which energy is transmitted through a body. Usually measured in Watts/m2 Kelvin. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcthermaltransmittancemeasure.htm"
},
"IfcThermodynamicTemperatureMeasure": {
"description": "A thermodynamic temperature measure is the value for the degree of heat of a body.",
"description": "A thermodynamic temperature measure is the value for the degree of heat of a body. Usually measured in degrees Kelvin (K). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcthermodynamictemperaturemeasure.htm"
},
"IfcTime": {
"description": "The IfcTime identifies a time within a day, expressed by hours, minutes and second. It is expressed by a string value following a particular lexical representation.",
"description": "The IfcTime identifies a time within a day, expressed by hours, minutes and second. It is expressed by a string value following a particular lexical representation. The lexical representation for IfcTime is: hh:mm:ss where where hh represents hours, mm minutes and ss seconds. Additional digits can be used to increase the precision of fractional seconds if desired i.e the format ss.ss... A time zone indicator may be provided by a representation of the different to the Coordinated Universal Time. It is appended with a sign [+/-] followed by hh and optionally :mm.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifctime.htm"
},
"IfcTimeMeasure": {
"description": "An IfcTimeMeasure is the value of the duration of periods.",
"description": "An IfcTimeMeasure is the value of the duration of periods. Measured in seconds (s) or days (d) or other units of time. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifctimemeasure.htm"
},
"IfcTimeOrRatioSelect": {
@@ -1444,11 +1444,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifctimeseriesdatatypeenum.htm"
},
"IfcTimeStamp": {
"description": "IfcTimeStamp is an indication of date and time by measuring the number of seconds which have elapsed since 1 January 1970, 00:00:00 UTC.",
"description": "IfcTimeStamp is an indication of date and time by measuring the number of seconds which have elapsed since 1 January 1970, 00:00:00 UTC. Type: INTEGER",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcdatetimeresource/lexical/ifctimestamp.htm"
},
"IfcTorqueMeasure": {
"description": "IfcTorqueMeasure is a measure of the torque or moment of a couple.",
"description": "IfcTorqueMeasure is a measure of the torque or moment of a couple. Usually measured in N m. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifctorquemeasure.htm"
},
"IfcTransformerTypeEnum": {
@@ -1456,7 +1456,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcelectricaldomain/lexical/ifctransformertypeenum.htm"
},
"IfcTransitionCode": {
"description": "The IfcTransitionCode indicated the continuity between consecutive segments of a curve or surface.",
"description": "The IfcTransitionCode indicated the continuity between consecutive segments of a curve or surface. Figure 1 illustrates transition types",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcgeometryresource/lexical/ifctransitioncode.htm"
},
"IfcTranslationalStiffnessSelect": {
@@ -1480,11 +1480,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifctubebundletypeenum.htm"
},
"IfcURIReference": {
"description": "The IfcURIReference provides for identifying a Uniform Resource Identifier (URI). A URI can be classified as a locator or a name or both, that is it may comprise a Uniform Resource Locator (URL) and/or a Uniform Resource Name (URN).",
"description": "The IfcURIReference provides for identifying a Uniform Resource Identifier (URI). A URI can be classified as a locator or a name or both, that is it may comprise a Uniform Resource Locator (URL) and/or a Uniform Resource Name (URN). - A Uniform Resource Locator, URL, is a string conforming to a standardized format, which refers to a resource on the internet (such as a document or an image) by its location. - A Uniform Resource Name, URN, is intended to serve as persistent, location-independent resource identifier and is designed to make it easy to map other namespaces (that share the properties of URNs) into URN-space.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcexternalreferenceresource/lexical/ifcurireference.htm"
},
"IfcUnit": {
"description": "A unit is a physical quantity, with a value of one, which is used as a standard in terms of which other quantities are expressed.",
"description": "SELECT - IfcNamedUnit: A unit which is identified by a name. - IfcDerivedUnit: A unit which is derived from an expression of units. - IfcMonetaryUnit: A unit for defining currencies.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcunit.htm"
},
"IfcUnitEnum": {
@@ -1500,7 +1500,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcunitaryequipmenttypeenum.htm"
},
"IfcValue": {
"description": "IfcValue is a select type for selecting between more specialised select types IfcSimpleValue, IfcMeasureValue and IfcDerivedMeasureValue.",
"description": "IfcValue is a select type for selecting between more specialised select types IfcSimpleValue, IfcMeasureValue and IfcDerivedMeasureValue. SELECT - IfcSimpleValue A select type for basic defined types of simple data type. - IfcMeasureValue A select type for basic measure types of ISO 10303-41. - IfcDerivedMeasureValue A select type for derived measure types.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcvalue.htm"
},
"IfcValveTypeEnum": {
@@ -1508,7 +1508,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcvalvetypeenum.htm"
},
"IfcVaporPermeabilityMeasure": {
"description": "IfcVaporPermeabilityMeasure is a measure of vapor permeability.",
"description": "IfcVaporPermeabilityMeasure is a measure of vapor permeability. Usually measured in kg / s m Pascal. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcvaporpermeabilitymeasure.htm"
},
"IfcVectorOrDirection": {
@@ -1524,11 +1524,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcstructuralelementsdomain/lexical/ifcvoidingfeaturetypeenum.htm"
},
"IfcVolumeMeasure": {
"description": "An IfcVolumeMeasure is the value of the solid content of a body.",
"description": "An IfcVolumeMeasure is the value of the solid content of a body. Usually measured in cubic metre (m3). Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcvolumemeasure.htm"
},
"IfcVolumetricFlowRateMeasure": {
"description": "IfcVolumetricFlowRateMeasure is a measure of the volume of a medium flowing per unit time.",
"description": "IfcVolumetricFlowRateMeasure is a measure of the volume of a medium flowing per unit time. Usually measured in m3/s. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcvolumetricflowratemeasure.htm"
},
"IfcWallTypeEnum": {
@@ -1536,11 +1536,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcwalltypeenum.htm"
},
"IfcWarpingConstantMeasure": {
"description": "IfcWarpingConstantMeasure is a measure for the warping constant or warping resistance of a cross section under torsional loading. It is usually measured in m\\^6.",
"description": "IfcWarpingConstantMeasure is a measure for the warping constant or warping resistance of a cross section under torsional loading. It is usually measured in m\\^6. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcwarpingconstantmeasure.htm"
},
"IfcWarpingMomentMeasure": {
"description": "The warping moment measure is a measure for the warping moment, which occurs in warping torsional analysis. It is usually measured in kN*m\\^2.",
"description": "The warping moment measure is a measure for the warping moment, which occurs in warping torsional analysis. It is usually measured in kN*m\\^2. Type: REAL",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcmeasureresource/lexical/ifcwarpingmomentmeasure.htm"
},
"IfcWarpingStiffnessSelect": {
@@ -1552,11 +1552,11 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcplumbingfireprotectiondomain/lexical/ifcwasteterminaltypeenum.htm"
},
"IfcWindowPanelOperationEnum": {
"description": "This enumeration defines the basic ways to describe how window panels operate, as shown in Figure 2.",
"description": "This enumeration defines the basic ways to describe how window panels operate, as shown in Figure 2. The opening direction of the window panels is given by the local placement of the IfcWindow. The positive y-axis determines the direction as shown in Figure 2. NOTE - Figures are shown as viewed from the outside (in direction of the positive y-axis). - Figures (symbolic representation) depend on the national building code - These figures are only shown as illustrations",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcwindowpaneloperationenum.htm"
},
"IfcWindowPanelPositionEnum": {
"description": "This enumeration defines the basic configuration of the window type in terms of the location of window panels. The window configurations are given for windows with one, two or three panels (including fixed panels) as shown in Figure 1. It corresponds to the OperationType of the IfcWindowStyle definition, which references the IfcWindowPanelProperties.",
"description": "This enumeration defines the basic configuration of the window type in terms of the location of window panels. The window configurations are given for windows with one, two or three panels (including fixed panels) as shown in Figure 1. It corresponds to the OperationType of the IfcWindowStyle definition, which references the IfcWindowPanelProperties. Windows which are subdivided into more than three panels have to be defined by the geometry only. The type of such windows is given by an IfcWindowType.OperationType = USERDEFINED or NOTDEFINED (see IfcWindowStyleOperationEnum for details). NOTE - The figures are shown as elevations in the XZ plane of the local placement of the window, looking into the direction of the positive Y axis. - These figures are only shown as illustrations.",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcwindowpanelpositionenum.htm"
},
"IfcWindowStyleConstructionEnum": {
@@ -1564,7 +1564,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcwindowstyleconstructionenum.htm"
},
"IfcWindowStyleOperationEnum": {
"description": "This enumeration defines the basic configuration of the window type in terms of the number of window panels and the subdivision of the total window. The window configurations are given for windows with one, two or three panels (including fixed panels) as shown in Figure 1.",
"description": "This enumeration defines the basic configuration of the window type in terms of the number of window panels and the subdivision of the total window. The window configurations are given for windows with one, two or three panels (including fixed panels) as shown in Figure 1. Windows which are subdivided into more than three panels have to be defined by the geometry only. The type of such windows is USERDEFINED. NOTE - The way how each panel operates is defined at the IfcWindowPanelProperties.OperationType. - The reference from the window panel to the location of that panel in the window style configuration is handled by the IfcWindowPanelProperties.PanelPosition. - The figures are shown as elevations in the XZ plane of the local placement of the window, looking into the direction of the positive Y axis. - These figures are only shown as illustrations",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcarchitecturedomain/lexical/ifcwindowstyleoperationenum.htm"
},
"IfcWindowTypeEnum": {
@@ -1572,7 +1572,7 @@
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcwindowtypeenum.htm"
},
"IfcWindowTypePartitioningEnum": {
"description": "This enumeration defines the basic configuration of the window type in terms of the number of window panels and the subdivision of the total window as shown in Figure 1. The window configurations are given for windows with one, two or three panels (including fixed panels).",
"description": "This enumeration defines the basic configuration of the window type in terms of the number of window panels and the subdivision of the total window as shown in Figure 1. The window configurations are given for windows with one, two or three panels (including fixed panels). Windows which are subdivided into more than three panels have to be defined by the geometry only. The type of such windows is USERDEFINED. NOTE - The way how each panel operates is defined at the IfcWindowPanelProperties.OperationType. - The reference from the window panel to the location of that panel in the window style configuration is handled by the IfcWindowPanelProperties.PanelPosition. - The figures are shown as elevations in the XZ plane of the local placement of the window, looking into the direction of the positive Y axis. - These figures are only shown as illustrations",
"spec_url": "https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgelements/lexical/ifcwindowtypepartitioningenum.htm"
},
"IfcWorkCalendarTypeEnum": {
@@ -39,8 +39,7 @@ import ifcopenshell.util.shape
import ifcopenshell.util.system
import ifcopenshell.util.unit
filter_elements_grammar = lark.Lark(
"""start: filter_group
filter_elements_grammar = lark.Lark("""start: filter_group
filter_group: facet_list ("+" facet_list)*
facet_list: facet ("," facet)*
@@ -111,11 +110,9 @@ filter_elements_grammar = lark.Lark(
NEWLINE: (CR? LF)+
%ignore WS // Disregard spaces in text
"""
)
""")
get_element_grammar = lark.Lark(
"""start: keys
get_element_grammar = lark.Lark("""start: keys
keys: key ("." key)*
key: quoted_string | regex_string | unquoted_string
@@ -130,11 +127,9 @@ get_element_grammar = lark.Lark(
WS: /[ \\t\\f\\r\\n]/+
%ignore WS // Disregard spaces in text
"""
)
""")
format_grammar = lark.Lark(
"""start: expression
format_grammar = lark.Lark("""start: expression
?expression: add_sub
?add_sub: mul_div
@@ -193,8 +188,7 @@ format_grammar = lark.Lark(
NEWLINE: (CR? LF)+
%ignore WS // Disregard spaces in text
"""
)
""")
class FormatTransformer(lark.Transformer):
@@ -0,0 +1,100 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2025 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.api.alignment
import ifcopenshell.api.context
import ifcopenshell.api.unit
import ifcopenshell.util.element
def test_add_positioning_referent():
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(GlobalId=ifcopenshell.guid.new(), Name="Test")
length = ifcopenshell.api.unit.add_si_unit(file, unit_type="LENGTHUNIT")
ifcopenshell.api.unit.assign_unit(file, units=[length])
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
file,
context_type="Model",
context_identifier="Axis",
target_view="MODEL_VIEW",
parent=geometric_representation_context,
)
alignment = ifcopenshell.api.alignment.create(file, "TestAlignment", start_station=2000.0)
horizontal_layout = ifcopenshell.api.alignment.get_horizontal_layout(alignment)
segment = ifcopenshell.api.alignment.get_layout_segments(horizontal_layout)[0]
referent = ifcopenshell.api.alignment.add_positioning_referent(
file, "P.C.", alignment, distance_along=0.0, station=2000.0, positioned_product=segment
)
assert referent.is_a("IfcReferent")
assert referent.PredefinedType == "POSITION"
assert referent.Name == "P.C."
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing")
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing", prop="Station") == 2000.0
assert referent.ObjectPlacement != None
assert len(referent.Positions) == 1
rel_positions = referent.Positions[0]
assert rel_positions.is_a("IfcRelPositions")
assert rel_positions.RelatingPositioningElement == referent
assert rel_positions.RelatedProducts == (segment,)
def test_add_positioning_referent_creates_separate_referent_per_call():
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(GlobalId=ifcopenshell.guid.new(), Name="Test")
length = ifcopenshell.api.unit.add_si_unit(file, unit_type="LENGTHUNIT")
ifcopenshell.api.unit.assign_unit(file, units=[length])
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
file,
context_type="Model",
context_identifier="Axis",
target_view="MODEL_VIEW",
parent=geometric_representation_context,
)
alignment = ifcopenshell.api.alignment.create(file, "TestAlignment", start_station=2000.0)
horizontal_layout = ifcopenshell.api.alignment.get_horizontal_layout(alignment)
segment = ifcopenshell.api.alignment.get_layout_segments(horizontal_layout)[0]
first_referent = ifcopenshell.api.alignment.add_positioning_referent(
file, "P.C.", alignment, distance_along=0.0, station=2000.0, positioned_product=segment
)
other_product = file.createIfcBuildingElementProxy(GlobalId=ifcopenshell.guid.new(), Name="Sign")
second_referent = ifcopenshell.api.alignment.add_positioning_referent(
file, "P.C.", alignment, distance_along=0.0, station=2000.0, positioned_product=other_product
)
# each call creates its own IfcReferent, each with its own IfcRelPositions to the product passed in
assert first_referent != second_referent
assert len(first_referent.Positions) == 1
assert first_referent.Positions[0].RelatedProducts == (segment,)
assert len(second_referent.Positions) == 1
assert second_referent.Positions[0].RelatedProducts == (other_product,)
test_add_positioning_referent()
test_add_positioning_referent_creates_separate_referent_per_call()
@@ -0,0 +1,115 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2025 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.api.alignment
import ifcopenshell.api.context
import ifcopenshell.api.unit
import ifcopenshell.util.element
def _create_test_file():
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(GlobalId=ifcopenshell.guid.new(), Name="Test")
length = ifcopenshell.api.unit.add_si_unit(file, unit_type="LENGTHUNIT")
ifcopenshell.api.unit.assign_unit(file, units=[length])
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
file,
context_type="Model",
context_identifier="Axis",
target_view="MODEL_VIEW",
parent=geometric_representation_context,
)
return file
def _create_test_alignment_with_vertical(file):
# include_vertical=True so that get_curve() (IfcGradientCurve, on the "Axis" representation)
# and get_basis_curve() (IfcCompositeCurve, on the "FootPrint" representation) are different
# entities, letting the on_basis_curve option be observed.
alignment = ifcopenshell.api.alignment.create(file, "TestAlignment", include_vertical=True, start_station=0.0)
assert ifcopenshell.api.alignment.get_basis_curve(alignment).is_a("IfcCompositeCurve")
assert ifcopenshell.api.alignment.get_curve(alignment).is_a("IfcGradientCurve")
assert ifcopenshell.api.alignment.get_basis_curve(alignment) != ifcopenshell.api.alignment.get_curve(alignment)
return alignment
def _assert_common_referent_asserts(referent, name, station):
assert referent.is_a("IfcReferent")
assert referent.PredefinedType == "STATION"
assert referent.Name == name
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing")
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing", prop="Station") == station
assert referent.ObjectPlacement != None
def test_add_stationing_referent_on_basis_curve_none_defaults_to_basis_curve():
# on_basis_curve=None should behave the same as on_basis_curve=True
file = _create_test_file()
alignment = _create_test_alignment_with_vertical(file)
referent = ifcopenshell.api.alignment.add_stationing_referent(
file, "1+00.000", alignment, distance_along=100.0, station=100.0, on_basis_curve=None
)
_assert_common_referent_asserts(referent, "1+00.000", 100.0)
assert referent.ObjectPlacement.is_a("IfcLinearPlacement")
assert referent.ObjectPlacement.RelativePlacement.Location.BasisCurve == ifcopenshell.api.alignment.get_basis_curve(
alignment
)
def test_add_stationing_referent_on_basis_curve_true():
file = _create_test_file()
alignment = _create_test_alignment_with_vertical(file)
referent = ifcopenshell.api.alignment.add_stationing_referent(
file, "1+00.000", alignment, distance_along=100.0, station=100.0, on_basis_curve=True
)
_assert_common_referent_asserts(referent, "1+00.000", 100.0)
assert referent.ObjectPlacement.is_a("IfcLinearPlacement")
assert referent.ObjectPlacement.RelativePlacement.Location.BasisCurve == ifcopenshell.api.alignment.get_basis_curve(
alignment
)
def test_add_stationing_referent_on_basis_curve_false():
# with a vertical layout present, on_basis_curve=False positions the referent on the
# alignment curve (IfcGradientCurve) rather than on the basis curve (IfcCompositeCurve).
file = _create_test_file()
alignment = _create_test_alignment_with_vertical(file)
referent = ifcopenshell.api.alignment.add_stationing_referent(
file, "1+00.000", alignment, distance_along=100.0, station=100.0, on_basis_curve=False
)
_assert_common_referent_asserts(referent, "1+00.000", 100.0)
assert referent.ObjectPlacement.is_a("IfcLinearPlacement")
basis_curve = referent.ObjectPlacement.RelativePlacement.Location.BasisCurve
assert basis_curve == ifcopenshell.api.alignment.get_curve(alignment)
assert basis_curve != ifcopenshell.api.alignment.get_basis_curve(alignment)
test_add_stationing_referent_on_basis_curve_none_defaults_to_basis_curve()
test_add_stationing_referent_on_basis_curve_true()
test_add_stationing_referent_on_basis_curve_false()
@@ -48,5 +48,26 @@ def test_add_stationing_to_alignment():
assert ifcopenshell.util.element.get_pset(element=referent, name="Pset_Stationing", prop="Station") == 2000.0
assert referent.ObjectPlacement != None
# add a station equation at 1000 distance along. this is station 3+000 in coming and 4+000 outgoing.
# this is a gap equation.
second_referent = ifcopenshell.api.alignment.add_stationing_referent(
file, "4+000.000", alignment, distance_along=1000.0, station=4000.0, incoming_station=3000.0
)
referent_nest = ifcopenshell.api.alignment.get_referent_nest(file, alignment)
assert len(referent_nest.RelatedObjects) == 2
assert second_referent == referent_nest.RelatedObjects[1]
assert second_referent.PredefinedType == "STATION"
assert second_referent.Name == "4+000.000"
assert ifcopenshell.util.element.get_pset(element=second_referent, name="Pset_Stationing")
assert ifcopenshell.util.element.get_pset(element=second_referent, name="Pset_Stationing", prop="Station") == 4000.0
assert (
ifcopenshell.util.element.get_pset(element=second_referent, name="Pset_Stationing", prop="IncomingStation")
== 3000.0
)
assert second_referent.ObjectPlacement != None
test_add_stationing_to_alignment()
@@ -21,9 +21,12 @@ import math
import pytest
import ifcopenshell
import ifcopenshell.api.aggregate
import ifcopenshell.api.alignment
import ifcopenshell.api.context
import ifcopenshell.api.spatial
import ifcopenshell.api.unit
import numpy as np
import ifcopenshell.util.unit
def test_create_representation():
@@ -53,4 +53,56 @@ def test_distance_along_from_station():
assert ifcopenshell.api.alignment.distance_along_from_station(file, alignment, 17525.36) == pytest.approx(7525.36)
def test_distance_along_from_station_with_station_equations():
# Reproduces the worked example from the IFC Alignment Geometry Implementation Guide, chapter 9.2.6:
# a gap equation (P3: incoming 14+00.00, outgoing 17+00.00) and an overlap equation
# (P4: incoming 19+00.00, outgoing 18+50.00).
file = ifcopenshell.file(schema="IFC4X3")
project = file.createIfcProject(GlobalId=ifcopenshell.guid.new(), Name="Test")
length = ifcopenshell.api.unit.add_conversion_based_unit(file, name="foot")
ifcopenshell.api.unit.assign_unit(file, units=[length])
geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model")
axis_model_representation_subcontext = ifcopenshell.api.context.add_context(
file,
context_type="Model",
context_identifier="Axis",
target_view="MODEL_VIEW",
parent=geometric_representation_context,
)
coordinates = [(500.0, 2500.0), (3340.0, 660.0), (4340.0, 5000.0), (7600.0, 4560.0), (8480.0, 2010.0)]
radii = [(1000.0), (1250.0), (950.0)]
vpoints = [(0.0, 100.0), (2000.0, 135.0), (5000.0, 105.0), (7400.0, 153.0), (9800.0, 105.0), (12800.0, 90.0)]
lengths = [(1600.0), (1200.0), (2000.0), (800.0)]
alignment = ifcopenshell.api.alignment.create_by_pi_method(
file, "TestAlignment", coordinates, radii, vpoints, lengths, start_station=1000.0
)
ifcopenshell.api.alignment.add_stationing_referent(
file, "P3", alignment, distance_along=400.0, station=1700.0, incoming_station=1400.0
)
ifcopenshell.api.alignment.add_stationing_referent(
file, "P4", alignment, distance_along=600.0, station=1850.0, incoming_station=1900.0
)
distance_along_from_station = ifcopenshell.api.alignment.distance_along_from_station
# between P2 and P3: Sta. 13+00.00
assert distance_along_from_station(file, alignment, 1300.0) == pytest.approx(300.0)
# between P3 and P4: Sta. 18+00.00
assert distance_along_from_station(file, alignment, 1800.0) == pytest.approx(500.0)
# between P4 and P5: Sta. 19+25.00
assert distance_along_from_station(file, alignment, 1925.0) == pytest.approx(675.0)
# Sta. 15+00.00 falls inside the gap opened by the equation at P3 and has no corresponding distance along
assert distance_along_from_station(file, alignment, 1500.0) is None
# Sta. 18+75.00 falls inside the overlap zone at P4; the post-equation (outgoing) match is returned
assert distance_along_from_station(file, alignment, 1875.0) == pytest.approx(625.0)
test_distance_along_from_station()
test_distance_along_from_station_with_station_equations()
@@ -1,5 +1,6 @@
import ifcopenshell
def test_skip_over_non_entity_instance():
data = """
ISO-10303-21;
+1 -1
View File
@@ -46,4 +46,4 @@ def test_file(filename):
if __name__ == "__main__":
pytest.main(["-sx", __file__, '--import-mode=importlib'])
pytest.main(["-sx", __file__, "--import-mode=importlib"])
+9
View File
@@ -187,6 +187,15 @@ void IfcUtil::sanitate_material_name(std::string& str) {
}
void IfcUtil::escape_xml(std::string& str) {
// Strip characters that are illegal in XML 1.0. Control characters other
// than tab (0x09), newline (0x0A) and carriage return (0x0D) are not valid
// XML 1.0 characters and cannot even be represented as numeric character
// references, so they would otherwise make the serialized XML/SVG output
// non-well-formed. Bytes belonging to a valid UTF-8 multibyte sequence are
// always >= 0x80, so filtering on the low control range leaves them intact.
str.erase(std::remove_if(str.begin(), str.end(), [](unsigned char c) {
return c < 0x20 && c != '\t' && c != '\n' && c != '\r';
}), str.end());
boost::replace_all(str, "&", "&amp;");
boost::replace_all(str, "\"", "&quot;");
boost::replace_all(str, "'", "&apos;");
@@ -111,7 +111,7 @@ class Patcher(ifcpatch.BasePatcher):
if element.is_a("IfcProject"):
proj = self.new.add(element)
for ctx in element.RepresentationContexts or ():
for coop in getattr(ctx, 'HasCoordinateOperation', ()):
for coop in getattr(ctx, "HasCoordinateOperation", ()):
self.new.add(coop)
return proj
return ifcopenshell.api.project.append_asset(
@@ -33,9 +33,7 @@ class TestDowngradeIndexedPolyCurve(test.bootstrap.IFC4):
Points=point_list,
Segments=segments,
)
self.file.create_entity(
"IfcArbitraryClosedProfileDef", ProfileType="AREA", OuterCurve=curve
)
self.file.create_entity("IfcArbitraryClosedProfileDef", ProfileType="AREA", OuterCurve=curve)
return curve
def test_run_without_segments(self):
@@ -80,9 +78,7 @@ class TestDowngradeIndexedPolyCurve(test.bootstrap.IFC4):
Points=point_list,
Segments=[self.file.createIfcLineIndex((1, 2, 3, 4, 1))],
)
self.file.create_entity(
"IfcArbitraryClosedProfileDef", ProfileType="AREA", OuterCurve=curve
)
self.file.create_entity("IfcArbitraryClosedProfileDef", ProfileType="AREA", OuterCurve=curve)
ifcpatch.execute(
{"input": "input.ifc", "file": self.file, "recipe": "DowngradeIndexedPolyCurve", "arguments": []}
)
@@ -110,9 +106,7 @@ class TestDowngradeIndexedPolyCurve(test.bootstrap.IFC4):
self.file.createIfcLineIndex((3, 4)),
],
)
self.file.create_entity(
"IfcArbitraryClosedProfileDef", ProfileType="AREA", OuterCurve=curve
)
self.file.create_entity("IfcArbitraryClosedProfileDef", ProfileType="AREA", OuterCurve=curve)
ifcpatch.execute(
{"input": "input.ifc", "file": self.file, "recipe": "DowngradeIndexedPolyCurve", "arguments": []}
)
+2
View File
@@ -977,12 +977,14 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
if (item == nullptr) {
throw IfcParse::IfcException("Failed to convert placement");
}
/*
if (st.get<ifcopenshell::geometry::settings::ConvertBackUnits>().get()) {
// we pass the settings to the Transformation object, but access the data just offloads to the
// generic cartesian_base<Matrix4> so there's no time to apply the settings to the translation part.
item = ifcopenshell::geometry::taxonomy::matrix4::ptr(item->clone_());
item->components().col(3).head<3>() /= kernel.settings().get<ifcopenshell::geometry::settings::LengthUnit>().get();
}
*/
return new IfcGeom::Transformation(kernel.settings(), item);
} else {
if (!representation) {
@@ -305,7 +305,7 @@ ptree* descend(Logger& logger, ifcopenshell::geometry::abstract_mapping* mapping
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinition>
(logger, object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
#ifdef SCHEMAS_HAS_IfcPropertySetDefinitionSet
#ifdef SCHEMA_HAS_IfcPropertySetDefinitionSet
aggregate_of<IfcSchema::IfcPropertySetDefinitionSet>::ptr property_set_sets = get_related
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinitionSet>
(logger, object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);