Fix #4109. Write docs about geometry serialiser.

This commit is contained in:
Dion Moult
2024-01-19 23:18:38 +11:00
parent 6f1cb16107
commit 4ee0930e05
@@ -12,6 +12,14 @@ 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.
.. warning::
This section describes individual processing only. This is useful for
learning how geometry processing works, but is not recommended for practical
applications. See the `Geometry iterator`_ section below after reading this
to see how to process geometry with multiple threads.
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``:
@@ -158,39 +166,34 @@ In these scenarios, a ``geometry`` is returned directly, equivalent to
geometry = geom.create_shape(settings, ifc_file.by_type("IfcProfileDef")[0])
Process individual element with multiple shape representations
---------------------
When an element contains multiple shape representations with the same identifier or when you want more explicit control over which representation is processed (e.g `Body` or `Tesselation`), you can use the third parameter of ``create_shape()`` to nominate a specific shape representation to be processed in the context of a product.
The element in your ifc file might look like this.
When an element contains multiple shape representations with the same
identifier or when you want more explicit control over which representation is
processed (e.g ``Body`` or ``Tessellation``), you can use the third parameter of
``create_shape()`` to nominate a specific shape representation to be processed
in the context of a product. The element in your ifc file might look like
this.
.. code-block:: ifc
#1618937=IFCSHAPEREPRESENTATION(#4,'Body','BRep',(#1617476));
#1618938=IFCSHAPEREPRESENTATION(#4,'Body','BRep',(#1617583));
#1618939=IFCSHAPEREPRESENTATION(#4,'Body','BRep',(#1617630));
#1618957=IFCPRODUCTDEFINITIONSHAPE($,$,(#1618937,#1618938,#1618939));
#1618958=IFCWINDOW('0Rrp2csNr07QrVCrEBJezu',#9,'test','test',$,#1618936,#1618957,'\X2\5EFA7B517A97\X0\',$,$,$,$,$);
#1=IFCSHAPEREPRESENTATION(#4,'Body','BRep',(#1617476));
#2=IFCSHAPEREPRESENTATION(#4,'Body','BRep',(#1617583));
#3=IFCSHAPEREPRESENTATION(#4,'Body','BRep',(#1617630));
#5=IFCPRODUCTDEFINITIONSHAPE($,$,(#1,#2,#3));
#6=IFCWINDOW('0Rrp2csNr07QrVCrEBJezu',#9,'test','test',$,#7,#5,'test',$,$,$,$,$);
In order to get the geometry data (e.g. vertices) for this ``IFCWINDOW``, we can use the Python code below:
In order to get the geometry data (e.g. vertices) for this ``IfcWindow``, we can use the Python code below:
.. code-block:: python
import ifcopenshell
import ifcopenshell.geom
settings = ifcopenshell.geom.settings()
ifc_file = ifcopenshell.open('window.ifc')
window = ifc_file.by_type('IfcWindow')[0] # Get the IFCWINDOW that contains multiple Representations
representations = window.Representation.Representations
for rep in representations:
shape = ifcopenshell.geom.create_shape(settings, window, rep) # The third parameter specifies which representation of the window is handled
vertices = shape.geometry.verts
print(vertices)
for representation in representations:
# ... code that filters which representation you want ...
shape = ifcopenshell.geom.create_shape(settings, window, representation)
.. seealso::
.. note::
You may still need to determine which representation don't contain geometry data or some type like Box need to be discarded at render time.
You may find the ``ifcopenshell.util.representation`` module useful to
filter out specific representations.
Geometry iterator
@@ -210,11 +213,6 @@ 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
@@ -241,28 +239,19 @@ Here is a simple example in Python:
break
The geometry iterator can process specific elements' geometry by using parameter ``include`` and ``exclude``, ``include`` and ``exclude`` cannot be specified simultaneously. Code below shows how to get specific wall's geometry (e.g. walls[1]):
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>`.
One of the more common settings used is the ``include`` setting, which
specifies only to process certain geometry. For example, this iterator will
only process wall elements.
.. code-block:: python
import ifcopenshell
import ifcopenshell.geom
import multiprocessing
import ifcopenshell.util.shape
ifc = ifcopenshell.open('model.ifc')
walls = ifc.by_type('ifcwall')
settings = ifcopenshell.geom.settings()
iterator = ifcopenshell.geom.iterator(settings, ifc, multiprocessing.cpu_count(), include=[walls[1]])
valid_file = iterator.initialize()
while True:
shape = iterator.get()
element = ifc.by_id(shape.id)
geometry = shape.geometry
verts = geometry.verts
print(verts)
if not iterator.next():
break
walls = ifc.by_type('IfcWall')
iterator = ifcopenshell.geom.iterator(settings, ifc, multiprocessing.cpu_count(), include=walls)
.. note::
@@ -297,3 +286,43 @@ specifically pinpoint the Radius parameter.
Given the advanced nature of manual processing, it is generally not recommended
except in specific tasks.
Geometry serialisation
----------------------
Geometry may be serialised into many different formats using
:doc:`IfcConvert<../ifcconvert>`. Alternatively, you may also access the
serialiser with Python to customise the conversion, such as by writing a script
the modifies the IFC on the fly before converting it, or writing complex
include and exclude filters.
Here is a typical example to serialising to glTF / glb.
.. code-block:: python
import ifcopenshell
import ifcopenshell.geom
import multiprocessing
settings = ifcopenshell.geom.settings()
settings.set(settings.STRICT_TOLERANCE, True)
settings.set(settings.INCLUDE_CURVES, True)
# Setting element GUIDs is optional, but useful to uniquely identify objects in non-semantic formats.
settings.set(settings.USE_ELEMENT_GUIDS, True)
# Note that applying default materials is required in glTF serialisation.
settings.set(settings.APPLY_DEFAULT_MATERIALS, True)
serialiser = ifcopenshell.geom.serializers.gltf("output.glb", settings)
# Alternatively, this is an example for OBJ
# serialiser = ifcopenshell.geom.serializers.obj('output.obj', 'output.mtl', settings)
serialiser.setFile(self.file)
serialiser.setUnitNameAndMagnitude("METER", 1.0)
serialiser.writeHeader()
iterator = ifcopenshell.geom.iterator(settings, self.file, multiprocessing.cpu_count())
if iterator.initialize():
while True:
serialiser.write(iterator.get())
if not iterator.next():
break
serialiser.finalize()