Skip to content

Commit b66bd2a

Browse files
committed
add offset, add step meta data support, small fixes
1 parent bf18811 commit b66bd2a

File tree

6 files changed

+119
-28
lines changed

6 files changed

+119
-28
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
* Added implementation of `compas_occ.brep.OCCBrepFace.__data__`.
2828
* Added implementation of `compas_occ.brep.OCCBrep.__data__`.
2929
* Added implementation of `compas_occ.brep.OCCBrep.__from_data__`.
30+
* Added support for conversion to OCC .brep format with `OCCBrep.to_brep`.
31+
* Added support for writing author, description, ... meta data to STEP files.
32+
* Added `OCCBrep.offset`.
3033

3134
### Changed
3235

36+
* Fixed bug in propagation of linear and angular deflection between discretisation functions `OCCBrep.to_viewmesh` and `OCCBrep.to_tesselation`.
37+
3338
### Removed
3439

40+
* Removed unnecessay instance/type checks in comparison methods of brep components.
3541

3642
## [1.3.0] 2025-03-21
3743

src/compas_occ/brep/brep.py

Lines changed: 111 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Optional
33
from typing import Union
44

5+
from OCC.Core import APIHeaderSection
56
from OCC.Core import Bnd
67
from OCC.Core import BOPAlgo
78
from OCC.Core import BRep
@@ -15,6 +16,7 @@
1516
from OCC.Core import BRepMesh
1617
from OCC.Core import BRepOffsetAPI
1718
from OCC.Core import BRepPrimAPI
19+
from OCC.Core import BRepTools
1820
from OCC.Core import GProp
1921
from OCC.Core import IFSelect
2022
from OCC.Core import IGESControl
@@ -23,6 +25,7 @@
2325
from OCC.Core import ShapeUpgrade
2426
from OCC.Core import STEPControl
2527
from OCC.Core import StlAPI
28+
from OCC.Core import TCollection
2629
from OCC.Core import TopAbs
2730
from OCC.Core import TopExp
2831
from OCC.Core import TopLoc
@@ -97,8 +100,8 @@ class OCCBrep(Brep):
97100
@property
98101
def __data__(self) -> dict:
99102
return {
100-
# "vertices": [vertex.__data__ for vertex in self.vertices],
101-
# "edges": [edge.__data__ for edge in self.edges],
103+
"vertices": [vertex.__data__ for vertex in self.vertices],
104+
"edges": [edge.__data__ for edge in self.edges],
102105
"faces": [face.__data__ for face in self.faces],
103106
}
104107

@@ -131,6 +134,12 @@ def __init__(self) -> None:
131134
self._shells = None
132135
self._solids = None
133136

137+
self._aabb = None
138+
self._obb = None
139+
self._area = None
140+
self._volume = None
141+
self._centroid = None
142+
134143
def copy(self) -> "OCCBrep":
135144
"""Deep-copy this BRep using the native OCC copying mechanism.
136145
@@ -232,11 +241,11 @@ def is_surface(self) -> bool:
232241
@property
233242
def points(self) -> list[Point]:
234243
points = []
235-
seen = []
244+
# seen = []
236245
for vertex in self.vertices:
237-
if any(vertex.is_same(test) for test in seen):
238-
continue
239-
seen.append(vertex)
246+
# if any(vertex.is_same(test) for test in seen):
247+
# continue
248+
# seen.append(vertex)
240249
points.append(vertex.point)
241250
return points
242251

@@ -346,22 +355,28 @@ def frame(self) -> Frame:
346355

347356
@property
348357
def area(self) -> float:
358+
# if self._area is None:
349359
props = GProp.GProp_GProps()
350360
BRepGProp.brepgprop.SurfaceProperties(self.native_brep, props)
351-
return props.Mass()
361+
self._area = props.Mass()
362+
return self._area
352363

353364
@property
354365
def volume(self) -> float:
366+
# if self._volume is None:
355367
props = GProp.GProp_GProps()
356368
BRepGProp.brepgprop.VolumeProperties(self.occ_shape, props)
357-
return props.Mass()
369+
self._volume = props.Mass()
370+
return self._volume
358371

359372
@property
360373
def centroid(self) -> Point:
374+
# if self._centroid is None:
361375
props = GProp.GProp_GProps()
362376
BRepGProp.brepgprop.VolumeProperties(self.occ_shape, props)
363377
pnt = props.CentreOfMass()
364-
return point_to_compas(pnt)
378+
self._centroid = point_to_compas(pnt)
379+
return self._centroid
365380

366381
@property
367382
def aabb(self) -> Box:
@@ -384,7 +399,7 @@ def convex_hull(self) -> Mesh:
384399
# ==============================================================================
385400

386401
@classmethod
387-
def from_step(cls, filename: Union[str, pathlib.Path], solid: bool = True) -> "OCCBrep":
402+
def from_step(cls, filename: Union[str, pathlib.Path], heal: bool = False, solid: bool = False) -> "OCCBrep":
388403
"""
389404
Conctruct a BRep from the data contained in a STEP file.
390405
@@ -400,9 +415,10 @@ def from_step(cls, filename: Union[str, pathlib.Path], solid: bool = True) -> "O
400415
:class:`compas_occ.brep.OCCBrep`
401416
402417
"""
403-
shape = DataExchange.read_step_file(str(filename))
418+
shape = DataExchange.read_step_file(str(filename), verbosity=False)
404419
brep = cls.from_native(shape) # type: ignore
405-
brep.heal()
420+
if heal:
421+
brep.heal()
406422
if solid:
407423
brep.make_solid()
408424
return brep
@@ -431,7 +447,35 @@ def from_iges(cls, filename: Union[str, pathlib.Path], solid: bool = True) -> "O
431447
brep.make_solid()
432448
return brep
433449

434-
def to_step(self, filepath: Union[str, pathlib.Path], schema: str = "AP203", unit: str = "MM") -> None:
450+
def to_brep(
451+
self,
452+
filepath: Union[str, pathlib.Path],
453+
) -> None:
454+
"""
455+
Write the BRep shape to a BREP file.
456+
457+
Parameters
458+
----------
459+
filepath : str | pathlib.Path
460+
Location of the file.
461+
462+
Returns
463+
-------
464+
None
465+
466+
"""
467+
BRepTools.breptools.Write(self.native_brep, str(filepath))
468+
469+
def to_step(
470+
self,
471+
filepath: Union[str, pathlib.Path],
472+
schema: str = "AP203",
473+
unit: str = "MM",
474+
author: Optional[str] = None,
475+
name: Optional[str] = None,
476+
description: Optional[str] = None,
477+
organization: Optional[str] = None,
478+
) -> None:
435479
"""
436480
Write the BRep shape to a STEP file.
437481
@@ -449,10 +493,33 @@ def to_step(self, filepath: Union[str, pathlib.Path], schema: str = "AP203", uni
449493
None
450494
451495
"""
452-
step_writer = STEPControl.STEPControl_Writer()
496+
writer = STEPControl.STEPControl_Writer()
497+
Interface.Interface_Static.SetCVal("write.step.schema", schema)
453498
Interface.Interface_Static.SetCVal("write.step.unit", unit)
454-
step_writer.Transfer(self.occ_shape, STEPControl.STEPControl_StepModelType.STEPControl_AsIs) # type: ignore
455-
status = step_writer.Write(str(filepath))
499+
Interface.Interface_Static.SetCVal("write.step.product.name", name or self.name)
500+
501+
writer.Transfer(self.occ_shape, STEPControl.STEPControl_StepModelType.STEPControl_AsIs) # type: ignore
502+
503+
if author or description or organization:
504+
model = writer.Model()
505+
model.ClearHeader()
506+
507+
header = APIHeaderSection.APIHeaderSection_MakeHeader()
508+
509+
if author:
510+
header.SetAuthorValue(1, TCollection.TCollection_HAsciiString(author))
511+
if organization:
512+
org = Interface.Interface_HArray1OfHAsciiString(1, 1)
513+
org.SetValue(1, TCollection.TCollection_HAsciiString(organization))
514+
if description:
515+
desc = Interface.Interface_HArray1OfHAsciiString(1, 1)
516+
desc.SetValue(1, TCollection.TCollection_HAsciiString(description))
517+
518+
model.AddHeaderEntity(header.FnValue())
519+
model.AddHeaderEntity(header.FsValue())
520+
model.AddHeaderEntity(header.FdValue())
521+
522+
status = writer.Write(str(filepath))
456523
assert status == IFSelect.IFSelect_RetDone, status
457524

458525
def to_stl(
@@ -651,7 +718,12 @@ def from_extrusion(
651718
return brep
652719

653720
@classmethod
654-
def from_loft(cls, curves: list[OCCCurve], start: Optional[Point] = None, end: Optional[Point] = None) -> "OCCBrep":
721+
def from_loft(
722+
cls,
723+
curves: list[OCCCurve],
724+
start: Optional[Point] = None,
725+
end: Optional[Point] = None,
726+
) -> "OCCBrep":
655727
"""Construct a Brep by lofing through a sequence of curves.
656728
657729
Parameters
@@ -1101,7 +1173,7 @@ def from_boolean_union(
11011173
# Converters
11021174
# ==============================================================================
11031175

1104-
def to_tesselation(self, linear_deflection: float = 1, angular_deflection: float = 0.1) -> tuple[Mesh, list[Polyline]]:
1176+
def to_tesselation(self, linear_deflection: Optional[float] = None, angular_deflection: Optional[float] = None) -> tuple[Mesh, list[Polyline]]:
11051177
"""
11061178
Create a tesselation of the shape for visualisation.
11071179
@@ -1117,6 +1189,9 @@ def to_tesselation(self, linear_deflection: float = 1, angular_deflection: float
11171189
tuple[:class:`compas.datastructures.Mesh`, list[:class:`compas.geometry.Polyline`]]
11181190
11191191
"""
1192+
linear_deflection = linear_deflection or TOL.lineardeflection
1193+
angular_deflection = angular_deflection or TOL.angulardeflection
1194+
11201195
BRepMesh.BRepMesh_IncrementalMesh(self.occ_shape, linear_deflection, False, angular_deflection, True)
11211196
bt = BRep.BRep_Tool()
11221197
mesh = Mesh()
@@ -1156,6 +1231,7 @@ def to_tesselation(self, linear_deflection: float = 1, angular_deflection: float
11561231
node = nodes.Value(i)
11571232
points.append(vertices[node - 1])
11581233
polylines.append(Polyline(points))
1234+
# This might not be necssary
11591235
lines = []
11601236
for edge in self.edges:
11611237
if any(edge.is_same(e) for e in seen):
@@ -1954,3 +2030,20 @@ def trimmed(self, plane: compas.geometry.Plane) -> "OCCBrep":
19542030
brep = self.copy()
19552031
brep.trim(plane)
19562032
return brep
2033+
2034+
def offset(self, distance: float) -> "OCCBrep":
2035+
"""Construct a thickened copy of the brep.
2036+
2037+
Parameters
2038+
----------
2039+
distance : float
2040+
The thickness in the form of an offset distance.
2041+
2042+
Returns
2043+
-------
2044+
:class:`OCCBrep`
2045+
2046+
"""
2047+
offset = BRepOffsetAPI.BRepOffsetAPI_MakeThickSolid()
2048+
offset.MakeThickSolidBySimple(self.native_brep, distance)
2049+
return Brep.from_native(offset.Shape())

src/compas_occ/brep/brepedge.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class OCCBrepEdge(BrepEdge):
9191

9292
@property
9393
def __data__(self) -> dict:
94-
raise NotImplementedError
94+
return {"vertices": [self.first_vertex.__data__, self.last_vertex.__data__]}
9595

9696
@classmethod
9797
def __from_data__(cls, data: dict) -> "OCCBrepEdge":
@@ -122,8 +122,6 @@ def is_same(self, other: "OCCBrepEdge") -> bool:
122122
``True`` if the edges are the same, ``False`` otherwise.
123123
124124
"""
125-
if not isinstance(other, OCCBrepEdge):
126-
return False
127125
return self.occ_edge.IsSame(other.occ_edge)
128126

129127
def is_equal(self, other: "OCCBrepEdge") -> bool:

src/compas_occ/brep/brepface.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,6 @@ def is_same(self, other: "OCCBrepFace") -> bool:
183183
``True`` if the faces are the same, ``False`` otherwise.
184184
185185
"""
186-
if not isinstance(other, OCCBrepFace):
187-
return False
188186
return self.occ_face.IsSame(other.occ_face)
189187

190188
def is_equal(self, other: "OCCBrepFace") -> bool:

src/compas_occ/brep/breploop.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ def is_same(self, other: "OCCBrepLoop") -> bool:
8181
``True`` if the loops are the same, ``False`` otherwise.
8282
8383
"""
84-
if not isinstance(other, OCCBrepLoop):
85-
return False
8684
return self.occ_wire.IsSame(other.occ_wire)
8785

8886
def is_equal(self, other: "OCCBrepLoop") -> bool:

src/compas_occ/brep/brepvertex.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class OCCBrepVertex(BrepVertex):
2626

2727
@property
2828
def __data__(self) -> dict:
29-
raise NotImplementedError
29+
return {"point": self.point}
3030

3131
@classmethod
3232
def __from_data__(cls, data: dict) -> "OCCBrepVertex":
@@ -55,8 +55,6 @@ def is_same(self, other: "OCCBrepVertex") -> bool:
5555
``True`` if the vertices are the same, ``False`` otherwise.
5656
5757
"""
58-
if not isinstance(other, OCCBrepVertex):
59-
return False
6058
return self.occ_vertex.IsSame(other.occ_vertex)
6159

6260
def is_equal(self, other: "OCCBrepVertex") -> bool:

0 commit comments

Comments
 (0)