- commit
- 8d8c1d3
- parent
- c38bb58
- author
- Neale Pickett
- date
- 2025-11-28 20:10:22 -0700 MST
Add a bunch of work from previously
27 files changed,
+1141,
-41
+44,
-0
1@@ -0,0 +1,44 @@
2+// stops is an array of [diameter, distance]
3+module rod(stops) {
4+ end_d = stops[len(stops)-1][1];
5+ p = concat([[0, 0]], stops, [[0, end_d]]);
6+ rotate_extrude(convexity=4) {
7+ polygon(p);
8+ }
9+}
10+
11+module cylinders_stacked(stops, y=0, i=0) {
12+ if (i < len(stops)) {
13+ cur = stops[i];
14+ translate([0, 0, y]) cylinder(h=cur[1], d=cur[0]);
15+ cylinders_stacked(stops, y+cur[1], i+1);
16+ }
17+}
18+
19+rod([
20+ [10.7, 0], // Bottom
21+ [10.4, 53.5],
22+ [10, 66],
23+ [9.5, 91],
24+ [9, 116],
25+ [8.5, 151.5],
26+ [8, 192],
27+ [7.5, 224],
28+ [7.2, 242.5],
29+ [7, 251],
30+ [6.7, 271],
31+ [6.5, 282],
32+ [6.3, 291],
33+ [6, 308],
34+ [5.8, 321],
35+ [5.5, 347],
36+ [5.2, 367],
37+ [5, 374],
38+ [4.7, 393],
39+ [4.5, 400],
40+ [4.2, 412],
41+ [4, 420],
42+ [3.8, 431],
43+ [7.1, 447.5],
44+ ]);
45+
+0,
-0
+8,
-0
1@@ -0,0 +1,8 @@
2+rev=$(shell git log -1 --pretty='format:%cd' --date='format:%Y-%m-%d')
3+
4+all: top.3mf bottom.3mf
5+clean:
6+ rm -f *.3mf
7+
8+%.3mf: %.scad chanter.quiet.scad ../common.scad
9+ openscad -D rev=\"$(rev)\" -o $@ $<
+16,
-0
1@@ -0,0 +1,16 @@
2+#! /usr/bin/python3
3+
4+import math
5+
6+# lengths are in millimeters
7+c = 343 * 1000 # speed of sound in air (mm/sec)
8+
9+def freq(l, r1, r2):
10+ return (c / (4*l)) * r2/r1
11+
12+for measurements in (
13+ [360, 5.51/2, 13.1/2],
14+ [360, 1.50, 3.56],
15+ [360, 1.50, 5.56],
16+):
17+ print (measurements, freq(*measurements))
+6,
-0
1@@ -0,0 +1,6 @@
2+include <../common.scad>
3+use <./chanter.quiet.scad>
4+
5+tenon(top=false) {
6+ chanter();
7+}
+164,
-0
1@@ -0,0 +1,164 @@
2+include <../common.scad>
3+use <MCAD/regular_shapes.scad>
4+
5+height = 360.0;
6+od = 16.0;
7+od_derlin = od + 0.5;
8+id_top = 5.51;
9+id_bot = 5.51;
10+cut_height = 155; // Where to cut it in half
11+axianov = false; // Axianov tone holes?
12+
13+rev = "α";
14+stamp = str("quiet ", rev);
15+debug_stamp = str("id=", id_top, "–", id_bot);
16+
17+module tonehole_axianov(h=10, d=10, undercut=0) {
18+ r = d/2;
19+ area = PI*r*r;
20+
21+ rounding_r = 2;
22+ rounding_area = (1 - PI/4) * (rounding_r^2); // area lost to rounding
23+ goal_area = area + rounding_area;
24+
25+ w = 6.25; // Marat uses 6.25mm on every hole, so we will too
26+ l = goal_area / w;
27+
28+ rotate([0, 0, -90]) {
29+ translate([od/2, 0, h]) {
30+ rotate([0, -90+undercut, 0]) {
31+ // Scoot it down a hair so it will penetrate the entire tube when rotated
32+ translate([0, 0, -od/2]) {
33+ render(convexity=2)
34+ intersection() {
35+ cube([l, w, 20]);
36+ union() {
37+ translate([r, r, 0]) cylinder(r=r, h=20, center=true);
38+ translate([r, 0, 0]) cube(20, center=true);
39+ translate([0, r, 0]) cube(20, center=true);
40+ }
41+ }
42+ }
43+ }
44+ }
45+ }
46+}
47+
48+module tonehole(h=0, d=10, depth=od/2, undercut=0) {
49+ translate([0, -od/2, h]) {
50+ rotate([-90+undercut, 0, 0]) {
51+ translate([0, 0, -d/2]) {
52+ cylinder(d=d, h=depth+d/2);
53+ }
54+ }
55+ }
56+}
57+
58+// Something like David Daye's Penny Chanter,
59+// based on my measurements and undercut hints on David's web site.
60+// This is symmetrical, so both pieces can be printed at the same time,
61+// with color changes
62+module chanter() {
63+ difference () {
64+ union() {
65+ // The core part of the instrument is this brass tube
66+ difference() {
67+ brass() cylinder(h=height, d=od);
68+
69+ // Seam guides
70+ translate([0, od/2, 0]) cylinder(d=1, h=400);
71+ }
72+
73+ // David adds this outer Derlin tube as an upgrade option
74+ difference() {
75+ derlin() translate([0, 0, 40.0]) cylinder(h=290, d=od_derlin);
76+
77+ // The slits up the sides serve three purposes:
78+ // 1. directs the slicer to place seams in it (and not on the face)
79+ // 2. provides alignment guides for attaching the two pieces
80+ // 3. looks like an intentional stylistic thing
81+ rotate([0, 0, 85]) translate([0, od_derlin/2, 0]) cylinder(d=1, h=cut_height+20); // overlap
82+ rotate([0, 0, -85]) translate([0, od_derlin/2, cut_height-20]) cylinder(d=1, h=400);
83+ }
84+
85+ // These little doodads are probably just to hide the Derlin seam
86+ ivory() translate([0, 0, 30.0]) {
87+ cylinder(h=3, d1=od, d2=22.0);
88+ translate([0, 0, 3]) cylinder(h=6, d=22.0);
89+ translate([0, 0, 9]) cylinder(h=3, d1=22.0, d2=od);
90+ }
91+ ivory() translate([0, 0, height - 30.0 - 12]) {
92+ cylinder(h=3, d1=od, d2=22.0);
93+ translate([0, 0, 3]) cylinder(h=6, d=22.0);
94+ translate([0, 0, 9]) cylinder(h=3, d1=22.0, d2=od);
95+ }
96+ }
97+
98+ // Seam guides
99+ translate([0, 11, 0]) cylinder(d=1, h=400);
100+
101+ // Inner bore, which runs up to the place where the reed drops in
102+ translate([0, 0, -1]) cylinder(h=338.01, d1=id_bot, d2=id_top);
103+ translate([0, 0, 337]) cylinder(h=23.1, d1=id_top, d2=7.1);
104+
105+ // Fancy looking rings
106+ translate([0, 0, 3]) rings(d=16.0);
107+
108+ // Sigil
109+ translate([0, 0, 4]) intersection() {
110+ cylinder_tube(50, od/2+0.5, 1);
111+ union() {
112+ translate([0, 0, 8]) rotate([90, 0, -90]) {
113+ linear_extrude(height=30, convexity=4) {
114+ translate([-4, -5]) import("ruby.svg");
115+ }
116+ }
117+ translate([0, 0, 2]) rotate([90, 90, -160]) {
118+ linear_extrude(height=30, convexity=10) {
119+ text(stamp, size=3, halign="right", valign="center");
120+ }
121+ }
122+ translate([0, 0, 2]) rotate([90, 90, -200]) {
123+ linear_extrude(height=30, convexity=10) {
124+ text(debug_stamp, size=3, halign="right", valign="center");
125+ }
126+ }
127+ }
128+ }
129+
130+ // Tone Holes!
131+ // XXX: Undercut angle should be taken into account here
132+ tonehole(h=57.1, d=6.68); // E-
133+ tonehole(h=87.0, d=4.25); // E
134+ tonehole(h=118.0, d=8.33); // F#
135+ tonehole(h=149.0, d=5.51, undercut=-15); // G
136+ tonehole(h=184.5, d=7.75, undercut=15); // A
137+ tonehole(h=184.5, d=7.75, undercut=-15); // A (wide undercut)
138+ tonehole(h=219.0, d=6.72, undercut=15); // B
139+ tonehole(h=250.0, d=5.50, undercut=30); // C
140+ rotate([0, 0, 180]) tonehole(h=270.0, d=7.11, undercut=30); // D
141+ }
142+}
143+
144+module tenon(top=false, h=cut_height, od=od, depth=20, thickness=3) {
145+ module cut() {
146+ cylinder(h=cut_height+depth, d=od-thickness-clearance);
147+ cylinder(h=cut_height, d=50);
148+ }
149+
150+ if (top) {
151+ difference() {
152+ children();
153+ cut();
154+ }
155+ } else {
156+ intersection() {
157+ children();
158+ cut();
159+ }
160+ }
161+}
162+
163+chanter();
164+//translate([0, 50, 360]) rotate([180, 0, 0]) chanter();
165+
+164,
-0
1@@ -0,0 +1,164 @@
2+include <../common.scad>
3+use <MCAD/regular_shapes.scad>
4+
5+height = 360.0;
6+od = 16.0;
7+od_derlin = od + 0.5;
8+id_bot = 13.2;
9+id_top = 5.51;
10+cut_height = 155; // Where to cut it in half
11+axianov = false; // Axianov tone holes?
12+
13+rev = "preview";
14+stamp = str(rev);
15+
16+module tonehole_axianov(h=10, d=10, undercut=0) {
17+ r = d/2;
18+ area = PI*r*r;
19+
20+ rounding_r = 2;
21+ rounding_area = (1 - PI/4) * (rounding_r^2); // area lost to rounding
22+ goal_area = area + rounding_area;
23+
24+ w = 6.25; // Marat uses 6.25mm on every hole, so we will too
25+ l = goal_area / w;
26+
27+ rotate([0, 0, -90]) {
28+ translate([od/2, 0, h]) {
29+ rotate([0, -90+undercut, 0]) {
30+ // Scoot it down a hair so it will penetrate the entire tube when rotated
31+ translate([0, 0, -od/2]) {
32+ render(convexity=2)
33+ intersection() {
34+ cube([l, w, 20]);
35+ union() {
36+ translate([r, r, 0]) cylinder(r=r, h=20, center=true);
37+ translate([r, 0, 0]) cube(20, center=true);
38+ translate([0, r, 0]) cube(20, center=true);
39+ }
40+ }
41+ }
42+ }
43+ }
44+ }
45+}
46+
47+module tonehole(h=10, d=10, undercut=0) {
48+ if (axianov) {
49+ tonehole_axianov(h, d, undercut);
50+ } else {
51+ rotate([0, 0, -90]) {
52+ translate([od/2, 0, h]) {
53+ rotate([0, -90+undercut, 0]) {
54+ translate([0, 0, -od/2]) {
55+ cylinder(d=d, h=20);
56+ }
57+ }
58+ }
59+ }
60+ }
61+ // Cutaways. I don't like them, but if you do, uncomment this.
62+ // translate([0, -od/2 - 8, h]) rotate([0, 90, 0]) cylinder(r=10, h=50, center=true);
63+}
64+
65+
66+// Something like David Daye's Penny Chanter,
67+// based on my measurements and undercut hints on David's web site.
68+// This is symmetrical, so both pieces can be printed at the same time,
69+// with color changes
70+module chanter() {
71+ difference () {
72+ union() {
73+ // The core part of the instrument is this brass tube
74+ difference() {
75+ brass() cylinder(h=height, d=od);
76+
77+ // Seam guides
78+ translate([0, od/2, 0]) cylinder(d=1, h=400);
79+ }
80+
81+ // David adds this outer Derlin tube as an upgrade option
82+ difference() {
83+ derlin() translate([0, 0, 40.0]) cylinder(h=290, d=od_derlin);
84+
85+ // The slits up the sides serve three purposes:
86+ // 1. directs the slicer to place seams in it (and not on the face)
87+ // 2. provides alignment guides for attaching the two pieces
88+ // 3. looks like an intentional stylistic thing
89+ rotate([0, 0, 85]) translate([0, od_derlin/2, 0]) cylinder(d=1, h=cut_height+20); // overlap
90+ rotate([0, 0, -85]) translate([0, od_derlin/2, cut_height-20]) cylinder(d=1, h=400);
91+ }
92+
93+ // These little doodads are probably just to hide the Derlin seam
94+ ivory() translate([0, 0, 30.0]) {
95+ cylinder(h=3, d1=od, d2=22.0);
96+ translate([0, 0, 3]) cylinder(h=9, d=22.0);
97+ }
98+ ivory() translate([0, 0, height - 30.0 - 12]) {
99+ cylinder(h=12, d=22.0);
100+ }
101+ }
102+
103+ // Seam guides
104+ translate([0, 11, 0]) cylinder(d=1, h=400);
105+
106+ // Inner bore, which runs up to the place where the reed drops in
107+ translate([0, 0, 0]) cylinder(h=337.01, d1=id_bot, d2=id_top);
108+ translate([0, 0, 337]) cylinder(h=23.1, d1=id_top, d2=7.1);
109+
110+ // I totally dig David's minimalist aesthetic
111+ translate([0, 0, 3]) rings(d=16.0);
112+
113+ // Sigil
114+ translate([0, 0, 4]) intersection() {
115+ cylinder_tube(50, od/2+0.5, 1);
116+ union() {
117+ translate([0, 0, 8]) rotate([90, 0, -90]) {
118+ linear_extrude(height=d1, convexity=4) {
119+ translate([-4, -5]) import("ruby.svg");
120+ }
121+ }
122+ translate([0, 0, 4]) rotate([90, 90, -160]) {
123+ linear_extrude(height=od, convexity=10) {
124+ text(stamp, size=2, halign="right", valign="center");
125+ }
126+ }
127+ }
128+ }
129+
130+ // Tone Holes!
131+ // XXX: Undercut angle should be taken into account here
132+ tonehole(h=57.1, d=6.68); // E-
133+ tonehole(h=87.0, d=4.25); // E
134+ tonehole(h=118.0, d=8.33); // F#
135+ tonehole(h=149.0, d=5.51, undercut=-15); // G
136+ tonehole(h=184.5, d=7.75, undercut=15); // A
137+ tonehole(h=184.5, d=7.75, undercut=-15); // A (wide undercut)
138+ tonehole(h=219.0, d=6.72, undercut=15); // B
139+ tonehole(h=250.0, d=5.50, undercut=30); // C
140+ rotate([0, 0, 180]) tonehole(h=270.0, d=7.11, undercut=30); // D
141+ }
142+}
143+
144+module tenon(top=false, h=cut_height, od=od, depth=20, thickness=2) {
145+ module cut() {
146+ cylinder(h=cut_height+depth, d=od-thickness-clearance);
147+ cylinder(h=cut_height, d=50);
148+ }
149+
150+ if (top) {
151+ difference() {
152+ children();
153+ cut();
154+ }
155+ } else {
156+ intersection() {
157+ children();
158+ cut();
159+ }
160+ }
161+}
162+
163+chanter();
164+//translate([0, 50, 360]) rotate([180, 0, 0]) chanter();
165+
+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>
+6,
-0
1@@ -0,0 +1,6 @@
2+module tonehole(d=10, depth=20, undercut=0) {
3+ rotate([-90+undercut, 0, 0]) translate([0, 0, -d/2]) cylinder(d=d, h=depth);
4+}
5+
6+tonehole(undercut=10);
7+
+6,
-0
1@@ -0,0 +1,6 @@
2+include <../common.scad>
3+use <./chanter.quiet.scad>
4+
5+tenon(top=true) {
6+ chanter();
7+}
+157,
-0
1@@ -0,0 +1,157 @@
2+include <../common.scad>
3+
4+height = 360.0;
5+od = 16.0;
6+od_derlin = od + 0.5;
7+id_bot = 13.2;
8+id_top = 5.51;
9+cut_height = 155; // Where to cut it in half
10+axianov = false; // Axianov tone holes?
11+
12+module tonehole_axianov(h=10, d=10, undercut=0) {
13+ r = d/2;
14+ area = PI*r*r;
15+
16+ rounding_r = 2;
17+ rounding_area = (1 - PI/4) * (rounding_r^2); // area lost to rounding
18+ goal_area = area + rounding_area;
19+
20+ w = 6.25; // Marat uses 6.25mm on every hole, so we will too
21+ l = goal_area / w;
22+
23+ rotate([0, 0, -90]) {
24+ translate([od/2, 0, h]) {
25+ rotate([0, -90+undercut, 0]) {
26+ // Scoot it down a hair so it will penetrate the entire tube when rotated
27+ translate([0, 0, -od/2]) {
28+ render(convexity=2)
29+ intersection() {
30+ cube([l, w, 20]);
31+ union() {
32+ translate([r, r, 0]) cylinder(r=r, h=20, center=true);
33+ translate([r, 0, 0]) cube(20, center=true);
34+ translate([0, r, 0]) cube(20, center=true);
35+ }
36+ }
37+ }
38+ }
39+ }
40+ }
41+}
42+
43+module tonehole(h=10, d=10, undercut=0) {
44+ if (axianov) {
45+ tonehole_axianov(h, d, undercut);
46+ } else {
47+ rotate([0, 0, -90]) {
48+ translate([od/2, 0, h]) {
49+ rotate([0, -90+undercut, 0]) {
50+ translate([0, 0, -od/2]) {
51+ cylinder(d=d, h=20);
52+ }
53+ }
54+ }
55+ }
56+ }
57+ translate([0, -od/2 - 8, h]) rotate([0, 90, 0]) cylinder(r=10, h=50, center=true);
58+}
59+
60+
61+// Something like David Daye's Penny Chanter,
62+// based on my measurements and undercut hints on David's web site.
63+// This is symmetrical, so both pieces can be printed at the same time,
64+// with color changes
65+module chanter() {
66+ difference () {
67+ union() {
68+ // The core part of the instrument is this brass tube
69+ difference() {
70+ brass() cylinder(h=height, d=od);
71+
72+ // Seam guides
73+ translate([0, od/2, 0]) cylinder(d=1, h=400);
74+ }
75+
76+ // David adds this outer Derlin tube as an upgrade option
77+ difference() {
78+ derlin() translate([0, 0, 40.0]) cylinder(h=290, d=od_derlin);
79+
80+ // The slits up the sides serve three purposes:
81+ // 1. directs the slicer to place seams in it (and not on the face)
82+ // 2. provides alignment guides for attaching the two pieces
83+ // 3. looks like an intentional stylistic thing
84+ rotate([0, 0, 85]) translate([0, od_derlin/2, 0]) cylinder(d=1, h=cut_height+20); // overlap
85+ rotate([0, 0, -85]) translate([0, od_derlin/2, cut_height-20]) cylinder(d=1, h=400);
86+ }
87+
88+ // These little doodads are probably just to hide the Derlin seam
89+ ivory() translate([0, 0, 30.0]) {
90+ cylinder(h=3, d1=od, d2=22.0);
91+ translate([0, 0, 3]) cylinder(h=9, d=22.0);
92+ }
93+ ivory() translate([0, 0, height - 30.0 - 12]) {
94+ cylinder(h=12, d=22.0);
95+ translate([0, 0, 12]) cylinder(h=3, d1=22.0, d2=od);
96+ }
97+ }
98+
99+ // Seam guides
100+ translate([0, 11, 0]) cylinder(d=1, h=400);
101+
102+ // Inner bore, which runs up to the place where the reed drops in
103+ translate([0, 0, 0]) cylinder(h=337.01, d1=id_bot, d2=id_top);
104+ translate([0, 0, 337]) cylinder(h=23.1, d1=id_top, d2=7.1);
105+
106+ // I totally dig David's minimalist aesthetic
107+ translate([0, 0, 3]) rings(d=16.0);
108+
109+ // Stamp my name on it
110+ difference() {
111+ translate([0, 0, 307]) {
112+ rotate([90, 0, 180]) {
113+ linear_extrude(50) {
114+ translate([0, 5]) text("neale", font="Fontdiner Swanky", valign="bottom", size=3, halign="center");
115+ translate([0, 0]) text("2024-2", font="Fontdiner Swanky", valign="bottom", size=3, halign="center");
116+ }
117+ }
118+ }
119+ cylinder(h=360, d=od-0.5);
120+ }
121+
122+
123+ // Tone Holes!
124+ // XXX: Undercut angle should be taken into account here
125+ tonehole(h=57.1, d=6.68); // E-
126+ tonehole(h=87.0, d=4.25); // E
127+ tonehole(h=118.0, d=8.33); // F#
128+ tonehole(h=149.0, d=5.51, undercut=-15); // G
129+ tonehole(h=184.5, d=7.75, undercut=15); // A
130+ tonehole(h=184.5, d=7.75, undercut=-15); // A (wide undercut)
131+ tonehole(h=219.0, d=6.72, undercut=15); // B
132+ tonehole(h=250.0, d=5.50, undercut=30); // C
133+ rotate([0, 0, 180]) tonehole(h=270.0, d=7.11, undercut=30); // D
134+ }
135+}
136+
137+module tenon(top=false, h=cut_height, od=od, depth=20, thickness=2) {
138+ module cut() {
139+ cylinder(h=cut_height+depth, d=od-thickness-clearance);
140+ cylinder(h=cut_height, d=50);
141+ }
142+
143+ if (top) {
144+ difference() {
145+ children();
146+ cut();
147+ }
148+ } else {
149+ intersection() {
150+ children();
151+ cut();
152+ }
153+ }
154+}
155+
156+chanter();
157+//translate([0, 50, 360]) rotate([180, 0, 0]) chanter();
158+
+8,
-0
1@@ -0,0 +1,8 @@
2+include <../common.scad>
3+
4+difference() {
5+ cylinder(d=82, h=80);
6+ translate([-50, -80, -1]) cube(100);
7+ translate([0, 30, 78]) linear_extrude(2) text("d=82mm", size=4, halign="center");
8+ translate([0, 24, 78]) linear_extrude(2) text("reed sanding block", size=4, halign="center");
9+}
+20,
-0
1@@ -0,0 +1,20 @@
2+module tonehole(h=10, d=10, undercut=0) {
3+ r = d/2;
4+ area = PI*r*r;
5+
6+ rounding_r = 2;
7+ rounding_area = (1 - PI/4) * (rounding_r^2); // area lost to rounding
8+ goal_area = area + rounding_area;
9+
10+ w = 6.25; // Marat uses 6.25mm on every hole, so we will too
11+ l = goal_area / w;
12+
13+ union() {
14+ translate([rounding_r, rounding_r, 0]) cylinder(r=rounding_r, h=20);
15+ translate([rounding_r, 0, 0]) cube([w-rounding_r, l, 20]);
16+ translate([0, rounding_r, 0]) cube([w, l-rounding_r, 20]);
17+ }
18+}
19+
20+tonehole($fn=60);
21+
+36,
-0
1@@ -0,0 +1,36 @@
2+include <../../common.scad>
3+
4+// 9mm od
5+// 7.5mm from plane on reed holder to back
6+// 9 - 7.5 = 2.5 thickness; 4.5-2.5=2 cutaway from center
7+
8+length = 45;
9+thickness = 0.55;
10+d = 9;
11+base_thickness = 1.5;
12+
13+scrape_angle = atan((base_thickness-thickness)/length)*2.6;
14+
15+translate([0, 0, -d/2+base_thickness])
16+difference() {
17+ rotate([-88, 0, 0]) cylinder(d=d, h=length);
18+
19+ // Chop off excess cylinder at the base
20+ translate([-d/2, -d, 0]) cube(d);
21+
22+ // Shave off reed
23+ translate([-d/2, -length, -d/2-base_thickness]) cube([d, length*3, d]);
24+
25+ // Scrape reed
26+ translate([0, length, d/2-base_thickness+thickness]) rotate([-scrape_angle, 0, 0]) translate([-d/2, -length, 0]) cube([d, length, d]);
27+
28+ // Reed chopper
29+ translate([0, length, 0]) {
30+ translate([0, -d/2, 0]) {
31+ difference() {
32+ translate([-d/2, 0, 0]) cube([d, d/2, d]);
33+ cylinder(d=d, h=d);
34+ }
35+ }
36+ }
37+}
+16,
-0
1@@ -0,0 +1,16 @@
2+include <../common.scad>
3+
4+difference() {
5+ union() {
6+ translate([0, 0, 0.0]) cylinder(h=50, d=15); // Interface to bellows
7+ for (h = [3 : 4 : 30]) {
8+ translate([0, 0, h]) scale([1, 1, 0.75]) sphere(d=16);
9+ }
10+ translate([0, 0, 33.0]) cylinder(h=16.9, d=21.9); // Bump-out stop
11+ translate([0, 0, 40.0]) cylinder(h=24, d=19.3); // Interface with tubing
12+ translate([0, 0, 40.0]) cylinder(h=34, d=18.2); // Carved out for binding
13+ translate([0, 0, 70.5]) cylinder(h=4, d=19.3); // Interface with tubing
14+ }
15+ cylinder(h=200, d=13); // Diameter here isn't very important
16+ translate([0, 0, -10]) cylinder(h=10, d=50); // Chop sphere off the bottom
17+}
1@@ -0,0 +1,8 @@
2+rev=$(shell git log -1 --pretty='format:%cd' --date='format:%Y-%m-%d')
3+
4+all: top.3mf bottom.3mf
5+clean:
6+ rm -f *.3mf
7+
8+%.3mf: %.scad chanter.scad
9+ openscad -D rev=\"$(rev)\" -o $@ $<
1@@ -0,0 +1,5 @@
2+use <./chanter.scad>
3+
4+tenon(top=false) {
5+ chanter();
6+}
1@@ -0,0 +1,126 @@
2+use <BOSL/math.scad>
3+
4+$fn = $preview ? 0 : 180; // Make circles circular in final renders.
5+
6+// stops is an array of [d, h]
7+module rod(stops) {
8+ beg = stops[0][1];
9+ end = stops[len(stops)-1][1];
10+ p = concat([[0, beg]], stops, [[0, end]]);
11+
12+ halfp = [for (i = p) [i[0]/2, i[1]]];
13+ rotate_extrude(convexity=4) {
14+ polygon(halfp);
15+ }
16+}
17+
18+// Drill a tonehole.
19+// The center of the tonehole will intersect [0, od, h] before rotation.
20+//
21+// Instrument makers think about
22+// drilling from the outside of the pipe in.
23+// So we need to know the outside diameter of the pipe.
24+//
25+// For now, we're pretending the pipe has a consistent outside diameter.
26+// BOSL2 may have something that will let us calculate od given h and measurements.
27+module tonehole(od, h, d, undercut=0, rot=0) {
28+ render(convexity=2)
29+ rotate([0, 0, rot]) {
30+ translate([0, -od/2, h]) {
31+ // Intersect with a cube to ensure an undercut tonehole doesn't drill past the center of the instrument
32+ intersection() {
33+ rotate([-90+undercut, 0, 0]) {
34+ cylinder(d=d, h=od, center=true);
35+ }
36+ cube(od, center=true);
37+ }
38+ }
39+ }
40+}
41+
42+function inch(x) = x * 25.4;
43+
44+od = inch(0.762);
45+
46+// The original height was 381.
47+// I couldn't get the reed to stay in with such a short reed area.
48+//height = 381;
49+height = 381+23;
50+
51+module chanter() {
52+ difference() {
53+ union() {
54+ cylinder(d=16, h=height);
55+ cylinder(d1=od, d2=16, h=280); // A nice taper
56+ }
57+
58+ tonehole(od, 56.0, inch(0.162));
59+ tonehole(od, 89.0, inch(0.182));
60+ tonehole(od, 122.2, inch(0.201));
61+ tonehole(od, 153.2, inch(0.180));
62+ tonehole(od, 190.1, inch(0.183));
63+ tonehole(od, 222.6, inch(0.187));
64+ tonehole(od, 252.2, inch(0.162));
65+ tonehole(od, 275.5, inch(0.173), rot=180);
66+
67+ rod([
68+ [7.1, height],
69+ [inch(0.168), mean([361.3, 361.3])],
70+ [inch(0.174), mean([351.9, 352.8])],
71+ [inch(0.180), mean([345.1, 347.2])],
72+ [inch(0.185), mean([342.0, 344.6])],
73+ [inch(0.189), mean([337.5, 339.5])],
74+ [inch(0.195), mean([330.7, 334.1])],
75+ [inch(0.200), mean([325.5, 329.6])],
76+ [inch(0.205), mean([318.2, 321.8])],
77+ [inch(0.210), mean([315.2, 317.7])],
78+ [inch(0.215), mean([309.7, 312.5])],
79+ [inch(0.220), mean([302.0, 305.5])],
80+ [inch(0.225), mean([294.7, 297.4])],
81+ [inch(0.230), mean([288.5, 292.0])],
82+ [inch(0.235), mean([282.5, 284.0])],
83+ [inch(0.241), mean([274.0, 278.7])],
84+ [inch(0.245), mean([269.0, 269.0])],
85+ [inch(0.250), mean([265.0, 265.0])],
86+ [inch(0.259), mean([266.0, 266.0])],
87+ [inch(0.270), mean([237.9, 241.5])],
88+ [inch(0.280), mean([224.5, 226.5])],
89+ [inch(0.290), mean([209.0, 212.2])],
90+ [inch(0.300), mean([195.4, 195.4])],
91+ [inch(0.310), mean([183.0, 185.2])],
92+ [inch(0.320), mean([179.0, 183.4])],
93+ [inch(0.330), mean([160.2, 160.2])],
94+ [inch(0.340), mean([135.0, 104.7])],
95+ [inch(0.350), mean([119.6, 123.6])],
96+ [inch(0.360), mean([104.0, 110.4])],
97+ [inch(0.370), mean([ 79.0, 81.0, 89.2])],
98+ [inch(0.380), mean([ 61.0, 63.0, 66.0])],
99+ [inch(0.390), mean([ 59.5, 61.6])],
100+ [inch(0.400), mean([ 51.0, 54.5, 56.5])],
101+ [inch(0.410), mean([ 27.0, 29.5])],
102+ [inch(0.420), mean([ 21.5, 29.0])],
103+ [inch(0.427), mean([ 0.0, 0.0])],
104+ ]);
105+ }
106+}
107+
108+module tenon(top=false, h=198, od=od-5, depth=20, thickness=2, clearance=0.12) {
109+ module cut() {
110+ cylinder(h=h+depth, d=od-thickness+clearance);
111+ cylinder(h=h, d=50);
112+ }
113+
114+ if (top) {
115+ difference() {
116+ children();
117+ cut();
118+ }
119+ } else {
120+ intersection() {
121+ children();
122+ cut();
123+ }
124+ }
125+}
126+
127+chanter();
1@@ -0,0 +1,119 @@
2+use <BOSL/math.scad>
3+
4+$fn = $preview ? 0 : 180; // Make circles circular in final renders.
5+
6+// stops is an array of [d, h]
7+module rod(stops) {
8+ beg = stops[0][1];
9+ end = stops[len(stops)-1][1];
10+ p = concat([[0, beg]], stops, [[0, end]]);
11+
12+ halfp = [for (i = p) [i[0]/2, i[1]]];
13+ rotate_extrude(convexity=4) {
14+ polygon(halfp);
15+ }
16+}
17+
18+// Drill a tonehole.
19+// The center of the tonehole will intersect [0, od, h] before rotation.
20+//
21+// Instrument makers think about
22+// drilling from the outside of the pipe in.
23+// So we need to know the outside diameter of the pipe.
24+//
25+// For now, we're pretending the pipe has a consistent outside diameter.
26+// BOSL2 may have something that will let us calculate od given h and measurements.
27+module tonehole(od, h, d, undercut=0, rot=0) {
28+ render(convexity=2)
29+ rotate([0, 0, rot]) {
30+ translate([0, -od/2, h]) {
31+ // Intersect with a cube to ensure an undercut tonehole doesn't drill past the center of the instrument
32+ intersection() {
33+ rotate([-90+undercut, 0, 0]) {
34+ cylinder(d=d, h=od, center=true);
35+ }
36+ cube(od, center=true);
37+ }
38+ }
39+ }
40+}
41+
42+function inch(x) = x * 25.4;
43+
44+od = inch(0.762);
45+
46+height = 360;
47+
48+module chanter() {
49+ difference() {
50+ union() {
51+ cylinder(d=16, h=height);
52+ cylinder(d1=od, d2=16, h=280); // A nice taper
53+ }
54+
55+ tonehole(od, 56.0, inch(0.162));
56+ tonehole(od, 89.0, inch(0.182));
57+ tonehole(od, 122.2, inch(0.201));
58+ tonehole(od, 153.2, inch(0.180));
59+ tonehole(od, 190.1, inch(0.183));
60+ tonehole(od, 222.6, inch(0.187));
61+ tonehole(od, 252.2, inch(0.162));
62+ tonehole(od, 275.5, inch(0.173), rot=180);
63+
64+ rod([
65+ [7.1, height],
66+ [inch(0.189), 337],
67+ [inch(0.195), mean([330.7, 334.1])],
68+ [inch(0.200), mean([325.5, 329.6])],
69+ [inch(0.205), mean([318.2, 321.8])],
70+ [inch(0.210), mean([315.2, 317.7])],
71+ [inch(0.215), mean([309.7, 312.5])],
72+ [inch(0.220), mean([302.0, 305.5])],
73+ [inch(0.225), mean([294.7, 297.4])],
74+ [inch(0.230), mean([288.5, 292.0])],
75+ [inch(0.235), mean([282.5, 284.0])],
76+ [inch(0.241), mean([274.0, 278.7])],
77+ [inch(0.245), mean([269.0, 269.0])],
78+ [inch(0.250), mean([265.0, 265.0])],
79+ [inch(0.259), mean([266.0, 266.0])],
80+ [inch(0.270), mean([237.9, 241.5])],
81+ [inch(0.280), mean([224.5, 226.5])],
82+ [inch(0.290), mean([209.0, 212.2])],
83+ [inch(0.300), mean([195.4, 195.4])],
84+ [inch(0.310), mean([183.0, 185.2])],
85+ [inch(0.320), mean([179.0, 183.4])],
86+ [inch(0.330), mean([160.2, 160.2])],
87+ [inch(0.340), mean([135.0, 104.7])],
88+ [inch(0.350), mean([119.6, 123.6])],
89+ [inch(0.360), mean([104.0, 110.4])],
90+ [inch(0.370), mean([ 79.0, 81.0, 89.2])],
91+ [inch(0.380), mean([ 61.0, 63.0, 66.0])],
92+ [inch(0.390), mean([ 59.5, 61.6])],
93+ [inch(0.400), mean([ 51.0, 54.5, 56.5])],
94+ [inch(0.410), mean([ 27.0, 29.5])],
95+ [inch(0.420), mean([ 21.5, 29.0])],
96+ [inch(0.427), mean([ 0.0, 0.0])],
97+ ]);
98+ }
99+}
100+
101+module tenon(top=false, h=198, od=od-5, depth=20, thickness=2, clearance=0.12) {
102+ module cut() {
103+ cylinder(h=h+depth, d=od-thickness+clearance);
104+ cylinder(h=h, d=50);
105+ }
106+
107+ if (top) {
108+ difference() {
109+ children();
110+ cut();
111+ }
112+ } else {
113+ intersection() {
114+ children();
115+ cut();
116+ }
117+ }
118+}
119+
120+chanter();
1@@ -0,0 +1,101 @@
2+Possible Harrington C# Chanter
3+Owned by Kevin Rowsome
4+
5+Copyright 1998 David C. Daye
6+
7+Notes: LOA: 381 mm
8+ Bell I.D.: .44" x .427"
9+ Construction: Medium brown wood
10+ Slight curved bore, slightly ovalled
11+ Special: Per Benedict Koehler, bottom end of chanter
12+ has been reamed and fit with new bottom &
13+ bell, feather-tapered into original top portion.
14+ "Gouges" and splinters I observed in bottom
15+ are feather edges chipping and peeling loose.
16+
17+ Measurement Notes:
18+ ID penetration numbers are identical for throat.
19+
20+ Elsewhere when 2 are same for an i.d., a tone hole
21+ is present and therefore only one penetration
22+ distance could be taken. It is not necessarily
23+ either the true min. or maximum penetration of
24+ a pre-hole bore.
25+
26+ I.D.'s marked by *** are in gouged/chipped area.
27+ Therefore 3 or 4 penetration numbers are given
28+ because of measurement uncertainties.
29+
30+Chanter is said to be well behaved, has good tone.
31+
32+Probe Distances From Bell (millimeters)
33+ID " Min Max Min2 Max2
34+
35+0.255 381.0 381.0
36+0.250 379.5 380.0
37+0.245 379.0 379.0
38+0.230 377.0 377.5
39+0.220 376.0 377.0
40+0.215 375.0 376.0
41+0.200 372.2 374.0
42+0.195 371.0 371.0
43+0.189 370.1 370.9
44+0.185 371.0 371.0
45+0.169 367.0 368.1
46+0.168 366.3 366.3
47+0.168 361.3 361.3
48+0.174 351.9 352.8
49+0.180 345.1 347.2
50+0.185 342.0 344.6
51+0.189 337.5 339.5
52+0.195 330.7 334.1
53+0.200 325.5 329.6
54+0.205 318.2 321.8
55+0.210 315.2 317.7
56+0.215 309.7 312.5
57+0.220 302.0 305.5
58+0.225 294.7 297.4
59+0.230 288.5 292.0
60+0.235 282.5 284.0
61+0.241 274.0 278.7
62+0.245 269.0 269.0
63+0.250 265.0 265.0
64+0.259 266.0 266.0
65+0.270 237.9 241.5
66+0.280 224.5 226.5
67+0.290 209.0 212.2
68+0.300 195.4 195.4
69+0.310 183.0 185.2
70+0.320 179.0 183.4
71+0.330 160.2 160.2
72+0.340 135.0 104.7
73+0.350 119.6 123.6
74+0.360 104.0 110.4
75+0.370 79.0 81.0 89.2 ***
76+0.380 61.0 63.0 66.0 ***
77+0.390 59.5 61.6
78+0.400 51.0 54.5 56.5 ***
79+0.410 27.0 29.5 41.5 47.0 ***
80+0.420 21.5 29.0 33.0 36.0 ***
81+0.427 0.0 0.0
82+0.430 11.0 11.0
83+0.440 0.0 0.0
84+
85+Tone Hole Data (yes E-flat hole is smaller than E hole)
86+
87+Note Dist from I.D. I.D.#2 Chanter Chanter
88+Name Bell (mm) (in.) (in.) Width " Depth "
89+
90+E flat 56.0 0.162 0.762 0.735
91+E 89.0 0.182 0.752 0.730
92+F 104.5
93+F# 122.2 0.201 0.749 0.690
94+G 153.2 0.180 0.709 0.735
95+A 190.1 0.183
96+B flat 205.5
97+B 222.6 0.187
98+C' 236.8
99+C 252.2 0.162 0.661
100+D" 264.1
101+D' 271.5 0.173 0.159 0.669 0.636
102+
1@@ -0,0 +1,5 @@
2+use <./chanter.scad>
3+
4+tenon(top=true) {
5+ chanter();
6+}
+1,
-0
1@@ -0,0 +1 @@
2+http://plans.source.pipers.ie.s3.amazonaws.com/plans/pdf_024758.pdf
+0,
-0
+85,
-0
1@@ -0,0 +1,85 @@
2+use <BOSL/math.scad>
3+
4+// A cylinder-like object defined by probe measurements.
5+// Each measurement is a 2-tuple: (d, h)
6+module rod(measurements) {
7+ end_d = measurements[len(measurements)-1][1];
8+ p = concat([[0, 0]], measurements, [[0, end_d]]);
9+ rotate_extrude(convexity=4) {
10+ polygon(p);
11+ }
12+}
13+
14+// Drill toneholes.
15+// Each hole is a 4-tuple: (h, d, rotation_angle, undercut_angle)
16+// The center of the tonehole will intersect [0, od, h] before rotation.
17+//
18+// Instrument makers think about
19+// drilling from the outside of the pipe in.
20+// So we need to know the outside diameter of the pipe.
21+//
22+// For now, we're pretending the pipe has a consistent outside diameter.
23+// BOSL2 may have something that will let us calculate od given h and measurements.
24+module toneholes(od, holes) {
25+ for (hole = holes) {
26+ h = holes[0];
27+ d = holes[1];
28+ rot = holes[2];
29+ undercut = holes[3];
30+
31+ rotate([0, 0, rot]) {
32+ translate([0, -od/2, h]) {
33+ // Intersect with a cube to ensure an undercut tonehole doesn't drill past the center of the instrument
34+ intersection() {
35+ rotate([-90+undercut, 0, 0]) {
36+ cylinder(d=d, h=od, center=true);
37+ }
38+ cube(od, center=true);
39+ }
40+ }
41+ }
42+ }
43+}
44+
45+rod([
46+ [6.2, 0], // Top
47+ [5.7, 5],
48+ [5.4, 7],
49+ [5, 10.5],
50+ [4.76, 11.5],
51+ [4.4, 14],
52+ [4.1, 15.5],
53+ [3.9, 16],
54+ [3.8, 16.5], // Height specified as "Through"
55+ [3.8, 20], // Continuation of "Through", I guess
56+ [3.9, 20.5],
57+ [4, 25.5],
58+ [4.2, 37.5],
59+ [4.5, 48],
60+ [4.7, 57.5],
61+ [5, 71.5],
62+ [5.2, 80],
63+ [5.5, 91.5],
64+ [5.8, 125.5],
65+ [6, 135.5],
66+ [6.3, 143],
67+ [6.5, 148.8],
68+ [6.7, 165.5],
69+ [7, 191.5],
70+ [7.2, 200.5],
71+ [7.5, 215.5],
72+ [7.8, 236.5],
73+ [8, 239],
74+ [8.2, 248.3],
75+ [8.5, 266],
76+ [8.8, 285.5],
77+ [9, 306],
78+ [9.2, 323],
79+ [9.5, 365.5],
80+ [9.8, 378],
81+ [10, 395.5],
82+ [10.6, 435.5], // End of timber
83+ [11.1, 440.5], // Ivory extension
84+ [11.5, 445.5], // Bell
85+ ]);
86+}
+3,
-2
1@@ -1,8 +1,9 @@
2-rev=$(shell git log -1 --pretty='format:%cd' --date='format:%Y-%m-%d')
3+commit_date=$(shell git log -1 --pretty='format:%cd' --date='format:%Y-%m-%d')
4+commit_number=$(shell git rev-list --count HEAD)
5
6 all: chanter.3mf split.3mf
7 clean:
8 rm -f *.3mf
9
10 %.3mf: %.scad chanter.scad
11- openscad -D rev=\"$(rev)\" -o $@ $<
12+ openscad -D rev=\"$(commit_number)/$(commit_date)\" -o $@ $<
+21,
-38
1@@ -3,6 +3,8 @@
2
3 use <./sigil.scad>
4
5+fancy = true; // Fancy decorations?
6+
7 rev = "preview";
8 stamp = str(rev);
9
10@@ -29,7 +31,7 @@ module wood() {
11
12 // A compression fitting, basically.
13 module ringyding(h, d) {
14- color("goldenrod") resize([d, d, h]) sphere(d=d);
15+ color("goldenrod") resize([d, d, h]) sphere();
16 }
17
18 // A fillet is a sort of trumpet bell shape
19@@ -45,25 +47,6 @@ module fillet(h, d1, d2) {
20 }
21 }
22
23-// Just a rotated cylinder
24-// h: height of the *top* of the protrusion
25-// d: height of the protrusion (diameter?)
26-// protrusion: amount of protrusion
27-module bumpout(h, d, protrusion) {
28- intersection() {
29- translate([0, -protrusion, h-d]) {
30- cylinder(h=d, d=20.4);
31- }
32- translate([0, -protrusion, h-d/2]) {
33- sphere(d=protrusion*4);
34- }
35- translate([0, 0, h-d]) {
36- cylinder(h=d, d1=19, d2=50);
37- }
38- }
39-}
40-
41-
42 // A tonehole with :
43 // * height=h
44 // * transverse diameter = td
45@@ -90,25 +73,25 @@ module chanter() {
46
47 // Wood body
48 translate([0, 0, 32.5]) wood() cylinder(h=258, d1=20.4, d2=18);
49+
50+ if (fancy) {
51+ // Bottom wood transition
52+ translate([0, 0, 33.7]) ringyding(h=8, d=23);
53+ translate([0, 0, 25.6]) ivory() fillet(h=7, d1=18, d2=22); // eliminate overhang
54+ translate([0, 0, 25.6]) ringyding(h=4.3, d=19);
55
56- // Bottom wood transition
57- translate([0, 0, 33.7]) ringyding(h=8, d=23);
58- translate([0, 0, 25.6]) ivory() fillet(h=7, d1=18, d2=22); // eliminate overhang
59- translate([0, 0, 25.6]) ringyding(h=4.3, d=19);
60-
61- // Top wood transition
62- translate([0, 0, 290.4]) ringyding(h=5.5, d=20);
63- translate([0, 0, 305.6]) ringyding(h=4.3, d=19);
64- translate([0, 0, 317.6]) ringyding(h=5.5, d=20.2);
65- translate([0, 0, 318.6]) ivory() fillet(h=8.4, d1=17, d2=24); // eliminate overhang
66- translate([0, 0, 328.3]) ringyding(h=8, d=25.4);
67-
68+ // Top wood transition
69+ translate([0, 0, 290.4]) ringyding(h=5.5, d=20);
70+ translate([0, 0, 305.6]) ringyding(h=4.3, d=19);
71+ translate([0, 0, 317.6]) ringyding(h=5.5, d=20.2);
72+ translate([0, 0, 318.6]) ivory() fillet(h=8.4, d1=17, d2=24); // eliminate overhang
73+ translate([0, 0, 328.3]) ringyding(h=8, d=25.4);
74+ } else {
75+ translate([0, 0, 27.5]) wood() cylinder(h=5, d1=17, d2=20.4);
76+ }
77+
78 // This protects the reed and provides a place for tubing to connect
79- translate([0, 0, 324.5]) metal() cylinder(h=32.7, d=14.8);
80-
81- // Bumpouts
82- // These angles are my best guess based on photos
83- rotate(220) wood() bumpout(161.2, 14.8, 6); // protrusion guessed
84+ translate([0, 0, 324.5]) metal() cylinder(h=32.7, d=16);
85 }
86
87 // Inner bore
88@@ -121,7 +104,7 @@ module chanter() {
89
90 // Tone Holes!
91 translate([0, 0, 5]) { // Magic offset from published measurements
92- rotate(180) tonehole(263, 6.04, 7.95, 10.1, 1.2); // back D
93+ rotate(180) tonehole(268, 6.04, 7.95, 10.1, 1.2); // back D
94 rotate(0) tonehole(246.4, 6.42, 6.57, 11.3); // C♯
95 rotate(0) tonehole(216.2, 6.89, 6.96, 11.3); // B
96 rotate(0) tonehole(182, 8.85, 9.06, 11.4); // A
+7,
-1
1@@ -1,4 +1,10 @@
2-use <MCAD/regular_shapes.scad>
3+// A simple re-implementation from mcad
4+module cylinder_tube(height, radius, wall) {
5+ difference() {
6+ cylinder(h=height, r=radius);
7+ cylinder(h=height, r=radius-wall);
8+ }
9+}
10
11 module sigil(d, stamp, rot=0) {
12 // Sigil