SolidCode / SolidPython

A python frontend for solid modelling that compiles to OpenSCAD
1.11k stars 173 forks source link

X, Y, Z scaling shim? #68

Closed nerdfever closed 7 years ago

nerdfever commented 7 years ago

Not sure if this is trivial or difficult...

I'm using SolidPython to generate .STL files for 3D printing camera lens adapters, where accuracy matters.

The material shrinks a bit, and worse, the printer prints slightly larger in the X direction than in the Y direction (by a fraction of 1%).

Is there some way to get SolidPython to apply X, Y, and Z correction factors to all positions before generating the OpenSCAD code? I'm thinking of a "shim" layer that takes all the sizes and positions and multiplies them by correction factors, like this:

Corrections = {
    'Z-ABS':    [1.0, 1.0, 1.0],
    'Z-ULTRAT': [1.00413, 0.99732, 1.0],
    'Z-PETG':   [1.0, 1.0, 1.0]
}

The idea is that for each material, there is an X, Y, and Z correction factor to be applied.

Any suggestions?

etjones commented 7 years ago

Hmm... there's nothing built in that would deal with this, but I would approach it by adding a @material('Z-ABS') decorator that would add a material attribute to each returned value, then run things through a scaling process before finally rendering to OpenSCAD. Here's a quick example.

I haven't tested this much or thought it all the way through. Note that it's only going to scale the top level of a tree, so if you take the union of an ABS part and a PET part and apply the ULTRAT material to that union, the entire piece will be scaled as if it were ULTRAT. But if you're serious about designing parts for particular materials, you probably wouldn't be mixing parts like that anyway.

So, uh... good luck!

from solid import *
from solid.utils import *

ABS = 'Z-ABS'
ULTRAT = 'Z-ULTRAT'
PET = 'Z-PETG'

MATERIAL_SCALES = {
    ABS:    [1.0, 1.0, 1.0],
    ULTRAT: [1.00413, 0.99732, 1.0],
    PET:   [1.0, 1.0, 1.0]
}

def material(material_name):
    def wrap(f):
        def wrapped_f(*args):
            val = f(*args)
            setattr(val, 'material', material_name)
            return val
        return wrapped_f
    return wrap

@material(ABS)
def abs_part():
    # your part logic here...
    part = right(3)(
        sphere(r=1)
    )
    return part

@material(ULTRAT)
def ultrat_part():
    # your part logic here...
    part = left(3)(
        cylinder(r=1, h=1)
    )
    return part

def apply_materials(sp_object):
    '''
    Recursively look at all solidpython objects in a tree, scaling
    any part of a tree that has a 'material' attribute by the appropriate
    scale factor.  

    Note that this stops evaluating objects in the tree once the first `material`
    property is found, so you could easily turn one material into another by 
    unioning/differencing it with a part of a different material.  
    Lots of room for improvement here.
    '''
    if hasattr(sp_object, 'material'):
        v = MATERIAL_SCALES.get(sp_object.material, [1,1,1])
        return scale(v)(sp_object)
    elif sp_object.children:
        sp_object.children = [apply_materials(c) for c in sp_object.children]
        return sp_object
    else:
        return sp_object

def material_example():
    my_objs = union()(abs_part(), ultrat_part(), cube(1, center=True))
    return apply_materials(my_objs)

mat_ex = material_example()
print(scad_render(mat_ex))
nerdfever commented 7 years ago

Wow; that's easier than I hoped. 👍

My printer (like most) can only print one material at a time, so if you did have parts with different materials fitting together in something, you'd need separate .STL files for each material anyway.

I'll play with this and let you know how it goes. May be a week or two (this is a spare-time project).

etjones commented 7 years ago

Makes sense. Although, if you're going to make separate STLs for each, you probably just want to get all your parts of a given material together all at once and then call scale(v=<material_scale_vec>)(parts_list) on them; don't even worry about the decorator stuff. Good luck!

gunderson commented 7 years ago

Why not handle this in the slicer software? It seems to me that the code should be an empirical representation of the object and calibration for actual printing should be done in the print software.

nerdfever commented 7 years ago

Because I'm not writing the slicer software, but I'm writing the SolidPython code.

gunderson commented 7 years ago

Every slicer i've used had a scale x, y and z already built-in so it seems redundant. If yours doesn't, then I guess I understand the request.