STL export - wrong positioning (different origins?)

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
0xCoto
Posts: 26
Joined: Wed Aug 10, 2022 3:56 pm

STL export - wrong positioning (different origins?)

Post by 0xCoto »

Every time I import a STEP file, it creates an "origin" for each compound:

Image

This is very annoying, because if I right click on the individual solids to export them separately, the origin is not picked up, and so when loading the STLs on a new project, they are not in the right position:

Image

Is there a workaround for this? E.g. some way to "force" FreeCAD to not use different origins for different compounds/shapes? Or some way to maintain the right positioning when exporting the STLs?

The alternative CadQuery package seems to do this correctly, but I'd prefer to use FreeCAD because of its versatility and additional utilities.

My end goal is to automate the STEP --> STLs conversion using Python. I'm currently using the following code (which works fine, other than the fact that the positioning is wrong, as mentioned above):

Code: Select all

def step2stl_fc(step_file="model.step", clear=True):
    # Prepare FreeCAD STEP configuration
    with open(os.getenv("HOME")+'/.FreeCAD/user.cfg', 'r') as file :
        filedata = file.read()
    
    filedata = filedata.replace('ReadShapeCompoundMode" Value="1', 'ReadShapeCompoundMode" Value="0')

    with open(os.getenv("HOME")+'/.FreeCAD/user.cfg', 'w') as file:
        file.write(filedata)

    # Import FreeCAD
    import FreeCAD
    import Import
    import Mesh

    # Delete pre-existing STL files
    dir_name = "data"
    test = os.listdir(dir_name)

    for item in test:
        if item.endswith(".stl"):
            os.remove(os.path.join(dir_name, item))

    # Empty out tmp directory
    if clear:
        dir = "data/tmp"
        if os.path.exists(dir):
            shutil.rmtree(dir)
        os.makedirs(dir)

    # Import STEP file
    Import.insert("data/" + step_file, "Unnamed")
    
    # Export full model as a single STL
    obj_list = []
    for obj in FreeCAD.ActiveDocument.Objects:
        if str(obj) == "<Part::PartFeature>" and obj.Shape.isClosed():
            obj_list.append(obj)

    def getObj(object):
        return FreeCAD.getDocument("Unnamed").getObject(str(object.Name))

    __objs__ = list(map(getObj, obj_list))
    Mesh.export(__objs__, "data/tmp/model.stl")

    # Export each object as a separate STL
    for object in obj_list:
        if object.Name.startswith("Part"):
            obj = FreeCAD.getDocument("Unnamed").getObject(str(object.Name))
            Mesh.export([obj], "data/" + str(object.Label) + ".stl")
I've attached the STEP file.
patch_offset.stp
(83.57 KiB) Downloaded 15 times
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: STL export - wrong positioning (different origins?)

Post by onekk »

No as STL is a bunch of triangles (coordinates) so no origin on them.

STL is a NOT a format meant to be and interchange fornat, it was created as a "solid description" based on mesh for STereoLitography see capitals that deode the extension name.

Probably other formats are more taylored to contain position or origin.

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
0xCoto
Posts: 26
Joined: Wed Aug 10, 2022 3:56 pm

Re: STL export - wrong positioning (different origins?)

Post by 0xCoto »

@onekk My goal is to replicate what CadQuery achieves. This works perfectly for me; I'm sure there has to be a way to merge the two origins into a single global origin in FreeCAD without messing the positioning of each solid? Maybe by somehow merging all parts into a single compound?

I understand that STLs are basically meshes, but unlike FreeCAD, the vertex coordinates of the triangles are output by CadQuery in a desirable manner during STL exports.

This implies that CadQuery does not handle the origins in the same manner, and the coordinates of the STL vertices of any exported shape remain in the right place.
User avatar
Roy_043
Veteran
Posts: 8450
Joined: Thu Dec 27, 2018 12:28 pm

Re: STL export - wrong positioning (different origins?)

Post by Roy_043 »

0xCoto
Posts: 26
Joined: Wed Aug 10, 2022 3:56 pm

Re: STL export - wrong positioning (different origins?)

Post by 0xCoto »

Roy_043 wrote: Sat Dec 03, 2022 10:38 pm Look at Part_TransformedCopy.
This looks to be exactly what I need, thanks!! :D

What would be the way to emulate that in Python (no GUI)?

Code: Select all

# Export each object as a separate STL
for object in obj_list:
    if object.Name.startswith("Part"):
        obj = FreeCAD.getDocument("Unnamed").getObject(str(object.Name))
        ### Can I put something here to execute Part_TransformedCopy ? ###
        #copyObj = FreeCAD.getDocument("Unnamed").addObject('Part::Feature','NAME')
        #copyObj.Shape = Part.getShape(obj,'',needSubElement=False,refine=False)
        #FreeCAD.getDocument("Unnamed").recompute()
        #obj = copyObj
        ### tried this ^ (got it from https://forum.freecadweb.org/viewtopic.php?style=4&t=67664&p=585221 ) but doesn't seem to work ###
        Mesh.export([obj], "data/" + str(object.Label) + ".stl")
User avatar
Roy_043
Veteran
Posts: 8450
Joined: Thu Dec 27, 2018 12:28 pm

Re: STL export - wrong positioning (different origins?)

Post by Roy_043 »

Run the command and then check the Python console.
0xCoto
Posts: 26
Joined: Wed Aug 10, 2022 3:56 pm

Re: STL export - wrong positioning (different origins?)

Post by 0xCoto »

Roy_043 wrote: Sat Dec 03, 2022 11:02 pm Run the command and then check the Python console.
Ok, I'm 99% there. My code works perfectly, but not for Objects within Parts:

Code: Select all

__shape = Part.getShape(App.getDocument('Unnamed').getObject('Unnamed001'),'Unnamed.Part.Part001.Part__Feature001.',needSubElement=False,refine=False)
In the above case, I have an object named Part__Feature001 which is nested into the Part001, nested into Part, nested into Unnamed.

If I don't know the name of the parts (but only have the object name, i.e. "Part__Feature001"), is there a way to derive the part names that encapsulate my object so that I can tell Part.getShape which object I am referring to?

Edit: Fixed, thank you very much for your help!

Code: Select all

    # Get objects
    obj_list = []
    for obj in FreeCAD.ActiveDocument.Objects:
        if str(obj) == "<Part object>" or str(obj) == "<Part::PartFeature>" and obj.Shape.isClosed():
            obj_list.append(obj)

    # Reverse list so that shapes are nested in parts from left to right
    obj_list = list(reversed(obj_list))

    # Export each object as a separate STL
    __objs__ = []
    part_name = ""
    for object in obj_list:
        print(object.Name)
        if not object.Name.startswith("Part"):
            if object.Name != "Unnamed001":
                part_name = part_name + object.Name + "."
        else:
            # Fix relative origin
            obj = FreeCAD.getDocument("Unnamed").getObject(str(object.Name))
            __shape = Part.getShape(FreeCAD.getDocument('Unnamed').getObject('Unnamed001'),part_name+str(object.Name)+'.',needSubElement=False,refine=False)
            FreeCAD.ActiveDocument.addObject('Part::Feature',str(object.Name)+'_transformed').Shape=__shape
            obj = FreeCAD.getDocument("Unnamed").getObject(str(object.Name)+'_transformed')
            __objs__.append(obj)
            
            # Export STL
            Mesh.export([obj], "data/" + str(object.Label) + ".stl")
    
    # Export full model as a single STL
    Mesh.export(__objs__, "data/tmp/model.stl")
Post Reply