Unable to revolve a face made from splines

61 views
Skip to first unread message

John Otter

unread,
Feb 7, 2024, 10:28:41 AMFeb 7
to CadQuery
Hello, 

Firstly I would like to say how great cadquery is! The API is very good and compared with FreeCAD scripting it a lot neater. The documentation is also very good so I would like to use cadquery in my next project. However, I am having trouble revolving a face that is made from splines. 

I am working with a shape that is defined in a cylindrical co-ordinate system. This shape that is defined as series of constant radius ((x**2+y**2)**0.5) profiles . To create this shape I would usually create a Face from a loft of these profiles. This shape will be non-planar and cannot be defined from primitives or analytically.  Then I would revolve this face about an axis to create a solid. However, I am having trouble doing this in cadquery. I am pretty sure this is a sane thing to do in CAD as I have done it before in FreeCAD. Most of the examples for sweeping and revolving are based on primitives so I get a bit lost trying to apply them to my case.

One problem I saw with my 'real' geometry was that the revolved shape was hollow, i.e. the faces did not enclose. So I decided to make a simple geometry to share here that demonstrated this problem. But, when I tried with a simplified geometry I got a different error! So let us start with the simplified geometry. 

The problem that I am seeing is that when I try and create a Solid it  contains a Compound object rather than a Face. This causes problems as the Solid class method revolve requires a face not a compound. 

I have defined a wire named 'sweep' as I thought I might be able sweep about this line, but ideally I don't want to do that as it is arc so want to revolve to save making this sweep line. 

I am running version '2.4.0.dev0'.

See my code below . 

import numpy as np
import cadquery as cq


#this is a simple example, but basically it is a shape defined by multiple constant radius ((x**2+y**2)**0.5) profiles
#it is non-planar
mid_points =[ [(1,1,0),(1,1,1),(1.2,0.7483,2) ], [(2,2,0),(2,2,1),(2.2,1.7776,2) ]]
N_sections = 2
sweep_angle = np.pi/4


edges = []
for i in range(N_sections):
    pts = [tuple(pt) for pt in mid_points[i]]
    edges.append(cq.Edge.makeSpline([cq.Vector(p) for p in pts]))
   
    if i==0:
        rad = np.sqrt(pts[0][0]**2 + pts[0][1]**2)
       
        t_start = np.arctan(pts[0][1]/pts[0][0])
        t_end = t_start - sweep_angle
   

        for_sweep =[]
        for_sweep.append(cq.Vector(pts[0]))
        for_sweep.append(cq.Vector((rad*np.cos(t_start -0.5*sweep_angle),rad*np.sin(t_start - 0.5*sweep_angle),pts[0][2])))
        for_sweep.append(cq.Vector((rad*np.cos(t_end),rad*np.sin(t_end),pts[0][2])))


my_solid = cq.Solid.makeLoft(
    [cq.Wire.assembleEdges([edge]) for edge in edges]
)

print(my_solid.faces())

sweep = cq.Wire.assembleEdges([cq.Edge.makeSpline(for_sweep)])

show_object(my_solid,name='my_solid')
show_object(sweep,'sweep_line')


As you can see the my_solid object is of type cadquery.occ_impl.shapes.Compound. 

I would really appreciate any help on how to achieve this! 

Thanks in advance,

John 

Lorenz

unread,
Feb 11, 2024, 8:22:14 PMFeb 11
to CadQuery
Hi John,

For now would `Face.makeRuledSurface` work for you?

import cadquery as cq

mid_points = [
    [(1, 1, 0), (1, 1, 1), (1.2, 0.7483, 2)],
    [(2, 2, 0), (2, 2, 1), (2.2, 1.7776, 2)],
]

edge0 = cq.Edge.makeSpline([cq.Vector(p) for p in mid_points[0]])
edge1 = cq.Edge.makeSpline([cq.Vector(p) for p in mid_points[1]])

face0 = cq.Face.makeRuledSurface(edge0, edge1)
solid0 = cq.Solid.revolve(face0, 30, edge0.startPoint(), edge1.startPoint())


Lorenz

John Otter

unread,
Feb 13, 2024, 8:43:39 AMFeb 13
to CadQuery
Hi Lorenz, 

Thank you for sharing, that does work and looks to achieve what I am aiming for. It will allow me to move onto the next step in my project.

However, a ruled surface doesn't fully meet my requirements as I want to have a smooth face. With a ruled surface the linear interpolation between the splines there will be kinks/ non-continuous curvature in my face - something I want to avoid. 

Is there anything fundamentally wrong with what I am trying to do? I feel as though one should be able to make a face through lofting splines. 

Kind regards, 

John 

Adam Urbanczyk

unread,
Feb 17, 2024, 6:16:00 AMFeb 17
to CadQuery
What is actually your question? The compound can be always unpacked with e.g. Faces() or *.

John Otter

unread,
Feb 20, 2024, 4:40:56 AMFeb 20
to CadQuery
My high-level question is : how to revolve a face that is constructed from splines. I can see the steps for how to do this, but in my example I am unable to do so. 

If I unpack the compound and try to invoke the Solid.revolve() function I am faced with an error: 

Solid.revolve() missing 1 required positional argument: 'innerWires'

Which doesn't make sense as the documentation for the Solid.revolve() class method accepts a Face as the first argument so innerWires should not be required : Lorenz's example above calls the calls the class method with a Face as the first argument. However, in Lorenz's example a ruled surface is used to make a Face whereas in my case I make a Solid through Solid.makeLoft. So, from this I deduce that there could be something wrong with the Face object I have created with my splines. So my secondary question is : have I made a mistake in the way I defined the splines for my Face? I cannot use a ruled surface for my application. 

For completeness I have included the example code required to generate the error I am seeing (I am running with  version 2.4.0.dev0')

import numpy as np
import cadquery as cq


#this is a simple example, but basically it is a shape defined by multiple constant radius ((x**2+y**2)**0.5) profiles
mid_points =[ [(1,1,0),(1,1,1),(1.2,0.7483,2) ], [(2,2,0),(2,2,1),(2.2,1.7776,2) ]]
N_sections = 2
sweep_angle = np.pi/4


edges = []
for i in range(N_sections):
    pts = [tuple(pt) for pt in mid_points[i]]
    edges.append(cq.Edge.makeSpline([cq.Vector(p) for p in pts]))
   
    if i==0:
        rad = np.sqrt(pts[0][0]**2 + pts[0][1]**2)
       
        t_start = np.arctan(pts[0][1]/pts[0][0])
        t_end = t_start - sweep_angle
   

        for_sweep =[]
        for_sweep.append(cq.Vector(pts[0]))
        for_sweep.append(cq.Vector((rad*np.cos(t_start -0.5*sweep_angle),rad*np.sin(t_start - 0.5*sweep_angle),pts[0][2])))
        for_sweep.append(cq.Vector((rad*np.cos(t_end),rad*np.sin(t_end),pts[0][2])))


per = cq.Solid.makeLoft(

    [cq.Wire.assembleEdges([edge]) for edge in edges]
)

sweep = cq.Wire.assembleEdges([cq.Edge.makeSpline(for_sweep)])


cq.Solid.revolve(per.faces(),angleDegrees=-45,axisStart=(0,0,0),axisEnd=(0,0,1))



Lorenz Neureuter

unread,
Feb 20, 2024, 8:45:40 PMFeb 20
to John Otter, CadQuery
I had suggested the shapes.Face method because you are interested in a face rather than solid.
face0 = shapes.Face.makeRuledSurface(*[cq.Wire.assembleEdges([edge]) for edge in edges])

shapes.Solid.makeLoft I think was more intended to be used with closed wires for creating a solid.
Theres is a docstring comment:
    "it is presumed that nobody ever actually wants to make an infinitely thin shell "
but that is actually what you want to do.
It might be nice to have a shapes.Face.makeLoft method which calls BRepOffsetAPI_ThruSections with the first argument as False (a version of loft like this already in PR #1469).  Easier to select the face with a single face returned instead of 3 in this case.

With Solid.makeLoft, you can try to select the face and revolve with:
per = shapes.Solid.makeLoft([cq.Wire.assembleEdges([edge]) for edge in edges])
face1 = per.faces(cq.selectors.AreaNthSelector(-1))
res = cq.Solid.revolve(face1, -45, (0, 0, 0), (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 a topic in the Google Groups "CadQuery" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cadquery/B-9w-U3gs1M/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/48b52563-f156-4fac-b29f-638534fce7d0n%40googlegroups.com.

Adam Urbanczyk

unread,
Feb 21, 2024, 2:01:12 AMFeb 21
to CadQuery
So to summarize, there were two issues in the code:

1. You need to pass a Face. faces() returns a compound. Faces() returns a list of Faces.
2. Multi-methods do not dispatch on keyword args. That was rather esoteric issue I'm afraid.

res = cq.Solid.revolve(per.Faces()[0],-45,(0,0,0),(0,0,1))

If you want to more more with faces, maybe this will be of interest: https://github.com/CadQuery/cadquery/pull/1469 No ETA though.

John Otter

unread,
Feb 23, 2024, 3:39:07 PMFeb 23
to CadQuery
Ah, I can't believe I missed that! I swear I tried without the key word arguments before I posted ...... 

Thank you Adam and Lorenz for helping me out!

Thanks for sharing the PR I'll take a look at it too. Is there anything I can do to help with the PR? 

Reply all
Reply to author
Forward
0 new messages