- commit
- 28cf438
- parent
- 1bf3902
- author
- Neale Pickett
- date
- 2025-10-24 15:49:21 -0600 MDT
more tweaks, add sigil
+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:
+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+}
+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();
+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
+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();
+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>