/* Copyright (c) 2013 Daniel M. Taub Copyright (c) 2010 Daniel K. Schneider This file is part of DobloFactory. DobloFactory is free source: you can redistribute it and/or modify modify it under the terms of the CC BY-NC-SA 3.0 License: CreativeCommons Attribution-NonCommerical-ShareAlike 3.0 as published by Creative Commons. You should have received a copy of the CC BY-NC-SA 3.0 License with this source package. If not, see */ // may change this include based on printer types include ; NUDGE = .001; // slight amount to make sure interfaces touch // -------------------------------- DOBLO and STL merging ------------------------------ // Import an STL and place it using the positioning system module merge_stl (file="stls/placeholder.stl", col=0, row=0, up=10,stl_z_offset_mm=-3, shrink=1, scale=LUGO) { // Move STOL right and forward - origin is x=leftmost and y=backmost x_offset_mm = col * PART_WIDTH(scale) + PART_WIDTH(scale) ; y_offset_mm = - (row * PART_WIDTH(scale) + PART_WIDTH(scale)) ; z_offset_mm = up * PART_HEIGHT(scale) + stl_z_offset_mm ; // the STL echo(str("shrink factor=", shrink)); translate([x_offset_mm, y_offset_mm, z_offset_mm]) { scale([1/shrink,1/shrink,1/shrink]) import(file); // scale(0.5) import(file); } } // ----------------------------------- DOBLO bricks making code ------------------------------- // all of these can be used in custom modules, i.e. they respect the grid model module doblo (col=0, row=0, up=0, width=4,length=2,height=FULL, nibbles_on_off=true,diamonds_on_off=false,scale=LUGO) /* Use cases: - typical Doblo block, use only once to create the first layer, e.g. a small or larger plate that can fit on top of another doblo or Duplo(TM) block. - See also "block", it doesn't have nibbles underneath and it should be used to build 3D structures */ { // build the cube from its center x_0 = col * PART_WIDTH(scale) + width * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length * PART_WIDTH(scale) / 2.0) ; z_0 = up * PART_HEIGHT(scale) + height * PART_HEIGHT(scale) / 2.0; // User info echo(str("DOBLO brick width(x)=", width * PART_WIDTH(scale), "mm, length=", length*PART_WIDTH(scale), "mm, height=", height*PART_HEIGHT(scale), "mm" )); // the cube is drawn at absolute x,y,z = 0 then moved translate ([x_0, y_0, z_0]) { //the doblo union () { if (diamonds_on_off && (width > 1 && length > 1)) { difference () { difference() { // the cube cube([width*PART_WIDTH(scale), length*PART_WIDTH(scale), height*PART_HEIGHT(scale)], true); // inner emptiness, a bit smaller and shifted down translate([0,0,-DOBLOWALL(scale)]) #cube([width*PART_WIDTH(scale) - 2*DOBLOWALL(scale), length*PART_WIDTH(scale)-2*DOBLOWALL(scale), height*PART_HEIGHT(scale)], true); } // diamonds diamonds (width, length, height, scale=scale); } } else { difference() { // the cube cube([width*PART_WIDTH(scale), length*PART_WIDTH(scale), height*PART_HEIGHT(scale)], true); // inner emptiness, a bit smaller and shifted down translate([0,0,-DOBLOWALL(scale)]) cube([width*PART_WIDTH(scale) - 2*DOBLOWALL(scale), length*PART_WIDTH(scale)-2*DOBLOWALL(scale), height*PART_HEIGHT(scale)], true); } } // nibbles on top if (nibbles_on_off) { // (col, row, up, width, length) nibbles (-width/2, -length/2, height/2, width, length, scale = scale); } // nibbles underneath - only if x or y is bigger than 1 if (width > 1 && length > 1) { // big nibbles underneath bottom_nibbles(width, length, height, scale = scale); // lattice (for low resolution printers - e.g. 0.35 layers - this is not needed) if (LATTICE_TYPE > 0 ) { bottom_lattice (width, length, height, scale = scale); } } else { // big nibbles underneath if (USE_INSET(scale)) bottom_nibbles_part (width, length, height, scale = scale); // lattice (for low resolution printers - e.g. 0.35 layers - this is not needed) /*if (LATTICE_TYPE > 0 ) { bottom_lattice (width, length, height, scale = scale); } */ } if ((scale < 0.6) && (!USE_INSET(scale))) { bottom_nibbles_thin (width, length, height,scale=scale); } //little walls inside (insets) if (USE_INSET(scale)) difference() { union() { for(j=[1:length]) translate([0,-PART_WIDTH(scale)*length/2+j*2*NO(scale)-2*NO(scale)/2,0]) cube([width*PART_WIDTH(scale), INSET_WIDTH(scale), height*PART_HEIGHT(scale)],true); for (i = [1:width]) translate([-PART_WIDTH(scale)*width/2+i*2*NO(scale)-2*NO(scale)/2,00]) cube([INSET_WIDTH(scale), length*PART_WIDTH(scale), height*PART_HEIGHT(scale)],true); } cube([width*PART_WIDTH(scale)-INSET_LENGTH(scale), length*PART_WIDTH(scale)-INSET_LENGTH(scale), height*PART_HEIGHT(scale)+2], true); } } } } module nibbles (col=0, row=0, up=FULL, width=1, length=1, scale=LUGO, extra =false, filled=false, hscale=1) /* Use cases: - needed by the doblo and the block modules - can also be stuck on top on parts of a nibble-less doblo or block */ { // Uses a local coordinate system left/back = 0,0 // E.g. nibbles (-2, -2, 0, 4, 4 ); // echo ("PART_WIDTH(scale)", PART_WIDTH(scale), "NO(scale)", NO(scale)); x_start = col * PART_WIDTH(scale) + NO(scale) ; // echo ("x_start", x_start, "col", col); y_start = - ( row * PART_WIDTH(scale) + NO(scale)); // echo ("y_start", y_start, "row", row); z_local = up * PART_HEIGHT(scale) + NH(scale) / 2; translate ([x_start , y_start, z_local]) { // 0,0 is left/back corner. Draw to the right (x) and forward (-y) for(j=[1:length]) { for (i = [1:width]) { translate([(i-1) * PART_WIDTH(scale), -(1) * (j-1) * PART_WIDTH(scale), 0]) doblonibble(scale = scale,extra=extra,filled=filled,heightscale=hscale); } } } } module diamonds (width, length, height) { x_start = -width/2 * PART_WIDTH(scale) + NBO(scale); y_start = -length/2 * PART_WIDTH(scale) + NBO(scale); z_pos = -height * PART_HEIGHT(scale)/2+DIAMOND; echo (str ("height = ", height, "z_pos= ", z_pos)); translate ([0, y_start, z_pos]) { // holes along y-axis for (i = [0:length-2]) { // echo (str ("diamond y offset=", i*PART_WIDTH(scale)+PART_WIDTH(scale))); translate([0, i* NBO(scale), 0]) rotate (a=45, v=[1,0,0]) { cube([width*PART_WIDTH(scale)+PART_WIDTH(scale),DIAMOND,DIAMOND],true); } } } translate ([x_start, 0, z_pos]) { // holes along x-axis for (j = [0:width-2]) { // echo (str ("diamond x offset=", j*PART_WIDTH(scale)-PART_WIDTH(scale))); translate([j * NBO(scale), 0, 0]) rotate (a=45, v=[0,1,0]) { cube([DIAMOND,length*PART_WIDTH(scale)+PART_WIDTH(scale),DIAMOND],true); } } } } // produces a wider spaced lattice (not used currently) module bottom_lattice_wide (width, length, height) { x_start = -width/2 * PART_WIDTH(scale) + NBO(scale); y_start = -length/2 * PART_WIDTH(scale) + NBO(scale); z_pos = height * PART_HEIGHT(scale)/2 - LATTICE_WIDTH(scale) - LATTICE_WIDTH(scale)/2; translate ([0, y_start, z_pos]) { // grid along y-axis for (i = [0:length-2]) { translate([0, i* NBO(scale), 0]) { cube([width*PART_WIDTH(scale), LATTICE_WIDTH(scale), LATTICE_WIDTH(scale)],true); } } } // grid along x-axis translate ([x_start, 0, z_pos]) { // holes along x-axis for (j = [0:width-2]) { translate([j * NBO(scale), 0, 0]) { cube([LATTICE_WIDTH(scale),length*PART_WIDTH(scale),LATTICE_WIDTH(scale)],true); } } } } module bottom_lattice (width, length, height) { spacing = NBO(scale)/LATTICE_TYPE; x_start = -width/2 * PART_WIDTH(scale) + spacing; y_start = -length/2 * PART_WIDTH(scale) + spacing; z_pos = height * PART_HEIGHT(scale)/2 - LATTICE_WIDTH(scale) - LATTICE_WIDTH(scale)/2; translate ([0, y_start, z_pos]) { // grid along y-axis for (i = [0:LATTICE_TYPE*length-2]) { translate([0, i* spacing, 0]) { cube([width*PART_WIDTH(scale), LATTICE_WIDTH(scale), LATTICE_WIDTH(scale)],true); } } } // grid along x-axis translate ([x_start, 0, z_pos]) { // holes along x-axis for (j = [0:LATTICE_TYPE*width-2]) { translate([j * spacing, 0, 0]) { cube([LATTICE_WIDTH(scale),length*PART_WIDTH(scale),LATTICE_WIDTH(scale)],true); } } } } module bottom_nibbles_thin (width, length, height, scale) /* Use cases: - needed by the doblo module */ { x_start = -width/2 * PART_WIDTH(scale) + NBO(scale); y_start = -length/2 * PART_WIDTH(scale) + NBO(scale); z_local = 0; translate ([x_start , y_start, z_local]) { if (width == 1){ for(j=[0:length-2]) { translate([-NBO(scale)/2, j * NBO(scale), 0]) doblobottomnibble_thin(height*PART_HEIGHT(scale),scale=scale); } } else if (length == 1){ for (i = [0:width-2]) { translate([i * NBO(scale), -NBO(scale)/2, 0]) doblobottomnibble_thin(height*PART_HEIGHT(scale),scale=scale); } } } } module bottom_nibbles_part (width, length, height, scale) /* Use cases: - needed by the doblo module for h or w == 1 */ { SUPPORT_HEIGHT = height * PART_HEIGHT(scale); x_start = -width/2 * PART_WIDTH(scale) + NBO(scale); y_start = -length/2 * PART_WIDTH(scale) + NBO(scale); z_local = 0; translate ([x_start , y_start, z_local]) { if(width == 1){ for(j=[0:length-1]){ translate([0-ALONG_LEN(scale)/4,j*2*NO(scale)-NO(scale),0]) cube([ALONG_LEN(scale)/2, INSET_WIDTH(scale), SUPPORT_HEIGHT],true); translate([-2*NO(scale)+ALONG_LEN(scale)/4,j*2*NO(scale)-NO(scale),0]) cube([ALONG_LEN(scale)/2, INSET_WIDTH(scale), SUPPORT_HEIGHT],true); if (j!= length-1) translate([-NO(scale),j*2*NO(scale),0]){ union(){ cube([INSET_WIDTH(scale), ALONG_LEN(scale), SUPPORT_HEIGHT],true); cube([CROSS_LEN(scale), INSET_WIDTH(scale), SUPPORT_HEIGHT],true); } } } } else if (length == 1) { for(i=[0:width-1]) { translate([i*2*NO(scale)-NO(scale),0-ALONG_LEN(scale)/4,0]) cube([INSET_WIDTH(scale), ALONG_LEN(scale)/2, SUPPORT_HEIGHT],true); translate([i*2*NO(scale)-NO(scale),-2*NO(scale)+ALONG_LEN(scale)/4,0]) cube([INSET_WIDTH(scale), ALONG_LEN(scale)/2, SUPPORT_HEIGHT],true); if (i != width-1) translate([i*2*NO(scale),-NO(scale),0]) union(){ cube([INSET_WIDTH(scale), CROSS_LEN(scale), SUPPORT_HEIGHT],true); cube([ALONG_LEN(scale), INSET_WIDTH(scale), SUPPORT_HEIGHT],true); } } } } } module bottom_nibbles (width, length, height, scale) /* Use cases: - needed by the doblo module - for an end-user module: see nibbles_bottom ! */ { x_start = -width/2 * PART_WIDTH(scale) + NBO(scale); y_start = -length/2 * PART_WIDTH(scale) + NBO(scale); z_local = 0; translate ([x_start , y_start, z_local]) { for(j=[0:length-2]) { for (i = [0:width-2]) { translate([i * NBO(scale), j * NBO(scale), 0]) doblobottomnibble(height*PART_HEIGHT(scale),scale=scale); } } } } // To be used by end users for sticking a bottom nible on an object module nibbles_bottom (col, row, up, width, length, height, scale=LUGO) { width_ = (width==1) ? 2 : width; length_ = (length==1) ? 2 : length; x_0 = col * PART_WIDTH(scale) + width_ * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length_ * PART_WIDTH(scale) / 2.0) ; z_0 = up * PART_HEIGHT(scale) + height * PART_HEIGHT(scale) / 2.0; translate ([x_0, y_0, z_0]) { bottom_nibbles (width_, length_, height, scale); } } // use by angle bricks module tri_prism(top,side,end) { translate([side/2,-end/2,-top/6-NUDGE])rotate([0,-90,0])linear_extrude(height=side) polygon(points=[[0,0],[top,end],[0,end]],paths=[[0,1,2]]); } // Made by DT module angle_doblo(col,row,up,width,length,width,height,nibbles_on_off,scale){ doblo(col,row,up,width,length,height,false,false,scale); angle_block(col,row,up+2,width,length,height,nibbles_on_off,scale); } // Added by DKS sept. 2015 - added a parameter for the height of the angle block // quite back hack module doblo_angle (col=0,row=0,up=0,width=2,length=4,height=FULL,a_height=FULL,nibbles_on_off=true,scale=LUGO){ # doblo (col,row,up,width,length,height,false,false,scale); if (scale==LUGO) { block_angle (col,row,height+height*0.6,width,length,a_height,nibbles_on_off,scale); } else { block_angle (col,row,4*height,width,length,a_height,nibbles_on_off,scale); } } // DKS, sept. 2015 - hacks by trial and error to make it sit on z=0 // just a dump hack, sort of ok with lego size, but not duplo module block_angle (col, row, up, width,length,height,nibbles_on_off, scale) { // build the cube from its center x_0 = col * PART_WIDTH(scale) + width * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length * PART_WIDTH(scale) / 2.0) ; z_0 = up + height/4; // found through trial and error :( / DKS top = PART_HEIGHT(scale)*height; side = PART_WIDTH(scale)*width; end = PART_WIDTH(scale)*length; ang = atan(top/end); hyp = top/sin(ang); off_y = (hyp-end)/2; off_z = -NUDGE ;//-top/6-NUDGE; oy = (hyp-end)/2; oz=-top/FULL-NUDGE; // the cube is drawn at absolute x,y,z = 0 then moved translate ([x_0, y_0, z_0]) { tri_prism(top,side,end); // nibbles on top if (nibbles_on_off) { // (col, row, up, width, length) translate([0,oy,oz])rotate([ang,0,0])translate([0,off_y,off_z])nibbles (-width/2, -length/2, height/2, width, length, scale=scale); } } } module angle_block (col, row, up, width,length,height,nibbles_on_off, scale) { // build the cube from its center x_0 = col * PART_WIDTH(scale) + width * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length * PART_WIDTH(scale) / 2.0) ; z_0 = up * PART_HEIGHT(scale) + height * PART_HEIGHT(scale) / 2.0; top = PART_HEIGHT(scale)*height; side = PART_WIDTH(scale)*width; end = PART_WIDTH(scale)*length; ang = atan(top/end); hyp = top/sin(ang); off_y = (hyp-end)/2; off_z = -NUDGE ;//-top/6-NUDGE; oy = (hyp-end)/2; oz=-top/FULL-NUDGE; // the cube is drawn at absolute x,y,z = 0 then moved translate ([x_0, y_0, z_0]) { tri_prism(top,side,end); // nibbles on top if (nibbles_on_off) { // (col, row, up, width, length) translate([0,oy,oz])rotate([ang,0,0])translate([0,off_y,off_z])nibbles (-width/2, -length/2, height/2, width, length, scale=scale); } } } module block (col=0, row=0, up=0, width=2,length=4,height=FULL,nibbles_on_off=false, scale=LUGO) /* Use cases: - building blocks for 3D structures (saves times and plastic, use a light fill in skeinforge) - movable blocks for games (printed apart) */ { // build the cube from its center x_0 = col * PART_WIDTH(scale) + width * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length * PART_WIDTH(scale) / 2.0) ; z_0 = up * PART_HEIGHT(scale) + height * PART_HEIGHT(scale) / 2.0; // the cube is drawn at absolute x,y,z = 0 then moved translate ([x_0, y_0, z_0]) { //the cube cube([width*PART_WIDTH(scale), length*PART_WIDTH(scale), height*PART_HEIGHT(scale)], true); // nibbles on top if (nibbles_on_off) { // (col, row, up, width, length) nibbles (-width/2, -length/2, height/2, width, length, scale=scale); } } } // Added a fix to shave off the bottom. But height is not correct, the roof (half of the length is not counted). // DKS sept. 2015 module house_lr (col, row, up, width, length, height, scale) /* Use cases: - create doors with openscad difference operator - a hack, only work along x axis and with a min. height and width */ { // build the cube from its center x_0 = col * PART_WIDTH(scale) + width * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length * PART_WIDTH(scale) / 2.0) ; z_0 = up * PART_HEIGHT(scale) + height * PART_HEIGHT(scale) / 2.0; roof_l = sqrt ( (length*PART_WIDTH(scale)*length*PART_WIDTH(scale)) + (length*PART_WIDTH(scale)*length*PART_WIDTH(scale) ) ) / 2; // the cube is drawn at absolute x,y,z = 0 then moved translate ([x_0, y_0, z_0]) { //the cube cube([width*PART_WIDTH(scale), length*PART_WIDTH(scale), height*PART_HEIGHT(scale)], true); //the "roof" translate ([0,0,height*PART_HEIGHT(scale)/2]) { difference () { rotate ([45,0,0]) { cube([width*PART_WIDTH(scale), roof_l, roof_l], true); } translate ([0,0,-4*height*PART_HEIGHT(scale)/2]) { cube([width*PART_WIDTH(scale)+2, length*PART_WIDTH(scale)+2, height*2*PART_HEIGHT(scale)], true); } } } } } module house_fb (col, row, up, width,length,height, scale) /* Use cases: - create doors with openscad difference operator - a hack, only work along y axis and with a min. height and length ... try ;) */ { // build the cube from its center x_0 = col * PART_WIDTH(scale) + width * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length * PART_WIDTH(scale) / 2.0) ; z_0 = up * PART_HEIGHT(scale) + height * PART_HEIGHT(scale) / 2.0; // That was 40 years ago roof_l = sqrt ( (width*PART_WIDTH(scale)*width*PART_WIDTH(scale)) + (width*PART_WIDTH(scale)*width*PART_WIDTH(scale) ) ) / 2; // the cube is drawn at absolute x,y,z = 0 then moved translate ([x_0, y_0, z_0]) { //the cube cube([width*PART_WIDTH(scale), length*PART_WIDTH(scale), height*PART_HEIGHT(scale)], true); translate ([0,0,height*PART_HEIGHT(scale)/2]) { rotate ([0,45,0]) { cube([roof_l, length*PART_WIDTH(scale), roof_l], true); } } } } module base_plate (col=0, row=0, up=0, width=8,length=8,height=THIRD,nibbles_on_off, scale=LUGO) /* Use cases: - Creating an easy to print base plate for showing off your prints. I believe that buying one in a shop is more efficient .... - to do: fix the lattice to match other small typical height, - an other version that has round holes allowing to stack it. */ { // construction of the grid underneath // spacing = (scale < 0.6) ? NBO(scale)/LATTICE_TYPE*2 : NBO(scale)/LATTICE_TYPE; spacing = (scale > 0.6) ? NBO(scale) : NBO(scale)*2; offset = NBO(scale); x_start = - width/2 * PART_WIDTH(scale) + NBO(scale); y_start = - length/2 * PART_WIDTH(scale) + NBO(scale); z_pos = (scale<0.6) ? height * PART_HEIGHT(scale)/2 - LATTICE_WIDTH(scale) * 2 : height * PART_HEIGHT(scale)/2 - LATTICE_WIDTH(scale) - LATTICE_WIDTH(scale) ; n_rows = (scale > 0.6) ? length-2 : (length-2)/2 ; // Need less for legos n_cols = (scale > 0.6) ? width-2 : (width-2)/2; // positioning of the grid with respect to the cube x_0 = col * PART_WIDTH(scale) + width * PART_WIDTH(scale) / 2.0; y_0 = - (row * PART_WIDTH(scale) + length * PART_WIDTH(scale) / 2.0) ; z_0 = up * PART_HEIGHT(scale) ; difference () { block (col, row, up, width,length,height,nibbles_on_off, scale) ; union () { translate ([x_0, y_0, z_0]) { translate ([0, y_start, z_pos]) { // grid along y-axis for (i = [0:n_rows]) { translate([0, i* spacing, 0]) { cube([width*PART_WIDTH(scale)-offset*2, LATTICE_WIDTH(scale), LATTICE_WIDTH(scale)],true); } } } // grid along x-axis translate ([x_start, 0, z_pos]) { // holes along x-axis for (j = [0:n_cols]) { translate([j * spacing, 0, 0]) { cube([LATTICE_WIDTH(scale),length*PART_WIDTH(scale)-offset*2,LATTICE_WIDTH(scale)],true); } } } } } } } // ------ Cylinder block module cyl_block (col=0, row=0, up=0, bottom_r=2, top_r=2, height=FULL, nibbles_on_off=true, scale=LUGO) { bottom_r_mm = bottom_r * NO(scale); top_r_mm = top_r * NO(scale); x_0 = col * PART_WIDTH(scale) + bottom_r_mm; y_0 = - (row * PART_WIDTH(scale) + bottom_r_mm); z_0 = up * PART_HEIGHT(scale); // the cylinder is drawn at absolute x,y,z = 0 then moved translate ([x_0, y_0, z_0]) { cylinder(h= height*PART_HEIGHT(scale), r1 = bottom_r_mm, r2 = top_r_mm, center = false, $fs = 0.2); if (nibbles_on_off) { // (col, row, up, width, length) // circle is a bit different from cube nibbles (-top_r/2+0.5, -top_r/2+0.5, height, top_r-1, top_r-1, scale); } } } // -------------- support block (triangle ) // Support block for building platforms // so bad ... :( // NOTE: make the support block stick into something, else it won't print module support (col=0,row=0,up=0,height=FULL,angle=0,thickness=1, scale=LUGO) { if (angle == 0) { translate ([0, 0, 0]) { support1 (col,row,up,height,angle,thickness, scale); } } if (angle == 90) { translate ([0, -PART_WIDTH(scale) , 0]) { support1 (col,row,up,height,angle,thickness, scale); } } if (angle == 180) { translate ([PART_WIDTH(scale), -PART_WIDTH(scale) - PART_WIDTH(scale)* (thickness-1) , 0]) { support1 (col,row,up,height,angle,thickness, scale); } } if (angle == 270) { translate ([PART_WIDTH(scale) + PART_WIDTH(scale) * (thickness-1), 0 , 0]) { support1 (col,row,up,height,angle,thickness, scale); } } } module support1 (col,row,up,height,angle,thickness, scale) { height_mm = height * PART_HEIGHT(scale) ; length_mm = height * PART_WIDTH(scale) / 4; width_mm = PART_WIDTH(scale)*thickness; x_0 = col * PART_WIDTH(scale); y_0 = - (row * PART_WIDTH(scale)); z_0 = up * PART_HEIGHT(scale); translate ([x_0, y_0 , z_0]) { rotate (a=angle, v=[0,0,1]) { polyhedron ( points = [[0, -width_mm, height_mm], [0, 0, height_mm], [0, 0, 0], [0, -width_mm, 0], [length_mm, -width_mm, height_mm], [length_mm, 0, height_mm]], faces = [[0,3,2], [0,2,1], [3,0,4], [1,2,5], [0,5,4], [0,1,5], [5,2,4], [4,2,3], ]); } } } // --------------------- ramp module ramp (col=0,row=0,up=0,height=FULL,angle=0,width=1,scale=LUGO) { width_mm = width * NO (scale); if (angle == 0) { translate ([0, -width_mm, 0]) { ramp1 (col,row,up,height,angle,width,scale); } } if (angle == 90) { translate ([width_mm, 0 , 0]) { ramp1 (col,row,up,height,angle,width,scale); } } if (angle == 180) { translate ([(width*width_mm)-2*width_mm, -width_mm , 0]) { ramp1 (col,row,up,height,angle,width,scale); } } if (angle == 270) { translate ([width_mm, 0 , 0]) { ramp1 (col,row,up,height,angle,width,scale); } } } module ramp1 (col=0,row=0,up=0,height=HALF,angle=0,width=1,scale=LUGO) { height_mm = height * PART_HEIGHT(scale); length_mm = height * PART_WIDTH(scale); width_mm = width * NO (scale); x_0 = col * PART_WIDTH(scale); y_0 = - (row * PART_WIDTH(scale)); z_0 = up * PART_HEIGHT(scale); translate ([x_0, y_0 , z_0]) { rotate (a=angle, v=[0,0,1]) { polyhedron ( points = [[0, -width_mm, height_mm], [0, width_mm, height_mm], [0, width_mm, 0], [0, -width_mm, 0], [length_mm, -width_mm, 0], [length_mm, width_mm, 0]], faces = [[0,3,2], [0,2,1], [3,0,4], [1,2,5], [0,5,4], [0,1,5], [5,2,4], [4,2,3], ]); } } } // ------------------------------- Text module module write (text="hello", col=0, row=0, up=0, rot=0, height=1, size=8, valign="top", halign="left", font="Liberation Sans,style=bold", scale=LUGO) { x_0 = col * PART_WIDTH(scale); y_0 = - (row * PART_WIDTH(scale)); z_0 = up * PART_HEIGHT(scale); translate ([x_0, y_0, z_0]) { rotate (rot) { linear_extrude (height=height) { text (text, font = font, size=size, valign=valign, halign=halign); } } } } // ---------------------------------- Auxiliary modules --------------------- module doblonibble(extra=false,filled=false,heightscale=1) { // Lego size does not have holes in the nibbles nb_r = NB_RADIUS(scale) + (extra ? DOBLOWALL(scale)/2.2 : 0); nb_r_i = NB_RADIUS_INSIDE(scale) + (extra ? DOBLOWALL(scale)/2.2 : 0); if (scale < 0.6 || filled) { cylinder(r=nb_r, h=heightscale*NH(scale), center=true, $fs = 0.2); } else { difference() { cylinder(r=nb_r, h=heightscale*NH(scale), center=true, $fs = 0.2); cylinder(r=nb_r_i,h=heightscale*NH(scale)+1,center=true, $fs = 0.2); } } } module doblobottomnibble(height_mm) { difference() { cylinder(r=NB_BOTTOM_RADIUS(scale), h=height_mm, center=true,$fs = 0.2); cylinder(r=NB_BOTTOM_RADIUS_INSIDE(scale), h=height_mm+1,center=true,$fs = 0.2); } } module doblobottomnibble_thin(height_mm) { cylinder(r=NB_BOTTOM_RADIUS_THIN(scale), h=height_mm, center=true,$fs = 0.2); //cylinder(r=NB_BOTTOM_RADIUS_INSIDE(scale)/4, h=height_mm+1,center=true,$fs = 0.2); } /* Hackey--should build shell separately and then insert bottom nibbles currently, has some holes. Even CW is duplo compatible, odd is offset .5 */ module doblo_curve_down(col,row,up,width,length,height,nibbles,size,cw=1){ difference(){ block_curve_down(col,row,up,width,length,height,nibbles,size,cw=cw); translate([0,0,-0.01])difference(){ block(col-cw/2,row,up,width+cw,length,height*2/3,false,size); doblo(col-cw/2,row,up,width+cw,length,height*2/3,false,false,size); } } } module doblo_curve_up(col,row,up,width,length,height,nibbles,size,cw=2,bottom_holes=true){ difference(){ union(){ translate([-0.01,0,0])curve(col+width,row,up,cw,length,height,false,true,size); translate([0.01,0,0])curve(col-1,row,up,cw,length,height,true,true,size); doblo(col,row,up,width,length,height,nibbles,false,size); if(nibbles && cw>1){ nibbles(col-floor(cw/2),row,up+height,floor(cw/2),length,size); nibbles(col+width,row,up+height,cw/2,length,size); } } if (bottom_holes){ nibbles(col+width,row,up,ceil(cw),length,DOBLO,filled=true,hscale=1.5); nibbles(col-ceil(cw),row,up,ceil(cw),length,DOBLO,filled=true,hscale=1.5); } } } module block_curve_down(col,row,up,width,length,height,nibbles,size,cw=1){ //intersection(){ union(){ curve(col-1,row,up,cw,length,height,blockType=size); block(col,row,up,width,length,height,nibbles,size); curve(col+width,row,up,cw,length,height,blockType=size,xflip=false); } // doblo(col,row,up,width,length,height,nibbles,false,size); // } } module curve(x=0,y=0,z=0,w=1,h=1,d=FULL,xflip=true,yflip=false,blockType=DOBLO){ bwidth = DOBLOWIDTH(blockType)/blockType; bheight = PART_HEIGHT(blockType); offset = xflip ? -bwidth/2 : 0; boxoffset = xflip ? (1-w)*bwidth/2 : 0; zoff = yflip ? bheight*d : 0; translate([(x/2)*bwidth-offset,(-y/2)*bwidth,(z)*bheight]) intersection(){ translate([0,0,zoff])rotate([90,0,0])scale([1,bheight*d*2/(bwidth*w/2) ,1])cylinder(h=bwidth/2*h,r=w*bwidth/4); translate([boxoffset+offset,-bwidth/2*h-.05,0])cube([(w)*bwidth/2,h*bwidth/2+.1,bheight*d]); } }