//whistle tree
//Holland Hopson
//2021
//generates a tree of whistles with a specific fundamental pitch and each segment tuned an interval above the segment below
//requires hopson_attachment_bar.scad
//Adjust Hz to tune whistle and interval to determine the relative tuning of each branch on the tree. Adjust x, z1, z2, and xy_offset to change the shape of the tree
include ;
$fn = 100; //make high resolution
//customize these variables
Hz = 900; //fundamental
interval = 0.666666; //specifies reduction of size of each segment and thus the sounding interval of each whistle segment.
//sample values:
//0.5 = octaves
//0.666666 = perfect 5th 3/2
//0.75 = perfect 4th 4/3
//0.8 = pythagorean major 3rd 5/4
//0.88888 = pythagorean second 9/8
//etc.
minSegmentHeight = 15; //set the minimum height of the smallest segment.
//this effectively controls the number of whistle segments in the tree
//<15mm is typically too small to sound, but this value depends on the resolution of your printer
//tree shape variables
x=15; //angle of branch. Larger values cause tree branches to spread more. 15 is a good compromise between tree shape and compact structure that rotates well.
z1=25; //amount of rotation for branches in positive x dimension. Adjust this to make sure slits aren't covered by other branches
z2=65; //amount of rotation for mirrored branches in negative x dimension. Adjust this to make sure slits aren't covered by other branches
xy_offset=0.13; //amount to shift the branches away from each other, so they're not on top of each other.
//constants and calculations
speed_of_sound = 343.; //
height = ((speed_of_sound / (Hz * 8.))*1000.); //multiply by 1000 to get mm
//echo(height, " mm");
//echo(height * 0.1, " cm"); //print height in cm
diameter = height*0.8; //width of object. Was hard coded, now proportional to height since it changes for each iteration
//variable for attachment bar
bar_radius = 3; //a chunkier bar radius since whistle trees tend to be heavier
cavity_diameter = 20; //At least 10mm, but less than 40mm (or diameter of whistle)
cavity_depth = 2*(bar_radius + 5); //2mm to provide clearance for smaller swivel. 5mm for larger swivel. Multiply by 2 because we're using a sphere, so this number is the diameter
//attachment bar bottom
translate([0,0,(-1*cavity_depth/2)+0.001]){ //move to bottom of cylinder, with a small offset to make sure the attachment bar touches the bottom of the whistle
rotate([180,0,0]){
attachmentCavity();
attachmentBar();
}
}
whistle_tree(height);
module whistle_tree(s)
{
//constants from module parameters.
diameter = s*0.8; //this was hard-coded at 40mm
slit_width=s*0.125; //this was hard-coded at 5.5mm
wall_thickness=2;
top_bottom_thickness=2;
angle=50; //angle of slit
//constants and calculations
echo(s);
slit_length = s/3; //one third of height of cylinder
translate([0,0,s/2]) //position bottom of object on origin
{
difference()
{
union() { //objects to be added to the design
//outer shell
cylinder(h = s, r = diameter/2, center=true);
}
union() { //objects to be subtracted from the design
//inner space
cylinder(h = s-(2*top_bottom_thickness), r = (diameter/2)-wall_thickness, center=true);
//slit
translate([0, -diameter*0.5, 0]) { //move slit so it pokes out the front side
rotate([0, 0, angle]) { //rotating too far eats into the inside wall
cube([slit_width,diameter*0.65,slit_length], center=true);
}
}
//channel to direct air to edge
translate([0, -diameter*0.5-(slit_length/2)+(wall_thickness*0.25), 0]) {
rotate([90, 90, angle*2]) { //rotate cylinder so rounded edge is approx. perpendicular to slit
scale([2,1,1]) //squash cylinder so it contacts the surface more broadly
cylinder(r = slit_length/2, h = slit_length);
}
}
}
}
}
//recursion
if(s>minSegmentHeight) //as long as size isn't too small
//for future improvement, detect if level of tree is even or odd, then change translate x and y using offset and -offset
translate([0,0,s*.99]){ //move base of next branch of tree
translate([s*xy_offset,-s*xy_offset,0]) //shift base of tree branch off center
rotate([x,0,z1])
whistle_tree(s*interval); //recursion of the tree. constant specifies reduction of size
//translate([10,0,0]) //shift branch to the side
rotate([-x,0,z2])
translate([-s*xy_offset,s*xy_offset,0])
rotate([0,0,180]) { //rotate the entire whistle module so the slit is always on the outside
whistle_tree(s*interval); //recursion of the tree. constant specifies reduction of size
}
}
}