cadquery icon indicating copy to clipboard operation
cadquery copied to clipboard

2D Sweep Along Line

Open utkuunlu opened this issue 3 years ago • 11 comments

Hello,

Is there a way to perform a 2D sweep along a path in CQ ? The resulting geometry should be a shell with zero thickness. Attached is a sample geometry created in FreeCAD and that's what I'm trying to do in CQ. The code below works when the profile geometry is a rectangle or circle but it doesn't work for line (ValueError: No pending wires present).

Thanks,

`import cadquery as cq

plane_path = "XY" pts_path = [(0,0), (100, 100), (100, 200), (200, 400)] path = cq.Workplane(plane_path, origin=pts_path[0]).polyline(pts_path) show_object(path)

plane_profile = "XZ" pts_profile = [(-5,0), (5, 0)] profile = cq.Workplane(plane_profile).polyline(pts_profile)

show_object(profile)

SweepShell = profile.sweep(path, makeSolid=False, isFrenet=True) show_object(SweepShell)`

sweep_along_path

utkuunlu avatar Jan 02 '22 12:01 utkuunlu

The resulting geometry you would get is not a shell but a face (or a set of faces) and as far as I know you cannot do that in CQ.

Jojain avatar Jan 02 '22 12:01 Jojain

Thanks for the answer Jojain, sorry for the naming, I'm from CAE world, so that a shell for us :)

utkuunlu avatar Jan 02 '22 12:01 utkuunlu

@utkuunlu Have a look at the makeSolid argument of sweep. https://cadquery.readthedocs.io/en/latest/classreference.html?highlight=sweep#cadquery.Solid.sweep

jmwright avatar Jan 02 '22 14:01 jmwright

@utkuunlu Have a look at the makeSolid argument of sweep. https://cadquery.readthedocs.io/en/latest/classreference.html?highlight=sweep#cadquery.Solid.sweep

Yes, I tried both makeSolid=False and True. Result is the same (error).

utkuunlu avatar Jan 02 '22 15:01 utkuunlu

Is your profile closed before the sweep? I only had time to take a glance, but with the polyline and two points you might be ending up with a 1D profile. You can set includeCurrent=True on polyline and try calling close afterwards. You could also try replacing the polyline call with rect temporarily as a baseline test to force the profile to be closed.

jmwright avatar Jan 02 '22 15:01 jmwright

@jmwright The problem here is that @utkuunlu wants to sweep a 1d profile which is not possible with the sweep method of CQ. I cannot test here but trying to create a rectangle with 0 height will surely result in an error.

To achieve this another algorithm than than the one used in the sweep method of CQ must be used.

A starting point could be to look there : BRepPrimAPI_MakeSweep Class Reference - Open CASCADE Technology Documentation - https://dev.opencascade.org/doc/refman/html/class_b_rep_prim_a_p_i___make_sweep.html

Jojain avatar Jan 02 '22 15:01 Jojain

Ok, thanks @Jojain . I was in a hurry to answer and didn't properly understand the intent.

jmwright avatar Jan 02 '22 17:01 jmwright

I think OCCT allows this, but it is not exposed [in CQ]. @Jojain https://dev.opencascade.org/doc/refman/html/class_b_rep_offset_a_p_i___make_pipe_shell.html suggests that the profile wire does not need to be closed.

adam-urbanczyk avatar Jan 02 '22 17:01 adam-urbanczyk

Thanks for the answers @adam-urbanczyk , @jmwright , @Jojain. The profile is not closed, the points on the path are generated automatically by another python subroutine.

I just come up with another idea, I'll try to generate the sweep with a thin rectangular profile and will delete unnecessary faces. I'll let you know if that works.

If that idea doesn't work, I guess I have to continue with FreeCAD scripting since I'm not familiar with C++ and OCCT. Thanks anyway.

utkuunlu avatar Jan 02 '22 18:01 utkuunlu

This solution will work but you'll have to take in account that your faces will have an offset compare to the neutral fiber of your solid.

As you may not be aware all the c++ functions are available through python via importing OCP.

So it might be a solution for you (you still need to refer to c++ doc however)

Jojain avatar Jan 02 '22 18:01 Jojain

I was solving this some time ago, because I needed to sweep a lot of complex sufraces with varying cross-section.

In principle, you can create an individual profile for each cross-section, as long as the number of points in the profile polyline stays the same. In gen_list you specify coordinates and plane rotation angle. also you can extend 3D coordinates and 3D rotation if you want. The only problem is that you need to adjust stretching factor of the profile every time you bend it. In this case I used cos(radians(halfangle) multiplier, which should be actually only applied to one axis, not all three as I did, but I am too lazy to fix it.

With minor adjustments you can do the same with closed polylines, then you will make solids with varying cross-sections.

You can also use splines or combination of polylines and splines, but this will be quite computationally intensive.

Hope this helps.

import cadquery as cq
import numpy as np
from math import sin, cos,radians,degrees

def sweep_flat(body_list):
    #this one concentrates edges to wires and then creates a ruled surface
    united=cq.Workplane("XZ")

    for j in range(0,int(len(body_list))):
        wires1=cq.Workplane('XZ').tag('body')
        wires2=cq.Workplane('XZ').tag('body')
        for i in range(0,int(len(body_list[j]))-1):
            
            wires1 = wires1.workplaneFromTagged('body').transformed(offset=cq.Vector((body_list[j][i][0],0,body_list[j][i][1])),rotate=cq.Vector(0,(body_list[j][i][2]),0)).polyline(body_list[j][i][3])
            wires2 = wires2.workplaneFromTagged('body').transformed(offset=cq.Vector((body_list[j][i+1][0],0,body_list[j][i+1][1])),rotate=cq.Vector(0,(body_list[j][i+1][2]),0)).polyline(body_list[j][i+1][3])
            WIRES1=cq.Wire.assembleEdges(wires1.vals())
            WIRES2=cq.Wire.assembleEdges(wires2.vals())
            swept=cq.Face.makeRuledSurface(WIRES1,WIRES2)
            united=united.add(swept)
    united=united.combine()
    return united

profile=np.array([[-1,0],[1,0],[1,1]])
gen_list=[]
gen_list.append([[0,0,90,profile],\
                [5,0,45,profile/cos(radians(45))],\
                [10,10,0,profile]])
    
result=sweep_flat(gen_list)

image

Andy-Valentine avatar Jan 11 '22 12:01 Andy-Valentine