Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export multiple objects to a single step file #212

Closed
abudden opened this issue Nov 21, 2019 · 12 comments
Closed

Export multiple objects to a single step file #212

abudden opened this issue Nov 21, 2019 · 12 comments
Labels
bug Something isn't working

Comments

@abudden
Copy link

abudden commented Nov 21, 2019

I've just discovered CQ2 and my first impressions are that it's brilliant. I've been trying to figure out how to get step files for a multi-object model out of a script and have drawn a blank: there doesn't seem to be any documentation and I haven't figured it out by trawling through the code either.

Given a simple model like the one below, I can load it in CQ-editor and export the model with no issues. This works fine, but I'd rather not use CQ-editor (it changes the line-endings in the text file resulting in huge diffs and moving the model around is painful without 3D mouse support).

So I know it's possible to export multiple objects in one STEP file as CQ-editor can do it, but how do I do the same in CQ python code?

Example code:

#!/usr/bin/python3
# vim: set fileencoding=utf-8 :
import cadquery as cq

# Create a box
length, width, height = (60.0, 80.0, 10.0)
box1 = (cq.Workplane('XY')
        .box(length=length, width=width, height=height)
        # Get the face highest in the Z axis
        .faces('>Z').workplane()
        # Draw a centred rectangle, 20×40
        .rect(xLen=20, yLen=40)
        # Cut the rectangle through the box to make a square pocket
        .cutThruAll()
        )

# Make a second box
box2 = (cq.Workplane('XY')
        # Move the centre of the workplane away from the first box
        .center(200.0, 100.0)
        # Draw the box
        .box(length=20.0, width=30.0, height=40.0)
        )

box3 = (cq.Workplane('XY')
        # Move the centre of the workplane away from the first and second boxes
        .center(-100.0, 50.0)
        # Draw the box
        .box(length=50.0, width=100.0, height=100.0)
        )

# If in CQ-Editor GUI:
if 'show_object' in globals():
    globals()['show_object'](box1) # Odd way of calling it to avoid a pylint error
    globals()['show_object'](box2) # Odd way of calling it to avoid a pylint error
    globals()['show_object'](box3) # Odd way of calling it to avoid a pylint error
    # Now select all objects under "CQ models" in the Objects pane and hit Tools...Export as STEP
else:
    # If not in CQ-Editor GUI:
    # How do I make this export box1, box2 and box3 in place in a single step file?
    box1.val().exportStep("example2.step")
@bragostin
Copy link
Contributor

Can you try this?

vals = []
for o in box1.all(): vals.extend(o.vals())
for o in box2.all(): vals.extend(o.vals())
for o in box3.all(): vals.extend(o.vals())
compound_object = cq.Compound.makeCompound(vals)
compound_object.exportStep('filename.stp')

@abudden
Copy link
Author

abudden commented Nov 21, 2019

@bragostin Thanks! That works perfectly. It would be great to see a bit more in the documentation on how to do this sort of thing.

I've written a simple function to simplify this:

def exportStep(object_list, filename):
    vals = list(itertools.chain(*[o.vals() for obj in object_list for o in obj.all()]))
    compound = cq.Compound.makeCompound(vals)
    compound.exportStep(filename)

# e.g.:
exportStep([box1, box2, box3], 'example2.step')

@bragostin
Copy link
Contributor

@abudden you are welcome. To have a function like you wrote makes a lot of sense when you have many distinct CQ objects that you want to save into the same step file.
That goes into the direction of assemblies, which is, I believe the purpose of cqparts
https://github.com/cqparts/cqparts

@bragostin
Copy link
Contributor

@abudden if this works for you, I guess the issue can be closed?

@abudden
Copy link
Author

abudden commented Nov 21, 2019

@bragostin Okay with me, unless you want to turn it into a documentation issue?

Unless I'm missing something, exportStep isn't referenced anywhere in the API reference. I only figured out my original .val().exportStep(filename) by delving into the source code.

@jmwright
Copy link
Member

Maybe it should be referenced here?

@adam-urbanczyk
Copy link
Member

@abudden exportStep belongs to the low-level API. That is why it is not referenced. You could achieve what you want with cq.exporters.exportShape but this code requires some serious cleanup.

Regarding the multiple objects thing, it can be currently achieved using the fluent API like so:

import cadquery as cq

# Create a box
length, width, height = (60.0, 80.0, 10.0)
box1 = (cq.Workplane('XY')
        .box(length=length, width=width, height=height)
        # Get the face highest in the Z axis
        .faces('>Z').workplane()
        # Draw a centred rectangle, 20×40
        .rect(xLen=20, yLen=40)
        # Cut the rectangle through the box to make a square pocket
        .cutThruAll()
        )

# Make a second box
box2 = (cq.Workplane('XY')
        # Move the centre of the workplane away from the first box
        .center(200.0, 100.0)
        # Draw the box
        .box(length=20.0, width=30.0, height=40.0)
        )

box3 = (cq.Workplane('XY')
        # Move the centre of the workplane away from the first and second boxes
        .center(-100.0, 50.0)
        # Draw the box
        .box(length=50.0, width=100.0, height=100.0)
        )

res = box1.union(box2).union(box3)

@abudden
Copy link
Author

abudden commented Nov 22, 2019

@adam-urbanczyk Thanks for the reference to exportShape - I noticed it in the documentation, but there wasn't much information so I kept digging until I found exportStep.

The union method of combining the objects doesn't work. Consider this example:

#!/usr/bin/python3
# vim: set fileencoding=utf-8 :
import itertools
import cadquery as cq

def exportStep(object_list, filename):
    vals = list(itertools.chain(*[o.vals() for obj in object_list for o in obj.all()]))
    compound = cq.Compound.makeCompound(vals)
    compound.exportStep(filename)

table = (cq.Workplane('XY')
        .box(length=2000.0, width=1000.0, height=25.0)
        .faces('<Z').workplane()
        .rect(xLen=1800.0, yLen=800.0)
        .vertices()
        .box(length=50, width=50, height=800.0, centered=(True, True, False))
        )

box_on_table = (table.faces('>Z').workplane()
    .box(length=200.0, width=400.0, height=300.0, centered=(False, False, False), combine=False)
    )

union_method = table.union(box_on_table)

# If in CQ-Editor GUI:
if 'show_object' in globals():
    show_object = globals()['show_object'] # Does nothing, but gets rid of a pylint error
    show_object(table)
    show_object(box_on_table)
else:
    exportStep([table, box_on_table], 'compound_method.step')
    union_method.val().exportStep('union_method.step')

If you run this outside of CQ-Editor and then open compound_method.step in (e.g.) FreeCAD, there are two objects: a table and a box sitting on the table. This is correct. If you open union_method.step there is only one object, which is wrong.

@adam-urbanczyk
Copy link
Member

@abudden weird, I tried the following:

import cadquery as cq

# Create a box
length, width, height = (60.0, 80.0, 10.0)
box1 = (cq.Workplane('XY')
        .box(length=length, width=width, height=height)
        # Get the face highest in the Z axis
        .faces('>Z').workplane()
        # Draw a centred rectangle, 20×40
        .rect(xLen=20, yLen=40)
        # Cut the rectangle through the box to make a square pocket
        .cutThruAll()
        )

# Make a second box
box2 = (cq.Workplane('XY')
        # Move the centre of the workplane away from the first box
        .center(200.0, 100.0)
        # Draw the box
        .box(length=20.0, width=30.0, height=40.0)
        )

box3 = (cq.Workplane('XY')
        # Move the centre of the workplane away from the first and second boxes
        .center(-100.0, 50.0)
        # Draw the box
        .box(length=50.0, width=100.0, height=100.0)
        )

res = box1.union(box2).union(box3)

res.val().exportStep('test.step')

and this is what I get in FreeCAD:
image
and in CQ-editor
image

@abudden
Copy link
Author

abudden commented Nov 25, 2019

@adam-urbanczyk Yes, it'll work if the objects aren't touching (like in this example). Where the objects are next to one another (like the box on a table example I posted), the union operation combines the two objects into a single one.

@adam-urbanczyk
Copy link
Member

Clear @abudden . There is a newObject method to "combine" objects into a single cq.Workplane but the official exporter API will only use the first value. I think we need to fix that.

@adam-urbanczyk
Copy link
Member

Solved by #415:

import cadquery as cq

res = (
    cq.Workplane()
    .rect(2,2,forConstruction=True)
    .vertices().circle(.5).extrude(1)
)

cq.exporters.export(res,'test.step')
cq.exporters.export(res,'test.step')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants