dcowden / cadquery

CadQuery-- a parametric cad script framework
Other
431 stars 56 forks source link

Feature Request: Sweep Operation #33

Closed jmwright closed 8 years ago

jmwright commented 10 years ago

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 commented 9 years ago

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 commented 9 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-60023689.

jmwright commented 9 years ago

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 commented 9 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-60335235.

jmwright commented 9 years ago
  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 commented 9 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-60470168.

jmwright commented 9 years ago

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 commented 9 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-60768976.

jmwright commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-64179954.

jmwright commented 9 years ago

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 commented 9 years ago

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

jmwright commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-214591112

jmwright commented 8 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-214591112

— You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub https://github.com/dcowden/cadquery/issues/33#issuecomment-214696082

jmwright commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-214762903

jmwright commented 8 years ago

@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 commented 8 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-214770703

jmwright commented 8 years ago

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 commented 8 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-214785052

jmwright commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

Created PR #145 to implement this operation.

dcowden commented 8 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-214926696

dcowden commented 8 years ago

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 https://github.com/dcowden/cadquery/issues/33#issuecomment-214927417