Skip to content

Commit ec1b9f8

Browse files
authored
Merge pull request #1057 from compas-dev/geometry_brep
Geometry brep
2 parents e48757d + 4d5d249 commit ec1b9f8

File tree

22 files changed

+2417
-2
lines changed

22 files changed

+2417
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
* Added `GLTFContent.get_node_by_name`, `GLTFContent.get_material_index_by_name`
1414
* Added `GLTFContent.add_material`, `GLTFContent.add_texture`, `GLTFContent.add_image`
1515

16+
* Added pluggable `Brep` support with `compas.geometry.brep`.
17+
* Added Rhino `Brep` plugin in `compas_rhino.geometry.brep`.
18+
1619
### Changed
1720
* Based all gltf data classes on `BaseGLTFDataClass`
1821

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def patched_parse(self):
192192
self._sections["keys"] = self._parse_keys_section
193193
self._sections["attributes"] = self._parse_attributes_section
194194
self._sections["class attributes"] = self._parse_class_attributes_section
195-
self._sections["others attributes"] = self._parse_other_attributes_section
195+
self._sections["other attributes"] = self._parse_other_attributes_section
196196
self._unpatched_parse()
197197

198198

docs/tutorial.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Tutorial
77

88
tutorial/data
99
tutorial/geometry
10+
tutorial/brep
1011
tutorial/networks
1112
tutorial/meshes
1213
tutorial/volmeshes

docs/tutorial/brep.rst

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
***********************
2+
Boundary Representation
3+
***********************
4+
5+
.. rst-class:: lead
6+
7+
Boundary representation (Brep) support is realized in COMPAS using its `plugin` system.
8+
The expected interface for Brep related classes is defined in the :mod:`compas.geometry.brep` module
9+
whereas the actual implementation is context dependent and implemented using plugins.
10+
11+
.. currentmodule:: compas.geometry
12+
13+
.. highlight:: python
14+
15+
Brep Basics
16+
===========
17+
Brep is a data structure used to describe a shape by means of recording topological and geometrical information of the shape's boundaries.
18+
Some topological properties are associated with an underlying geometry, while others are purely topological.
19+
20+
A Brep is comprised of the following:
21+
22+
.. rst-class:: table table-bordered
23+
24+
.. list-table::
25+
:widths: auto
26+
:header-rows: 1
27+
28+
* - Topology
29+
- Geometry
30+
- Description
31+
* - Vertex
32+
- 3D Point
33+
- The most basic element of a Brep, geometrically described as a point in 3D space.
34+
* - Edge
35+
- 3D Curve
36+
- An edge has a start vertex and an end vertex. The underlying 3D curve describes the geometry of the edge (Line, Circle etc.). Closed edges feature start_vertex == end_vertex.
37+
* - Loop
38+
- None
39+
- A collection of trims which define the inner or outer boundary of a face.
40+
* - Face
41+
- Surface
42+
- Defines the geometry of one of the shape's faces using a surface. Associated with at least one loop which describes the trimmed outer boundary of the surface. Inner loops are referred to as holes in the face.
43+
* - Trim
44+
- 2D Curve
45+
- A 2D curve which trims a face. Trims are associated with a corresponding edge.
46+
47+
48+
Getting Started with COMPAS Brep
49+
================================
50+
51+
To create an empty `Brep`
52+
53+
.. code-block::
54+
55+
>>> from compas.geometry import Brep
56+
>>> brep = Brep()
57+
58+
59+
Notice that the type of the actual instance created by `Brep()` will differ depending on the currently available backend.
60+
For example, when in Rhino
61+
62+
.. code-block::
63+
64+
>>> type(brep)
65+
compas_rhino.geometry.RhinoBrep
66+
67+
Every backend is expected to implement some alternative constructors
68+
69+
.. code-block::
70+
71+
>>> from compas.geometry import Box
72+
>>> from compas.geometry import Brep
73+
>>> ...
74+
>>> box = Box.from_width_height_depth(5., 5., 5.)
75+
>>> brep_box = Brep.from_box(box)
76+
77+
78+
`Brep` can also be instantiated from an instance of a backend native `Brep`
79+
80+
.. code-block::
81+
82+
>>> import Rhino
83+
>>> from compas.geometry import Brep
84+
>>> ...
85+
>>> Brep.from_brep(Rhino.Geometry.Brep())
86+
87+
Brep operations
88+
===============
89+
90+
Trimming a Brep in Grasshopper
91+
92+
.. code-block::
93+
94+
from compas.geometry import Frame
95+
from compas.geometry import Point
96+
from compas.geometry import Brep
97+
98+
box = Box.from_width_height_depth(5, 5, 10)
99+
100+
brep = Brep.from_box(box)
101+
cutting_plane = Frame(Point(0, 2.5, 0), [1, 0, 0], [0, 1, 1.5])
102+
103+
brep.trim(cutting_plane)
104+
105+
|pic1| |pic2|
106+
107+
.. |pic1| image:: files/box_w_plane.png
108+
:width: 48%
109+
110+
.. |pic2| image:: files/trimmed_box.png
111+
:width: 48%
112+
113+
114+
Implementing a new backend
115+
==========================
116+
117+
If you wish to create an additional backend to `Brep` in your package, this can be done using the plugin system of COMPAS.
118+
119+
Create a Brep type in your package which inherits from :class:`compas.geometry.Brep` and override the `__new__` dundle as follows:
120+
121+
.. code-block::
122+
123+
from compas.geometry import Brep
124+
125+
class OccBrep(Brep):
126+
127+
def __new__(cls, *args, **kwargs):
128+
# This breaks the endless recursion when calling `compas.geometry.Brep()` and allows
129+
# having Brep here as the parent class. Otherwise OccBrep() calls Brep.__new__()
130+
# which calls OccBrep() and so on...
131+
return object.__new__(cls, *args, **kwargs)
132+
133+
Whenever instantiating `compas.geometry.Brep`, the actual instantiation is delegated to the available factory plugin
134+
135+
.. code-block::
136+
137+
@plugin(category="factories", requires=["OCC"])
138+
def new_brep(*args, **kwargs):
139+
# Note: this is called inside Brep.__new__, thus Brep.__init__ will be ran by the interpreter
140+
# upon returning from __new__. This means any fully initialized instance returned here will be overwritten!
141+
return object.__new__(OccBrep, *args, **kwargs)
142+
143+
Now, a call to `compas.geometry.Brep()` will result in an instance of `your.package.OccBrep`, given that the plugin is available and loaded.
144+
`OccBrep` encapsulates the native Brep object available by the underlying implementation. If necessary, it can be accessed using `occ_brep_instance.native_brep`.
145+
146+
Implementing the `compas.data.Data` interface
147+
---------------------------------------------
148+
A powerful feature of this approach is to be able to serialize a Brep created in one backend and de-serialize it using another.
149+
For that, it is required that `your.package.OccBrep` implements the :class:`compas.data.Data` interface and follows the unified serialization protocol.
150+
319 KB
Loading
332 KB
Loading

src/compas/data/schemas/brep.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "brep.json",
4+
"$compas": "1.16.0",
5+
"type": "object",
6+
"properties": {
7+
"faces": {
8+
"type": "array",
9+
"minItems": 1,
10+
"items": {}
11+
}
12+
}
13+
}

src/compas/geometry/__init__.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,25 @@
7474
Torus
7575
Polyhedron
7676
77+
Boundary Representation (Brep)
78+
------------------------------
79+
80+
.. autosummary::
81+
:toctree: generated/
82+
:nosignatures:
83+
84+
Brep
85+
BrepVertex
86+
BrepEdge
87+
BrepLoop
88+
BrepFace
89+
90+
BrepType
91+
BrepOrientation
92+
93+
BrepError
94+
BrepInvalidError
95+
BrepTrimmingError
7796
7897
Transformations
7998
===============
@@ -897,6 +916,21 @@
897916
NurbsSurface
898917
)
899918

919+
from .brep import (
920+
Brep,
921+
BrepVertex,
922+
BrepFace,
923+
BrepLoop,
924+
BrepEdge,
925+
926+
BrepType,
927+
BrepOrientation,
928+
929+
BrepError,
930+
BrepInvalidError,
931+
BrepTrimmingError,
932+
)
933+
900934
__all__ = [
901935
'close',
902936
'allclose',
@@ -1201,7 +1235,20 @@
12011235
'NurbsCurve',
12021236

12031237
'Surface',
1204-
'NurbsSurface'
1238+
'NurbsSurface',
1239+
1240+
"Brep",
1241+
"BrepLoop",
1242+
"BrepEdge",
1243+
"BrepVertex",
1244+
"BrepFace",
1245+
1246+
"BrepType",
1247+
"BrepOrientation",
1248+
1249+
"BrepError",
1250+
"BrepInvalidError",
1251+
"BrepTrimmingError",
12051252
]
12061253

12071254
if not compas.IPY:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from .brep import Brep
2+
from .brep import BrepOrientation
3+
from .brep import BrepType
4+
from .edge import BrepEdge
5+
from .loop import BrepLoop
6+
from .face import BrepFace
7+
from .vertex import BrepVertex
8+
9+
10+
class BrepError(Exception):
11+
"""Represents a generic error in the Brep context"""
12+
13+
pass
14+
15+
16+
class BrepInvalidError(BrepError):
17+
"""Raised when the process of re-constructing a Brep has resulted in an invalid Brep"""
18+
19+
pass
20+
21+
22+
class BrepTrimmingError(BrepError):
23+
"""Raised when a trimming operation has failed or had not result"""
24+
25+
pass
26+
27+
28+
__all__ = [
29+
"Brep",
30+
"BrepEdge",
31+
"BrepLoop",
32+
"BrepFace",
33+
"BrepVertex",
34+
"BrepOrientation",
35+
"BrepType",
36+
"BrepError",
37+
"BrepInvalidError",
38+
"BrepTrimmingError",
39+
]

0 commit comments

Comments
 (0)