diff --git a/cad/test1/Makefile b/cad/test1/Makefile
new file mode 100644
index 0000000..86a9f40
--- /dev/null
+++ b/cad/test1/Makefile
@@ -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)
diff --git a/cad/test1/README b/cad/test1/README
new file mode 100644
index 0000000..1044722
--- /dev/null
+++ b/cad/test1/README
@@ -0,0 +1,340 @@
+Comparison of Free scripted 3D CAD systems
+==========================================
+
+Werner Almesberger
+
+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/
+
+---------------------------------------------------------------------
diff --git a/cad/test1/button.py b/cad/test1/button.py
new file mode 100755
index 0000000..7e58864
--- /dev/null
+++ b/cad/test1/button.py
@@ -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")
diff --git a/cad/test1/button.scad b/cad/test1/button.scad
new file mode 100644
index 0000000..f5fc7bb
--- /dev/null
+++ b/cad/test1/button.scad
@@ -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();