Skip to content
This repository has been archived by the owner on Feb 9, 2021. It is now read-only.

Feature Request: Sweep Operation #33

Closed
jmwright opened this issue Aug 13, 2014 · 32 comments
Closed

Feature Request: Sweep Operation #33

jmwright opened this issue Aug 13, 2014 · 32 comments

Comments

@jmwright
Copy link
Collaborator

The source code makes note that this functionality might be added to the extrude function at some point. While I don't see this being as important as the revolve operation, I think it's something that will be needed.

I'll need to think a little bit about how this could most cleanly be merged with the extrude function.

@jmwright
Copy link
Collaborator Author

This is the next feature in my sights. FreeCAD has separate sweep and extrude operations, but there's that note in the CQ source about extending the extrude operation to extrude along a path. This is a pretty significant philosophical/design difference. Is there one of those ways that would be more CQ-ic?

@dcowden
Copy link
Owner

dcowden commented Oct 22, 2014

hah! funny you ask this. This is one area i explored quite deeply, and
ultimately discovered that FreeCAD is a limitation i could no longer live
with.

The use case I was working on was creating a herringbone gear:

to create this geometry, you need to extrude over a piecewise linear path,
but you also need to rotate the cross section through an angle as you
sweep. This problem, it turned out, was only possible to solve in freecad
by dropping down to first principles-- creating the faces manually, and
then sewing them back together aftwards. It worked, but it was horribly
slow.

This is definitely a situation where, though the actual operation you do is
the same, the user perceives them to be different operations, and i think
that's ok. For example, most users will think of "extrude" as simply
sweeping it along a linear spine in the direction normal to the work plane.
They will associate "sweep" as moving a face over a general curve, while
adjusting the orientation of the face so that the face normal is aligned
with the curve tangent vector as it moves. These are, in fact, the same
operation. If you add the ability to twist through an angle as well, you
have a single, general-purpose function that is really extrude+sweep+twist
all together.

I think it woudl be most CQ-esque to implement this as a single, general
funciton, but then provide fluent shortcuts that map to what the user
expects. for example, suppose we have some function like:

 extrudeSweepTwist(face, curve, rotateThroughAngle )

in this case, curve coudl be a line or a spline or whatever.

in cq, we would still want .extrude( distance, rotateAngle=0.0 ) which
simplifies things to a conceptual distance and angle.
this is CQ-esque because it boils the operation down to something a user
can just specify numeric values for.

we also probably want .sweep(curve,rotateAngle=0.0 ), which is conceptually
different because now i'm specifying a curve to sweep over.

On Tue, Oct 21, 2014 at 9:14 PM, Jeremy Wright notifications@github.com
wrote:

This is the next feature in my sights. FreeCAD has separate sweep and
extrude operations, but there's that note in the CQ source about extending
the extrude operation to extrude along a path. This is a pretty significant
philosophical/design difference. Is there one of those ways that would be
more CQ-ic?


Reply to this email directly or view it on GitHub
#33 (comment).

@jmwright
Copy link
Collaborator Author

In the source, the current extrude implementation doesn't take a rotation angle.

def extrude(self,distance,combine=True):

There's also a twistExtrude function.

def twistExtrude(self,distance,angleDegrees,combine=True):

If I'm understanding you correctly, we would implement the extrudeSweepTwist function and then combine the extrude and twistExtrude functions.

def extrude(self, distance, angleDegrees=0.0, combine=True):

That would not break any of the examples that currently use the extrude function, but would add the necessary functionality to do the twist.

Then there would also be sweep, which as you mentioned would take a curve. Is there any way to use the forConstruction boolean to avoid having to pass a curve to the function? Just taking a stab at it, some thing resembling the rect example here
So...

s = Workplane().spline(pnts, forConstruction=True).curve().circle(0.25)

That's a rough stab at it that won't stand up under much scrutiny, but hopefully it gets my line of thinking across. It seems very CQ-esque to have some form that works that way if possible.

@dcowden
Copy link
Owner

dcowden commented Oct 24, 2014

if memory serves, i think twistExtrude might have some problems. i'd first
make sure it works. I had problem using it to do a gear, i think, but i
never figured out whether the problems were with the twisting, or with the
complex geometry of the gear involutes.

i like the direction you're heading with constructing the spline to avoid a
function parameter. its definitely easier to conceptualize written that
way. one issue, though, is that a sweep curve will normally be a 3d curve
in space that does not lie in the workplane. so though it is nice to have
the spline constructed separately, its doesnt make a lot of sense to be
constructed on the workplane-- which the user takes as a fundamentally 2d
construct for most operations ( at least that's the point). of course a
workplane is still appropriate for creating the circle that we will sweep
along the curve.

i guess it is kind of true that the curve starts on the workplane though...

On Thu, Oct 23, 2014 at 9:57 PM, Jeremy Wright notifications@github.com
wrote:

In the source, the current extrude implementation doesn't take a rotation
angle.

def extrude(self,distance,combine=True):

There's also a twistExtrude function.

def twistExtrude(self,distance,angleDegrees,combine=True):

If I'm understanding you correctly, we would implement the
extrudeSweepTwist function and then combine the extrude and twistExtrude
functions.

def extrude(self, distance, angleDegrees=0.0, combine=True):

That would not break any of the examples that currently use the extrude
function, but would add the necessary functionality to do the twist.

Then there would also be sweep, which as you mentioned would take a curve.
Is there any way to use the forConstruction boolean to avoid having to pass
a curve to the function? Just taking a stab at it, some thing resembling
the rect example here
http://parametricparts.com/docs/classreference.html?highlight=rect#cadfile.cadutils.cadquery.Workplane.rect
So...

s = Workplane().spline(pnts, forConstruction=True).curve().circle(0.25)

That's a rough stab at it that won't stand up under much scrutiny, but
hopefully it gets my line of thinking across. It seems very CQ-esque to
have some form that works that way if possible.


Reply to this email directly or view it on GitHub
#33 (comment).

@jmwright
Copy link
Collaborator Author

  1. Is it possible to define a wire (or set of wires) in 3 dimensions with CQ?
  2. Can a single wire be a spline?
  3. What about modifying functions like spline and lineTo so that if they're passed 3 tuples they recognize that and act accordingly in 3 dimensions? Would that be completely out of line with how CQ is designed?

@dcowden
Copy link
Owner

dcowden commented Oct 25, 2014

CQ doesnt currently support creating wires in 3d space, but it should be
easy to add.

A wire need not be closed. Here's how the topology formally works:

A curve is a geometric construct. curves include splines, circles, lines,
etc.
An edge is a portion of a curve that represents part of an object
definition.
A wire is a collection of more than one edge, in a particular order
A face is one or more wires, that form a closed loop

So you would have a wire that has an edge that uses some or all of a spline
as its basis curve.

Regarding sweeping, i do not actually know offhand whether you'd need a
wire to do the sweep. you might just need a curve.

Finally, regarding 3d operations, when the user is working with a
workplane, the intent is to allow the user to "let go" of 3d space and 3d
orientation, and think in much easier 2d space. I think your suggestion
would be something like this:

         wp =

Workplane("XZ").circle(0.2).spline((0,.4,2),(0,4,3)...).sweep(shell=True)
#sweep it. Shell=True means do we want to create a "tube" or a solid

this is simple and clean syntactically. And, it has the advantage that the
starting point of the spline is automatically inside and on the same plane
as the curve we are sweeping.
The main concern i have is how the user interprets the 3 axes of the tuple.

Notice that the example i used is using XZ as the workplane. That means
that for 2-tuple operations, like lineTo, the first two elements will be
coordinates in X and Z, not X and Y. (conceptually, CQ will always orient
the local X axis of a 2-d plane in the same direction as global X. So you
can imagine the XZ plane as X to the right, Z up, and Y pointing "into" the
page.

By extension, a three-tuple in this context would be (x,z,y), not (x,y,z).
I think thats confusing. Its certainly not obvious.

One way we could solve this is to add a CQ operation that allows the user
to explicitly switch to 3d coordinates, while retaining the same origin.
something like this perhaps:

         wp =

Workplane("XZ").circle(0.2).globalCoords().spline((0,.4,2),(0,4,3)...).sweep(shell=True)

in this example, after the call to globalCoords(), 3-tuples are x-y-z, not
in the local space of the Workplane. But it is weird because if other
operations are done after the sweep, what coordinate system are we in then?
Users will probably expect to be sitting on the workplane at the definition
of the original workplane, even after the sweep finishes.

On Fri, Oct 24, 2014 at 11:28 PM, Jeremy Wright notifications@github.com
wrote:

  1. Is it possible to define a wire (or set of wires) in 3 dimensions
    with CQ?
  2. Can a single wire be a spline?
  3. What about modifying functions like spline and lineTo so that if
    they're passed 3 tuples they recognize that and act accordingly in 3
    dimensions? Would that be completely out of line with how CQ is designed?


Reply to this email directly or view it on GitHub
#33 (comment).

@jmwright
Copy link
Collaborator Author

It sounds to me like we should start with defining a "curve" (or set of curves) in 3D space. We can expand from there to wires if the techniques we try work well. If we decided to add this functionality I would probably treat it as a separate feature request that supports the Sweep request.

Above, you gave the following example form.

wp = Workplane("XZ").circle(0.2).spline((0,.4,2),(0,4,3)...).sweep(shell=True)

Would you want to use a shell boolean that way, or control shelling by allowing the user to draw an inner outline to define the wall? If we did use the shell boolean, wouldn't the user also need to define a wall thickness? Also, should the order of the circle and spline be reversed, with a forConstruction boolean on the spline? That way it would be consistent with the way things seem to currently work. This is from one of the CQ examples:

result = cadquery.Workplane(cadquery.Plane.XY()).box(4, 2, 0.5).faces(">Z").workplane().rect(3.5, 1.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82.0, depth=None)

So maybe...

wp = Workplane("XZ").spline((0,.4,2),(0,4,3)..., forConstruction=True).circle(0.2).sweep(shell=True, wallThickness=1.0)

Regarding the user having to shift their frame of reference in 3 dimensions:

wp = Workplane("XZ").circle(0.2).globalCoords().spline((0,.4,2),(0,4,3)...).sweep(shell=True) 

It seems like it would be reasonable to expect more of the user if they want a more advanced operation like this. The first form above (without globalCoords) would be the most intuitive to me if I was working with the operation (I think). You just have to look at the first two letters in your workplane designation to get your bearings. The user already has to keep track of the fact that Workplane("XZ").lineTo(0,1) will draw a line straight up the Z axis from the origin. I know that simply adding that 3rd dimension causes a lot of people problems, but to be fair, they are attempting to do something beyond what I think most people will try (a sweep operation in 3 dimensions).

There's more discussion to be had on what the right answer is, but I'm leaning towards my modification of your first form (with spline first and the thickness added for the shell).

@dcowden
Copy link
Owner

dcowden commented Oct 28, 2014

wp = Workplane("XZ").spline((0,.4,2),(0,4,3)...,

forConstruction=True).circle(0.2).sweep(shell
=True, wallThickness=1.0)

wallThickness would be needed for a shell. there's also, now that you
mention it, the direction-- do you shell inwards or outwards. I think we
need to check on the pythonOcc and/or FreeCAD apis for sweeping-- no sense
coming up with options we cannot support. our api needs to do it however
theirs does.

the order for the spline and circle are tricky. the question becomes: what
is the active workplane and its orientation after creating the spline? With
the sample above, we're making the assumption that the spline command does
not change the work plane, or its origin, correct?

Regarding the user having to shift their frame of reference in 3 dimensions:

wp =
Workplane("XZ").circle(0.2).globalCoords().spline((0,.4,2),(0,4,3)...).sweep(shell=True)

It seems like it would be reasonable to expect more of the user if they
want a more advanced operation like this. The first form above (without
globalCoords) would be the most intuitive to me if I was working with the
operation (I think). You just have to look at the first two letters in your
workplane designation to get your bearings. The user already has to keep
track of the fact that Workplane("XZ").lineTo(0,1) will draw a line
straight up the Z axis from the origin. I know that simply adding that 3rd
dimension causes a lot of people problems, but to be fair, they are
attempting to do something beyond what I think most people will try (a
sweep operation in 3 dimensions).

yes you are right. One other thing to consider here is that, in this case,
you really cannot do this operation in 2d only. the curve you are
sweeping over could not lie in the same plane as the x-section you are
sweeping, so this is an inherently 3d operation.

Here's one other datapoint to consider-- Solidworks. I use Solidworks a
lot, and its the 'cadillac' of 3d parametric modelling software. So an
interesting question is: how do they solve this problem?

In Solidworks ( albeit a very dated version i got as a student), they have
two different notions of sketches. 2-d sketches lie in a plane, like a CQ
workplane. But to sweep, you define the sweep curve in something called a
"3d sketch". a 3d sketch is inherently 3d. there is no attempt to blend
its creation with a 2d operation.

In practice, this separation is quite helpful. I use 2-d coordinates for my
cross section, and then my mental context shifts to 3d coordinates for the
creation of the sweep curve. Then, i do a separate operation in which i
pick the sketch to sweep and the 3d sketch that represents the sweep
curve. So in CQ that's more like this form:

spine = 3dSketch().spline((0,0,0),(0,.1,.2)....)

sketch = Workplane("XY").circle(0.2).sweep(spine)

though this is more typing, im beginning to think that pounding all of this
into a single fluent step is not going to work well. Workplanes are
designed to have the user thinking in "2.5d". I think its more confusing
than helpful to do a bunch of 3d stuff inside of a 2d thought process. Its
one more step but i think its a much more complex operation.

on top of that, i think there's the possibility that the person wants to
use a separate 2d context to create the spine. For example:

spine = Workplane("YZ").arcTo((0,2)).lineTo(2,2))

sketch = Workplane("XY").circle(0.2).sweep(spine)

in this case, i was able to work in 2d both times-- but in different
workplanes. I think separating them gives me the flexibilty to set up a
different "graphical context" for each operation. This is really not
possible in the one-line form.

On Tue, Oct 28, 2014 at 10:58 AM, Jeremy Wright notifications@github.com
wrote:

It sounds to me like we should start with defining a "curve" (or set of
curves) in 3D space. We can expand from there to wires if the techniques we
try work well. If we decided to add this functionality I would probably
treat it as a separate feature request that supports the Sweep request.

Above, you gave the following example form.

wp = Workplane("XZ").circle(0.2).spline((0,.4,2),(0,4,3)...).sweep(shell=True)

Would you want to use a shell boolean that way, or control shelling by
allowing the user to draw an inner outline to define the wall? If we did
use the shell boolean, wouldn't the user also need to define a wall
thickness? Also, should the order of the circle and spline be reversed,
with a forConstruction boolean on the spline? That way it would be
consistent with the way things seem to currently work. This is from one of
the CQ examples:

result = cadquery.Workplane(cadquery.Plane.XY()).box(4, 2, 0.5).faces(">Z").workplane().rect(3.5, 1.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82.0, depth=None)

So maybe...

wp = Workplane("XZ").spline((0,.4,2),(0,4,3)..., forConstruction=True).circle(0.2).sweep(shell
=True, wallThickness=1.0)

Regarding the user having to shift their frame of reference in 3
dimensions:

wp = Workplane("XZ").circle(0.2).globalCoords().spline((0,.4,2),(0,4,3)...).sweep(shell=True)

It seems like it would be reasonable to expect more of the user if they
want a more advanced operation like this. The first form above (without
globalCoords) would be the most intuitive to me if I was working with the
operation (I think). You just have to look at the first two letters in your
workplane designation to get your bearings. The user already has to keep
track of the fact that Workplane("XZ").lineTo(0,1) will draw a line
straight up the Z axis from the origin. I know that simply adding that 3rd
dimension causes a lot of people problems, but to be fair, they are
attempting to do something beyond what I think most people will try (a
sweep operation in 3 dimensions).

There's more discussion to be had on what the right answer is, but I'm
leaning towards my modification of your first form (with spline first and
the thickness added for the shell).


Reply to this email directly or view it on GitHub
#33 (comment).

@jmwright
Copy link
Collaborator Author

I think we need to check on the pythonOcc and/or FreeCAD apis for sweeping-- no sense coming up with options we cannot support. our api needs to do it however theirs does.

Yes, good point.

With the sample above, we're making the assumption that the spline command does not change the work plane, or its origin, correct?

Correct, I was thinking that the spline call would not change anything. Everything would stay grounded in/on the workplane. It seems like that would give the user a safe haven to always retreat to when things got confusing. They could always count on the workplane being the stable basis for the coordinate system and origin.

@eudoxos
Copy link

eudoxos commented Nov 24, 2014

Is sweeping going to make possible something like this? That would be cool.

  1. define a curve in 3d, with straight segments and circular arcs between them (not that I have an idea how to do it now with CQ)
  2. sweep annulus to get pipeline, or circle to get circular rod.

@dcowden
Copy link
Owner

dcowden commented Nov 24, 2014

yes those both describe sweeping operations. for use case #1, splines
would also be common/typical.

there are also a couple of other common parameters with a sweep operation:

(1) twist. sometimes, you would like to "twist" the profile being swept as
it goes along the spine. this is typically expressed as a rotation angle
along the length of the spine. An example would be creating gear with a
pressure angle

(2). tube/solid. this normally controls whether you get a solid or a tube.

(3) shell thickness. if you want a tube, you also normally specify a
thickness of the shell.

On Mon, Nov 24, 2014 at 6:19 AM, eudoxos notifications@github.com wrote:

Is sweeping going to make possible something like this? That would be cool.

  1. define a curve in 3d, with straight segments and circular arcs
    between them (not that I have an idea how to do it now with CQ)
  2. sweep annulus to get pipeline, or circle to get circular rod.


Reply to this email directly or view it on GitHub
#33 (comment).

@jmwright
Copy link
Collaborator Author

The following FreeCAD script will give a solid coil, which I'm using as an approximation of a spring in an assembly. It gives a reference of how CQ might handle sweeping a pipe or coil under the hood. We can already make a helix in CQ and the section to sweep. What we lack is the ability to sweep the section over the path.

import Part
from FreeCAD import Base

helix = Part.makeHelix(1.016, 8.89, 10.795, 0.0)

section = Part.Wire(Part.makeCircle(0.4445, Base.Vector(10.795, 0, 0), Base.Vector(0, 1, 0)))

makeSolid = True
isFrenet = True

pipe = Part.Wire(helix).makePipeShell([section], makeSolid, isFrenet)

Part.show(pipe)

It's also interesting to note that this script runs in the CQ module for FreeCAD without modification, and prevents me from having to run it in the stock scripting window or in the Python console. Any valid Python should run fine in the module because of how it executes the code.

@jmwright
Copy link
Collaborator Author

Here's a resource that I used to figure out the code above.

@jmwright
Copy link
Collaborator Author

jmwright commented Apr 5, 2016

On things like this I normally start hacking around with FreeCAD scripting in the CadQuery FreeCAD module, and then try to figure out how to make that mesh with how we want it to work in CadQuery. Below are a couple of my experiments. I've been trying to figure out how to set shell wall thickness, but everything I've tried so far ends up crashing FreeCAD.

Straight wire path and a solid result:

import Part
from FreeCAD import Base

l=Part.Line()
l.StartPoint=(0.0,0.0,0.0)
l.EndPoint=(0.0,1.0,0.0)

section = Part.Wire(Part.makeCircle(0.4445, Base.Vector(0, 0, 0), Base.Vector(0, 1, 0)))

makeSolid = True
isFrenet = True
wire = Part.Wire([l.toShape()])
pipe = wire.makePipeShell([section], makeSolid, isFrenet)

Part.show(wire)
Part.show(section)
Part.show(pipe)

screenshot_2016-04-04_21-33-23

Curved wire path and a solid result:

import Part
from FreeCAD import Base

# Circular section to sweep
section = Part.Wire(Part.makeCircle(0.4445, Base.Vector(0, 0, 0), Base.Vector(0, 1, 0)))

# Three point arc
arc = Part.Arc(Base.Vector(0, 0, 0), Base.Vector(-10, 0, 0), Base.Vector(0, -10, 0))

makeSolid = True
isFrenet = True

# Create a wire from the line/edge
wire = Part.Wire([arc.toShape()])

# Doc the actual sweep
pipe = wire.makePipeShell([section], makeSolid, isFrenet)

Part.show(wire)
Part.show(section)
Part.show(pipe)

screenshot_2016-04-04_21-35-09

@jmwright
Copy link
Collaborator Author

jmwright commented Apr 22, 2016

Latest iteration, which can be switched between a linear path and an arc path:

import Part
from FreeCAD import Base
import cadquery as cq
S = cq.selectors.StringSyntaxSelector

# Circular section to sweep
section = Part.Wire(Part.makeCircle(0.4445, Base.Vector(0, 0, 0), Base.Vector(0, 1, 0)))

l=Part.Line()
l.StartPoint=(0.0,0.0,0.0)
l.EndPoint=(0.0,10.0,0.0)

# Three point arc
arc = Part.Arc(Base.Vector(0, 0, 0), Base.Vector(-10, 0, 0), Base.Vector(0, -10, 0))
# arc = l

makeSolid = True
isFrenet = True

# Create a wire from the line/edge
wire = Part.Wire([arc.toShape()])

# Doc the actual sweep
pipe = wire.makePipeShell([section], makeSolid, isFrenet)

# Part.show(wire)
# Part.show(section)
# Part.show(pipe)

# CQ object conversion
cqObj = cq.CQ(cq.Shape.cast(pipe))
tube = cqObj.newObject([cq.Shape.cast(pipe)])

# Shell operation
hollow_tube = tube.faces((S('|X') + S('|Z'))).shell(0.1)
# hollow_tube = tube.faces((S('|Y') + S('|Z'))).shell(0.1)

Part.show(hollow_tube.val().wrapped)

There's a parallel discussion going on here.

screenshot_2016-04-22_08-24-29

@jmwright
Copy link
Collaborator Author

jmwright commented Apr 26, 2016

I have a rough implementation of this operation in the sweep branch. Here's sample code to sweep over a polyline.

import cadquery as cq
from Helpers import show

pts = [
    (0, 1),
    (1, 2),
    (2, 4)
]

path = cq.Workplane("XZ").polyline(pts)
sweep = cq.Workplane("XY").circle(1.0).sweep(path)

show(path)
show(sweep)

I need to do some cleanup and a lot of manual testing before I even consider adding unit tests, but I think it's a decent start. You pass it a path (the polyline) as an argument. I'm not totally crazy about that implementation, but I feel it's the best option we have.

screenshot_2016-04-25_22-50-24

@dcowden
Copy link
Owner

dcowden commented Apr 26, 2016

Will it accept a spline path? I think that would also be a common case to
include in the poc before proceeding with implementation
On Apr 25, 2016 10:43 PM, "Jeremy Wright" notifications@github.com wrote:

I have a rough implementation of this operation in the sweep branch
https://github.com/dcowden/cadquery/tree/sweep. Here's sample code to
sweep over a polyline.

import cadquery as cqfrom Helpers import show

pts = [
(0, 1),
(1, 2),
(2, 4)
]

path = cq.Workplane("XZ").polyline(pts)
sweep = cq.Workplane("XY").circle(1.0).sweep(path)

show(path)
show(sweep)

I need to do some cleanup and a lot of manual testing before I even
consider adding unit tests, but I think it's a decent start. You pass it a
path (the polyline) as an argument. I'm not totally crazy about that
implementation, but I feel it's the best option we have.


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
#33 (comment)

@jmwright
Copy link
Collaborator Author

It should accept anything that is, or can be, converted to a wire. I
haven't tested a spline yet though. I'll try that out.
On Apr 26, 2016 6:28 AM, "Dave Cowden" notifications@github.com wrote:

Will it accept a spline path? I think that would also be a common case to
include in the poc before proceeding with implementation
On Apr 25, 2016 10:43 PM, "Jeremy Wright" notifications@github.com
wrote:

I have a rough implementation of this operation in the sweep branch
https://github.com/dcowden/cadquery/tree/sweep. Here's sample code to
sweep over a polyline.

import cadquery as cqfrom Helpers import show

pts = [
(0, 1),
(1, 2),
(2, 4)
]

path = cq.Workplane("XZ").polyline(pts)
sweep = cq.Workplane("XY").circle(1.0).sweep(path)

show(path)
show(sweep)

I need to do some cleanup and a lot of manual testing before I even
consider adding unit tests, but I think it's a decent start. You pass it
a
path (the polyline) as an argument. I'm not totally crazy about that
implementation, but I feel it's the best option we have.


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
#33 (comment)


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub
#33 (comment)

@jmwright
Copy link
Collaborator Author

The way FreeCAD is sweeping it is kind of a train wreck, but a spline definitely works.

import cadquery as cq
from Helpers import show

pts = [
    (0, 1),
    (1, 2),
    (2, 4)
]

path = cq.Workplane("XZ").spline(pts)
sweep = cq.Workplane("XY").circle(1.0).sweep(path)

show(path)
show(sweep)

screenshot_2016-04-26_09-32-15

@jmwright
Copy link
Collaborator Author

Ok, I had the isFrenet option of makePipeShell set to True, and that was causing the problem. With that set to False I get a clean sweep.

screenshot_2016-04-26_09-39-01

I'm not sure if I should just always leave frenet as False, or if I should expose that option to the user. I don't really want to complicate the API with options like that, but I suppose there are cases where Frenet might make sense.

@jmwright
Copy link
Collaborator Author

Here's some additional info on Frenet. http://www.freecadweb.org/wiki/index.php?title=Part_Sweep#Frenet

I think I'm going to have to expose that option in the CadQuery API. I know that a user is going to ask for that eventually.

@jmwright
Copy link
Collaborator Author

Making a shell is tricky with these sweeps. It's very easy to end up with a face that's difficult (or impossible) to grab with CadQuery's selectors, which means that the shell function doesn't work quite right. In that case, the following solution isn't ideal, but works.

import cadquery as cq
from Helpers import show

pts = [
    (0, 1),
    (1, 2),
    (2, 4)
]

cutpts = [
    (0, 1),
    (1, 2),
    (2, 4.02)
]

path = cq.Workplane("XZ").spline(pts)
section = cq.Workplane("XY").circle(1.0)
sweep = section.sweep(path)

cutPath = cq.Workplane("XZ").spline(cutpts)
cutSection = cq.Workplane("XY").circle(0.9)
cutSweep = cutSection.sweep(cutPath).translate((0, 0, -0.01))

# show(path)
# show(cutPath)
# show(section)
# show(cutSection)
# show(cutSweep)
show(sweep.cut(cutSweep))

screenshot_2016-04-26_10-22-38

@dcowden
Copy link
Owner

dcowden commented Apr 26, 2016

Nice!
​yeah, i think we need better selectors. What'd​ love to eventually get to
is a 'produced by' selector, that allows you to select objects that were
the result of another. example: if you extrude a square, the edges are
produced by the vertices, and the side faces are produced by the edges. the
top and bottom faces are produced by the wire itself ( or the face ).

combined with filters by operation id also, we could select the end faces
of a sweep to remove them for shell.

On Tue, Apr 26, 2016 at 10:26 AM, Jeremy Wright notifications@github.com
wrote:

Making a shell is tricky with these sweeps. It's very easy to end up with
a face that's difficult (or impossible) to grab with CadQuery's selectors,
which means that the shell function doesn't work quite right. In that case,
the following solution isn't ideal, but works.

import cadquery as cqfrom Helpers import show

pts = [
(0, 1),
(1, 2),
(2, 4)
]

cutpts = [
(0, 1),
(1, 2),
(2, 4.02)
]

path = cq.Workplane("XZ").spline(pts)
section = cq.Workplane("XY").circle(1.0)
sweep = section.sweep(path)

cutPath = cq.Workplane("XZ").spline(cutpts)
cutSection = cq.Workplane("XY").circle(0.9)
cutSweep = cutSection.sweep(cutPath).translate((0, 0, -0.01))

show(path)# show(cutPath)# show(section)# show(cutSection)# show(cutSweep)

show(sweep.cut(cutSweep))

[image: screenshot_2016-04-26_10-22-38]
https://cloud.githubusercontent.com/assets/1015439/14821562/36f89bd0-0b99-11e6-8616-9d0ff223e046.png


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
#33 (comment)

@jmwright
Copy link
Collaborator Author

@dcowden Awesome, and a leading reason for CadQuery 2.0.

It seems like this is about as good as it's going to get in CadQuery v0.x. Users will be able to do what they need to do (I think), but it just won't be as easy/convenient as we want things to be with CadQuery. Is that your take on this also?

@dcowden
Copy link
Owner

dcowden commented Apr 26, 2016

yes i agree. And, it is worth noting that the 'parent of' selectors are
what requires a departure from FreeCAD. OCC supports ( in many but not all
cases ) the ability to get information about what edge/vertex generated
what. FreeCAD interfaces have abstracted away the ability to get to this,
because the information is typically available on the OCC object (
BRepAPI_Builder, for example).

On Tue, Apr 26, 2016 at 10:50 AM, Jeremy Wright notifications@github.com
wrote:

@dcowden https://github.com/dcowden Awesome, and a leading reason for
CadQuery 2.0.

It seems like this is about as good as it's going to get in CadQuery v0.x.
Users will be able to do what they need to do (I think), but it just won't
be as easy/convenient as we want things to be with CadQuery. Is that your
take on this also?


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#33 (comment)

@jmwright
Copy link
Collaborator Author

I'll expose the isFrenet option and do the unit tests tonight then. We'll call this one good for now, and look forward to the good things coming with CadQuery 2.0.

@dcowden
Copy link
Owner

dcowden commented Apr 26, 2016

yep, sounds great! thanks for adding this!

On Tue, Apr 26, 2016 at 11:34 AM, Jeremy Wright notifications@github.com
wrote:

I'll expose the isFrenet option and do the unit tests tonight then. We'll
call this one good for now, and look forward to the good things coming with
CadQuery 2.0.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#33 (comment)

@jmwright
Copy link
Collaborator Author

@dcowden So, I'm about to do a PR for this, but have one last question. It bothers me a little bit that I'm calling val().wrapped here instead of just wrapped.

Should I be calling val() on the path before passing it into freecad_impl?

@jmwright
Copy link
Collaborator Author

After thinking through the implementation again, I do think it's better to call val() when calling the freecad_impl.Solid.sweep function rather than calling it inside freecad_impl. It keeps the objects at the same level as the other functions in that class.

@jmwright
Copy link
Collaborator Author

jmwright commented Apr 27, 2016

Created PR #145 to implement this operation.

@dcowden
Copy link
Owner

dcowden commented Apr 27, 2016

I think this is fine.
On Apr 26, 2016 8:14 PM, "Jeremy Wright" notifications@github.com wrote:

@dcowden https://github.com/dcowden So, I'm about to do a PR for this,
but have one last question. It bothers me a little bit that I'm calling
val().wrapped here
https://github.com/dcowden/cadquery/compare/sweep?expand=1#diff-991608f6de186b2ea8a5dbe98ea14359R925
instead of just wrapped.

Should I be calling val() on the path before passing it into freecad_impl?


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#33 (comment)

@dcowden
Copy link
Owner

dcowden commented Apr 27, 2016

Yeah that's good logic. You are right, consistent level of abstraction is
good
On Apr 26, 2016 8:19 PM, "Jeremy Wright" notifications@github.com wrote:

After thinking through the implementation again, I do think it's better to
call val() when calling the freecad_impl.Solid.sweep function rather than
calling it inside freecad_impl. It keeps the objects at the same level as
the other functions in that class.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#33 (comment)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants