diff --git a/core.scad b/core.scad index afe9726..c97ae0c 100644 --- a/core.scad +++ b/core.scad @@ -25,3 +25,15 @@ include // Global functions and modules // use + +module use_stl(name) { //! Import an STL to make a build platter + stl(name); + + import(str("../stls/", name, ".stl")); +} + +module use_dxf(name) { //! Import a DXF to make a build panel + dxf(name); + + import(str("../dxfs/", name, ".dxf")); +} diff --git a/lib.scad b/lib.scad index 50f8c39..80ff86a 100644 --- a/lib.scad +++ b/lib.scad @@ -61,6 +61,7 @@ include include include include +include use use diff --git a/libtest.png b/libtest.png index ddbeec6..e7dac46 100644 Binary files a/libtest.png and b/libtest.png differ diff --git a/libtest.scad b/libtest.scad index 5605e20..7de27e2 100644 --- a/libtest.scad +++ b/libtest.scad @@ -34,6 +34,7 @@ use use use use +use use use use @@ -300,6 +301,9 @@ steppers_y = batteries_y + 70; translate([x3, veroboard_y]) veroboard_test(); +translate([x3 + 70, veroboard_y + 30]) + geared_steppers(); + translate([x3, d_connectors_y]) d_connectors(); diff --git a/printed/butt_box.scad b/printed/butt_box.scad index 6a5b475..6880fa9 100644 --- a/printed/butt_box.scad +++ b/printed/butt_box.scad @@ -27,6 +27,10 @@ //! A list specifies the internal dimensions, screw type, top, bottom and side sheet types and the block //! maximum spacing. //! +//! * An optional name can be specified to allow more then one box in a project. +//! * An optional list of fixing blocks to be omitted can be given. +//! * Star washers can be omitted by setting the 11th parameter to false. +//! //! Uses [fixing blocks](#fixing_block) and [corner blocks](#corner_block). // @@ -34,17 +38,20 @@ use use use <../utils/maths.scad> -function bbox_screw(type) = type[0]; //! Screw type for corner blocks -function bbox_sheets(type) = type[1]; //! Sheet type for the sides -function bbox_base_sheet(type)= type[2]; //! Sheet type for the base -function bbox_top_sheet(type) = type[3]; //! Sheet type for the top -function bbox_span(type) = type[4]; //! Maximum span between fixing blocks -function bbox_width(type) = type[5]; //! Internal width -function bbox_depth(type) = type[6]; //! Internal depth -function bbox_height(type) = type[7]; //! Internal height +function bbox_screw(type) = type[0]; //! Screw type for corner blocks +function bbox_sheets(type) = type[1]; //! Sheet type for the sides +function bbox_base_sheet(type) = type[2]; //! Sheet type for the base +function bbox_top_sheet(type) = type[3]; //! Sheet type for the top +function bbox_span(type) = type[4]; //! Maximum span between fixing blocks +function bbox_width(type) = type[5]; //! Internal width +function bbox_depth(type) = type[6]; //! Internal depth +function bbox_height(type) = type[7]; //! Internal height +function bbox_name(type) = type[8] ? type[8] : "bbox"; //! Optional name if there is more than one box in a project +function bbox_skip_blocks(type)= type[9] ? type[9] : []; //! List of fixing blocks to skip, used to allow a hinged panel for example +function star_washers(type) = type[10] ? type[10] : is_undef(type[10]); //! Set to false to remove star washers. module bbox_shelf_blank(type) { //! 2D template for a shelf - dxf("bbox_shelf"); + dxf(str(bbox_name(type), "_shelf")); sheet_2D(bbox_sheets(type), bbox_width(type), bbox_depth(type), 1); } @@ -60,18 +67,8 @@ function corner_block_positions(type) = let( y = [-1,-1,1,1][corner] ) translate([x * (width / 2), y * (depth / 2), z * height / 2]) * rotate([z > 0 ? 180 : 0, 0, corner * 90 + (z > 0 ? 90 : 0)]) - ]; -module corner_block_positions(type) { - bt = sheet_thickness(bbox_base_sheet(type)); - tt = sheet_thickness(bbox_top_sheet(type)); - for(p = corner_block_positions(type)) - let($thickness = transform([0, 0, 0], p).z > 0 ? tt : bt) - multmatrix(p) - children(); -} - function corner_holes(type) = [for(p = corner_block_positions(type), q = corner_block_holes(bbox_screw(type))) p * q]; function fixing_block_positions(type) = let( @@ -84,36 +81,28 @@ function fixing_block_positions(type) = let( dspans = floor(depth / span), dspan = depth / (dspans + 1), hspans = floor(height / span), - hspan = height / (hspans + 1) + hspan = height / (hspans + 1), + skips = bbox_skip_blocks(type) ) [ for(i = [0 : 1 : wspans - 1], y = [-1, 1], z = [-1, 1]) - translate([(i - (wspans - 1) / 2) * wspan, y * depth / 2, z * height / 2]) * - rotate([0, z * 90 + 90, y * 90 + 90]), + if(!in(skips, [0, y, z])) + translate([(i - (wspans - 1) / 2) * wspan, y * depth / 2, z * height / 2]) * + rotate([0, z * 90 + 90, y * 90 + 90]), for(i = [0 : 1 : dspans - 1], x = [-1, 1], z = [-1, 1]) - translate([x * width / 2, (i - (dspans - 1) / 2) * dspan, z * height / 2]) * - rotate([0, z * 90 + 90, x * 90]), + if(!in(skips, [x, 0, z])) + translate([x * width / 2, (i - (dspans - 1) / 2) * dspan, z * height / 2]) * + rotate([0, z * 90 + 90, x * 90]), for(i = [0 : 1 : hspans - 1], x = [-1, 1], y = [-1, 1]) - translate([x * width / 2, y * depth / 2, (i - (hspans - 1) / 2) * hspan]) * - rotate([y > 0 ? 180 : 0, x * y * 90, 0]), - + if(!in(skips, [x, y, 0])) + translate([x * width / 2, y * depth / 2, (i - (hspans - 1) / 2) * hspan]) * + rotate([y > 0 ? 180 : 0, x * y * 90, 0]), ]; function side_holes(type) = [for(p = fixing_block_positions(type), q = fixing_block_holes(bbox_screw(type))) p * q]; -module fixing_block_positions(type) { - t = sheet_thickness(bbox_sheets(type)); - bt = sheet_thickness(bbox_base_sheet(type)); - tt = sheet_thickness(bbox_top_sheet(type)); - h = bbox_height(type) / 2 - 1; - for(p = fixing_block_positions(type)) - let(z = transform([0, 0, 0], p).z, $thickness = z > h ? tt : z < -h ? bt : t) - multmatrix(p) - children(); -} - module drill_holes(type, t) for(list = [corner_holes(type), side_holes(type)], p = list) let(q = t * p) @@ -122,7 +111,7 @@ module drill_holes(type, t) drill(screw_clearance_radius(bbox_screw(type)), 0); module bbox_base_blank(type) { //! 2D template for the base - dxf("bbox_base"); + dxf(str(bbox_name(type), "_base")); difference() { sheet_2D(bbox_base_sheet(type), bbox_width(type), bbox_depth(type), 1); @@ -132,7 +121,7 @@ module bbox_base_blank(type) { //! 2D template for the base } module bbox_top_blank(type) { //! 2D template for the top - dxf("bbox_top"); + dxf(str(bbox_name(type), "_top")); t = sheet_thickness(bbox_sheets(type)); @@ -144,36 +133,40 @@ module bbox_top_blank(type) { //! 2D template for the top } } -module bbox_left_blank(type) { //! 2D template for the left side - dxf("bbox_left"); +function subst_sheet(type, sheet) = + let(s = bbox_sheets(type)) + sheet ? assert(sheet_thickness(sheet) == sheet_thickness(s)) sheet : s; + +module bbox_left_blank(type, sheet = false) { //! 2D template for the left side + dxf(str(bbox_name(type), "_left")); t = sheet_thickness(bbox_sheets(type)); bb = sheet_thickness(bbox_base_sheet(type)); difference() { translate([-t / 2, -bb / 2]) - sheet_2D(bbox_sheets(type), bbox_depth(type) + t, bbox_height(type) + bb); + sheet_2D(subst_sheet(type, sheet), bbox_depth(type) + t, bbox_height(type) + bb); drill_holes(type, rotate([0, 90, 90]) * translate([bbox_width(type) / 2, 0])); } } -module bbox_right_blank(type) { //! 2D template for the right side - dxf("bbox_right"); +module bbox_right_blank(type, sheet = false) { //! 2D template for the right side + dxf(str(bbox_name(type), "_right")); t = sheet_thickness(bbox_sheets(type)); bb = sheet_thickness(bbox_base_sheet(type)); difference() { translate([t / 2, -bb / 2]) - sheet_2D(bbox_sheets(type), bbox_depth(type) + t, bbox_height(type) + bb); + sheet_2D(subst_sheet(type, sheet), bbox_depth(type) + t, bbox_height(type) + bb); - drill_holes(type, rotate([0, -90, 90]) * translate([-bbox_width(type) / 2, 0])); + drill_holes(type, rotate([0, 90, 90]) * translate([-bbox_width(type) / 2, 0])); } } -module bbox_front_blank(type) { //! 2D template for the front - dxf("bbox_front"); +module bbox_front_blank(type, sheet = false) { //! 2D template for the front + dxf(str(bbox_name(type), "_front")); t = sheet_thickness(bbox_sheets(type)); bb = sheet_thickness(bbox_base_sheet(type)); @@ -181,23 +174,23 @@ module bbox_front_blank(type) { //! 2D template for the front difference() { translate([0, (bt - bb) / 2]) - sheet_2D(bbox_sheets(type), bbox_width(type) + 2 * t, bbox_height(type) + bb + bt); + sheet_2D(subst_sheet(type, sheet), bbox_width(type) + 2 * t, bbox_height(type) + bb + bt); drill_holes(type, rotate([-90, 0, 0]) * translate([0, bbox_depth(type) / 2])); } } -module bbox_back_blank(type) { //! 2D template for the back - dxf("bbox_back"); +module bbox_back_blank(type, sheet = false) { //! 2D template for the back + dxf(str(bbox_name(type), "_back")); bb = sheet_thickness(bbox_base_sheet(type)); t = sheet_thickness(bbox_sheets(type)); difference() { translate([0, -bb / 2]) - sheet_2D(bbox_sheets(type), bbox_width(type), bbox_height(type) + bb); + sheet_2D(subst_sheet(type, sheet), bbox_width(type), bbox_height(type) + bb); - drill_holes(type, rotate([90, 0, 0]) * translate([0, -bbox_depth(type) / 2])); + drill_holes(type, rotate([-90, 0, 0]) * translate([0, -bbox_depth(type) / 2])); } } @@ -208,8 +201,7 @@ module bbox_front(type) render_2D_sheet(bbox_sheets(type)) bbox_front_blank(type module bbox_left(type) render_2D_sheet(bbox_sheets(type)) bbox_left_blank(type); //! Default left side, can be overridden to customise module bbox_right(type) render_2D_sheet(bbox_sheets(type)) bbox_right_blank(type); //! Default right side, can be overridden to customise -module _bbox_assembly(type, top = true, base = true, left = true, right = true, back = true, front = true) //! The box assembly, wrap with a local copy without parameters -assembly("bbox") { +module _bbox_assembly(type, top = true, base = true, left = true, right = true, back = true, front = true) { //! The box assembly, wrap with a local copy without parameters width = bbox_width(type); depth = bbox_depth(type); height = bbox_height(type); @@ -219,43 +211,54 @@ assembly("bbox") { bt = sheet_thickness(bbox_base_sheet(type)); tt = sheet_thickness(bbox_top_sheet(type)); - corner_block_positions(type) - fastened_corner_block_assembly(t, bbox_screw(type), $thickness); + function is_missing_screw(p) = p.y > depth / 2 - 1 ? !back : false; - fixing_block_positions(type) - fastened_fixing_block_assembly(t, bbox_screw(type), thickness2 = $thickness); + assembly(bbox_name(type)) { - for(x = [-1, 1]) - translate([x * (width / 2 + t / 2 + eps + 25 * exploded()), 0]) - rotate([90, 0, x * 90]) - if(x > 0) { - if(right) - bbox_right(type); + for(p = corner_block_positions(type)) + let(q = transform([0, 0, 0], p), thickness = q.z > 0 ? tt : bt) + multmatrix(p) + fastened_corner_block_assembly(is_missing_screw(q) && ((q.z > 0) != (q.x > 0)) ? 0 : t, bbox_screw(type), thickness, + is_missing_screw(q) && ((q.z > 0) == (q.x > 0)) ? 0 : t, star_washers = star_washers(type)); + + h = height / 2 - 1; + for(p = fixing_block_positions(type)) + let(q = transform([0, 0, 0], p), thickness = q.z > h ? tt : q.z < -h ? bt : t) + multmatrix(p) + fastened_fixing_block_assembly(is_missing_screw(q) ? 0 : t, bbox_screw(type), thickness2 = thickness, star_washers = star_washers(type)); + + for(x = [-1, 1]) + translate([x * (width / 2 + t / 2 + eps + 25 * exploded()), 0]) + rotate([90, 0, x * 90]) + if(x > 0) { + if(right) + bbox_right(type); + } + else + if(left) + bbox_left(type); + + for(y = [1, -1]) + translate([0, y * (depth / 2 + t / 2 + eps + 25 * exploded())]) + rotate([90, 0, y * 90 + 90]) + if(y < 0) { + if(front) + bbox_front(type); + } + else + if(back) + bbox_back(type); + + for(z = [-1, 1]) { + sheet_thickness = z > 0 ? tt : bt; + translate_z(z * (height / 2 + sheet_thickness / 2 + eps + 100 * exploded())) + if(z > 0) { + if(top) + bbox_top(type); } else - if(left) - bbox_left(type); - - for(y = [-1, 1]) - translate([0, y * (depth / 2 + t / 2 + eps + 25 * exploded())]) - rotate([90, 0, y * 90 + 90]) - if(y < 0) { - if(front) - bbox_front(type); - } - else - if(back) - bbox_back(type); - - for(z = [-1, 1]) { - sheet_thickness = z > 0 ? tt : bt; - translate_z(z * (height / 2 + sheet_thickness / 2 + eps + 100 * exploded())) - if(z > 0) { - if(top) - bbox_top(type); - } - else - if(base) - bbox_base(type); + if(base) + bbox_base(type); + } } } diff --git a/printed/corner_block.scad b/printed/corner_block.scad index 396666e..b9d20c1 100644 --- a/printed/corner_block.scad +++ b/printed/corner_block.scad @@ -24,6 +24,11 @@ //! See [butt_box](#Butt_box) for an example of usage. //! //! Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly. +//! +//! Specific fasteners can be omitted by setting a side's thickness to 0 and the block omitted by setting ```show_block``` to false. +//! This allows the block and one set of fasteners to be on one assembly and the other fasteners on the mating assemblies. +//! +//! Star washers can be omitted by setting ```star_washers``` to false. // include <../core.scad> include <../vitamins/screws.scad> @@ -57,8 +62,8 @@ module corner_block_v_hole(screw = def_screw) //! Place children at the bottom s multmatrix(corner_block_v_hole(screw)) children(); -module corner_block_h_holes(screw = def_screw) //! Place children at the side screw holes - for(p = corner_block_h_holes(screw)) +module corner_block_h_holes(screw = def_screw, index = undef) //! Place children at the side screw holes + for(p = !is_undef(index) ? [corner_block_h_holes(screw)[index]] : corner_block_h_holes(screw)) multmatrix(p) children(); @@ -126,22 +131,33 @@ assembly(str("corner_block_M", 20 * screw_radius(screw))) { insert(insert); } -module fastened_corner_block_assembly(thickness, screw = def_screw, thickness_below = undef, name = false) { //! Printed block with all fasteners +module fastened_corner_block_assembly(thickness, screw = def_screw, thickness_below = undef, thickness_side2 = undef, name = false, show_block = true, star_washers = true) { //! Printed block with all fasteners + thickness2 = !is_undef(thickness_below) ? thickness_below : thickness; + thickness3 = !is_undef(thickness_side2) ? thickness_side2 : thickness; washer = screw_washer(screw); insert = screw_insert(screw); - screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + insert_length(insert) + overshoot); + function screw_length(t) = screw_shorter_than((star_washers ? 2 : 1) * washer_thickness(washer) + t + insert_length(insert) + overshoot); + screw_length = screw_length(thickness); + screw_length2 = screw_length(thickness2); + screw_length3 = screw_length(thickness3); - corner_block_assembly(screw, name) children(); + if(show_block) + corner_block_assembly(screw, name) children(); - corner_block_h_holes(screw) - translate_z(thickness) - screw_and_washer(screw, screw_length, true); + if(thickness) + corner_block_h_holes(screw, 0) + translate_z(thickness) + screw_and_washer(screw, screw_length, star_washers); - thickness2 = thickness_below ? thickness_below : thickness; - screw_length2 = screw_shorter_than(2 * washer_thickness(washer) + thickness2 + insert_length(insert) + overshoot); - corner_block_v_hole(screw) - translate_z(thickness2) - screw_and_washer(screw, screw_length2, true); + if(thickness3) + corner_block_h_holes(screw, 1) + translate_z(thickness3) + screw_and_washer(screw, screw_length3, star_washers); + + if(thickness2) + corner_block_v_hole(screw) + translate_z(thickness2) + screw_and_washer(screw, screw_length2, star_washers); } module corner_block_M20_stl() corner_block(M2_cap_screw); diff --git a/printed/fixing_block.scad b/printed/fixing_block.scad index 2e36007..1524280 100644 --- a/printed/fixing_block.scad +++ b/printed/fixing_block.scad @@ -24,6 +24,11 @@ //! See [butt_box](#Butt_box) for an example of usage. //! //! Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly. +//! +//! Specific fasteners can be omitted by setting a side's thickness to 0 and the block omitted by setting ```show_block``` to false. +//! This allows the block and one set of fasteners to be on one assembly and the other fasteners on the mating assemblies. +//! +//! Star washers can be omitted by setting ```star_washers``` to false. // include <../core.scad> include <../vitamins/screws.scad> @@ -116,20 +121,24 @@ assembly(str("fixing_block_M", 20 * screw_radius(screw))) { insert(insert); } -module fastened_fixing_block_assembly(thickness, screw = def_screw, screw2 = undef, thickness2 = undef) { //! Assembly with fasteners in place +module fastened_fixing_block_assembly(thickness, screw = def_screw, screw2 = undef, thickness2 = undef, show_block = true, star_washers = true) { //! Assembly with fasteners in place module fb_screw(screw, thickness) { washer = screw_washer(screw); insert = screw_insert(screw); - screw_length = screw_longer_than(2 * washer_thickness(washer) + thickness + insert_length(insert)); + screw_length = screw_longer_than((star_washers ? 2 : 1) * washer_thickness(washer) + thickness + insert_length(insert)); - translate_z(thickness) - screw_and_washer(screw, screw_length, true); + if(thickness) + translate_z(thickness) + screw_and_washer(screw, screw_length, star_washers); } - no_pose() fixing_block_assembly(screw); + if(show_block) + no_pose() + fixing_block_assembly(screw); + t2 = !is_undef(thickness2) ? thickness2 : thickness; fixing_block_v_holes(screw) - fb_screw(screw, thickness2 ? thickness2 : thickness); + fb_screw(screw, t2); fixing_block_h_hole(screw) fb_screw(screw2 ? screw2 : screw, thickness); diff --git a/printed/foot.scad b/printed/foot.scad index 5097f67..94bc0a6 100644 --- a/printed/foot.scad +++ b/printed/foot.scad @@ -66,7 +66,7 @@ module foot(type = foot) { //! Generate STL } } -module foot_assembly(t = 0, type = foot) { //! Assembly with fasteners in place for specified sheet thickness +module foot_assembly(t = 0, type = foot, flip = false) { //! Assembly with fasteners in place for specified sheet thickness screw = foot_screw(type); washer = screw_washer(screw); nut = screw_nut(screw); @@ -79,11 +79,17 @@ module foot_assembly(t = 0, type = foot) { //! Assembly with fasteners in place if(t) explode(15, true) translate_z(foot_thickness(type)) - screw_and_washer(screw, screw_length); + if(flip) + nut_and_washer(nut, true); + else + screw_and_washer(screw, screw_length); } if(t) translate_z(t) - nut_and_washer(nut, true); + if(flip) + screw_and_washer(screw, screw_length); + else + nut_and_washer(nut, true); } module insert_foot(type = insert_foot) { //! Generate STL for foot with insert diff --git a/readme.md b/readme.md index 126d663..1c4909d 100644 --- a/readme.md +++ b/readme.md @@ -18,33 +18,34 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa ## Table of Contents - - + + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
Vitamins A-N Vitamins O-Z Printed Utilities Core Utilities
Ball_bearings Opengrab Box Annotation Bom
Batteries O_ring Butt_box Bezier Clip
Ball_bearings O_ring Box Annotation Bom
Batteries Opengrab Butt_box Bezier Clip
Belts Pcbs Cable_grommets Dogbones Global
Blowers Pillars Carriers Fillet Polyholes
Bulldogs Pin_headers Corner_block Hanging_hole Rounded_rectangle
Buttons Psus Door_hinge Layout Sphere
Cable_strips Pulleys Door_latch Maths Teardrops
Components Rails Fan_guard Offset
Displays Ring_terminals Fixing_block Quadrant
D_connectors Rockers Flat_hinge Round
D_connectors Ring_terminals Fixing_block Quadrant
Displays Rockers Flat_hinge Round
Fans Rod Foot Rounded_cylinder
Fuseholder Screws Handle Rounded_polygon
Green_terminals Sealing_strip Psu_shroud Sector
Hot_ends Sheets Ribbon_clamp Sweep
Iecs Spades Screw_knob Tube
Inserts Spools Socket_box
Jack Springs Ssr_shroud
Ldrs Ssrs Strap_handle
Leadnuts Stepper_motors
Leds Toggles
Light_strips Transformers
Linear_bearings Tubings
Mains_sockets Variacs
Meter Veroboard
Microswitches Washers
Microview Wire
Modules Zipties
Geared_steppers Sealing_strip Psu_shroud Sector
Green_terminals Sheets Ribbon_clamp Sweep
Hot_ends Spades Screw_knob Tube
Iecs Spools Socket_box
Inserts Springs Ssr_shroud
Jack Ssrs Strap_handle
Ldrs Stepper_motors
Leadnuts Toggles
Leds Transformers
Light_strips Tubings
Linear_bearings Variacs
Mains_sockets Veroboard
Meter Washers
Microswitches Wire
Microview Zipties
Modules
Nuts
@@ -476,55 +477,6 @@ Various electronic components used in hot ends and heated beds. | 1 | ```resistor(Honewell)``` | Thermistor Honeywell 135-104LAC-J01 100K 1% | -
Top - ---- - -## Displays -LCD dispays. - - -[vitamins/displays.scad](vitamins/displays.scad) Object definitions. - -[vitamins/display.scad](vitamins/display.scad) Implementation. - -[tests/displays.scad](tests/displays.scad) Code for this example. - -### Properties -| Function | Description | -|:--- |:--- | -| ```display_aperture(type)``` | Size of the aperture including its depth | -| ```display_height(type)``` | Depth of the metal part | -| ```display_pcb(type)``` | PCB mounted on the back | -| ```display_pcb_offset(type)``` | 3D offset of the PCB centre | -| ```display_ribbon(type)``` | Keep out region for ribbon cable | -| ```display_thickness(type)``` | Height of the metal part | -| ```display_threads(type)``` | Length that studs protrude from the PCB holes | -| ```display_touch_screen(type)``` | Touch screen position and size | -| ```display_width(type)``` | Width of the metal part | - -### Functions -| Function | Description | -|:--- |:--- | -| ```display_depth(type)``` | Total thickness including touch screen and PCB | -| ```display_ts_thickness(type)``` | Touch screen thickness or 0 | - -### Modules -| Module | Description | -|:--- |:--- | -| ```display(type)``` | Draw specified display | -| ```display_aperture(type, clearance, clear_pcb = false)``` | Make aperture cutout | - -![displays](tests/png/displays.png) - -### Vitamins -| Qty | Module call | BOM entry | -| ---:|:--- |:---| -| 1 | ```display(HDMI5)``` | HDMI display 5" | -| 1 | ```display(LCD1602A)``` | LCD display 1602A | -| 1 | ```display(SSD1963_4p3)``` | LCD display SSD1963 4.3" | - - Top --- @@ -581,6 +533,55 @@ D-connectors. Can be any number of ways, male or female, solder buckets, PCB mou | 6 | ```d_pillar()``` | D-type connector pillar | +Top + +--- + +## Displays +LCD dispays. + + +[vitamins/displays.scad](vitamins/displays.scad) Object definitions. + +[vitamins/display.scad](vitamins/display.scad) Implementation. + +[tests/displays.scad](tests/displays.scad) Code for this example. + +### Properties +| Function | Description | +|:--- |:--- | +| ```display_aperture(type)``` | Size of the aperture including its depth | +| ```display_height(type)``` | Depth of the metal part | +| ```display_pcb(type)``` | PCB mounted on the back | +| ```display_pcb_offset(type)``` | 3D offset of the PCB centre | +| ```display_ribbon(type)``` | Keep out region for ribbon cable | +| ```display_thickness(type)``` | Height of the metal part | +| ```display_threads(type)``` | Length that studs protrude from the PCB holes | +| ```display_touch_screen(type)``` | Touch screen position and size | +| ```display_width(type)``` | Width of the metal part | + +### Functions +| Function | Description | +|:--- |:--- | +| ```display_depth(type)``` | Total thickness including touch screen and PCB | +| ```display_ts_thickness(type)``` | Touch screen thickness or 0 | + +### Modules +| Module | Description | +|:--- |:--- | +| ```display(type)``` | Draw specified display | +| ```display_aperture(type, clearance, clear_pcb = false)``` | Make aperture cutout | + +![displays](tests/png/displays.png) + +### Vitamins +| Qty | Module call | BOM entry | +| ---:|:--- |:---| +| 1 | ```display(HDMI5)``` | HDMI display 5" | +| 1 | ```display(LCD1602A)``` | LCD display 1602A | +| 1 | ```display(SSD1963_4p3)``` | LCD display SSD1963 4.3" | + + Top --- @@ -684,6 +685,57 @@ Can draw three styles: solid, open frame and open frame with screw bosses. | 1 | ```fuseholder(6)``` | Fuse holder 20mm | +Top + +--- + +## Geared_steppers +Geared tin can steppers + + +[vitamins/geared_steppers.scad](vitamins/geared_steppers.scad) Object definitions. + +[vitamins/geared_stepper.scad](vitamins/geared_stepper.scad) Implementation. + +[tests/geared_steppers.scad](tests/geared_steppers.scad) Code for this example. + +### Properties +| Function | Description | +|:--- |:--- | +| ```gs_boss_d(type)``` | Boss around the shaft diameter | +| ```gs_boss_h(type)``` | Boss around the shaft height | +| ```gs_bulge2_d(type)``` | Plastic rear bulge depth from centre | +| ```gs_bulge2_h(type)``` | Plastic rear bulge height | +| ```gs_bulge2_w(type)``` | Plastic rear bulge width | +| ```gs_bulge_d(type)``` | Plastic bulge depth from centre | +| ```gs_bulge_h(type)``` | Plastic bulge height | +| ```gs_bulge_w(type)``` | Plastic bulge width | +| ```gs_diameter(type)``` | Can diameter | +| ```gs_flat_length(type)``` | Shaft flat length | +| ```gs_height(type)``` | Can height | +| ```gs_hole_d(type)``` | Screw hole diameter | +| ```gs_lug_t(type)``` | Screw lug thickness | +| ```gs_lug_w(type)``` | Screw lug width | +| ```gs_offset(type)``` | Offset of the shaft from the centre of the can | +| ```gs_pitch(type)``` | Screw pitch | +| ```gs_shaft_d(type)``` | Shaft diameter | +| ```gs_shaft_flat(type)``` | Shaft width across the flats | +| ```gs_shaft_length(type)``` | Shaft length | + +### Modules +| Module | Description | +|:--- |:--- | +| ```geared_stepper(type)``` | Draw the specified geared stepper | +| ```geared_stepper_screw_positions(type)``` | Place children at the screw positions | + +![geared_steppers](tests/png/geared_steppers.png) + +### Vitamins +| Qty | Module call | BOM entry | +| ---:|:--- |:---| +| 1 | ```geared_stepper(28BYJ_48)``` | Geared stepper - 28BYJ-48 5V | + + Top --- @@ -1490,6 +1542,34 @@ If a nut is given a child then it gets placed on its top surface. | 1 | ```wingnut(M4_wingnut)``` | Wingnut M4 | +Top + +--- + +## O_ring +Nitrile rubber O-rings. + +Just a black torus specified by internal diameter, ```id``` and ```minor_d``` plus a BOM entry. +Can be shown stretched by specifying the ```actual_id```. + + +[vitamins/o_ring.scad](vitamins/o_ring.scad) Implementation. + +[tests/o_ring.scad](tests/o_ring.scad) Code for this example. + +### Modules +| Module | Description | +|:--- |:--- | +| ```O_ring(id, minor_d, actual_id = 0)``` | Draw O-ring with specified internal diameter and minor diameter. ```actual_id``` can be used to stretch it around something. | + +![o_ring](tests/png/o_ring.png) + +### Vitamins +| Qty | Module call | BOM entry | +| ---:|:--- |:---| +| 1 | ```O_ring(2.5, 1.6)``` | O-ring nitrile 2.5mm x 1.6mm | + + Top --- @@ -1527,34 +1607,6 @@ A permanent magnet that can be magnatized and de-magnatized electronically. | 1 | ```opengrab()``` | OpenGrab V3 electro permanent magnet | -Top - ---- - -## O_ring -Nitrile rubber O-rings. - -Just a black torus specified by internal diameter, ```id``` and ```minor_d``` plus a BOM entry. -Can be shown stretched by specifying the ```actual_id```. - - -[vitamins/o_ring.scad](vitamins/o_ring.scad) Implementation. - -[tests/o_ring.scad](tests/o_ring.scad) Code for this example. - -### Modules -| Module | Description | -|:--- |:--- | -| ```O_ring(id, minor_d, actual_id = 0)``` | Draw O-ring with specified internal diameter and minor diameter. ```actual_id``` can be used to stretch it around something. | - -![o_ring](tests/png/o_ring.png) - -### Vitamins -| Qty | Module call | BOM entry | -| ---:|:--- |:---| -| 1 | ```O_ring(2.5, 1.6)``` | O-ring nitrile 2.5mm x 1.6mm | - - Top --- @@ -1606,11 +1658,12 @@ PCBs and perfboard with optional components. The shape can be a rectangle with o | ```pcb_assembly(type, height, thickness)``` | Draw PCB assembly with spaces and fasteners in place | | ```pcb_base(type, height, thickness, wall = 2)``` | Generate STL for a base with PCB spacers | | ```pcb_component(comp, cutouts = false, angle = undef)``` | Draw pcb component from description | +| ```pcb_component_position(type, name)``` | Position child at the specified component position | | ```pcb_components(type, cutouts = false, angle = undef)``` | Draw list of PCB components on the PCB | | ```pcb_cutouts(type, angle = undef)``` | Make cut outs to clear components on a PCB | | ```pcb_grid(type, x, y, z = 0)``` | Positions children at specified grid positions | | ```pcb_screw_positions(type)``` | Positions children at the mounting hole positions | -| ```pcb_spacer(screw, height, wall = 1.8)``` | Generate STL for PCB spacer | +| ```pcb_spacer(screw, height, wall = 1.8, taper = 0)``` | Generate STL for PCB spacer | | ```rj45(cutout = false)``` | Draw RJ45 Ethernet connector | | ```terminal_35(ways)``` | Draw 3.5mm terminal block | | ```uSD(size, cutout = false)``` | Draw uSD socket | @@ -1636,7 +1689,7 @@ PCBs and perfboard with optional components. The shape can be a rectangle with o | 1 | ```molex_254(2)``` | Molex KK header 2 way | | 1 | ```molex_254(3)``` | Molex KK header 3 way | | 16 | ```nut(M2_nut, nyloc = true)``` | Nut M2 x 1.6mm nyloc | -| 12 | ```nut(M2p5_nut, nyloc = true)``` | Nut M2.5 x 2.2mm nyloc | +| 16 | ```nut(M2p5_nut, nyloc = true)``` | Nut M2.5 x 2.2mm nyloc | | 12 | ```nut(M3_nut, nyloc = true)``` | Nut M3 x 2.4mm nyloc | | 12 | ```nut(M4_nut, nyloc = true)``` | Nut M4 x 3.2mm nyloc | | 1 | ```pcb(PI_IO)``` | PI_IO V2 | @@ -1652,6 +1705,7 @@ PCBs and perfboard with optional components. The shape can be a rectangle with o | 4 | ```screw(M2_cap_screw, 25)``` | Screw M2 cap x 25mm | | 4 | ```screw(M2p5_cap_screw, 16)``` | Screw M2.5 cap x 16mm | | 8 | ```screw(M2p5_pan_screw, 20)``` | Screw M2.5 pan x 20mm | +| 4 | ```screw(M2p5_pan_screw, 30)``` | Screw M2.5 pan x 30mm | | 8 | ```screw(M3_cap_screw, 25)``` | Screw M3 cap x 25mm | | 4 | ```screw(M3_cap_screw, 30)``` | Screw M3 cap x 30mm | | 12 | ```screw(M4_cap_screw, 30)``` | Screw M4 cap x 30mm | @@ -1659,9 +1713,10 @@ PCBs and perfboard with optional components. The shape can be a rectangle with o | 2 | ```green_terminal(gt_2p54, 4)``` | Terminal block 4 way 0.1" | | 1 | | USB A to Mini B lead | | 16 | ```washer(M2_washer)``` | Washer M2 x 5mm x 0.3mm | -| 12 | ```washer(M2p5_washer)``` | Washer M2.5 x 5.9mm x 0.5mm | +| 16 | ```washer(M2p5_washer)``` | Washer M2.5 x 5.9mm x 0.5mm | | 12 | ```washer(M3_washer)``` | Washer M3 x 7mm x 0.5mm | | 12 | ```washer(M4_washer)``` | Washer M4 x 9mm x 0.8mm | +| 1 | ```pcb(ZC_A0591)``` | ZC-A0591 ULN2003 driver PCB | ### Printed | Qty | Filename | @@ -1670,10 +1725,10 @@ PCBs and perfboard with optional components. The shape can be a rectangle with o | 4 | pcb_spacer20110.stl | | 4 | pcb_spacer20120.stl | | 4 | pcb_spacer20130.stl | -| 4 | pcb_spacer2560.stl | -| 8 | pcb_spacer2570.stl | -| 4 | pcb_spacer2580.stl | -| 4 | pcb_spacer2590.stl | +| 4 | pcb_spacer25200.stl | +| 4 | pcb_spacer2570.stl | +| 4 | pcb_spacer2580_2.stl | +| 4 | pcb_spacer2590_2.stl | | 4 | pcb_spacer30140.stl | | 4 | pcb_spacer30150.stl | | 4 | pcb_spacer30190.stl | @@ -2101,13 +2156,19 @@ Rocket switch. Also used for neon indicator in the same form factor. --- ## Rod -Steel rods, with optional chamfer. +Steel rods and studding with chamfered ends. [vitamins/rod.scad](vitamins/rod.scad) Implementation. [tests/rod.scad](tests/rod.scad) Code for this example. +### Modules +| Module | Description | +|:--- |:--- | +| ```rod(d , l)``` | Draw a smooth rod with specified length and diameter | +| ```studding(d , l)``` | Draw a threaded rod with specified length and diameter | + ![rod](tests/png/rod.png) ### Vitamins @@ -2120,6 +2181,13 @@ Steel rods, with optional chamfer. | 1 | ```rod(5, 80)``` | Smooth rod 5mm x 80mm | | 1 | ```rod(6, 80)``` | Smooth rod 6mm x 80mm | | 1 | ```rod(8, 80)``` | Smooth rod 8mm x 80mm | +| 1 | ```studding(10, 80)``` | Threaded rod M10 x 80mm | +| 1 | ```studding(12, 80)``` | Threaded rod M12 x 80mm | +| 1 | ```studding(3, 80)``` | Threaded rod M3 x 80mm | +| 1 | ```studding(4, 80)``` | Threaded rod M4 x 80mm | +| 1 | ```studding(5, 80)``` | Threaded rod M5 x 80mm | +| 1 | ```studding(6, 80)``` | Threaded rod M6 x 80mm | +| 1 | ```studding(8, 80)``` | Threaded rod M8 x 80mm | Top @@ -2285,6 +2353,7 @@ Note that modules that drill holes will return a 2D object if ```h``` is set to | 1 | ```sheet(MDF19, 30, 30, 2)``` | Sheet MDF 30mm x 30mm x 19mm | | 1 | ```sheet(MDF6, 30, 30, 2)``` | Sheet MDF 30mm x 30mm x 6mm | | 1 | ```sheet(PMMA10, 30, 30, 2)``` | Sheet acrylic 30mm x 30mm x 10mm | +| 1 | ```sheet(PMMA2, 30, 30, 2)``` | Sheet acrylic 30mm x 30mm x 2mm | | 1 | ```sheet(PMMA3, 30, 30, 2)``` | Sheet acrylic 30mm x 30mm x 3mm | | 1 | ```sheet(PMMA6, 30, 30, 2)``` | Sheet acrylic 30mm x 30mm x 6mm | | 1 | ```sheet(PMMA8, 30, 30, 2)``` | Sheet acrylic 30mm x 30mm x 8mm | @@ -2579,7 +2648,7 @@ Toggle switches | ---:|:--- |:---| | 1 | ```toggle(AP5236, 3)``` | Toggle switch AP5236 | | 1 | ```toggle(CK7101, 3)``` | Toggle switch CK7101 | -| 1 | ```toggle(CK7105, 3)``` | Toggle switch CK7101 | +| 1 | ```toggle(CK7105, 3)``` | Toggle switch CK7105 | | 1 | ```toggle(MS332F, 3)``` | Toggle switch MS332F | @@ -2921,7 +2990,7 @@ Just a BOM entry at the moment and cable bundle size functions for holes, plus c |:--- |:--- | | ```cable_tie(cable_r, thickness)``` | A ziptie threaded around cable radius ```cable_r``` and through a panel with specified ```thickness```. | | ```cable_tie_holes(cable_r, h = 100)``` | Holes to thread a ziptie through a panel to make a cable tie. | -| ```mouse_hole(cable, h = 100)``` | A mouse hole to allow a panel to go over a wire bundle. | +| ```mouse_hole(cable, h = 100, teardrop = false)``` | A mouse hole to allow a panel to go over a wire bundle. | | ```ribbon_cable(ways, length)``` | Add ribbon cable to the BOM | | ```wire(color, strands, length, strand = 0.2)``` | Add stranded wire to the BOM | @@ -3099,6 +3168,10 @@ and mounted components. A list specifies the internal dimensions, screw type, top, bottom and side sheet types and the block maximum spacing. + * An optional name can be specified to allow more then one box in a project. + * An optional list of fixing blocks to be omitted can be given. + * Star washers can be omitted by setting the 11th parameter to false. + Uses [fixing blocks](#fixing_block) and [corner blocks](#corner_block). @@ -3112,26 +3185,29 @@ Uses [fixing blocks](#fixing_block) and [corner blocks](#corner_block). | ```bbox_base_sheet(type)``` | Sheet type for the base | | ```bbox_depth(type)``` | Internal depth | | ```bbox_height(type)``` | Internal height | +| ```bbox_name(type)``` | Optional name if there is more than one box in a project | | ```bbox_screw(type)``` | Screw type for corner blocks | | ```bbox_sheets(type)``` | Sheet type for the sides | +| ```bbox_skip_blocks(type)``` | List of fixing blocks to skip, used to allow a hinged panel for example | | ```bbox_span(type)``` | Maximum span between fixing blocks | | ```bbox_top_sheet(type)``` | Sheet type for the top | | ```bbox_width(type)``` | Internal width | +| ```star_washers(type)``` | Set to false to remove star washers. | ### Modules | Module | Description | |:--- |:--- | | ```_bbox_assembly(type, top = true, base = true, left = true, right = true, back = true, front = true)``` | The box assembly, wrap with a local copy without parameters | | ```bbox_back(type)``` | Default back, can be overridden to customise | -| ```bbox_back_blank(type)``` | 2D template for the back | +| ```bbox_back_blank(type, sheet = false)``` | 2D template for the back | | ```bbox_base(type)``` | Default base, can be overridden to customise | | ```bbox_base_blank(type)``` | 2D template for the base | | ```bbox_front(type)``` | Default front, can be overridden to customise | -| ```bbox_front_blank(type)``` | 2D template for the front | +| ```bbox_front_blank(type, sheet = false)``` | 2D template for the front | | ```bbox_left(type)``` | Default left side, can be overridden to customise | -| ```bbox_left_blank(type)``` | 2D template for the left side | +| ```bbox_left_blank(type, sheet = false)``` | 2D template for the left side | | ```bbox_right(type)``` | Default right side, can be overridden to customise | -| ```bbox_right_blank(type)``` | 2D template for the right side | +| ```bbox_right_blank(type, sheet = false)``` | 2D template for the right side | | ```bbox_shelf_blank(type)``` | 2D template for a shelf | | ```bbox_top(type)``` | Default top, can be overridden to customise | | ```bbox_top_blank(type)``` | 2D template for the top | @@ -3249,6 +3325,11 @@ See [butt_box](#Butt_box) for an example of usage. Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly. +Specific fasteners can be omitted by setting a side's thickness to 0 and the block omitted by setting ```show_block``` to false. +This allows the block and one set of fasteners to be on one assembly and the other fasteners on the mating assemblies. + +Star washers can be omitted by setting ```star_washers``` to false. + [printed/corner_block.scad](printed/corner_block.scad) Implementation. @@ -3269,10 +3350,10 @@ Note that the block with its inserts is defined as a sub assembly, but its faste |:--- |:--- | | ```corner_block(screw = def_screw, name = false)``` | Generate the STL for a printed corner block | | ```corner_block_assembly(screw = def_screw, name = false)``` | The printed block with inserts | -| ```corner_block_h_holes(screw = def_screw)``` | Place children at the side screw holes | +| ```corner_block_h_holes(screw = def_screw, index = undef)``` | Place children at the side screw holes | | ```corner_block_holes(screw = def_screw)``` | Place children at all the holes | | ```corner_block_v_hole(screw = def_screw)``` | Place children at the bottom screw hole | -| ```fastened_corner_block_assembly(thickness, screw = def_screw, thickness_below = undef, name = false)``` | Printed block with all fasteners | +| ```fastened_corner_block_assembly(thickness, screw = def_screw, thickness_below = undef, thickness_side2 = undef, name = false, show_block = true, star_washers = true)``` | Printed block with all fasteners | ![corner_block](tests/png/corner_block.png) @@ -3461,6 +3542,11 @@ See [butt_box](#Butt_box) for an example of usage. Note that the block with its inserts is defined as a sub assembly, but its fasteners get added to the parent assembly. +Specific fasteners can be omitted by setting a side's thickness to 0 and the block omitted by setting ```show_block``` to false. +This allows the block and one set of fasteners to be on one assembly and the other fasteners on the mating assemblies. + +Star washers can be omitted by setting ```star_washers``` to false. + [printed/fixing_block.scad](printed/fixing_block.scad) Implementation. @@ -3480,7 +3566,7 @@ Note that the block with its inserts is defined as a sub assembly, but its faste ### Modules | Module | Description | |:--- |:--- | -| ```fastened_fixing_block_assembly(thickness, screw = def_screw, screw2 = undef, thickness2 = undef)``` | Assembly with fasteners in place | +| ```fastened_fixing_block_assembly(thickness, screw = def_screw, screw2 = undef, thickness2 = undef, show_block = true, star_washers = true)``` | Assembly with fasteners in place | | ```fixing_block(screw = def_screw)``` | Generate the STL | | ```fixing_block_assembly(screw = def_screw)``` | Printed part with the inserts inserted | | ```fixing_block_h_hole(screw = def_screw)``` | Position children on the horizontal hole | @@ -3630,7 +3716,7 @@ inserts don't grip well in rubber. |:--- |:--- | | ```fastened_insert_foot_assembly(t = 3, type = insert_foot)``` | Assembly with fasteners in place for specified sheet thickness | | ```foot(type = foot)``` | Generate STL | -| ```foot_assembly(t = 0, type = foot)``` | Assembly with fasteners in place for specified sheet thickness | +| ```foot_assembly(t = 0, type = foot, flip = false)``` | Assembly with fasteners in place for specified sheet thickness | | ```insert_foot(type = insert_foot)``` | Generate STL for foot with insert | | ```insert_foot_assembly(type = insert_foot)``` | Printed part with insert in place | diff --git a/scripts/deps.py b/scripts/deps.py index d383a3f..599d665 100644 --- a/scripts/deps.py +++ b/scripts/deps.py @@ -37,9 +37,10 @@ def read_deps(dname): deps.append(dep) return deps -def check_deps(target_mtime, dname): +def check_deps(target, dname): + target_mtime = mtime(target) if not target_mtime: - return "target missing" + return target + " missing" if not os.path.isfile(dname): return "no deps" deps = read_deps(dname) diff --git a/scripts/exports.py b/scripts/exports.py index 953674d..384a8f6 100644 --- a/scripts/exports.py +++ b/scripts/exports.py @@ -84,7 +84,6 @@ def make_parts(target, part_type, parts = None): # Find all the scad files # lib_dir = os.environ['OPENSCADPATH'] + '/NopSCADlib/printed' - used = [] module_suffix = '_dxf' if part_type == 'svg' else '_' + part_type for dir in [source_dir, lib_dir]: for filename in os.listdir(dir): @@ -113,7 +112,7 @@ def make_parts(target, part_type, parts = None): # part_file = target_dir + "/" + part dname = deps_name(deps_dir, filename) - changed = check_deps(mtime(part_file), dname) + changed = check_deps(part_file, dname) changed = times.check_have_time(changed, part) if part_type == 'stl' and not changed and not part in bounds_map: changed = "No bounds" @@ -125,14 +124,9 @@ def make_parts(target, part_type, parts = None): if part_type == 'stl': bounds = c14n_stl.canonicalise(part_file) bounds_map[part] = bounds + targets.remove(part) os.remove(part_maker_name) - # - # Add the files on the BOM to the used list for plates.py - # - for line in open("openscad.log"): - if line[:7] == 'ECHO: "' and line[-6:] == '.' + part_type + '"\n': - used.append(line[7:-2]) # # Write new bounds file # @@ -150,4 +144,3 @@ def make_parts(target, part_type, parts = None): print("Could not find a module called", part[:-4] + module_suffix, "to make", part) sys.exit(1) times.print_times() - return used diff --git a/scripts/make_all.py b/scripts/make_all.py index 60a5494..c77659a 100755 --- a/scripts/make_all.py +++ b/scripts/make_all.py @@ -26,6 +26,7 @@ from exports import make_parts from bom import boms from render import render from views import views +from plateup import plateup if __name__ == '__main__': target = None if len(sys.argv) == 1 else sys.argv[1] @@ -33,4 +34,6 @@ if __name__ == '__main__': for part in ['stl', 'dxf']: make_parts(target, part) render(target, part) + plateup(target, part) + views(target) diff --git a/scripts/openscad.py b/scripts/openscad.py index b33e547..3dded6f 100644 --- a/scripts/openscad.py +++ b/scripts/openscad.py @@ -24,11 +24,12 @@ from __future__ import print_function import subprocess, sys -def run(*args): +def run(*args, silent = False): cmd = ["openscad"] + list(args) - for arg in cmd: - print(arg, end=" ") - print() + if not silent: + for arg in cmd: + print(arg, end=" ") + print() with open("openscad.log", "w") as log: rc = subprocess.call(cmd, stdout = log, stderr = log) for line in open("openscad.log", "rt"): diff --git a/scripts/panels.py b/scripts/panels.py new file mode 100644 index 0000000..1ef83e6 --- /dev/null +++ b/scripts/panels.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# nop.head@gmail.com +# hydraraptor.blogspot.com +# +# This file is part of NopSCADlib. +# +# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with NopSCADlib. +# If not, see . +# +#! Panelises DXF files so they can be routed together by running scad files found in the ```panels``` directory. + +from __future__ import print_function +import sys + +from plateup import plateup + +if __name__ == '__main__': + if len(sys.argv) > 1: + target = sys.argv[1] + else: + target = None + plateup(target, 'dxf') diff --git a/scripts/plateup.py b/scripts/plateup.py new file mode 100644 index 0000000..54a44f6 --- /dev/null +++ b/scripts/plateup.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# nop.head@gmail.com +# hydraraptor.blogspot.com +# +# This file is part of NopSCADlib. +# +# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with NopSCADlib. +# If not, see . +# + +from __future__ import print_function +import os +import openscad +import sys +import c14n_stl +from set_config import * +from deps import * +from shutil import copyfile + +source_dirs = { "stl" : "platters", "dxf" : "panels" } +target_dirs = { "stl" : "printed", "dxf" : "routed" } + +def plateup(target, part_type): + # + # Make the target directory + # + top_dir = set_config(target) + parts_dir = top_dir + part_type + 's' + target_dir = parts_dir + '/' + target_dirs[part_type] + source_dir = top_dir + source_dirs[part_type] + deps_dir = source_dir + "/deps" + if not os.path.isdir(source_dir): + return + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + if not os.path.isdir(deps_dir): + os.makedirs(deps_dir) + # + # Decide which files to make + # + sources = [file for file in os.listdir(source_dir) if file.endswith('.scad')] + # + # Run OpenSCAD on the source files to make the targets + # + used = [] + for src in sources: + src_file = source_dir + '/' + src + part_file = target_dir + '/' + src[:-4] + part_type + dname = deps_name(deps_dir, src) + changed = check_deps(part_file, dname) + if changed: + print(changed) + openscad.run("-D$bom=1", "-d", dname, "-o", part_file, src_file) + if part_type == 'stl': + c14n_stl.canonicalise(part_file) + log_name = 'openscad.log' + else: + log_name = 'openscad.echo' + openscad.run("-D$bom=1", "-o", log_name, src_file, silent = True) + # + # Add the files on the BOM to the used list + # + with open(log_name) as file: + for line in file.readlines(): + if line.startswith('ECHO: "~') and line.endswith('.' + part_type + '"\n'): + used.append(line[8:-2]) + # + # Copy file that are not included + # + copied = [] + for file in os.listdir(parts_dir): + if file.endswith('.' + part_type) and not file in used: + src = parts_dir + '/' + file + dst = target_dir + '/' + file + if mtime(src) > mtime(dst): + print("Copying %s to %s" % (src, dst)) + copyfile(src, dst) + copied.append(file) + # + # Remove any cruft + # + targets = [file[:-4] + part_type for file in sources] + for file in os.listdir(target_dir): + if file.endswith('.' + part_type): + if not file in targets and not file in copied: + print("Removing %s" % file) + os.remove(target_dir + '/' + file) diff --git a/scripts/platters.py b/scripts/platters.py new file mode 100644 index 0000000..4a59948 --- /dev/null +++ b/scripts/platters.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# +# NopSCADlib Copyright Chris Palmer 2018 +# nop.head@gmail.com +# hydraraptor.blogspot.com +# +# This file is part of NopSCADlib. +# +# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with NopSCADlib. +# If not, see . +# +#! Generates build plates of STL files for efficient printing by running scad files found in the ```platters``` directory. + +from __future__ import print_function +import sys + +from plateup import plateup + +if __name__ == '__main__': + if len(sys.argv) > 1: + target = sys.argv[1] + else: + target = None + plateup(target, 'stl') diff --git a/scripts/readme.md b/scripts/readme.md index 23ff635..bd6e53a 100644 --- a/scripts/readme.md +++ b/scripts/readme.md @@ -12,6 +12,8 @@ They should work with both Python 2 and Python 3. | ```dxfs.py``` | Generates DXF files for all the routed parts listed on the BOM or a specified list. | | ```gallery.py``` | Finds projects and adds them to the gallery. | | ```make_all.py``` | Generates all the files for a project by running ```bom.py```, ```stls.py```, ```dxfs.py```, ```render.py``` and ```views.py```. | +| ```panels.py``` | Panelises DXF files so they can be routed together by running scad files found in the ```panels``` directory. | +| ```platters.py``` | Generates build plates of STL files for efficient printing by running scad files found in the ```platters``` directory. | | ```render.py``` | Renders STL and DXF files to PNG for inclusion in the build instructions. | | ```set_config.py``` | Sets the target configuration for multi-target projects that have variable configurations. | | ```stls.py``` | Generates STL files for all the printed parts listed on the BOM or a specified list. | diff --git a/scripts/tests.py b/scripts/tests.py index 784e77b..436d772 100755 --- a/scripts/tests.py +++ b/scripts/tests.py @@ -107,7 +107,7 @@ def tests(tests): # # List of individual part files # - scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"] + scads = [i for i in sorted(os.listdir(scad_dir)) if i[-5:] == ".scad"] for scad in scads: base_name = scad[:-5] @@ -187,7 +187,7 @@ def tests(tests): body += ["![%s](%s)\n" %(base_name, png_name)] dname = deps_name(deps_dir, scad) - oldest = min(mtime(png_name), mtime(bom_name)) + oldest = png_name if mtime(png_name) < mtime(bom_name) else bom_name changed = check_deps(oldest, dname) changed = times.check_have_time(changed, scad_name) if changed: @@ -257,7 +257,7 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa print('', file = doc_file, end = '') for type in types: if i < len(index[type]): - name = index[type][i] + name = sorted(index[type])[i] print(' ' + name + ' ', file = doc_file, end = '') else: print('', file = doc_file, end = '') diff --git a/scripts/views.py b/scripts/views.py index efac634..ab2d8c7 100755 --- a/scripts/views.py +++ b/scripts/views.py @@ -154,14 +154,14 @@ def views(target, do_assemblies = None): f.write("use <%s/%s>\n" % (dir, filename)) f.write("%s();\n" % module); # - # Run openscad on the created file + # Run openscad on th created file # dname = deps_name(deps_dir, filename) for explode in [0, 1]: png_name = target_dir + '/' + module + '.png' if not explode: png_name = png_name.replace('_assembly', '_assembled') - changed = check_deps(mtime(png_name), dname) + changed = check_deps(png_name, dname) changed = times.check_have_time(changed, png_name) tmp_name = 'tmp.png' if changed: diff --git a/tests/geared_steppers.scad b/tests/geared_steppers.scad new file mode 100644 index 0000000..ba9ef57 --- /dev/null +++ b/tests/geared_steppers.scad @@ -0,0 +1,27 @@ +// +// NopSCADlib Copyright Chris Palmer 2019 +// nop.head@gmail.com +// hydraraptor.blogspot.com +// +// This file is part of NopSCADlib. +// +// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +// GNU General Public License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with NopSCADlib. +// If not, see . +// +include <../vitamins/geared_steppers.scad> + +use <../utils/layout.scad> + +module geared_steppers() + layout([for(g = geared_steppers) gs_diameter(g)], 5) + geared_stepper(geared_steppers[$i]); + +geared_steppers(); diff --git a/tests/png/geared_steppers.png b/tests/png/geared_steppers.png new file mode 100644 index 0000000..8f76076 Binary files /dev/null and b/tests/png/geared_steppers.png differ diff --git a/tests/png/pcbs.png b/tests/png/pcbs.png index d106cde..77f5476 100644 Binary files a/tests/png/pcbs.png and b/tests/png/pcbs.png differ diff --git a/tests/png/rod.png b/tests/png/rod.png index 2921e9e..8bfea67 100644 Binary files a/tests/png/rod.png and b/tests/png/rod.png differ diff --git a/tests/png/sheets.png b/tests/png/sheets.png index 68d551f..6a369f2 100644 Binary files a/tests/png/sheets.png and b/tests/png/sheets.png differ diff --git a/tests/png/wire.png b/tests/png/wire.png index 2020778..447b71a 100644 Binary files a/tests/png/wire.png and b/tests/png/wire.png differ diff --git a/tests/rod.scad b/tests/rod.scad index f83873a..0e25577 100644 --- a/tests/rod.scad +++ b/tests/rod.scad @@ -24,8 +24,13 @@ include <../vitamins/linear_bearings.scad> use <../vitamins/rod.scad> module rods() - layout([for(b = linear_bearings) 2 * bearing_radius(b)]) + layout([for(b = linear_bearings) 2 * bearing_radius(b)]) { + rod(bearing_rod_dia(linear_bearings[$i]), 80); + translate([0, 20]) + studding(bearing_rod_dia(linear_bearings[$i]), 80); + } + if($preview) rods(); diff --git a/tests/wire.scad b/tests/wire.scad index 0041fcb..9af46d5 100644 --- a/tests/wire.scad +++ b/tests/wire.scad @@ -61,7 +61,7 @@ module wires() { translate([-w / 2, 0]) square([w, h]); - mouse_hole(bundle, 0); + mouse_hole(bundle, 0, true); } translate_z(-thickness) diff --git a/vitamins/geared_stepper.scad b/vitamins/geared_stepper.scad new file mode 100644 index 0000000..30d2c4e --- /dev/null +++ b/vitamins/geared_stepper.scad @@ -0,0 +1,114 @@ +// +// NopSCADlib Copyright Chris Palmer 2019 +// nop.head@gmail.com +// hydraraptor.blogspot.com +// +// This file is part of NopSCADlib. +// +// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +// GNU General Public License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with NopSCADlib. +// If not, see . +// + +// +//! Geared tin can steppers +// +include <../core.scad> +use <../utils/rounded_cylinder.scad> +use <../utils/round.scad> + +function gs_diameter(type) = type[2]; //! Can diameter +function gs_height(type) = type[3]; //! Can height +function gs_pitch(type) = type[4]; //! Screw pitch +function gs_lug_w(type) = type[5]; //! Screw lug width +function gs_lug_t(type) = type[6]; //! Screw lug thickness +function gs_hole_d(type) = type[7]; //! Screw hole diameter +function gs_offset(type) = type[8]; //! Offset of the shaft from the centre of the can +function gs_boss_d(type) = type[9]; //! Boss around the shaft diameter +function gs_boss_h(type) = type[10]; //! Boss around the shaft height +function gs_shaft_d(type) = type[11]; //! Shaft diameter +function gs_shaft_flat(type) = type[12]; //! Shaft width across the flats +function gs_shaft_length(type) = type[13]; //! Shaft length +function gs_flat_length(type) = type[14]; //! Shaft flat length +function gs_bulge_w(type) = type[15]; //! Plastic bulge width +function gs_bulge_d(type) = type[16]; //! Plastic bulge depth from centre +function gs_bulge_h(type) = type[17]; //! Plastic bulge height +function gs_bulge2_w(type) = type[18]; //! Plastic rear bulge width +function gs_bulge2_d(type) = type[19]; //! Plastic rear bulge depth from centre +function gs_bulge2_h(type) = type[20]; //! Plastic rear bulge height + +module geared_stepper_screw_positions(type) //! Place children at the screw positions + for(side = [-1, 1]) + translate([side * gs_pitch(type) / 2, -gs_offset(type)]) + children(); + +module geared_stepper(type) { //! Draw the specified geared stepper + vitamin(str("geared_stepper(", type[0], "): Geared stepper - ", type[1])); + + radius = gs_diameter(type) / 2; + height = gs_height(type); + offset = gs_offset(type); + color("silver") { + translate([0, -offset]) + rounded_cylinder(r = radius, h = height, r2 = 1); + + cylinder(d = gs_boss_d(type), h = 2 * gs_boss_h(type), center = true); + + linear_extrude(height = gs_lug_t(type)) + difference() { + hull() + geared_stepper_screw_positions(type) + circle(d = gs_lug_w(type)); + + geared_stepper_screw_positions(type) + circle(d = gs_hole_d(type)); + } + + translate([0, -offset - radius, eps]) + cube([gs_bulge_w(type) - 2, 2 * (gs_bulge_d(type) - radius) - 2, 2 * eps], center = true); + } + vflip() + color(brass) { + d = gs_shaft_d(type); + h = gs_shaft_length(type); + linear_extrude(height = h) + intersection() { + circle(d = d); + + square([d + 1, gs_shaft_flat(type)], center = true); + } + + cylinder(d = d, h = h - gs_flat_length(type)); + } + + color("skyblue") { + h1 = gs_bulge_h(type); + translate([0, - offset - radius, eps]) + rounded_rectangle([gs_bulge_w(type), 2 * (gs_bulge_d(type) - radius), h1], 0.5, center = false); + + h2 = gs_bulge2_h(type); + translate([0, - offset, h1 + 1 - h2]) + linear_extrude(height = h2) + round(0.5) + intersection() { + circle(gs_bulge2_d(type)); + + translate([0, -50]) + square([gs_bulge2_w(type), 100], center = true); + } + } + + translate_z(2.5) + for(i = [0 : 4]) + translate([i - 2.5, 0]) + rotate([90, 0, 0]) + color(["yellow", "orange", "red", "pink", "blue"][i]) + cylinder(d = 1, h = radius + offset + 10); +} diff --git a/vitamins/geared_steppers.scad b/vitamins/geared_steppers.scad new file mode 100644 index 0000000..917e89c --- /dev/null +++ b/vitamins/geared_steppers.scad @@ -0,0 +1,27 @@ +// +// NopSCADlib Copyright Chris Palmer 2019 +// nop.head@gmail.com +// hydraraptor.blogspot.com +// +// This file is part of NopSCADlib. +// +// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the +// GNU General Public License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with NopSCADlib. +// If not, see . +// + +// +//! Geared tin can steppers +// +28BYJ_48 = ["28BYJ_48", "28BYJ-48 5V", 28, 19, 35, 7, 0.85, 4.2, 8, 9, 1.5, 5, 3, 10, 6, 14.7, 17, 16.5, 17.7, 15.5, 13.8 ]; + +geared_steppers = [28BYJ_48]; + +use diff --git a/vitamins/pcb.scad b/vitamins/pcb.scad index 5bce753..a0e4c44 100644 --- a/vitamins/pcb.scad +++ b/vitamins/pcb.scad @@ -667,6 +667,22 @@ module pcb_component(comp, cutouts = false, angle = undef) { //! Draw pcb compon } } +module pcb_component_position(type, name) { //! Position child at the specified component position + for(comp = pcb_components(type)) { + p = pcb_coord(type, [comp.x, comp.y]); + if(comp[3][0] == "-") { + if(comp[3] == str("-", name)) + translate([p.x, p.y]) + vflip() + children(); + } + else + if(comp[3] == name) + translate([p.x, p.y, pcb_thickness(type)]) + children(); + } +} + module pcb_components(type, cutouts = false, angle = undef) { //! Draw list of PCB components on the PCB not_on_bom(pcb_parts_on_bom(type)) for(comp = pcb_components(type)) { diff --git a/vitamins/pcbs.scad b/vitamins/pcbs.scad index a48a560..deb0a82 100644 --- a/vitamins/pcbs.scad +++ b/vitamins/pcbs.scad @@ -275,6 +275,10 @@ PI_IO = ["PI_IO", "PI_IO V2", 35.56, 25.4, 1.6, 0, 0, 0, "green", tru [(3.4 - 2.7) * 25.4, (4.5 - 4.15) * 25.4, 0, "2p54socket", 13, 2, true], ], []]; +ZC_A0591 = ["ZC_A0591", "ZC-A0591 ULN2003 driver PCB", 35, 32, 1.6, 0, 2.5, 0, "green", false, [[2.25, 3.25], [-2.25, 3.25], [2.25, -3.25], [-2.25, -3.25] ], + [], [], [], [], M2p5_pan_screw]; + + PERF80x20 = ["PERF80x20", "Perfboard 80 x 20mm", 80, 20, 1.6, 0, 2.3, 0, "green", true, [[2,2],[-2,2],[2,-2],[-2,-2]], [], [], [5.87, 3.49]]; PERF70x50 = ["PERF70x50", "Perfboard 70 x 50mm", 70, 50, 1.6, 0, 2.3, 0, "green", true, [[2,2],[-2,2],[2,-2],[-2,-2]], [], [], [5.87, 3.49]]; @@ -287,6 +291,6 @@ PERF74x51 = ["PERF74x51", "Perfboard 74 x 51mm", 74, 51, 1.0, 0, 3.0, 0, "sienna PSU12V1A = ["PSU12V1A", "PSU 12V 1A", 67, 31, 1.7, 0, 3.9, 0, "green", true, [[3.5, 3.5], [-3.5, 3.5], [-3.5, -3.5], [3.5, -3.5]], [], []]; -pcbs = [ExtruderPCB, PI_IO, RPI3, ArduinoUno3, Keyes5p1, PERF80x20, PERF70x50, PERF70x30, PERF60x40, PERF74x51, PSU12V1A, DuetE, Duex2, Duex5, Melzi]; +pcbs = [ExtruderPCB, PI_IO, RPI3, ArduinoUno3, Keyes5p1, PERF80x20, PERF70x50, PERF70x30, PERF60x40, PERF74x51, PSU12V1A, DuetE, Duex2, Duex5, Melzi, ZC_A0591]; use diff --git a/vitamins/rod.scad b/vitamins/rod.scad index eb4fba5..ffaed1f 100644 --- a/vitamins/rod.scad +++ b/vitamins/rod.scad @@ -18,13 +18,14 @@ // // -//! Steel rods, with optional chamfer. +//! Steel rods and studding with chamfered ends. // include <../core.scad> rod_colour = grey80; +studding_colour = grey70; -module rod(d , l) { +module rod(d , l) { //! Draw a smooth rod with specified length and diameter vitamin(str("rod(", d, ", ", l, "): Smooth rod ", d, "mm x ", l, "mm")); chamfer = d / 10; @@ -35,3 +36,15 @@ module rod(d , l) { cylinder(d = d - 2 * chamfer, h = l, center = true); } } + +module studding(d , l) { //! Draw a threaded rod with specified length and diameter + vitamin(str("studding(", d, ", ", l,"): Threaded rod M", d, " x ", l, "mm")); + + chamfer = d / 20; + color(studding_colour) + hull() { + cylinder(d = d, h = l - 2 * chamfer, center = true); + + cylinder(d = d - 2 * chamfer, h = l, center = true); + } +} diff --git a/vitamins/sheets.scad b/vitamins/sheets.scad index 8600ad1..1260695 100644 --- a/vitamins/sheets.scad +++ b/vitamins/sheets.scad @@ -31,6 +31,7 @@ MDF10 = [ "MDF10", "Sheet MDF", 10, mdf_colour, MDF12 = [ "MDF12", "Sheet MDF", 12, mdf_colour, true]; // ~1/2" MDF18 = [ "MDF18", "Sheet MDF", 18, mdf_colour, true]; MDF19 = [ "MDF19", "Sheet MDF", 19, mdf_colour, true]; // ~3/4" +PMMA2 = [ "PMMA2", "Sheet acrylic", 2, [1, 1, 1, 0.5 ], false]; PMMA3 = [ "PMMA3", "Sheet acrylic", 3, [1, 1, 1, 0.5 ], false]; // ~1/8" PMMA6 = [ "PMMA6", "Sheet acrylic", 6, [1, 1, 1, 0.5 ], false]; // ~1/4" PMMA8 = [ "PMMA8", "Sheet acrylic", 8, [1, 1, 1, 0.5 ], false]; // ~5/16" @@ -45,6 +46,6 @@ AL6 = [ "AL6", "Aluminium tooling plate", 6, [0.9, 0.9, 0.9, 1 ], AL8 = [ "AL8", "Aluminium tooling plate", 8, [0.9, 0.9, 0.9, 1 ], false]; Steel06 = [ "Steel06", "Sheet mild steel", 0.6,"silver" , false]; -sheets = [MDF6, MDF10, MDF12, MDF19, PMMA3, PMMA6, PMMA8, PMMA10, glass2, DiBond, DiBond6, Cardboard, FoilTape, Foam20, AL6, AL8, Steel06]; +sheets = [MDF6, MDF10, MDF12, MDF19, PMMA2, PMMA3, PMMA6, PMMA8, PMMA10, glass2, DiBond, DiBond6, Cardboard, FoilTape, Foam20, AL6, AL8, Steel06]; use diff --git a/vitamins/toggles.scad b/vitamins/toggles.scad index 2112990..b8938f6 100644 --- a/vitamins/toggles.scad +++ b/vitamins/toggles.scad @@ -33,7 +33,7 @@ MS332F_pin = [4.0 , 1.0, 1.0, 4.7, 4.83]; // w l d t i c od id thread pv a tl pd pw CK7101 = ["CK7101", "CK7101", 6.86, 12.7, 8.89, 0.4, 1.7, "red", 6.1, 4, 8.89, 0, 0, 4.45, 25/2, 10.67, 2.92, 0, toggle_nut, toggle_washer, 3, CK7000_tag]; -CK7105 = ["CK7105", "CK7101", 6.86, 12.7, 8.89, 0.4, 1.7, "red", 6.1, 4, 8.89, 0, 0, 4.45,-25/2, 21.33, 5.08, 2.54,toggle_nut, toggle_washer, 3, CK7000_pin]; +CK7105 = ["CK7105", "CK7105", 6.86, 12.7, 8.89, 0.4, 1.7, "red", 6.1, 4, 8.89, 0, 0, 4.45,-25/2, 21.33, 5.08, 2.54,toggle_nut, toggle_washer, 3, CK7000_pin]; AP5236 = ["AP5236", "AP5236", 7.0, 13.6, 11, 0.4, 1.7, "blue",6.1, 4, 8.0 , 8, 1, 3.0 , 25/2, 22.2, 4.90, 2.50,toggle_nut, toggle_washer, 3, AP5236_pin]; MS332F = ["MS332F", "MS332F", 12.6, 13.1, 9.5, 0.4, 1.7, "blue",6.1, 4, 8.0 , 8, 1, 4.0 , 25/2, 14.4, 5.0, 2.2 ,toggle_nut, toggle_washer, 6, MS332F_pin]; diff --git a/vitamins/wire.scad b/vitamins/wire.scad index 471dc9c..ce88643 100644 --- a/vitamins/wire.scad +++ b/vitamins/wire.scad @@ -46,10 +46,14 @@ function cable_bundle(cable) = //! Arrangement of a bundle in a flat cable clip function cable_width(cable) = cable_bundle(cable)[0] * cable_wire_size(cable); //! Width in flat clip function cable_height(cable) = cable_bundle(cable)[1] * cable_wire_size(cable); //! Height in flat clip -module mouse_hole(cable, h = 100) { //! A mouse hole to allow a panel to go over a wire bundle. +module mouse_hole(cable, h = 100, teardrop = false) { //! A mouse hole to allow a panel to go over a wire bundle. r = wire_hole_radius(cable); - rotate(90) slot(r, 2 * r, h = h); + if(teardrop) + vertical_tearslot(r = r, l = 2 * r, h = h); + else + rotate(90) + slot(r, 2 * r, h = h); } module cable_tie_holes(cable_r, h = 100) { //! Holes to thread a ziptie through a panel to make a cable tie.