Skip to content

A python frontend for solid modelling that compiles to OpenSCAD

Notifications You must be signed in to change notification settings

lfagundes/SolidPython

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Documentation Status

If you switch from the regular SolidPython:master branch to this branch, have a look at Version 2.x.x.

SolidPython is a generalization of Phillip Tiefenbacher's openscad module, found on Thingiverse. It generates valid OpenSCAD code from Python code with minimal overhead. Here's a simple example:

This Python code:

from solid2 import *
d = difference()(
    cube(10),
    sphere(15)
)
d.as_scad()

Generates this OpenSCAD code:

difference(){
    cube(10);
    sphere(15);
}

That doesn't seem like such a savings, but the following SolidPython code is a lot shorter (and I think clearer) than the SCAD code it compiles to:

from solid2 import *
d = cube(5) + right(5)(sphere(5)) - cylinder(r=2, h=6)

Generates this OpenSCAD code:

difference(){
    union(){
        cube(5);
        translate( [5, 0,0]){
            sphere(5);
        }
    }
    cylinder(r=2, h=6);
}

In contrast to OpenSCAD -- which is a constrained domain specific language -- Python is a full blown modern programming language and as such supports pretty much all modern programming features. Furthermore a huge number of libraries is available.

SolidPython lets you use all these fancy python features to generate your constructive solid geometry models.

On the one hand it makes the generation of your models a lot easier, because you don't need to learn another domain specific language and you can use all the programming technique you're already familiar with. On the other hand it gives you a lot more power, because you can use all the comprehensive python libraries to generate your models.

I would almost say this enables you to do what ever you want with ease. As (maybe little uncommon) example, you could write a program that:

  • looks up the mail adress of your actuall president (based on your ip address)
  • writes a mail to him or her and asks for a portrait
  • waits for a reply
  • generates a heightmap from the picture you received and maps it onto a vase

This should be pretty straight forward with SolidPython but is impossible with pure OpenSCAD.

Furhtermore SolidPython 2.x.x is designed to be extendible. As such you can extend SolidPython itself using python. Actually parts of SolidPython itself are implemented as extensions (everything but the core one-to-one mapping of OpenScad to Python), these include operators, access style syntax, convenience functions, scad_interface and bosl2 support. Furthermore some of the SolidPython 1.x.x solid.utils features are also implemented as extensions (bill of material & part-hole).

  • Install latest release via PyPI:

    pip install solidpython2

    (You may need to use sudo pip install solidpython2, depending on your environment. This is commonly discouraged though. You'll be happiest working in a virtual environment where you can easily control dependencies for a given project)

  • Install current master straight from Github:

    pip install git+https://github.com/jeff-dh/SolidPython
  • Include SolidPython at the top of your Python file:

    from solid2 import *

    (See this issue for a discussion of other import styles)

  • OpenSCAD uses curly-brace blocks ({}) to create its tree. SolidPython uses parentheses with comma-delimited lists.

    OpenSCAD:

    difference(){
        cube(10);
        sphere(15);
    }
    

    SolidPython:

    d = difference()(
        cube(10),  # Note the comma between each element!
        sphere(15)
    )
    
  • Call py_scad_obj.as_scad() to generate SCAD code. This returns a string of valid OpenSCAD code.

  • or: call py_scad_obj.save_as_scad("filepath.scad") to store that code in a file.

  • If filepath.scad is open in the OpenSCAD IDE and Design => 'Automatic Reload and Compile' is checked in the OpenSCAD IDE, running py_scad_obj.save_as_scad() from Python will load the object in the IDE.

  • Alternately, you could call OpenSCAD's command line and render straight to STL.

  • Use solid2.import_scad(path) to import OpenSCAD code. Relative paths will check the current location designated in OpenSCAD library directories.

Ex:

scadfile.scad

module box(w,h,d){
    cube([w,h,d]);
}

your_file.py

from solid2 import *

scadfile = import_scad('/path/to/scadfile.scad')
b = scadfile.box(2,4,6)
b.save_as_scad('out_file.scad')
  • Recursively import OpenSCAD code by calling import_scad() with a directory argument.
from solid2 import *

# MCAD is OpenSCAD's most common utility library: https://github.com/openscad/MCAD
# If it's installed for OpenSCAD (on MacOS, at: ``$HOME/Documents/OpenSCAD/libraries``)
mcad = import_scad('MCAD')

# MCAD contains about 15 separate packages, each included as its own namespace
print(dir(mcad)) # => ['bearing', 'bitmap', 'boxes', etc...]
mount = mcad.motors.stepper_motor_mount(nema_standard=17)
mount.save_as_scad('motor_mount_file.scad')
  • OpenSCAD has the use() and include() statements for importing SCAD code, and SolidPython has them, too. They pollute the global namespace, though, and you may have better luck with import_scad(),

Ex:

scadfile.scad

module box(w,h,d){
    cube([w,h,d]);
}

your_file.py

from solid2 import *

# use() puts the module `box()` into the global namespace
use('/path/to/scadfile.scad')
b = box(2,4,6)
scad_render_to_file(b, 'out_file.scad')

The best way to learn how SolidPython works is to look at the included example code. If you've installed SolidPython, the following line of Python will print (the location of) the examples directory:

import os, solid2; print(os.path.dirname(solid2.__file__) + '/examples')

Or browse the example code on Github here

SolidPython overrides the basic operators + and | (union), - (difference), * and & (intersection) and ~ (debug). So

c = cylinder(r=10, h=5) + cylinder(r=2, h=30)

is the same as:

c = union()(
    cylinder(r=10, h=5),
    cylinder(r=2, h=30)
)

Likewise:

c = cylinder(r=10, h=5)
c -= cylinder(r=2, h=30)

is the same as:

c = difference()(
    cylinder(r=10, h=5),
    cylinder(r=2, h=30)
)

Since at least some people (including me) don't like the OpenSCAD Syntax, SolidPython 2.x.x introduces the support for the so called "Access-Style-Syntax". This enables you to call some of the SolidPython / OpenSCAD functions as member functions of any OpenSCADObject instead of wrapping it in an instance of it.

In other words, e.g. code:

up(10)(cube(1))
#is equal to
cube(1).up(10)

The available member functions are the following:

union, difference, intersection, translate, scale, rotate, mirror, resize,
color, offset, hull, render, projection, surface, linear_extrude,
rotate_extrude, debug, background, root and disable

Also the convenience functions are available:

up, down, left, right, forward, fwd, back, translateX, translateY, translateZ,
rotateX, rotateY, rotateZ, mirrorX, mirrorY, mirrorZ, scaleX, scaleY, scaleZ,
resizeX, resizeY, resizeZ

Furthermore you can chain these functions, because they all return the transformed OpenSCADObject, e.g.:

cube(1).up(10).back(20).rotate(10, 0, 5).mirror(1, 0, 0).color("green").root()

SolidPython includes a number of convenience functions. Currently these include:

Directions for arranging things:

up, down, left, right, forward, fwd, back

Transformations per dimension:

translateX, translateY, translateZ, rotateX, rotateY, rotateZ, mirrorX,
mirrorY, mirrorZ, resizeX, resizeY, resizeZ, scaleX, scaleY, scaleZ

Furthermore the operations translate, scale, resize, mirror, rotate, cube and square are overwritten in a way that they accept single integer or float values as first parameter. (translate(1, 2, 3) equals translate([1, 2, 3]))

cylinder().rotateY(90).up(10)

seems a lot clearer to me than:

translate([0,0,10])(
    rotate([0, 90, 0])(
      cylinder()
))

SolidPython supports -- at least -- quite a lot of the bosl2 library. You can use it by importing the solid2.extensions.bosl2. Take a look at bosl2 example and mazebox example to get an idea how to use it and what's possible.

I would suggest to use it as kind of a standard library for SolidPython. Take a look at their Wiki to get an idea about it's features.

SolidPython supports the following features

SolidPython can be rendered inside a Jupyter Notebook using ViewScad. Unfortunately the pypi version of viewscad seems to be not compatible with solid2. @jreiberkyle created this viewscad fork and made it work with solid2 (#7)

SolidPython 2.x.x is a refactored version of SolidPython 1.x.x. The refactoring process was based on the following proposal: SolidCode#169

The goal was to

  • extract the "core" from SolidPython
  • make a solid package that only contains the fundamentals (+ a few convenience features)
  • make it extendible
  • try to get complex libraries working properly (mcad, bosl, bosl2)
  • KISS: from solid2 import * -> imports only ~1000 lines of source code and has (almost?) all the feautres SolidPython 1.x.x has
  • be a drop in replacement for SolidPython 1.x.x -- as far as possible, see Backwards Compatibility Section
  • get all kinds of nice features working (see Features section)

The result is a refactored and in some parts rewritten version of SolidPython we would like to release as SolidPython 2.x.x. The major improvement is a code base that should be better maintainable and extendible.

Besides these benefits SolidPython 2.x.x implemented quite a few nice new features (cf. Features section).

SolidPython 2.x.x has support for the following new features:

Furthermore it has several minor improvements, like these which are based on ideas from posts from the SolidPython universe:

  • use invert operator (~) as # in OpenSCAD #167
  • convenience function including to pass sizes as integer parameters (translate(10, 20, 30)) #63
  • access-style syntax: cube(1).up(5).rotate(45, 0, 0) #66 This is additional! The OpenSCAD / SolidPython style syntax is still fully supported.

Another nice little feature especially to play around and debug it is that the __repr__ operator of each "OpenSCADObject" now calls scad_render. With this the python shell becomes pretty good in debuging and playing around with solid code and the library itself:

>>> from solid2 import *
>>> c = cube(5)
>>> c.up(5)
translate(v = [0, 0, 5]) {
        cube(size = 5);
};
>>> c.up(5).save_as_scad()
'/home/xxx/xxx/xxx/SolidPython/expsolid_out.scad'
>>>

SolidPython 2.x.x should be a complete and mostly backwards compatible drop in replacement for SolidPython 1.x.x. The backwards compatibility is not 100% as depicted by the version number. Somethings (and even interfaces) changed. We tried to stay as backward compatible as possible. The package should behave 98% the same as SolidPython unless you do some "deep access" -- that's by 99% chance not backwards compatible (like modifying OpenSCADObjects or import internal modules).

As long as you stick to:

from solid2 import *

you shoul be fine.

solid.utils

solid.utils consisted of convenience functions and "modelling extensions" (kind of a small third party library like mcad, bosl, bosl2). The convenience functions are now -- or the missing ones are supposed to be -- part of solid2.extensions.convenience and are automatically importet with the main package.

Concerning the "modelling extensions" I would actually like to get rid of them as part of the SolidPython 2.x.x package. The resons are the following:

And a looooot more.....

I don't see why SolidPython should implement and maintain its own set of these features. Furthermore I assume a third party library (like bosl2) is probably able to provide more sophisticated implementations than we will ever be able to provide.

Please take a look at the bosl2 implementations. I did some very basic tests in examples/07-libs-bosl2.py and -- at least -- was able to create basic examples for the core solid.utils features using bosl2.

I would also be fine with a python third party library that implements these features, but I would like to seperate it from SolidPython itself. The reason is to achieve a SolidPython module which is independent from it (development, bugs, maintainance) with the goal to get an as solid and stable as possible SolidPython (core) package.

BUT, since I assume quite a few people out there are using solid.utils up until now and simply getting rid of it might cause some brouhaha, my suggestion for a compromise is the solid_legay extension.

solid2_legacy

The solid2_legacy extension is basicly everything that used to be solid.utils. Furhtermore it tries to "mimic" the SolidPython 1.x.x interface. This is the effort to become as backward compatible as possible. This might for example be useful when trying to get existing SolidPython 1.x.x code running.

The solid2_legacy extension got extracted into a seperate repo (and pip package). You should be able to just import the package if it is installed or somewhere in your import path.

If you want to use those features import the extension and take a look at it.

from solid2_legacy import *

Anyway SolidPython 1.x.x imports do not work with SolidPython 2.x.x! (see Interface changes - imoprt paths have changed)

I was able to get the SolidPython 1.x.x examples running just by changing the imports and they all (except for the splines example which seems to have an internal issue) worked "out of the box".

Interface changes

  • OpenSCAD identifier escaping:
    • all illegal python idetifiers are escape with a single prepending underscore
    • special variables $fn -> _fn (note: segments still works)
    • identifier starting with a digit module 12ptStar() -> _12ptStar() (note: __12ptStar still works)
    • python keywords module import() -> _import() (note: import\_ still works)
  • import paths have changed (a lot)
    • as long as you only import the root package it should be fine, otherwise probably not
    from solid2 import * #fine
    from solid2 import objects #crash
    from solid2 import solidpython #crash
    from solid2 import splines #crash
    from solid2 import utils #crash
  • all extensions have been moved:
    • solid.utils has been moved to solid2_legacy. If you want to use them import that extension
    • there are some example implementations of the part / hole feature and bill of materials in solid2_legacy. They seem to work but are not tested extensively. Take a look at examples/xx_legacy*.
    • please take a look at the bosl2 example. BOSL2 provides many features which might be alternatives.
  • OpenSCADObject internally changed a lot

    If you access it directly (e.g. mycube.set_modifier) this might not work. But if you import solid2_legacy some dummy methods will be monkey patched onto OpenSCADObject so you might be able to at least run the code, but it might render not correctly.

  • maybe some more things I can't remember. Some function signatures changed slightly. But as long as as you stick to the regular public interface everything should be fine.

Enjoy!

If you have any questions or bug reports please report them to the SolidPython GitHub page!

Cheers!

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

Full text of the license.

Some class docstrings are derived from the OpenSCAD User Manual, so are available under the Creative Commons Attribution-ShareAlike License.

About

A python frontend for solid modelling that compiles to OpenSCAD

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 83.4%
  • OpenSCAD 16.6%