mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 22:33:33 +00:00
Write docs about new geometry tree clash functions
This commit is contained in:
@@ -1,14 +1,16 @@
|
|||||||
Geometry tree
|
Geometry tree
|
||||||
=============
|
=============
|
||||||
|
|
||||||
IfcOpenShell includes a utility to build a unbalanced binary tree of geometry
|
IfcOpenShell includes a utility to build trees of geometry and their bounding
|
||||||
and their bounding boxes. After a tree is built, you can efficiently select
|
boxes. Geometry trees can be used to efficiently select geometry or collide
|
||||||
geometry by specifying a point, radius, or bounding box.
|
geometry with one another.
|
||||||
|
|
||||||
.. image:: images/geometry-tree.png
|
.. image:: images/geometry-tree.png
|
||||||
|
|
||||||
The most efficient way to build tree is by using the iterator, as shown in the
|
The most efficient way to build a tree is by using the iterator. If the native
|
||||||
example below:
|
OpenCASCADE shape is added to the tree, a **UB Tree** is built. Alternatively,
|
||||||
|
if triangulation is added to the tree, a **BVH Tree** is built. The type of
|
||||||
|
tree determines the type of operation you can perform.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@@ -21,27 +23,208 @@ example below:
|
|||||||
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
|
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
|
||||||
if iterator.initialize():
|
if iterator.initialize():
|
||||||
while True:
|
while True:
|
||||||
tree.add_element(iterator.get_native())
|
# Use triangulation to build a BVH tree
|
||||||
|
tree.add_element(iterator.get())
|
||||||
|
|
||||||
|
# Alternatively, use this code to build an unbalanced binary tree
|
||||||
|
# tree.add_element(iterator.get_native())
|
||||||
|
|
||||||
if not iterator.next():
|
if not iterator.next():
|
||||||
break
|
break
|
||||||
|
|
||||||
Once built, there are three methods you can use to select elements in the tree:
|
Clashing or selecting geometry from a geometry tree
|
||||||
``select_box``, ``select``, and ``select_ray``.
|
---------------------------------------------------
|
||||||
|
|
||||||
``select_box`` lets you query for elements that contain a point or another
|
With a **BVH Tree**, you can efficiently clash sets of elements with other
|
||||||
element. However, it only checks the bounding box of elements instead of their
|
elements. You can find elements that intersect, collide, or are within a
|
||||||
exact geometry. This is the fastest approach and is recommended if you don't
|
clearance distance threshold of one another. There are three methods you can
|
||||||
need precise geometry selection.
|
use to clash elements in the tree. Each function collides one set of elements
|
||||||
|
with another set of elements.
|
||||||
|
|
||||||
``select`` lets you query for elements that contain a point, a sphere, or
|
- `Detecting intersection clashes between elements`_ detects when an element intersects with another
|
||||||
another element. ``select`` is similar to select box, but additionally
|
element. This is the most common type of clash detection used when
|
||||||
considers the actual geometry of the object. This is slower but more precise.
|
coordinating designs. For example, you might want to know if any pipes go
|
||||||
|
through structural columns or beams.
|
||||||
|
- `Detecting collision clashes between elements`_ detects when an element
|
||||||
|
touches another element. It is the fastest type of clash detection but does
|
||||||
|
not consider the distance that an element goes inside another element. This
|
||||||
|
considers surfaces only so it works on non-manifold geometry but will not
|
||||||
|
detect if an element is completely within another element.
|
||||||
|
- `Detecting clearance clashes between elements`_ detects when an element comes
|
||||||
|
near to another element within a clearance threshold. This is the slowest
|
||||||
|
type of clash detection. It works on non-manifold geometry and does not
|
||||||
|
consider inside vs outside. Elements like pipe and ducts with insulation,
|
||||||
|
structural openings, and equipment will typically require clearance checks.
|
||||||
|
|
||||||
``select_ray`` lets you query for elements that intersect with a ray.
|
With a **UB Tree**, you can efficiently select geometry by specifying a point,
|
||||||
|
radius, or bounding box. There are three methods you can use to select elements
|
||||||
|
in the tree.
|
||||||
|
|
||||||
|
- `Selecting elements using bounding boxes`_ lets you query for elements that
|
||||||
|
contain a point or another element. However, it only checks the bounding box
|
||||||
|
of elements instead of their exact geometry. This is the fastest approach and
|
||||||
|
is recommended if you don't need precise geometry selection.
|
||||||
|
- `Selecting elements using precise geometry`_ lets you query for elements that
|
||||||
|
contain a point, a sphere, or another element. This is similar to selecting
|
||||||
|
using bounding boxes, but additionally considers the actual geometry of the
|
||||||
|
element. This is slower but more precise.
|
||||||
|
- `Selecting elements using a ray`_ lets you query for elements that intersect
|
||||||
|
with a ray.
|
||||||
|
|
||||||
|
Detecting intersection clashes between elements
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
``clash_intersection_many`` detects when an element intersects with or is
|
||||||
|
contained within another element.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
clashes = tree.clash_intersection_many(
|
||||||
|
group_a_elements, # e.g. from model.by_type("IfcWall")
|
||||||
|
group_b_elements, # Group b can be the same as group a if you want to clash within a single set
|
||||||
|
tolerance=0.002, # Any protrusions less than 2mm are ignored
|
||||||
|
check_all=True, # Keep on checking all potential intersections to find a worst case protrusion distance
|
||||||
|
)
|
||||||
|
|
||||||
|
for clash in clashes:
|
||||||
|
# Get the two elements that clash and their metadata
|
||||||
|
element1 = clash.a
|
||||||
|
element2 = clash.b
|
||||||
|
a_global_id = element1.get_argument(0)
|
||||||
|
b_global_id = element2.get_argument(0)
|
||||||
|
a_ifc_class = element1.is_a()
|
||||||
|
b_ifc_class = element2.is_a()
|
||||||
|
a_name = element1.get_argument(2)
|
||||||
|
b_name = element2.get_argument(2)
|
||||||
|
|
||||||
|
# Potential clash types that can be detected are protrusions, pierces, and collisions
|
||||||
|
clash_type = ["protrusion", "pierce", "collision", "clearance"][clash.clash_type],
|
||||||
|
|
||||||
|
# P1 and P2 represents two XYZ coordinates. The meaning of the coordinate depends on the clash type.
|
||||||
|
p1 = list(clash.p1)
|
||||||
|
p2 = list(clash.p2)
|
||||||
|
|
||||||
|
# This represents the protrusion or piercing distance in meters.
|
||||||
|
# It is also the distance between P1 and P2.
|
||||||
|
distance = clash.distance
|
||||||
|
|
||||||
|
If you specify a ``tolerance`` value, intersections with a protrusion distance
|
||||||
|
smaller than this tolerance are excluded. It is recommended to specify a
|
||||||
|
non-zero tolerance to distinguish between when elements merely touch (e.g. a
|
||||||
|
GPO on a wall) versus if they are truly intersecting (e.g. a pipe going through
|
||||||
|
a beam).
|
||||||
|
|
||||||
|
If ``check_all`` is ``False``, the clash check will return as soon as an
|
||||||
|
intersection is found. This is faster but may not return the worst-case
|
||||||
|
protrusion distance. If you are not interested in the protrusion distance, it
|
||||||
|
is recommended to set this to ``False``. If you want the protrusion distance,
|
||||||
|
such as to prioritise which clashes are more severe, set this to ``True``.
|
||||||
|
|
||||||
|
This includes:
|
||||||
|
|
||||||
|
1. When an element X protrudes inside element Y, where element Y is manifold.
|
||||||
|
In this case, a protrusion distance is calculated as the deepest point of
|
||||||
|
element X to the closest surface of element Y. ``P1`` is defined as the XYZ
|
||||||
|
coordinate on element X, and ``P2`` is defined as the nearest point on the
|
||||||
|
surface of element Y.
|
||||||
|
2. When an element X pierces element Y, such that an edge of element X enters
|
||||||
|
element Y and leaves through another face. In this case, a piercing distance
|
||||||
|
is calculated as the distance where that edge is inside element Y. ``P1`` is
|
||||||
|
defined as the point on an edge of element X which enters element Y, and
|
||||||
|
``P2`` is the point where that edge leaves element Y.
|
||||||
|
3. When neither X or Y is manifold, we cannot detect protrusion or piercing, so
|
||||||
|
instead when X and Y have any touching face. This is the same as the
|
||||||
|
``clash_collision_many`` check below. The distance is considered to be zero
|
||||||
|
and ignores your specified tolerance. ``P1`` and ``P2`` are equal and
|
||||||
|
represent an arbitrary XYZ point where the two elements touch.
|
||||||
|
|
||||||
|
Detecting collision clashes between elements
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
``clash_collision_many`` detects when the surface of an element collides with
|
||||||
|
another element. The surfaces may either merely touch (e.g. are coplanar) or
|
||||||
|
intersect.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
clashes = tree.clash_collision_many(
|
||||||
|
group_a_elements, # e.g. from model.by_type("IfcWall")
|
||||||
|
group_b_elements, # Group b can be the same as group a if you want to clash within a single set
|
||||||
|
allow_touching=True, # Include results where faces merely touch but do not intersect
|
||||||
|
)
|
||||||
|
|
||||||
|
for clash in clashes:
|
||||||
|
# Get the two elements that clash and their metadata
|
||||||
|
element1 = clash.a
|
||||||
|
element2 = clash.b
|
||||||
|
a_global_id = element1.get_argument(0)
|
||||||
|
b_global_id = element2.get_argument(0)
|
||||||
|
a_ifc_class = element1.is_a()
|
||||||
|
b_ifc_class = element2.is_a()
|
||||||
|
a_name = element1.get_argument(2)
|
||||||
|
b_name = element2.get_argument(2)
|
||||||
|
|
||||||
|
# P1 and P2 represents two possible arbitrary points where a collision is found.
|
||||||
|
# P1 may or may not be equal to P2.
|
||||||
|
p1 = list(clash.p1)
|
||||||
|
p2 = list(clash.p2)
|
||||||
|
|
||||||
|
A collision between two surface triangles may be "touching" or "intersecting".
|
||||||
|
Two touching triangles may be coplanar or merely have a single edge or vertex
|
||||||
|
touching the other triangle. An intersecting triangle will have at least one
|
||||||
|
edge that goes through the other triangle.
|
||||||
|
|
||||||
|
Detecting clearance clashes between elements
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
``clash_clearance_many`` detects with the surface of an element comes within a
|
||||||
|
clearance distance threshold of another element.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
clashes = tree.clash_clearance_many(
|
||||||
|
group_a_elements, # e.g. from model.by_type("IfcWall")
|
||||||
|
group_b_elements, # Group b can be the same as group a if you want to clash within a single set
|
||||||
|
clearance=0.1, # Any surface closer than than 100mm is a clash
|
||||||
|
check_all=False, # Stop measuring distances once the first clearance violation is found per element.
|
||||||
|
)
|
||||||
|
|
||||||
|
for clash in clashes:
|
||||||
|
# Get the two elements that clash and their metadata
|
||||||
|
element1 = clash.a
|
||||||
|
element2 = clash.b
|
||||||
|
a_global_id = element1.get_argument(0)
|
||||||
|
b_global_id = element2.get_argument(0)
|
||||||
|
a_ifc_class = element1.is_a()
|
||||||
|
b_ifc_class = element2.is_a()
|
||||||
|
a_name = element1.get_argument(2)
|
||||||
|
b_name = element2.get_argument(2)
|
||||||
|
|
||||||
|
# P1 and P2 represents the two XYZ coordinates between element1 and element2.
|
||||||
|
p1 = list(clash.p1)
|
||||||
|
p2 = list(clash.p2)
|
||||||
|
|
||||||
|
# This represents the distance between element1 and element2 that is less than the clearance.
|
||||||
|
# It is the distance between P1 and P2. It cannot be less than 0.
|
||||||
|
distance = clash.distance
|
||||||
|
|
||||||
|
You cannot specify a ``clearance`` less than 0.
|
||||||
|
|
||||||
|
If ``check_all`` is ``False``, the clash check will return as soon as a
|
||||||
|
clearance violation is found. This is faster but may not return the worst-case
|
||||||
|
distance. If you only interested whether there is a clearance issue, it is
|
||||||
|
recommended to set this to ``False``. If you want the exact worst case
|
||||||
|
clearance distance, such as to prioritise which clashes are more severe, set
|
||||||
|
this to ``True``.
|
||||||
|
|
||||||
Selecting elements using bounding boxes
|
Selecting elements using bounding boxes
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
Elements may be queried using an axis aligned bounding box. An axis aligned
|
||||||
|
bounding box is the bounding box using global XYZ axes, not the element's local
|
||||||
|
XYZ axes. If you have a vertical construction project, this means that your
|
||||||
|
model should be oriented to project north to get the best results.
|
||||||
|
|
||||||
You may select all elements that have a bounding box containing the point with
|
You may select all elements that have a bounding box containing the point with
|
||||||
XYZ coordinates of ``(0., 0., 0.)``.
|
XYZ coordinates of ``(0., 0., 0.)``.
|
||||||
|
|
||||||
@@ -128,11 +311,11 @@ geometry. It will return:
|
|||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
elements = tree.select_box(wall, completely_within=True)
|
elements = tree.select(wall, completely_within=True)
|
||||||
|
|
||||||
# Alternatively, you may also specify an extension to dilate the geometry
|
# Alternatively, you may also specify an extension to dilate the geometry
|
||||||
# of the wall.
|
# of the wall.
|
||||||
elements = tree.select_box(wall, completely_within=True, extend=5.)
|
elements = tree.select(wall, completely_within=True, extend=5.)
|
||||||
|
|
||||||
Selecting elements using a ray
|
Selecting elements using a ray
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user