whistles

3d printable Irish whistles
git clone https://git.woozle.org/neale/whistles.git

Neale Pickett  ·  2025-12-13

common.scad

  1// Make circles circular in final renders.
  2$fn = $preview ? 0 : 180;
  3
  4// clearance in a permanent tenon/mortise joint
  5clearance = 0.2;
  6
  7// www.tauday.com
  8TAU = PI * 2;
  9
 10module brass() {
 11  color("gold") children();
 12}
 13
 14module derlin() {
 15  color("dimgray") children();
 16}
 17
 18module ivory() {
 19  color("ivory") children();
 20}
 21
 22// tube creates a hollow cylinder
 23module tube(h=10, id=5, od=10) {
 24  difference() {
 25    cylinder(h=h, d=od);
 26    translate([0, 0, -1]) cylinder(h=h+2, d=id);
 27  }
 28}
 29
 30// donut creates a torus
 31module donut(h=1, d=10) {
 32  rotate_extrude(angle=360) {
 33    translate([d/2, 0]) {
 34      circle(d=h, $fn=120);
 35    }
 36  }
 37}
 38
 39// default height of rings
 40rings_height = 1.8;
 41
 42// rings of adornment.
 43// This engraves two rings, 1.4mm apart, at a depth of 0.4.
 44module rings(h=0.4, d=1, depth=0.4, spacing=1.4) {
 45  tube(h=h, id=d-depth, od=d+1);
 46  translate([0, 0, spacing]) tube(h=h, id=d-depth, od=d+1);
 47}
 48
 49// default brass tube thickness
 50brass_thickness = 0.75;
 51
 52// brass_tube creates a chamfered hollow cylinder with given inside diameter,
 53// and wall thickness of 0.75.
 54// Decorative rings are etched into the top and bottom.
 55module brass_tube(h=10, id=10, t=brass_thickness) {
 56  od = id + (t*2);
 57  cd = (id + od) / 2; // Chamfer diameter
 58
 59  brass() {
 60    difference() {
 61      tube(h=h, id=id, od=od);
 62
 63      // Chamfered ends
 64      translate([0, 0, 0]) cylinder(h=1, d1=cd, d2=id);
 65      translate([0, 0, h-1]) cylinder(h=1, d1=id, d2=cd);
 66
 67      translate([0, 0, 2]) rings(d=od);
 68      translate([0, 0, h-2-rings_height]) rings(d=od);
 69    }
 70  }
 71}
 72
 73// brass_bend provides a 180 degree bend in brass
 74module brass_bend(od=12.7, bend_r=16, t=brass_thickness) {
 75  color("gold") {
 76    rotate([90, 0, 0]) {
 77      rotate_extrude(angle=180) {
 78        translate([bend_r, 0, 0]) {
 79          difference() {
 80            circle(d=od);
 81            circle(d=od-t*2);
 82          }
 83        }
 84      }
 85    }
 86  }
 87}
 88
 89
 90// derlin_tube is a non-chamfered sky blue tube.
 91// I use blue so it's easier to see in preview.
 92module derlin_tube(h=10, id=5, od=10) {
 93  derlin() {
 94    tube(h=h, id=id, od=od);
 95  }
 96}
 97
 98// derlin_stop is a derlin tube, chamfered on one end.
 99module derlin_stop(h=10, id=5, od=10) {
100  chamferh = h/5;
101  derlin() {
102    difference() {
103      union() {
104        cylinder(h=h-chamferh, d=od);
105        translate([0, 0, h-chamferh]) cylinder(h=chamferh, d1=od, d2=id);
106        }
107      cylinder(h=h, d=id);
108      }
109    }
110}
111
112// default bell height
113bell_height = 17;
114
115// derlin_bell makes a decorative flared bell.
116// od will default to 33% larger than id.
117module derlin_bell(h=bell_height, id=5, od=0) {
118  chamferh = 1;
119  chamferd = 1;
120  ringsmargin = 1;
121  toph = rings_height + ringsmargin*2 + chamferh;
122  flareh = h - toph;
123  od = (od == 0) ? (id * 1.33) : od;
124  derlin() {
125    difference() {
126      union() {
127        cylinder(h=flareh, d1=id, d2=od);
128        translate([0, 0, flareh]) cylinder(h=toph-chamferh, d=od);
129        translate([0, 0, flareh+toph-chamferh]) cylinder(h=chamferh, d1=od, d2=od-chamferd);
130      }
131      cylinder(h=h, d=id);
132      translate([0, 0, flareh + ringsmargin]) rings(d=od);
133    }
134  }
135}
136
137// A tenon joint.
138//
139// You have to do this twice, to make two pieces. Glue them together.
140module tenon(h, od, top, depth=20, thickness=2) {
141  cl = top ? 0 : clearance;
142  module cut() {
143    cutd = od - thickness - cl;
144    cylinder(h=h+depth, d=cutd);
145    cylinder(h=h, d=50);
146    translate([0, 0, h + depth]) cylinder(h=cutd, d1=cutd, d2=0);
147  }
148
149  if (top) {
150    difference() {
151      children();
152      cut();
153    }
154  } else {
155    intersection() {
156      children();
157      cut();
158    }
159  }
160}