"ValueError: More than one wire is required"

560 views
Skip to first unread message

Aaron E-J

unread,
Jan 25, 2022, 5:30:48 PM1/25/22
to CadQuery

I am trying to model a basic shoe but I am running into challenges figuring out how to use splines to create a 3D surface that I can extrude from the sole to the opening at the top (for the sides of the shoe).  I'm getting an error saying: "ValueError: More than one wire is required" but I am not sure how to make a wire from an edge.  In general, I am looking for examples of using splines as wires that can be extruded or lofted.  I was also looking for example of how to use splines as Bezier curves, with XYZ vertices that each have "handles" you use to change the weight?

soleDim=[(0,0,0),(110,0,0),(120,151.5,0),(110,330,0),(0,330,0),(-85,330,0),(-80,151.5,0),(-85,0,0),(0,0,0)]
openingDim=[(0,0,110),(30,80,110),(8,130,105),(0,160,80),(-8,130,105),(-70,80,110),(0,0,110)]
shoe=(cq.Workplane('XY').spline(soleDim).close().extrude(10).faces('>Z').spline(openingDim).close().loft(combine=True,filled=True))
shoe

Here is a picture of the sole without the opening or the sides (this works, although the place where the beginning and end of the array (0,0,0) meet is messed up)

SoleCadquery2022-01-25-1621.png
And here is a example mock-up I started to do in blender that I am trying to reproduce and make more accurate using CadQuery (note, I was having spline troubles in blender as well):
ShoeBlender2022-01-25-1724.png

Jeremy Wright

unread,
Jan 26, 2022, 7:54:43 AM1/26/22
to CadQuery

spline also has a tangents parameter to give you more control over the curve: https://cadquery.readthedocs.io/en/latest/classreference.html?highlight=spline#cadquery.Workplane.spline

There will be others in the community who will be better at giving advice on how to make more organic shapes like this, but you might want to also look into interpPlate.


Only example of interpPlate with a picture I could find off-hand: https://github.com/CadQuery/cadquery/issues/623#issuecomment-776830603

Adam Urbanczyk

unread,
Jan 26, 2022, 12:39:15 PM1/26/22
to CadQuery
Additionally, you need to add one more spline to pending wires to be able to use loft . You probably meant to add the existing outer wire of the top edge and then create a workplane with a height offset, add your opening and loft between those.

Aaron E-J

unread,
Jan 26, 2022, 3:01:49 PM1/26/22
to CadQuery

Extrude/Loft: I have two splines that I am trying to loft the one from the other.  Apparently the splines are not "wires".  How do you make a loft from two or more, 3D, splines?  I can make 3D line splines, I just don't know how to use it

openingDim=[(0,0,110),(30,80,110),(8,130,105),(0,160,80),(-8,130,105),(-70,80,110),(0,0,110)]
shoe=(cq.Workplane('XY').spline(openingDim).close())
3DLineSplines2022-01-26-1459.png

Aaron E-J

unread,
Jan 26, 2022, 3:02:53 PM1/26/22
to CadQuery

Spline: I saw this example in the "examples" section but I am specifically looking for an example of how to use the tangents and parameters to control the shape of the lines.

Roger Maitland

unread,
Jan 26, 2022, 5:10:11 PM1/26/22
to Aaron E-J, CadQuery
Here is the code to generate your shoe showing what is going on behinds the scenes of the Workplane:
import cadquery as cq

openingDim = [
(0, 0, 110),
(30, 80, 110),
(8, 130, 105),
(0, 160, 80),
(-8, 130, 105),
(-70, 80, 110),
(0, 0, 110),
]
soleDim = [
(0, 0, 0),
(110, 0, 0),
(120, 151.5, 0),
(110, 330, 0),
(0, 330, 0),
(-85, 330, 0),
(-80, 151.5, 0),
(-85, 0, 0),
(0, 0, 0),
]
opening_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingDim])
sole_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleDim])
print(f"{type(opening_edge)=},{type(sole_edge)=}")

shoe = cq.Solid.makeLoft(
[cq.Wire.assembleEdges([sole_edge]), cq.Wire.assembleEdges([opening_edge])]
)
if "show_object" in locals():
show_object(opening_edge, name="opening_edge")
show_object(sole_edge, name="sole_edge")
show_object(shoe, name="shoe")

which generates:
shoe.png
The print returns:
type(opening_edge)=<class 'cadquery.occ_impl.shapes.Edge'>,type(sole_edge)=<class 'cadquery.occ_impl.shapes.Edge'>

So what's going on? This describes the base types. The loft method needs the Wire class but the spline method generates a single Edge so I used the assembleEdges() method to group Edges into a Wire (in this case just one).  Once a pair of Wires are available, they can be used in the loft() method.

With respect to fine tuning the spline, here is the definition of spline():
def makeSpline(
cls,
listOfVector: List[Vector],
tangents: Optional[Sequence[Vector]] = None,
periodic: bool = False,
parameters: Optional[Sequence[float]] = None,
scale: bool = True,
tol: float = 1e-6,
) -> "Edge":
where one can see there is a tangents parameter which can be used to define the start and end tangents. The scale parameter allows the size of these tangents to control the shape of the spline. Multiple splines can be put together into a complex Wire.  Note that there is an Edge.tangentAt() method which allows tangent matching between edges if required.

Once one understands what is going on behind the scenes it's easier to compact the code into a single (?) Workplane call.

Good luck with your shoe.

Cheers,
Roger

--
cadquery home: https://github.com/CadQuery/cadquery
post issues at https://github.com/CadQuery/cadquery/issues
run it at home at : https://github.com/CadQuery/CQ-editor
---
You received this message because you are subscribed to the Google Groups "CadQuery" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cadquery+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cadquery/13989b33-edcd-465a-ba65-f8ef41eece24n%40googlegroups.com.

Aaron E-J

unread,
Jan 27, 2022, 7:47:07 PM1/27/22
to CadQuery

Hi Roger,

Thanks so much!  It would have taken me a long time to figure this out.  The challenge I am facing now is how to add volume to the side of the shoe.  I made an inner and outer wall (see below), but I am stuck on how to span the two together.  Is there a way to use the extrudeLinear function?  I am still stuck on how to go between the API layers to select wires from a solid.

Thanks!

```python
        import cadquery as cq
        from cadquery import *
        openingOuterDim = [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80)]
        openingInnerDim = [(0, 155, 80),(-4, 127, 105),(-65, 80, 110),(0, 5, 110),(25, 80, 110),(4, 127, 105),(0, 155, 80)]
        soleOuterDim = [(0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0),(0, 375, 0)]
        soleInnerDim = [(0, 335, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0),(0, 335, 0)]
        opening_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingOuterDim])
        opening_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingInnerDim])
        sole_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleOuterDim])
        sole_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleInnerDim])
        print(f"{type(opening_outer_edge)=},{type(sole_outer_edge)=}")
        top = cq.Solid.makeLoft(
                [cq.Wire.assembleEdges([sole_outer_edge]), cq.Wire.assembleEdges([opening_outer_edge])]
        )
        top_inside=cq.Solid.makeLoft(
                [cq.Wire.assembleEdges([sole_inner_edge]), cq.Wire.assembleEdges([opening_inner_edge])]
        )
        print(top)
        print(top_inside)
    top_solid=top.extrudeLinear(top, top_inside)#(not right, obviously)
        sole=cq.Workplane('XY').spline(soleOuterDim).close().extrude(-10)
        shoe=(
                Assembly(sole,loc=Location(Vector(0,0,0)),color=Color(.3, .3, .3, 1))
                .add(top_inside,loc=Location(Vector(0,0,0)),color=Color('red'))
                .add(top,loc=Location(Vector(0,0,0)),color=Color('brown'))
        )
        shoe
```
This is what I have so far:
ShoeTryingToLoftCQ2022-01-27-1917.png

Roger Maitland

unread,
Jan 28, 2022, 10:42:20 AM1/28/22
to Aaron E-J, CadQuery
These are non-planar faces which tend to be more tricky to work with. Here are a couple approaches:
  1. My cq_warehouse.extensions package has a Face.thicken() method which creates an offset to the face and makes a solid out of it. As the sides of your shoe is a complex shape that closes on itself I expect that the Opencascade function that implements thicken might have trouble but you'll have to try. If this works, you could make another face for the sole and thicken it (see below for how to make a nonPlanarFace).
  2. You could build a solid from the entire shoe, and then use the shell() method to hollow it out leaving a shell of the given thickness. To build the entire shoe as a solid, you'll need to create faces to enclose the whole thing, including the sole and the foot opening. These faces can be created with my Wire.makeNonPlanarFace() method. Once all the faces are available, use the Shell.makeShell() method with a list of the faces then Solid.makeSolid() method - like this `shoe_solid = cq.Solid.makeSolid(cq.Shell.makeShell(<list of shoe faces>))`.  The shell() method can be passed a face(s) that will be open when the shell is created, which would be the foot opening.
Hopefully this is clear. If not, let me know. Also, if you have trouble or anything isn't clear in cq_warehouse.extensions please let me know.

Cheers,
Roger

Adam Urbanczyk

unread,
Jan 28, 2022, 3:35:19 PM1/28/22
to CadQuery
I would not immediately propose using extensions if CQ has methods that already provide the functionality:


With that being said, you'll be IMHO better off if you start with lofts and using shell. If that does not work try two lofts (2nd being subtractive) and 2d offset on planar wires.

Aaron E-J

unread,
Jan 28, 2022, 6:52:38 PM1/28/22
to CadQuery

Thanks, I think the interPlate is what I need, however I am not sure how to format the lists.  I tried using a list of xyz coordinates for both the first and second parameters, a list of wires for the first and xyz coordinates for the second, and only including the first parameter and leaving the second out (aka: [] ).  They all produced errors that it is not abundantly clear how to resolve them:

All xyz coordinates error:
top = cq.Solid.interpPlate(
         [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80),
          (0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0),(0, 375, 0),],
         [(0, 335, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0),(0, 335, 0),
           (0, 155, 80),(-4, 127, 105),(-65, 80, 110),(0, 5, 110),(25, 80, 110),(4, 127, 105),(0, 155, 80)],
         5
)

---------------------------------------------------------------------------
        StdFail_NotDone                           Traceback (most recent call last)
        <ipython-input-23-81de8825280e> in <module>
                 10 sole_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleInnerDim])
                 11 print(f"{type(opening_outer_edge)=},{type(sole_outer_edge)=}")
        ---> 12 top = cq.Solid.interpPlate(
                 13         # [cq.Wire.assembleEdges([sole_outer_edge]), cq.Wire.assembleEdges([opening_outer_edge])],
                 14         [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80),

        E:\anaconda\envs\python397\lib\site-packages\cadquery\occ_impl\shapes.py in interpPlate(cls, surf_edges, surf_pts, thickness, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments)
           2673         # MAKE SURFACE
           2674         continuity = GeomAbs_C0  # Fixed, changing to anything else crashes.
        -> 2675         face = Face.makeNSidedSurface(
           2676             edges,
           2677             pts_array,

        E:\anaconda\envs\python397\lib\site-packages\cadquery\occ_impl\shapes.py in makeNSidedSurface(cls, edges, points, continuity, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments)
           2207             n_sided.Add(pt)
           2208         n_sided.Build()
        -> 2209         face = n_sided.Shape()
           2210         return Face(face).fix()
           2211

        StdFail_NotDone: BRep_API: command not done

1st wire 2nd xyz error:
top = cq.Solid.interpPlate(
        [cq.Wire.assembleEdges([sole_outer_edge]), cq.Wire.assembleEdges([opening_outer_edge])],
        [(0, 335, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0),(0, 335, 0),
         (0, 155, 80),(-4, 127, 105),(-65, 80, 110),(0, 5, 110),(25, 80, 110),(4, 127, 105),(0, 155, 80)],
        5
)

        ---------------------------------------------------------------------------
        TypeError                                 Traceback (most recent call last)
        <ipython-input-24-4b99b1181a45> in <module>
                 12         # [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80),
                 13         #  (0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0),(0, 375, 0),],
        ---> 14 top = cq.Solid.interpPlate(
                 15         [cq.Wire.assembleEdges([sole_outer_edge]), cq.Wire.assembleEdges([opening_outer_edge])],
                 16         [(0, 335, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0),(0, 335, 0),

        E:\anaconda\envs\python397\lib\site-packages\cadquery\occ_impl\shapes.py in interpPlate(cls, surf_edges, surf_pts, thickness, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments)
           2662         # If a list of (x,y,z) points provided, build closed polygon
           2663         if isinstance(surf_edges, list):
        -> 2664             e_array = [Vector(*e) for e in surf_edges]
           2665             wire_builder = BRepBuilderAPI_MakePolygon()
           2666             for e in e_array:  # Create polygon from edges

        E:\anaconda\envs\python397\lib\site-packages\cadquery\occ_impl\shapes.py in <listcomp>(.0)
           2662         # If a list of (x,y,z) points provided, build closed polygon
           2663         if isinstance(surf_edges, list):
        -> 2664             e_array = [Vector(*e) for e in surf_edges]
           2665             wire_builder = BRepBuilderAPI_MakePolygon()
           2666             for e in e_array:  # Create polygon from edges

        TypeError: cadquery.occ_impl.geom.Vector() argument after * must be an iterable, not Wire

Just 1st xyz error:

top = cq.Solid.interpPlate(
        [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80),
         (0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0),(0, 375, 0),],
        [],
        5
)

        ---------------------------------------------------------------------------
        Standard_NoSuchObject                     Traceback (most recent call last)
        <ipython-input-26-090a4ce16f6d> in <module>
                 14                 # (0, 155, 80),(-4, 127, 105),(-65, 80, 110),(0, 5, 110),(25, 80, 110),(4, 127, 105),(0, 155, 80)],
                 15         # [cq.Wire.assembleEdges([sole_outer_edge]), cq.Wire.assembleEdges([opening_outer_edge])],
        ---> 16 top = cq.Solid.interpPlate(
                 17         [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80),
                 18          (0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0),(0, 375, 0),],

        E:\anaconda\envs\python397\lib\site-packages\cadquery\occ_impl\shapes.py in interpPlate(cls, surf_edges, surf_pts, thickness, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments)
           2673         # MAKE SURFACE
           2674         continuity = GeomAbs_C0  # Fixed, changing to anything else crashes.
        -> 2675         face = Face.makeNSidedSurface(
           2676             edges,
           2677             pts_array,

        E:\anaconda\envs\python397\lib\site-packages\cadquery\occ_impl\shapes.py in makeNSidedSurface(cls, edges, points, continuity, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments)
           2206         for pt in points:
           2207             n_sided.Add(pt)
        -> 2208         n_sided.Build()
           2209         face = n_sided.Shape()
           2210         return Face(face).fix()
        Standard_NoSuchObject: NCollection_BaseList::PRemove


The full code:

        import cadquery as cq
        from cadquery import *
        openingOuterDim = [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80)]
        openingInnerDim = [(0, 155, 80),(-4, 127, 105),(-65, 80, 110),(0, 5, 110),(25, 80, 110),(4, 127, 105),(0, 155, 80)]
        soleOuterDim = [(0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0),(0, 375, 0)]
        soleInnerDim = [(0, 335, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0),(0, 335, 0)]
        opening_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingOuterDim])
        opening_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingInnerDim])
        sole_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleOuterDim])
        sole_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleInnerDim])
        print(f"{type(opening_outer_edge)=},{type(sole_outer_edge)=}")
        top = cq.Solid.interpPlate(
                # [cq.Wire.assembleEdges([sole_outer_edge]), cq.Wire.assembleEdges([opening_outer_edge])],
                [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80),
                 (0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0),(0, 375, 0),],
                [(0, 335, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0),(0, 335, 0),
                 (0, 155, 80),(-4, 127, 105),(-65, 80, 110),(0, 5, 110),(25, 80, 110),(4, 127, 105),(0, 155, 80)],
                5
        )
        print(top)

        sole=cq.Workplane('XY').spline(soleOuterDim).close().extrude(-10)
        shoe=(
                Assembly(sole,loc=Location(Vector(0,0,0)),color=Color(.3, .3, .3, 1))
                .add(top,loc=Location(Vector(0,0,0)),color=Color('brown'))
        )
        shoe

Adam Urbanczyk

unread,
Jan 29, 2022, 6:13:58 AM1/29/22
to CadQuery
Actually my statement above is wrong - current interface of interpPlate and makeSided does not support well your use case involving inner wires. The code below almost works but I had to modify your splines (as-is they resulted in self-intersections) and solve #625 locally (the tol parameter is not implemented for cut in master):

import cadquery as cq
from cadquery import *

openingOuterDim = [(0, 160, 80),(-8, 130, 105),(-70, 80, 110),(0, 0, 110),(30, 80, 110),(8, 130, 105),(0, 160, 80)]
openingInnerDim = [(0, 155, 80),(-4, 127, 105),(-65, 80, 110),(0, 5, 110),(25, 80, 110),(4, 127, 105),(0, 155, 80)]
soleOuterDim = [(0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0)]
soleInnerDim = [(0, 335, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0)]

opening_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingOuterDim][1:-1],periodic=True)
opening_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingInnerDim][1:-1],periodic=True)
sole_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleOuterDim],periodic=True)
sole_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleInnerDim],periodic=True)

fs_outer = cq.Face.makeRuledSurface(opening_outer_edge, sole_outer_edge)
fs_inner = cq.Face.makeRuledSurface(opening_inner_edge, sole_inner_edge)

ft_outer = cq.Face.makeNSidedSurface([opening_outer_edge], [])
ft_inner = cq.Face.makeNSidedSurface([opening_inner_edge], [])

fb_outer = cq.Face.makeNSidedSurface([sole_outer_edge], [])
fb_inner = cq.Face.makeNSidedSurface([sole_inner_edge], [])

so = cq.Solid.makeSolid(cq.Shell.makeShell([fs_outer, ft_outer, fb_outer]))
si = cq.Solid.makeSolid(cq.Shell.makeShell([fs_inner, ft_inner, fb_inner]))

show_object(so.cut(si, tol=1))

Roger Maitland

unread,
Jan 29, 2022, 10:44:23 AM1/29/22
to Adam Urbanczyk, CadQuery
I created makeNonPlanarFace() to work around some of the difficulties with makeNSidedSurface(), specifically:
  • the points parameter takes a list of gp_Pnt which isn't a native cadquery type
  • it doesn't support holes
  • makeNSidedSurface error messages are hard to understand as they are almost always `Stdfail_NotDone` while makeNonPlanarFace extends these exceptions to tell the user which phase of the internal operations causes the problem (exterior edges/wires, surface points, or holes)
Cheers,
Roger

Aaron E-J

unread,
Feb 5, 2022, 9:28:20 AM2/5/22
to cadq...@googlegroups.com
Hi CADQueriers:

I finally got the upper part of the shoe looking reasonable!  I am now working on tread.  I was thinking the way I would make the bottom of the shoe have the ridged appearance is by creating a mask surface that I used to cut out the bottom of the sole.  I am stuck on trying to figure out how to create a Shape object from a 3D closed Wire.  I still am struggling on going from lists, to vectors, to edges, to wires, to faces, to surfaces, to solids and back.  I tried in vain to find applicable functions in the code, but as to yet have been unable to.  Here is what I have:

import cadquery as cq
from cadquery import *
openingOuterDim=[(-12,160,80),(-20, 130,   105),(-70, 80,   110),(0, 0,110),(30, 80,110),(8, 130,105),(0, 160, 80)]
openingInnerDim=[(-7, 155, 75),(-15, 127, 100),(-65, 80, 105),(0, 5, 105),(25, 80, 105),(4, 127, 100),(0, 155, 75)]
holeBottom=     [(0, 90,  0),(-5, 90,   0),(-5, 100,   0),(0,100,   0)]#,(0, 12,   0)]
holeTop=        [(3, 1, 120), (30,30, 120),(10, 180, 120),(-8, 220, 120),(-10, 220, 120),(-25, 160, 120),(-70, 120, 120)]
soleOuterDim=   [(0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0)]
soleInnerDim =  [(0, 370, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0)]
def treadFun(highRidge,lowRidge,spaceHigh,spaceLow):
    w=50;h=400;tread=[cq.Vector((-200,0,highRidge))];
    i=0
    while(i<h):
        i=i+spaceHigh
        tread.append(cq.Vector((-200,i,highRidge)))
        tread.append(cq.Vector((-200,i,lowRidge)))
        i=i+spaceLow
        tread.append(cq.Vector((-200,i,lowRidge)))
        tread.append(cq.Vector((-200,i,highRidge)))
    i=i+spaceHigh
    tread.append(cq.Vector((-200,i,highRidge)))
    tread.append(cq.Vector((200,i,highRidge)))
    i=i-spaceHigh
    while(i>0):
        tread.append(cq.Vector((200,i,highRidge)))
        tread.append(cq.Vector((200,i,lowRidge)))
        i=i-spaceLow
        tread.append(cq.Vector((200,i,lowRidge)))
        tread.append(cq.Vector((200,i,highRidge)))
        i=i-spaceHigh
    tread.append(cq.Vector((200,i,highRidge)))
    treads=[]
    for v in range(len(tread)):
        edg=cq.Edge.makeLine(tread[v],tread[((v+1)%len(tread))])
        treads.append(edg)
    return treads
treadDim=treadFun(-12,-9,25,5)
tread_wire=cq.Wire.assembleEdges(treadDim).close()
tread_wire
opening_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingOuterDim][1:-1],periodic=True,tol=1).close()
opening_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingInnerDim][1:-1],periodic=True,tol=1).close()
sole_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleOuterDim],periodic=True).close()
sole_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleInnerDim],periodic=True).close()
holeBottom_edge = cq.Edge.makeSpline([cq.Vector(p) for p in holeBottom],periodic=True).close()
holeTop_edge = cq.Edge.makeSpline([cq.Vector(p) for p in holeTop],periodic=True).close()
fs_outer = cq.Face.makeRuledSurface(opening_outer_edge, sole_outer_edge)
fs_inner = cq.Face.makeRuledSurface(opening_inner_edge, sole_inner_edge)
holeBottom_face = cq.Face.makeRuledSurface(holeBottom_edge, holeTop_edge)
ft_outer = cq.Face.makeNSidedSurface([opening_outer_edge], [])
ft_inner = cq.Face.makeNSidedSurface([opening_inner_edge], [])
fb_outer = cq.Face.makeNSidedSurface([sole_outer_edge], [])
fb_inner = cq.Face.makeNSidedSurface([sole_inner_edge], [])
holeBottom_nsided=cq.Face.makeNSidedSurface([holeBottom_edge], [])
holeTop_nsided=cq.Face.makeNSidedSurface([holeTop_edge], [])
so = cq.Solid.makeSolid(cq.Shell.makeShell([fs_outer, ft_outer, fb_outer]))
si = cq.Solid.makeSolid(cq.Shell.makeShell([fs_inner, ft_inner, fb_inner]))
hole = cq.Solid.makeSolid(cq.Shell.makeShell([holeBottom_face, holeBottom_nsided, holeTop_nsided]))
top=so.cut(si).clean()
top=top.cut(hole).clean()
soleOuterDim.append((0, 375, 0))
sole=cq.Workplane('XY').spline(soleOuterDim).close().extrude(-12)
sole=sole.cut(tread_wire).clean() #this obviously doesn't work because it is not a solid
shoe=(
    Assembly(sole,loc=Location(Vector(0,0,0)),color=Color(0, 0, 0, 1))
    .add(top,loc=Location(Vector(0,0,0)),color=Color(.3, .1, .1, 1))
)
shoe

Trying to cut as-is obviously does not work because the the tread is still just a Wire:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-25-00979b1c0e38> in <module>
     63 soleOuterDim.append((0, 375, 0))
     64 sole=cq.Workplane('XY').spline(soleOuterDim).close().extrude(-12) #.workplane('XY').polyline(treadDim).close()
---> 65 sole=sole.cut(tread_wire).clean() #this obviously doesn't work because it is not a surface
     66 shoe=(
     67         Assembly(sole,loc=Location(Vector(0,0,0)),color=Color(0, 0, 0, 1))

E:\anaconda\envs\python397\lib\site-packages\cadquery\cq.py in cut(self, toCut, clean)
   3348             solidToCut = (toCut,)
   3349         else:
-> 3350             raise ValueError("Cannot cut type '{}'".format(type(toCut)))
   3351 
   3352         newS = solidRef.cut(*solidToCut)

ValueError: Cannot cut type '<class 'cadquery.occ_impl.shapes.Wire'>'

Output with cut commented out:




Aaron E-J
You received this message because you are subscribed to a topic in the Google Groups "CadQuery" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cadquery/Zk1GuvDjkOE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cadquery+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cadquery/CACPJjBL1ymW7E7b7nYBDF_2OY_e4PjkkbW4FEmHtpXKuguj2Mw%40mail.gmail.com.

Aaron E-J

unread,
Feb 7, 2022, 8:43:23 PM2/7/22
to CadQuery
I am trying to get "cq.Solid.extrudeLinear" to work but I keep getting "DispatchError: ('extrudeLinear: 0 methods found', (<class 'type'>, <class 'cadquery.occ_impl.shapes.Wire'>, <class 'multimethod.<class 'list'>'>), [])"
I think extrudeLinear is what I need to use, but I don't know what the correct input is

```
... Pretty much the same as last post here up ...

tread_wire=cq.Wire.assembleEdges(treadDim).close()

opening_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingOuterDim][1:-1],periodic=True,tol=1).close()
opening_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingInnerDim][1:-1],periodic=True,tol=1).close()
sole_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleOuterDim],periodic=True).close()
sole_wire=cq.Wire.assembleEdges([sole_outer_edge]).close()


sole_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleInnerDim],periodic=True).close()
holeBottom_edge = cq.Edge.makeSpline([cq.Vector(p) for p in holeBottom],periodic=True).close()
holeTop_edge = cq.Edge.makeSpline([cq.Vector(p) for p in holeTop],periodic=True).close()

fs_outer = cq.Face.makeRuledSurface(opening_outer_edge, sole_outer_edge)
fs_inner = cq.Face.makeRuledSurface(opening_inner_edge, sole_inner_edge)
holeBottom_face = cq.Face.makeRuledSurface(holeBottom_edge, holeTop_edge)

ft_outer = cq.Face.makeNSidedSurface([opening_outer_edge], [])
ft_inner = cq.Face.makeNSidedSurface([opening_inner_edge], [])
fb_outer = cq.Face.makeNSidedSurface([sole_outer_edge], [])
fb_inner = cq.Face.makeNSidedSurface([sole_inner_edge], [])#type(sole_inner_edge)type(holeBottom_edge)

holeBottom_nsided=cq.Face.makeNSidedSurface([holeBottom_edge], [])
holeTop_nsided=cq.Face.makeNSidedSurface([holeTop_edge], [])
so = cq.Solid.makeSolid(cq.Shell.makeShell([fs_outer, ft_outer, fb_outer]))
si = cq.Solid.makeSolid(cq.Shell.makeShell([fs_inner, ft_inner, fb_inner]))
hole = cq.Solid.makeSolid(cq.Shell.makeShell([holeBottom_face, holeBottom_nsided, holeTop_nsided]))

tread=cq.Solid.extrudeLinear(sole_wire,[tread_wire])


top=so.cut(si).clean()
top=top.cut(hole).clean()
soleOuterDim.append((0, 375, 0))
sole=cq.Workplane('XY').spline(soleOuterDim).close().extrude(-12) #.workplane('XY').polyline(treadDim).close()
direct=cq.Vector((0,0,1))
tread
sole=sole.cut(tread).clean()
shoe=(
        Assembly(sole,loc=Location(Vector(0,0,0)),color=Color(0, 0, 0, 1))
        .add(top,loc=Location(Vector(0,0,0)),color=Color(.3, .1, .1, 1))
)
shoe
```
Full output of error:
---------------------------------------------------------------------------
DispatchError                             Traceback (most recent call last)
<ipython-input-54-b938acbcfd07> in <module>
     85 hole = cq.Solid.makeSolid(cq.Shell.makeShell([holeBottom_face, holeBottom_nsided, holeTop_nsided]))
     86
---> 87 tread=cq.Solid.extrudeLinear(sole_wire,[tread_wire])
     88
     89 top=so.cut(si).clean()

E:\anaconda\envs\python397\lib\site-packages\multimethod\__init__.py in __call__(self, *args, **kwargs)
    299         if self.pending:  # check first to avoid function call
    300             self.evaluate()
--> 301         func = self[tuple(func(arg) for func, arg in zip(self.type_checkers, args))]
    302         try:
    303             return func(*args, **kwargs)

E:\anaconda\envs\python397\lib\site-packages\multimethod\__init__.py in __missing__(self, types)
    293             return self.setdefault(types, *funcs)
    294         msg = f"{self.__name__}: {len(keys)} methods found"  # type: ignore
--> 295         raise DispatchError(msg, types, keys)
    296
    297     def __call__(self, *args, **kwargs):

DispatchError: ('extrudeLinear: 0 methods found', (<class 'type'>, <class 'cadquery.occ_impl.shapes.Wire'>, <class 'multimethod.<class 'list'>'>), [])

Aaron E-J

unread,
Feb 9, 2022, 12:43:38 PM2/9/22
to CadQuery
Alright, I realized I needed to add a vector to indicate the normal that the solid should be extruded through, but now I am getting "ValueError: Cannot build face(s): wires not planar"
tread=cq.Solid.extrudeLinear(tread_wire,[sole_wire],cq.Vector(0,0,-1))

Roger Maitland

unread,
Feb 9, 2022, 2:00:39 PM2/9/22
to Aaron E-J, CadQuery
extrudeLinear() only works with planar wires (i.e. wires where all of the vertices are one a single plane). The thicken() method of cq_warehouse can "extrude" non planar wires.

Cheers,
Roger

On Wed, Feb 9, 2022 at 12:43 PM Aaron E-J <t...@otherrealm.org> wrote:
Alright, I realized I needed to add a vector to indicate the normal that the solid should be extruded through, but now I am getting "ValueError: Cannot build face(s): wires not planar"
tread=cq.Solid.extrudeLinear(tread_wire,[sole_wire],cq.Vector(0,0,-1))

--
cadquery home: https://github.com/CadQuery/cadquery
post issues at https://github.com/CadQuery/cadquery/issues
run it at home at : https://github.com/CadQuery/CQ-editor
---
You received this message because you are subscribed to the Google Groups "CadQuery" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cadquery+u...@googlegroups.com.

Adam Urbanczyk

unread,
Feb 10, 2022, 1:49:36 AM2/10/22
to CadQuery
That is not correct. If you construct a face (that can be non-planar), then you can use extrudeLinear like so:

f = cq.Workplane().parametricSurface(lambda x,y: (x,y,x**2+y**2))
solid = cq.Solid.extrudeLinear(f.val(),cq.Vector(0,0,.2))

show_object(solid)

You can use cq.Face.makeNSidedSurface to construct a face from a list of edges forming a closed wire.

Roger Maitland

unread,
Feb 10, 2022, 9:54:38 AM2/10/22
to Adam Urbanczyk, CadQuery
Thanks Adam, I didn't know that.

Even with that, thicken and extrude do different things. Thicken creates an offset face along the face normals and fills between the two so the area of the new face can be larger or smaller than the original. Extrude just creates a new face by displacing the original in a single direction and fills between the two.

Sorry if I caused any confusion.

Cheers,
Roger

Aaron E-J

unread,
Feb 10, 2022, 6:53:33 PM2/10/22
to CadQuery
Thanks all, I now have a basic shoe.  I never got the 3D wire to surface method to work, but I reworked the shapes and just lofted the left tread wire from the right tread wire. 
Now is problem is...  it is just one shoe😉 ... Is there any way of mirroring an assembly? Here is my code and what it looks like now:

import cadquery as cq
from cadquery import *
from cq_warehouse import *
from cq_warehouse.extensions import *

openingOuterDim=[(-12,160,80),(-20, 130,   105),(-70, 80,   110),(0, 0,110),(30, 80,110),(8, 130,105),(0, 160, 80)]
openingInnerDim=[(-7, 155, 75),(-15, 127, 100),(-65, 80, 105),(0, 5, 105),(25, 80, 105),(4, 127, 100),(0, 155, 75)]
holeBottom=                [(0, 90,  0),(-5, 90,   0),(-5, 100,   0),(0,100,   0)]#,(0, 12,   0)]
holeTop=                [(3, 1, 120), (30,30, 120),(10, 180, 120),(-8, 220, 120),(-10, 220, 120),(-25, 160, 120),(-70, 120, 120)]#,(-5, 90, 120),(-35, 30,  120)]#,(0, 12, 200)]

soleOuterDim=        [(0, 375, 0),(-85, 330, 0),(-80, 151.5, 0),(-85, 0, 0),(0, 0, 0),(110, 0, 0),(120, 151.5, 0),(110, 330, 0)]
soleInnerDim =        [(0, 370, 0),(-80, 325, 0),(-75, 151.5, 0),(-80, 5, 0),(0, 5, 0),(105, 5, 0),(115, 151.5, 0),(105, 325, 0)]
def treadFun(highRidge,lowRidge,spaceHigh,spaceLow):
        w=50;h=450;

        tread=[cq.Vector((-200,0,highRidge))];
        i=0
        while(i<h):
                i=i+spaceHigh
                tread.append(cq.Vector((-200,i,highRidge)))
                tread.append(cq.Vector((-200,i,lowRidge)))
                i=i+spaceLow
                tread.append(cq.Vector((-200,i,lowRidge)))
                tread.append(cq.Vector((-200,i,highRidge)))
        i=i+spaceHigh
        tread.append(cq.Vector((-200,i,highRidge)))
        treadL=tread;
        tread=[cq.Vector((200,0,highRidge))]
        i=0
        while(i<h):
                i=i+spaceHigh
                tread.append(cq.Vector((200,i,highRidge)))
                tread.append(cq.Vector((200,i,lowRidge)))
                i=i+spaceLow
                tread.append(cq.Vector((200,i,lowRidge)))
                tread.append(cq.Vector((200,i,highRidge)))
        i=i+spaceHigh
        tread.append(cq.Vector((200,i,highRidge)))
        treadR=tread
        treadR.append(cq.Vector((200,i,-15)))
        treadR.append(cq.Vector((200,0,-15)))
        treadL.append(cq.Vector((-200,i,-15)))
        treadL.append(cq.Vector((-200,0,-15)))
        treadsL=[]
        treadsR=[]
        for v in range(len(treadR)):
                edgL=cq.Edge.makeLine(treadL[v],treadL[((v+1)%len(treadL))])
                edgR=cq.Edge.makeLine(treadR[v],treadR[((v+1)%len(treadR))])
                treadsL.append(edgL)
                treadsR.append(edgR)
        return treadL,treadR,treadsL,treadsR
treadVL,treadVR,treadsEdgL,treadsEdgR=treadFun(-9,-12,5,25)

tread_wireL=cq.Wire.assembleEdges(treadsEdgL).close()
tread_wireR=cq.Wire.assembleEdges(treadsEdgR).close()
sole_wire=cq.Wire.assembleEdges([sole_outer_edge]).close()


opening_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingOuterDim][1:-1],periodic=True,tol=1).close()
opening_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in openingInnerDim][1:-1],periodic=True,tol=1).close()
sole_outer_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleOuterDim],periodic=True).close()
sole_inner_edge = cq.Edge.makeSpline([cq.Vector(p) for p in soleInnerDim],periodic=True).close()
holeBottom_edge = cq.Edge.makeSpline([cq.Vector(p) for p in holeBottom],periodic=True).close()
holeTop_edge =    cq.Edge.makeSpline([cq.Vector(p) for p in holeTop],periodic=True).close()

fs_outer =        cq.Face.makeRuledSurface(opening_outer_edge, sole_outer_edge)
fs_inner =        cq.Face.makeRuledSurface(opening_inner_edge, sole_inner_edge)
holeBottom_face = cq.Face.makeRuledSurface(holeBottom_edge, holeTop_edge)

ft_outer = cq.Face.makeNSidedSurface([opening_outer_edge], [])
ft_inner = cq.Face.makeNSidedSurface([opening_inner_edge], [])
fb_outer = cq.Face.makeNSidedSurface([sole_outer_edge], [])
fb_inner = cq.Face.makeNSidedSurface([sole_inner_edge], [])#type(sole_inner_edge)type(holeBottom_edge)
holeBottom_nsided=cq.Face.makeNSidedSurface([holeBottom_edge], [])
holeTop_nsided=cq.Face.makeNSidedSurface([holeTop_edge], [])
so = cq.Solid.makeSolid(cq.Shell.makeShell([fs_outer, ft_outer, fb_outer]))
si = cq.Solid.makeSolid(cq.Shell.makeShell([fs_inner, ft_inner, fb_inner]))
hole = cq.Solid.makeSolid(cq.Shell.makeShell([holeBottom_face, holeBottom_nsided, holeTop_nsided]))
tread=cq.Solid.makeLoft([tread_wireL,tread_wireR],True)


top=so.cut(si).clean()
top=top.cut(hole).clean()
soleOuterDim.append((0, 375, 0))
sole=cq.Workplane('XY').spline(soleOuterDim).close().extrude(-12)
sole=sole.cut(tread).clean()
shoe=(
        Assembly(sole,loc=Location(Vector(0,0,0)),color=Color(.1, .1, .1, 1))
        .add(top,loc=Location(Vector(0,0,0)),color=Color(.4, .1, .1, 1))
)
shoe
ShoeCQ2022-02-10-1850.png
Reply all
Reply to author
Forward
0 new messages