mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2024-12-19 10:17:31 +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