- commit
- c38bb58
- parent
- cb65741
- author
- Neale Pickett
- date
- 2025-11-28 09:55:45 -0700 MST
maybe fix tenon, clean up everything
16 files changed,
+145,
-333
+10,
-2
1@@ -1,9 +1,17 @@
2-default: chanter.stl
3+default: chanter
4
5+.PHONY: chanter
6+chanter: chanter/top.3mf
7+chanter: chanter/bottom.3mf
8+
9+.PHONY: clean
10 clean:
11- rm -f *.stl
12+ rm -f */*.stl */*.3mf
13
14 %.stl: %.scad
15 openscad -o $@ $<
16
17+%.3mf: %.scad
18+ openscad -o $@ $<
19+
20 chanter/chanter-%.stl: chanter/chanter-%.scad chanter/chanter.scad
+1,
-1
1@@ -1,5 +1,5 @@
2 include <../common.scad>
3-use <./chanter.scad>
4+use <./complete.scad>
5
6 tenon(top=false) {
7 chanter();
+0,
-114
1@@ -1,114 +0,0 @@
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-
11-module tonehole(h=10, d=10, undercut=0) {
12- rotate([0, 0, -90])
13- translate([od/2, 0, h]) {
14- rotate([0, -90+undercut, 0]) {
15- // Scoot it down a hair so it will penetrate the entire tube when rotated
16- translate([0, 0, -od/2]) {
17- cylinder(h=od, d=d);
18- }
19- }
20- }
21-}
22-
23-
24-// Something like David Daye's Penny Chanter,
25-// based on my measurements and undercut hints on David's web site.
26-// This is symmetrical, so both pieces can be printed at the same time,
27-// with color changes
28-module chanter() {
29- difference () {
30- union() {
31- // The core part of the instrument is this brass tube
32- difference() {
33- brass() cylinder(h=height, d=od);
34-
35- // Seam guides
36- translate([0, od/2, 0]) cylinder(d=1, h=400);
37- }
38-
39- // David adds this outer Derlin tube as an upgrade option
40- difference() {
41- derlin() translate([0, 0, 40.0]) cylinder(h=290, d=od_derlin);
42-
43- // The slits up the sides serve three purposes:
44- // 1. directs the slicer to place seams in it (and not on the face)
45- // 2. provides alignment guides for attaching the two pieces
46- // 3. looks like an intentional stylistic thing
47- rotate([0, 0, 85]) translate([0, od_derlin/2, 0]) cylinder(d=1, h=cut_height+20); // overlap
48- rotate([0, 0, -85]) translate([0, od_derlin/2, cut_height-20]) cylinder(d=1, h=400);
49- }
50-
51- // These little doodads are probably just to hide the Derlin seam
52- ivory() translate([0, 0, 30.0]) cylinder(h=12, d=22.0);
53- ivory() translate([0, 0, height - 30.0 - 12]) cylinder(h=12, d=22.0);
54- }
55-
56- // Seam guides
57- translate([0, 11, 0]) cylinder(d=1, h=400);
58-
59- // Inner bore, which runs up to the place where the reed drops in
60- translate([0, 0, 0]) cylinder(h=337.01, d1=id_bot, d2=id_top);
61- translate([0, 0, 337]) cylinder(h=23.1, d1=id_top, d2=7.1);
62-
63- // I totally dig David's minimalist aesthetic
64- translate([0, 0, 3]) rings(d=16.0);
65-
66- // Stamp my name on it
67- difference() {
68- translate([0, 0, 307]) {
69- rotate([90, 0, 180]) {
70- linear_extrude(50) {
71- translate([0, 5]) text("neale", font="Fontdiner Swanky", valign="bottom", size=3, halign="center");
72- translate([0, 0]) text("2024-2", font="Fontdiner Swanky", valign="bottom", size=3, halign="center");
73- }
74- }
75- }
76- cylinder(h=360, d=od-0.5);
77- }
78-
79-
80- // Tone Holes!
81- // XXX: Undercut angle should be taken into account here
82- tonehole(h=57.1, d=6.68); // E-
83- tonehole(h=87.0, d=4.25); // E
84- tonehole(h=118.0, d=8.33); // F#
85- tonehole(h=149.0, d=5.51, undercut=-15); // G
86- tonehole(h=184.5, d=7.75, undercut=15); // A
87- tonehole(h=184.5, d=7.75, undercut=-15); // A (wide undercut)
88- tonehole(h=219.0, d=6.72, undercut=15); // B
89- tonehole(h=250.0, d=5.50, undercut=30); // C
90- rotate([0, 0, 180]) tonehole(h=270.0, d=7.11, undercut=30); // D
91- }
92-}
93-
94-module tenon(top=false, h=cut_height, od=od, depth=20, thickness=2) {
95- module cut() {
96- cylinder(h=cut_height+depth, d=od-thickness-clearance);
97- cylinder(h=cut_height, d=50);
98- }
99-
100- if (top) {
101- difference() {
102- children();
103- cut();
104- }
105- } else {
106- intersection() {
107- children();
108- cut();
109- }
110- }
111-}
112-
113-chanter();
114-//translate([0, 50, 360]) rotate([180, 0, 0]) chanter();
115-
+16,
-15
1@@ -1,5 +1,7 @@
2 include <../common.scad>
3
4+$fn = 180;
5+
6 // Evertjan 't Hart's guide was super helpful:
7 // https://www.hartdd.com/reedmaking/reed1.html
8 //
9@@ -7,13 +9,12 @@ include <../common.scad>
10 // the width comes out to 13.2mm,
11 // which is what the page says it should be.
12
13+printing = true; // If false, sand the inside to be circular. FDM 3d printers can't do this, so you have to sand it afterwards.
14 length = 50;
15 thickness = 0.55;
16 cane_d = 24;
17 base_thickness = 2;
18
19-scrape_angle = atan((base_thickness-thickness)/length)*2.6;
20-
21 difference() {
22 translate([0, 0, -cane_d/2+base_thickness]) {
23 difference() {
24@@ -27,23 +28,23 @@ difference() {
25
26 // Cut the tails
27 // I totally guessed at all of these
28- #translate([0, 10, 0]) rotate([0, 0, -30]) cube(20);
29+ for (m=[0:1]) {
30+ mirror([m, 0, 0]) {
31+ a=60; // I just eyeballed this
32+ translate([a+1, 0, -1]) cylinder(r=a, h=10);
33+ }
34+ }
35
36
37 // Sand inside to 1.5mm thickness
38- sander_d = 82;
39- translate([0, 0, base_thickness - 1.5 - sander_d/2]) rotate([-90, 0, 0]) cylinder(d=sander_d, h=length);
40+ if (!printing) {
41+ sander_d = 82;
42+ translate([0, -0.5, base_thickness - 1.5 - sander_d/2]) rotate([-90, 0, 0]) cylinder(d=sander_d, h=length+1);
43+ }
44
45 // Scrape reed
46- translate([0, length, d/2-base_thickness+thickness]) rotate([-scrape_angle, 0, 0]) translate([-d/2, -length, 0]) cube([d, length, d]);
47-
48- // Reed chopper
49- translate([0, length, 0]) {
50- translate([0, -d/2, 0]) {
51- difference() {
52- translate([-d/2, 0, 0]) cube([d, d/2, d]);
53- cylinder(d=d, h=d);
54- }
55- }
56+ scrape_angle = 3.5;
57+ for (m=[-5,0,5]) {
58+ rotate([0, m, 0]) translate([-cane_d/2, 30, base_thickness]) rotate([-scrape_angle, 0, 0]) cube([cane_d, length, cane_d]);
59 }
60 }
+1,
-1
1@@ -1,4 +1,4 @@
2-use <./chanter.scad>
3+use <./complete.scad>
4
5 tenon(top=true) {
6 chanter();
+2,
-2
1@@ -1,8 +1,8 @@
2 // Make circles circular in final renders.
3-$fn = $preview ? 0 : 60;
4+$fn = $preview ? 0 : 180;
5
6 // clearance in a permanent tenon/mortise joint
7-clearance = 0.03;
8+clearance = 0.04;
9
10 module brass() {
11 color("gold") children();
+4,
-12
1@@ -1,6 +1,6 @@
2 include<../../common.scad>
3
4-id = 7.12;
5+id = 6;
6 od = 8;
7 pluglen = 5; // Length of the plug at the end
8 opening = [2, 10, 6]; // Opening size
9@@ -9,23 +9,15 @@ cutaway = [10, 0.74, 28]; // Tongue cutaway
10 module reed_body() {
11 difference() {
12 // The main piece
13- cylinder(h=100, d=od);
14+ cylinder(h=100, d=od, $fn=6);
15
16 // Inner bore
17 translate([0, 0, pluglen]) {
18- difference() {
19- cylinder(h=100, d=id);
20-
21- // Leave material for a platform on the tongue cutaway
22- translate([-5, id/2 - cutaway[1], 0]) cube(cutaway);
23- }
24+ cylinder(h=100, d=id, $fn=6);
25 }
26
27- // Tongue cutaway
28- translate([-5, od/2 - cutaway[1], 0]) cube(cutaway);
29-
30 // Opening
31- translate([-opening[0]/2+.6, 0, pluglen]) cube(opening);
32+ translate([-opening[0]/2, 0, pluglen]) cube(opening);
33 }
34 }
35
+0,
-36
1@@ -1,36 +0,0 @@
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-}
+5,
-3
1@@ -77,10 +77,12 @@ module chanter() {
2 }
3
4 joint = 160; // Where to split it in half for printing
5-module tenon(top=false, h=joint, od=od-5, depth=20, thickness=2, clearance=0.12) {
6+module tenon(top=false, h=joint, od=od-5, depth=20, thickness=2, clearance=0.2) {
7 module cut() {
8- cylinder(h=h+depth, d=od-thickness+clearance);
9- cylinder(h=h, d=50);
10+ d = od-thickness+clearance;
11+ cylinder(h=h+depth, d=d);
12+ cylinder(h=h, d=d+20);
13+ translate([0, 0, h+depth]) cylinder(h=d/2, d1=d, d2=0);
14 }
15
16 if (top) {
+4,
-3
1@@ -1,13 +1,14 @@
2 use <MCAD/regular_shapes.scad>
3
4-module sigil(d, stamp) {
5+module sigil(d, stamp, rot=0) {
6 // Sigil
7 translate([0, 0, 4]) intersection() {
8 cylinder_tube(50, d/2+0.5, 1);
9 union() {
10- rotate([90, 90, -90]) {
11+ rotate([90, 0, -90]) {
12 linear_extrude(height=d, convexity=4) {
13- translate([-8, -5]) import("ruby.svg");
14+ translate([-4, 0]) import("ruby.svg");
15+ // XXX: when this is in openscad prod, add center to import and remove translate
16 }
17 }
18 rotate([90, 90, -160]) {
+1,
-1
1@@ -1,6 +1,6 @@
2 rev=$(shell git log -1 --pretty='format:%cd' --date='format:%Y-%m-%d')
3
4-all: top.3mf bottom.3mf
5+all: chanter.3mf split.3mf
6 clean:
7 rm -f *.3mf
8
+0,
-5
1@@ -1,5 +0,0 @@
2-use <./chanter.scad>
3-
4-split(top=false) {
5- chanter();
6-}
+97,
-108
1@@ -1,88 +1,66 @@
2 // Based on O'Flynn Rowsome Chanter Measurements
3 // http://pipers.ie/source/media/?mediaId=31307&galleryId=1353
4
5-use <./tenon.scad>
6+use <./sigil.scad>
7
8-$fn =60;
9+rev = "preview";
10+stamp = str(rev);
11+
12+$fn =120;
13
14 // Diameter of screw hole
15 Screwhole = 4; // [1:10]
16
17 module metal() {
18- color("silver") children();
19+ color("silver") children();
20 }
21
22 module leather() {
23- color("sienna") children();
24+ color("sienna") children();
25 }
26
27 module ivory() {
28- color("wheat") children();
29+ color("wheat") children();
30 }
31
32 module wood() {
33- color("saddlebrown") children();
34+ color("saddlebrown") children();
35 }
36
37-// A shape like a hamburger patty
38-module patty(h, d) {
39- intersection() {
40- cylinder(h=h, d=d);
41- translate([0, 0, h/2]) {
42- resize([d, d, h*3]) {
43- sphere(d=d);
44- }
45- }
46- }
47-}
48-
49-// A cylinder with something like a compression fitting around it
50+// A compression fitting, basically.
51 module ringyding(h, d) {
52- ivory() resize([d, d, h]) sphere(d=d);
53+ color("goldenrod") resize([d, d, h]) sphere(d=d);
54 }
55
56 // A fillet is a sort of trumpet bell shape
57 module fillet(h, d1, d2) {
58- r = abs(d1-d2)/2;
59- render(convexity=4) {
60- resize([d1, d1, h]) {
61- rotate_extrude() {
62- translate([d2/2, 0, 0]) {
63- difference() {
64- square([r, r]);
65- translate([r, r]) circle(r=r);
66- }
67- }
68- }
69+ rmin = min(d1, d2)/2;
70+ rmax = max(d1, d2)/2;
71+ r = rmax-rmin;
72+ rotate_extrude(convexity=4) {
73+ difference() {
74+ square([rmax, h]);
75+ translate([rmax, d1>d2?h:0]) resize([r*2, h*2]) circle();
76 }
77 }
78 }
79
80-// An upside-down fillet
81-module tellif(h, d2, d1) {
82- translate([0, 0, h]) mirror([0, 0, 1]) fillet(h, d1, d2);
83-}
84-
85-// Absolutely nothing: helps make the code look better
86-module nothing(h) {
87-}
88-
89 // Just a rotated cylinder
90 // h: height of the *top* of the protrusion
91 // d: height of the protrusion (diameter?)
92 // protrusion: amount of protrusion
93 module bumpout(h, d, protrusion) {
94- intersection() {
95- translate([0, -protrusion, h-d]) {
96- cylinder(h=d, d=20.4);
97- }
98- translate([0, -protrusion, h-d/2]) {
99- sphere(d=protrusion*4);
100- }
101- translate([0, 0, h-d]) {
102- cylinder(h=d, d1=19, d2=50);
103- }
104+ intersection() {
105+ translate([0, -protrusion, h-d]) {
106+ cylinder(h=d, d=20.4);
107 }
108+ translate([0, -protrusion, h-d/2]) {
109+ sphere(d=protrusion*4);
110+ }
111+ translate([0, 0, h-d]) {
112+ cylinder(h=d, d1=19, d2=50);
113+ }
114+ }
115 }
116
117
118@@ -103,73 +81,84 @@ module tonehole(h, td, ld, ch, undercut=1) {
119 }
120
121 module chanter() {
122- difference() {
123- union() {
124- translate([0, 0, 0]) metal() cylinder(h=22.0, d=17.1);
125- translate([0, 0, 22]) wood() cylinder(h=23.5, d=17.1); // Rings go around this
126+ // We start by building everything up in a union(),
127+ // then we ream it with difference().
128+ difference() {
129+ union() {
130+ // Inner metal tube
131+ metal() cylinder(h=331.2, d=17);
132
133- // Decorative stuff on the bottom
134- translate([0, 0, 32]) {
135- translate([0, 0, 1.7]) ringyding(h=8, d=28);
136- translate([0, 0, 8.2]) ringyding(h=5, d=22);
137- }
138-
139- // A taper on that bottom ring so it will print nicely
140- translate([0, 0, 12]) wood() tellif(h=20, d1=25.5, d2=100);
141+ // Wood body
142+ translate([0, 0, 32.5]) wood() cylinder(h=258, d1=20.4, d2=18);
143
144- // Main body
145- translate([0, 0, 32.5]) wood() cylinder(h=258, d1=20.4, d2=18);
146+ // Bottom wood transition
147+ translate([0, 0, 33.7]) ringyding(h=8, d=23);
148+ translate([0, 0, 25.6]) ivory() fillet(h=7, d1=18, d2=22); // eliminate overhang
149+ translate([0, 0, 25.6]) ringyding(h=4.3, d=19);
150
151- // Top decoration
152- translate([0, 0, 290.4]) {
153- color("silver") cylinder(h=40.8, d=17);
154-
155- translate([0, 0, 0.0]) ringyding(h=5.5, d=20);
156- translate([0, 0, 5.5]) nothing(h=9.7); // metal
157- translate([0, 0, 15.2]) ringyding(h=4.3, d=19);
158- translate([0, 0, 19.5]) nothing(h=6.7); // metal
159- translate([0, 0, 27.2]) ringyding(h=5.5, d=20.2);
160- translate([0, 0, 28.2]) leather() {
161- // XXX: kludge
162- tellif(h=8, d2=19, d1=23);
163- cylinder(h=8, d=19);
164- }
165- translate([0, 0, 37.9]) ringyding(h=8, d=25.4);
166- }
167+ // Top wood transition
168+ translate([0, 0, 290.4]) ringyding(h=5.5, d=20);
169+ translate([0, 0, 305.6]) ringyding(h=4.3, d=19);
170+ translate([0, 0, 317.6]) ringyding(h=5.5, d=20.2);
171+ translate([0, 0, 318.6]) ivory() fillet(h=8.4, d1=17, d2=24); // eliminate overhang
172+ translate([0, 0, 328.3]) ringyding(h=8, d=25.4);
173
174- // This protects the reed and provides a place for tubing to connect
175- translate([0, 0, 324.5]) metal() cylinder(h=32.7, d=14.8);
176+ // This protects the reed and provides a place for tubing to connect
177+ translate([0, 0, 324.5]) metal() cylinder(h=32.7, d=14.8);
178
179- // Bumpouts
180- // These angles are my best guess based on photos
181- rotate(220) wood() bumpout(161.2, 14.8, 6); // protrusion guessed
182- }
183-
184- // Inner bore
185- union() {
186- translate([0, 0, -0.01]) { // Go just a bit past the ends
187- translate([0, 0, 0]) cylinder(h=337.01, d1=13.2, d2=5.51);
188- translate([0, 0, 337]) cylinder(h=21, d1=5.51, d2=7.1);
189- }
190- }
191-
192- // Tone Holes!
193- translate([0, 0, 5]) { // This offset is specified nowhere. I'm guessing to make it fit the bumpouts.
194- rotate(180) tonehole(263, 6.04, 7.95, 10.1, 1.2); // back D
195- rotate(0) tonehole(246.4, 6.42, 6.57, 11.3); // C♯
196- rotate(0) tonehole(216.2, 6.89, 6.96, 11.3); // B
197- rotate(0) tonehole(182, 8.85, 9.06, 11.4); // A
198- rotate(0) tonehole(147.4, 6.98, 7.44, 12.2); // G
199- rotate(0) tonehole(116.2, 8.39, 8.88, 12.7); // F♯
200- rotate(0) tonehole(84.7, 5.25, 5.49, 14); // E
201- rotate(0) tonehole(53.3, 6.94, 7.16, 14.1); // E♭
202- }
203+ // Bumpouts
204+ // These angles are my best guess based on photos
205+ rotate(220) wood() bumpout(161.2, 14.8, 6); // protrusion guessed
206+ }
207+
208+ // Inner bore
209+ union() {
210+ translate([0, 0, -0.01]) { // Go just a bit past the ends
211+ translate([0, 0, 0]) cylinder(h=337.01, d1=13.2, d2=5.51);
212+ translate([0, 0, 337]) cylinder(h=21, d1=5.51, d2=7.1);
213+ }
214 }
215+
216+ // Tone Holes!
217+ translate([0, 0, 5]) { // Magic offset from published measurements
218+ rotate(180) tonehole(263, 6.04, 7.95, 10.1, 1.2); // back D
219+ rotate(0) tonehole(246.4, 6.42, 6.57, 11.3); // C♯
220+ rotate(0) tonehole(216.2, 6.89, 6.96, 11.3); // B
221+ rotate(0) tonehole(182, 8.85, 9.06, 11.4); // A
222+ rotate(0) tonehole(147.4, 6.98, 7.44, 12.2); // G
223+ rotate(0) tonehole(116.2, 8.39, 8.88, 12.7); // F♯
224+ rotate(0) tonehole(84.7, 5.25, 5.49, 14); // E
225+ rotate(0) tonehole(53.3, 6.94, 7.16, 14.1); // E♭
226+ }
227+
228+ sigil(17.1, stamp);
229+ }
230 }
231
232 module split(top=false) {
233- tenon(top=top, h=162, d=15, depth=15) {
234- children();
235+ h=162; // Where to make the cut
236+ d=14; // Diameter of the tenon
237+ depth=15; // Insertion depth of the tenon
238+ clearance=0.12; // Gap between tenon and mortise
239+
240+ module cut(id) {
241+ translate([0, 0, h+depth]) cylinder(h=id/2, d1=id, d2=0);
242+ cylinder(h=h+depth, d=id);
243+ cylinder(h=h, d=id+40);
244+ }
245+
246+ if (top) {
247+ translate([0, 0, -h]) {
248+ difference() {
249+ chanter();
250+ cut(d+clearance);
251+ }
252+ }
253+ } else {
254+ intersection() {
255+ chanter();
256+ cut(d);
257+ }
258 }
259 }
260
+4,
-0
1@@ -0,0 +1,4 @@
2+use <./chanter.scad>
3+
4+translate([-20, 0, 0]) split(true);
5+translate([20, 0, 0]) split(false);
+0,
-24
1@@ -1,24 +0,0 @@
2-use <BOSL/math.scad>
3-
4-module tenon(top=false, h=100, d=5, depth=20, thickness=2, clearance=0.2) {
5- module cut() {
6- id = d-thickness+clearance;
7- translate([0, 0, h+depth]) cylinder(h=id/2, d1=id, d2=0);
8- cylinder(h=h+depth, d=id);
9- cylinder(h=h, d=id+40);
10- }
11-
12- if (top) {
13- difference() {
14- children();
15- cut();
16- }
17- } else {
18- intersection() {
19- children();
20- cut();
21- }
22- }
23-}
24-
25-chanter();
+0,
-6
1@@ -1,6 +0,0 @@
2-use <./chanter.scad>
3-use <./tenon.scad>
4-
5-split(top=true) {
6- chanter();
7-}