I have programmed this function which generates the offset wires of a face. In a nutshell, it is performing the following tasks:
- Generating the offset wires of the outer face wire by performing
offset2D(offset_distance)
- Generating the offset wires of all inner face wires by performing
offset2D(-offset_distance)
- Creating a face from all outer offset wires
- Creating a face from all inner offset wires
- Subtracting the inner face from the outer face and return the resulting wires of the face
def offset_wires(face: Face, offset_distance: float) -> list[Wire]:
outer_wire_offsets: list[Wire] = face.outerWire().offset2D(offset_distance)
inner_wires_offsets: list[Wire] = [offset_wire
for inner_wire in face.innerWires()
for offset_wire in inner_wire.offset2D(-offset_distance)]
outer_faces = [Face.makeFromWires(outer_wire_offset) for outer_wire_offset in outer_wire_offsets]
if not outer_faces:
raise Exception("No outer wires found")
outer_face = outer_faces[0] if len(outer_faces) == 1 else outer_faces[0].fuse(*outer_faces[1:]).clean()
inner_faces = [Face.makeFromWires(inner_wires_offset) for inner_wires_offset in inner_wires_offsets]
if not inner_faces:
resulting_offset_wires: list[Wire] = outer_face.Wires()
else:
inner_face = inner_faces[0] if len(inner_faces) == 1 else inner_faces[0].fuse(*inner_faces[1:]).clean()
resulting_offset_wires: list[Wire] = (outer_face - inner_face).clean().Wires()
return resulting_offset_wires
Creating the offset for the outer wire works fine, but having a circle as the inner wire seems to cause some problems. Not only is it depending on the chosen value for offset_value, it also depends on how the circle is generated. I am using the following shape for testing:
Case 1 - Circle using Edge classmethod
First I am creating the pill shaped outer wire and after that creating a circle using the Edge.makeCircle() method.
def make_geometry() -> Face:
width: float = 4.0
height: float = 10.0
outer_wire: Wire = (Workplane()
.moveTo(width/2, -height/2 + width/ 2)
.vLine(height - width)
.tangentArcPoint((-width, 0.0))
.vLine(-height + width)
.tangentArcPoint((width, 0.0))
.consolidateWires()
).wires().val()
inner_edge: Edge = Edge.makeCircle(width/4, pnt=Vector(0.0, height/2 - width/2, 0), dir=Vector(0, 0, -1), orientation=True)
inner_wire: Wire = Wire.assembleEdges([inner_edge])
return Face.makeFromWires(outer_wire, [inner_wire])
If I apply the offset_wires function with a value of offset_distance < 0 it works as expected. But for offset_distance >=0 a ValueError with the message "Null TopoDS_Shape object" is thrown.
Case 2 - Circle using the Workplane method
In this case, the outer wire is generated in the same way. The inner wire is created by doing
inner_wire: Wire = (Workplane(origin=(0, height/2 - width/2, 0))
.circle(width/4)
).wires().val()
By using a non-negative value for offset_distance CADQuery does not crash but the result mismatches my expectation. The location of the offset circle seems to be changed.
And, for values offset_distance < 0 a ValueError exception is thrown.
If I instead use a rectangle or rectangle with rounded corners for the inner wire, the behavior is as expected again.
Setup
I tried this using both the CQ-Editor (Version 0.6.dev0, Python 3.13.11) and the cadquery package (2.7.0, Python 3.13.13).
Full script for testing:
offset_geometry.py
I have programmed this function which generates the offset wires of a face. In a nutshell, it is performing the following tasks:
offset2D(offset_distance)offset2D(-offset_distance)Creating the offset for the outer wire works fine, but having a circle as the inner wire seems to cause some problems. Not only is it depending on the chosen value for
offset_value, it also depends on how the circle is generated. I am using the following shape for testing:Case 1 - Circle using Edge classmethod
First I am creating the pill shaped outer wire and after that creating a circle using the
Edge.makeCircle()method.If I apply the
offset_wiresfunction with a value ofoffset_distance < 0it works as expected. But foroffset_distance >=0aValueErrorwith the message "Null TopoDS_Shape object" is thrown.Case 2 - Circle using the Workplane method
In this case, the outer wire is generated in the same way. The inner wire is created by doing
By using a non-negative value for
offset_distanceCADQuery does not crash but the result mismatches my expectation. The location of the offset circle seems to be changed.And, for values
offset_distance < 0aValueErrorexception is thrown.If I instead use a rectangle or rectangle with rounded corners for the inner wire, the behavior is as expected again.
Setup
I tried this using both the CQ-Editor (Version
0.6.dev0, Python 3.13.11) and the cadquery package (2.7.0, Python 3.13.13).Full script for testing:
offset_geometry.py