Compare commits
125 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1dbfafd366 | ||
|
68b3dfb098 | ||
|
25dceee20a | ||
|
d70ddf5359 | ||
|
70b60522ce | ||
|
ecba7eaea4 | ||
|
f751dd9a73 | ||
|
3f359f6839 | ||
|
3e5947c161 | ||
|
66dc430541 | ||
|
4dc83d62cb | ||
|
ebbec3c903 | ||
|
9944aab73e | ||
|
eb9bcf0ada | ||
|
ff5e8c0372 | ||
|
17ebf36e27 | ||
|
e38d9abfa0 | ||
|
fc7fd5482e | ||
|
cee1202fd9 | ||
|
6e342441c6 | ||
|
072c38f955 | ||
|
b342549d74 | ||
|
2b83a15e5d | ||
|
ab81c6538c | ||
|
27b0a442e4 | ||
|
38acef9e27 | ||
|
5415beb80d | ||
|
040985c0db | ||
|
0216093a68 | ||
|
30302431c0 | ||
|
1fb429e9a5 | ||
|
9571e68629 | ||
|
b01e6a673c | ||
|
9239c6da3c | ||
|
ba5e5fa390 | ||
|
c7dfdd0fb9 | ||
|
814ce4f15d | ||
|
f661cf6934 | ||
|
305d2146f2 | ||
|
e39ee1797d | ||
|
520569cb30 | ||
|
f73a7b46a2 | ||
|
fb9eca85c6 | ||
|
166ed05d4a | ||
|
ce6aec428d | ||
|
4e9d169c31 | ||
|
1810160103 | ||
|
0c9ae8d60c | ||
|
9a0bad4e61 | ||
|
90047815b0 | ||
|
b583202fb7 | ||
|
eac0086199 | ||
|
03beaec470 | ||
|
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 |
@@ -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>
|
||||
//
|
||||
|
@@ -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
|
||||
|
4
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>
|
||||
@@ -52,6 +54,7 @@ include <vitamins/ring_terminals.scad>
|
||||
include <vitamins/rails.scad>
|
||||
include <vitamins/rod.scad>
|
||||
include <vitamins/scs_bearing_blocks.scad>
|
||||
include <vitamins/shaft_couplings.scad>
|
||||
include <vitamins/sheets.scad>
|
||||
include <vitamins/sk_brackets.scad>
|
||||
include <vitamins/spools.scad>
|
||||
@@ -83,6 +86,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: 811 KiB After Width: | Height: | Size: 848 KiB |
53
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.
|
||||
//
|
||||
@@ -30,10 +47,12 @@ use <tests/bulldogs.scad>
|
||||
use <tests/buttons.scad>
|
||||
use <tests/cable_strips.scad>
|
||||
use <tests/cameras.scad>
|
||||
use <tests/camera_housing.scad>
|
||||
use <tests/circlips.scad>
|
||||
use <tests/components.scad>
|
||||
use <tests/d_connectors.scad>
|
||||
use <tests/displays.scad>
|
||||
use <tests/drag_chain.scad>
|
||||
use <tests/extrusions.scad>
|
||||
use <tests/extrusion_brackets.scad>
|
||||
use <tests/fans.scad>
|
||||
@@ -50,6 +69,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>
|
||||
@@ -67,13 +87,14 @@ use <tests/rod.scad>
|
||||
use <tests/screws.scad>
|
||||
use <tests/SCS_bearing_blocks.scad>
|
||||
use <tests/sealing_strip.scad>
|
||||
use <tests/shaft_couplings.scad>
|
||||
use <tests/sheets.scad>
|
||||
use <tests/SK_brackets.scad>
|
||||
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>
|
||||
@@ -116,17 +137,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();
|
||||
@@ -271,8 +292,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;
|
||||
@@ -288,6 +310,9 @@ translate([x2 + 35, leds_y])
|
||||
translate([x2 + 8, carriers_y])
|
||||
carriers();
|
||||
|
||||
translate([x2, magnets_y])
|
||||
magnets();
|
||||
|
||||
translate([x2 + 20, carriers_y])
|
||||
led_meters();
|
||||
|
||||
@@ -341,6 +366,9 @@ translate([x3 + 170, veroboard_y + 16])
|
||||
translate([x3, d_connectors_y])
|
||||
d_connectors();
|
||||
|
||||
translate([x3 + 170, d_connectors_y - 10])
|
||||
camera_housings();
|
||||
|
||||
translate([x3, iecs_y])
|
||||
iecs();
|
||||
|
||||
@@ -385,13 +413,17 @@ 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])
|
||||
opengrab_test();
|
||||
|
||||
}
|
||||
|
||||
translate([x4 + 175, belts_y, -20])
|
||||
drag_chains();
|
||||
|
||||
translate([x4, rails_y + 130])
|
||||
rails();
|
||||
|
||||
@@ -407,6 +439,9 @@ translate([x4, sk_brackets_y])
|
||||
translate([x4, extrusion_brackets_y])
|
||||
extrusion_brackets();
|
||||
|
||||
translate([x4 + 120, extrusion_brackets_y])
|
||||
shaft_couplings();
|
||||
|
||||
translate([x4, scs_bearing_blocks_y])
|
||||
scs_bearing_blocks();
|
||||
|
||||
|
392
printed/camera_housing.scad
Normal file
@@ -0,0 +1,392 @@
|
||||
//
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Housings for PCB cameras.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/cameras.scad>
|
||||
use <../vitamins/pcb.scad>
|
||||
use <../vitamins/insert.scad>
|
||||
|
||||
wall = 1.75;
|
||||
min_wall = 2 * extrusion_width;
|
||||
clearance = 0.2;
|
||||
|
||||
connector_size = [23, 6, 2.65]; // Worst case size of flat flex connector
|
||||
|
||||
cam_back_clearance = round_to_layer(1.5); // Clearance for components on the back of the pcb
|
||||
cam_back_overlap = 1; // How much the back overlaps the edge of the pcb
|
||||
cam_back_wall = min_wall;
|
||||
|
||||
function cam_front_clearance(cam) = round_to_layer(camera_connector_size(cam).z + clearance);
|
||||
|
||||
function cam_back_size(cam) = let(
|
||||
pcb = camera_pcb(cam),
|
||||
pcb_size = pcb_size(pcb),
|
||||
nut = screw_nut(pcb_screw(pcb)),
|
||||
holes = [for(h = pcb_holes(pcb)) pcb_coord(pcb, h).x],
|
||||
pitch = max(holes) - min(holes),
|
||||
length = pitch + 2 * (nut_radius(nut) + min_wall),
|
||||
width = pcb_size.y + (length - pcb_size.x) * cos(30)
|
||||
) [length, width, wall + max(connector_size.z, cam_back_clearance + nut_trap_depth(nut))];
|
||||
|
||||
|
||||
function cam_front_size(cam) = cam_back_size(cam) + [ //! Outside dimensions of the case
|
||||
2 * (wall + clearance),
|
||||
2 * (wall + clearance),
|
||||
pcb_thickness(camera_pcb(cam)) + cam_front_clearance(cam) + wall
|
||||
];
|
||||
|
||||
hinge_screw = M2_cap_screw;
|
||||
hinge_nut = screw_nut(hinge_screw);
|
||||
hinge_screw_length = 12;
|
||||
|
||||
hinge_r = nut_trap_radius(hinge_nut) + 3 * extrusion_width;
|
||||
hinge_h = wall + nut_trap_depth(hinge_nut);
|
||||
hinge_offset = hinge_r + 1;
|
||||
|
||||
bracket_screw = M3_dome_screw;
|
||||
|
||||
function cam_screw_length(cam) = let(
|
||||
front = cam_front_size(cam),
|
||||
screw = pcb_screw(camera_pcb(cam)),
|
||||
nut = screw_nut(screw)
|
||||
) screw_longer_than(front.z + washer_thickness(screw_washer(screw)) - nut_trap_depth(nut) + nut_thickness(nut, true));
|
||||
|
||||
function hinge_z(cam) = cam_screw_length(cam) - hinge_r;
|
||||
|
||||
module cam_holes(cam) {
|
||||
pcb = camera_pcb(cam);
|
||||
lens_y = camera_lens_offset(cam).y;
|
||||
two_holes = !!len([for (h = pcb_holes(pcb)) if(abs(pcb_coord(pcb, h).y - lens_y) < 1) true]);
|
||||
pcb_screw_positions(pcb) // screw holes
|
||||
if($i > 1 || !two_holes)
|
||||
children();
|
||||
}
|
||||
|
||||
module rpi_camera_focus_ring_stl() { //! Focus ring the glue onto RPI lens
|
||||
stl("rpi_camera_focus_ring");
|
||||
|
||||
rad = 15 / 2;
|
||||
hole_r1 = 2.5 / 2;
|
||||
hole_r2 = 5 / 2;
|
||||
thickness = 3;
|
||||
flutes = 8;
|
||||
angle = 180 / flutes;
|
||||
x = rad / (sin(angle / 2) + cos(angle / 2));
|
||||
r = x * sin(angle / 2);
|
||||
|
||||
difference() {
|
||||
linear_extrude(height = thickness, convexity = 5)
|
||||
difference() {
|
||||
union() {
|
||||
circle(x);
|
||||
for(i = [0 : flutes - 1])
|
||||
rotate([0, 0, 2 * angle * i])
|
||||
translate([x, 0])
|
||||
circle(r);
|
||||
}
|
||||
for(i = [0 : flutes - 1])
|
||||
rotate([0, 0, 2 * angle * i + angle])
|
||||
translate([x, 0])
|
||||
circle(r);
|
||||
}
|
||||
hull() {
|
||||
poly_cylinder(r = hole_r1, h = 0.1, center = true);
|
||||
|
||||
translate([0, 0, thickness])
|
||||
poly_cylinder(r = hole_r2, h = 0.1, center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module camera_back(cam) { //! Make the STL for a camera case back
|
||||
stl(str("camera_back_", cam[0]));
|
||||
pcb = camera_pcb(cam);
|
||||
back = cam_back_size(cam);
|
||||
screw = pcb_screw(pcb);
|
||||
nut = screw_nut(screw);
|
||||
|
||||
translate_z(back.z)
|
||||
hflip()
|
||||
difference() {
|
||||
translate_z(back.z / 2)
|
||||
cube(back, center = true);
|
||||
|
||||
translate([0, -cam_back_overlap])
|
||||
cube([pcb_length(pcb) - 2 * cam_back_overlap, pcb_width(pcb), 2 * cam_back_clearance], center = true);
|
||||
|
||||
translate([0, -pcb_width(pcb) / 2])
|
||||
cube([connector_size.x + 2 * clearance, 2 * connector_size.y + 1, 2 * round_to_layer(connector_size.z + clearance)], center = true);
|
||||
|
||||
translate_z(back.z)
|
||||
cam_holes(cam)
|
||||
hflip()
|
||||
nut_trap(screw, nut, supported = true);
|
||||
}
|
||||
}
|
||||
|
||||
module camera_front(cam, hinge = 0) { //! Make the STL for a camera case front
|
||||
stl(str("camera_front_", cam[0]));
|
||||
front = cam_front_size(cam);
|
||||
back = cam_back_size(cam);
|
||||
pcb = camera_pcb(cam);
|
||||
pcb_size = pcb_size(pcb);
|
||||
lens_offset = camera_lens_offset(cam);
|
||||
screw = pcb_screw(pcb);
|
||||
|
||||
shelf = front.z - back.z;
|
||||
|
||||
connector_slot = connector_size + 2 * [clearance, 0, layer_height];
|
||||
rad = wall;
|
||||
led_hole_r = 1;
|
||||
led_clearance = [5, 2, 1 * 2];
|
||||
res_clearance = [3.5, 2, 1 * 2];
|
||||
|
||||
conn_pos = camera_connector_pos(cam);
|
||||
conn = camera_connector_size(cam);
|
||||
sensor_length = conn_pos.y + conn.y / 2 - lens_offset.y + clearance;
|
||||
|
||||
module hinge_pos()
|
||||
if(!is_undef(hinge))
|
||||
rotate(hinge * 90)
|
||||
translate([0, (hinge ? front.x * hinge : front.y) / 2 + hinge_offset, hinge_r])
|
||||
children();
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
hull()
|
||||
for(x = [-1, 1], y = [-1, 1])
|
||||
translate([x * (front.x / 2 - rad), y * (front.y / 2 - rad)])
|
||||
hull() { // 3D truncated teardrop gives radiused edges without exceeding 45 degree overhang
|
||||
translate_z(front.z - 1)
|
||||
cylinder(r = rad, h = 1);
|
||||
|
||||
translate_z(rad)
|
||||
sphere(rad);
|
||||
|
||||
cylinder(r = rad * (sqrt(2) - 1), h = eps);
|
||||
}
|
||||
|
||||
hinge_pos()
|
||||
hull() {
|
||||
rotate([-90, 0, -90])
|
||||
teardrop(r = hinge_r, h = hinge_h, center = false);
|
||||
|
||||
translate([0, -10, -hinge_r])
|
||||
cube([hinge_h, eps, 2 * hinge_r]);
|
||||
}
|
||||
}
|
||||
|
||||
hinge_pos()
|
||||
rotate([90, 0, 90])
|
||||
teardrop_plus(r = screw_clearance_radius(hinge_screw), h = 100, center = true);
|
||||
|
||||
translate_z(front.z / 2 + shelf - layer_height) // recess for the back
|
||||
cube([back.x + 2 * clearance, back.y + 2 * clearance, front.z], center = true);
|
||||
|
||||
translate_z(front.z / 2 + shelf - pcb_size.z) // recess for PCB
|
||||
cube([pcb_size.x + 2 * clearance, pcb_size.y + 2 * clearance, front.z], center = true);
|
||||
|
||||
translate_z(shelf)
|
||||
hflip() {
|
||||
pcb_component_position(pcb, "smd_led") // clearance for LED
|
||||
cube(led_clearance, center = true);
|
||||
|
||||
pcb_component_position(pcb, "smd_res") // clearance for resistor
|
||||
cube(res_clearance, center = true);
|
||||
}
|
||||
|
||||
translate([conn_pos.x, lens_offset.y + sensor_length / 2, shelf - pcb_size.z]) // clearance for sensor connector
|
||||
cube([conn.x + 2 * clearance, sensor_length, 2 * cam_front_clearance(cam)], center = true);
|
||||
|
||||
translate([0, -front.y / 2, shelf + front.z / 2]) // slot for connector
|
||||
cube([connector_slot.x, connector_slot.y, front.z], center = true);
|
||||
|
||||
translate_z(cam_back_clearance + layer_height)
|
||||
cam_holes(cam)
|
||||
rotate(90)
|
||||
poly_cylinder(r = screw_clearance_radius(screw), h = 100, center = true);
|
||||
|
||||
translate_z(shelf - pcb_size.z)
|
||||
hflip()
|
||||
camera_lens(cam, clearance);
|
||||
|
||||
hflip()
|
||||
pcb_component_position(pcb, "smd_led")
|
||||
rotate(45)
|
||||
poly_cylinder(r = led_hole_r, h = 100, center = true); // hole for led
|
||||
}
|
||||
}
|
||||
|
||||
function bracket_thickness(cam) = max(wall, min(3.5, hinge_z(cam) - hinge_r - 1));
|
||||
|
||||
module camera_bracket_screw_positions(cam) { //! Position children at the bracket screw positions
|
||||
r = washer_radius(screw_washer(bracket_screw)) + 0.5;
|
||||
wide = bracket_thickness(cam) == wall;
|
||||
pitch = wide ? cam_front_size(cam).x / 2 - r : hinge_h + 1 + r;
|
||||
|
||||
for(side = [-1, 1])
|
||||
translate([side * pitch, 0])
|
||||
children();
|
||||
}
|
||||
|
||||
module camera_bracket_position(cam) //! Position children at the bracket position
|
||||
translate([0, cam_front_size(cam).y / 2 + hinge_offset])
|
||||
children();
|
||||
|
||||
module camera_bracket(cam) { //! Make the STL for the camera bracket
|
||||
stl(str("camera_bracket_", cam[0]));
|
||||
|
||||
t = bracket_thickness(cam);
|
||||
z = hinge_z(cam);
|
||||
translate([hinge_h / 2, 0])
|
||||
difference() {
|
||||
hull() {
|
||||
translate_z(eps / 2)
|
||||
cube([hinge_h, 2 * hinge_r, eps], center = true);
|
||||
|
||||
translate_z(z)
|
||||
rotate([0, 90, 0])
|
||||
cylinder(r = hinge_r, h = hinge_h, center = true);
|
||||
}
|
||||
translate([hinge_h / 2, 0, z])
|
||||
rotate([90, 0, 90])
|
||||
nut_trap(hinge_screw, screw_nut(hinge_screw), horizontal = true);
|
||||
}
|
||||
|
||||
linear_extrude(t)
|
||||
difference() {
|
||||
hull()
|
||||
camera_bracket_screw_positions(cam)
|
||||
circle(washer_radius(screw_washer(bracket_screw)) + 0.5);
|
||||
|
||||
camera_bracket_screw_positions(cam)
|
||||
poly_circle(screw_clearance_radius(bracket_screw));
|
||||
}
|
||||
}
|
||||
|
||||
module camera_assembly(cam, angle = 0) //! Camera case assembly
|
||||
assembly(str("camera_", cam[0])) {
|
||||
front = cam_front_size(cam);
|
||||
screw = pcb_screw(camera_pcb(cam));
|
||||
nut = screw_nut(screw);
|
||||
screw_length = cam_screw_length(cam);
|
||||
hinge_z = hinge_z(cam);
|
||||
hinge_pos = [0, front.y / 2 + hinge_offset, -hinge_r];
|
||||
|
||||
camera_bracket_position(cam) {
|
||||
nut = screw_nut(hinge_screw);
|
||||
|
||||
stl_colour(pp1_colour) render()
|
||||
camera_bracket(cam);
|
||||
|
||||
translate([-hinge_h, 0, hinge_z(cam)])
|
||||
rotate([-90, 0, 90]) {
|
||||
vflip()
|
||||
translate_z(2 * hinge_h - nut_trap_depth(nut))
|
||||
nut(nut, true);
|
||||
|
||||
screw_and_washer(hinge_screw, screw_longer_than(2 * hinge_h));
|
||||
}
|
||||
}
|
||||
|
||||
translate_z(hinge_z(cam) + hinge_r)
|
||||
translate(hinge_pos)
|
||||
rotate([-angle, 0, 0])
|
||||
translate(-hinge_pos) {
|
||||
translate_z(cam_back_size(cam).z - front.z)
|
||||
camera(cam);
|
||||
|
||||
stl_colour(pp1_colour) render()
|
||||
translate_z(-front.z)
|
||||
camera_back(cam);
|
||||
|
||||
cam_holes(cam) {
|
||||
screw_and_washer(screw, screw_length);
|
||||
|
||||
translate_z(-front.z + nut_trap_depth(nut))
|
||||
vflip()
|
||||
nut(nut, true);
|
||||
}
|
||||
|
||||
*translate(camera_lens_offset(cam))
|
||||
translate_z(1.5)
|
||||
stl_colour(pp1_colour) render()
|
||||
rpi_camera_focus_ring_stl();
|
||||
|
||||
stl_colour(pp2_colour) render()
|
||||
hflip()
|
||||
camera_front(cam, 0);
|
||||
}
|
||||
}
|
||||
|
||||
module camera_fastened_assembly(cam, thickness, angle = 0) {
|
||||
camera_assembly(cam, angle);
|
||||
|
||||
camera_bracket_position(cam)
|
||||
camera_bracket_screw_positions(cam) {
|
||||
nut = screw_nut(bracket_screw);
|
||||
washer = screw_washer(bracket_screw);
|
||||
t = bracket_thickness(cam);
|
||||
screw_length = screw_longer_than(thickness + t + nut_thickness(nut, true) + 2 * washer_thickness(washer));
|
||||
vflip()
|
||||
translate_z(thickness)
|
||||
screw_and_washer(bracket_screw, screw_length);
|
||||
|
||||
translate_z(t)
|
||||
nut_and_washer(nut, true);
|
||||
}
|
||||
}
|
||||
|
||||
module camera_back_rpi_camera_stl() camera_back(rpi_camera);
|
||||
module camera_back_rpi_camera_v1_stl() camera_back(rpi_camera_v1);
|
||||
module camera_back_rpi_camera_v2_stl() camera_back(rpi_camera_v2);
|
||||
|
||||
module camera_front_rpi_camera_stl() camera_front(rpi_camera);
|
||||
module camera_front_rpi_camera_v1_stl() camera_front(rpi_camera_v1);
|
||||
module camera_front_rpi_camera_v2_stl() camera_front(rpi_camera_v2);
|
||||
|
||||
module camera_bracket_rpi_camera_stl() camera_bracket(rpi_camera);
|
||||
module camera_bracket_rpi_camera_v1_stl() camera_bracket(rpi_camera_v1);
|
||||
module camera_bracket_rpi_camera_v2_stl() camera_bracket(rpi_camera_v2);
|
||||
|
||||
module camera_rpi_camera_assembly() camera_assembly(rpi_camera);
|
||||
module camera_rpi_camera_v1_assembly() camera_assembly(rpi_camera_v1);
|
||||
module camera_rpi_camera_v2_assembly() camera_assembly(rpi_camera_v2);
|
||||
|
||||
module camera_housing(cam) {
|
||||
front = cam_front_size(cam);
|
||||
|
||||
camera_front(cam, 0);
|
||||
|
||||
translate([front.x, 0])
|
||||
camera_back(cam);
|
||||
|
||||
translate([-front.x / 2 - 2 - hinge_r, 0])
|
||||
rotate(90)
|
||||
camera_bracket(cam);
|
||||
}
|
||||
|
||||
cam = rpi_camera_v2;
|
||||
if($preview)
|
||||
camera_fastened_assembly(cam, 3);
|
||||
else
|
||||
camera_housing(cam);
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
285
printed/drag_chain.scad
Normal file
@@ -0,0 +1,285 @@
|
||||
//
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
//
|
||||
//! Parametric cable drag chain to limit the bend radius of a cable run.
|
||||
//!
|
||||
//! Each link has a maximum bend angle of 45°, so the mininium radius is proportional to the link length.
|
||||
//!
|
||||
//! The travel property is how far it can move in each direction, i.e. half the maximum travel if the chain is mounted in the middle of the travel.
|
||||
//
|
||||
|
||||
include <../core.scad>
|
||||
use <../utils/horiholes.scad>
|
||||
use <../utils/maths.scad>
|
||||
|
||||
function drag_chain_name(type) = type[0]; //! The name to allow more than one in a project
|
||||
function drag_chain_size(type) = type[1]; //! The internal size and link length
|
||||
function drag_chain_travel(type) = type[2]; //! X travel
|
||||
function drag_chain_wall(type) = type[3]; //! Side wall thickness
|
||||
function drag_chain_bwall(type) = type[4]; //! Bottom wall
|
||||
function drag_chain_twall(type) = type[5]; //! Top wall
|
||||
function drag_chain_screw(type) = type[6]; //! Mounting screw for the ends
|
||||
function drag_chain_screw_lists(type) = type[7]; //! Two lists of four bools to say which screws positions are used
|
||||
|
||||
function drag_chain_radius(type) = //! The bend radius at the pivot centres
|
||||
let(s = drag_chain_size(type))
|
||||
s.x / 2 / sin(360 / 16);
|
||||
|
||||
function drag_chain_z(type) = //! Outside dimension of a 180 bend
|
||||
let(os = drag_chain_outer_size(type), s = drag_chain_size(type))
|
||||
2 * drag_chain_radius(type) + os.z;
|
||||
|
||||
function drag_chain(name, size, travel, wall = 1.6, bwall = 1.5, twall = 1.5, screw = M2_cap_screw, screw_lists = [[1,0,0,1],[1,0,0,1]]) = //! Constructor
|
||||
[name, size, travel, wall, bwall, twall, screw, screw_lists];
|
||||
|
||||
clearance = 0.1;
|
||||
|
||||
function drag_chain_outer_size(type) = //! Link outer dimensions
|
||||
let(s = drag_chain_size(type), z = s.z + drag_chain_bwall(type) + drag_chain_twall(type))
|
||||
[s.x + z, s.y + 4 * drag_chain_wall(type) + 2 * clearance, z];
|
||||
|
||||
function screw_lug_radius(screw) = //! Radius if a screw lug
|
||||
corrected_radius(screw_clearance_radius(screw)) + 3.1 * extrusion_width;
|
||||
|
||||
module screw_lug(screw, h = 0) //! Create a D shaped lug for a screw
|
||||
extrude_if(h, center = false)
|
||||
difference() {
|
||||
r = screw_lug_radius(screw);
|
||||
hull() {
|
||||
circle4n(r);
|
||||
|
||||
translate([-r, -r])
|
||||
square([2 * r, eps]);
|
||||
}
|
||||
poly_circle(screw_clearance_radius(screw));
|
||||
}
|
||||
|
||||
function bool2int(b) = b ? 1 : 0;
|
||||
|
||||
module drag_chain_screw_positions(type, end) {//! Place children at the screw positions, end = 0 for the start, 1 for the end
|
||||
r = screw_lug_radius(drag_chain_screw(type));
|
||||
s = drag_chain_size(type);
|
||||
os = drag_chain_outer_size(type);
|
||||
R = os.z / 2;
|
||||
x0 = end ? R + norm([drag_chain_cam_x(type), R - drag_chain_twall(type)]) + clearance + r : r;
|
||||
x1 = end ? os.x - r : os.x - 2 * R - clearance - r;
|
||||
for(i = [0 : 3]) {
|
||||
x = i % 2;
|
||||
y = bool2int(i > 1);
|
||||
if(drag_chain_screw_lists(type)[bool2int(end)][i])
|
||||
translate([(x0 + x1) / 2, 0])
|
||||
mirror([x, 0])
|
||||
mirror([0, y])
|
||||
translate([(x1 - x0) / 2, s.y / 2 + r])
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
function drag_chain_cam_x(type) = // how far the cam sticks out
|
||||
let(s = drag_chain_size(type),
|
||||
r = drag_chain_outer_size(type).z / 2,
|
||||
wall = drag_chain_wall(type),
|
||||
cam_r = s.x - 2 * clearance - wall - r, // inner_x_normal - clearance - r
|
||||
twall = drag_chain_twall(type)
|
||||
) min(sqrt(max(sqr(cam_r) - sqr(r - twall), 0)), r);
|
||||
|
||||
|
||||
module drag_chain_link(type, start = false, end = false) { //! One link of the chain, special case for start and end
|
||||
stl(str(drag_chain_name(type), "_drag_chain_link", start ? "_start" : end ? "_end" : ""));
|
||||
|
||||
s = drag_chain_size(type);
|
||||
wall = drag_chain_wall(type);
|
||||
bwall = drag_chain_bwall(type);
|
||||
twall = drag_chain_twall(type);
|
||||
os = drag_chain_outer_size(type);
|
||||
r = os.z / 2;
|
||||
pin_r = r / 2;
|
||||
|
||||
socket_x = r;
|
||||
pin_x = socket_x + s.x;
|
||||
|
||||
outer_normal_x = pin_x - r - clearance; // s.x - clearance
|
||||
outer_end_x = end ? os.x : outer_normal_x;
|
||||
|
||||
inner_x = start ? 0 : outer_normal_x - wall; // s.x - clearance - wall
|
||||
|
||||
roof_x_normal = 2 * r - twall;
|
||||
roof_x = start ? 0 : roof_x_normal;
|
||||
|
||||
floor_x = start ? 0 : 2 * r;
|
||||
cam_x = drag_chain_cam_x(type);
|
||||
assert(r + norm([drag_chain_cam_x(type), r - drag_chain_twall(type)]) + clearance <= inner_x || start, "Link must be longer");
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
for(side = [-1, 1])
|
||||
rotate([90, 0, 0]) {
|
||||
// Outer cheeks
|
||||
translate_z(side * (os.y / 2 - wall / 2))
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
hull() {
|
||||
if(start)
|
||||
square([eps, os.z]);
|
||||
else
|
||||
translate([socket_x, r])
|
||||
rotate(180)
|
||||
teardrop(r = r, h = 0);
|
||||
|
||||
translate([outer_end_x - eps, 0])
|
||||
square([eps, os.z]);
|
||||
}
|
||||
if(!start)
|
||||
translate([socket_x, r])
|
||||
horihole(pin_r, r);
|
||||
}
|
||||
// Inner cheeks
|
||||
translate_z(side * (s.y / 2 + wall / 2))
|
||||
linear_extrude(wall, center = true)
|
||||
difference() {
|
||||
union() {
|
||||
hull() {
|
||||
if(!end) {
|
||||
translate([pin_x, r])
|
||||
rotate(180)
|
||||
teardrop(r = r, h = 0);
|
||||
|
||||
translate([pin_x, twall])
|
||||
square([cam_x, eps]);
|
||||
}
|
||||
else
|
||||
translate([os.x - eps, 0])
|
||||
square([eps, os.z]);
|
||||
|
||||
translate([inner_x, 0])
|
||||
square([eps, os.z]);
|
||||
}
|
||||
}
|
||||
// Cutout for top wall
|
||||
if(!end)
|
||||
intersection() {
|
||||
translate([pin_x - r, 0])
|
||||
square([3 * r, twall]); // When straight
|
||||
|
||||
translate([pin_x, r])
|
||||
rotate(-45)
|
||||
translate([-r + roof_x_normal, -r - twall]) // When bent fully
|
||||
square(os.z);
|
||||
}
|
||||
}
|
||||
// Pin
|
||||
if(!end)
|
||||
translate([pin_x, r, side * (s.y / 2 + wall + clearance)])
|
||||
horicylinder(r = pin_r, z = r, h = 2 * wall);
|
||||
|
||||
// Cheek joint
|
||||
translate([inner_x, 0, side * (s.y / 2 + wall) - 0.5])
|
||||
cube([outer_end_x - inner_x, os.z, 1]);
|
||||
}
|
||||
|
||||
// Roof, actually the floor when printed
|
||||
roof_end = end ? s.x + 2 * r : s.x + r - twall - clearance;
|
||||
translate([roof_x, -s.y / 2 - 0.5])
|
||||
cube([roof_end - roof_x , s.y + 1, twall]);
|
||||
|
||||
translate([roof_x, -os.y / 2 + 0.5])
|
||||
cube([s.x - clearance - roof_x, os.y - 1, twall]);
|
||||
|
||||
// Floor, actually the roof when printed
|
||||
floor_end = end ? s.x + 2 * r : s.x + r;
|
||||
translate([floor_x, -s.y / 2 - wall, os.z - bwall])
|
||||
cube([floor_end - floor_x, s.y + 2 * wall, bwall]);
|
||||
|
||||
translate([floor_x, -os.y / 2 + 0.5, os.z - bwall])
|
||||
cube([s.x - floor_x - clearance, os.y -1, bwall]);
|
||||
|
||||
if(start || end)
|
||||
drag_chain_screw_positions(type, end)
|
||||
screw_lug(drag_chain_screw(type), os.z);
|
||||
}
|
||||
if(start || end)
|
||||
translate_z(-eps)
|
||||
drag_chain_screw_positions(type, end)
|
||||
poly_cylinder(r = screw_clearance_radius(drag_chain_screw(type)), h = os.z + 2 * eps, center = false);
|
||||
|
||||
}
|
||||
|
||||
if(show_supports() && !end) {
|
||||
for(side = [-1, 1]) {
|
||||
w = 2.1 * extrusion_width;
|
||||
translate([s.x + r + cam_x - w / 2, side * (s.y / 2 + wall / 2), twall / 2])
|
||||
cube([w, wall, twall], center = true);
|
||||
|
||||
h = round_to_layer(r - pin_r / sqrt(2));
|
||||
y = s.y / 2 + max(wall + w / 2 + clearance, 2 * wall + clearance - w / 2);
|
||||
translate([s.x + r, side * y, h / 2])
|
||||
cube([pin_r * sqrt(2), w, h], center = true);
|
||||
|
||||
gap = cam_x - pin_r / sqrt(2) + extrusion_width;
|
||||
translate([s.x + r + cam_x - gap / 2, side * (s.y / 2 + wall + clearance / 2), layer_height / 2])
|
||||
cube([gap, 2 * wall + clearance, layer_height], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! 1. Remove the support material from the links with side cutters.
|
||||
//! 1. Clip the links together with the special ones at the ends.
|
||||
module drag_chain_assembly(type, pos = 0) { //! Drag chain assembly
|
||||
s = drag_chain_size(type);
|
||||
r = drag_chain_radius(type);
|
||||
travel = drag_chain_travel(type);
|
||||
links = ceil(travel / s.x);
|
||||
actual_travel = links * s.x;
|
||||
z = drag_chain_outer_size(type).z;
|
||||
|
||||
zb = z / 2; // z of bottom track
|
||||
c = [actual_travel / 2 + pos / 2, 0, r + zb]; // centre of bend
|
||||
|
||||
points = [ // Calculate list of hinge points
|
||||
for(i = 0, p = [0, 0, z / 2 + 2 * r]; i < links + 5;
|
||||
i = i + 1,
|
||||
dx = p.z > c.z ? s.x : -s.x,
|
||||
p = max(p.x + dx, p.x) <= c.x ? p + [dx, 0, 0] // Straight sections
|
||||
: let(q = circle_intersect(p, s.x, c, r))
|
||||
q.x <= c.x ? [p.x - sqrt(sqr(s.x) - sqr(p.z - zb)), 0, zb] // Transition back to straight
|
||||
: q) // Circular section
|
||||
p
|
||||
];
|
||||
npoints = len(points);
|
||||
|
||||
module link(n) // Position and colour link with origin at the hinge hole
|
||||
translate([-z / 2, 0, -z / 2])
|
||||
stl_colour(n % 2 ? pp1_colour : pp2_colour)
|
||||
drag_chain_link(type, start = n == -1, end = n == npoints - 1);
|
||||
|
||||
assembly(str(drag_chain_name(type), "_drag_chain")) {
|
||||
for(i = [0 : npoints - 2]) let(v = points[i+1] - points[i])
|
||||
translate(points[i])
|
||||
rotate([0, -atan2(v.z, v.x), 0])
|
||||
link(i);
|
||||
|
||||
translate(points[0] - [s.x, 0, 0])
|
||||
link(-1);
|
||||
|
||||
translate(points[npoints - 1])
|
||||
hflip()
|
||||
link(npoints - 1);
|
||||
}
|
||||
}
|
@@ -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) + wall); //! 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,28 @@ 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);
|
||||
module ribbon_clamp_7_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);
|
||||
|
||||
//! * Place inserts into the holes and press home with a soldering iron with a conical bit heated to 200°C.
|
||||
module ribbon_clamp_7_2_assembly() ribbon_clamp_assembly(8, M2_dome_screw);
|
||||
|
@@ -87,24 +87,25 @@ def plateup(target, part_type, usage = None):
|
||||
match = re.match(r'^ECHO: "~(.*?\.' + part_type + r').*"$', line)
|
||||
if match:
|
||||
used.append(match.group(1))
|
||||
#
|
||||
# Copy file that are not included
|
||||
#
|
||||
copied = []
|
||||
for file in os.listdir(parts_dir):
|
||||
if file.endswith('.' + part_type) and not file in used:
|
||||
src = parts_dir + '/' + file
|
||||
dst = target_dir + '/' + file
|
||||
if mtime(src) > mtime(dst):
|
||||
print("Copying %s to %s" % (src, dst))
|
||||
copyfile(src, dst)
|
||||
copied.append(file)
|
||||
#
|
||||
# Remove any cruft
|
||||
#
|
||||
targets = [file[:-4] + part_type for file in all_sources]
|
||||
for file in os.listdir(target_dir):
|
||||
if file.endswith('.' + part_type):
|
||||
if not file in targets and not file in copied:
|
||||
print("Removing %s" % file)
|
||||
os.remove(target_dir + '/' + file)
|
||||
if all_sources:
|
||||
#
|
||||
# Copy files that are not included
|
||||
#
|
||||
for file in os.listdir(parts_dir):
|
||||
if file.endswith('.' + part_type) and not file in used:
|
||||
src = parts_dir + '/' + file
|
||||
dst = target_dir + '/' + file
|
||||
if mtime(src) > mtime(dst):
|
||||
print("Copying %s to %s" % (src, dst))
|
||||
copyfile(src, dst)
|
||||
copied.append(file)
|
||||
#
|
||||
# Remove any cruft
|
||||
#
|
||||
targets = [file[:-4] + part_type for file in all_sources]
|
||||
for file in os.listdir(target_dir):
|
||||
if file.endswith('.' + part_type):
|
||||
if not file in targets and not file in copied:
|
||||
print("Removing %s" % file)
|
||||
os.remove(target_dir + '/' + file)
|
||||
|
@@ -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:]:
|
||||
|
@@ -86,10 +86,13 @@ 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)],
|
||||
@@ -118,7 +121,8 @@ test_pcb = ["TestPCB", "Test PCB",
|
||||
[ 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) ],
|
||||
|
@@ -23,46 +23,52 @@ use <../vitamins/insert.scad>
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module belt_test() {
|
||||
p1 = [75, -50];
|
||||
p2 = [-75, -50];
|
||||
p3 = [-75, 100];
|
||||
p4 = [75, 100];
|
||||
|
||||
p5 = [75 - pulley_pr(GT2x20ob_pulley) - pulley_pr(GT2x16_plain_idler), -pulley_pr(GT2x16_plain_idler)];
|
||||
p6 = [-75 + pulley_pr(GT2x20ob_pulley) + pulley_pr(GT2x16_plain_idler), -pulley_pr(GT2x16_plain_idler)];
|
||||
p5 = [75 + pulley_pr(GT2x20ob_pulley) - pulley_pr(GT2x16_plain_idler), +pulley_pr(GT2x16_plain_idler)];
|
||||
p6 = [-75 + pulley_pr(GT2x20ob_pulley) + pulley_pr(GT2x16_plain_idler), -pulley_pr(GT2x16_plain_idler)];
|
||||
|
||||
translate(p1) pulley_assembly(GT2x20ob_pulley);
|
||||
translate(p2) pulley_assembly(GT2x20ob_pulley);
|
||||
translate(p3) pulley_assembly(GT2x20_toothed_idler);
|
||||
translate(p4) pulley_assembly(GT2x20_toothed_idler);
|
||||
module pulleys(flip = false) {
|
||||
translate(p2) rotate([0, flip ? 180 : 0, 0]) pulley_assembly(GT2x20ob_pulley);
|
||||
translate(p3) pulley_assembly(GT2x20_toothed_idler);
|
||||
translate(p4) pulley_assembly(GT2x20_toothed_idler);
|
||||
translate(p5) {
|
||||
pulley = GT2x16_toothed_idler;
|
||||
screw = find_screw(hs_cs_cap, pulley_bore(pulley));
|
||||
insert = screw_insert(screw);
|
||||
|
||||
translate(p5) {
|
||||
pulley = GT2x16_plain_idler;
|
||||
screw = find_screw(hs_cs_cap, pulley_bore(pulley));
|
||||
insert = screw_insert(screw);
|
||||
|
||||
pulley_assembly(pulley);
|
||||
translate_z(pulley_height(pulley) + pulley_offset(pulley) + screw_head_depth(screw, pulley_bore(pulley)))
|
||||
screw(screw, 20);
|
||||
|
||||
translate_z(pulley_offset(pulley) - insert_length(insert))
|
||||
vflip()
|
||||
insert(insert);
|
||||
hflip(flip) {
|
||||
pulley_assembly(pulley);
|
||||
translate_z(pulley_height(pulley) + pulley_offset(pulley) + screw_head_depth(screw, pulley_bore(pulley)))
|
||||
screw(screw, 20);
|
||||
|
||||
translate_z(pulley_offset(pulley) - insert_length(insert))
|
||||
vflip()
|
||||
insert(insert);
|
||||
}
|
||||
}
|
||||
translate(p6) pulley_assembly(GT2x16_plain_idler);
|
||||
}
|
||||
translate(p6) pulley_assembly(GT2x16_plain_idler);
|
||||
|
||||
path = [ [p1.x, p1.y, pulley_pr(GT2x20ob_pulley)],
|
||||
[p5.x, p5.y, -pulley_pr(GT2x16_plain_idler)],
|
||||
path = [ [p5.x, p5.y, pulley_pr(GT2x16_plain_idler)],
|
||||
[p6.x, p6.y, -pulley_pr(GT2x16_plain_idler)],
|
||||
[p2.x, p2.y, pulley_pr(GT2x20ob_pulley)],
|
||||
[p3.x, p3.y, pulley_pr(GT2x20ob_pulley)],
|
||||
[p4.x, p4.y, pulley_pr(GT2x20ob_pulley)]
|
||||
];
|
||||
belt = GT2x6;
|
||||
belt(belt, path, 80, [0, belt_pitch_height(belt) - belt_thickness(belt) / 2]);
|
||||
|
||||
translate([-25, 0])
|
||||
belt = GT2x6;
|
||||
belt(belt, path, 80, [0, 0]);
|
||||
pulleys();
|
||||
translate_z(20)
|
||||
hflip() {
|
||||
belt(belt, path, 80, [0, 0], belt_colour = grey(90), tooth_colour = grey(50));
|
||||
pulleys(flip=true);
|
||||
}
|
||||
|
||||
translate([-25, 0, 10])
|
||||
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 ? grey(90) : grey(20), tooth_colour = $i%2==0 ? grey(70) : grey(50));
|
||||
|
33
tests/camera_housing.scad
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// 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>
|
||||
|
||||
use <../printed/camera_housing.scad>
|
||||
|
||||
include <../vitamins/cameras.scad>
|
||||
|
||||
use <../vitamins/pcb.scad>
|
||||
|
||||
module camera_housings()
|
||||
layout([for(c = cameras) pcb_length(camera_pcb(c))], 15, false) let(c = cameras[$i])
|
||||
camera_fastened_assembly(c, 3);
|
||||
|
||||
if($preview)
|
||||
camera_housings();
|
@@ -24,7 +24,7 @@ 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])
|
||||
layout([for(c = cameras) pcb_length(camera_pcb(c))], 15, false) let(c = cameras[$i])
|
||||
camera(c);
|
||||
|
||||
if($preview)
|
||||
|
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();
|
59
tests/drag_chain.scad
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
// Link length between hinges
|
||||
x = 10; //[8 : 30]
|
||||
|
||||
// Link inner width
|
||||
y = 10; //[5 : 30]
|
||||
|
||||
// Link inner height
|
||||
z = 5; //[4 : 11]
|
||||
// Side wall thickness
|
||||
wall = 1.6; //[0.9: 0.1: 3]
|
||||
// Bottom wall thickness
|
||||
bwall = 1.5; //[1: 0.25: 3]
|
||||
// Top wall thickness
|
||||
twall = 1.5; //[1: 0.25: 3]
|
||||
// Max travel in each direction
|
||||
travel = 100;
|
||||
// Current position
|
||||
pos = 50; // [-100 : 1 : 100]
|
||||
|
||||
include <../core.scad>
|
||||
use <../printed/drag_chain.scad>
|
||||
|
||||
include <../vitamins/leadnuts.scad>
|
||||
|
||||
drag_chain = drag_chain("x", [x, y, z], travel, wall = wall, bwall = bwall, twall = twall);
|
||||
|
||||
module drag_chains()
|
||||
drag_chain_assembly(drag_chain, pos);
|
||||
|
||||
if($preview)
|
||||
drag_chains();
|
||||
else {
|
||||
drag_chain_link(drag_chain);
|
||||
|
||||
translate([-x * 2, 0])
|
||||
drag_chain_link(drag_chain, start = true);
|
||||
|
||||
translate([x * 2, 0])
|
||||
drag_chain_link(drag_chain, end = true);
|
||||
}
|
@@ -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)
|
||||
|
@@ -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: 98 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 133 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
BIN
tests/png/camera_housing.png
Normal file
After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 68 KiB |
BIN
tests/png/catenary.png
Normal file
After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
BIN
tests/png/drag_chain.png
Normal file
After Width: | Height: | Size: 132 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 |
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: 61 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 173 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: 66 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 188 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 84 KiB |
BIN
tests/png/shaft_couplings.png
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 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);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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();
|
||||
|
30
tests/shaft_couplings.scad
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// NopSCADlib Copyright Chris Palmer 2018
|
||||
// nop.head@gmail.com
|
||||
// hydraraptor.blogspot.com
|
||||
//
|
||||
// This file is part of NopSCADlib.
|
||||
//
|
||||
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
include <../core.scad>
|
||||
include <../vitamins/shaft_couplings.scad>
|
||||
|
||||
use <../utils/layout.scad>
|
||||
|
||||
module shaft_couplings()
|
||||
layout([for(s = shaft_couplings) sc_diameter(s)],5)
|
||||
shaft_coupling(shaft_couplings[$i]);
|
||||
|
||||
if($preview)
|
||||
shaft_couplings();
|
||||
|
@@ -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>
|
||||
//
|
||||
|
@@ -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 vflip(flip=true) rotate([flip ? 180 : 0, 0, 0]) children(); //! Invert children by doing a 180° flip around the X axis
|
||||
module hflip(flip=true) rotate([0, flip ? 180: 0, 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);
|
||||
}
|
||||
}
|
@@ -20,7 +20,18 @@
|
||||
//
|
||||
//! Maths utilities for manipulating vectors and matrices.
|
||||
//
|
||||
function sqr(x) = x * x;
|
||||
function sqr(x) = x * x; //! Square x
|
||||
function radians(degrees) = degrees * PI / 180; //! Convert radians to degrees
|
||||
function degrees(radians) = radians * 180 / PI; //! Convert degrees to radians
|
||||
|
||||
function sinh(x) = (exp(x) - exp(-x)) / 2; //! hyperbolic sine
|
||||
function cosh(x) = (exp(x) + exp(-x)) / 2; //! hyperbolic cosine
|
||||
function tanh(x) = sinh(x) / cosh(x); //! hyperbolic tangent
|
||||
function coth(x) = cosh(x) / sinh(x); //! hyperbolic cotangent
|
||||
function argsinh(x) = ln(x + sqrt(sqr(x) + 1)); //! inverse hyperbolic sine
|
||||
function argcosh(x) = ln(x + sqrt(sqr(x) - 1)); //! inverse hyperbolic cosine
|
||||
function argtanh(x) = ln((1 + x) / (1 - x)) / 2;//! inverse hyperbolic tangent
|
||||
function argcoth(x) = ln((x + 1) / (x - 1)) / 2;//! inverse hyperbolic cotangent
|
||||
|
||||
function translate(v) = let(u = is_list(v) ? len(v) == 2 ? [v.x, v.y, 0] //! Generate a 4x4 translation matrix, ```v``` can be ```[x, y]```, ```[x, y, z]``` or ```z```
|
||||
: v
|
||||
@@ -63,6 +74,12 @@ function rot3_z(a) = //! Generate a 3x3 matrix to rotate around z
|
||||
[ s, c, 0],
|
||||
[ 0, 0, 1] ];
|
||||
|
||||
function rot2_z(a) = //! Generate a 2x2 matrix to rotate around z
|
||||
let(c = cos(a),
|
||||
s = sin(a))
|
||||
[ [ c, -s],
|
||||
[ s, c] ];
|
||||
|
||||
function scale(v) = let(s = is_list(v) ? v : [v, v, v]) //! Generate a 4x4 matrix that scales by ```v```, which can be a vector of xyz factors or a scalar to scale all axes equally
|
||||
[
|
||||
[s.x, 0, 0, 0],
|
||||
@@ -129,3 +146,10 @@ function invert(m) = let(n =len(m), m = solve(augment(m))) [ //! Invert a matrix
|
||||
each m[i][j]
|
||||
]
|
||||
];
|
||||
|
||||
function circle_intersect(c1, r1, c2, r2) = //! Calculate one point where two circles in the X-Z plane intersect, clockwise around c1
|
||||
let(
|
||||
v = c1 - c2, // Line between centres
|
||||
d = norm(v), // Distance between centres
|
||||
a = atan2(v.z, v.x) - acos((sqr(d) + sqr(r2) - sqr(r1)) / (2 * d * r2)) // Cosine rule to find angle from c2
|
||||
) c2 + r2 * [cos(a), 0, sin(a)]; // Point on second circle
|
||||
|
@@ -23,18 +23,21 @@
|
||||
include <../utils/core/core.scad>
|
||||
|
||||
module quadrant(w, r, center = false) { //! Draw a square with one rounded corner, can be centered on the arc centre, when ```center``` is ```true```.
|
||||
offset = center ? r - w : 0;
|
||||
translate([offset, offset])
|
||||
h = is_list(w) ? w.y : w;
|
||||
w = is_list(w) ? w.x : w;
|
||||
offset_w = center ? r - w : 0;
|
||||
offset_h = center ? r - h : 0;
|
||||
translate([offset_w, offset_h])
|
||||
hull() {
|
||||
intersection() {
|
||||
translate([w - r, w - r])
|
||||
translate([w - r, h - r])
|
||||
circle4n(r);
|
||||
|
||||
square(w);
|
||||
square([w, h]);
|
||||
}
|
||||
|
||||
square([w, eps]);
|
||||
|
||||
square([eps, w]);
|
||||
square([eps, h]);
|
||||
}
|
||||
}
|
||||
|
@@ -34,14 +34,22 @@ function transpose3(m) = [ [m[0].x, m[1].x, m[2].x],
|
||||
[m[0].y, m[1].y, m[2].y],
|
||||
[m[0].z, m[1].z, m[2].z] ];
|
||||
//
|
||||
// Find the first non-colinear point
|
||||
//
|
||||
tiny = 0.00001;
|
||||
function find_curve(tangents, i = 1) =
|
||||
i >= len(tangents) - 1 || norm(cross(tangents[0], tangents[i] - tangents[0])) > tiny ? i
|
||||
: find_curve(tangents, i + 1);
|
||||
//
|
||||
// Frenet-Serret frame
|
||||
//
|
||||
function fs_frame(tangents) =
|
||||
let(tangent = tangents[0],
|
||||
normal = tangents[1] - tangents[0],
|
||||
i = find_curve(tangents),
|
||||
normal = tangents[i] - tangents[0],
|
||||
binormal = cross(tangent, normal),
|
||||
z = unit(tangent),
|
||||
x = assert(norm(binormal) > 0.00001, "first three points are colinear") unit(binormal),
|
||||
x = assert(norm(binormal) > tiny, "all points are colinear") unit(binormal),
|
||||
y = unit(cross(z, x))
|
||||
) [[x.x, y.x, z.x],
|
||||
[x.y, y.y, z.y],
|
||||
@@ -70,7 +78,6 @@ function orientate(p, r) =
|
||||
[x.y, y.y, z.y],
|
||||
[x.z, y.z, z.z],
|
||||
[p.x, p.y, p.z]];
|
||||
|
||||
//
|
||||
// Rotate around z
|
||||
//
|
||||
@@ -145,10 +152,10 @@ function sweep(path, profile, loop = false, twist = 0) = //! Generate the point
|
||||
faces = loop ? skin_faces : concat([cap(facets)], skin_faces, [cap(facets, npoints - 1)])
|
||||
) [points, faces];
|
||||
|
||||
module sweep(path, profile, loop = false, twist = 0) { //! Draw a polyhedron that is the swept volume
|
||||
module sweep(path, profile, loop = false, twist = 0, convexity = 1) { //! Draw a polyhedron that is the swept volume
|
||||
mesh = sweep(path, profile, loop, twist);
|
||||
|
||||
polyhedron(points = mesh[0], faces = mesh[1]);
|
||||
polyhedron(points = mesh[0], faces = mesh[1], convexity = convexity);
|
||||
}
|
||||
|
||||
function path_length(path, i = 0, length = 0) = //! Calculated the length along a path
|
||||
|
@@ -26,6 +26,8 @@
|
||||
//! Threads are by default solid, so the male version is wrapped around a cylinder and the female inside a tube. This can be suppressed to just get the helix, for
|
||||
//! example to make a printed pot with a screw top lid.
|
||||
//!
|
||||
//! A left hand thread can be made by using mirror([0,1]).
|
||||
//!
|
||||
//! Threads with a typical 60 degree angle appear too bright with OpenSCAD's primitive lighting model as they face towards the lights more than the top and sides of
|
||||
//! a cylinder. To get around this a colour can be passed to thread that is used to colour the cylinder and then toned down to colour the helix.
|
||||
//!
|
||||
@@ -47,7 +49,7 @@ function thread_profile(h, crest, angle, overlap = 0.1) = //! Create thread prof
|
||||
let(base = crest + 2 * (h + overlap) * tan(angle / 2))
|
||||
[[-base / 2, -overlap, 0], [-crest / 2, h, 0], [crest / 2, h, 0], [base / 2, -overlap, 0]];
|
||||
|
||||
module thread(dia, pitch, length, profile, center = true, top = -1, bot = -1, starts = 1, solid = true, female = false, colour = undef) { //! Create male or femail thread, ends can be tapered, chamfered or square
|
||||
module thread(dia, pitch, length, profile, center = true, top = -1, bot = -1, starts = 1, solid = true, female = false, colour = undef) { //! Create male or female thread, ends can be tapered, chamfered or square
|
||||
//
|
||||
// Apply colour if defined
|
||||
//
|
||||
@@ -61,10 +63,12 @@ module thread(dia, pitch, length, profile, center = true, top = -1, bot = -1, st
|
||||
// Extract some properties from the profile, perhaps they should be stored in it.
|
||||
//
|
||||
h = max([for(p = sprofile) p.y]);
|
||||
maxx = max([for(p = sprofile) p.x]);
|
||||
minx = min([for(p = sprofile) p.x]);
|
||||
crest_xmax = max([for(p = sprofile) if(p.x != maxx) p.x]);
|
||||
crest_xmin = min([for(p = sprofile) if(p.x != minx) p.x]);
|
||||
xs = [for(p = sprofile) p.x];
|
||||
maxx = max(xs);
|
||||
minx = min(xs);
|
||||
crest_xs = [for(p = sprofile) if(p.y == h) p.x];
|
||||
crest_xmax = max(crest_xs);
|
||||
crest_xmin = min(crest_xs);
|
||||
//
|
||||
// If the ends don't taper we need an extra half turn past the ends to be cropped horizontally.
|
||||
//
|
||||
@@ -129,11 +133,13 @@ module thread(dia, pitch, length, profile, center = true, top = -1, bot = -1, st
|
||||
render() intersection() {
|
||||
polyhedron(points, ends_faces);
|
||||
|
||||
len = length - 2 * eps;
|
||||
shorten = !is_undef(colour);
|
||||
len = shorten ? length - 2 * eps : length;
|
||||
offset = shorten ? eps : 0;
|
||||
rotate_extrude()
|
||||
if(female) {
|
||||
difference() {
|
||||
translate([0, eps])
|
||||
translate([0, offset])
|
||||
square([r + h + overlap, len]);
|
||||
|
||||
if(top_chamfer_h)
|
||||
@@ -146,7 +152,7 @@ module thread(dia, pitch, length, profile, center = true, top = -1, bot = -1, st
|
||||
else
|
||||
difference() {
|
||||
hull() {
|
||||
translate([0, eps])
|
||||
translate([0, offset])
|
||||
square([r, len]);
|
||||
|
||||
translate([0, bot_chamfer_h])
|
||||
|
@@ -31,3 +31,41 @@ module ring(or, ir) //! Create a ring with specified external and internal radii
|
||||
module tube(or, ir, h, center = true) //! Create a tube with specified external and internal radii and height ```h```
|
||||
linear_extrude(h, center = center, convexity = 5)
|
||||
ring(or, ir);
|
||||
|
||||
module woven_tube(or, ir, h, center= true, colour = grey(30), colour2, warp = 2, weft) {//! Create a woven tube with specified external and internal radii, height ```h```, colours, warp and weft
|
||||
colour2 = colour2 ? colour2 : colour * 0.8;
|
||||
weft = weft ? weft : warp;
|
||||
warp_count = max(floor(PI * or / warp), 0.5);
|
||||
angle = 360 / (2 * warp_count);
|
||||
|
||||
module layer(weft) {
|
||||
points = [[ir, weft / 2], [or, weft / 2], [or, -weft / 2], [ir, -weft / 2]];
|
||||
color(colour)
|
||||
for (i = [0 : warp_count])
|
||||
rotate(2 * i * angle)
|
||||
rotate_extrude(angle = angle)
|
||||
polygon(points);
|
||||
color(colour2)
|
||||
for (i = [0 : warp_count])
|
||||
rotate((2 * i + 1) * angle)
|
||||
rotate_extrude(angle = angle)
|
||||
polygon(points);
|
||||
}
|
||||
|
||||
translate_z(center ? -h / 2 : 0) {
|
||||
weft_count = floor(h / weft);
|
||||
if (weft_count > 0)
|
||||
for (i = [0 : weft_count - 1]) {
|
||||
translate_z(i * weft + weft / 2)
|
||||
rotate(i * angle)
|
||||
layer(weft);
|
||||
}
|
||||
remainder = h - weft * weft_count;
|
||||
if (remainder) {
|
||||
translate_z(weft_count * weft + remainder / 2)
|
||||
rotate(weft_count * angle)
|
||||
layer(remainder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,7 @@ include <../utils/core/core.scad>
|
||||
include <../utils/round.scad>
|
||||
|
||||
module wire_link(d, l, h = 1, tail = 3) { //! Draw a wire jumper link.
|
||||
vitamin(str("wire_link(", d, ", ", l, arg(h, 1, "h"), arg(tail, 3, "tail"), "): Wire link ", d, "mm x ", l / inch(1), "\""));
|
||||
r = d;
|
||||
$fn = 32;
|
||||
|
||||
@@ -57,13 +58,16 @@ module orientate_axial(length, height, pitch, wire_d) { // Orient horizontal or
|
||||
min_pitch = ceil((length + 1) / inch(0.1)) * inch(0.1);
|
||||
lead_pitch = pitch ? pitch : min_pitch;
|
||||
if(lead_pitch >= min_pitch) {
|
||||
wire_link(wire_d, lead_pitch, height);
|
||||
not_on_bom()
|
||||
wire_link(wire_d, lead_pitch, height);
|
||||
|
||||
translate_z(height)
|
||||
rotate([0, 90, 0])
|
||||
children();
|
||||
}
|
||||
else {
|
||||
wire_link(wire_d, lead_pitch, length + 0.7 + wire_d);
|
||||
not_on_bom()
|
||||
wire_link(wire_d, lead_pitch, length + 0.7 + wire_d);
|
||||
|
||||
translate([-pitch / 2, 0, length / 2 + 0.2])
|
||||
children();
|
||||
|
@@ -52,21 +52,21 @@ module ball_bearing(type) { //! Draw a ball bearing
|
||||
rim_chamfer = rim / 6;
|
||||
rotate_extrude()
|
||||
hull() {
|
||||
translate([or - rim / 2, 0])
|
||||
square([rim, h - 2 * rim_chamfer], center = true);
|
||||
translate([or - rim, -h / 2 + rim_chamfer])
|
||||
square([rim, h - 2 * rim_chamfer]);
|
||||
|
||||
translate([or - rim / 2 - rim_chamfer, 0])
|
||||
square([rim - rim_chamfer, h], center = true);
|
||||
translate([or - rim, -h / 2])
|
||||
square([rim - rim_chamfer, h]);
|
||||
}
|
||||
|
||||
hub_chamfer = hub / 6;
|
||||
rotate_extrude()
|
||||
hull() {
|
||||
translate([ir + hub / 2, 0])
|
||||
square([hub, h - 2 * hub_chamfer], center = true);
|
||||
translate([ir, -h / 2 + hub_chamfer])
|
||||
square([hub, h - 2 * hub_chamfer]);
|
||||
|
||||
translate([ir + hub / 2 + hub_chamfer, 0])
|
||||
square([hub - hub_chamfer, h], center = true);
|
||||
translate([ir + hub_chamfer, -h / 2])
|
||||
square([hub - hub_chamfer, h]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -16,11 +16,12 @@
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
BB624 = ["624", 4, 13, 5, "blue", 1.2, 1.2]; // 624 ball bearing for idlers
|
||||
BB608 = ["608", 8, 22, 7, "OrangeRed", 1.4, 2.0]; // 608 bearings for wades
|
||||
BB6200 = ["6200", 10, 30, 9, "black", 2.3, 3.6]; // 6200 bearings for KP pillow blocks
|
||||
BB6201 = ["6201", 12, 32, 10, "black", 2.4, 3.7]; // 6201 bearings for KP pillow blocks
|
||||
BB6808 = ["6808", 40, 52, 7, "black", 1.5, 1.6];
|
||||
ball_bearings = [BB624, BB608, BB6200, BB6201, BB6808];
|
||||
BBSMR95 = ["SMR95", 5, 9, 2.5, "silver", 0.5, 0.7]; // SMR95 ball bearing for FlexDrive extruder
|
||||
BB624 = ["624", 4, 13, 5, "blue", 1.2, 1.2]; // 624 ball bearing for idlers
|
||||
BB608 = ["608", 8, 22, 7, "OrangeRed", 1.4, 2.0]; // 608 bearings for wades
|
||||
BB6200 = ["6200", 10, 30, 9, "black", 2.3, 3.6]; // 6200 bearings for KP pillow blocks
|
||||
BB6201 = ["6201", 12, 32, 10, "black", 2.4, 3.7]; // 6201 bearings for KP pillow blocks
|
||||
BB6808 = ["6808", 40, 52, 7, "black", 1.5, 1.6];
|
||||
ball_bearings = [BBSMR95, BB624, BB608, BB6200, BB6201, BB6808];
|
||||
|
||||
use <ball_bearing.scad>
|
||||
|
@@ -19,61 +19,67 @@
|
||||
|
||||
//
|
||||
//! Models timing belt running over toothed or smooth pulleys and calculates an accurate length.
|
||||
//! Only models 2D paths, so not core XY!
|
||||
//! Only models 2D paths, so not crossed belt core XY!
|
||||
//!
|
||||
//! To make the back of the belt run against a smooth pulley on the outside of the loop specify a negative pitch radius.
|
||||
//!
|
||||
//! By default the path is a closed loop but a gap length and position can be specified to make open loops.
|
||||
//! To draw the gap its XY position is specified by ```gap_pos```. ```gap_pos.z``` can be used to specify a rotation if the gap is not at the bottom of the loop.
|
||||
//!
|
||||
//! Individual teeth are not drawn, instead they are represented by a lighter colour.
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
use <../utils/rounded_polygon.scad>
|
||||
use <../utils/maths.scad>
|
||||
|
||||
function belt_pitch(type) = type[1]; //! Pitch in mm
|
||||
function belt_width(type) = type[2]; //! Width in mm
|
||||
function belt_thickness(type) = type[3]; //! Total thickness including teeth
|
||||
function belt_tooth_height(type) = type[4]; //! Tooth height
|
||||
function belt_pitch_height(type) = belt_tooth_height(type) + type[4]; //! Offset of the pitch radius from the tips of the teeth
|
||||
function belt_pitch_height(type) = type[5] + belt_tooth_height(type); //! Offset of the pitch radius from the tips of the teeth
|
||||
|
||||
function no_point(str) = chr([for(c = str) if(c == ".") ord("p") else ord(c)]);
|
||||
function belt_pitch_to_back(type) = belt_thickness(type) - belt_pitch_height(type); //! Offset of the back from the pitch radius
|
||||
//
|
||||
// We model the belt path at the pitch radius of the pulleys and the pitch line of the belt to get an accurate length.
|
||||
// The belt is then drawn by offseting each side from the pitch line.
|
||||
//
|
||||
module belt(type, points, gap = 0, gap_pt = undef, belt_colour = grey(20), tooth_colour = grey(50)) { //! Draw a belt path given a set of points and pitch radii where the pulleys are. Closed loop unless a gap is specified
|
||||
module belt(type, points, gap = 0, gap_pos = undef, belt_colour = grey(20), tooth_colour = grey(50)) { //! Draw a belt path given a set of points and pitch radii where the pulleys are. Closed loop unless a gap is specified
|
||||
width = belt_width(type);
|
||||
pitch = belt_pitch(type);
|
||||
thickness = belt_thickness(type);
|
||||
part = str(type[0],pitch);
|
||||
vitamin(str("belt(", no_point(part), "x", width, ", ", points, arg(gap, 0), arg(gap_pt, undef), "): Belt ", part," x ", width, "mm x ", length, "mm"));
|
||||
vitamin(str("belt(", no_point(part), "x", width, ", ", points, arg(gap, 0), arg(gap_pos, undef), "): Belt ", part," x ", width, "mm x ", length, "mm"));
|
||||
|
||||
len = len(points);
|
||||
|
||||
tangents = rounded_polygon_tangents(points);
|
||||
|
||||
length = ceil((rounded_polygon_length(points, tangents) - gap) / pitch) * pitch;
|
||||
length = ceil((rounded_polygon_length(points, tangents) - (is_list(gap) ? gap.x + gap.y : gap)) / pitch) * pitch;
|
||||
|
||||
module shape() rounded_polygon(points, tangents);
|
||||
|
||||
ph = belt_pitch_height(type);
|
||||
th = belt_tooth_height(type);
|
||||
module gap()
|
||||
if(gap)
|
||||
translate(gap_pt)
|
||||
square([gap, thickness + eps], center = true);
|
||||
translate([gap_pos.x, gap_pos.y])
|
||||
rotate(is_undef(gap_pos.z) ? 0 : gap_pos.z)
|
||||
translate([0, ph - thickness / 2])
|
||||
square(is_list(gap) ? [gap.x, gap.y + thickness + eps] : [gap, thickness + eps], center = true);
|
||||
|
||||
color(belt_colour)
|
||||
linear_extrude(width, center = true)
|
||||
difference() {
|
||||
offset(thickness - belt_pitch_height(type)) shape();
|
||||
offset(-belt_pitch_height(type) + belt_tooth_height(type)) shape();
|
||||
offset(-ph + thickness ) shape();
|
||||
offset(-ph + th) shape();
|
||||
gap();
|
||||
|
||||
}
|
||||
|
||||
color(tooth_colour)
|
||||
linear_extrude(width, center = true)
|
||||
difference() {
|
||||
offset(-belt_pitch_height(type) + belt_tooth_height(type)) shape();
|
||||
offset(-belt_pitch_height(type)) shape();
|
||||
offset(-ph + th) shape();
|
||||
offset(-ph) shape();
|
||||
gap();
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,8 @@
|
||||
//
|
||||
include <../utils/core/core.scad>
|
||||
use <../utils/rounded_cylinder.scad>
|
||||
use <../utils/quadrant.scad>
|
||||
use <screw.scad>
|
||||
|
||||
function blower_length(type) = type[2]; //! Length of enclosing rectangle
|
||||
function blower_width(type) = type[3]; //! Width of enclosing rectangle
|
||||
@@ -39,8 +41,87 @@ function blower_top(type) = type[14]; //! Thickness of the top
|
||||
function blower_wall(type) = type[15]; //! Side wall thickness
|
||||
function blower_lug(type) = type[16]; //! Height of the lugs
|
||||
|
||||
function blower_casing_is_square(type) = len(blower_screw_holes(type)) > 3; //! True for square radial fans, false for spiral shape radial blowers
|
||||
function blower_exit_offset(type) = blower_casing_is_square(type) ? blower_length(type) / 2 : blower_exit(type) / 2; //! Offset of exit's centre from the edge
|
||||
|
||||
fan_colour = grey(20);
|
||||
|
||||
module blower_fan(type, casing_is_square) {
|
||||
module squarish(s, n) {
|
||||
polygon([
|
||||
for(i = [0 : n]) [i * s.x / n, s.y + (i % 2) * eps],
|
||||
for(i = [0 : n]) [s.x - i * s.x / n, (i % 2) * eps],
|
||||
]);
|
||||
}
|
||||
|
||||
depth = blower_depth(type);
|
||||
blade_ir = blower_hub(type) / 2 + 0.5; // slight gap between main part of blades and hub
|
||||
blade_len = casing_is_square
|
||||
? (blower_bore(type) - 1) / 2 - blade_ir // fan constrained by bore hole
|
||||
: blower_width(type) - blower_axis(type).x- blower_wall(type) - blade_ir; // fan extends to casing
|
||||
blade_thickness = 0.75;
|
||||
blade_count = 25;
|
||||
|
||||
base_offset = 1;
|
||||
translate([blower_axis(type).x, blower_axis(type).y, blower_base(type) + base_offset])
|
||||
linear_extrude(blower_hub_height(type) - 0.5 - blower_base(type) - base_offset, center = false, convexity = 4, twist = -30, slices = round(depth / 2))
|
||||
for(i = [0 : blade_count - 1])
|
||||
rotate((360 * i) / blade_count)
|
||||
translate([blade_ir, -blade_thickness / 2])
|
||||
squarish([blade_len, blade_thickness], round(blade_len / 2));
|
||||
}
|
||||
|
||||
module blower_square(type) { //! Draw a square blower
|
||||
width = blower_width(type);
|
||||
depth = blower_depth(type);
|
||||
wall = blower_wall(type);
|
||||
hole_pitch = (blower_screw_holes(type)[1].x - blower_screw_holes(type)[0].x) / 2;
|
||||
corner_radius = width / 2 - hole_pitch;
|
||||
corner_inset = (width - blower_exit(type)) / 2;
|
||||
|
||||
module square_inset_corners(remove_center = false)
|
||||
difference() {
|
||||
//overall outside
|
||||
square([width, width], center = false);
|
||||
|
||||
if (remove_center) {
|
||||
// cut out the inside, leaving the corners
|
||||
translate([corner_inset + wall, -eps])
|
||||
square([width - 2 * (wall + corner_inset), width - wall + eps], center = false);
|
||||
|
||||
translate([wall, corner_inset + wall])
|
||||
square([width - 2 * wall, width - 2 * (wall + corner_inset)], center = false);
|
||||
} else {
|
||||
// cut out the bore for the fan
|
||||
translate(blower_axis(type))
|
||||
circle(d = blower_bore(type));
|
||||
}
|
||||
// corner inset
|
||||
translate([width / 2, width / 2])
|
||||
for(i = [0 : 3])
|
||||
rotate(i * 90)
|
||||
translate([-width / 2 - eps, -width/ 2 - eps])
|
||||
quadrant(corner_inset, corner_inset - corner_radius);
|
||||
}
|
||||
|
||||
base_height = blower_base(type);
|
||||
linear_extrude(base_height)
|
||||
difference () {
|
||||
rounded_square([width, width], corner_radius, center = false);
|
||||
|
||||
blower_hole_positions(type)
|
||||
circle(d = blower_screw_hole(type));
|
||||
}
|
||||
|
||||
translate_z(base_height)
|
||||
linear_extrude(depth - base_height)
|
||||
square_inset_corners(remove_center = true);
|
||||
|
||||
translate_z(depth - base_height)
|
||||
linear_extrude(blower_top(type))
|
||||
square_inset_corners();
|
||||
}
|
||||
|
||||
module blower(type) { //! Draw specified blower
|
||||
length = blower_length(type);
|
||||
width = blower_width(type);
|
||||
@@ -70,55 +151,63 @@ module blower(type) { //! Draw specified blower
|
||||
|
||||
vitamin(str("blower(", type[0], "): ", type[1]));
|
||||
|
||||
is_square = blower_casing_is_square(type); // Description starts with square!
|
||||
color(fan_colour) {
|
||||
// screw lugs
|
||||
linear_extrude(blower_lug(type), center = false)
|
||||
for(hole = blower_screw_holes(type))
|
||||
difference() {
|
||||
hull() {
|
||||
if (is_square) {
|
||||
blower_square(type);
|
||||
} else {
|
||||
// screw lugs
|
||||
linear_extrude(blower_lug(type), center = false)
|
||||
for(hole = blower_screw_holes(type))
|
||||
difference() {
|
||||
hull() {
|
||||
translate(hole)
|
||||
circle(d = blower_screw_hole(type) + 2 * blower_wall(type));
|
||||
|
||||
translate(blower_axis(type))
|
||||
circle(d = blower_screw_hole(type) + 2 * blower_wall(type) + 7);
|
||||
}
|
||||
translate(hole)
|
||||
circle(d = blower_screw_hole(type) + 2 * blower_wall(type));
|
||||
circle(d = blower_screw_hole(type));
|
||||
|
||||
translate(blower_axis(type))
|
||||
circle(d = blower_screw_hole(type) + 2 * blower_wall(type) + 7);
|
||||
}
|
||||
translate(hole)
|
||||
circle(d = blower_screw_hole(type));
|
||||
shape(true);
|
||||
}
|
||||
|
||||
shape(true);
|
||||
}
|
||||
// rotor
|
||||
translate(concat(blower_axis(type), [blower_base(type) + 1]))
|
||||
rounded_cylinder(r = blower_hub(type) / 2, h = blower_hub_height(type) - blower_base(type) - 1, r2 = 1);
|
||||
*%square([length, width]);
|
||||
|
||||
*%square([length, width]);
|
||||
|
||||
// base
|
||||
linear_extrude(blower_base(type))
|
||||
difference() {
|
||||
shape();
|
||||
|
||||
translate(concat(blower_axis(type), [blower_base(type)]))
|
||||
circle(d = 2);
|
||||
}
|
||||
// sides
|
||||
linear_extrude(depth)
|
||||
difference() {
|
||||
shape();
|
||||
|
||||
offset(-blower_wall(type))
|
||||
shape(true);
|
||||
}
|
||||
|
||||
// top
|
||||
translate_z(depth -blower_top(type))
|
||||
linear_extrude(blower_top(type))
|
||||
// base
|
||||
linear_extrude(blower_base(type))
|
||||
difference() {
|
||||
shape();
|
||||
|
||||
translate(concat(blower_axis(type), [blower_base(type)]))
|
||||
circle(d = blower_bore(type));
|
||||
}
|
||||
circle(d = 2);
|
||||
}
|
||||
|
||||
// sides
|
||||
linear_extrude(depth)
|
||||
difference() {
|
||||
shape();
|
||||
|
||||
offset(-blower_wall(type))
|
||||
shape(true);
|
||||
}
|
||||
|
||||
// top
|
||||
translate_z(depth -blower_top(type))
|
||||
linear_extrude(blower_top(type))
|
||||
difference() {
|
||||
shape();
|
||||
|
||||
translate(concat(blower_axis(type), [blower_base(type)]))
|
||||
circle(d = blower_bore(type));
|
||||
}
|
||||
}
|
||||
// rotor
|
||||
translate(concat(blower_axis(type), [blower_base(type) + 1]))
|
||||
rounded_cylinder(r = blower_hub(type) / 2, h = blower_hub_height(type) - blower_base(type) - 1, r2 = 1);
|
||||
|
||||
blower_fan(type, is_square);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -16,10 +16,16 @@
|
||||
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// l w d b s h a s s e h b t w l
|
||||
// e i e o c u x c c x u a o a u
|
||||
// n d p r r b i r r i b s p l g
|
||||
// g t t e e s e e t e l
|
||||
// t h h w d w w t
|
||||
// h h s t t
|
||||
RB5015 = ["RB5015", "Blower Runda RB5015", 51.3, 51, 15, 31.5, M4_cap_screw, 26, [27.3, 25.4], 4.5, [[4.3, 45.4], [47.3,7.4]], 20, 14, 1.5, 1.3, 1.2, 15];
|
||||
PE4020 = ["PE4020", "Blower Pengda Technology 4020", 40, 40, 20, 27.5, M3_cap_screw, 22, [21.5, 20 ], 3.2, [[37,3],[3,37],[37,37]], 29.3, 17, 1.7, 1.2, 1.3, 13];
|
||||
BL40x10 =["BL40x10","Square radial 4010", 40, 40,9.5, 27, M2_cap_screw, 16, [24, 20 ], 2.4, [[2,2],[38,2],[2,38],[38,38]], 30 , 9.5, 1.5, 1.5, 1.1, 1.5];
|
||||
|
||||
RB5015 = ["RB5015", "Blower Runda RB5015", 51.3, 51, 15, 31.5, M4_cap_screw, 26, [27.3, 25.4], 4.5, [[4.3, 45.4], [47.3,7.4]], 20, 14, 1.5, 1.3, 1.2, 15];
|
||||
PE4020 = ["PE4020", "Blower Pengda Technology 4020", 40, 40, 20, 27.5, M3_cap_screw, 22, [21.5, 20 ], 3.2, [[37,3],[3,37],[37,37]], 29.3, 17, 1.7, 1.2, 1.3, 13];
|
||||
|
||||
blowers = [PE4020, RB5015];
|
||||
blowers = [BL40x10, PE4020, RB5015];
|
||||
|
||||
use <blower.scad>
|
||||
|
@@ -20,7 +20,7 @@
|
||||
//
|
||||
//! A strip of polypropylene used with ribbon cable to make a cable flexible in one direction only.
|
||||
//!
|
||||
//! Modelled with a Bezier spline, which is not quite the same as a miniumum energy curve but very close, epecially
|
||||
//! Modelled with a Bezier spline, which is not quite the same as a minimum energy curve but very close, epecially
|
||||
//! near the extreme positions, where the model needs to be accurate.
|
||||
//!
|
||||
//! When the sides are constrained then a circular model is more accurate.
|
||||
|
@@ -29,7 +29,7 @@ function camera_lens(type) = type[4]; //! Stack of lens parts, can be r
|
||||
function camera_connector_pos(type) = type[5]; //! The flex connector block for the camera itself's position
|
||||
function camera_connector_size(type)= type[6]; //! The flex connector block for the camera itself's size
|
||||
|
||||
module camera_lens(type, offset = 0)
|
||||
module camera_lens(type, offset = 0) //! Draw the lens stack, with optional offset for making a clearance hole
|
||||
color(grey(20))
|
||||
translate(camera_lens_offset(type))
|
||||
for(p = camera_lens(type)) {
|
||||
@@ -81,7 +81,7 @@ module camera(type) { //! Draw specified PCB camera
|
||||
cube([flex.x, eps, flex.y], center = true);
|
||||
|
||||
translate_z(conn.z - flex.y)
|
||||
translate(pos - [0, conn.y / 2])
|
||||
translate([camera_lens_offset(type).x, pos.y] - [0, conn.y / 2])
|
||||
cube([flex.x, eps, flex.y], center = true);
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,21 @@ rpi_camera_v1 = ["rpi_camera_v1", "Raspberry Pi camera V1", rpi_camera_v1_pcb, [
|
||||
[0, 12 - 1.5 - 2.5], [8, 5, 1]
|
||||
];
|
||||
|
||||
rpi_camera_v2_pcb = ["", "", 25, 23.862, 1, 2, 2.2, 0, "green", false, [[2, -2], [-2, -2], [2, -14.5], [-2, -14.5]],
|
||||
[
|
||||
[12.5, 2.75, 0, "-flat_flex", true],
|
||||
],
|
||||
[]];
|
||||
|
||||
rpi_camera_v2 = ["rpi_camera_v2", "Raspberry Pi camera V2", rpi_camera_v2_pcb, [0, 9.6 - 12],
|
||||
[
|
||||
[[8.5, 8.5, 3], 0],
|
||||
[[0, 0, 4], 7.5 / 2],
|
||||
[[0, 0, 5], 5.5 / 2, [1.5/2, 2/2, 0.5]],
|
||||
],
|
||||
[-13.8 + 12.5, 23.862 / 2 - 4.7], [8.5, 4, 1]
|
||||
];
|
||||
|
||||
rpi_camera_pcb = ["", "", 36, 36, 1.6, 0, 3.2, 0, "green", false, [[3.5, -3.5], [-3.5, -3.5], [3.5, 3.5], [-3.5, 3.5]],
|
||||
[
|
||||
[18, 4.25, 0, "-flat_flex", true],
|
||||
@@ -53,6 +68,6 @@ rpi_camera = ["rpi_camera", "Raspberry Pi focusable camera", rpi_camera_pcb, [0,
|
||||
[0, 18 - 1.5 - 2.5], [8, 5, 1.6]
|
||||
];
|
||||
|
||||
cameras = [rpi_camera_v1, rpi_camera];
|
||||
cameras = [rpi_camera_v1, rpi_camera_v2, rpi_camera];
|
||||
|
||||
use <camera.scad>
|
||||
|
@@ -161,13 +161,13 @@ module al_clad_resistor(type, value, leads = true) { //! Draw an aluminium clad
|
||||
}
|
||||
linear_extrude(thickness)
|
||||
difference() {
|
||||
for(end = [-1, 1])
|
||||
translate([end * (length - tab) / 2, end * (width - width / 2) / 2])
|
||||
square([tab, width / 2], center = true);
|
||||
union()
|
||||
for(end = [-1, 1])
|
||||
translate([end * (length - tab) / 2, end * (width - width / 2) / 2])
|
||||
square([tab, width / 2], center = true);
|
||||
|
||||
al_clad_resistor_hole_positions(type)
|
||||
circle(d = al_clad_hole(type));
|
||||
|
||||
}
|
||||
if(leads) {
|
||||
translate_z(height / 2)
|
||||
|
@@ -73,6 +73,42 @@ SSD1963_4p3 = ["SSD1963_4p3", "LCD display SSD1963 4.3\"", 105.5, 67.2, 3.4, SSD
|
||||
[[0, -34.5], [12, -31.5]],
|
||||
];
|
||||
|
||||
displays = [HDMI5, SSD1963_4p3, LCD1602A, LCDS7282B];
|
||||
BigTreeTech_TFT35v3_0_PCB = ["", "",
|
||||
110, 55.77, 1.6, 0, 3, 0, "green", false,
|
||||
[ [-3.12, 3.17], [-3.12, -3.17], [3.12, -3.17], [3.12, 3.17] ],
|
||||
[
|
||||
[ 10, 7.5, 0, "-button_6mm" ],
|
||||
[ 9, 43, 0, "-buzzer", 5, 9 ],
|
||||
[ 9, 27, 0, "-potentiometer" ],
|
||||
[ 102,28.82, 0, "uSD", [26.5, 16, 3] ],
|
||||
[16.5, 5.9, 0, "2p54boxhdr", 5, 2 ],
|
||||
[36.5, 5.9, 0, "2p54boxhdr", 5, 2 ],
|
||||
[56.5, 5.9, 0, "2p54boxhdr", 5, 2 ],
|
||||
[82.5, 4, 0, "jst_xh", 5 ],
|
||||
[26.5, 52.8, 180, "jst_xh", 2 ],
|
||||
[39.5, 52.8, 180, "jst_xh", 3 ],
|
||||
[52.5, 52.8, 180, "jst_xh", 3 ],
|
||||
[65.5, 52.8, 180, "jst_xh", 3 ],
|
||||
[78.5, 52.8, 180, "jst_xh", 3 ],
|
||||
[94.5, 52.8, 180, "jst_xh", 5 ],
|
||||
[ 8, 43, 180, "usb_A" ],
|
||||
[ 97, 4, 0, "chip", 9, 3.5, 1, grey(20) ],
|
||||
// ESP-8266
|
||||
[ 23, 28, 90, "2p54socket", 4, 2 ],
|
||||
],
|
||||
[]
|
||||
];
|
||||
|
||||
BigTreeTech_TFT35v3_0 = ["BigTreeTech_TFT35v3_0", "BigTreeTech TFT35 v3.0",
|
||||
84.5, 54.5, 4, BigTreeTech_TFT35v3_0_PCB,
|
||||
[-6, 0, 0], // pcb offset
|
||||
[[-40, -26.5], [41.5, 26.5, 0.5]], // aperture
|
||||
[], // touch screen
|
||||
0, // thread length
|
||||
[], // clearance need for the ts ribbon
|
||||
];
|
||||
|
||||
|
||||
displays = [HDMI5, SSD1963_4p3, BigTreeTech_TFT35v3_0, LCD1602A, LCDS7282B];
|
||||
|
||||
use <display.scad>
|
||||
|
@@ -26,6 +26,8 @@ include <tubings.scad>
|
||||
include <zipties.scad>
|
||||
include <fans.scad>
|
||||
|
||||
use <../utils/rounded_cylinder.scad>
|
||||
use <../utils/thread.scad>
|
||||
use <../utils/tube.scad>
|
||||
|
||||
rad_dia = 22; // Diam of the part with ailettes
|
||||
@@ -94,7 +96,34 @@ module heater_block(type, naked = false, resistor_wire_rotate = [0,0,0]) {
|
||||
}
|
||||
}
|
||||
|
||||
module bowden_connector(cap_colour = grey(20)) {
|
||||
ir = 4.25 / 2;
|
||||
body_colour = silver;
|
||||
|
||||
color(body_colour) {
|
||||
translate_z(-4.5) {
|
||||
tube(or = 2.5, ir = ir, h = 4.5, center = false);
|
||||
male_metric_thread(6, metric_coarse_pitch(5), length = 4.5, center = false, solid = false, colour = body_colour);
|
||||
}
|
||||
tube(or = 7.7 / 2, ir = ir, h = 2, center = false);
|
||||
translate_z(2)
|
||||
linear_extrude(6.5)
|
||||
difference() {
|
||||
circle(d = 11.55, $fn = 6);
|
||||
circle(r = ir);
|
||||
}
|
||||
translate_z(8.5)
|
||||
rounded_cylinder(r = 9.8 / 2, h = 2, r2 = 1.5, ir = ir);
|
||||
translate_z(10.5)
|
||||
tube(or = 3.5, ir = ir, h = 0.5, center = false);
|
||||
}
|
||||
color(cap_colour) {
|
||||
translate_z(11)
|
||||
tube(or = 3, ir = ir, h = 1, center = false);
|
||||
translate_z(12)
|
||||
tube(or = 5.5, ir = ir, h = 1.75, center = false);
|
||||
}
|
||||
}
|
||||
|
||||
module e3d_fan_duct(type) {
|
||||
color("DeepSkyBlue")
|
||||
@@ -123,7 +152,7 @@ module e3d_fan(type) {
|
||||
fan(fan30x10);
|
||||
}
|
||||
|
||||
module e3d_hot_end(type, filament, naked = false, resistor_wire_rotate = [0,0,0]) {
|
||||
module e3d_hot_end(type, filament, naked = false, resistor_wire_rotate = [0,0,0], bowden = false) {
|
||||
insulator_length = hot_end_insulator_length(type);
|
||||
inset = hot_end_inset(type);
|
||||
h_ailettes = rad_len / (2 * rad_nb_ailettes - 1);
|
||||
@@ -150,6 +179,10 @@ module e3d_hot_end(type, filament, naked = false, resistor_wire_rotate = [0,0,0]
|
||||
square([100, hot_end_groove(type)]);
|
||||
}
|
||||
|
||||
if(bowden)
|
||||
translate_z(inset)
|
||||
bowden_connector();
|
||||
|
||||
rotate(90)
|
||||
heater_block(type, naked, resistor_wire_rotate);
|
||||
|
||||
@@ -158,10 +191,10 @@ module e3d_hot_end(type, filament, naked = false, resistor_wire_rotate = [0,0,0]
|
||||
e3d_fan();
|
||||
}
|
||||
|
||||
module e3d_hot_end_assembly(type, filament, naked = false, resistor_wire_rotate = [0,0,0]) {
|
||||
module e3d_hot_end_assembly(type, filament, naked = false, resistor_wire_rotate = [0,0,0], bowden = false) {
|
||||
bundle = 3.2;
|
||||
|
||||
e3d_hot_end(type, filament, naked, resistor_wire_rotate);
|
||||
e3d_hot_end(type, filament, naked, resistor_wire_rotate, bowden);
|
||||
|
||||
// Wire and ziptie
|
||||
if(!naked)
|
||||
|