Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thickening surface works with CQ2.1 but crashes with master #1768

Open
bragostin opened this issue Feb 14, 2025 · 10 comments
Open

Thickening surface works with CQ2.1 but crashes with master #1768

bragostin opened this issue Feb 14, 2025 · 10 comments
Labels
bug Something isn't working

Comments

@bragostin
Copy link
Contributor

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:

Image

@bragostin bragostin added the bug Something isn't working label Feb 14, 2025
@jmwright
Copy link
Member

@bragostin It looks like you might have installed via pip. Can you also try with conda to see if you get the same result?

@bragostin
Copy link
Contributor Author

@jmwright yes that's right. Will try with conda.

@bragostin
Copy link
Contributor Author

@jmwright same issue with conda install

@jmwright
Copy link
Member

@bragostin Thanks for confirming.

@lorenzncode
Copy link
Member

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))

@lorenzncode
Copy link
Member

Note also that the Solid.interpPlate is deprecated.

Nevermind, you're calling the Workplane method which uses Face.makeNSidedSurface.

@lorenzncode
Copy link
Member

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.

@bragostin
Copy link
Contributor Author

@lorenzncode maybe something changed in BRepOffset_MakeOffset() on the OCC side.
Thank you for checking, I really love the Free API!

@bragostin
Copy link
Contributor Author

bragostin commented Feb 15, 2025

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:

Image

@bragostin
Copy link
Contributor Author

bragostin commented Feb 17, 2025

Maybe linked to this OCC bug: https://tracker.dev.opencascade.org/view.php?id=33166

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants