1
0
mirror of https://github.com/nophead/NopSCADlib.git synced 2025-01-17 21:48:43 +01:00
NopSCADlib/vitamins/belt.scad
2021-03-14 12:48:14 +01:00

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 = ceil(_belt_length(type, info, open, gap) / pitch) * pitch;
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[1]]) {
mirrored = xor(twisted[i], p[2] < 0) ? 0 : 1;
color(tooth_colour) rotate_extrude(angle=arc[0]) translate([abs(p[2]),0,0]) mirror([mirrored,0,0]) beltp();
color(belt_colour) rotate_extrude(angle=arc[0]) translate([abs(p[2]),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][2]) && auto_twist ? !pulley_teeth(points[in][2]) && !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[2]) ? p : [p.x, p.y, let( // if p[2] 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[2], twisted=!xor(pulley_teeth(p[2]),xor(isleft, twisted[i][1])))
) isleft ? -r : r ] ),
tangents = rounded_polygon_tangents(pointsx),
arcs = rounded_polygon_arcs(pointsx, tangents)
) [ [ for (t = twisted) t[0] ], [ for (t = twisted) t[1] ], 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 - d) ,
arcs = slice(info[4], d, len - d),
beltl = sumv( map( concat(tangents, arcs), function(e) e[2] ) ),
gapl = is_list(gap) ? gap.x : is_undef(gap) ? 0 : gap
) beltl - gapl;