Write docs on IOS geometry processing. See #671.

This commit is contained in:
Dion Moult
2022-10-06 17:06:37 +11:00
parent 15b4ab543b
commit 0db28917a4
5 changed files with 207 additions and 58 deletions
@@ -12,5 +12,5 @@ system, as well as high level analysis and authoring functions.
ifcopenshell-python/installation
ifcopenshell-python/hello_world
ifcopenshell-python/code_examples
ifcopenshell-python/geometry_iterator
ifcopenshell-python/geometry_processing
ifcopenshell-python/developer_guide
@@ -1,53 +0,0 @@
Geometry iterator
=================
IfcOpenShell provides a geometry iterator function to efficiently process
geometry in an IFC model. The iterator is always used in IfcConvert, and may
also be invoked in C++ or in Python.
The geometry iterator makes it easy to collect possible geometry in a model,
supports multicore processing, and implements caching and reuse to improve the
efficiency of geometry processing. It is also possible to process geometry one
by one using ``create_shape()``, but is significantly less efficient.
By default, the geometry iterator processes all 3D geometry in a model from all
elements, and returns a list of X Y Z vertex ordinates in a flattend list, as
well as a flattend list of triangulated faces denoted by vertex indices.
There are a variety of configuration settings to get different output. For
example, you may filter elements from processing, extract 2D data, or return
non-triangulated OpenCASCADE BReps. For more information on the various
settings, see :doc:`Geometry Settings<../ifcopenshell/geometry_settings>`.
Here is a simple example in Python:
.. code-block:: python
import multiprocessing
import ifcopenshell
import ifcopenshell.geom
ifc_file = ifcopenshell.open('model.ifc')
settings = ifcopenshell.geom.settings()
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
if iterator.initialize():
while True:
shape = iterator.get()
# Get the current IFC element we are iterating over
element = ifc_file.by_guid(shape.guid)
# Indices of vertices per triangle face e.g. [f1v1, f1v2, f1v3, f2v1, f2v2, f2v3, ...]
faces = shape.geometry.faces
# X Y Z of vertices in flattened list e.g. [v1x, v1y, v1z, v2x, v2y, v2z, ...]
verts = shape.geometry.verts
# Material names and colour style information that are relevant to this shape
materials = shape.geometry.materials
# Indices of material applied per triangle face e.g. [f1m, f2m, ...]
material_ids = shape.geometry.material_ids
# Since the lists are flattened, you may prefer to group them per
# face like so depending on your geometry kernel
grouped_verts = [[verts[i], verts[i + 1], verts[i + 2]] for i in range(0, len(verts), 3)]
grouped_faces = [[faces[i], faces[i + 1], faces[i + 2]] for i in range(0, len(faces), 3)]
if not iterator.next():
break
@@ -0,0 +1,202 @@
Geometry processing
===================
Geometry is specified in many ways in IFC. Some geometry is defined explicitly
with coordinates, vertices, and faces. Some geometry is defined implicitly with
equations, boolean operations, and parametric shapes.
Individual processing
---------------------
The simplest way to process any geometry in a standardised fashion is to use the
IfcOpenShell ``create_shape()`` function. This will provide a list of vertices,
edges, and faces, or alternatively an OpenCASCADE BRep.
Here is a simple example of processing a single wall into a list of vertices and
faces. In this example, a ``shape`` variable is returned, which holds geometry
related information in ``shape.geometry``:
.. code-block:: python
import ifcopenshell
import ifcopenshell.geom
ifc_file = ifcopenshell.open('model.ifc')
element = ifc_file.by_type('IfcWall')[0]
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, element)
# The GUID of the element we processed
print(shape.guid)
# The ID of the element we processed
print(shape.id)
# The element we are processing
print(ifc_file.by_guid(shape.guid))
# A unique geometry ID, useful to check whether or not two geometries are
# identical for caching and reuse. The naming scheme is:
# IfcShapeRepresentation.id{-layerset-LayerSet.id}{-material-Material.id}{-openings-[Opening n.id ...]}{-world-coords}
print(shape.geometry.id())
# Indices of vertices per triangle face e.g. [f1v1, f1v2, f1v3, f2v1, f2v2, f2v3, ...]
faces = shape.geometry.faces
# X Y Z of vertices in flattened list e.g. [v1x, v1y, v1z, v2x, v2y, v2z, ...]
verts = shape.geometry.verts
# Since the lists are flattened, you may prefer to group them per face like so depending on your geometry kernel
grouped_verts = [[verts[i], verts[i + 1], verts[i + 2]] for i in range(0, len(verts), 3)]
grouped_faces = [[faces[i], faces[i + 1], faces[i + 2]] for i in range(0, len(faces), 3)]
# A list of styles that are relevant to this shape
styles = shape.geometry.materials
for style in styles:
# Each style is named after the entity class if a default
# material is applied. Otherwise, it is named "surface-style-{SurfaceStyle.name}"
# All non-alphanumeric characters are replaced with a "-".
print(style.original_name)
# A more human readable name
print(style.name)
# Each style may have diffuse colour RGB codes
if style.has_diffuse:
print(style.diffuse)
# Each style may have transparency data
if style.has_transparency:
print(style.transparency)
# Indices of material applied per triangle face e.g. [f1m, f2m, ...]
material_ids = shape.geometry.material_ids
Alternatively, you may choose to retrieve an OpenCASCADE BRep:
.. code-block:: python
import ifcopenshell
import ifcopenshell.geom
ifc_file = ifcopenshell.open('model.ifc')
element = ifc_file.by_type('IfcWall')[0]
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_PYTHON_OPENCASCADE, True)
try:
shape = geom.create_shape(settings, element)
geometry = shape.geometry # see #1124
# These are methods of the TopoDS_Shape class from pythonOCC
shape_gpXYZ = geometry.Location().Transformation().TranslationPart()
# These are methods of the gpXYZ class from pythonOCC
print(shape_gpXYZ.X(), shape_gpXYZ.Y(), shape_gpXYZ.Z())
except:
print("Shape creation failed")
When an entire element is passed into ``create_shape()``, the 3D representation
is processed by default with all openings applied. However, it is also possible
to only process a single shape representation with no openings, representation
item, or profile definition.
In these scenarios, a ``geometry`` is returned directly, equivalent to
``shape.geometry`` in the example above.
.. code-block:: python
ifc_file = ifcopenshell.open('model.ifc')
element = ifc_file.by_type('IfcWall')[0]
# Process a shape representation
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body")
# Note: geometry is returned directly, equivalent to shape.geometry when passing in an element
geometry = geom.create_shape(settings, body)
# Process a representation item
geometry = geom.create_shape(settings, ifc_file.by_type("IfcExtrudedAreaSolid")[0])
# Process a profile
geometry = geom.create_shape(settings, ifc_file.by_type("IfcProfileDef")[0])
Geometry iterator
-----------------
IfcOpenShell provides a geometry iterator function to efficiently process
geometry in an IFC model. The iterator is always used in IfcConvert, and may
also be invoked in C++ or in Python. It offers the same features as the
``create_shape()`` function for `Individual processing`_.
The geometry iterator makes it easy to collect possible geometry in a model,
supports multicore processing, and implements caching and reuse to improve the
efficiency of geometry processing. For any bulk geometry processing, it is
always recommended to use the iterator.
By default, the geometry iterator processes all 3D geometry in a model from all
elements, and returns a list of X Y Z vertex ordinates in a flattened list, as
well as a flattened list of triangulated faces denoted by vertex indices.
There are a variety of configuration settings to get different output. For
example, you may filter elements from processing, extract 2D data, or return
non-triangulated OpenCASCADE BReps. For more information on the various
settings, see :doc:`Geometry Settings<../ifcopenshell/geometry_settings>`.
Here is a simple example in Python:
.. code-block:: python
import multiprocessing
import ifcopenshell
import ifcopenshell.geom
ifc_file = ifcopenshell.open('model.ifc')
settings = ifcopenshell.geom.settings()
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
if iterator.initialize():
while True:
shape = iterator.get()
faces = shape.geometry.faces
verts = shape.geometry.verts
materials = shape.geometry.materials
material_ids = shape.geometry.material_ids
# ... write code to process geometry here ...
if not iterator.next():
break
.. note::
The iterator can only be used to process whole elements, not individual
shape representations, representation items, and profiles.
Manual parsing
--------------
IfcOpenShell lets you traverse any IFC entity graph. This means it is possible
for you to manually browse through the ``Representation`` attribute of IFC
elements, and parse the corresponding IFC shape representations yourself instead
of using generic geometric processing such as `Individual processing`_ and the
`Geometry iterator`_.
This approach requires an in-depth understanding of IFC geometry
representations, as well as its many caveats with units and transformations, but
can be very simple and extremely fast to extract specific types of geometry. For
example, if you know you are dealing with IfcCircle geometry, you can
specifically pinpoint the Radius parameter.
.. code-block:: python
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
for circle in ifc_file.by_type("IfcCircle"):
# In project length units
print(circle.Radius)
# In SI meters
print(circle.Radius * unit_scale)
Given the advanced nature of manual processing, it is generally not recommended
except in specific tasks.
+2 -2
View File
@@ -41,8 +41,8 @@ utility:
Exactly how it is run depends on the recipe. A recipe may require zero or more
arguments which are specific to the recipe. Here's an example which runs the
`ExtractElements` recipe, which, as the same suggests, extracts out elements.
This recipe expects one argument, which uses the [IFC Query
syntax](https://wiki.osarch.org/index.php?title=IfcOpenShell_code_examples#IFC_Query_Syntax).
This recipe expects one argument, which uses the `IFC Query
syntax <https://wiki.osarch.org/index.php?title=IfcOpenShell_code_examples#IFC_Query_Syntax>`_.
In this example, we'll extract out all `IfcWall` elements.
::