mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Write docs about new geometry tree clash functions
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
Geometry tree
|
||||
=============
|
||||
|
||||
IfcOpenShell includes a utility to build a unbalanced binary tree of geometry
|
||||
and their bounding boxes. After a tree is built, you can efficiently select
|
||||
geometry by specifying a point, radius, or bounding box.
|
||||
IfcOpenShell includes a utility to build trees of geometry and their bounding
|
||||
boxes. Geometry trees can be used to efficiently select geometry or collide
|
||||
geometry with one another.
|
||||
|
||||
.. image:: images/geometry-tree.png
|
||||
|
||||
The most efficient way to build tree is by using the iterator, as shown in the
|
||||
example below:
|
||||
The most efficient way to build a tree is by using the iterator. If the native
|
||||
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
|
||||
|
||||
@@ -21,27 +23,208 @@ example below:
|
||||
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
|
||||
if iterator.initialize():
|
||||
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():
|
||||
break
|
||||
|
||||
Once built, there are three methods you can use to select elements in the tree:
|
||||
``select_box``, ``select``, and ``select_ray``.
|
||||
Clashing or selecting geometry from a geometry tree
|
||||
---------------------------------------------------
|
||||
|
||||
``select_box`` 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.
|
||||
With a **BVH Tree**, you can efficiently clash sets of elements with other
|
||||
elements. You can find elements that intersect, collide, or are within a
|
||||
clearance distance threshold of one another. There are three methods you can
|
||||
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
|
||||
another element. ``select`` is similar to select box, but additionally
|
||||
considers the actual geometry of the object. This is slower but more precise.
|
||||
- `Detecting intersection clashes between elements`_ detects when an element intersects with another
|
||||
element. This is the most common type of clash detection used when
|
||||
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
|
||||
---------------------------------------
|
||||
|
||||
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
|
||||
XYZ coordinates of ``(0., 0., 0.)``.
|
||||
|
||||
@@ -128,11 +311,11 @@ geometry. It will return:
|
||||
|
||||
.. 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
|
||||
# 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
|
||||
------------------------------
|
||||
|
||||
Reference in New Issue
Block a user