cadquery
cadquery copied to clipboard
Thickening surface works with CQ2.1 but crashes with master
This code used to work with Cadquery 2.2.0b2 + Python 3.8 (under Debian Bookworm). It was convenient to sew surfaces together and thicken them all at once so that the seams would remain continuous.
import cadquery as cq
from OCP.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin, BRepOffset_RectoVerso
from OCP.GeomAbs import GeomAbs_Intersection, GeomAbs_Arc, GeomAbs_Tangent
def _thicken(self, thickness, join=GeomAbs_Arc):
solid = BRepOffset_MakeOffset()
solid.Initialize(self.wrapped, thickness, 1e-5, BRepOffset_Skin, False, False, join, True)#, True) #The last True is important to make solid
solid.MakeOffsetShape()
return cq.Shape.cast(solid.Shape())
cq.Shell.thicken = _thicken
pts = [ (0, 0, 0), (1, 0, 0), (1, 1, 0) , (0, 1, 0), (0, 0, 0) ]
wires = cq.Wire.makePolygon(listOfVertices=pts, forConstruction=False)
wires = cq.Workplane().newObject([wires])
face_1 = cq.Workplane('XY').interpPlate(wires, [(0.5,0.5,0.5)], 0, degree=2, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=7, maxSegments=9).val()
face_1.exportStep("face_1.stp")
pts = [ (1, 0, 0), (2, 0, 0), (2, 1, 0) , (1, 1, 0), (1, 0, 0) ]
wires = cq.Wire.makePolygon(listOfVertices=pts, forConstruction=False)
wires = cq.Workplane().newObject([wires])
face_2 = cq.Workplane('XY').interpPlate(wires, [(1.5,0.5,-0.5)], 0, degree=2, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=7, maxSegments=9).val()
shells = cq.Shell.makeShell([face_1, face_2])
skin = shells.thicken(0.1)
Now with CQ master and pyton 3.11, it now yields:
File "/home/issue_thicken.py", line 28, in <module>
skin = shells.thicken(0.1)
^^^^^^^^^^^^^^^^^^^
File "/home/issue_thicken.py", line 11, in _thicken
return cq.Shape.cast(solid.Shape())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/cadquery/lib/python3.11/site-packages/cadquery/occ_impl/shapes.py", line 445, in cast
t = shapetype(obj)
^^^^^^^^^^^^^^
File "/home/cadquery/lib/python3.11/site-packages/cadquery/occ_impl/shapes.py", line 367, in shapetype
raise ValueError("Null TopoDS_Shape object")
ValueError: Null TopoDS_Shape object
What might have caused this change?
This is the expected result:
@bragostin It looks like you might have installed via pip. Can you also try with conda to see if you get the same result?
@jmwright yes that's right. Will try with conda.
@jmwright same issue with conda install
@bragostin Thanks for confirming.
I reproduced the error with master and OCP 7.8.1.1.
As a workaround you might try the following. Note also that the Solid.interpPlate is deprecated.
from cadquery.func import *
pts1 = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 0)]
wire1 = polyline(*pts1)
face1 = Face.makeNSidedSurface(wire1, [(0.5, 0.5, 0.5)])
pts2 = [(1, 0, 0), (2, 0, 0), (2, 1, 0), (1, 1, 0), (1, 0, 0)]
wire2 = polyline(*pts2)
face2 = Face.makeNSidedSurface(wire2, [(1.5, 0.5, -0.5)])
shell1 = shell(face1, face2)
skin1 = offset(shell1, 0.1)
print(check(skin1))
Note also that the Solid.interpPlate is deprecated.
Nevermind, you're calling the Workplane method which uses Face.makeNSidedSurface.
The original code works after changing with degree=2 to degree=3 (degree=2 also results in error in the free func version).
I don't think it's a CQ issue.
@lorenzncode maybe something changed in BRepOffset_MakeOffset() on the OCC side.
Thank you for checking, I really love the Free API!
A more representative example, for reference, creating thick Schwarz-D surfaces with degree=3, nbPtsOnCur=20, that used to work with CQ2.1 but not anymore with Master:
# Works with Cadquery 2.2.0b2 + Python 3.8 but not with master
# Already not working with Cadquery 2.3.1 + Python 3.10
import cadquery as cq
from OCP.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin, BRepOffset_RectoVerso
from OCP.GeomAbs import GeomAbs_Intersection, GeomAbs_Arc, GeomAbs_Tangent
def _thicken(self, thickness, join=GeomAbs_Arc):
solid = BRepOffset_MakeOffset()
solid.Initialize(self.wrapped, thickness, 1e-5, BRepOffset_Skin, False, False, join, True)#, True) #The last True is important to make solid
solid.MakeOffsetShape()
return cq.Shape.cast(solid.Shape())
cq.Shell.thicken = _thicken
pts = (
[[-5. , 4.6875, 4.5 ], [-5. , 4.6875, -4.5 ], [ 5. , 4.6875, -4.5 ],
[ 5. , -4.6875, -4.5 ], [ 5. , -4.6875, 4.5 ], [-5. , -4.6875, 4.5 ],
[-5. , 4.6875, 4.5 ]]
)
wires = cq.Workplane().polyline(pts)
face_0 = cq.Workplane('XY').interpPlate(wires, [(0,0,0)], 0, degree=3, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9)
bb = face_0.val().BoundingBox()
xmin, xmax, ymin, ymax, zmin, zmax = bb.xmin, bb.xmax, bb.ymin, bb.ymax, bb.zmin, bb.zmax
face_1 = face_0.translate((0,0,0))
face_1 = face_1.add( face_0.translate((-(xmax-xmin), -(ymax-ymin), 0)).rotate((0,0,0),(0,0,1), 180) )
face_1 = face_1.add( face_0.translate((-(xmax-xmin), 0, (zmax-zmin))).rotate((0,0,0),(0,1,0), 180) )
face_1 = face_1.add( face_0.translate((0, -(ymax-ymin), (zmax-zmin))).rotate((0,0,0),(1,0,0), 180) )
shell = cq.Shell.makeShell(face_1.vals())
shell.exportStep("shell_CQ_2.1.stp")
skin = shell.thicken(1)
skin.exportStep("skin_CQ_2.1.stp")
# Re-written with Free Function API : does not work with Cadquery Master + Python 3.11
import cadquery as cq
from cadquery.func import *
from OCP.GeomAbs import GeomAbs_C0
pts = (
[[-5. , 4.6875, 4.5 ], [-5. , 4.6875, -4.5 ], [ 5. , 4.6875, -4.5 ],
[ 5. , -4.6875, -4.5 ], [ 5. , -4.6875, 4.5 ], [-5. , -4.6875, 4.5 ],
[-5. , 4.6875, 4.5 ]]
)
wires = polyline(*pts)
face_0 = Face.makeNSidedSurface(edges=wires, constraints=[(0,0,0)], continuity=GeomAbs_C0, degree=3, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=1e-05, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9)
bb = face_0.BoundingBox()
xmin, xmax, ymin, ymax, zmin, zmax = bb.xmin, bb.xmax, bb.ymin, bb.ymax, bb.zmin, bb.zmax
face_1 = face_0.moved((0,0,0))
face_1 = face_1 + face_0.moved((-(xmax-xmin), -(ymax-ymin), 0)).rotate((0,0,0),(0,0,1), 180)
face_1 = face_1 + face_0.moved((-(xmax-xmin), 0, (zmax-zmin))).rotate((0,0,0),(0,1,0), 180)
face_1 = face_1 + face_0.moved((0, -(ymax-ymin), (zmax-zmin))).rotate((0,0,0),(1,0,0), 180)
shell_1 = shell(face_1)
shell_1.exportStep("shell_CQ_Master.stp")
skin_1 = offset(shell_1, 1)
print(check(skin_1))
skin_1.exportStep("skin_CQ_Master.stp")
Expected result:
Maybe linked to this OCC bug: https://tracker.dev.opencascade.org/view.php?id=33166