piccolo

3d Printable Piccolo
git clone https://git.woozle.org/neale/piccolo.git

commit
28cf438
parent
1bf3902
author
Neale Pickett
date
2025-10-24 15:49:21 -0600 MDT
more tweaks, add sigil
9 files changed,  +173, -231
A .gitignore
+1, -0
1@@ -0,0 +1 @@
2+*.3mf
M Makefile
+3, -1
 1@@ -1,7 +1,9 @@
 2 all: head.3mf body.3mf
 3 
 4+rev=$(shell git rev-list --count HEAD)
 5+
 6 %.3mf: %.scad piccolo.scad common.scad
 7-	openscad -o $@ $<
 8+	openscad -D rev=\"$(rev)\" -o $@ $<
 9 
10 .PHONY: clean
11 clean:
A assembly.scad
+7, -0
1@@ -0,0 +1,7 @@
2+use <body.scad>
3+use <head.scad>
4+
5+body();
6+translate([0, 0, 83+189.68+25]) {
7+  rotate([0, 180, 0]) head();
8+}
M body.scad
+69, -1
 1@@ -1,3 +1,71 @@
 2-use <piccolo.scad>
 3+include <common.scad>
 4+use <MCAD/regular_shapes.scad>
 5+
 6+body_height = 189.68 + 25; // add 25 for built-in barrel
 7+rev = "P"; // revision to stamp on the side
 8+
 9+module body() {
10+  d1 = 17.0;
11+  d2 = 20.4;
12+  toneholes = [
13+               [40, 7.0],
14+               [66, 8.5],
15+               [81, 7.0],
16+               [106.5, 7.0],
17+               [125.0, 7.5],
18+               [144.4, 7.0],
19+               ];
20+
21+
22+  union() {
23+    difference() {
24+      // Combine cylinders to make it look curvy
25+      union() {
26+        bend = 80;
27+        cylinder(h=body_height, d1=d1, d2=(d1+d2)/2);
28+        translate([0, 0, body_height-bend]) cylinder(h=bend, d1=d1, d2=d2);
29+      }
30+
31+      // Inner bore, the most important part of the whole instrument!
32+      translate([0, 0, -1]) cylinder(h=body_height+2, d1=id_bot, d2=id_top);
33+
34+      // Tone holes
35+      for (hole = toneholes) {
36+        translate([0, 0, hole[0]]) tonehole(d=hole[1]);
37+      }
38+
39+      // Mortise
40+      translate([0, 0, body_height-25]) {
41+        d = 16.9;
42+        cylinder(h=25, d=d);
43+        translate([0, 0, -d/2]) cylinder(h=d/2, d1=0, d2=d);
44+      }
45+
46+      translate([0, 0, 4]) intersection() {
47+        cylinder_tube(50, d1/2+0.5, 1);
48+        union() {
49+          rotate([90, 90, -90]) {
50+            linear_extrude(height=d1, convexity=4) {
51+              translate([-9, -5]) import("ruby.svg");
52+            }
53+          }
54+          rotate([90, 90, -180]) {
55+            linear_extrude(height=d1, convexity=10) {
56+              text(rev, size=5, halign="right", valign="center");
57+            }
58+          }
59+        }
60+      }
61+    }
62+
63+    // Rings
64+    translate([0, 0, body_height-1.5]) torus((d2+3)/2, (d2-3)/2);
65+    difference() {
66+      // Fat one at the bottom, for good bed adhesion
67+      translate([0, 0, 0]) torus((d1+3)/2, (d1-3)/2);
68+      translate([0, 0, -25]) cube(50, center=true);
69+    }
70+  }
71+}
72 
73 body();
M common.scad
+2, -148
  1@@ -1,154 +1,8 @@
  2 // Make circles circular in final renders.
  3 $fn = $preview ? 30 : 180;
  4 
  5-// clearance in a permanent tenon/mortise joint
  6-clearance = 0.2;
  7-
  8-// www.tauday.com
  9-TAU = PI * 2;
 10-
 11-module brass() {
 12-  color("gold") children();
 13-}
 14-
 15-module derlin() {
 16-  color("dimgray") children();
 17-}
 18-
 19-module ivory() {
 20-  color("ivory") children();
 21-}
 22-
 23-// tube creates a hollow cylinder
 24-module tube(h=10, id=5, od=10) {
 25-  difference() {
 26-    cylinder(h=h, d=od);
 27-    translate([0, 0, -1]) cylinder(h=h+2, d=id);
 28-  }
 29-}
 30-
 31-// default height of rings
 32-rings_height = 1.8;
 33-
 34-// rings of adornment.
 35-// This engraves two rings, 1.4mm apart, at a depth of 0.4.
 36-module rings(h=0.4, d=1, depth=0.4, spacing=1.4) {
 37-  tube(h=h, id=d-depth, od=d+1);
 38-  translate([0, 0, spacing]) tube(h=h, id=d-depth, od=d+1);
 39-}
 40-
 41-// default brass tube thickness
 42-brass_thickness = 0.75;
 43-
 44-// brass_tube creates a chamfered hollow cylinder with given inside diameter,
 45-// and wall thickness of 0.75.
 46-// Decorative rings are etched into the top and bottom.
 47-module brass_tube(h=10, id=10, t=brass_thickness) {
 48-  od = id + (t*2);
 49-  cd = (id + od) / 2; // Chamfer diameter
 50-
 51-  brass() {
 52-    difference() {
 53-      tube(h=h, id=id, od=od);
 54-
 55-      // Chamfered ends
 56-      translate([0, 0, 0]) cylinder(h=1, d1=cd, d2=id);
 57-      translate([0, 0, h-1]) cylinder(h=1, d1=id, d2=cd);
 58-
 59-      translate([0, 0, 2]) rings(d=od);
 60-      translate([0, 0, h-2-rings_height]) rings(d=od);
 61-    }
 62-  }
 63-}
 64-
 65-// brass_bend provides a 180 degree bend in brass
 66-module brass_bend(od=12.7, bend_r=16, t=brass_thickness) {
 67-  color("gold") {
 68-    rotate([90, 0, 0]) {
 69-      rotate_extrude(angle=180) {
 70-        translate([bend_r, 0, 0]) {
 71-          difference() {
 72-            circle(d=od);
 73-            circle(d=od-t*2);
 74-          }
 75-        }
 76-      }
 77-    }
 78-  }
 79-}
 80-
 81-
 82-// derlin_tube is a non-chamfered sky blue tube.
 83-// I use blue so it's easier to see in preview.
 84-module derlin_tube(h=10, id=5, od=10) {
 85-  derlin() {
 86-    tube(h=h, id=id, od=od);
 87-  }
 88-}
 89-
 90-// derlin_stop is a derlin tube, chamfered on one end.
 91-module derlin_stop(h=10, id=5, od=10) {
 92-  chamferh = h/5;
 93-  derlin() {
 94-    difference() {
 95-      union() {
 96-        cylinder(h=h-chamferh, d=od);
 97-        translate([0, 0, h-chamferh]) cylinder(h=chamferh, d1=od, d2=id);
 98-        }
 99-      cylinder(h=h, d=id);
100-      }
101-    }
102-}
103-
104-// default bell height
105-bell_height = 17;
106-
107-// derlin_bell makes a decorative flared bell.
108-// od will default to 33% larger than id.
109-module derlin_bell(h=bell_height, id=5, od=0) {
110-  chamferh = 1;
111-  chamferd = 1;
112-  ringsmargin = 1;
113-  toph = rings_height + ringsmargin*2 + chamferh;
114-  flareh = h - toph;
115-  od = (od == 0) ? (id * 1.33) : od;
116-  derlin() {
117-    difference() {
118-      union() {
119-        cylinder(h=flareh, d1=id, d2=od);
120-        translate([0, 0, flareh]) cylinder(h=toph-chamferh, d=od);
121-        translate([0, 0, flareh+toph-chamferh]) cylinder(h=chamferh, d1=od, d2=od-chamferd);
122-      }
123-      cylinder(h=h, d=id);
124-      translate([0, 0, flareh + ringsmargin]) rings(d=od);
125-    }
126-  }
127-}
128-
129-// A tenon joint.
130-//
131-// You have to do this twice, to make two pieces. Glue them together.
132-module tenon(h, od, top, depth=20, thickness=2) {
133-  cl = top ? 0 : clearance;
134-  module cut() {
135-    cutd = od - thickness - cl;
136-    cylinder(h=h+depth, d=cutd);
137-    cylinder(h=h, d=50);
138-    translate([0, 0, h + depth]) cylinder(h=cutd, d1=cutd, d2=0);
139-  }
140-
141-  if (top) {
142-    difference() {
143-      children();
144-      cut();
145-    }
146-  } else {
147-    intersection() {
148-      children();
149-      cut();
150-    }
151-  }
152-}
153+id_bot = 10.5;
154+id_top = 12.6;
155 
156 // Round tone hole
157 module tonehole(d=10, h=50) {
M head.3mf
+0, -0
M head.scad
+63, -1
 1@@ -1,3 +1,65 @@
 2-use <piccolo.scad>
 3+include <common.scad>
 4+use <MCAD/regular_shapes.scad>
 5+
 6+module head() {
 7+  od_top = 20.4;
 8+  union() {
 9+    difference() {
10+      union() {
11+        // Body
12+        cylinder(h=83, d=od_top);
13+
14+        // Tenon
15+        color("white") cylinder(h=108, d=16.6);
16+
17+        difference() {
18+          // Fat one at the bottom, for good bed adhesion
19+          translate([0, 0, 0]) torus((od_top+3)/2, (od_top-3)/2);
20+          translate([0, 0, -25]) cube(50, center=true);
21+        }
22+      }
23+
24+      // Area for the string
25+      translate([0, 0, 90]) {
26+        r = 16.6/2;
27+        difference() {
28+          cylinder_tube(15, r, 0.4);
29+
30+          // fillets
31+          cylinder(r1=r, r2=0, h=r);
32+          translate([0, 0, 15-r]) cylinder(r1=0, r2=r, h=r);
33+        }
34+      }
35+
36+      // Inner bore
37+      translate([0, 0, 17]) cylinder(h=200, d=id_top);
38+
39+      // Resonance chamber
40+      translate([0, 0, -1]) cylinder(h=18+1, d=6.5);
41+      translate([0, 0, -0.1]) cylinder(h=10+.2, d1=15.5, d2=6.5); // d1=15.5 in Marat's model, mine's smaller for better bed adhesion
42+      translate([0, 0, 16]) rotate([30, 0, -30]) cylinder(d=6, h=200);
43+
44+      // Embouchure
45+      translate([0, 0, 45]) {
46+        rotate([90, 0, 0]) {
47+          hull() {
48+            for (y = [0, 4]) {
49+              translate([0, y, 0]) cylinder(d1=13, d2=7, h=id_top);
50+            }
51+          }
52+        }
53+      }
54+    }
55+
56+    // Resonance chamber plate
57+    intersection() {
58+      cylinder(h=83, d=id_top+1);
59+      translate([0, 0, 25]) rotate([-60, 30, 0]) cube([200, 200, 0.8], center=true);
60+    }
61+
62+    translate([0, 0, 8.5]) torus((od_top+3)/2, (od_top-3)/2);
63+    translate([0, 0, 83-3/2]) torus((od_top+3)/2, (od_top-3)/2);
64+  }
65+}
66 
67 head();
M piccolo.scad
+19, -80
  1@@ -1,92 +1,31 @@
  2-include <common.scad>
  3-use <MCAD/regular_shapes.scad>
  4+// Make circles circular in final renders.
  5+$fn = $preview ? 30 : 180;
  6 
  7-height = 189.68;
  8 id_bot = 10.5;
  9 id_top = 12.6;
 10 
 11-toneholes = [
 12-  [40, 7.0],
 13-  [66, 8.5],
 14-  [81, 7.0],
 15-  [106.5, 7.0],
 16-  [125.0, 7.5],
 17-  [144.4, 7.0],
 18-];
 19-
 20-module body() {
 21-  union() {
 22-    d1 = 17.0;
 23-    d2 = 21.6;
 24-    difference() {
 25-      cylinder(h=height, d1=d1, d2=d2);
 26-      translate([0, 0, -1]) cylinder(h=height+2, d1=id_bot, d2=id_top);
 27-      for (hole = toneholes) {
 28-        translate([0, 0, hole[0]]) tonehole(d=hole[1]);
 29-      }
 30-
 31-      translate([0, 0, height-25]) {
 32-        d = 16.9;
 33-        cylinder(h=25, d=d);
 34-      }
 35-    }
 36-    // rings
 37-    translate([0, 0, height-1.5]) torus((d2+3)/2, (d2-3)/2);
 38-    difference() {
 39-      translate([0, 0, 1.5]) torus((d1+3)/2, (d1-3)/2);
 40-      translate([0, 0, -25]) cube(50, center=true);
 41-    }
 42-  }
 43+// Round tone hole
 44+module tonehole(d=10, h=50) {
 45+  rotate([90, 0, 0]) cylinder(d=d, h=h);
 46 }
 47 
 48-module head() {
 49-  od_top = 20.4;
 50-  union() {
 51-    difference() {
 52-      union() {
 53-        // Body
 54-        cylinder(h=83, d=od_top);
 55-
 56-        // Tenon
 57-        color("white") cylinder(h=108, d=16.6);
 58-      }
 59-
 60-      // Area for the string
 61-      translate([0, 0, 90]) {
 62-        r = 16.6/2;
 63-        difference() {
 64-          cylinder_tube(15, r, 0.4);
 65+// Axianov tonehole with the same surface area as an equivalent cylinder
 66+module axianov_hole(d=10, h=50) {
 67+  r = d/2;
 68+  area = PI*r*r;
 69 
 70-          // fillets
 71-          cylinder(r1=r, r2=0, h=r);
 72-          translate([0, 0, 15-r]) cylinder(r1=0, r2=r, h=r);
 73-        }
 74-      }
 75+  rounding_r = 2;
 76+  rounding_area = (1 - PI/4) * (rounding_r^2); // area lost to rounding
 77+  goal_area = area + rounding_area;
 78 
 79-      // Inner bore
 80-      translate([0, 0, 17]) cylinder(h=200, d=id_top);
 81+  w = max(6.25, d-4); // Marat uses 6.25mm on every hole, so we will too
 82+  l = goal_area / w;
 83 
 84-      // Resonance chamber
 85-      translate([0, 0, -1]) cylinder(h=18+1, d=6.5);
 86-      translate([0, 0, -0.1]) cylinder(h=10+.2, d1=15.5, d2=6.5);
 87-      translate([0, 0, 20]) rotate([45, 0, -30]) cylinder(d=6, h=200);
 88-
 89-      // Embouchure
 90-      translate([0, 0, 40]) {
 91-        scale([1, 1, 1.2])
 92-          rotate([90, 0, 0])
 93-          cylinder(d1=id_top, d2=7, h=id_top);
 94-      }
 95-    }
 96-
 97-
 98-    intersection() {
 99-      cylinder(h=83, d=id_top+1);
100-      translate([0, 0, 25]) rotate([-45, 30, 0]) cube([200, 200, 0.8], center=true);
101+  translate([-w/2, -l/2, 0]) {
102+    union() {
103+      translate([rounding_r, rounding_r, 0]) cylinder(r=rounding_r, h=h);
104+      translate([rounding_r, 0, 0]) cube([w-rounding_r, l, h]);
105+      translate([0, rounding_r, 0]) cube([w, l-rounding_r, h]);
106     }
107-
108-    translate([0, 0, 8.5]) torus((od_top+3)/2, (od_top-3)/2);
109-    translate([0, 0, 83-3/2]) torus((od_top+3)/2, (od_top-3)/2);
110   }
111 }
112-
A ruby.svg
+9, -0
 1@@ -0,0 +1,9 @@
 2+<?xml version="1.0" encoding="utf-8"?>
 3+<svg xmlns="http://www.w3.org/2000/svg" height="1cm" width="0.924cm" viewBox="0 0 500 500" xmlns:bx="https://boxy-svg.com">
 4+  <defs>
 5+    <bx:export>
 6+      <bx:file format="png" path="ruby.png"/>
 7+    </bx:export>
 8+  </defs>
 9+  <path style="stroke: rgb(0, 0, 0); fill: rgb(107, 84, 84); transform-box: fill-box; transform-origin: 50% 50%;" d="M 187.594 147.071 C 222.949 153.12 270.972 155.731 309.277 143.299 C 317.317 140.69 324.255 101.717 337.777 84.73 C 365.301 50.153 418.636 3.565 440.95 1.462 C 463.264 -0.641 461.076 -0.732 469.218 12.046 C 477.36 24.824 453.472 138.031 453.472 138.031 C 453.472 138.031 453.953 147.266 444.263 160.449 C 434.573 173.632 447.673 167.61 440.944 190.185 C 434.215 212.76 438.017 200.224 427.718 215.259 C 417.419 230.294 397.67 242.025 388.524 251.061 C 379.378 260.097 386.798 291.019 386.798 291.019 C 386.798 291.019 387.847 326.31 381.141 347.368 C 374.435 368.426 346.18 391.255 346.18 391.255 C 346.18 391.255 325.323 472.928 268.206 481.757 C 211.089 490.586 166.483 394.938 166.483 394.938 C 166.483 394.938 144.005 389.647 131.704 371.16 C 119.403 352.673 108.748 336.052 108.361 311.035 C 107.974 286.018 101.518 264.509 101.518 264.509 C 101.518 264.509 89.405 262.542 85.275 256.613 C 81.145 250.684 78.859 245.845 70.152 238.568 C 59.127 229.354 42.841 226.273 39.22 199.414 C 35.599 172.555 36.125 179.11 36.125 179.11 C 36.125 179.11 23.121 168.902 31.274 144.728 C 39.427 120.554 89.353 134.561 107.159 120.958 C 124.965 107.355 114.883 115.961 133.013 98.781 C 151.143 81.601 175.442 122.255 175.442 122.255 C 175.442 122.255 178.724 145.553 187.594 147.071 Z" id="object-1" transform="matrix(0.999073, 0.043046, -0.043046, 0.999073, 0.014376, 0.000286)"/>
10+</svg>