mirror of
https://github.com/nophead/NopSCADlib.git
synced 2025-01-17 21:48:43 +01:00
240334784d
Can now: - render open loops - twist the belt - use pulleys instead of radius in the points list Fixes some precision a few places Breaking change in belt_length(); now requires a type argument
165 lines
8.7 KiB
OpenSCAD
165 lines
8.7 KiB
OpenSCAD
//
|
|
// 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/>.
|
|
//
|
|
|
|
//
|
|
//! Models timing belt running in a path over toothed or smooth pulleys and calculates an accurate length.
|
|
//! Only models 2D paths, belt may twist to support crossed belt core XY and other designes where the belt twists!
|
|
//!
|
|
//! By default the path is a closed loop. An open loop can be specified in two different ways:
|
|
//! 1) If a gap length and position i specified, you can make some forms of 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.
|
|
//! 2) Alternativly, at open loop can more flexible be drawn by specifying `open=true`, and in that case the start and end points are not connected, leaving the loop open.
|
|
//!
|
|
//! To get a 180 degree twist of the loop, you can use the `twist` argument. `Twist` can be a single number, and in that case the belt will twist after
|
|
//! the position with that number. Alternatively `twist` can be a list of boolean values with a boolean for each position; the belt will then twist after
|
|
//! the position that have a `true` value in the `twist` list. If the path is specified with pulley/idler types, then you can use `auto_twist=true`; in
|
|
//! that case the belt will automatically twist so the back of the belt always runs against idlers and the tooth side runs against pullies. If you use
|
|
//! `open=true` then you might also use `start_twist=true` to let the belt start the part with the back side out.
|
|
//!
|
|
//! The path must be specified as a list of positions. Each position should be either a vector with `[x, y, pulley]` or `[x, y, r]`. A pully is a type from
|
|
//! `pulleys.scad`, and correct radius and angle will automatically be calculated. Alternativly a radius can be specifed directly.
|
|
//! To make the back of the belt run against a smooth pulley on the outside of the loop specify a negative pitch radius. Alternativly you can just specify
|
|
//! smooth pulleys in the path, and it will then happen automaticall.
|
|
//!
|
|
//! 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>
|
|
use <pulley.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) = type[5] + belt_tooth_height(type); //! Offset of the pitch radius from the tips of the teeth
|
|
|
|
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.
|
|
//
|
|
module belt(type, points, gap = 0, gap_pos = undef, belt_colour = grey(20), tooth_colour = grey(50), open = false, twist = undef, auto_twist = false, start_twist = false) { //! 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);
|
|
|
|
info = _belt_points_info(type, points, open, twist, auto_twist, start_twist);
|
|
dotwist = info[0]; // array of booleans, true if a twist happen after the position
|
|
twisted = info[1]; // array of booleans, true if the belt is twisted at the position
|
|
pointsx = info[2]; // array of [x,y,r], r is negative if left-angle (points may have pulleys as third element, but pointsx have radi)
|
|
tangents = info[3];
|
|
arcs = info[4];
|
|
length = _belt_length(type, info, open, gap);
|
|
|
|
part = str(type[0],pitch);
|
|
vitamin(str("xbelt(", no_point(part), "x", width, ", ", points, "): Belt ", part," x ", width, "mm x ", length, "mm"));
|
|
|
|
len = len(points);
|
|
|
|
th = belt_tooth_height(type);
|
|
ph = belt_pitch_height(type);
|
|
module beltp() translate([ph-th,-width/2]) square([th,width]);
|
|
module beltb() translate([ph-thickness,-width/2]) square([thickness-th,width]);
|
|
|
|
difference() {
|
|
for (i = [0:len-(open?2:1)]) {
|
|
p1 = tangents[i].x;
|
|
p2 = tangents[i].y;
|
|
v = p2-p1;
|
|
a = atan(v.y/v.x) - (v.x < 0 ? 180 : 0);//a2(p2-p1);
|
|
l = norm(v);
|
|
translate(p1) rotate([-90, 0, a-90]) {
|
|
twist = dotwist[i] ? 180 : 0;
|
|
mirrored = twisted[i] ? 1 : 0;
|
|
color(tooth_colour) linear_extrude(l, twist = twist) mirror([mirrored, 0, 0]) beltp();
|
|
color(belt_colour) linear_extrude(l, twist = twist) mirror([mirrored, 0, 0]) beltb();
|
|
}
|
|
}
|
|
if(gap)
|
|
linear_extrude(width + 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);
|
|
}
|
|
for (i = [(open?1:0):len-(open?2:1)]) {
|
|
p = pointsx[i];
|
|
arc = arcs[i];
|
|
translate([p.x,p.y,0]) rotate([0,0,arc.y]) {
|
|
mirrored = !xor(twisted[i], p.z < 0) ? 1 : 0;
|
|
color(tooth_colour) rotate_extrude(angle=arc.x) translate([abs(p.z),0,0]) mirror([mirrored,0,0]) beltp();
|
|
color(belt_colour) rotate_extrude(angle=arc.x) translate([abs(p.z),0,0]) mirror([mirrored,0,0]) beltb();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
function _belt_points_info(type, points, open, twist, auto_twist, start_twist) = //! Helper function that calulates [twist, istwisted, points, tangents, arcs]
|
|
let(
|
|
len = len(points),
|
|
isleft = function(i) let(
|
|
p = vec2(points[i]),
|
|
p0 = vec2(points[(i - 1 + len) % len]),
|
|
p1 = vec2(points[(i + 1) % len])
|
|
) cross(p-p0,p1-p) > 0,
|
|
dotwist = function(i,istwisted) let( in = (i + 1) % len )
|
|
is_list(twist) ? twist[i] :
|
|
!is_undef(twist) ? i == twist :
|
|
open && is_list(points[in].z) && auto_twist ? !pulley_teeth(points[in].z) && !xor(isleft(in),istwisted) :
|
|
false,
|
|
twisted = [ for (
|
|
i = 0,
|
|
istwisted = start_twist,
|
|
twist = dotwist(i,istwisted),
|
|
nexttwisted = xor(twist,istwisted);
|
|
i < len;
|
|
i = i + 1,
|
|
istwisted = nexttwisted,
|
|
twist = dotwist(i,istwisted),
|
|
nexttwisted = xor(twist,istwisted)
|
|
) [twist,istwisted] ],
|
|
pointsx = mapi(points, function(i, p) !is_list(p.z) ? p : [p.x, p.y, let( // if p.z is not a list it is just r, otherwise it is taken to be a pulley and we calculate r
|
|
isleft = isleft(i),
|
|
r = belt_pulley_pr(type, p.z, twisted=!xor(pulley_teeth(p.z),xor(isleft, twisted[i].y)))
|
|
) isleft ? -r : r ] ),
|
|
tangents = rounded_polygon_tangents_v2(pointsx),
|
|
arcs = rounded_polygon_arcs(pointsx, tangents)
|
|
) [ [ for (t = twisted) t.x ], [ for (t = twisted) t.y ], pointsx, tangents, arcs];
|
|
|
|
function belt_pulley_pr(type, pulley, twisted=false) = //! Pitch radius. Default it expects the belt tooth to be against a toothed pulley an the backside to be against a smooth pulley (an idler). If `twisted` is true, the the belt is the other way around.
|
|
let(
|
|
thickness = belt_thickness(type),
|
|
ph = belt_pitch_height(type)
|
|
) pulley_teeth(pulley)
|
|
? pulley_pr(pulley) + (twisted ? thickness - ph : 0 )
|
|
: pulley_ir(pulley) + (twisted ? ph : thickness - ph );
|
|
|
|
|
|
function belt_length(type, points, open = false, gap = 0) = _belt_length(type, _belt_points_info(type, points, open), open, gap); //! Compute belt length given path and optional gap
|
|
|
|
function _belt_length(type, info, open, gap) = let(
|
|
len = len(info[0]),
|
|
pitch = belt_pitch(type),
|
|
d = open ? 1 : 0,
|
|
tangents = slice(info[3], [0:len - 1 - d]) ,
|
|
arcs = slice(info[4], [d:len - 1 - d]),
|
|
beltl = sumv( map( concat(tangents, arcs), function(e) e.z ) ),
|
|
gapl = is_list(gap) ? gap.x : is_undef(gap) ? 0 : gap
|
|
) ceil((beltl - gapl) / pitch) * pitch;
|