diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst index 6aea0c6d23..5f6aaa9175 100644 --- a/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst +++ b/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst @@ -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()