/******** * Loxodrome Desk Lamp * * An adaptation by Marcio Teixeira of: * * Loxodrome, by kitwallace, published Feb 11, 2014 * * http://www.thingiverse.com/thing:246965 * * License: * Creative Commons - Attribution - Share Alike * * http://creativecommons.org/licenses/by-sa/3.0/ * * Includes code by Johannes Reinhardt * ********/ /* [Global] */ // Select what to see. part = "shell"; // [assembly, loxodrome, shell] /**** Loxodrome, by kitwallace (original source) ****/ // loxodrome // uses open tube code // orginal endless tube code by nop head // requires openscad development snapshot 2014-01-14 e // http://www.openscad.org/downloads.html // with concat enabled in edit/preferences/features function m_translate(v) = [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [v.x, v.y, v.z, 1 ] ]; function m_rotate(v) = [ [1, 0, 0, 0], [0, cos(v.x), sin(v.x), 0], [0, -sin(v.x), cos(v.x), 0], [0, 0, 0, 1] ] * [ [ cos(v.y), 0, -sin(v.y), 0], [0, 1, 0, 0], [ sin(v.y), 0, cos(v.y), 0], [0, 0, 0, 1] ] * [ [ cos(v.z), sin(v.z), 0, 0], [-sin(v.z), cos(v.z), 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ]; function vec3(v) = [v.x, v.y, v.z]; function transform(v, m) = vec3([v.x, v.y, v.z, 1] * m); function orientate(p0, p) = m_rotate([0, atan2(sqrt(pow(p[0], 2) + pow(p[1], 2)), p[2]), 0]) * m_rotate([0, 0, atan2(p[1], p[0])]) * m_translate(p0); function circle_points(r = 1, a = 0) = a < 360 ? concat([[r * sin(a), r * cos(a),0]], circle_points(r, a + 360 / $fn)) : [] ; function loop_points(step, end , t = 0) = t <= end ? concat([f(t)], loop_points(step, end, t + step)) : [] ; function transform_points(list, matrix, i = 0) = i < len(list) ? concat([ transform(list[i], matrix) ], transform_points(list, matrix, i + 1)) : []; function tube_points(loop_points, section_points, i=0) = i < len(loop_points)-1 ? concat( transform_points( section_points, orientate(loop_points[i],(loop_points[i + 1]- loop_points[i])/2)), tube_points(loop_points,section_points, i + 1)) : [] ; function tube_faces(facets, s, i = 0) = i < facets ? concat([[s * facets + i, s * facets + (i + 1) % facets, (s + 1) * facets + (i + 1) % facets, (s + 1) * facets + i] ], tube_faces(facets, s, i + 1)) : []; function tube_end(facets, s, i = 0) = i < facets ? concat( [s * facets + i], tube_end(facets, s, i+1)) : []; function loop_faces(segs,facets, j = 0) = j < segs ? concat(tube_faces(facets, j), loop_faces(segs, facets, j + 1)) : []; function loop_all_faces(segs,facets) = concat ( [reverse(tube_end(facets,0))], // reverse direction for this face loop_faces(segs,facets), [tube_end(facets,segs)]); function reverse_r(v,n) = n == 0 ? [v[0]] : concat([v[n]],reverse_r(v,n-1)) ; function reverse(v) = reverse_r(v, len(v)-1); e = 2.718281828; pi = 3.14159; rad = 2 * pi / 360; function sinh(x) = (1 - pow(e, -2 * x)) / (2 * pow(e, -x)); function cosh(x) = (1 + pow(e, -2 * x)) / (2 * pow(e, -x)); function tanh(x) = sinh(x) / cosh(x); function cot(x) = 1 / tan(x); /* for ( x = [ -e: 0.1: e]) echo (x, sinh(x), cosh(x), tanh(x)); */ function m(beta,long,long0) = cot(beta) * ( long - long0) * rad; function lox (t,beta,long0) = [ cos(t) / cosh(m(beta,t,long0)), sin(t) / cosh(m(beta,t,long0)), tanh(m(beta,t,long0)) ]; function f(t) = [0,r *sin(t), r * cos(t) ]; function loop_points(step, end, t = 0, beta,long0, r ) = t <= end ? concat(r*[lox(t,beta,long0)], loop_points(step, end, t + step, beta,long0, r )) : [] ; step=10; r=50; thickness=3.0; long0 = 0; // integrity of polyhedron is sensitive to values of $fn section_points = circle_points(thickness, $fn=20); // echo(section_points); module loxodrome(beta=70) { translate([0,0,r + thickness]) for (a= [0:90:270]) { rotate([0,0,a]) { loop_points = loop_points(5, 500, -500, beta ,long0, r); // echo(loop_points); tube_points = tube_points(loop_points,section_points); // echo(tube_points); faces = loop_all_faces(len(loop_points)-2, len(section_points)); // echo(faces); polyhedron(points = tube_points, faces = faces); } } } /******** * End of Loxodrome, by kitwallace ********/ /* The following code was added by MLT to adapt the Loxodrome * into a freestanding lamp */ /* Variables */ $fn = 40; /****** LOXODROME SCONCE *******/ rim_hole_dia = 18; rim_wall_thickness = thickness*2.5; rim_thickness = thickness*2.5; rim_wall_thickness = thickness*2.5; top_dia = rim_hole_dia + rim_wall_thickness; top_thickness = 6; top_z = 2.5; /* A cap for the top of the loxodrome */ module loxodrome_top() { translate([0,0,r*2+top_z]) difference() { cylinder (d = top_dia, h = top_thickness, center=true); translate([0,0,-0.2]) cylinder (d1 = top_dia, d2 = 0, h = top_thickness, center=true); } } /* A cap for the bottom of the loxodrome */ module loxodrome_rim() { translate([0,0,rim_thickness/2]) cylinder (d = rim_hole_dia + rim_wall_thickness, h = rim_thickness, center=true); } /* On opening cut into the bottom of the Loxodrome. This allows a supporting * shaft to enter the Loxodrome from below. */ module loxodrome_thru_hole() { translate([0,0,rim_thickness/2]) cylinder (d = rim_hole_dia, h = rim_thickness + 1, center=true); } /* A modified version of kitwallace's Loxodrome. We add a pivot point on * top and a hole at the base, that allows the Loxodrome to freely spin * on the tip of a pen or a suitable support structure. */ module loxodrome_sconce() { difference() { union() { loxodrome_top(); loxodrome(75); loxodrome_rim(); } loxodrome_thru_hole(); } } /****** MOTORISED LOXODROME BASE ASSEMBLY *******/ module cross_section_xz() { intersection() { children(); translate([0,5000,0]) cube([10000,10000,10000], center=true); } } $fn = 40; /***** End of definition *****/ /***** Definition for top assembly *****/ side_wall_thickness = 4; top_assm_top_dia = rim_hole_dia + rim_wall_thickness + 15; top_assm_dia = rim_hole_dia + rim_wall_thickness + side_wall_thickness*2; bottom_wall = 2; top_assembly_h = 12; total_h = top_assembly_h + bottom_wall; module top_assembly() { translate([0,0,total_h/2]) cylinder( d2 = top_assm_dia, d1 = top_assm_dia, h = total_h, center=true ); } /***** End of definition *****/ /**** Definition: stand *****/ function easing(x) = 1 - 0.2 * pow((1-x), 25) - 0.8 * pow((1-x), 2); module vase_with_easing(d1, d2, thickness = 1, h, steps = 10, hollow = true) { r1 = d1/2; r2 = d2 / 2; x_scale = r2 - r1; y_scale = h; points = concat( [ for (y = [0:1/steps:1]) [easing(y) * x_scale + r1 - thickness, y * y_scale ] ], hollow ? [ for (y = [1:-1/steps:0]) [easing(y) * x_scale + r1 , y * y_scale ] ] : [[0, y_scale], [0, 0]] ); rotate_extrude(convexity = 10) polygon(points); } loxodrome_stand_h = 120; loxodrome_stand_d = 90; loxodrome_stand_opening_d = 60; stand_screw_dia = 2.4; module stand() { difference() { vase_with_easing( loxodrome_stand_d, top_assm_top_dia, thickness = 2, h = loxodrome_stand_h, steps = 10, hollow = true ); cutout() top_assembly(); } } module stand_screw() { translate([0, 0, 7]) rotate([90,0,0]) cylinder(d = stand_screw_dia, h = top_assm_top_dia*2, center=true); } base_screw_dist_from_center = 39; base_screw_dia = 3.2; base_screw_length = 3; module base_panel_screws() { for(a=[0,120,240]) rotate([0,0,a]) translate([base_screw_dist_from_center,0,base_screw_length/2-0.1]) cylinder(d = base_screw_dia, h = base_screw_length, center=true); } /***** End of definition *****/ module cutout(clearance=0.6) { minkowski() { children(); cube(size=[clearance,clearance,clearance],center=true); } } module stand_rim() { outer_d = loxodrome_stand_d-4; inner_d = loxodrome_stand_opening_d+10; linear_extrude(2) difference() { circle(d=outer_d); circle(d=inner_d); } // Pegs for zip typing stuff //for(a=[0:360/12:360]) // rotate([0,0,a+15]) // translate([inner_d/2,0,0]) // cylinder(d=2.5,h=11); } module stand_assembly() { difference() { translate([0,0,-loxodrome_stand_h+14]) { difference() { union() { stand(); stand_rim(); } base_panel_screws(); } } cutout() top_assembly(); stand_screw(); } //stand_screw(); } module assembly() { cross_section_xz() stand_assembly(); translate([0,0,20]) loxodrome_sconce(); } if (part == "assembly") { assembly(); } else if (part == "loxodrome") { loxodrome_sconce(); } else if (part == "shell") { stand_assembly(); }