//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 } } }