/* TransiDupe Duplicate tansmissive film. (We are running out of names for slide duplicators.) 1/10/2016 Version .9.007 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 26mm) // "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=3; // [1:9] number_of_front_sections=3; // [1:9] // Set reardiskdiameter to the diameter of the step-up ring that is mounted on your lens. // (Or maybe slightly smaller.) reardiskdiameter=67; // [50:127] // Which part do you want to make? part = "exploded"; // [fronttube:The front tube,reartube:The tube that attaches to the lens, springpart:The spring cap that holds the slide, diffuser:The optional diffuser,exploded:Exploded view of all three parts (for viewing, not printing),calslide:Blank slide for calibration,ruler:Ruler that shows how far the slide should be, for the designated lengths of the tubes,negativecarrier:Negative carrier,playpen1:For experimenting,xslidecarrier:Xtend-a-Slide slide carrier] // What is the slide format? film = "film6x6x"; // [film6x6:Mounted 6x6cm slides,film35:Mounted 35mm (135 format) slides] /* [Hidden] */ /* 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. */ // Debugging. "true" makes some parts thinner/shorter for making test prints // Anything other than "true" == false; "truex" == false // i.e., debug="truex" to make normal sized prints. debug="truex"; num_slits = 10; // Number of slits in the front tube slit_thickness = 2; // 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 the round plates on the tubes makeroundplate = "true"; // Make the tubes maketube = "true"; makethreads = "truex"; // Make threads on back plate of rear tube. (Slow...) numthreads=3; // Number of threads. Was 3 in original design // What is the filter thread pitch? 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? thread_tolerance = 100; // [0:500] // Thread rotation angle (to align slide mask with the camera)? thread_rotate = 45; // [0:359] // 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") ? 8 : 23.5 ); 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. filmx=((film == "film6x6") ? 60 : 36); filmy=((film == "film6x6") ? 60 : 24); framex=((film == "film6x6") ? 70 : 50); framey=((film == "film6x6") ? 70 : 50); negwidth=((film == "film6x6") ? 60.2 : 35.2); framez=((debug == "true") ? 0 : 3.25); frametol=1; lip=2; diag=sqrt(((framex+frametol)*(framex+frametol))+((framey+frametol)*(framey+frametol))); innertall=80; //overlap=5; overlap=((debug == "true") ? 1.8 : 5); spring=1; // Added by Wayne // 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=((finishquality == "fine") ? 5 : 30); $fn=((finishquality == "fine") ? 90 : 50); tol=0.25; // Xtend-a-Slideish dimensions // Front tube frontdiskdiameter=((film == "film6x6") ? 110 : 87); td2dh=1.0025; // Compensator to translate between TransiDupe's notion of the front disk's diameter to // DupliHood's notion frontdiskheight=((debug == "true") ? 1 : 3.65 ); fronttubebackindia=50; fronttubefrontindia=framex; fronttubethickness=3.33; 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=49.5; reartubethickness=2.35; // Xtend-a-Slide carrier // length of notch to fish the slide out of the carrier with your finger xslidefingernotchlength=31; xslidefingernotchwidth=22.5; // diameter of slide carrier xslidedia=88; //xfrontdiskheight=((debug == "true") ? 1 : 2.7); xfrontdiskheight=2.7; slidethickness=1.12; //calc length of front tube // 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, ", sectionheight_h: ", sectionheight_h)); 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().")); echo(str("Tall 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); } } } // Calibration slide module calslide () { echo(str("Making calibration slide for ", film, ".")); echo(str("Slide Thickness = ", slidethickness, "mm. ")); difference() { cube([framex+frametol, framey+frametol, slidethickness], center=true); //translate([framex-filmx, framey-filmx, 0]) { cube([filmx,filmy, slidethickness*2], center=true) ////cylinder(h=slidethickness*2,r=slidethickness,center=true) ; //} for (i=[1:8]) { translate ([(framex/10*i)- framex/2.25, (framey/2)-(filmy/2)+1 , 0]) { cube([slidethickness*2, slidethickness/3, slidethickness*2], center=true); cube([slidethickness/3, slidethickness*1, slidethickness*2], center=true); //cylinder(h=slidethickness*2,r=slidethickness,center=true); } } for (i=[1:8]) { translate ([(framex/10*i)- framex/2.25, -((framey/2)- (filmy/2)+1), 0]) { cube([slidethickness*2, slidethickness/3, slidethickness*3], center=true); cube([slidethickness/3, slidethickness*1, slidethickness*2], center=true); //cylinder(h=slidethickness*2,r=slidethickness,center=true); } } } } // 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", print_length = "truex") { 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.5) { text(text = str(length, "mm"), size=5); } } } } // 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.")); //echo(str(sections, " sections, sectionheight_h ", sectionheight_h, "mm, total length_h: ",sections*sectionheight_h, "mm.")); // (Maybe) need Pythagorean Theorem here (for film6x6) // Used for calculating fronttube's dimensions shortsection=(film == "film6x6") ? (sections*sectionheight) : (sections*sectionheight); start_straighttube_at=shortsection-(sectionheight*.7); straighttubeheight = (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, ".")); // Start with adding an outer rim on the front disk difference() { cylinder(r=(frontdiskdiameter/2)+(lip*.999), h=frontrimheight); cylinder(r=((frontdiskdiameter-fronttubethickness)/2)+(lip*.999), 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(r=(frontdiskdiameter/2)+(lip*.999), h=frontdiskheight); } // Then the tube. Solid, at this point if (buildtube == "true" ) { // cylinder(r1=(fronttubefrontindia+fronttubethickness)/2, r2=(fronttubebackindia+fronttubethickness)/2-(fronttubethickness*(fudgeif1/5)), h=start_straighttube_at + fudgeif1); // shortsection-3.9 } } // union() // Now difference() kicks in and eliminates these: //translate([0, 0, (sections*sectionheight)/2+0]) //translate([0, 0, (shortsection)/2+0]) translate([0, 0, -2]) // Eliminate the inner tube cylinder(r1=fronttubefrontindia/2, r2=fronttubebackindia/2, h=start_straighttube_at+4); // make slits by rotating a skinny rectangle around the tube (which are eliminated with "difference()") } // difference() // For debugging. Uncomment to verify tube length calculations (ruler should == tube height) // Normally is commented out! //makeruler(offset=frontdiskdiameter/2 + 4,length=start_straighttube_at+straighttubeheight-1,orientation = "upright"); 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,+2]); cylinder(r=(fronttubebackindia)/2, h=straighttubeheight+4); } } if (slits == "true") { 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=thread_diameter/10; dia=reardiskdiameter+.4; mag=((dia<49) ? 0.5 : 1); pitch=thread_pitch/100.0; ctol=thread_tolerance/1000.0; threadheight = numthreads * (pitch*.0002); echo(str("Making rear tube for ", film, ".")); //echo(str(sections, " sections.")); echo(str(sections, " sections, sectionheight ", sectionheight, "mm, total length: ",sections*sectionheight, "mm.")); difference() { // Temporarily change to "union()" to see what is subtracted union() { if (buildplate == "true" ) { if (makethreads != "true") { cylinder(r=(reardiskdiameter/2)+(lip*.0), h=reardiskheight); } else { translate([0,0,(threadheight)]) male_thread(pitch, dia-ctol, numthreads, 0.25); } } if (buildtube == "true" ) { //translate([0, 0, reardiskheight]) cylinder(r=reartubeoutdia/2, h=sections*sectionheight); } } // Eliminate the inner tube. translate([0, 0, (sections*sectionheight)/2]) cylinder(r=(reartubeoutdia-reartubethickness)/2, h=(sections*sectionheight)+20,center=true); // How high should the slits start? translate([0, 0, (sections*sectionheight-sectionheight*.75) + (reardiskheight+1)]) { if (slits == "true") { for (i=[1:360/num_slits:360]) { rotate([0,0,i]) cube([fronttubebackindia-(fronttubebackindia*.35), slit_thickness, sectionheight*1.1],center=false); } } } } } // AKA slide carrier module springpart() { union() { echo(str("Making Spring Holder for ", film, ".")); echo(str("frontdiskdiameter = ", frontdiskdiameter*td2dh, "mm. ")); difference() { // outer rim cylinder(r=(frontdiskdiameter*td2dh)/2+lip+lip, h=framez+spring+overlap); //cylinder(r=(frontdiskdiameter*td2dh)/2, h=framez+spring+overlap); // slide holder translate([0, 0, framez+spring]) cylinder(r=(frontdiskdiameter*td2dh)/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) ; } 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.94, h=framex*1, center=true, $fn=180); } 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() module diffuser() { echo(str("Making optional diffuser for ", film, ".")); echo(str("frontdiskdiameter = ", (frontdiskdiameter*td2dh), "mm. ")); translate([0, 0, overlap*2+1]) mirror([0, 0, 1]) union() { difference() { // outer rim cylinder(r=(frontdiskdiameter*td2dh)/2+lip+lip+lip, h=overlap); cylinder(r=(frontdiskdiameter*td2dh)/2+lip+lip, h=1000, center=true); } translate([0, 0, overlap]) difference() { cylinder(r=(frontdiskdiameter*td2dh)/2+lip+lip+lip, h=overlap+1); cylinder(r1=(frontdiskdiameter*td2dh)/2+lip+lip-1, r2=frontdiskdiameter/2+lip+lip+lip-1, h=overlap); translate([0, 0, -500]) cube([1000, 1000, 1000], center=true); } } } print_part(); // 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 == "xslidecarrier") { color("white") xslidecarrier(); } else if (part == "diffuser") { color ("white") diffuser(); } else if (part == "calslide") { color ("white") calslide(); } else if (part == "ruler") { rulerwidth=15; 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"); } else if (part == "playpen1") { color ("white") playpen1(); } else if (part == "bothtubes") { 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 { // ; } } // Experimental or unfinished modules go here. module playpen1() { // Playing with rim and torus torheight=3; if (6 == 9) { // Rim + torus union() { echo(str("Playpen1 ", film, ".")); echo(str("frontdiskdiameter = ", frontdiskdiameter, ". ")); //translate([0, 500-(framey/2)+frametol/4, 0]) //color("red") //cube([framex+frametol, framex+frametol, 1.5], center=true) difference() { // outer rim cylinder(r=frontdiskdiameter/2+lip+lip, h=framez+spring+overlap); //cylinder(r=frontdiskdiameter/2, h=framez+spring+overlap); // slide holder translate([0, 0, 0]) cylinder(r=frontdiskdiameter/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(circle="truex"); } } } } else { // Just torus. translate([0,0,torheight]) torus(circle="truex"); } } // playpen1() module torus (dia =frontdiskdiameter+2, circle="truex" ) { div=.25; torus_dia = .07; scale([dia*div,dia*div,dia*div]) { rotate_extrude(convexity = 5, $fn = 90) translate([2, 0, 0]) if (circle == "true" ) { circle(r = torus_dia, $fn = 90); } else { square([torus_dia*1.25,torus_dia*1.25], $fn = 100); } } } // module torus() // Unfinished. Does not work now. module negativecarrier() { union() { echo(str("Making negative carrier for ", film, ".")); echo(str("frontdiskdiameter = ", frontdiskdiameter*td2dh, "mm. ")); echo(str("Negative width = ", negwidth, "mm.")); enlarge=((film == "film6x6") ? 1 : 4 ); if (6 == 6) { union() { //translate([(frontdiskdiameter*td2dh)- (negwidth*.45),0,3]) translate([((frontdiskdiameter*td2dh)*.5) + ((negwidth*.5)),0,3]) //translate([(frontdiskdiameter*td2dh*1)- (filmy*1.2),0,3]) cube([(frontdiskdiameter*td2dh) , (frontdiskdiameter*td2dh),2], center=true) ; mirror() { translate([(negwidth),0,3]) //cube([(frontdiskdiameter*td2dh) , (frontdiskdiameter*td2dh),2], center=true) ; } } } if (6 == 6) { difference() { // difference() // outer rim cylinder(r=(frontdiskdiameter*td2dh)/2+lip+lip, h=framez+spring+overlap); //cylinder(r=(frontdiskdiameter*td2dh)/2, h=framez+spring+overlap); // slide holder //translate([0, 0, framez+spring]) translate([0, 0, 1.5]) cylinder(r=(frontdiskdiameter*td2dh)/2+lip, h=1000) ; //rotate([0,90,0]) cube([filmy+enlarge,filmx+enlarge, 2000], center=true) // 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) ; } } } } // negativecarrier() // 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 ()