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 solid 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 solid 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 solidpython
(You may need to use
sudo pip install solidpython
, 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/SolidCode/SolidPython.git
Include SolidPython at the top of your Python file:
from solid 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, runningpy_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
solid.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 solid 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 solid 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()
andinclude()
statements for importing SCAD code, and SolidPython has them, too. They pollute the global namespace, though, and you may have better luck withimport_scad()
,
Ex:
scadfile.scad
module box(w,h,d){ cube([w,h,d]); }
your_file.py
from solid 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, solid; print(os.path.dirname(solid.__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 solid.extensions.bosl2. Take a look at bosl2 example mazebox example to get an idea how to use it and what's possible.
You need to install BOSL2 into your OpenSCAD libraries folder (~/.local/share/OpenSCAD/libraries/) for the bosl2 exteions to work. Grab it from bosl2 github.
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
- native OpenSCAD customizer support customizer example greedy scad interface example
- native OpenSCAD animation support animation example and animation example 2
- custom fonts fonts example
- supports ImplicitCAD implicitCAD example implicitCAD example 2
- SolidPython is extendible extensions example 1 extension example 2
Render SolidPython or OpenSCAD code in Jupyter notebooks using ViewSCAD, or install directly via:
pip install viewscad
(Take a look at the repo page, though, since there's a tiny bit more installation required)
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 solid 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.
Besies 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:
- bosl2 - SolidPython is now able to handle bosl2 pretty well (don't know whether everything works, but quite a lot). bosl2 example and mazebox example
- native OpenSCAD customizer support customizer example and greedy scad interface example
- native OpenSCAD animation support animation example and animation example 2
- custom fonts fonts example
- supports ImplicitCAD implicitCAD example and implicitCAD example 2
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 solid 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 solid import *
you shoul be fine.
solid.utils
this is my (jeff) personal opinion and the base for design decisions for the current SolidPytho 2.x.x beta design. I'd like to have a discussion about it!
solid.utils
consists 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 solid.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:
- these modelling extensions (like extrude_along_path, splines, screw_threads, part_hole,...) don't align with the (core) purpose of SolidPython as I understand it (I think SolidPython is supposed to be a python "wrapper" / interface for OpenSCAD)
- these modelling extensions are "yet another implementation" of common modelling task that need to be maintained. I would prefere a SolidPython design where these features are outsourced into a third party library
- SolidPython 2.x.x has a pretty good bosl2 support and bosl2 has all (?) the features provided by solid.utils:
- extrude_along_path: https://github.com/revarbat/BOSL2/wiki/mutators.scad#module-path_extrude
- First-class Negative Space (Holes): https://github.com/revarbat/BOSL2/wiki/attachments.scad#module-diff
- Splines / Bezier: https://github.com/revarbat/BOSL2/wiki/beziers.scad
- Screw threads: https://github.com/revarbat/BOSL2/wiki/screws.scad https://github.com/revarbat/BOSL2/wiki/metric_screws.scad https://github.com/revarbat/BOSL2/wiki/threading.scad
- distributors: https://github.com/revarbat/BOSL2/wiki/distributors.scad
- bouding boxes: https://github.com/revarbat/BOSL2/wiki/mutators.scad#module-bounding_box
- arcs, pie slices, tubes, ...: https://github.com/revarbat/BOSL2/wiki/shapes3d.scad https://github.com/revarbat/BOSL2/wiki/drawing.scad
- cut models in "half" / by a plane: https://github.com/revarbat/BOSL2/wiki/mutators.scad#functionmodule-half_of
- attachments: https://github.com/revarbat/BOSL2/wiki/attachments.scad
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.extensions.legay extension.
solid.extensions.legacy
The solid.extensions.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.
If you want to use those features import the extension and take a look at it.
from solid.extensions.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 solid import * #fine from solid import objects #crash from solid import solidpython #crash from solid import splines #crash from solid import utils #crash
- all extensions have been moved:
- solid.utils has been moved to
solid.extensions.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
solid.extensions.legacy
. They seem to work but are not tested extensively. Take a look atexamples/xx_legacy*
. - please take a look at the bosl2 example. BOSL2 provides many features which might be alternatives.
- solid.utils has been moved to
- OpenSCADObject internally changed a lot
If you access it directly (e.g. mycube.set_modifier) this might not work. But if you import
solid.extensions.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.
Some class docstrings are derived from the OpenSCAD User Manual, so are available under the Creative Commons Attribution-ShareAlike License.