mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2024-12-19 12:27:11 +02:00
cad/test1/: experiment with Free scripted CAD systems (OpenSCAD and Cadmium)
This commit is contained in:
parent
c0395dc4c9
commit
2db4c1a3a8
15
cad/test1/Makefile
Normal file
15
cad/test1/Makefile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
OUT=scad.stl cadmium.stl
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(OUT)
|
||||||
|
|
||||||
|
scad.stl: button.scad
|
||||||
|
time openscad -s $@ $<
|
||||||
|
|
||||||
|
cadmium.stl: button.py
|
||||||
|
time ./$<
|
||||||
|
mv botton.stl $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OUT)
|
340
cad/test1/README
Normal file
340
cad/test1/README
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
Comparison of Free scripted 3D CAD systems
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
Werner Almesberger <werner@almesberger.net>
|
||||||
|
|
||||||
|
This is a brief evaluation of the scripted 3D CAD systems OpenSCAD
|
||||||
|
and Cadmium, comparing the workflow, resource consumption, and the
|
||||||
|
quality of the results.
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
This file and the sources of the models can be found in
|
||||||
|
http://projects.qi-hardware.com/index.php/p/wernermisc/source/tree/master/cad/test1/
|
||||||
|
|
||||||
|
|
||||||
|
Objectives
|
||||||
|
----------
|
||||||
|
|
||||||
|
This test aims to determine the general suitability of currently
|
||||||
|
available Free scripted 3D CAD system for the construction of
|
||||||
|
real-life objects.
|
||||||
|
|
||||||
|
Aspects considered were the ease or difficulty of model development,
|
||||||
|
the clarity of the modeling language, resource consumption during
|
||||||
|
rendering, and the quality of the resulting mesh.
|
||||||
|
|
||||||
|
A second objective was to evaluate the suitability of CSG as the only
|
||||||
|
means for constructing models suitable for large-scale industrial
|
||||||
|
production.
|
||||||
|
|
||||||
|
|
||||||
|
Object description
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The object to model is a simple button/key cap shape. The shape
|
||||||
|
consists of a top part shaped as a 10 x 15 mm rectangle with rounded
|
||||||
|
corners and at height of 1.5 mm. The top part rests on a base that's
|
||||||
|
0.5 mm thin and has a border of 1 mm on each side.
|
||||||
|
|
||||||
|
The corners of the rectangle are rounded with a radius of 2 mm. All
|
||||||
|
other external edges are rounded (chamfered) with a radius of 0.2 mm.
|
||||||
|
The edge where top and base meet is filleted with a radius of 0.4 mm.
|
||||||
|
|
||||||
|
Note that a real button would typically have an internal cavity,
|
||||||
|
possibly some depression or other structure on its top, and on the
|
||||||
|
bottom side a pusher in the middle and possibly other support
|
||||||
|
elements.
|
||||||
|
|
||||||
|
Also, if the design was to be used for injection molding, sidewalls
|
||||||
|
would be slightly tilted.
|
||||||
|
|
||||||
|
The rounding of the bottom plate is not strictly necessary and was
|
||||||
|
added for visual appearance.
|
||||||
|
|
||||||
|
|
||||||
|
Candidate 1: OpenSCAD
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
OpenSCAD [1] uses its own language, somewhat similar to POV-Ray's, to
|
||||||
|
describe 3D objects. It has an IDE with a quick preview capability
|
||||||
|
using OpenCSG [2].
|
||||||
|
|
||||||
|
High-quality rendering, e.g., to STL, is done with CGAL [3] and can
|
||||||
|
also be run non-interactively.
|
||||||
|
|
||||||
|
OpenSCAD and OpenCSG are licensed under the GNU GPL v2. Parts of CGAL
|
||||||
|
are licensed under the GNU LGPL v2.1 while others are licensed under
|
||||||
|
the incompatible QPL. See [4] for details.
|
||||||
|
|
||||||
|
The version tested was the openscad 2011.06-1+lucid1 Ubuntu package.
|
||||||
|
|
||||||
|
|
||||||
|
OpenSCAD front-ends
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
There also a number of Python-based scripted front-ends for OpenSCAD,
|
||||||
|
namely OpenSCADpy [5], PyOpenSCAD [6], and pySCAD [7].
|
||||||
|
|
||||||
|
Furthermore, there is Mecha [8, 9] for Haskell.
|
||||||
|
|
||||||
|
Cadmium (see below) appears to be on par or better in terms of syntax
|
||||||
|
clarity and tidiness than the OpenSCAD Python bindings. Therefore,
|
||||||
|
only pure OpenSCAD was considered for this comparison.
|
||||||
|
|
||||||
|
|
||||||
|
Candidate 2: Cadmium
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Cadmium [10] is similar in concept to OpenSCAD, but uses Python
|
||||||
|
instead of a homegrown language. Open CASCADE [11] (via pythonOCC
|
||||||
|
[12]) provides the 3D operations here.
|
||||||
|
|
||||||
|
The respective licenses are GNU AGPL v3 for Cadmium, GNU LGPL v3 for
|
||||||
|
pythonOCC, and a homegrown "LGPL-like" license [13] for Open CASCADE.
|
||||||
|
|
||||||
|
The Cadmium version tested was Sun Jul 10 16:04:07 2011 +0530 commit
|
||||||
|
d4ff63b150ee060a8179a74e369b5df3d0a4a3fc, with pythonOCC 0.5.
|
||||||
|
|
||||||
|
|
||||||
|
Results and observations
|
||||||
|
========================
|
||||||
|
|
||||||
|
Model development was efficient with both systems, with most of the
|
||||||
|
difficulties coming from the task of making the model, not from
|
||||||
|
inadequacies of the tools.
|
||||||
|
|
||||||
|
Both systems also also produced correct-looking meshes.
|
||||||
|
|
||||||
|
Notable differences exist in the time the rendering takes, where
|
||||||
|
rough previews with OpenSCAD are instantaneous and proper rendering
|
||||||
|
takes minutes, while Cadmium has no preview and the rendering takes
|
||||||
|
hours.
|
||||||
|
|
||||||
|
On the other hand, some small anomalies could be found in the mesh
|
||||||
|
generated by OpenSCAD while the Cadmium's mesh looks perfect.
|
||||||
|
|
||||||
|
|
||||||
|
Model development
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Both systems offer the same basic CSG primitives and operations,
|
||||||
|
which made the model development per se straightforward and the
|
||||||
|
porting from one system to the other effortless.
|
||||||
|
|
||||||
|
The very quick preview of OpenSCAD is immensely helpful during
|
||||||
|
development. The usefulness of the preview is diminished by
|
||||||
|
differences only being shown as unions of the solids involved, with
|
||||||
|
color indicating their role. It was thus often necessary to isolate
|
||||||
|
and simplify elements before the resulting shape could be guessed, or
|
||||||
|
to render with slower CGAL.
|
||||||
|
|
||||||
|
Given the slow rendering process, debugging non-trivial designs with
|
||||||
|
Cadmium is currently quite time-consuming.
|
||||||
|
|
||||||
|
Development of the basic model (without chamfers and fillets) was
|
||||||
|
first done with Cadmium. I then switched to OpenSCAD to develop the
|
||||||
|
more advanced features, and finally ported them back to the Cadmium
|
||||||
|
model.
|
||||||
|
|
||||||
|
Designing the model elements for filleting and chamfering was
|
||||||
|
somewhat awkward with only CSG and - without understanding the
|
||||||
|
entire construction process - it may not be easy to see what the
|
||||||
|
resulting code does.
|
||||||
|
|
||||||
|
|
||||||
|
Modeling language
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The limited programming language of OpenSCAD proved to be more than
|
||||||
|
adequate for this simple design. To ease comparison and to reduce the
|
||||||
|
porting effort, the Cadmium model has the same code structure as the
|
||||||
|
OpenSCAD model.
|
||||||
|
|
||||||
|
It should be noted that some redundancy could be avoided in Cadmium
|
||||||
|
if all the "rbox_*" functions were placed in a common class whose
|
||||||
|
objects could then remember the box's geometry for reuse with the
|
||||||
|
fillet and chamfer functions/methods.
|
||||||
|
|
||||||
|
One nuisance with OpenSCAD is that mistyped variable names merely
|
||||||
|
generate a warning but let rendering proceed - often with confusing
|
||||||
|
results.
|
||||||
|
|
||||||
|
One difficulty encountered when making the Cadmium model was that
|
||||||
|
there appears to be no null value for the "union" operation, which
|
||||||
|
means functions that generate all their objects in a loop have to
|
||||||
|
special-case the first element, making them look a bit awkward (e.g.,
|
||||||
|
rbox_chamfer_top_corners). It should be easy to remedy this
|
||||||
|
shortcoming.
|
||||||
|
|
||||||
|
The Python language also introduces complications to Cadmium that
|
||||||
|
OpenSCAD can avoid, such as the Python parser's limited ability to
|
||||||
|
detect continuation lines, requiring continuations to be marked with
|
||||||
|
a backslash, and the need to pay attention to the mixing of
|
||||||
|
floating-point and integer numbers when using divisions.
|
||||||
|
|
||||||
|
Cadmium's ability to use short operators instead of blocks generally
|
||||||
|
yielded only marginally more compact code, since many operations
|
||||||
|
ended up being longer than one line anyway. In fact, the code
|
||||||
|
structure often looks a bit tidier in OpenSCAD.
|
||||||
|
|
||||||
|
The placement of transformations before creation of the object in
|
||||||
|
OpenSCAD e.g.,
|
||||||
|
translate(...) rotate(...) cylinder(...);
|
||||||
|
is slightly less intuitive than the reverse order Cadmium uses, e.g.,
|
||||||
|
Cylinder(...).rotate(...).translate(...)
|
||||||
|
|
||||||
|
Furthermore, if each step is placed on a separate line, Cadmium's
|
||||||
|
syntax puts the object in a more prominent position than the list of
|
||||||
|
translations.
|
||||||
|
|
||||||
|
|
||||||
|
Bugs
|
||||||
|
----
|
||||||
|
|
||||||
|
OpenSCAD got stuck allocating excessive amounts of memory when trying
|
||||||
|
to preview with OpenCSG from the IDE.
|
||||||
|
|
||||||
|
Cadmium fails at line 113 of button.py if the "noise" parameter
|
||||||
|
introduced to work around this bug is absent or set to zero.
|
||||||
|
|
||||||
|
The mesh generated by Open SCAD appears to have some small anomalies,
|
||||||
|
see section "Resulting mesh".
|
||||||
|
|
||||||
|
|
||||||
|
Execution
|
||||||
|
---------
|
||||||
|
|
||||||
|
On a lightly loaded Intel Q6600, the "high quality" rendering time
|
||||||
|
was as follows:
|
||||||
|
|
||||||
|
real user sys
|
||||||
|
OpenSCAD 1m25.491s 1m24.990s 0m00.410s
|
||||||
|
Cadmium 81m44.408s 81m41.110s 0m01.540s
|
||||||
|
|
||||||
|
This is consistent with the time the rendering of earlier stages of
|
||||||
|
the design took: OpenSCAD with CGAL was always much faster than
|
||||||
|
Cadmium with Open CASCADE.
|
||||||
|
|
||||||
|
I didn't attempt to systematically search for costly operations, but
|
||||||
|
observed that the crossed cubes/boxes forming the core of the rounded
|
||||||
|
box took considerably longer than a run with one of them removed.
|
||||||
|
|
||||||
|
|
||||||
|
Resulting mesh
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The rendering results are available at
|
||||||
|
http://downloads.qi-hardware.com/people/werner/cad/test1/
|
||||||
|
|
||||||
|
The STL files are scad.stl.bz2 and cadmium.stl.bz2 for OpenSCAD and
|
||||||
|
Cadmium, respectively. scad.png and cadmium.png show screenshots of
|
||||||
|
the meshes rendered with MeshLab 1.2.2, with double side lighting and
|
||||||
|
"flat" rendering.
|
||||||
|
|
||||||
|
The two meshes are of similar size, as reported by MeshLab:
|
||||||
|
|
||||||
|
Vertices Faces
|
||||||
|
OpenSCAD 3351 7798
|
||||||
|
Cadmium 3183 8362
|
||||||
|
|
||||||
|
Note that the OpenSCAD model uses a slightly larger number of circle
|
||||||
|
segments (explicitly set with $fn) than the Cadmium model (which just
|
||||||
|
uses whatever is the default behaviour).
|
||||||
|
|
||||||
|
At earlier stages of the design, the Cadmium mesh was found to be
|
||||||
|
significantly larger then the OpenSCAD mesh.
|
||||||
|
|
||||||
|
Both meshes look clean and at a first glance show now major
|
||||||
|
distortions (*).
|
||||||
|
|
||||||
|
(*) Note that the model already takes care of avoiding situations
|
||||||
|
where the subtraction of volumes could leave behind solids with
|
||||||
|
the thickness of a rounding error.
|
||||||
|
|
||||||
|
When viewed with MeshLab 1.2.2, with smooth rendering and
|
||||||
|
"Fancy Lighting", some faces appear to be inverted. These faces are
|
||||||
|
shown in red in
|
||||||
|
http://downloads.qi-hardware.com/people/werner/cad/test1/scad-reversed.png
|
||||||
|
|
||||||
|
A peek at the inside of the OpenSCAD-generated mesh reveals internal
|
||||||
|
structures left over from the construction process, as shown on
|
||||||
|
http://downloads.qi-hardware.com/people/werner/cad/test1/scad-inside.png
|
||||||
|
|
||||||
|
No anomalies could be found in the mesh generated by Cadmium.
|
||||||
|
|
||||||
|
|
||||||
|
Conclusion
|
||||||
|
==========
|
||||||
|
|
||||||
|
In the conclusions, I first consider the relative performance of the
|
||||||
|
two CAD system and then reflect on the whether the CSG-only workflow
|
||||||
|
as such proved to be satisfactory.
|
||||||
|
|
||||||
|
|
||||||
|
OpenSCAD vs. Cadmium
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Both systems succeeded in handling the task. OpenSCAD impressed with
|
||||||
|
fast response allowing highly interactive development, while Cadmium
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
soon gets very slow. It is not clear whether this slowness is a
|
||||||
|
general shortcoming of Cadmium or whether it is a consequence of poor
|
||||||
|
choices made when making the model.
|
||||||
|
|
||||||
|
The mesh generated by OpenSCAD shows some anomalies, but it's not
|
||||||
|
clear whether they would affect further processing steps, e.g.,
|
||||||
|
conversion to toolpaths.
|
||||||
|
|
||||||
|
In terms of resource consumption and stability, even this relatively
|
||||||
|
simple model exhausted both systems, with OpenSCAD exhibiting
|
||||||
|
stability issues and Cadmium requiring excessive processing time.
|
||||||
|
|
||||||
|
Both modeling languages can be used in very similar ways and were
|
||||||
|
pleasant to use. Python-based Cadmium may be more suitable for tasks
|
||||||
|
requiring structured building blocks.
|
||||||
|
|
||||||
|
|
||||||
|
The CSG-only workflow
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
With both systems, translating the mental models of the various
|
||||||
|
components into correct instructions was difficult where more
|
||||||
|
abstract operations were involved, requiring some amount of trial and
|
||||||
|
error.
|
||||||
|
|
||||||
|
Also, the resulting code does not easily reveal its purpose and
|
||||||
|
textual comments are an unsatisfactory means of illustrating
|
||||||
|
geometrical properties. (As an example, consider the above section
|
||||||
|
"Object description".)
|
||||||
|
|
||||||
|
A workflow that includes distinct steps with a visual representation
|
||||||
|
of intermediate results, e.g., instead of CSG, using extrusion with
|
||||||
|
shapes and paths generated by some 2D CAD system, may be less
|
||||||
|
demanding.
|
||||||
|
|
||||||
|
Also, while generating the basic shape was very easy, most of the
|
||||||
|
work went into the addition of fillets and chamfers. Neither of the
|
||||||
|
two systems provides operations to automate such tasks.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
[1] http://www.openscad.org/
|
||||||
|
[2] http://www.opencsg.org/
|
||||||
|
[3] http://www.cgal.org/
|
||||||
|
[4] http://www.cgal.org/license.html
|
||||||
|
[5] https://github.com/hmeyer/openscadpy
|
||||||
|
[6] https://github.com/etjones/MCAD/tree/master/PyOpenScad
|
||||||
|
[7] https://github.com/kevinmehall/pyscad
|
||||||
|
[8] http://hackage.haskell.org/package/mecha/
|
||||||
|
[9] https://github.com/tomahawkins/mecha/blob/master/Language/Mecha/Examples/CSG.hs
|
||||||
|
[10] http://jayesh3.github.com/cadmium/
|
||||||
|
[11] http://www.opencascade.org/
|
||||||
|
[12] http://www.pythonocc.org/
|
||||||
|
[13] http://www.opencascade.org/getocc/license/
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
146
cad/test1/button.py
Executable file
146
cad/test1/button.py
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# all dimensions are mm
|
||||||
|
|
||||||
|
from cadmium import *
|
||||||
|
|
||||||
|
|
||||||
|
epsilon = 0.01
|
||||||
|
noise = epsilon/10
|
||||||
|
|
||||||
|
but_top_x = 10.0
|
||||||
|
but_top_y = but_top_x+5.0
|
||||||
|
but_top_z = 1.5
|
||||||
|
|
||||||
|
but_corner_r = 2.0
|
||||||
|
|
||||||
|
but_base_border = 1.0
|
||||||
|
but_base_x = but_top_x+2*but_base_border
|
||||||
|
but_base_y = but_top_y+2*but_base_border
|
||||||
|
but_base_z = 0.5
|
||||||
|
|
||||||
|
but_fillet_r = 0.4
|
||||||
|
but_chamfer_r = 0.2
|
||||||
|
|
||||||
|
|
||||||
|
# ----- Helper elements for fillets -------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def fillet_line(x, r):
|
||||||
|
s = Box(x, r, r)
|
||||||
|
s -= Cylinder(r, h = x+2*epsilon). \
|
||||||
|
translate(0, 0, -epsilon). \
|
||||||
|
rotate(Y_axis, 90). \
|
||||||
|
translate(0, r, r)
|
||||||
|
return s.translate(-x/2, 0, 0)
|
||||||
|
|
||||||
|
def fillet_circle(r, fillet_r):
|
||||||
|
return Cylinder(r+fillet_r, h = fillet_r)- \
|
||||||
|
Torus(r+fillet_r, fillet_r, center = True). \
|
||||||
|
translate(0, 0, fillet_r)
|
||||||
|
|
||||||
|
|
||||||
|
# ----- Helper elements for chamfers ------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def chamfer_line (x, r):
|
||||||
|
s = Box(x, r+epsilon, r+epsilon)
|
||||||
|
s -= Cylinder(r, h = x+2*epsilon). \
|
||||||
|
translate(0, 0, -epsilon). \
|
||||||
|
rotate(Y_axis, 90)
|
||||||
|
return s.translate(-x/2, -r, -r)
|
||||||
|
|
||||||
|
def chamfer_circle(r, fillet_r):
|
||||||
|
return Box(2*(r+epsilon), 2*(r+epsilon), fillet_r+epsilon). \
|
||||||
|
translate(-r-epsilon, -r-epsilon, -fillet_r)- \
|
||||||
|
Cylinder(r-fillet_r, h = fillet_r). \
|
||||||
|
translate(0, 0, -fillet_r)- \
|
||||||
|
Torus(r-fillet_r, fillet_r, center = True). \
|
||||||
|
translate(0, 0, -fillet_r)
|
||||||
|
|
||||||
|
|
||||||
|
# ----- Box with rounded corners ----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def rbox_core(x, y, z, r):
|
||||||
|
return Box(x-2*r, y, z, center = True).translate(0, 0, z/2)+ \
|
||||||
|
Box(x, y-2*r, z, center = True).translate(0, 0, z/2)
|
||||||
|
|
||||||
|
def rbox(x, y, z, r):
|
||||||
|
s = rbox_core(x, y, z, r)
|
||||||
|
for dx in [-1, 1]:
|
||||||
|
for dy in [-1, 1]:
|
||||||
|
s += Cylinder(r, h = z). \
|
||||||
|
translate(dx*(x/2-r), dy*(y/2-r), 0)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def rbox_fillet_bottom(x, y, z, r, fillet_r):
|
||||||
|
s = None
|
||||||
|
for a in [0, 180]:
|
||||||
|
t = fillet_line(x-2*r, fillet_r). \
|
||||||
|
translate(0, y/2, 0). \
|
||||||
|
rotate(Z_axis, a)
|
||||||
|
if s is None:
|
||||||
|
s = t
|
||||||
|
else:
|
||||||
|
s += t
|
||||||
|
s += fillet_line(y-2*r, fillet_r). \
|
||||||
|
translate(0, x/2, 0). \
|
||||||
|
rotate(Z_axis, a+90)
|
||||||
|
for dx in [-1, 1]:
|
||||||
|
for dy in [-1, 1]:
|
||||||
|
s += fillet_circle(r, fillet_r). \
|
||||||
|
translate(dx*(x/2-r), dy*(y/2-r), 0)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def rbox_chamfer_top_corners(x, y, z, r, chamfer_r):
|
||||||
|
s = None
|
||||||
|
for dx in [-1, 1]:
|
||||||
|
for dy in [-1, 1]:
|
||||||
|
t = chamfer_circle(r, chamfer_r). \
|
||||||
|
translate(dx*(x/2-r), dy*(y/2-r), z)
|
||||||
|
if s is None:
|
||||||
|
s = t
|
||||||
|
else:
|
||||||
|
s += t
|
||||||
|
return s-rbox_core(x-epsilon, y-epsilon, z, r)
|
||||||
|
|
||||||
|
def rbox_chamfer_top(x, y, z, r, chamfer_r):
|
||||||
|
s = rbox_chamfer_top_corners(x, y, z, r, chamfer_r)
|
||||||
|
for a in [0, 180]:
|
||||||
|
s += chamfer_line(x-2*r, chamfer_r). \
|
||||||
|
translate(0, y/2, z+noise). \
|
||||||
|
rotate(Z_axis, a)
|
||||||
|
s += chamfer_line(y-2*r, chamfer_r). \
|
||||||
|
translate(0, x/2, z+noise). \
|
||||||
|
rotate(Z_axis, a+90)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def rbox_chamfer_bottom(x, y, z, r, chamfer_r):
|
||||||
|
return rbox_chamfer_top(x, y, z, r, chamfer_r). \
|
||||||
|
translate(0, 0, -z). \
|
||||||
|
rotate(X_axis, 180)
|
||||||
|
|
||||||
|
# ----- Button ----------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def button_top():
|
||||||
|
return rbox(but_top_x, but_top_y, but_top_z, but_corner_r)- \
|
||||||
|
rbox_chamfer_top(but_top_x, but_top_y, but_top_z, \
|
||||||
|
but_corner_r, but_chamfer_r)+ \
|
||||||
|
rbox_fillet_bottom(but_top_x, but_top_y, but_top_z, but_corner_r,
|
||||||
|
but_fillet_r)
|
||||||
|
|
||||||
|
def button_base():
|
||||||
|
s = rbox(but_base_x, but_base_y, but_base_z, but_corner_r)- \
|
||||||
|
rbox_chamfer_top(but_base_x, but_base_y, but_base_z, \
|
||||||
|
but_corner_r, but_chamfer_r)- \
|
||||||
|
rbox_chamfer_bottom(but_base_x, but_base_y, but_base_z, \
|
||||||
|
but_corner_r, but_chamfer_r)
|
||||||
|
return s.translate(0, 0, -but_base_z)
|
||||||
|
|
||||||
|
def button():
|
||||||
|
return button_top()+button_base()
|
||||||
|
|
||||||
|
b = button()
|
||||||
|
b.toSTL("button.stl")
|
216
cad/test1/button.scad
Normal file
216
cad/test1/button.scad
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
epsilon = 0.01;
|
||||||
|
|
||||||
|
but_top_x = 10;
|
||||||
|
but_top_y = but_top_x+5;
|
||||||
|
but_top_z = 1.5;
|
||||||
|
|
||||||
|
but_corner_r = 2;
|
||||||
|
|
||||||
|
but_base_border = 1;
|
||||||
|
but_base_x = but_top_x+2*but_base_border;
|
||||||
|
but_base_y = but_top_y+2*but_base_border;
|
||||||
|
but_base_z = 0.5;
|
||||||
|
|
||||||
|
but_push_r = 5;
|
||||||
|
but_push_z = 0.5;
|
||||||
|
|
||||||
|
but_fillet_r = 0.4;
|
||||||
|
but_chamfer_r = 0.2;
|
||||||
|
|
||||||
|
$fn = 40;
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- Basic solids ------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
module torus(r0, r1)
|
||||||
|
{
|
||||||
|
rotate_extrude()
|
||||||
|
translate([r0, 0, 0])
|
||||||
|
circle(r = r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- Helper elements for fillets --------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
module fillet_line(x, r)
|
||||||
|
{
|
||||||
|
translate([-x/2, 0, 0])
|
||||||
|
difference() {
|
||||||
|
cube([x, r, r]);
|
||||||
|
translate([0, r, r])
|
||||||
|
rotate([0, 90, 0])
|
||||||
|
translate([0, 0, -epsilon])
|
||||||
|
cylinder(h = x+2*epsilon, r = r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module fillet_circle(r, fillet_r)
|
||||||
|
{
|
||||||
|
difference() {
|
||||||
|
cylinder(h = fillet_r, r = r+fillet_r);
|
||||||
|
translate([0, 0, fillet_r])
|
||||||
|
torus(r+fillet_r, fillet_r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- Helper elements for chamfers -------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
module chamfer_line(x, r)
|
||||||
|
{
|
||||||
|
translate([-x/2, -r, -r])
|
||||||
|
difference() {
|
||||||
|
cube([x, r+epsilon, r+epsilon]);
|
||||||
|
rotate([0, 90, 0])
|
||||||
|
translate([0, 0, -epsilon])
|
||||||
|
cylinder(h = x+2*epsilon, r = r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module chamfer_circle(r, fillet_r)
|
||||||
|
{
|
||||||
|
difference() {
|
||||||
|
translate([-r-epsilon, -r-epsilon, -fillet_r])
|
||||||
|
cube([2*(r+epsilon), 2*(r+epsilon), fillet_r+epsilon]);
|
||||||
|
translate([0, 0, -fillet_r])
|
||||||
|
cylinder(h = fillet_r, r = r-fillet_r);
|
||||||
|
translate([0, 0, -fillet_r])
|
||||||
|
torus(r-fillet_r, fillet_r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- Box with rounded corners ------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
module rbox_core(x, y, z, r)
|
||||||
|
{
|
||||||
|
union() {
|
||||||
|
translate([0, 0, z/2])
|
||||||
|
cube([x-2*r, y, z], center = true);
|
||||||
|
translate([0, 0, z/2])
|
||||||
|
cube([x, y-2*r, z], center = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module rbox(x, y, z, r)
|
||||||
|
{
|
||||||
|
union() {
|
||||||
|
rbox_core(x, y, z, r);
|
||||||
|
for (dx = [-1, 1]) {
|
||||||
|
for (dy = [-1, 1]) {
|
||||||
|
translate([dx*(x/2-r), dy*(y/2-r), 0])
|
||||||
|
cylinder(h = z, r = r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module rbox_fillet_bottom(x, y, z, r, fillet_r)
|
||||||
|
{
|
||||||
|
union() {
|
||||||
|
for (a = [0, 180]) {
|
||||||
|
rotate([0, 0, a])
|
||||||
|
translate([0, y/2, 0])
|
||||||
|
fillet_line(x-2*r, fillet_r);
|
||||||
|
rotate([0, 0, a+90])
|
||||||
|
translate([0, x/2, 0])
|
||||||
|
fillet_line(y-2*r, fillet_r);
|
||||||
|
}
|
||||||
|
for (dx = [-1, 1]) {
|
||||||
|
for (dy = [-1, 1]) {
|
||||||
|
translate([dx*(x/2-r), dy*(y/2-r), 0])
|
||||||
|
fillet_circle(r, fillet_r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module rbox_chamfer_top_corners(x, y, z, r, chamfer_r)
|
||||||
|
{
|
||||||
|
difference() {
|
||||||
|
union() {
|
||||||
|
for (dx = [-1, 1]) {
|
||||||
|
for (dy = [-1, 1]) {
|
||||||
|
translate([dx*(x/2-r), dy*(y/2-r), z])
|
||||||
|
chamfer_circle(r, chamfer_r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rbox_core(x-epsilon, y-epsilon, z, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module rbox_chamfer_top(x, y, z, r, chamfer_r)
|
||||||
|
{
|
||||||
|
union() {
|
||||||
|
for (a = [0, 180]) {
|
||||||
|
rotate([0, 0, a])
|
||||||
|
translate([0, y/2, z])
|
||||||
|
chamfer_line(x-2*r, chamfer_r);
|
||||||
|
rotate([0, 0, a+90])
|
||||||
|
translate([0, x/2, z])
|
||||||
|
chamfer_line(y-2*r, chamfer_r);
|
||||||
|
}
|
||||||
|
rbox_chamfer_top_corners(x, y, z, r, chamfer_r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module rbox_chamfer_bottom(x, y, z, r, chamfer_r)
|
||||||
|
{
|
||||||
|
rotate([180, 0, 0])
|
||||||
|
translate([0, 0, -z])
|
||||||
|
rbox_chamfer_top(x, y, z, r, chamfer_r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- Button ------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
module button_top()
|
||||||
|
{
|
||||||
|
union() {
|
||||||
|
difference() {
|
||||||
|
rbox(but_top_x, but_top_y, but_top_z, but_corner_r);
|
||||||
|
rbox_chamfer_top(but_top_x, but_top_y, but_top_z, but_corner_r, but_chamfer_r);
|
||||||
|
}
|
||||||
|
rbox_fillet_bottom(but_top_x, but_top_y, but_top_z,
|
||||||
|
but_corner_r, but_fillet_r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module button_base()
|
||||||
|
{
|
||||||
|
translate([0, 0, -but_base_z])
|
||||||
|
difference() {
|
||||||
|
rbox(but_base_x, but_base_y, but_base_z, but_corner_r);
|
||||||
|
rbox_chamfer_top(but_base_x, but_base_y, but_base_z,
|
||||||
|
but_corner_r, but_chamfer_r);
|
||||||
|
rbox_chamfer_bottom(but_base_x, but_base_y, but_base_z,
|
||||||
|
but_corner_r, but_chamfer_r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module button()
|
||||||
|
{
|
||||||
|
union() {
|
||||||
|
button_top();
|
||||||
|
button_base();
|
||||||
|
// button_pusher();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button();
|
Loading…
Reference in New Issue
Block a user