/* TransiDupe Duplicate tansmissive film. (We are running out of names for slide duplicators.) 2/14/2016 Version .9.017 Remixed from "DupliHood -- a slide copying attachment" by Hank Dietz http://www.thingiverse.com/thing:431624 http://www.instructables.com/id/3D-Printed-Slide-Copy-Attachment/ Modified to work with the PhotoSolve Xtend-a-Slide https://www.photosolve.com/main/product/xtendaslide/index.html TransiDupe is released under the Creative Commons - attribution license http://creativecommons.org/licenses/by/3.0/ requires OpenSCAD version 2015.03, or newer. */ /* Global */ // How many sections? (each section is 23.5mm) // "2" (ea.) is typical for a P&S + closeup lens // "3" (ea.) works for an APS-C (crop) camera + 60mm macro lens for 35mm slides/negatives // "7" (ea.) works for an APS-C (crop) camera + 100mm macro lens for 35mm slides/negatives // Sections are ~50mm in diameter. So probably shouldn't make more than 1 or 2 rear sections // for film6x6 (AKA 120 film) which has approx. 70mm diagonal. number_of_rear_sections=2; // [1:9] number_of_front_sections=2; // [1:9] // Which part do you want to make? part = "exploded"; // [fronttube:The front tube,reartube:The tube that attaches to the step-up ring that is attached to the lens, slidecarrier:Slide carrier,negativecarrier:, Negative carrier,negativecarriertop:, Negative carrier top part, negativecarrierboth: Both parts of the negative carrier (with rim removed--to check fit of both parts),springpart:The spring cap that holds the slide,diffuser:The optional diffuser,exploded:Exploded view of all three parts (for viewing, not printing),bothtubes:Like "exploded", but only prints the front and rear tubes,calslide:Blank slide for calibration,ruler:Ruler that shows how far the slide should be for the designated lengths of the tubes,thumbshell: Fits over a hose clamp screw so you don't need a screwdriver] // playpen1:For experimenting (currently, is about toruses (o-rings...)), xslidecarrier:Xtend-a-Slide slide carrier // What is the slide format? (Anything other than "film6x6", exactly, means 35mm or 110 (Instamatic).) // We support both slides and negatives. "film6x6 is 6x6cm slides or 120 film negative // Not film6x6 could be either 35mm slides or negatives, or 110 (Instamatic) negative. film = "film6x6x"; // [film6x6:Mounted 6x6cm slides,film35:Mounted 35mm (135 format) slides] is110 = "truex"; // [is110:Treat "35mm" film like 110 film (we have three choices of film size)] // Debugging. "true" makes some parts thinner/shorter for making test prints // (to see if everything fits together.) // Anything other than "true" == false; "truex" == false // i.e., debug="truex" to make normal sized prints. debug="truex"; // [true:Debug mode. Makes prints be shorter but the same diameter, false:Normal mode, truex: is the same as "false" (but is easier to type in the code.)] // Make the round plates on the tubes makeroundplate = "true"; // [true, false] // If maketube is false, we make a crossbar across the center of the rear tube plate // (on the assumption that you will be manipulating the rear tube plate to see if it fits) // We *can* write whatever 'reardiskdiameter' is, but this takes time (+ ~17 seconds) labelcrossbar = "true"; // [true, false] // Make the tubes maketube = "true"; // [true, false] // Set reardiskdiameter to the diameter of the step-up ring that is mounted on your lens. // Because plastic is soft, I suggest that you mount the rear tube to a step-up adapter *once* // and never remove the step-up adapter from the rear tube. // However you *can* set to the filter size on your lens, if you don't want to bother // with a step-up ring. // reardiskdiameter can't be smaller than 52mm. reardiskdiameter=58; // [52, 147] reardiskdiametertol=.1; // Fudge factor to make threads fit. YMMV // Make threads on back plate of rear tube. (Slow...but see finishquality, below) // "Not true" (no threads) implies that you are going to glue the rear tube onto a step-up ring. makethreads = "true"; // [true, false] // Number of threads. Was 3 in original design numthreads=3; // [2,6] // Rendering threads is slow. However, you *must* use fine when printing for real. // (Rendering threads at "fine" adds almost a minute to rendering time.) finishquality="fine"; // [fine:use this when you are going to print for real,draftmode: use this when you are still developing.] // Increases the inside diameter of the front tube. Increase this if the front tube can't fit over the back tube. // (Print both tubes in "debug" mode for testing this. And you don't need the plates to test how thes tube fit.) // Normal is = 0.25 front_tube_tol = 0.75; // [0,2] // Number of slits in the tubes (Default is 10.) num_slits = 10; // [0:100] // Slit thickness in mm (Default is 2.) slit_thickness = 1.5; // Increases the inside diameter of the springpart. // Increase this if the springpart can't fit over the front tube. td2dh_sp=0.025; // [0,2] // Increases the inside diameter of the diffuser. // Increase this if the diffuser can't fit over the springpart. td2dh_df=0.025; // [0,2] // What is the filter thread pitch? (Default is 75) thread_pitch = 75; // [50:Fine 0.5mm, 75:Standard 0.75mm, 100:Coarse 1.0mm, 150:Very coarse 1.5mm] // Tolerance to allow for inaccurate filament placement (usually extrusion diameter/2), in microns? // Default is 100. thread_tolerance = 100; // [0:500] /* [Hidden] */ // Thread rotation angle (to align slide mask with the camera)? // (Shouldn't need to change this.) thread_rotate = 45; // [0:359] /* I don't think that "exploded" is printable. It is handy to see all the parts together. You can load an .stl file into netfabb to see how many cubic centimeter of filiment that all the parts will use. */ // For making test prints to see if the parts fit // (Shorter print time and use less filament.) num_rear_sections=((debug == "true") ? 1 : number_of_rear_sections); num_front_sections=((debug == "true") ? 1 : number_of_front_sections); // Make "sectionheight" be small (5) if you want // to make a test tube to check that the parts fit together properly // before you make the full size part. // 23.5mm is the height of an Xtend-a-slide section //sectionheight = 23.5; sectionheight=((debug == "true") ? 23.5/3 : 23.5 ); film35x=((is110 == "true") ? 18 : 36); film35y=((is110 == "true") ? 14 : 24); filmx=((film == "film6x6") ? 60 : film35x); filmy=((film == "film6x6") ? 60 : film35y); framex=((film == "film6x6") ? 70 : 50); framey=((film == "film6x6") ? 70 : 50); width35mm=((is110 == "true") ? 16.1 : 35.2); // Is "35mm" film really 110 film? negwidth=((film == "film6x6") ? 61 : width35mm); framez=((debug == "true") ? 0 : 3.25); frametol=1; lip=2; //diag=sqrt(((framex+frametol)*(framex+frametol))+((framey+frametol)*(framey+frametol))); innertall=80; overlap=5; spring=1; // mystep defines how fine to render the threads // The smaller mystep is, the longer it takes to render... // Should be = 5 for final part. // mystep = 5 takes about 53 seconds to render on my quadcore i7 // mystep = 30 takes about 8 seconds... // finishquality="fine" == mystep = 30 mystep=((finishquality == "fine") ? 5 : 30); // Must be a multiple of 5 $fn=((finishquality == "fine") ? 90 : 50); tol=0.25; // Xtend-a-Slideish dimensions // Front tube frontdiskdiameter=((film == "film6x6") ? 115 : 87); fdiskheight=2.0; // measured 3.3 frontdiskheight=((debug == "true") ? 1 : fdiskheight ); fronttubefrontindia=framex+front_tube_tol; fronttubethickness=3.5; // Measured 3.33 frontrimwall=2; //fronttubebackthickness=((film == "film6x6") ? fronttubethickness*3 : fronttubethickness); frontrimheight=((debug == "true") ? 3 : 8 ); // Rear tube // Set reardiskdiameter to the diameter of the step-up ring that is mounted on your lens. //reardiskdiameter=67; //reardiskheight=2; reardiskheight=((debug == "true") ? 1 : 2 ); reartubeoutdia=50; reartubethickness=2.35; // measured 2.35mm fronttubebackindia=reartubeoutdia+front_tube_tol; // Xtend-a-Slide carrier cutoutfilmarea = "true"; // Cut out the film area on the Xtend-a-Slide slide carrier // Yes, if supplying your own diffusion // No, if you are printing the carrier in white and it will // be the diffuser. // length of notch to fish the slide out of the carrier with your finger xslidefingernotchlength=31; xslidefingernotchwidth=22.5; xnotchdistance=((film == "film6x6") ? -1 : 13); // Distance finger notch is from the rim // diameter of slide carrier xslidedia=88; //xfrontdiskheight=((debug == "true") ? 1 : 2.7); xfrontdiskheight=2; slidethickness=1.12; // calc length of front tube (Not used yet.) // Using the Pythagorean Theorem // http://www.montereyinstitute.org/courses/DevelopmentalMath/COURSE_TEXT2_RESOURCE/U07_L1_T4_text_final.html // (a*a) + (b*b) = (c*c) lega=(fronttubefrontindia-fronttubebackindia)/2; // Height of vertical part legb=sectionheight; // Length of bottom part legc=sqrt((lega*lega) + (legb*legb)); // hypotenuse sectionheight_h = legc; //length_of_both_tubes = (num_rear_sections+num_front_sections)*sectionheight; length_of_both_tubes = (num_rear_sections*sectionheight)+(num_front_sections*sectionheight); //echo(str("lega: ", lega, ", legb: ", legb, ", legc: ", legc, ", num_front_sections*sectionheight: ", num_front_sections*sectionheight)); echo(str("film: ", film, ", sectionheight: ", sectionheight)); //-----------End of global variables-------- // Make a shell that fits over a hex hose clamp nut. So you don't need to use a screwdriver to adjust it. module thumbshell (od = 12.5, id = 9.7, ht = 16) { difference () { translate([0,0,ht/2]) cylinder(d=od, h=ht, $fn=12, center=true); translate([0,0,ht/2]) cylinder(d=id, h = ht+2, $fn=6, center=true); } if (6 == 9) { translate([0,0,ht*1]) cylinder(d=od, h=2, $fn=12, center=true); translate([0,0,ht*1.8]) rotate([0,90,0]) cube([od, od, 2], center=true); } } // Calibration slide module calslide () { echo(str("Making calibration slide for ", film, ".")); echo(str("Slide Thickness = ", slidethickness, "mm, Frame size is ", filmy, "x", filmx, "mm" )); union() { difference() { // difference() translate([0,0,slidethickness*.5]) cube([framex+frametol*0, framey+frametol*0, slidethickness], center=true); // Full slide translate([0,0,slidethickness*1]) // We are going to remove half the depth of the slide frame. // (So that the patterns within the slide frame will be in the center of the slide. // like real film is... (for focus accuracy.) cube([filmx,filmy, slidethickness], center=true); // punch out the slide frame //#cube([filmx+frametol, 5, slidethickness/2], center=true); crosshatch_thickness = slidethickness/2; // Width of the slits in the crosshatches frame_cutout = slidethickness/2; // Now punch out the calibration crosshatches offset = 1.5; // How far above the film frame should the crosshatches be? numsteps = 9; // How many crosshatches do we want? stretch = 0; // Stretch calculation doesn't work yet. :-( start_x = (-filmx/2); // Position of the first crosshatch end_x = start_x+filmx; // Position of the last crosshatch step = (filmx/(numsteps-1)); // Increment each position by this much (in the loop) y = (-filmy/2); // How high should the crosshatches be on the slide? start_y = y; end_y = (start_y+filmy); // Punch out slits around the outside of the slide flame translate([-start_x-.55, 1,-slidethickness*1]) // left top cube([frame_cutout,filmy/2-2,slidethickness*2]); // vertical translate([start_x-.0, 1,-slidethickness*1]) // left bottom cube([frame_cutout,filmy/2-2,slidethickness*2]); // vertical translate([-start_x-.55, start_y+1,-slidethickness*1]) // right top cube([frame_cutout,filmy/2-2,slidethickness*2]); // vertical translate([start_x+.0, start_y+1,-slidethickness*1]) // right bottom cube([frame_cutout,filmy/2-2,slidethickness*2]); // vertical translate([1,-start_y-.55, -slidethickness*1]) // right top cube([filmx/2-2,frame_cutout,slidethickness*2]); // horizontal translate([1,start_y-.55, -slidethickness*1]) // right bottom cube([filmx/2-2,frame_cutout,slidethickness*2]); // horizontal translate([start_x+1,-start_y-.55, -slidethickness*1]) // left bottom cube([filmx/2-2,frame_cutout,slidethickness*2]); // horizontal translate([start_x+1,start_y-.55, -slidethickness*1]) // left bottom cube([filmx/2-2,frame_cutout,slidethickness*2]); // horizontal // Punch out stuff within the slide frame //#cube([slidethickness*2, crosshatch_thickness, slidethickness*2], center=true); // Exact center //#cube([crosshatch_thickness, slidethickness*2, slidethickness*2], center=true); for (i=[start_x+step:step:end_x-step]) { translate ([i, start_y + offset*4, 0]) { // Below the film frame cube([slidethickness*2, crosshatch_thickness, slidethickness*2], center=true); cube([crosshatch_thickness, slidethickness*2, slidethickness*2], center=true); } translate ([i, 0, 0]) { // cube([slidethickness*2, crosshatch_thickness, slidethickness*2], center=true); cube([crosshatch_thickness, slidethickness*2, slidethickness*2], center=true); } translate ([i, (end_y - offset*4), 0]) { // Above the film frame cube([slidethickness*2, crosshatch_thickness, slidethickness*2], center=true); cube([crosshatch_thickness, slidethickness*2, slidethickness*2], center=true); } } echo (str("numsteps ",numsteps," step ",step, " start_x ",start_x," end_x ",end_x," start_y ",start_y, " end_y ", end_y)); for (i=[start_x:step:end_x]) { //echo(i ); translate ([i, y - offset, 0]) { // Below the film frame cube([slidethickness*2, crosshatch_thickness, slidethickness*2], center=true); cube([crosshatch_thickness, slidethickness*1, slidethickness*2], center=true); //cylinder(h=slidethickness*2,r=slidethickness,center=true); } translate ([i, 0 , 0]) { // On the center (Not needed anymore.) //cube([slidethickness*2, slidethickness/3, slidethickness*2], center=true); //cube([slidethickness/3, slidethickness*1, slidethickness*2], center=true); ////cylinder(h=slidethickness*2,r=slidethickness*1.1,center=true); //} } translate ([i, y-y-y+offset, 0]) { // Above the film frame cube([slidethickness*2, crosshatch_thickness, slidethickness*2], center=true); cube([crosshatch_thickness, slidethickness*1, slidethickness*2], center=true); } // translate () } // for (i=[start_x:step:end_x]) { } // difference } // union() } // module calslide() // Make a ruler to test measurements. // orientation = 90 stands upright to comparing in openSCAD. orientation = 0 is flat (for printing for real) // offset is the distance from 0. (so you can have an upright ruler standing next to the real model.) module makeruler(length = 100, width = 15, offset = 80, orientation = "upright", font_size = 5, print_length = "truex", print_text="true", the_text="Test text") { angle=((orientation == "upright") ? 90 : 0); xoffset=((orientation != "upright") ? length/2 : 0); echo(str("Ruler length = ", length)); rotate([0,-angle,0]) translate([-xoffset,offset,0]){ cube([length, width, 1.5], center = false); if (print_length == "true") { translate([2,width/4,1.5]) linear_extrude(height= 0.6) { text(text = str(length, "mm"), size=5); } } else if (print_text == "true") { translate([length/2,width/4,1.5]) linear_extrude(height= 0.5) { text(text = str(the_text), size=font_size, halign="center"); } } } } // module makeruler() module fronttube( sections=3, buildplate="true", buildtube="true", slits="true") { echo(str("Making front tube for ", film, ".")); echo(str(sections, " sections, sectionheight ", sectionheight, "mm, total length: ",sections*sectionheight, "mm.")); // (Maybe) need Pythagorean Theorem here (for film6x6) (Not thus far.) // Used for calculating fronttube's dimensions shortsection=(film == "film6x6") ? (sections*sectionheight) : (sections*sectionheight); start_straighttube_at=shortsection-(sectionheight*.7); straighttubeheight = ((number_of_front_sections < 1) ? sectionheight*number_of_front_sections : sectionheight*.7); fudgeif1=((sections == 1 && film == "film6x6") ? 1 : 0); // need to lower the straight section if sections == 1. echo(str("Real length of the fronttube: ", shortsection, ".")); filletsize=2.5; if (buildplate == "true" ) { // Start with adding an outer rim on the front disk difference() { cylinder(d=frontdiskdiameter, h=frontrimheight); translate([0,0,-.5]) cylinder(d=frontdiskdiameter-frontrimwall, h=frontrimheight+1); } } difference() { // Temporarily change to "union()" to see what is subtracted // First build a solid front plate and tube union() { if (buildplate == "true" ) { cylinder(d=frontdiskdiameter, h=frontdiskheight); } // Then the tube. Solid, at this point if (buildtube == "true" ) { // //cylinder(d1=(fronttubefrontindia+fronttubethickness), d2=(fronttubebackindia+fronttubethickness)-(fronttubethickness*(fudgeif1/5)), h=start_straighttube_at + fudgeif1); // shortsection-3.9 cylinder(d1=(fronttubefrontindia+fronttubethickness), d2=(fronttubebackindia+fronttubethickness)-(fronttubethickness*(fudgeif1/5)), h=start_straighttube_at + fudgeif1); // shortsection-3.9 if (buildplate == "true" ) { translate([0,0,frontdiskheight-0.5]) // Fake a fillet cylinder(d1=fronttubefrontindia+fronttubethickness+filletsize+0.5+1, d2=fronttubefrontindia+fronttubethickness, h=filletsize+0.5); } } } // union() // Now difference() kicks in and eliminates these: // Eliminate the inner tube translate([0, 0, -2]) cylinder(r1=fronttubefrontindia/2, r2=fronttubebackindia/2, h=start_straighttube_at+40); } // difference() // For debugging. Uncomment to verify tube length calculations (ruler should == tube height) // Normally is commented out! (Prints a vertical "ruler" at one side of the tube.) //makeruler(offset=frontdiskdiameter/2 + 4,length=start_straighttube_at+straighttubeheight-1,orientation = "upright", print_length = "truex", print_text="truex"); // make slits by rotating a skinny rectangle around the tube (which are eliminated with "difference()") translate([0, 0, start_straighttube_at -1 - fudgeif1*0]) { difference() { // Normally is difference() if (buildtube == "true" ) { difference() { cylinder(r=(fronttubebackindia+fronttubethickness)/2, h=straighttubeheight); translate([0,0,-4]) cylinder(r=(fronttubebackindia)/2, h=straighttubeheight+8); } } if (slits == "true" && buildtube == "true") { // Make the slits translate([0,0,+6]) // Raise (or lower) the slits for (i=[1:360/num_slits:360]) { rotate([0,0,i]) cube([fronttubebackindia-(fronttubebackindia*.35), slit_thickness, sectionheight*1.1],center=false); } } } } } // module fronttube() module reartube(sections=3, buildplate="true", buildtube="true", slits="true") { dia=reardiskdiameter+reardiskdiametertol; mag=((dia<49) ? 0.5 : 1); pitch=thread_pitch/100.0; ctol=thread_tolerance/1000.0; threadheight = numthreads * (pitch*.0002); filletsize=3; echo(str("Making rear tube for ", film, ".")); //echo(str(sections, " sections.")); echo(str(sections, " sections, sectionheight of ", sectionheight, "mm, total length: ",sections*sectionheight, "mm.")); echo(str("reartubeoutdia ", reartubeoutdia, "mm, ",reardiskdiameter, "mm reardiskdiameter")); union() { difference() { // Temporarily change to "union()" to see what is subtracted union() { if (buildplate == "true" ) { difference() { if (makethreads != "true") { cylinder(r=(reardiskdiameter/2)+(lip*.0), h=reardiskheight); } else { translate([0,0,(threadheight)]) male_thread(pitch, dia-ctol, numthreads, 0.25); } translate([0,0,reardiskheight]) cylinder(d=reardiskdiameter-3, 20); } // difference() } if (buildtube == "true" ) { translate([0, 0, reardiskheight]) // Make the rear tube cylinder(r=reartubeoutdia/2, h=sections*sectionheight); translate([0,0,reardiskheight-0.5]) // Fake a fillet cylinder(d1=reartubeoutdia+filletsize+0.5+1, d2=reartubeoutdia, h=filletsize+0.5); } } // Eliminate the inner tube. translate([0, 0, (sections*sectionheight)/2]) cylinder(d=reartubeoutdia-reartubethickness*1.5, h=(sections*sectionheight)+20,center=true); if (buildtube == "true" ) { // How high should the slits start? translate([0, 0, (sections*sectionheight-sectionheight*.75) + (reardiskheight+1)]) { if (slits == "true" && buildtube == "true" && 0) { for (i=[1:360/num_slits:360]) { rotate([0,0,i]) cube([fronttubebackindia-(fronttubebackindia*.35), slit_thickness, sectionheight*1.1],center=false) ; } } } // translate() } // if (buildtube == "true" ) } // difference() if (buildtube != "true" && buildplate == "true") { thewidth=10; translate([0,-thewidth/2,0.25]) makeruler(length = 50, width=thewidth, offset = 0, font_size = 4.7, orientation = "uprightx", print_length = "truex", print_text=labelcrossbar, the_text=str("Dia. ", reardiskdiameter, "mm")); } } // union() } // AKA slide carrier ((mostly)from DupliHood) module springpart() { union() { echo(str("Making Spring Holder for ", film, ".")); echo(str("frontdiskdiameter = ", frontdiskdiameter+td2dh_sp, "mm. ")); difference() { // outer rim cylinder(r=(frontdiskdiameter+td2dh_sp)/2+lip+lip, h=framez+spring+overlap); //cylinder(r=(frontdiskdiameter+td2dh_sp)/2, h=framez+spring+overlap); // slide holder translate([0, 0, framez+spring]) cylinder(r=(frontdiskdiameter+td2dh_sp)/2+lip, h=1000) ; // slide removal access, shifted up as of 20140824 translate([0, 500-(framey/2)+frametol/4, 0]) cube([framex+frametol, 1000, 1000], center=true) // Cuts the slits beside the slide carrier. ; } difference() { union() { // spring translate([0, 0, spring/2]) cube([framex, (framey+frametol)*1, spring], center=true); // bump on the spring, thicker as of 20140824 difference() { translate([0, filmy/7, -filmy*2+spring*3]) rotate([0, 90, 0]) if (debug == "true") { cylinder(r=filmy*1.923, h=framex, center=true, $fn=80); } else { cylinder(r=filmy*2, h=framex, center=true, $fn=180); } translate([0, 0, -500]) cube([1000, 1000, 1000], center=true) ; } } // clear film area + double tolerance as of 20140824 translate([0, 500-(filmy+frametol)/2-frametol, 0]) cube([filmx+2*frametol, 1000, 1000], center=true) ; } } } // module springpart() // Optional diffuser ((mostly)from DupliHood) module diffuser() { echo(str("Making optional diffuser for ", film, ".")); echo(str("frontdiskdiameter = ", (frontdiskdiameter+td2dh_df), "mm. ")); translate([0, 0, overlap*2+1]) mirror([0, 0, 1]) union() { difference() { // outer rim cylinder(r=(frontdiskdiameter+td2dh_df)/2+lip+lip+lip, h=overlap); cylinder(r=(frontdiskdiameter+td2dh_df)/2+lip+lip, h=1000, center=true); } translate([0, 0, overlap]) difference() { cylinder(r=(frontdiskdiameter+td2dh_df)/2+lip+lip+lip, h=overlap+1); cylinder(r1=(frontdiskdiameter+td2dh_df)/2+lip+lip-1, r2=frontdiskdiameter/2+lip+lip+lip-1, h=overlap); translate([0, 0, -500]) cube([1000, 1000, 1000], center=true); } } } // Makes threads. Called from reartube() (from DupliHood) module male_thread(P, D_maj, Tall, tol=0, step=mystep) { // male thread is also called external thread echo(str("Step is ", step, " in male_thread() (360/step > 5 == coarse).")); echo(str("numthreads is ", Tall, ".")); H=sqrt(3)/2*P; fudge=0.0001; //assign(H=sqrt(3)/2*P) //assign(fudge=0.0001) intersection() { translate([0, 0, Tall/2]) cube([2*D_maj, 2*D_maj, Tall], center=true); for(k=[-P:P:Tall+P]) translate([0, 0, k]) for(i=[0:step:360-step]) hull() for(j = [0,step]) { rotate([0,0,(i+j)]) translate([D_maj/2, 0, (i+j)/360*P]) rotate([90, 0, 0]) linear_extrude(height=0.1, center=true, convexity=10) polygon(points=[ [-H*2/8-P/8-tol, 0], [P/2-H*2/8-P/8-tol, P/2], [-H*2/8-P/8-tol, P], [-D_maj/2-fudge, P], [-D_maj/2-fudge, 0]], paths=[[0,1,2,3,4]], convexity=10); } } } print_part(); // From Thingiverse Customizer docs. You must use "part" and "print_part()" to work on Customizer. // Colors only affect F5 (CGS) preview mode; // These colors go away after F6 (CGAL) render mode module print_part() { min_length = length_of_both_tubes - sectionheight; max_length = length_of_both_tubes - 10; echo(str("length_of_both_tubes: ",length_of_both_tubes )); echo(str("Working length of both tubes is approx. ", min_length , "mm through ", max_length, "mm.")); if (part == "fronttube") { color ("gray") fronttube(sections=num_front_sections, buildplate=makeroundplate, buildtube=maketube); } else if (part == "reartube") { color ("gray") reartube(sections=num_rear_sections, buildplate=makeroundplate, buildtube=maketube); } else if (part == "springpart") { color("white") springpart(); } else if (part == "negativecarrier") { color("white") negativecarrier(); } else if (part == "negativecarriertop") { color("white") negativecarriertop(); } else if (part == "negativecarrierboth") { union() { color("white") negativecarrier(makerim="truex"); translate([0,0, diskthickness + railthickness*3.25]) color("green") rotate([0,180,0]) negativecarriertop() ; } } else if (part == "slidecarrier") { color("white") slidecarrier(); } else if (part == "xslidecarrier") { color("white") xslidecarrier(); } else if (part == "diffuser") { color ("white") diffuser(); } else if (part == "calslide") { color ("white") calslide(); //translate([0,0,5]) //calslide_2(); } else if (part == "thumbshell") { color("silver") thumbshell(); } else if (part == "ruler") { rulerwidth=10; color ("white") echo(str("Ruler is ", length_of_both_tubes-(sectionheight/2), " (length_of_both_tubes-(sectionheight/2))")); translate([0,-rulerwidth*0.5,0]) makeruler(length = length_of_both_tubes-(sectionheight/2), width=rulerwidth, offset = 0, orientation = "uprightx", print_length = "true"); //makeruler(length = 50, width=rulerwidth, offset = 0, orientation = "uprightx", print_length = "truex", print_text="true", the_text="Test text"); } else if (part == "playpen1") { color ("white") playpen1(); } else if (part == "playpen2") { color ("gray") playpen2(); } else if (part == "bothtubes" || part == "bothtube") { union() { color("blue") fronttube(sections=num_front_sections, buildplate=makeroundplate, buildtube=maketube); color("green") translate([0,0,num_front_sections * sectionheight-1]) reartube(sections=num_rear_sections, buildplate=makeroundplate, buildtube=maketube); } } else if (part == "exploded") { union() { color("white") diffuser(); translate([0, 0, (lip*1)+overlap*2+1]) union() { color("red") springpart(); translate([0, 0, lip*6]) color("blue") fronttube(sections=num_front_sections, buildplate=makeroundplate, buildtube=maketube); translate([0, 0, (num_front_sections*sectionheight)+ (sectionheight*.01)+lip*7]) color("green") reartube(sections=num_rear_sections, buildplate=makeroundplate, buildtube=maketube); color("white") translate([0, 0, (num_rear_sections+num_front_sections)*sectionheight+(sectionheight*.1)+lip*8]) // So you can look down through the tubes in "exploded", in debug mode if (debug != "true") { calslide(); } else { //calslide(); } ; } } } else { echo (str("Part '", part, "' is not defined.")) ; } } // Experimental or unfinished modules go here. // For negative carrier. // Move these to the top when they are done. diskthickness = 2.5; // Thickness of the negative carrier base plate platethickness = 3.5; // Thickness of Xtend-a-Slide round plate backpadding = 0.9; // Amount of (felt, etc.) padding on the top part of the neg carrier to provide some "give". rw=((film != "film6x6") ? 3. : 2.8); // 3.24mm = 1/8" railwidth=((is110 == "true" && film != "film6x6") ? 1.0 : rw); echo (str("film is ",film, " is110 is ", is110, " railwidth: ", railwidth, "mm")); railthickness=1.21; // Thickness of the rails that the sprocket hole area rides on //railthickness=3; enlarge=((film == "film6x6" || is110 == "true") ? 0 : 5 ); // Enlarge the film frame by this much (to see part // of the sprocket hole area.) Only for 35mm. negframex=((film != "film6x6") ? framex : 75); // The size of the top negative carrier negframey=((film != "film6x6") ? framey : 75); // 2*2 inches for 35mm; yet to be determined for 120 film. onetwentyfudge=((film != "film6x6") ? 0 : 2); // Fudge factor for 120 film make_neg_rails = "true"; // Make the negative support rails? (We might want to make them ourselves out of softer material.) makebaseplate = "true"; rimwall=4; negframetol=-0.4; negrailshrink=0.5; module negativecarriertop() { rotate([0,180,0]){ translate([0,0,-(railthickness+slidethickness)*.76]) { difference() { translate([0,0,railthickness*1.0]) // Make space for top negative carrier cube([negframex+negframetol, negframey+negframetol, slidethickness],center=true); cube([filmy+enlarge,filmx+enlarge, 50], center=true) // Cutout for image frame ; } if (make_neg_rails == "true") { // translate([(negwidth/2)-railwidth/2-negrailshrink,0,0]) // one rail //cube([railwidth, (frontdiskdiameter+td2dh_sp)*0.8,railthickness], center=true); makebeveledrail(railwidth, (frontdiskdiameter+td2dh_sp)*0.78,railthickness*1, up="upx"); translate([-(negwidth/2)+railwidth/2+negrailshrink,0,0]) // the other rail //cube([railwidth, (frontdiskdiameter+td2dh_sp)*0.7,railthickness], center=true); makebeveledrail(railwidth, (frontdiskdiameter+td2dh_sp)*0.78,railthickness*1, up="upx"); // Cover the top (bottom) of the part of the rails that are sticking out translate([-(negwidth/2-negrailshrink/2), (frontdiskdiameter+td2dh_sp)*0.293-1, railthickness/2]) cube([negwidth-negrailshrink,13,slidethickness]); translate([-(negwidth/2-negrailshrink/2), -(frontdiskdiameter+td2dh_sp)*0.293-11, railthickness/2]) cube([negwidth-negrailshrink,13,slidethickness]); } // if (make_neg_rails == "true") } } } // Create a rail with a bevel at each end (so film doesn't catch.) module makebeveledrail(wd=5, len=50, ht=1, rad=50, up="up") { ln = len-(rad*.2); // Needs work; it is kind of fragile and doesn't scale. ort=((up == "up") ? 0 : 180); // Should bevel be on top or bottom? shifter=((up == "up") ? 0 : -ht/2); // Flipping 180 degrees and compensating, depending. rotate([0,ort,0]) { // Flip 180 degrees (or not) translate([0,0,shifter]) { // Compensate for the 180 degree flip (or not) difference() { union() { translate([0,0,ht/2]) cube([wd,ln,ht],center=true); // Make the rail translate([-wd/2,(ln/2)-(wd/2),-rad+ht/1]) rotate([0,90,0]) // The bevels are created from a section of a cylinder cylinder(r=rad, h=wd, $fn=180); mirror([0,1,0]) // One cylinder for each end translate([-wd/2,(ln/2)-(wd/2),-rad+ht/1]) rotate([0,90,0]) cylinder(r=rad, h=wd, $fn=180); } translate([0,0,-200/2]) cube([200,200,200],center=true); // Whop off the large portion of the cylinders (leaving // just the bevels at the ends of the rail.) } } } } // negative carrier. module negativecarrier(makerim="true") { ourdiameter = frontdiskdiameter+td2dh_sp*4; rimheight=diskthickness*1.2 + railthickness*2 + platethickness +5 ; echo(str("Making negative carrier for ", film, ".")); echo(str("ourdiameter = ", ourdiameter, "mm, rim height:", rimheight, "mm")); echo(str("Negative width = ", negwidth, "mm.")); if (makeroundplate == "true") { intersection() { negativeplate() ; cylinder(d=ourdiameter,h=100,center=true) // Make the square negative carrier be a circle //cube([framex+20,framey+20,100], center=true); ; } // intersection() } if (makebaseplate == "true" && makerim == "true") { difference() { translate([0,0,(rimheight)/2]) // outside rim maketube(dia=ourdiameter, hgt=rimheight, wall=rimwall); translate([0,0,diskthickness*2.5+backpadding+(railthickness*2.09)]) // dig a channel to mount on the front plate cylinder(d=ourdiameter+rimwall*.39, h=platethickness, center=true); if (6 == 9) { // Unsupported slit (!) (Eventually replace with 45 degree triangle, maybe. Or an arch.) // (If needed for strength.) translate([0,0,diskthickness+railthickness*1.8]) // cut out slits in rim for negative cube([negwidth*1.0,frontdiskdiameter*1.5,(railthickness*2.49)],center=true); } else { // Cut it out all the way to the top of the rim (so nothing is unsupported.) translate([0,0,diskthickness+railthickness*(20/2) + railthickness]) // cut out slits in rim for negative cube([negwidth*1.0,frontdiskdiameter*1.5,(railthickness*20)],center=true); } } } } // negativecarrier() module negativeplate () { topplateheight = diskthickness + railthickness*2.09; difference() { // baseplate translate([0,0,(topplateheight + backpadding + slidethickness*1)/2]) cube([frontdiskdiameter*2.2, frontdiskdiameter*2.2, topplateheight + backpadding + slidethickness*1.0],center=true); cube([filmy+enlarge,filmx+enlarge, 100], center=true) // Cutout for image frame ; //translate([0,0,(diskthickness+topplateheight*4)/2+railthickness]) // Clear out space for the negative (and rails) translate([0,0,diskthickness+(topplateheight*2)]) // Clear out space for the negative (and rails) cube([negwidth,frontdiskdiameter*2,(topplateheight*4)],center=true) ; translate([0,0,topplateheight+slidethickness*4.89]) // Make space for top negative carrier cube([negframex+frametol, negframey+frametol, slidethickness*10],center=true) ; } // difference() (baseplate) if (make_neg_rails == "true") { translate([(negwidth/2)-railwidth/2+onetwentyfudge ,0,diskthickness-.01+railthickness/2]) // one rail cube([railwidth, (frontdiskdiameter+td2dh_sp)*1.0,railthickness], center=true) ; translate([-(negwidth/2)+railwidth/2-onetwentyfudge ,0,diskthickness-.01+railthickness/2]) // the other rail cube([railwidth, (frontdiskdiameter+td2dh_sp)*1.0,railthickness], center=true) ; } // if (make_neg_rails == "true") } // negativeplate() module maketube(dia = 50, hgt = 100, wall = 4, cnt=true) { difference() { cylinder(d = dia+wall, h = hgt, center=cnt); cylinder(d = dia, h = hgt*1.25, center=cnt); } } // Slide carrier for Xtend-a-Slide (35mm only) and TransiDupe tubes module slidecarrier () { echo(str("Making slide carrier for ", film, ".")); echo(str("frontdiskdiameter: ", frontdiskdiameter, " frontdiskdiameter (adjusted): ", frontdiskdiameter+td2dh_sp*4, ".")); echo(str("thickness is ", xfrontdiskheight, ".")); frametol=.5; if (makeroundplate == "true") { difference() { cylinder(d=frontdiskdiameter+td2dh_sp*4, h=xfrontdiskheight+slidethickness*1.1); // Bottom plate if (cutoutfilmarea == "true") { cube([filmy+1, filmx+1, xfrontdiskheight+20], center=true); // Cut out the film area } translate([xslidedia/2-xnotchdistance,0,-5]) { // Make pieces for finger notch cylinder(r=xslidefingernotchwidth/2, h=xfrontdiskheight+20); translate([0,-xslidefingernotchwidth/2,0]) cube([xslidefingernotchlength,xslidefingernotchwidth,xfrontdiskheight+20],center=false); } translate([-(framex+frametol)/2, -(framex+frametol)/2, xfrontdiskheight]) // Cut out area for slide cube([(framex+frametol)*2, framey+frametol, xfrontdiskheight*1.1], center=false) ; // Add bevels to the end of the slide cutout. translate([framex/2,framey/5,xfrontdiskheight]) rotate([0,0,18]) cube([(framex+frametol)*.5, (framey+frametol)*.3, xfrontdiskheight*2.5], center=false); translate([framex/2,-framey*.5,xfrontdiskheight]) rotate([0,0,-15]) cube([(framex+frametol)*.5, (framey+frametol)*.3, xfrontdiskheight*2.5], center=false); } } frontrimhgt=9; if (maketube == "true") { difference() { // Outside rim translate([0,0,frontrimhgt/2]) maketube(dia=frontdiskdiameter-0.4+td2dh_sp*4*0.9, hgt=frontrimhgt, wall=rimwall); translate([0,0,xfrontdiskheight+slidethickness+2.1]) // dig a channel to mount on the front plate cylinder(d=frontdiskdiameter+td2dh_sp*4+.5, h=frontdiskheight-.1, center=true) ; translate([-(framex+frametol)/2, -(framex+frametol+3.9)/2, xfrontdiskheight*1.0]) // Cut out area for slide cube([(framex+frametol)*2, framey+frametol+5, xfrontdiskheight*11.1], center=false); translate([xslidedia/2,0,xfrontdiskheight*.5]) cube([xslidefingernotchlength,xslidefingernotchwidth,xfrontdiskheight],center=true); } } } // slidecarrier () // Slide carrier for Xtend-a-Slide. // (You need to add a layer of Staples adhesive rubber magnet. // that you cut out yourself.) module xslidecarrier () { echo(str("Making slide carrier for ", film, ".")); echo(str("thickness is ", xfrontdiskheight, ".")); difference() { cylinder(r=xslidedia/2, h=xfrontdiskheight); // Bottom plate if (cutoutfilmarea == "true") { cube([filmy+1, filmx+1, xfrontdiskheight+20], center=true); // Cut out the film area } translate([xslidedia/2-13,0,-5]) { // Make pieces for finger notch cylinder(r=xslidefingernotchwidth/2, h=xfrontdiskheight+20); translate([0,-xslidefingernotchwidth/2,0]) cube([xslidefingernotchlength,xslidefingernotchwidth,xfrontdiskheight+20],center=false); } } translate([0,0,xfrontdiskheight*.9]) { // Need to lower it a bit to pervent (mesh) holes difference() { // h=xfrontdiskheight-1.7 sets height of top layer cylinder(r=xslidedia/2, h=xfrontdiskheight-1.9); // 2nd layer translate([-(framex+frametol)/2, -(framex+frametol)/2,0]) // Cut out area for slide cube([(framex+frametol)*2, framey+frametol, xfrontdiskheight*1.1], center=false); // Add bevels to the end of the slide cutout. translate([framex/2,framey/5,0]) rotate([0,0,15]) cube([(framex+frametol)*.5, (framey+frametol)*.3, xfrontdiskheight*2.5], center=false); translate([framex/2,-framey*.5,0]) rotate([0,0,-15]) cube([(framex+frametol)*.5, (framey+frametol)*.3, xfrontdiskheight*2.5], center=false); } } } // xslidecarrier () module playpen1() { // Playing with rim and torus torheight=3; rimdiameter = frontdiskdiameter; //rimdiameter = 67; torus_dia = .065; torus_dia = 1; outerskin = torus_dia*1.95; innerskin = torus_dia*1; wall = torus_dia*.2; // 0 1 2 3 4 5 6 7 points = [ [0,0],[outerskin,0],[outerskin,outerskin],[outerskin-wall,outerskin,],[outerskin-wall,wall],[wall,wall],[wall, outerskin],[0,outerskin] ]; paths= [[0,1,2,3,4,5,6,7]]; //polygon(points, paths, 10); if (6 == 9) { // Rim + torus union() { echo(str("Playpen1 ", film, ".")); echo(str("rimdiameter = ", rimdiameter, ". ")); //translate([0, 500-(framey/2)+frametol/4, 0]) //color("red") //cube([framex+frametol, framex+frametol, 1.5], center=true) difference() { // outer rim cylinder(r=rimdiameter/2+lip+lip, h=framez+spring+overlap); //cylinder(r=rimdiameter/2+lip+lip, h=55); //cylinder(r=rimdiameter/2, h=framez+spring+overlap); // slide holder translate([0, 0, 0]) cylinder(r=rimdiameter/2+lip, h=1000) ; // slide removal access, shifted up as of 20140824 translate([0, 500-(framey/2)+frametol/4, 0]) //color("red") //cube([framex+frametol, 1000, 1000], center=true) ; translate([0,0,torheight]){ //torus(our_circle="coarse", location="inner"); translate([0,0,torheight*1.5]) //#torus(dia = rimdiameter+2, our_circle="up-channel", location = "outer" ) ; } } // difference translate([0,0,torheight*3.2]){ #torus(dia = rimdiameter+1, width=6, our_circle="square", location = "outer" ); } } // Union } else { // Just torus. translate([0,0,torheight*2.2]) //torus(dia = rimdiameter+2,our_circle="square",location="outer") belttorus(dia = rimdiameter+2,our_circle="square",location="outer") ; } } // playpen1() // Still needs works. The scaling doesn't scale. But it would work if you only needed one size of torus // and didn't mind fiddling with the assorted scaling parameters here. module torus (dia =frontdiskdiameter+2, width=5, our_circle="circlex", location = "outer" ) { //div=.26; // .25 for inner bevel, .26 for outer div=((location == "outer") ? .2659 : .25 ); //div=.25; //echo(str("our_circle ", our_circle, " div ", div)); torus_dia = .065; torus_dia = width*.01; scale([dia*div,dia*div,dia*div]) { // Don't really know how this works. translate([0,0,-.20]) rotate_extrude(convexity = 10, $fn = 190) translate([2, 0, 0]) if (our_circle == "true" ) { circle(r = torus_dia, $fn = 90); } else if (our_circle == "coarse" ) { // Namely for fast rendering speed. circle(r = torus_dia, $fn = 20); } else if (our_circle == "up-channel" ) { // channel-shaped polygon outerskin = torus_dia*2.95; wall = torus_dia*.5; // Plot paths counter-clockwise around the polygon // 0 1 2 3 4 5 6 7 points = [ [0,0],[outerskin,0],[outerskin,outerskin],[outerskin-wall,outerskin,],[outerskin-wall,wall],[wall,wall],[wall, outerskin],[0,outerskin] ]; paths= [[0,1,2,3,4,5,6,7]]; rotate([0,0,270]) // Outward facing //rotate([0,0,90]) // Inward facing polygon(points, paths, 10); } else { square([torus_dia*1.25,torus_dia*1.25], $fn = 90); } } // scale() (!) } // module torus() // Trying to make something to put on the edge of a plate for a rim to snap onto. // Will be used both convex (plate edge) and concave (rim that snaps onto the plate) // Not really, yet. As fragile as a sand castle on a skateboard. module belttorus (dia =frontdiskdiameter+2, wid=5, steps=1, slope=2, $fn=60) { width = wid/1; translate([0,0,-width*2+2]) { difference() { union() { for (i = [0:steps:width-1]) { translate([0,0,width-i/slope]) cylinder(d=dia+i-width+1,h=(width-i)/slope); translate([0,0,(i-width/slope)+steps]) cylinder(d=dia+i-width+1,h=(width-i)/slope); } } translate([0,0,-width*2]) cylinder(d=dia-width, h=width*5); } } }