Compare commits
117 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
814ce4f15d | ||
|
f661cf6934 | ||
|
305d2146f2 | ||
|
e39ee1797d | ||
|
520569cb30 | ||
|
f73a7b46a2 | ||
|
fb9eca85c6 | ||
|
166ed05d4a | ||
|
ce6aec428d | ||
|
4e9d169c31 | ||
|
1810160103 | ||
|
0c9ae8d60c | ||
|
9a0bad4e61 | ||
|
90047815b0 | ||
|
eac0086199 | ||
|
51c649cc53 | ||
|
5fa33d7c4d | ||
|
78ce51d045 | ||
|
23cbadf6df | ||
|
c9c2ffafba | ||
|
2e0e833d40 | ||
|
6c51f8726c | ||
|
0b035dbd15 | ||
|
34b58e3b64 | ||
|
df43fe7dc6 | ||
|
b5fe03fcb2 | ||
|
1658f6f0b4 | ||
|
7b126f9792 | ||
|
479207fd4f | ||
|
3ee55981f9 | ||
|
8c2b4a20fe | ||
|
1529759406 | ||
|
c4a986aa21 | ||
|
ebee729d08 | ||
|
90e7f1a315 | ||
|
e39af154bb | ||
|
933fea687c | ||
|
a7803b1efb | ||
|
1255e71271 | ||
|
b11c5914b3 | ||
|
ac60057801 | ||
|
332933a4fd | ||
|
6b0132c32e | ||
|
afac5f9737 | ||
|
8d8df3cb8a | ||
|
81eb183db9 | ||
|
c99ed98a64 | ||
|
7f65e5d539 | ||
|
ffb7f87cc5 | ||
|
d0513c7299 | ||
|
d1429a3b7d | ||
|
70513993bd | ||
|
9eb35accfd | ||
|
7276f18566 | ||
|
d944198dc4 | ||
|
04f2499a9e | ||
|
1eb8b378e9 | ||
|
362dbdb4fc | ||
|
57d223d84b | ||
|
699385342f | ||
|
547a418cea | ||
|
b6d25048bc | ||
|
4cdab218d9 | ||
|
b6147e5684 | ||
|
966ba536ed | ||
|
2419d50641 | ||
|
02211c2034 | ||
|
77d73b075d | ||
|
cb54a3131b | ||
|
3cf275579c | ||
|
fb41f218fe | ||
|
e6a26bc7b1 | ||
|
cb4fa40643 | ||
|
6a26903514 | ||
|
d08d949887 | ||
|
574a73e527 | ||
|
87a35126de | ||
|
1ca485b66b | ||
|
bc919529d3 | ||
|
9f4ed2b915 | ||
|
7ce055373a | ||
|
71ac571346 | ||
|
e4d93366fa | ||
|
f047ac27f7 | ||
|
a9e479d971 | ||
|
47b01af1ea | ||
|
fe19eba237 | ||
|
235f7b86e3 | ||
|
92d7e18b16 | ||
|
6a7226120f | ||
|
8aa00cd041 | ||
|
f6b512da1f | ||
|
c7ea0939b9 | ||
|
265b5ab555 | ||
|
186dbbfd08 | ||
|
60659a43f8 | ||
|
f412cb1736 | ||
|
2b878556fc | ||
|
1f1a360b7c | ||
|
a547c98995 | ||
|
d9fa8c8668 | ||
|
bf5b6d7c30 | ||
|
9cfde7f524 | ||
|
184f19ef04 | ||
|
c88472121e | ||
|
a74bf094aa | ||
|
5a06f79466 | ||
|
98e17080d8 | ||
|
d3f308a45e | ||
|
fe454884e0 | ||
|
c019448dd3 | ||
|
eadc541e8f | ||
|
02791c40ac | ||
|
85b8ffbbc3 | ||
|
dd5d3869ad | ||
|
b84eb3cf31 | ||
|
0ec7aabcfb |
@@ -18,7 +18,7 @@
|
||||
//
|
||||
|
||||
//
|
||||
// Include this file to use the miniumum library plus screws, nuts and washers
|
||||
// Include this file to use the minimum library plus screws, nuts and washers
|
||||
//
|
||||
include <utils/core/core.scad>
|
||||
//
|
||||
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 168 KiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 290 KiB |
Before Width: | Height: | Size: 291 KiB After Width: | Height: | Size: 293 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 236 KiB |
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 178 KiB |
@@ -33,7 +33,7 @@ $exploded = is_undef($explode) ? 0 : $explode; // 1 f
|
||||
layer_height = is_undef($layer_height) ? 0.25 : $layer_height; // layer heigth when printing
|
||||
extrusion_width = is_undef($extrusion_width) ? 0.5 : $extrusion_width; // filament width when printing
|
||||
nozzle = is_undef($nozzle) ? 0.45 : $nozzle; // 3D printer nozzle
|
||||
cnc_bit_r = is_undef($cnc_bit_r) ? 1.2 : $cnc_bit_r; // miniumum tool radius when milling 2D objects
|
||||
cnc_bit_r = is_undef($cnc_bit_r) ? 1.2 : $cnc_bit_r; // minimum tool radius when milling 2D objects
|
||||
pp1_colour = is_undef($pp1_colour) ? [0, 146/255, 0] : $pp1_colour; // printed part colour 1, RepRap logo colour
|
||||
pp2_colour = is_undef($pp2_colour) ? "red" : $pp2_colour; // printed part colour 2
|
||||
pp3_colour = is_undef($pp3_colour) ? "blue" : $pp3_colour; // printed part colour 3
|
||||
@@ -52,14 +52,7 @@ $fs = extrusion_width / 2;
|
||||
|
||||
function round_to_layer(z) = ceil(z / layer_height) * layer_height;
|
||||
// Some additional named colours
|
||||
grey20 = [0.2, 0.2, 0.2];
|
||||
grey30 = [0.3, 0.3, 0.3];
|
||||
grey40 = [0.4, 0.4, 0.4];
|
||||
grey50 = [0.5, 0.5, 0.5];
|
||||
grey60 = [0.6, 0.6, 0.6];
|
||||
grey70 = [0.7, 0.7, 0.7];
|
||||
grey80 = [0.8, 0.8, 0.8];
|
||||
grey90 = [0.9, 0.9, 0.9];
|
||||
function grey(n) = [0.01, 0.01, 0.01] * n; //! Generate a shade of grey to pass to color().
|
||||
gold = [255/255, 215/255, 0/255];
|
||||
brass = [255/255, 220/255, 100/255];
|
||||
silver = [0.75, 0.75, 0.75];
|
||||
|
3
lib.scad
@@ -29,6 +29,7 @@ include <vitamins/batteries.scad>
|
||||
include <vitamins/blowers.scad>
|
||||
include <vitamins/bulldogs.scad>
|
||||
include <vitamins/buttons.scad>
|
||||
include <vitamins/cameras.scad>
|
||||
include <vitamins/components.scad>
|
||||
include <vitamins/displays.scad>
|
||||
include <vitamins/extrusions.scad>
|
||||
@@ -42,6 +43,7 @@ include <vitamins/ldrs.scad>
|
||||
include <vitamins/leadnuts.scad>
|
||||
include <vitamins/led_meter.scad>
|
||||
include <vitamins/light_strips.scad>
|
||||
include <vitamins/magnets.scad>
|
||||
include <vitamins/mains_sockets.scad>
|
||||
include <vitamins/modules.scad>
|
||||
include <vitamins/panel_meters.scad>
|
||||
@@ -83,6 +85,7 @@ use <utils/rounded_cylinder.scad>
|
||||
use <utils/dogbones.scad>
|
||||
use <utils/tube.scad>
|
||||
use <utils/quadrant.scad>
|
||||
use <utils/gears.scad>
|
||||
use <utils/hanging_hole.scad>
|
||||
use <utils/fillet.scad>
|
||||
use <utils/rounded_polygon.scad>
|
||||
|
BIN
libtest.png
Before Width: | Height: | Size: 794 KiB After Width: | Height: | Size: 820 KiB |
44
libtest.scad
@@ -17,6 +17,23 @@
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//!# NopSCADlib
|
||||
//! An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc.
|
||||
//!
|
||||
//! It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and some utilities.
|
||||
//! There are also Python scripts to generate Bills of Materials (BOMs),
|
||||
//! STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
|
||||
//! instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md).
|
||||
//!
|
||||
//! A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
|
||||
//!
|
||||
//! For more examples of what it can make see the [gallery](gallery/readme.md).
|
||||
//!
|
||||
//! The license is GNU General Public License v3.0, see [COPYING](COPYING).
|
||||
//!
|
||||
//! See [usage](docs/usage.md) for requirements, installation instructions and a usage guide.
|
||||
//!
|
||||
//! <img src="libtest.png" width="100%"/>
|
||||
//
|
||||
// This file shows all the parts in the library.
|
||||
//
|
||||
@@ -29,6 +46,7 @@ use <tests/blowers.scad>
|
||||
use <tests/bulldogs.scad>
|
||||
use <tests/buttons.scad>
|
||||
use <tests/cable_strips.scad>
|
||||
use <tests/cameras.scad>
|
||||
use <tests/circlips.scad>
|
||||
use <tests/components.scad>
|
||||
use <tests/d_connectors.scad>
|
||||
@@ -49,6 +67,7 @@ use <tests/LEDs.scad>
|
||||
use <tests/light_strips.scad>
|
||||
use <tests/linear_bearings.scad>
|
||||
use <tests/LED_meters.scad>
|
||||
use <tests/magnets.scad>
|
||||
use <tests/microswitches.scad>
|
||||
use <tests/modules.scad>
|
||||
use <tests/nuts.scad>
|
||||
@@ -72,7 +91,7 @@ use <tests/spades.scad>
|
||||
use <tests/springs.scad>
|
||||
use <tests/SSRs.scad>
|
||||
use <tests/stepper_motors.scad>
|
||||
use <tests/swiss_clips.scad>
|
||||
use <tests/Swiss_clips.scad>
|
||||
use <tests/toggles.scad>
|
||||
use <tests/transformers.scad>
|
||||
use <tests/tubings.scad>
|
||||
@@ -115,17 +134,17 @@ cable_grommets_y = 0;
|
||||
translate([x5, cable_grommets_y])
|
||||
cable_grommets();
|
||||
|
||||
translate([x5, cable_grommets_y + 50])
|
||||
feet();
|
||||
translate([x5 + 80, cable_grommets_y])
|
||||
ribbon_clamps();
|
||||
|
||||
translate([x5, cable_grommets_y + 75])
|
||||
translate([x5, cable_grommets_y + 60])
|
||||
fixing_blocks();
|
||||
|
||||
translate([x5, cable_grommets_y + 100])
|
||||
translate([x5, cable_grommets_y + 90])
|
||||
corner_blocks();
|
||||
|
||||
translate([x5, cable_grommets_y + 150])
|
||||
ribbon_clamps();
|
||||
feet();
|
||||
|
||||
translate([x5 + 70, cable_grommets_y + 150])
|
||||
screw_knobs();
|
||||
@@ -270,8 +289,9 @@ translate([x1, leadnuts_y])
|
||||
|
||||
leds_y = 0;
|
||||
carriers_y = leds_y + 40;
|
||||
spades_y = carriers_y + 40;
|
||||
buttons_y = spades_y + 40;
|
||||
magnets_y = carriers_y + 40;
|
||||
spades_y = magnets_y + 20;
|
||||
buttons_y = spades_y + 20;
|
||||
jacks_y = buttons_y + 40;
|
||||
microswitches_y = jacks_y + 40;
|
||||
rockers_y = microswitches_y + 40;
|
||||
@@ -287,6 +307,9 @@ translate([x2 + 35, leds_y])
|
||||
translate([x2 + 8, carriers_y])
|
||||
carriers();
|
||||
|
||||
translate([x2, magnets_y])
|
||||
magnets();
|
||||
|
||||
translate([x2 + 20, carriers_y])
|
||||
led_meters();
|
||||
|
||||
@@ -334,6 +357,9 @@ translate([x3 + 70, veroboard_y + 30])
|
||||
translate([x3 + 140, veroboard_y + 20])
|
||||
pcb_mounts();
|
||||
|
||||
translate([x3 + 170, veroboard_y + 16])
|
||||
cameras();
|
||||
|
||||
translate([x3, d_connectors_y])
|
||||
d_connectors();
|
||||
|
||||
@@ -381,7 +407,7 @@ sk_brackets_y = extrusion_brackets_y + 80;
|
||||
kp_pillow_blocks_y = sk_brackets_y + 50;
|
||||
scs_bearing_blocks_y = kp_pillow_blocks_y + 60;
|
||||
|
||||
translate([x4 + 150, belts_y + 58]) {
|
||||
translate([x4 + 200, belts_y + 58]) {
|
||||
belt_test();
|
||||
|
||||
translate([0, 60])
|
||||
|
169
printed/box.scad
@@ -31,29 +31,29 @@
|
||||
//!
|
||||
//! Normally the side sheets are the same type but they can be overridden individually as long as the substitute has the same thickness.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
include <../core.scad>
|
||||
use <../vitamins/sheet.scad>
|
||||
use <../vitamins/screw.scad>
|
||||
use <../vitamins/washer.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
use <../utils/quadrant.scad>
|
||||
use <../utils/round.scad>
|
||||
|
||||
bezel_clearance = 0.2;
|
||||
sheet_end_clearance = 1;
|
||||
sheet_slot_clearance = 0.2;
|
||||
|
||||
function box_screw(type) = type[0]; //! Screw type to be used at the corners
|
||||
function box_wall(type) = type[1]; //! Wall thickness of 3D parts
|
||||
function box_sheets(type) = type[2]; //! Sheet type used for the sides
|
||||
function box_top_sheet(type) = type[3]; //! Sheet type for the top
|
||||
function box_base_sheet(type)= type[4]; //! Sheet type for the bottom
|
||||
function box_feet(type) = type[5]; //! True to enable feet on the bottom bezel
|
||||
function box_width(type) = type[6]; //! Internal width
|
||||
function box_depth(type) = type[7]; //! Internal depth
|
||||
function box_height(type) = type[8]; //! Internal height
|
||||
function box_screw(type) = type[0]; //! Screw type to be used at the corners
|
||||
function box_shelf_screw(type) = type[1]; //! Screw type to hold a shelf
|
||||
function box_wall(type) = type[2]; //! Wall thickness of 3D parts
|
||||
function box_sheets(type) = type[3]; //! Sheet type used for the sides
|
||||
function box_top_sheet(type) = type[4]; //! Sheet type for the top
|
||||
function box_base_sheet(type) = type[5]; //! Sheet type for the bottom
|
||||
function box_feet(type) = type[6]; //! True to enable feet on the bottom bezel
|
||||
function box_width(type) = type[7]; //! Internal width
|
||||
function box_depth(type) = type[8]; //! Internal depth
|
||||
function box_height(type) = type[9]; //! Internal height
|
||||
|
||||
function box(screw, wall, sheets, top_sheet, base_sheet, size, feet = false) = //! Construct a property list for a box.
|
||||
concat([screw, wall, sheets, top_sheet, base_sheet, feet], size);
|
||||
function box(screw, wall, sheets, top_sheet, base_sheet, size, feet = false, shelf_screw = M3_dome_screw) = //! Construct a property list for a box.
|
||||
concat([screw, shelf_screw, wall, sheets, top_sheet, base_sheet, feet], size);
|
||||
|
||||
function box_bezel_clearance(type) = bezel_clearance;
|
||||
|
||||
@@ -62,6 +62,7 @@ function box_profile_overlap(type) = 3 + sheet_end_clearance / 2;
|
||||
|
||||
function box_washer(type) = screw_washer(box_screw(type));
|
||||
function box_insert(type) = screw_insert(box_screw(type));
|
||||
function box_shelf_insert(type) = screw_insert(box_shelf_screw(type));
|
||||
|
||||
function box_hole_inset(type) = washer_radius(box_washer(type)) + 1;
|
||||
function box_insert_r(type) = insert_hole_radius(box_insert(type));
|
||||
@@ -90,23 +91,32 @@ function box_bezel_height(type, bottom) = //! Bezel height for top or bottom
|
||||
|
||||
grill_hole = 5;
|
||||
grill_gap = 1.9;
|
||||
module grill(width, height, r = 1000, poly = false, h = 0) { //! A staggered array of 5mm holes to make grills in sheets. Can be constrained to be circular. Set ```poly``` ```true``` for printing, ```false``` for milling.
|
||||
|
||||
function box_grill_hole_r() = grill_hole / 2;
|
||||
|
||||
module grill_hole_positions(width, height, r = 1000) {
|
||||
nx = floor(width / (grill_hole + grill_gap));
|
||||
xpitch = width / nx;
|
||||
ny = floor(height / ((grill_hole + grill_gap) * cos(30)));
|
||||
ypitch = height / ny;
|
||||
|
||||
for(y = [0 : ny - 1], x = [0 : nx - 1 - (y % 2)]) {
|
||||
$x = -width / 2 + (x + 0.5 + (y % 2) / 2) * xpitch;
|
||||
$y = -height / 2 + (y + 0.5) * ypitch;
|
||||
if(sqrt(sqr($x) + sqr($y)) + grill_hole / 2 <= r)
|
||||
translate([$x, $y])
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
module grill(width, height, r = 1000, poly = false, h = 0) { //! A staggered array of 5mm holes to make grills in sheets. Can be constrained to be circular. Set ```poly``` ```true``` for printing, ```false``` for milling.
|
||||
extrude_if(h)
|
||||
for(y = [0 : ny - 1], x = [0 : nx - 1 - (y % 2)]) {
|
||||
x = -width / 2 + (x + 0.5 + (y % 2) / 2) * xpitch;
|
||||
y = -height / 2 + (y + 0.5) * ypitch;
|
||||
if(sqrt(sqr(x) + sqr(y)) + grill_hole / 2 <= r)
|
||||
translate([x, y])
|
||||
if(poly)
|
||||
poly_circle(r = grill_hole / 2);
|
||||
else
|
||||
circle(d = grill_hole);
|
||||
}
|
||||
if(poly)
|
||||
grill_hole_positions(width, height, r)
|
||||
poly_circle(r = grill_hole / 2);
|
||||
else
|
||||
grill_hole_positions(width, height, r)
|
||||
circle(d = grill_hole);
|
||||
}
|
||||
|
||||
module box_corner_profile_2D(type) { //! The 2D shape of the corner profile.
|
||||
@@ -178,6 +188,15 @@ module box_corner_profile_section(type, section, sections) { //! Generates inter
|
||||
}
|
||||
}
|
||||
|
||||
module box_corner_profile_sections(type, section, sections) { //! Generate four copies of a corner profile section
|
||||
stl("box_corner_profile");
|
||||
offset = box_boss_r(type) + 1;
|
||||
for(i = [0 : 3])
|
||||
rotate(i * 90)
|
||||
translate([offset, offset])
|
||||
box_corner_profile_section(type, section, sections);
|
||||
}
|
||||
|
||||
module box_corner_quadrants(type, width, depth)
|
||||
for(corner = [0:3]) {
|
||||
x = [-1,1,1,-1][corner];
|
||||
@@ -261,10 +280,11 @@ dowel_length = 20;
|
||||
dowel_wall = extrusion_width * 3;
|
||||
dowel_h_wall = layer_height * 6;
|
||||
|
||||
|
||||
module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlocking sections of the bezel to allow it to be bigger than the printer
|
||||
w = (box_width(type) + 2 * box_outset(type)) / cols;
|
||||
h = (box_depth(type) + 2 * box_outset(type)) / rows;
|
||||
tw = box_width(type) + 2 * box_outset(type);
|
||||
w = tw / cols;
|
||||
th = box_depth(type) + 2 * box_outset(type);
|
||||
h = th / rows;
|
||||
bw = box_outset(type) - bezel_clearance / 2;
|
||||
bw2 = box_outset(type) + box_inset(type);
|
||||
|
||||
@@ -339,7 +359,7 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
render() difference() {
|
||||
union() {
|
||||
clip(xmin = 0, xmax = w, ymin = 0, ymax = h)
|
||||
translate([box_width(type) / 2 + box_outset(type) - x * w, box_depth(type) / 2 + box_outset(type) - y * h, box_profile_overlap(type)])
|
||||
translate([tw / 2 - x * w, th / 2 - y * h, box_profile_overlap(type)])
|
||||
box_bezel(type, bottom);
|
||||
|
||||
if(x < cols - 1 && y == 0)
|
||||
@@ -399,7 +419,6 @@ module box_bezel_section(type, bottom, rows, cols, x, y) { //! Generates interlo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module box_screw_hole_positions(type)
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * (box_width(type) / 2 - box_hole_inset(type)), y * (box_depth(type) / 2 - box_hole_inset(type))])
|
||||
@@ -442,6 +461,96 @@ module box_shelf_blank(type, sheet = false) { //! Generates a 2D template for a
|
||||
}
|
||||
}
|
||||
|
||||
module box_shelf_screw_positions(type, screw_positions, thickness = 0, wall = undef) { //! Place children at the shelf screw positions
|
||||
w = is_undef(wall) ? box_wall(type) : wall;
|
||||
insert = box_shelf_insert(type);
|
||||
translate_z(-insert_boss_radius(insert, w))
|
||||
for(p = screw_positions)
|
||||
multmatrix(p)
|
||||
translate_z(thickness)
|
||||
children();
|
||||
}
|
||||
|
||||
module box_shelf_bracket(type, screw_positions, wall = undef) { //! Generates a shelf bracket, the first optional child is a 2D cutout and the second 3D cutouts
|
||||
stl("shelf_bracket");
|
||||
w = is_undef(wall) ? box_wall(type) : wall;
|
||||
insert = box_shelf_insert(type);
|
||||
lip = 2 * insert_boss_radius(insert, w);
|
||||
width = insert_length(insert) + w;
|
||||
|
||||
module shape()
|
||||
difference() {
|
||||
square([box_width(type), box_depth(type)], center = true);
|
||||
|
||||
offset(bezel_clearance / 2)
|
||||
box_corner_quadrants(type, box_width(type), box_depth(type));
|
||||
|
||||
if($children)
|
||||
hflip()
|
||||
children();
|
||||
}
|
||||
|
||||
module boss()
|
||||
translate_z(-width + eps)
|
||||
linear_extrude(width - 2 * eps)
|
||||
hull() {
|
||||
circle4n(r = lip / 2 - eps);
|
||||
|
||||
translate([-lip / 2, -lip / 2 + eps])
|
||||
square([lip, eps]);
|
||||
}
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(w)
|
||||
difference() {
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
|
||||
round(2) offset(-width)
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
}
|
||||
|
||||
linear_extrude(lip)
|
||||
difference() {
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
|
||||
offset(-w)
|
||||
shape()
|
||||
if($children)
|
||||
children(0);
|
||||
}
|
||||
|
||||
hflip()
|
||||
box_shelf_screw_positions(type, screw_positions, 0, w)
|
||||
boss();
|
||||
}
|
||||
if($children > 1)
|
||||
hflip()
|
||||
children(1);
|
||||
|
||||
hflip()
|
||||
box_shelf_screw_positions(type, screw_positions, 0, w)
|
||||
insert_hole(insert, counterbore = 1, horizontal = true);
|
||||
}
|
||||
}
|
||||
|
||||
module box_shelf_bracket_section(type, rows, cols, x, y) { //! Generates sections of the shelf bracket to allow it to be bigger than the printer
|
||||
tw = box_width(type);
|
||||
w = tw / cols;
|
||||
th = box_depth(type);
|
||||
h = th / rows;
|
||||
|
||||
clip(xmin = 0, xmax = w, ymin = 0, ymax = h)
|
||||
translate([tw / 2 - x * w, th / 2 - y * h])
|
||||
children();
|
||||
}
|
||||
|
||||
module box_left_blank(type, sheet = false) { //! Generates a 2D template for the left sheet, ```sheet``` can be set to override the type
|
||||
dxf("box_left");
|
||||
|
||||
|
@@ -110,12 +110,9 @@ function fixing_block_positions(type) = let(
|
||||
|
||||
function side_holes(type) = [for(p = fixing_block_positions(type), q = fixing_block_holes(bbox_screw(type))) p * q];
|
||||
|
||||
module drill_holes(type, t)
|
||||
for(list = [corner_holes(type), side_holes(type)], p = list)
|
||||
let(q = t * p)
|
||||
if(abs(transform([0, 0, 0], q).z) < eps)
|
||||
multmatrix(q)
|
||||
drill(screw_clearance_radius(bbox_screw(type)), 0);
|
||||
module bbox_drill_holes(type, t)
|
||||
position_children(concat(corner_holes(type), side_holes(type)), t)
|
||||
drill(screw_clearance_radius(bbox_screw(type)), 0);
|
||||
|
||||
module bbox_base_blank(type) { //! 2D template for the base
|
||||
dxf(str(bbox_name(type), "_base"));
|
||||
@@ -123,7 +120,7 @@ module bbox_base_blank(type) { //! 2D template for the base
|
||||
difference() {
|
||||
sheet_2D(bbox_base_sheet(type), bbox_width(type), bbox_depth(type), 1);
|
||||
|
||||
drill_holes(type, translate(bbox_height(type) / 2));
|
||||
bbox_drill_holes(type, translate(bbox_height(type) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +133,7 @@ module bbox_top_blank(type) { //! 2D template for the top
|
||||
translate([0, t / 2])
|
||||
sheet_2D(bbox_top_sheet(type), bbox_width(type) + 2 * t, bbox_depth(type) + t);
|
||||
|
||||
drill_holes(type, translate(-bbox_height(type) / 2));
|
||||
bbox_drill_holes(type, translate(-bbox_height(type) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +151,7 @@ module bbox_left_blank(type, sheet = false) { //! 2D template for the left side
|
||||
translate([-t / 2, -bb / 2])
|
||||
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]));
|
||||
bbox_drill_holes(type, rotate([0, 90, 90]) * translate([bbox_width(type) / 2, 0]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +165,7 @@ module bbox_right_blank(type, sheet = false) { //! 2D template for the right sid
|
||||
translate([t / 2, -bb / 2])
|
||||
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]));
|
||||
bbox_drill_holes(type, rotate([0, 90, 90]) * translate([-bbox_width(type) / 2, 0]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +180,7 @@ module bbox_front_blank(type, sheet = false, width = 0) { //! 2D template for th
|
||||
translate([0, (bt - bb) / 2])
|
||||
sheet_2D(subst_sheet(type, sheet), max(bbox_width(type) + 2 * t, width), bbox_height(type) + bb + bt);
|
||||
|
||||
drill_holes(type, rotate([-90, 0, 0]) * translate([0, bbox_depth(type) / 2]));
|
||||
bbox_drill_holes(type, rotate([-90, 0, 0]) * translate([0, bbox_depth(type) / 2]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +194,7 @@ module bbox_back_blank(type, sheet = false) { //! 2D template for the back
|
||||
translate([0, -bb / 2])
|
||||
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]));
|
||||
bbox_drill_holes(type, rotate([-90, 0, 0]) * translate([0, -bbox_depth(type) / 2]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -74,7 +74,7 @@ module door_hinge(door_thickness) { //! Generates STL fo
|
||||
square([1, thickness + door_thickness]);
|
||||
}
|
||||
translate([dia / 2, thickness + door_thickness / 2])
|
||||
teardrop(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
teardrop_plus(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
}
|
||||
linear_extrude(thickness)
|
||||
difference() {
|
||||
@@ -127,7 +127,7 @@ module door_hinge_stat_stl() { //! Generates the STL for the stationary part
|
||||
square([dia, 1], center = true);
|
||||
}
|
||||
translate([0, dia / 2 + stat_clearance])
|
||||
teardrop(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
teardrop_plus(r = screw_clearance_radius(pin_screw), h = 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -78,7 +78,7 @@ function pbox_screw_inset(type) = //! How far the base screws are inset
|
||||
R = pbox_radius(type)
|
||||
) max(r, R - (R - r) / sqrt(2));
|
||||
|
||||
module pbox_screw_positions(type) {
|
||||
module pbox_screw_positions(type) { //! Place children at base screw positions
|
||||
foot = pbox_foot(type);
|
||||
inset = pbox_screw_inset(type);
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
@@ -127,6 +127,9 @@ module pbox_inner_shape(type) {
|
||||
rounded_square([w, d], rad, center = true);
|
||||
}
|
||||
|
||||
module pbox_outer_shape(type) //! 2D outer shape of the box
|
||||
offset(pbox_wall(type) / 2) pbox_mid_shape(type);
|
||||
|
||||
module pbox_base(type) { //! Generate the STL for the base
|
||||
stl(str(pbox_name(type),"_base"));
|
||||
t = pbox_base(type);
|
||||
@@ -161,7 +164,7 @@ module pbox(type) { //! Generate the STL for the main case
|
||||
difference() {
|
||||
union() {
|
||||
linear_extrude(total_height)
|
||||
offset(wall / 2) pbox_mid_shape(type);
|
||||
pbox_outer_shape(type);
|
||||
|
||||
if($children > 2)
|
||||
children(2);
|
||||
|
@@ -24,69 +24,72 @@ include <../core.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
use <../vitamins/cable_strip.scad>
|
||||
|
||||
wall = 2;
|
||||
wall = 1.6;
|
||||
min_wall = 2 * extrusion_width;
|
||||
screw = M3_cap_screw;
|
||||
insert = screw_insert(screw);
|
||||
screw_depth = insert_length(insert) + 1;
|
||||
|
||||
function ribbon_clamp_hole_pitch(ways) = ribbon_clamp_slot(ways) + 2 * min_wall + 2 * corrected_radius(insert_hole_radius(insert)); //! Hole pitch
|
||||
function ribbon_clamp_width() = 2 * (insert_hole_radius(insert) + 2); //! Width
|
||||
function ribbon_clamp_length(ways) = ribbon_clamp_hole_pitch(ways) + ribbon_clamp_width(); //! Length given ways
|
||||
function ribbon_clamp_height() = screw_depth + 1; //! Height
|
||||
function ribbon_clamp_screw_depth(screw = screw) = insert_length(screw_insert(screw)) + 1;
|
||||
function ribbon_clamp_hole_pitch(ways, screw = screw) =
|
||||
ribbon_clamp_slot(ways) + 2 * min_wall + 2 * corrected_radius(insert_hole_radius(screw_insert(screw))); //! Hole pitch
|
||||
|
||||
module ribbon_clamp_hole_positions(ways, side = undef) //! Place children at hole positions
|
||||
function ribbon_clamp_width(screw = screw) = 2 * (insert_hole_radius(screw_insert(screw)) + wall); //! Width
|
||||
function ribbon_clamp_length(ways, screw = screw) = ribbon_clamp_hole_pitch(ways, screw) + ribbon_clamp_width(screw); //! Length given ways
|
||||
function ribbon_clamp_height(screw = screw) = ribbon_clamp_screw_depth(screw) + 1; //! Height
|
||||
|
||||
module ribbon_clamp_hole_positions(ways, screw = screw, side = undef) //! Place children at hole positions
|
||||
for(x = is_undef(side) ? [-1, 1] : side)
|
||||
translate([x * ribbon_clamp_hole_pitch(ways) / 2, 0])
|
||||
translate([x * ribbon_clamp_hole_pitch(ways, screw) / 2, 0])
|
||||
children();
|
||||
|
||||
module ribbon_clamp_holes(ways, h = 20) //! Drill screw holes
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
module ribbon_clamp_holes(ways, h = 20, screw = screw) //! Drill screw holes
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
drill(screw_clearance_radius(screw), h);
|
||||
|
||||
module ribbon_clamp(ways) { //! Generate STL for given number of ways
|
||||
stl(str("ribbon_clamp_", ways));
|
||||
module ribbon_clamp(ways, screw = screw) { //! Generate STL for given number of ways
|
||||
screw_d = screw_radius(screw) * 2;
|
||||
stl(str("ribbon_clamp_", ways, screw_d != 3 ? str("_", screw_d) : ""));
|
||||
|
||||
pitch = ribbon_clamp_hole_pitch(ways);
|
||||
d = ribbon_clamp_width();
|
||||
h = ribbon_clamp_height();
|
||||
t = h - ribbon_clamp_slot_depth() - wall;
|
||||
pitch = ribbon_clamp_hole_pitch(ways, screw);
|
||||
d = ribbon_clamp_width(screw);
|
||||
h = ribbon_clamp_height(screw);
|
||||
t = round_to_layer(ribbon_clamp_slot_depth() + wall);
|
||||
insert = screw_insert(screw);
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
hull() {
|
||||
translate_z(h - t / 2)
|
||||
cube([ribbon_clamp_hole_pitch(ways), d, t], center = true);
|
||||
cube([ribbon_clamp_hole_pitch(ways, screw), d, t], center = true);
|
||||
|
||||
translate_z(1)
|
||||
cube([pitch, max(wall, d - 2 * (h - t)), 2], center = true);
|
||||
}
|
||||
ribbon_clamp_hole_positions(ways, -1)
|
||||
ribbon_clamp_hole_positions(ways, screw, -1)
|
||||
cylinder(d = d, h = h);
|
||||
|
||||
ribbon_clamp_hole_positions(ways, 1)
|
||||
ribbon_clamp_hole_positions(ways, screw, 1)
|
||||
cylinder(d = d, h = h);
|
||||
|
||||
}
|
||||
|
||||
translate_z(h)
|
||||
cube([ribbon_clamp_slot(ways), d + 1, ribbon_clamp_slot_depth() * 2], center = true);
|
||||
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
translate_z(h)
|
||||
rotate(22.5)
|
||||
insert_hole(insert, screw_depth - insert_length(insert));
|
||||
insert_hole(insert, ribbon_clamp_screw_depth(screw) - insert_length(insert));
|
||||
}
|
||||
}
|
||||
|
||||
module ribbon_clamp_assembly(ways) pose([55, 180, 25]) //! Printed part with inserts in place
|
||||
assembly(str("ribbon_clamp_", ways)) {
|
||||
h = ribbon_clamp_height();
|
||||
module ribbon_clamp_assembly(ways, screw) pose([55, 180, 25]) //! Printed part with inserts in place
|
||||
assembly(let(screw_d = screw_radius(screw) * 2)str("ribbon_clamp_", ways, screw_d != 3 ? str("_", screw_d) : "")) {
|
||||
h = ribbon_clamp_height(screw);
|
||||
insert = screw_insert(screw);
|
||||
|
||||
stl_colour(pp1_colour) render()
|
||||
translate_z(h) vflip() ribbon_clamp(ways);
|
||||
translate_z(h) vflip() ribbon_clamp(ways, screw);
|
||||
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
vflip()
|
||||
insert(insert);
|
||||
}
|
||||
@@ -99,20 +102,24 @@ module ribbon_clamp_fastened_assembly(ways, thickness, screw = screw) { //! Clam
|
||||
vitamin(str(": Tape self amalgamating silicone ",tape_l," x 25mm"));
|
||||
|
||||
washer = screw_washer(screw);
|
||||
screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + screw_depth);
|
||||
screw_length = screw_shorter_than(2 * washer_thickness(washer) + thickness + ribbon_clamp_screw_depth(screw));
|
||||
|
||||
ribbon_clamp_assembly(ways);
|
||||
ribbon_clamp_assembly(ways, screw);
|
||||
|
||||
color("red") translate_z(tape_thickness / 2)
|
||||
cube([tape_l, tape_width, tape_thickness], center = true);
|
||||
|
||||
ribbon_clamp_hole_positions(ways)
|
||||
ribbon_clamp_hole_positions(ways, screw)
|
||||
vflip()
|
||||
translate_z(thickness)
|
||||
screw_and_washer(screw, screw_length, true);
|
||||
}
|
||||
|
||||
module ribbon_clamp_20_stl() ribbon_clamp(20);
|
||||
module ribbon_clamp_8_2_stl() ribbon_clamp(8, M2_dome_screw);
|
||||
|
||||
//! * Place inserts into the holes and press home with a soldering iron with a conical bit heated to 200°C.
|
||||
module ribbon_clamp_20_assembly() ribbon_clamp_assembly(20);
|
||||
|
||||
//! * Place inserts into the holes and press home with a soldering iron with a conical bit heated to 200°C.
|
||||
module ribbon_clamp_8_2_assembly() ribbon_clamp_assembly(8, M2_dome_screw);
|
||||
|
@@ -135,7 +135,7 @@ module ssr_shroud_fastened_assembly(type, cable_d, thickness, name) //! Assembly
|
||||
|
||||
*translate_z(cable_d / 2)
|
||||
rotate([90, 0, 0])
|
||||
stl_colour(grey20)
|
||||
stl_colour(grey(20))
|
||||
cylinder(d = cable_d, h = 20, center = true);
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ import c14n_stl
|
||||
from set_config import *
|
||||
from deps import *
|
||||
from shutil import copyfile
|
||||
import re
|
||||
|
||||
source_dirs = { "stl" : "platters", "dxf" : "panels" }
|
||||
target_dirs = { "stl" : "printed", "dxf" : "routed" }
|
||||
@@ -38,43 +39,54 @@ def plateup(target, part_type, usage = None):
|
||||
top_dir = set_config(target, usage)
|
||||
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)
|
||||
source_dir1 = source_dirs[part_type]
|
||||
source_dir2 = top_dir + source_dirs[part_type]
|
||||
#
|
||||
# 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
|
||||
# Loop through source directories
|
||||
#
|
||||
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_silent("-D$bom=1", "-o", log_name, src_file)
|
||||
all_sources = []
|
||||
for dir in [source_dir1, source_dir2]:
|
||||
if not os.path.isdir(dir):
|
||||
continue
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
#
|
||||
# Add the files on the BOM to the used list
|
||||
# Make the deps dir
|
||||
#
|
||||
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])
|
||||
deps_dir = dir + "/deps"
|
||||
if not os.path.isdir(deps_dir):
|
||||
os.makedirs(deps_dir)
|
||||
#
|
||||
# Decide which files to make
|
||||
#
|
||||
sources = [file for file in os.listdir(dir) if file.endswith('.scad')]
|
||||
all_sources += sources
|
||||
#
|
||||
# Run OpenSCAD on the source files to make the targets
|
||||
#
|
||||
for src in sources:
|
||||
src_file = 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_silent("-D$bom=1", "-o", log_name, src_file)
|
||||
#
|
||||
# Add the files on the BOM to the used list
|
||||
#
|
||||
with open(log_name) as file:
|
||||
for line in file.readlines():
|
||||
match = re.match(r'^ECHO: "~(.*?\.' + part_type + r').*"$', line)
|
||||
if match:
|
||||
used.append(match.group(1))
|
||||
#
|
||||
# Copy file that are not included
|
||||
#
|
||||
@@ -90,7 +102,7 @@ def plateup(target, part_type, usage = None):
|
||||
#
|
||||
# Remove any cruft
|
||||
#
|
||||
targets = [file[:-4] + part_type for file in sources]
|
||||
targets = [file[:-4] + part_type for file in all_sources]
|
||||
for file in os.listdir(target_dir):
|
||||
if file.endswith('.' + part_type):
|
||||
if not file in targets and not file in copied:
|
||||
|
@@ -97,7 +97,6 @@ def tests(tests):
|
||||
for dir in [deps_dir, png_dir, bom_dir]:
|
||||
if not os.path.isdir(dir):
|
||||
os.makedirs(dir)
|
||||
doc_name = "readme.md"
|
||||
index = {}
|
||||
bodies = {}
|
||||
done = []
|
||||
@@ -108,19 +107,33 @@ def tests(tests):
|
||||
#
|
||||
png_name = "libtest.png"
|
||||
scad_name = "libtest.scad"
|
||||
if not os.path.isfile(png_name):
|
||||
openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name);
|
||||
do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name])
|
||||
if os.path.isfile(scad_name):
|
||||
libtest = True
|
||||
lib_blurb = scrape_blurb(scad_name)
|
||||
if not os.path.isfile(png_name):
|
||||
openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name);
|
||||
do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name])
|
||||
else:
|
||||
#
|
||||
# Project tests so just a title
|
||||
#
|
||||
libtest = False
|
||||
project = ' '.join(word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_'))
|
||||
lib_blurb = '#' + project + ' Tests\n'
|
||||
|
||||
doc_base_name = "readme" if libtest else "tests"
|
||||
doc_name = doc_base_name + ".md"
|
||||
#
|
||||
# List of individual part files
|
||||
#
|
||||
scads = [i for i in sorted(os.listdir(scad_dir), key = lambda s: s.lower()) if i[-5:] == ".scad"]
|
||||
|
||||
scads = [i for i in sorted(os.listdir(scad_dir), key = lambda s: s.lower()) if i[-5:] == ".scad"]
|
||||
types = []
|
||||
for scad in scads:
|
||||
base_name = scad[:-5]
|
||||
if not tests or base_name in tests:
|
||||
done.append(base_name)
|
||||
print(base_name)
|
||||
print('\n'+base_name)
|
||||
cap_name = base_name[0].capitalize() + base_name[1:]
|
||||
base_name = base_name.lower()
|
||||
scad_name = scad_dir + '/' + scad
|
||||
@@ -132,29 +145,42 @@ def tests(tests):
|
||||
if is_plural(base_name) and os.path.isfile(vits_name):
|
||||
objects_name = vits_name
|
||||
|
||||
locations = [
|
||||
('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'),
|
||||
('printed/' + base_name + '.scad', 'Printed'),
|
||||
('utils/' + base_name + '.scad', 'Utilities'),
|
||||
('utils/core/' + base_name + '.scad', 'Core Utilities'),
|
||||
]
|
||||
locations = []
|
||||
if os.path.isdir('vitamins'):
|
||||
locations.append(('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'))
|
||||
if os.path.isdir('printed'):
|
||||
locations.append(('printed/' + base_name + '.scad', 'Printed'))
|
||||
if os.path.isdir('utils'):
|
||||
locations.append(('utils/' + base_name + '.scad', 'Utilities'))
|
||||
if libtest and os.path.isdir('utils/core'):
|
||||
locations.append(('utils/core/' + base_name + '.scad', 'Core Utilities'))
|
||||
|
||||
for name, type in locations:
|
||||
if os.path.isfile(name):
|
||||
impl_name = name
|
||||
break
|
||||
else:
|
||||
print("Can't find implementation!")
|
||||
continue
|
||||
if libtest:
|
||||
print("Can't find implementation!")
|
||||
continue
|
||||
else:
|
||||
type = 'Tests' # OK when testing part of a project
|
||||
impl_name = None
|
||||
|
||||
vsplit = "AJR" + chr(ord('Z') + 1)
|
||||
vtype = locations[0][1]
|
||||
types = [vtype + ' ' + vsplit[i] + '-' + chr(ord(vsplit[i + 1]) - 1) for i in range(len(vsplit) - 1)] + [loc[1] for loc in locations[1 :]]
|
||||
if type == vtype:
|
||||
for i in range(1, len(vsplit)):
|
||||
if cap_name[0] < vsplit[i]:
|
||||
type = types[i - 1]
|
||||
break
|
||||
if libtest:
|
||||
vsplit = "AJR" + chr(ord('Z') + 1)
|
||||
vtype = locations[0][1]
|
||||
types = [vtype + ' ' + vsplit[i] + '-' + chr(ord(vsplit[i + 1]) - 1) for i in range(len(vsplit) - 1)] + [loc[1] for loc in locations[1 :]]
|
||||
if type == vtype:
|
||||
for i in range(1, len(vsplit)):
|
||||
if cap_name[0] < vsplit[i]:
|
||||
type = types[i - 1]
|
||||
break
|
||||
else:
|
||||
if not types:
|
||||
types = [loc[1] for loc in locations] # No need to split up the vitamin list
|
||||
if not type in types: # Will happen when implementation is not found and type is set to Tests
|
||||
types.append(type)
|
||||
|
||||
for t in types:
|
||||
if not t in bodies:
|
||||
@@ -250,24 +276,7 @@ def tests(tests):
|
||||
usage()
|
||||
|
||||
with open(doc_name, "wt") as doc_file:
|
||||
print('# NopSCADlib', file = doc_file)
|
||||
print('''\
|
||||
An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc.
|
||||
|
||||
It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and
|
||||
some utilities. There are also Python scripts to generate Bills of Materials (BOMs),
|
||||
STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
|
||||
instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md). A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
|
||||
|
||||
For more examples of what it can make see the [gallery](gallery/readme.md).
|
||||
|
||||
The license is GNU General Public License v3.0, see [COPYING](COPYING).
|
||||
|
||||
See [usage](docs/usage.md) for requirements, installation instructions and a usage guide.
|
||||
|
||||
<img src="libtest.png" width="100%"/>\n
|
||||
''', file = doc_file)
|
||||
|
||||
print(lib_blurb, file = doc_file)
|
||||
print('## Table of Contents<a name="top"/>', file = doc_file)
|
||||
print('<table><tr>', file = doc_file)
|
||||
n = 0
|
||||
@@ -288,10 +297,10 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa
|
||||
for type in types:
|
||||
for line in bodies[type]:
|
||||
print(line, file = doc_file)
|
||||
with open("readme.html", "wt") as html_file:
|
||||
do_cmd("python -m markdown -x tables readme.md".split(), html_file)
|
||||
with open(doc_base_name + ".html", "wt") as html_file:
|
||||
do_cmd(("python -m markdown -x tables " + doc_name).split(), html_file)
|
||||
times.print_times()
|
||||
do_cmd('codespell -L od readme.md'.split())
|
||||
do_cmd(('codespell -L od ' + doc_name).split())
|
||||
|
||||
if __name__ == '__main__':
|
||||
for arg in sys.argv[1:]:
|
||||
|
@@ -34,7 +34,7 @@ TMC2130 = ["TMC2130", "TMC2130",
|
||||
[
|
||||
[ 10, 1, 0, "-2p54header", 8, 1 ,undef, "blue" ],
|
||||
[ 10, 13, 0, "-2p54header", 8, 1],
|
||||
[ 12, 7, 0, "-chip", 6, 4, 1, grey20 ],
|
||||
[ 12, 7, 0, "-chip", 6, 4, 1, grey(20) ],
|
||||
// mock up a heat sink
|
||||
[ 10, 7, 0, "block", 9, 9, 2, TMC2130HeatSinkColor ],
|
||||
[ 10, 11, 0, "block", 9, 1, 11, TMC2130HeatSinkColor ],
|
||||
@@ -58,14 +58,18 @@ test_pcb = ["TestPCB", "Test PCB",
|
||||
// components
|
||||
[
|
||||
[ 20, -5, 180, "trimpot10"],
|
||||
[ 20, -15, 0, "trimpot10", true],
|
||||
[ 10, 2, 0, "smd_led", LED0805, "red"],
|
||||
[ 20, -15, 90, "trimpot10", true],
|
||||
[ 10, 2, 90, "smd_led", LED0805, "red"],
|
||||
[ 13, 2, 90, "smd_led", LED0603, "orange"],
|
||||
[ 16, 2, 90, "smd_res", RES1206, "1K"],
|
||||
[ 19, 2, 90, "smd_res", RES0805, "1K"],
|
||||
[ 22, 2, 90, "smd_res", RES0603, "1K"],
|
||||
[ 10, 10, 0, "2p54header", 4, 1],
|
||||
[ 25, 10, 0, "2p54header", 5, 1, undef, "blue" ],
|
||||
[ 25, 10, 0, "2p54header", 5, 1, false, "blue" ],
|
||||
[ 10, 20, 0, "2p54boxhdr", 4, 2],
|
||||
[ 10, 30, 0, "2p54socket", 6, 1],
|
||||
[ 25, 30, 0, "2p54socket", 4, 1, undef, undef, undef, "red" ],
|
||||
[ 10, 40, 0, "chip", 10, 5, 1, grey20],
|
||||
[ 25, 30, 0, "2p54socket", 4, 1, false, 0, false, "red" ],
|
||||
[ 10, 40, 0, "chip", 10, 5, 1, grey(20)],
|
||||
[ 5, 50, 0, "led", LED3mm, "red"],
|
||||
[ 12, 50, 0, "led", LED5mm, "orange"],
|
||||
[ 25, 50, 0, "led", LED10mm, "yellow"],
|
||||
@@ -82,22 +86,26 @@ test_pcb = ["TestPCB", "Test PCB",
|
||||
[ 5, 218, 180, "hdmi"],
|
||||
[ 3, 235, 180, "mini_hdmi"],
|
||||
[ 6, 175, 180, "uSD", [12, 11.5, 1.4]],
|
||||
|
||||
[ 65, 9, 0, "link", inch(0.4)],
|
||||
[ 65, 12, 0, "ax_res", res1_8, 1000],
|
||||
[ 65, 17, 0, "ax_res", res1_4, 10000],
|
||||
[ 65, 22, 0, "ax_res", res1_2, 100000],
|
||||
|
||||
[ 80, 9, 0, "link", inch(0.2), inch(0.4)],
|
||||
[ 80, 12, 0, "ax_res", res1_8, 1000000, 1, inch(0.1)],
|
||||
[ 80, 17, 0, "ax_res", res1_4, 100, 2, inch(0.1)],
|
||||
[ 80, 22, 0, "ax_res", res1_2, 10, 10, inch(0.2)],
|
||||
|
||||
[ 60, 3, 0, "flex"],
|
||||
[ 50, 15, 0, "flat_flex"],
|
||||
[ 50, 15, -90, "flat_flex"],
|
||||
[ 40, 15, -90, "flat_flex", true],
|
||||
[ 60, 35, 0, "D_plug", DCONN9],
|
||||
|
||||
[ 50, 50, 0, "molex_hdr", 2],
|
||||
[ 50, 60, 0, "jst_xh", 2],
|
||||
[ 50, 70, 180, "term254", 3],
|
||||
[ 63, 70, 180, "term254", 3, undef, grey20],
|
||||
[ 63, 70, 180, "term254", 3, undef, grey(20)],
|
||||
[ 75, 70, 180, "gterm508",2, undef, "blue"],
|
||||
|
||||
[ 50, 90, 180, "gterm35", 4, [1,2]],
|
||||
@@ -107,19 +115,20 @@ test_pcb = ["TestPCB", "Test PCB",
|
||||
|
||||
[ 55, 110, 180, "gterm635", 2],
|
||||
[ 75, 110, 180, "gterm635", 2, undef, "blue"],
|
||||
[ 90, 110, 180, "gterm", gt_5x17, 2, undef, grey20],
|
||||
[ 90, 110, 180, "gterm", gt_5x17, 2, undef, grey(20)],
|
||||
|
||||
[ 50, 130, 180, "term35", 4],
|
||||
[ 70, 130, 180, "term35", 3, "lime"],
|
||||
[ 50, 150, 0, "transition", 5],
|
||||
[ 50, 160, 0, "block", 10, 5, 8, "orange"],
|
||||
[ 50, 170, 0, "button_6mm"],
|
||||
[ 45, 170, 0, "button_6mm"],
|
||||
[ 55, 170, 0, "button_4p5mm"],
|
||||
[ 50, 185, 0, "microswitch", small_microswitch],
|
||||
[ 52, 200, 0, "pcb", 11, TMC2130 ],
|
||||
[ 80, 200, 0, "pdip", 24, "27C32", true, inch(0.6) ],
|
||||
[ 80, 170, 0, "pdip", 8, "NE555" ],
|
||||
[ 52, 206, 0, "2p54socket", 8, 1 ],
|
||||
[ 52, 194, 0, "2p54socket", 8, 1, undef, undef, undef, "red" ],
|
||||
[ 52, 194, 0, "2p54socket", 8, 1, false, 0, false, "red" ],
|
||||
[ 50, 220, 0, "standoff", 5, 4.5, 12.5, 2.54],
|
||||
[ 50, 240, 0, "potentiometer"],
|
||||
[ 75, 240, 0, "potentiometer", 7, 8],
|
||||
|
@@ -21,9 +21,14 @@ use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/smds.scad>
|
||||
|
||||
module smds()
|
||||
layout([for(l = smd_leds) smd_led_size(l).x], 1)
|
||||
smd_led(smd_leds[$i], ["green", "blue", "red"][$i % 3]);
|
||||
module smds() {
|
||||
layout([for(r = smd_resistors) smd_res_size(r).x], 1)
|
||||
smd_resistor(smd_resistors[$i], ["1R0", "10M", "100K"][$i % 3]);
|
||||
|
||||
translate([0, 3])
|
||||
layout([for(l = smd_leds) smd_led_size(l).x], 1)
|
||||
smd_led(smd_leds[$i], ["green", "blue", "red"][$i % 3]);
|
||||
}
|
||||
|
||||
if($preview)
|
||||
smds();
|
||||
|
@@ -60,12 +60,12 @@ module belt_test() {
|
||||
[p4.x, p4.y, pulley_pr(GT2x20ob_pulley)]
|
||||
];
|
||||
belt = GT2x6;
|
||||
belt(belt, path, 80, [0, belt_pitch_height(belt) - belt_thickness(belt) / 2]);
|
||||
belt(belt, path, 80, [0, 0]);
|
||||
|
||||
translate([-25, 0])
|
||||
layout([for(b = belts) belt_width(b)], 10)
|
||||
rotate([0, 90, 0])
|
||||
belt(belts[$i], [[0, 0, 20], [0, 1, 20]], belt_colour = $i%2==0 ? grey90 : grey20, tooth_colour = $i%2==0 ? grey70 : grey50);
|
||||
belt(belts[$i], [[0, 0, 20], [0, 1, 20]], belt_colour = $i%2==0 ? grey(90) : grey(20), tooth_colour = $i%2==0 ? grey(70) : grey(50));
|
||||
}
|
||||
|
||||
if($preview)
|
||||
|
31
tests/cameras.scad
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/cameras.scad>
|
||||
|
||||
use <../vitamins/pcb.scad>
|
||||
|
||||
module cameras()
|
||||
layout([for(c = cameras) pcb_length(camera_pcb(c))], 10, false) let(c = cameras[$i])
|
||||
camera(c);
|
||||
|
||||
if($preview)
|
||||
cameras();
|
56
tests/catenary.scad
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
l = 250; // [1: 1000]
|
||||
x = 200; // [1: 1000]
|
||||
y = 50; //[-500 : 500]
|
||||
|
||||
include <../utils/core/core.scad>
|
||||
use <../utils/catenary.scad>
|
||||
use <../utils/sweep.scad>
|
||||
use <../utils/annotation.scad>
|
||||
|
||||
module catenaries() {
|
||||
//
|
||||
// catenary curve path from control points
|
||||
//
|
||||
curve = [for(p = catenary_points(l, x, y)) [p.x, p.y, 0]];
|
||||
//
|
||||
// Draw the curve
|
||||
//
|
||||
r = 0.5;
|
||||
sweep(curve, circle_points(r, $fn = 64));
|
||||
//
|
||||
// Minimum Z
|
||||
//
|
||||
min_z = catenary_points(l, x, y, 0);
|
||||
|
||||
color("blue") {
|
||||
translate([min_z.x, min_z.y + r])
|
||||
rotate([-90, 0, 0])
|
||||
arrow();
|
||||
|
||||
translate([min_z.x, min_z.y - r])
|
||||
rotate([90, 0, 0])
|
||||
arrow();
|
||||
}
|
||||
}
|
||||
|
||||
if($preview)
|
||||
rotate(is_undef($bom) ? 0 : [70, 0, 315])
|
||||
catenaries();
|
@@ -41,9 +41,12 @@ module components() {
|
||||
translate([0, 50])
|
||||
TO220("Generic TO220 package");
|
||||
|
||||
translate([50, 50])
|
||||
translate([40, 50])
|
||||
panel_USBA();
|
||||
|
||||
translate([80, 50])
|
||||
fack2spm();
|
||||
|
||||
translate([0,80])
|
||||
thermal_cutouts();
|
||||
|
||||
|
@@ -21,14 +21,14 @@ use <../printed/foot.scad>
|
||||
|
||||
module feet()
|
||||
if($preview) {
|
||||
translate([50, 0])
|
||||
translate([40, 0])
|
||||
foot_assembly(3);
|
||||
|
||||
translate([foot_diameter(insert_foot()) / 2, 0])
|
||||
fastened_insert_foot_assembly(3);
|
||||
}
|
||||
else {
|
||||
translate([50, 0])
|
||||
translate([40, 0])
|
||||
foot();
|
||||
|
||||
insert_foot();
|
||||
|
66
tests/gears.scad
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
use <../utils/gears.scad>
|
||||
|
||||
// left gear teeth
|
||||
z1 = 39; // [7 : 1 : 99]
|
||||
|
||||
// Right gear teeth
|
||||
z2 = 7; // [7 : 1 : 99]
|
||||
|
||||
// Modulus
|
||||
m = 2.0; // [0.1 : 0.1 : 5.0]
|
||||
|
||||
// Pressure angle
|
||||
pa = 20; // [14.5, 20, 22.5, 25]
|
||||
|
||||
$show_numbers = false;
|
||||
|
||||
module gears() {
|
||||
color(pp1_colour)
|
||||
rotate(-$t * 360)
|
||||
linear_extrude(eps, center = true, convexity = z1)
|
||||
difference() {
|
||||
involute_gear_profile(m, z1, pa);
|
||||
|
||||
circle(r = m * z1 / 10);
|
||||
}
|
||||
|
||||
color(pp2_colour)
|
||||
translate([centre_distance(m, z1, z2, pa), 0])
|
||||
rotate(180 + 180 / z2 + $t * 360 * z1 / z2)
|
||||
linear_extrude(eps, center = true, convexity = z2)
|
||||
difference() {
|
||||
involute_gear_profile(m, z2, pa);
|
||||
|
||||
circle(r = m * z2 / 10);
|
||||
}
|
||||
|
||||
z3 = floor((z1 + z2) / PI);
|
||||
angle = -$t * 360 + 90 - floor(z1 / 4) * 360 / z1; // Line up the rack 1/4 turn around the gear
|
||||
pitch = m * PI;
|
||||
color(pp3_colour)
|
||||
translate([(angle % ((z3 / z1) * 360)) / 360 * z1 * pitch, -centre_distance(m, z1, 0, pa)])
|
||||
linear_extrude(eps, center = true)
|
||||
involute_rack_profile(m, z3, 3 * m, pa);
|
||||
}
|
||||
|
||||
rotate(is_undef($bom) ? 0 : [70, 0, 315])
|
||||
gears();
|
@@ -29,6 +29,23 @@ module globals() {
|
||||
translate([50, 0])
|
||||
right_triangle(10, 20, 0);
|
||||
}
|
||||
assert(slice("ABCD") == "ABCD");
|
||||
assert(slice("ABCD", 1) == "BCD");
|
||||
assert(slice("ABCD", 2) == "CD");
|
||||
assert(slice("ABCD", 3) == "D");
|
||||
assert(slice("ABCD", 4) == "");
|
||||
assert(slice("ABCD", 1, -1) == "BC");
|
||||
assert(slice("ABCD", 2, -1) == "C");
|
||||
assert(slice("ABCD", 3, -1) == "");
|
||||
assert(slice("ABCD", 4, -1) == "");
|
||||
assert(slice("ABCD", 0, -1) == "ABC");
|
||||
assert(slice("ABCD", 0, -2) == "AB");
|
||||
assert(slice("ABCD", 0, -3) == "A");
|
||||
assert(slice("ABCD", 0, -4) == "");
|
||||
assert(slice("ABCD", 0, 0) == "");
|
||||
assert(slice("ABCD", 0, 1) == "A");
|
||||
assert(slice("ABCD", 0, 2) == "AB");
|
||||
assert(slice("ABCD", 0, 3) == "ABC");
|
||||
}
|
||||
|
||||
rotate([70, 0, 315]) globals();
|
||||
|
94
tests/horiholes.scad
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
$layer_height = 0.25;
|
||||
include <../utils/core/core.scad>
|
||||
use <../utils/horiholes.scad>
|
||||
|
||||
show_disc = true;
|
||||
use_horihole = true;
|
||||
thickness = 6;
|
||||
length = 60;
|
||||
height = 20;
|
||||
overlap_x = 15;
|
||||
overlap_y = 10;
|
||||
|
||||
module hole_positions() {
|
||||
x0 = (length - 40) / 2;
|
||||
for($i = [0 : 4], $z = 5 + $i * layer_height / 5, $r = 3)
|
||||
translate([x0 + $i * 10, $z])
|
||||
children();
|
||||
|
||||
for($i = [0 : 4], $z = 15 + $i * layer_height / 5, $r = 0.5 + $i / 2)
|
||||
translate([x0 + $i * 10, $z])
|
||||
children();
|
||||
}
|
||||
|
||||
module horiholes_stl(t = thickness) {
|
||||
rotate([90, 0, 0])
|
||||
difference() {
|
||||
linear_extrude(t, center = true) {
|
||||
difference() {
|
||||
square([length, height]);
|
||||
|
||||
hole_positions()
|
||||
if(use_horihole)
|
||||
horihole($r, $z);
|
||||
else
|
||||
teardrop_plus(h = 0, r = $r);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(t == thickness)
|
||||
translate([length / 2, 0])
|
||||
rounded_rectangle([length + 2 * overlap_x, thickness + 2 * overlap_y, 2], 5);
|
||||
}
|
||||
|
||||
module horiholes() {
|
||||
stl_colour(pp1_colour)
|
||||
rotate([-90, 0, 0])
|
||||
horiholes_stl(eps);
|
||||
|
||||
if(show_disc)
|
||||
hole_positions()
|
||||
color(silver)
|
||||
cylinder(r = $r, h = eps, center = true, $fn = 360);
|
||||
|
||||
hole_positions()
|
||||
color("blue")
|
||||
horicylinder(r = $r, z = $z, h = 2 * eps, center = true, $fn = 360);
|
||||
|
||||
hole_positions()
|
||||
color("red")
|
||||
linear_extrude(3 * eps, center = true)
|
||||
intersection() {
|
||||
difference() {
|
||||
square(8, center = true);
|
||||
|
||||
horihole($r, $z);
|
||||
}
|
||||
|
||||
circle($r, $fn = 360);
|
||||
}
|
||||
}
|
||||
|
||||
if($preview)
|
||||
rotate(is_undef($bom) ? 0 : [70, 0, 315])
|
||||
horiholes();
|
||||
else
|
||||
horiholes_stl();
|
@@ -25,7 +25,7 @@ module hot_ends()
|
||||
layout([for(h = hot_ends) 40])
|
||||
translate([-20, 0])
|
||||
rotate(90)
|
||||
hot_end(hot_ends[$i], 3);
|
||||
hot_end(hot_ends[$i], 3, bowden = $i == 3);
|
||||
|
||||
if($preview)
|
||||
hot_ends();
|
||||
|
29
tests/magnets.scad
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
include <../vitamins/magnets.scad>
|
||||
|
||||
module magnets()
|
||||
layout([for(m = magnets) magnet_od(m)], 5)
|
||||
magnet(magnets[$i]);
|
||||
|
||||
if($preview)
|
||||
magnets();
|
@@ -69,6 +69,33 @@ module maths() {
|
||||
// Test Euler
|
||||
//
|
||||
assert(euler(rotate(r)) == r, "euler() failed");
|
||||
//
|
||||
// Circle intersect
|
||||
//
|
||||
r1 = 10;
|
||||
c1 = [50, 0, 10];
|
||||
r2 = 20;
|
||||
c2 = [67, 0, 0];
|
||||
p1 = circle_intersect(c1, r1, c2, r2);
|
||||
p2 = circle_intersect(c2, r2, c1, r1);
|
||||
|
||||
rotate(90) {
|
||||
color(grey(90))
|
||||
translate(c1) rotate([90, 0, 0]) cylinder(r = r1, h = 4 * eps, center = true);
|
||||
|
||||
color(grey(80))
|
||||
translate(c2) rotate([90, 0, 0]) cylinder(r = r2, h = eps, center = true);
|
||||
|
||||
color("red")
|
||||
translate(p1) rotate([90, 0, 0]) cylinder(r = 0.1, h = 6 * eps, center = true);
|
||||
|
||||
color("blue")
|
||||
translate(p2) rotate([90, 0, 0]) cylinder(r = 0.1, h = 6 * eps, center = true);
|
||||
|
||||
translate(p1) arrow();
|
||||
|
||||
translate(p2) vflip() arrow();
|
||||
}
|
||||
}
|
||||
|
||||
rotate(45)
|
||||
|
@@ -21,11 +21,11 @@ include <../utils/core/core.scad>
|
||||
use <../vitamins/opengrab.scad>
|
||||
|
||||
module opengrab_test() {
|
||||
opengrab_target();
|
||||
|
||||
rotate(45)
|
||||
translate_z(opengrab_target_thickness())
|
||||
opengrab();
|
||||
opengrab_target();
|
||||
|
||||
translate_z(opengrab_target_thickness())
|
||||
opengrab();
|
||||
}
|
||||
|
||||
if($preview)
|
||||
|
@@ -22,7 +22,7 @@ use <../utils/layout.scad>
|
||||
include <../vitamins/panel_meters.scad>
|
||||
|
||||
module panel_meters()
|
||||
layout([for(p = panel_meters) pmeter_size(p).x], 20)
|
||||
layout([for(p = panel_meters) pmeter_bezel(p).x], 10)
|
||||
panel_meter(panel_meters[$i]);
|
||||
|
||||
if($preview)
|
||||
|
@@ -23,26 +23,56 @@ include <../vitamins/pin_headers.scad>
|
||||
|
||||
pins = 10;
|
||||
|
||||
module pin_headers()
|
||||
module pin_headers() {
|
||||
layout([for(p = pin_headers) hdr_pitch(p) * pins], 15) {
|
||||
idc_transition(pin_headers[$i], 10);
|
||||
|
||||
translate([0, 20])
|
||||
pin_header(pin_headers[$i], 10, 2, right_angle = true);
|
||||
pin_header(pin_headers[$i], 3, 2, right_angle = true);
|
||||
|
||||
translate([-10, 20])
|
||||
pin_header(pin_headers[$i], 3, 1, right_angle = true);
|
||||
|
||||
translate([10, 20])
|
||||
pin_header(pin_headers[$i], 3, 3, right_angle = true);
|
||||
|
||||
translate([0, 30])
|
||||
pin_header(pin_headers[$i], 8, 1);
|
||||
|
||||
translate([0, 40])
|
||||
pin_header(pin_headers[$i], 10, 2);
|
||||
|
||||
translate([0, 50])
|
||||
box_header(pin_headers[$i], 8, 1);
|
||||
|
||||
translate([0, 60])
|
||||
box_header(pin_headers[$i], 10, 2);
|
||||
|
||||
translate([0, 70])
|
||||
pin_socket(pin_headers[$i], 8, 1);
|
||||
|
||||
translate([0, 80])
|
||||
pin_socket(pin_headers[$i], 10, 2);
|
||||
|
||||
translate([0, 110])
|
||||
pin_socket(pin_headers[$i], 10, 2, right_angle = true);
|
||||
translate([-10, 105])
|
||||
pin_socket(pin_headers[$i], 3, 1, right_angle = true);
|
||||
|
||||
translate([0, 105])
|
||||
pin_socket(pin_headers[$i], 3, 2, right_angle = true);
|
||||
|
||||
translate([10, 105])
|
||||
pin_socket(pin_headers[$i], 3, 3, right_angle = true);
|
||||
}
|
||||
|
||||
for(i = [0, 1], p = [5, 2][i], j = [0 , 1]) {
|
||||
h = [jst_ph_header, jst_xh_header][j];
|
||||
translate([-20 * (i + 1), 0 + j * 40])
|
||||
jst_xh_header(h, p);
|
||||
|
||||
translate([-20 * (i + 1), 20 + j * 40])
|
||||
jst_xh_header(h, p, true);
|
||||
}
|
||||
}
|
||||
|
||||
if($preview)
|
||||
pin_headers();
|
||||
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 251 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
BIN
tests/png/cameras.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
tests/png/catenary.png
Normal file
After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 102 KiB |
BIN
tests/png/gears.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
tests/png/horiholes.png
Normal file
After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
BIN
tests/png/magnets.png
Normal file
After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 174 KiB |
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 188 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 219 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 72 KiB |
@@ -67,7 +67,7 @@ module box1_external_additions() {
|
||||
|
||||
module box1_holes() {
|
||||
box1_feet_positions()
|
||||
teardrop(r = screw_pilot_hole(foot_screw(foot)), h = 10, center = true);
|
||||
teardrop_plus(r = screw_pilot_hole(foot_screw(foot)), h = 10, center = true);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -25,15 +25,15 @@ use <../vitamins/nut.scad>
|
||||
sheet = 3;
|
||||
|
||||
module rails()
|
||||
layout([for(l = rails) carriage_width(rail_carriage(l))], 25)
|
||||
layout([for(l = rails) carriage_width(rail_carriage(l))], 20)
|
||||
rotate(-90) {
|
||||
rail = rails[$i];
|
||||
length = rail == MGN15 ? 260 : 200;
|
||||
length = 200;
|
||||
screw = rail_screw(rail);
|
||||
nut = screw_nut(screw);
|
||||
washer = screw_washer(screw);
|
||||
|
||||
rail_assembly(rail, length, rail_travel(rail, length) / 2, $i<2 ? grey20 : "green", $i<2 ? grey20 : "red");
|
||||
rail_assembly(rail, length, rail_travel(rail, length) / 2, $i<2 ? grey(20) : "green", $i<2 ? grey(20) : "red");
|
||||
|
||||
rail_screws(rail, length, sheet + nut_thickness(nut, true) + washer_thickness(washer));
|
||||
|
||||
|
@@ -16,20 +16,22 @@
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
include <../core.scad>
|
||||
use <../printed/ribbon_clamp.scad>
|
||||
use <../vitamins/wire.scad>
|
||||
|
||||
ways = 20;
|
||||
ways = [8, 20];
|
||||
screws = [M2_dome_screw, M3_cap_screw];
|
||||
|
||||
module ribbon_clamps()
|
||||
translate([ribbon_clamp_length(ways) / 2, 0])
|
||||
if($preview) {
|
||||
ribbon_clamp_fastened_assembly(ways, 3);
|
||||
for(i = [0 : len(screws) - 1])
|
||||
translate([ribbon_clamp_length(ways[i]) / 2, i * 30])
|
||||
if($preview) {
|
||||
ribbon_clamp_fastened_assembly(ways[i], 3, screws[i]);
|
||||
|
||||
ribbon_cable(ways, 100);
|
||||
}
|
||||
else
|
||||
ribbon_clamp(ways);
|
||||
ribbon_cable(ways[i], 100);
|
||||
}
|
||||
else
|
||||
ribbon_clamp(ways[i], screws[i]);
|
||||
|
||||
ribbon_clamps();
|
||||
|
@@ -22,12 +22,12 @@ include <../vitamins/stepper_motors.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module stepper_motors()
|
||||
layout([for(s = stepper_motors) NEMA_width(s)], 5) {
|
||||
layout([for(s = stepper_motors) NEMA_width(s)], 5) let(m = stepper_motors[$i]) {
|
||||
rotate(180)
|
||||
NEMA(stepper_motors[$i]);
|
||||
NEMA(m, 0, m == NEMA17M || m == NEMA17M8);
|
||||
|
||||
translate_z(4)
|
||||
NEMA_screws(stepper_motors[$i], M3_pan_screw, n = $i, earth = $i > 4 ? undef : $i - 1);
|
||||
NEMA_screws(m, M3_pan_screw, n = $i, earth = $i > 4 ? undef : $i - 1);
|
||||
}
|
||||
|
||||
if($preview)
|
||||
|
@@ -41,7 +41,10 @@ module teardrops() {
|
||||
|
||||
translate([20, 10])
|
||||
semi_teardrop(h = 0, r = 3);
|
||||
}
|
||||
|
||||
translate([20, 20])
|
||||
teardrop(h = 0, r = 3, truncate = false, plus = true);
|
||||
}
|
||||
}
|
||||
translate([40, 0, 1.5]) {
|
||||
h = 3 + eps;
|
||||
@@ -61,6 +64,9 @@ module teardrops() {
|
||||
|
||||
translate([20, 10])
|
||||
semi_teardrop(h = h, r = 3, chamfer = chamfer);
|
||||
|
||||
translate([20, 20])
|
||||
teardrop(h = h, r = 3, truncate = false, plus = false, chamfer = chamfer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,27 +26,27 @@ profile = thread_profile(pitch / 2, pitch * 0.366, 30);
|
||||
|
||||
module threads()
|
||||
for(female = [false, true]) translate([0, female ? -20 : 0]) {
|
||||
length = female ? 8 : 40;
|
||||
dia = female ? 8 : 8 - pitch;
|
||||
colour = female ? brass : silver;
|
||||
length = female ? 8 : 40;
|
||||
dia = female ? 8 : 8 - pitch;
|
||||
colour = female ? brass : silver;
|
||||
|
||||
thread(dia, starts * pitch, length, profile, starts = starts, top = 45, bot = 45, female = female, colour = colour);
|
||||
thread(dia, starts * pitch, length, profile, starts = starts, top = 45, bot = 45, female = female, colour = colour);
|
||||
|
||||
color(colour)
|
||||
translate([20, 0])
|
||||
thread(dia, starts * pitch, length, profile, starts = starts, top = 0, bot = 0, female = female);
|
||||
color(colour)
|
||||
translate([20, 0])
|
||||
thread(dia, starts * pitch, length, profile, starts = starts, top = 0, bot = 0, female = female);
|
||||
|
||||
translate([40, 0])
|
||||
thread(dia, starts * pitch, length, profile, starts = starts, top = -1, bot = -1, female = female, colour = colour);
|
||||
translate([40, 0])
|
||||
thread(dia, starts * pitch, length, profile, starts = starts, top = -1, bot = -1, female = female, colour = colour);
|
||||
|
||||
color(colour)
|
||||
translate([60, 0])
|
||||
thread(dia, 2 * pitch, length, profile, starts = 2, top = -1, bot = -1, female = female);
|
||||
color(colour)
|
||||
translate([60, 0])
|
||||
thread(dia, 2 * pitch, length, profile, starts = 2, top = -1, bot = -1, female = female);
|
||||
|
||||
color(colour)
|
||||
translate([80, 0])
|
||||
thread(dia, pitch, length, profile, starts = 1, top = -1, bot = -1, female = female);
|
||||
}
|
||||
color(colour)
|
||||
translate([80, 0])
|
||||
thread(dia, pitch, length, profile, starts = 1, top = -1, bot = -1, female = female);
|
||||
}
|
||||
|
||||
let($show_threads = true)
|
||||
threads();
|
||||
|
@@ -22,7 +22,7 @@ use <../utils/layout.scad>
|
||||
include <../vitamins/tubings.scad>
|
||||
|
||||
module tubings()
|
||||
layout([for(t = tubings) tubing_od(t)], 10)
|
||||
layout([for(t = tubings) tubing_od(t)], 8)
|
||||
tubing(tubings[$i]);
|
||||
|
||||
if($preview)
|
||||
|
52
utils/catenary.scad
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Catenary curve to model hanging wires, etc.
|
||||
//!
|
||||
//! Although the equation of the curve is simply ```y = a cosh(x / a)``` there is no explicit formula to calculate the constant ```a``` or the range of ```x``` given the
|
||||
//! length of the cable and the end point coordinates. See <https://en.wikipedia.org/wiki/Catenary#Determining_parameters>. The Newton-Raphson method is used to find
|
||||
//! ```a``` numerically, see <https://en.wikipedia.org/wiki/Newton%27s_method>.
|
||||
//!
|
||||
//! The coordinates of the lowest point on the curve can be retrieved by calling ```catenary_points()``` with ```steps``` equal to zero.
|
||||
//
|
||||
include <core/core.scad>
|
||||
use <maths.scad>
|
||||
|
||||
function catenary(t, a) = let(u = argsinh(t)) a * [u, cosh(u)]; //! Parametric catenary function linear along the length of the curve.
|
||||
function catenary_s(d, a) = 2 * a * sinh(d / a); //! Length of a symmetric catenary with width ```2d```.
|
||||
function catenary_ds_by_da(d, a) = 2 * sinh(d / a) - 2 * d / a * cosh(d / a); //! First derivative of the length with respect to ```a```.
|
||||
|
||||
function catenary_find_a(d, l, a = 1, best_e = inf, best_a = 1) = //! Find the catenary constant ```a```, given half the horizontal span and the length.
|
||||
assert(l > 2 * d, "Not long enough to span the gap") assert(d) let(error = abs(catenary_s(d, a) - l))
|
||||
error >= best_e && error < 0.0001 ? best_a
|
||||
: catenary_find_a(d, l, max(a - (catenary_s(d, a) - l) / catenary_ds_by_da(d, a), d / argsinh(1e99)), error, a);
|
||||
|
||||
function catenary_points(l, x, y, steps = 100) = //! Returns a list of 2D points on the curve that goes from the origin to ```(x,y)``` and has length ```l```.
|
||||
let(
|
||||
d = x / 2,
|
||||
a = catenary_find_a(d, sqrt(sqr(l) - sqr(y))), // Find a to get the correct length
|
||||
offset = argsinh(y / catenary_s(d, a)),
|
||||
t0 = sinh(-d / a + offset),
|
||||
t1 = sinh( d / a + offset),
|
||||
h = a * cosh(-d / a + offset) - a,
|
||||
lowest = offset > d / a ? [0, 0] : offset < -d / a ? [x, y] : [d - offset * a, -h],
|
||||
p0 = catenary(t0, a)
|
||||
)
|
||||
steps ? [for(t = [t0 : (t1 - t0) / steps : t1]) catenary(t, a) - p0] : lowest;
|
@@ -18,7 +18,7 @@
|
||||
//
|
||||
|
||||
//
|
||||
// Include this file to use the miniumum library
|
||||
// Include this file to use the minimum library
|
||||
//
|
||||
include <../../global_defs.scad>
|
||||
//
|
||||
@@ -28,12 +28,12 @@ use <global.scad>
|
||||
|
||||
module use_stl(name) { //! Import an STL to make a build platter
|
||||
stl(name);
|
||||
|
||||
import(str("../stls/", name, ".stl"));
|
||||
path = is_undef($target) ? "../stls/" : str("../", $target, "/stls/");
|
||||
import(str(path, name, ".stl"));
|
||||
}
|
||||
|
||||
module use_dxf(name) { //! Import a DXF to make a build panel
|
||||
dxf(name);
|
||||
|
||||
import(str("../dxfs/", name, ".dxf"));
|
||||
path = is_undef($target) ? "../dxfs/" : str("../", $target, "/dxfs/");
|
||||
import(str(path, name, ".dxf"));
|
||||
}
|
||||
|
@@ -31,16 +31,27 @@ function m(x) = x * 1000.0;
|
||||
|
||||
function sqr(x) = x * x; //! Returns the square of ```x```
|
||||
function echoit(x) = echo(x) x; //! Echo expression and return it, useful for debugging
|
||||
function no_point(str) = chr([for(c = str(str)) if(c == ".") ord("p") else ord(c)]);//! Replace decimal point in string with 'p'
|
||||
function in(list, x) = !!len([for(v = list) if(v == x) true]); //! Returns true if ```x``` is an element in the ```list```
|
||||
function Len(x) = is_list(x) ? len(x) : 0; //! Returns the length of a list or 0 if ```x``` is not a list
|
||||
function r2sides(r) = $fn ? $fn : ceil(max(min(360/ $fa, r * 2 * PI / $fs), 5)); //! Replicates the OpenSCAD logic to calculate the number of sides from the radius
|
||||
function r2sides4n(r) = floor((r2sides(r) + 3) / 4) * 4; //! Round up the number of sides to a multiple of 4 to ensure points land on all axes
|
||||
function limit(x, min, max) = max(min(x, max), min); //! Force x in range min <= x <= max
|
||||
|
||||
module translate_z(z) translate([0, 0, z]) children(); //! Shortcut for Z only translations
|
||||
module vflip() rotate([180, 0, 0]) children(); //! Invert children by doing a 180° flip around the X axis
|
||||
module hflip() rotate([0, 180, 0]) children(); //! Invert children by doing a 180° flip around the Y axis
|
||||
module ellipse(xr, yr) scale([1, yr / xr]) circle4n(xr); //! Draw an ellipse
|
||||
|
||||
function slice_str(str, start, end, s ="") = start >= end ? s : slice_str(str, start + 1, end, str(s, str[start])); // Helper for slice()
|
||||
|
||||
function slice(list, start = 0, end = undef) = let( //! Slice a list or string with Python type semantics
|
||||
len = len(list),
|
||||
start = limit(start < 0 ? len + start : start, 0, len),
|
||||
end = is_undef(end) ? len : limit(end < 0 ? len + end : end, 0, len)
|
||||
) is_string(list) ? slice_str(list, start, end) : [for(i = [start : 1 : end - 1]) list[i]];
|
||||
|
||||
|
||||
module extrude_if(h, center = true) //! Extrudes 2D object to 3D when ```h``` is nonzero, otherwise leaves it 2D
|
||||
if(h)
|
||||
linear_extrude(h, center = center, convexity = 2) // 3D
|
||||
|
@@ -32,10 +32,14 @@ module poly_circle(r, sides = 0) { //! Make a circle adjusted to print the corre
|
||||
circle(r = corrected_radius(r,n), $fn = n);
|
||||
}
|
||||
|
||||
module poly_cylinder(r, h, center = false, sides = 0) //! Make a cylinder adjusted to print the correct size
|
||||
module poly_cylinder(r, h, center = false, sides = 0, chamfer = false) {//! Make a cylinder adjusted to print the correct size
|
||||
extrude_if(h, center)
|
||||
poly_circle(r, sides);
|
||||
|
||||
if(h && chamfer)
|
||||
poly_cylinder(r + layer_height, center ? layer_height * 2 : layer_height, center, sides = sides ? sides : sides(r));
|
||||
}
|
||||
|
||||
module poly_ring(or, ir, sides = 0) { //! Make a 2D ring adjusted to have the correct internal radius
|
||||
cir = corrected_radius(ir, sides);
|
||||
filaments = (or - cir) / extrusion_width;
|
||||
|
@@ -20,17 +20,32 @@
|
||||
//
|
||||
//! For making horizontal holes that don't need support material.
|
||||
//! Small holes can get away without it, but they print better with truncated teardrops.
|
||||
//!
|
||||
//! Using teardrop_plus() or setting the plus option on other modules will elongate the teardrop vertically by the layer height, so when sliced the staircase tips
|
||||
//! do not intrude into the circle. See <https://hydraraptor.blogspot.com/2020/07/horiholes-2.html>
|
||||
//
|
||||
module teardrop(h, r, center = true, truncate = true, chamfer = 0) { //! For making horizontal holes that don't need support material, set ```truncate = false``` to make traditional RepRap teardrops that don't even need bridging
|
||||
module teardrop(h, r, center = true, truncate = true, chamfer = 0, plus = false) { //! For making horizontal holes that don't need support material, set ```truncate = false``` to make traditional RepRap teardrops that don't even need bridging
|
||||
module teardrop_2d(r, truncate) {
|
||||
hull() {
|
||||
circle4n(r);
|
||||
if(truncate)
|
||||
translate([0, r / 2])
|
||||
square([2 * r * (sqrt(2) - 1), r], center = true);
|
||||
else
|
||||
polygon([[0, 0], [eps, 0], [0, r * sqrt(2)]]);
|
||||
}
|
||||
er = layer_height / 2 - eps; // Extrustion edge radius
|
||||
R = plus ? r + er : r; // Corrected radius
|
||||
offset = plus ? -er : 0; // Offset inwards
|
||||
hull()
|
||||
for(side = [0 : 1])
|
||||
mirror([side, 0, 0])
|
||||
intersection() {
|
||||
hull()
|
||||
translate([offset, 0]) {
|
||||
circle4n(R);
|
||||
|
||||
if(truncate)
|
||||
translate([0, R / 2])
|
||||
square([2 * R * (sqrt(2) - 1), R], center = true);
|
||||
else
|
||||
polygon([[0, 0], [eps, 0], [0, R * sqrt(2)]]);
|
||||
}
|
||||
translate([0, -2 * R])
|
||||
square([R, 4 * R]);
|
||||
}
|
||||
}
|
||||
|
||||
render(convexity = 5)
|
||||
@@ -40,23 +55,23 @@ module teardrop(h, r, center = true, truncate = true, chamfer = 0) { //! For mak
|
||||
teardrop_chamfer(h, center, chamfer) {
|
||||
linear_extrude(eps, center = true)
|
||||
teardrop_2d(r + chamfer / 2, truncate);
|
||||
|
||||
translate_z(-chamfer / 2)
|
||||
linear_extrude(eps, center = true)
|
||||
teardrop_2d(r, truncate);
|
||||
}
|
||||
}
|
||||
|
||||
module semi_teardrop(h, r, d = undef, center = true, chamfer = 0) { //! A semi teardrop in the positive Y domain
|
||||
module semi_teardrop_2d(r, d) {
|
||||
module semi_teardrop(h, r, d = undef, center = true, chamfer = 0, plus = false) { //! A semi teardrop in the positive Y domain
|
||||
module semi_teardrop_2d(r, d)
|
||||
intersection() {
|
||||
R = is_undef(d) ? r : d / 2;
|
||||
teardrop(r = R, h = 0);
|
||||
teardrop(r = R, h = 0, plus = plus);
|
||||
|
||||
sq = R + 1;
|
||||
translate([-sq, 0])
|
||||
square([2 * sq, sq]);
|
||||
}
|
||||
}
|
||||
|
||||
render(convexity = 5)
|
||||
extrude_if(h, center)
|
||||
@@ -65,22 +80,21 @@ module semi_teardrop(h, r, d = undef, center = true, chamfer = 0) { //! A semi t
|
||||
teardrop_chamfer(h, center, chamfer) {
|
||||
linear_extrude(eps, center = true)
|
||||
semi_teardrop_2d(r + chamfer / 2, d);
|
||||
|
||||
translate_z(-chamfer / 2)
|
||||
linear_extrude(eps, center = true)
|
||||
semi_teardrop_2d(r, d);
|
||||
}
|
||||
}
|
||||
|
||||
module teardrop_plus(h, r, center = true, truncate = true, chamfer = 0) //! Slightly bigger teardrop to allow for the 3D printing staircase effect
|
||||
teardrop(h, r + layer_height / 4, center, truncate, chamfer);
|
||||
module teardrop_plus(h, r, center = true, truncate = true, chamfer = 0) //! Slightly elongated teardrop to allow for the 3D printing staircase effect
|
||||
teardrop(h, r, center, truncate, chamfer, plus = true);
|
||||
|
||||
module tearslot(h, r, w, center = true, chamfer = 0) { //! A horizontal slot that doesn't need support material
|
||||
module tearslot_2d(r, w) {
|
||||
hull() {
|
||||
translate([-w / 2, 0]) teardrop(r = r, h = 0);
|
||||
translate([w / 2, 0]) teardrop(r = r, h = 0);
|
||||
}
|
||||
}
|
||||
module tearslot(h, r, w, center = true, chamfer = 0, plus = false) { //! A horizontal slot that doesn't need support material
|
||||
module tearslot_2d(r, w)
|
||||
hull()
|
||||
for(x = [-1, 1])
|
||||
translate([x * w / 2, 0]) teardrop(r = r, h = 0, plus = plus);
|
||||
|
||||
extrude_if(h, center)
|
||||
tearslot_2d(r, w);
|
||||
@@ -88,19 +102,19 @@ module tearslot(h, r, w, center = true, chamfer = 0) { //! A horizontal slot tha
|
||||
teardrop_chamfer(h, center, chamfer) {
|
||||
linear_extrude(eps, center = true)
|
||||
tearslot_2d(r + chamfer / 2, w);
|
||||
|
||||
translate_z(-chamfer / 2)
|
||||
linear_extrude(eps, center = true)
|
||||
tearslot_2d(r, w);
|
||||
}
|
||||
}
|
||||
|
||||
module vertical_tearslot(h, r, l, center = true, chamfer = 0) { //! A vertical slot that doesn't need support material
|
||||
module vertical_tearslot_2d(r, l) {
|
||||
hull() {
|
||||
translate([0, l / 2]) teardrop(0, r, true);
|
||||
translate([0, -l / 2]) circle4n(r);
|
||||
}
|
||||
}
|
||||
module vertical_tearslot(h, r, l, center = true, chamfer = 0, plus = false) { //! A vertical slot that doesn't need support material
|
||||
module vertical_tearslot_2d(r, l)
|
||||
hull()
|
||||
for(y = [-1, 1])
|
||||
translate([0, y * l / 2])
|
||||
teardrop(0, r, true, plus = plus);
|
||||
|
||||
extrude_if(h, center)
|
||||
vertical_tearslot_2d(r, l);
|
||||
@@ -108,6 +122,7 @@ module vertical_tearslot(h, r, l, center = true, chamfer = 0) { //! A vertical s
|
||||
teardrop_chamfer(h, center, chamfer) {
|
||||
linear_extrude(eps, center = true)
|
||||
vertical_tearslot_2d(r + chamfer / 2, l);
|
||||
|
||||
translate_z(-chamfer / 2)
|
||||
linear_extrude(eps, center = true)
|
||||
vertical_tearslot_2d(r, l);
|
||||
@@ -123,4 +138,3 @@ module teardrop_chamfer(h, center, chamfer) { //! Helper module for adding chamf
|
||||
hull()
|
||||
children();
|
||||
}
|
||||
|
||||
|
138
utils/gears.scad
Normal file
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Utilities for making involute gears.
|
||||
//!
|
||||
//! Formulas from <https://khkgears.net/new/gear_knowledge/gear_technical_reference/involute_gear_profile.html>
|
||||
//! <https://khkgears.net/new/gear_knowledge/gear_technical_reference/calculation_gear_dimensions.html>
|
||||
//! and <https://www.tec-science.com/mechanical-power-transmission/involute-gear/calculation-of-involute-gears/>
|
||||
//!
|
||||
//! ```involute_gear_profile()``` returns a polygon that can have the bore and spokes, etc, subtracted from it before linear extruding it to 3D.
|
||||
//! Helical gears can be made using ```twist``` and bevel gears using ```scale``` parameters of ```linear_extrude()```.
|
||||
//!
|
||||
//! Gears with less than 19 teeth (when pressure angle is 20) are profile shifted to avoid undercutting the tooth root. 7 teeth is considered
|
||||
//! the practical minimum.
|
||||
//!
|
||||
//! The clearance between tip and root defaults to module / 6, but can be overridden by setting the ```clearance``` parameter.
|
||||
//!
|
||||
//! The origin of the rack is the left end of the pitch line and its width is below the pitch line. I.e. it does not include the addendum.
|
||||
//!
|
||||
//! ```involute_worm_profile()``` returns a tooth profile that can be passed to ```thread()``` to make worms.
|
||||
//
|
||||
include <core/core.scad>
|
||||
use <maths.scad>
|
||||
|
||||
function involute(r, u) = let(a = degrees(u), c = cos(a), s = sin(a)) r * [c + u * s, s - u * c]; //! Involute of circle radius r at angle u in radians
|
||||
|
||||
function profile_shift(z, pa) = z ? max(1 - z * sqr(sin(pa)) / 2, 0) : 0; //! Calculate profile shift for small gears
|
||||
|
||||
function centre_distance(m, z1, z2, pa = 20) = //! Calculate distance between centres taking profile shift into account
|
||||
let(x1 = profile_shift(z1, pa), x2 = profile_shift(z2, pa)) m * (z1/2 + z2/2 + x1 + x2);
|
||||
|
||||
function involute_gear_od(m, z, pa = 20) = //! involute gear outside diameter given modulus, tooth count and pressure angle
|
||||
m * (z + 2 * profile_shift(z, pa) + 2);
|
||||
|
||||
module involute_gear_profile(m, z, pa = 20, clearance = undef, steps = 20) { //! Calculate gear profile given module, number of teeth and pressure angle
|
||||
assert(z >= 7, "Gears must have at least 7 teeth.");
|
||||
d = m * z; // Reference pitch circle diameter
|
||||
x = profile_shift(z, pa); // Profile shift
|
||||
c = is_undef(clearance) ? m / 6 : clearance; // Clearance from tip to root
|
||||
|
||||
base_d = d * cos(pa); // Base diameter
|
||||
root_r = d / 2 + m * (x - 1) - c; // Root radius (dedendum circle radius)
|
||||
tip_d = d + 2 * m * (1 + x); // Tip diameter (addendum circle diameter)
|
||||
tpa = acos(base_d / tip_d); // Tip pressure angle
|
||||
inva = tan(pa) - radians(pa); // Involute alpha
|
||||
invaa = tan(tpa) - radians(tpa); // Involute alphaa
|
||||
ta = PI / (2 * z) + 2 * x * tan(pa) / z + inva - invaa; // Tooth tip thickness angle, radians
|
||||
crest_w = ta * tip_d; // Crest width
|
||||
umax = sqrt(sqr(tip_d / base_d) - 1); // Max value of the involute parameter
|
||||
|
||||
base_r = base_d / 2;
|
||||
p1 = involute(base_r, 0);
|
||||
p2 = involute(base_r, umax);
|
||||
dist = norm(p2 - p1); // distance between beginning and end of the involute curve
|
||||
|
||||
base_angle = 2 * acos((sqr(base_r) + sqr(tip_d / 2) - sqr(dist)) / base_r / tip_d) + degrees(2 * ta);
|
||||
root_angle = 360 / z - base_angle;
|
||||
root_circle_r = base_r * sin(root_angle / 2);
|
||||
|
||||
if(!is_undef($show_numbers) && $show_numbers) {
|
||||
echo(d=d);
|
||||
echo(base_d=base_d);
|
||||
echo(tip_d=tip_d);
|
||||
echo(tpa = tpa);
|
||||
echo(inva=inva);
|
||||
echo(invaa=invaa);
|
||||
echo(x=x);
|
||||
echo(ta=ta);
|
||||
echo(crest_w=crest_w);
|
||||
echo(umax = umax);
|
||||
echo(base_angle=base_angle);
|
||||
echo(root_angle=root_angle);
|
||||
}
|
||||
involute = [for(i = [0 : steps], u = umax * i / steps) involute(base_r, u)]; // involute for the bottom side of the tooth
|
||||
truncated = [for(p = involute) if((rot2_z(-base_angle / 2) * p).y <= 0) p]; // removed any above the centreline to prevent overlap
|
||||
reflection = reverse([for(p = truncated) rot2_z(base_angle) * [p.x, -p.y] ]); // reflect and rotate to make the top edge
|
||||
|
||||
root = reverse([for(a = [90 : 180 / steps : 270]) rot2_z(base_angle + root_angle / 2) * ([base_r, 0] + root_circle_r * [cos(a), sin(a)]) ]);
|
||||
tooth = concat(truncated, reflection, root);
|
||||
gear = concat([for(i = [0 : z - 1], p = tooth) rot2_z(i * 360 / z) * p]);
|
||||
rotate(-base_angle / 2)
|
||||
union() {
|
||||
polygon(gear);
|
||||
|
||||
circle(root_r);
|
||||
}
|
||||
}
|
||||
|
||||
function involute_rack_tooth_profile(m, pa = 20, clearance = undef) = //! Calculate rack tooth profile given module and pressure angle
|
||||
let(p = PI * m, // Pitch
|
||||
ha = m, // Addendum
|
||||
c = is_undef(clearance) ? m / 4 : clearance, // Tip root clearance
|
||||
hf = m + c, // Dedendum
|
||||
hw = 2 * m, // Working depth
|
||||
h = ha + hf, // Tooth depth
|
||||
crest_w = p / 2 - 2 * ha * tan(pa), // Crest width
|
||||
base_w = crest_w + 2 * hw * tan(pa), // Base width
|
||||
root_w = p - base_w, // Root width
|
||||
clearance_w = root_w - 2 * c * tan(pa), // Width of clearance without fillet
|
||||
kx = tan(pa / 2 + 45), // Fillet ratio of radius and xoffset
|
||||
pf = min(0.38 * m, kx * clearance_w / 2), // Dedendum fillet radius
|
||||
x = pf / kx, // Fillet centre x offset from corner
|
||||
sides = ceil(r2sides(pf) * (90 - pa) / 360), // Fillet facets taking $fa, $fs and $fn into account
|
||||
fillet = [ for(i = [0 : sides - 1], a = i * (90 - pa) / sides + 270) [clearance_w / 2 - x, -hf + pf] + pf * [cos(a), sin(a)] ],
|
||||
reflection = reverse([for(pt = fillet) [p - pt.x, pt.y] ]) // reflect for trailing edge
|
||||
) concat(fillet, [ [root_w / 2, -hw / 2], [p / 2 - crest_w / 2, ha], [p / 2 + crest_w / 2, ha], [p - root_w / 2, -hw / 2] ], reflection);
|
||||
|
||||
module involute_rack_profile(m, z, w, pa = 20, clearance = undef) { //! Calculate rack profile given module, number of teeth and pressure angle
|
||||
p = PI * m; // Pitch
|
||||
hf = 1.25 * m; // Dedendum
|
||||
tooth = involute_rack_tooth_profile(m, pa, clearance);
|
||||
teeth = [for(i = [0 : z - 1], pt = tooth) [pt.x + i * p, pt.y] ];
|
||||
|
||||
polygon(concat([[0, -w], [0, -hf]], teeth, [[z * p, -hf ], [z * p, -w]])); // Add the corners
|
||||
}
|
||||
|
||||
function involute_worm_profile(m, pa = 20, clearance = undef) = //! Calculate worm profile suitable for passing to thread()
|
||||
let(tooth = involute_rack_tooth_profile(m),
|
||||
pitch = PI * m,
|
||||
y_min = min([for(p = tooth) p.y])
|
||||
) [for(p = tooth) [p.x - pitch / 2, p.y - y_min, 0]]; // Offset to be positive in y, centred in x and add 0 z ordintate
|
@@ -39,11 +39,13 @@ module hanging_hole(z, ir, h = 100, h2 = 100) { //! Hole radius ```ir``` hanging
|
||||
infill_angle = z % (2 * layer_height) ? -45 : 45;
|
||||
below = min(z + eps, h2);
|
||||
big = 1000;
|
||||
|
||||
render(convexity = 3) translate_z(z)
|
||||
union() {
|
||||
translate_z(2 * layer_height)
|
||||
polyhole(ir - eps, h - 2 * layer_height);
|
||||
if(sides(ir) > 4)
|
||||
polyhole(ir - eps, h - 2 * layer_height);
|
||||
else
|
||||
poly_cylinder(ir, h - 2 * layer_height);
|
||||
|
||||
difference() {
|
||||
translate_z(-below)
|
||||
|
83
utils/horiholes.scad
Normal file
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2020
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Utilities for depicting the staircase slicing of horizontal holes made with [`teardrop_plus()`](#teardrops), see <https://hydraraptor.blogspot.com/2020/07/horiholes-2.html>
|
||||
//!
|
||||
//! ```horicylinder()``` makes cylinders that fit inside a round hole. Layers that are less than 2 filaments wide and layers that need more than a 45 degree overhang are omitted.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
|
||||
function teardrop_plus_x(r, y, h) = //! Calculate the ordinate of a compensated teardrop given y and layer height.
|
||||
let(fr = h / 2,
|
||||
hpot = r + fr,
|
||||
x2 = sqr(hpot) - sqr(y),
|
||||
x = x2 > 0 ? sqrt(x2) : 0
|
||||
)
|
||||
max(0,
|
||||
y < hpot / sqrt(2) ? x - fr :
|
||||
y < hpot ? hpot * sqrt(2) - y - fr :
|
||||
0);
|
||||
|
||||
module horihole(r, z, h = 0, center = true) { //! For making horizontal holes that don't need support material and are correct dimensions
|
||||
bot_layer = floor((z - r) / layer_height);
|
||||
top_layer = ceil((z + r) / layer_height);
|
||||
render(convexity = 5)
|
||||
extrude_if(h, center)
|
||||
for(i = [bot_layer : top_layer]) {
|
||||
Z = i * layer_height;
|
||||
y = Z - z + layer_height / 2;
|
||||
x = teardrop_plus_x(r, y, layer_height);
|
||||
if(x > 0)
|
||||
translate([0, y])
|
||||
difference() {
|
||||
square([2 * x + layer_height, layer_height], center = true);
|
||||
|
||||
for(end = [-1, 1])
|
||||
translate([end * (x + layer_height / 2), 0])
|
||||
circle(d = layer_height, $fn = 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function teardrop_minus_x(r, y, h) = //! Calculate the ordinate of a compensated teardrop given y and layer height.
|
||||
let(fr = h / 2,
|
||||
hpot = r - fr,
|
||||
x2 = sqr(hpot) - sqr(y),
|
||||
x = x2 > 0 ? sqrt(x2) : 0,
|
||||
X = y >= -hpot / sqrt(2) ? x + fr : 0
|
||||
)
|
||||
X >= extrusion_width ? X : 0;
|
||||
|
||||
module horicylinder(r, z, h = 0, center = true) { //! For making horizontal cylinders that don't need support material and are correct dimensions
|
||||
bot_layer = floor((z - r) / layer_height);
|
||||
top_layer = ceil((z + r) / layer_height);
|
||||
render(convexity = 5)
|
||||
extrude_if(h, center)
|
||||
for(i = [bot_layer : top_layer]) {
|
||||
Z = i * layer_height;
|
||||
y = Z - z + layer_height / 2;
|
||||
x = teardrop_minus_x(r, y, layer_height);
|
||||
if(x >= extrusion_width)
|
||||
hull()
|
||||
for(end = [-1, 1])
|
||||
translate([end * (x - layer_height / 2), y])
|
||||
circle(d = layer_height, $fn = 32);
|
||||
}
|
||||
}
|
@@ -28,6 +28,6 @@ function layout_offset(widths, i, gap = 2) = //! Calculate the offset for the ``
|
||||
|
||||
module layout(widths, gap = 2, no_offset = false) //! Layout children passing ```$i```
|
||||
translate([no_offset ? -widths[0] / 2 : 0, 0])
|
||||
for($i = [0 : len(widths) - 1])
|
||||
for($i = [0 : 1 : len(widths) - 1])
|
||||
translate([layout_offset(widths, $i, gap), 0])
|
||||
children();
|
||||
|