diff --git a/src/dotSCAD.scad b/src/dotSCAD.scad
index da2cee57..ab148ad1 100644
--- a/src/dotSCAD.scad
+++ b/src/dotSCAD.scad
@@ -8,173 +8,209 @@
* @see https://github.com/JustinSDK/dotSCAD
*/
-/**
-* circle_path.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-circle_path.html
-*
-**/
-
-
-function circle_path(radius, n) =
+function __polytransversals(transversals) =
let(
- _frags = __frags(radius),
- step_a = 360 / _frags,
- end_a = 360 - step_a * ((n == undef || n > _frags) ? 1 : _frags - n + 1)
- )
- [
- for(a = [0 : step_a : end_a])
- [radius * cos(a), radius * sin(a)]
- ];
-
-
-/**
-* split_str.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-split_str.html
-*
-**/
-
-function _split_t_by(idxs, t, ts = [], i = -1) =
- i == -1 ? _split_t_by(idxs, t, [sub_str(t, 0, idxs[0])], i + 1) : (
- i == len(idxs) - 1 ? concat(ts, sub_str(t, idxs[i] + 1)) :
- _split_t_by(idxs, t, concat(ts, sub_str(t, idxs[i] + 1, idxs[i + 1])), i + 1)
- );
-
-function split_str(t, delimiter) =
- len(search(delimiter, t)) == 0 ?
- [t] : _split_t_by(search(delimiter, t, 0)[0], t);
-
-/**
-* rounded_square.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_square.html
-*
-**/
-
-
-module rounded_square(size, corner_r, center = false) {
- is_flt = __is_float(size);
- x = is_flt ? size : size[0];
- y = is_flt ? size : size[1];
-
- position = center ? [0, 0] : [x / 2, y / 2];
- points = __trapezium(
- length = x,
- h = y,
- round_r = corner_r
+ leng_trs = len(transversals),
+ leng_tr = len(transversals[0]),
+ lefts = [
+ for(i = [1:leng_trs - 2])
+ let(tr = transversals[leng_trs - i])
+ tr[0]
+ ],
+ rights = [
+ for(i = [1:leng_trs - 2])
+ let(tr = transversals[i])
+ tr[leng_tr - 1]
+ ]
+ ) concat(
+ transversals[0],
+ rights,
+ __reverse(transversals[leng_trs - 1]),
+ lefts
);
- translate(position)
- polygon(points);
-
- // hook for testing
- test_rounded_square(position, points);
-}
-
-// override it to test
-module test_rounded_square(position, points) {
-}
-
/**
-* m_mirror.scad
+* m_translation.scad
*
* @copyright Justin Lin, 2019
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-m_mirror.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-m_translation.html
*
**/
-function m_mirror(v) =
- let(
- nv = v / norm(v),
- txx = -2* nv[0] * nv[0],
- txy = -2* nv[0] * nv[1],
- txz = -2* nv[0] * nv[2],
- tyy = -2* nv[1] * nv[1],
- tyz = -2* nv[1] * nv[2],
- tzz = -2* nv[2] * nv[2]
- )
+
+function _to_3_elems_translation_vect(v) =
+ let(leng = len(v))
+ leng == 3 ? v : (
+ leng == 2 ? [v[0], v[1], 0] : [v[0], 0, 0]
+ );
+
+function _to_translation_vect(v) = __is_float(v) ? [v, 0, 0] : _to_3_elems_translation_vect(v);
+
+function m_translation(v) =
+ let(vt = _to_translation_vect(v))
[
- [1 + txx, txy, txz, 0],
- [txy, 1 + tyy, tyz, 0],
- [txz, tyz, 1 + tzz, 0],
+ [1, 0, 0, vt[0]],
+ [0, 1, 0, vt[1]],
+ [0, 0, 1, vt[2]],
[0, 0, 0, 1]
];
+function __angy_angz(p1, p2) =
+ let(
+ dx = p2[0] - p1[0],
+ dy = p2[1] - p1[1],
+ dz = p2[2] - p1[2],
+ ya = atan2(dz, sqrt(pow(dx, 2) + pow(dy, 2))),
+ za = atan2(dy, dx)
+ ) [ya, za];
+
/**
-* bijection_offset.scad
+* cross_sections.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-cross_sections.html
+*
+**/
+
+
+function cross_sections(shape_pts, path_pts, angles, twist = 0, scale = 1.0) =
+ let(
+ len_path_pts_minus_one = len(path_pts) - 1,
+ sh_pts = len(shape_pts[0]) == 3 ? shape_pts : [for(p = shape_pts) __to3d(p)],
+ pth_pts = len(path_pts[0]) == 3 ? path_pts : [for(p = path_pts) __to3d(p)],
+ scale_step_vt = __is_float(scale) ?
+ [(scale - 1) / len_path_pts_minus_one, (scale - 1) / len_path_pts_minus_one] :
+ [(scale[0] - 1) / len_path_pts_minus_one, (scale[1] - 1) / len_path_pts_minus_one]
+ ,
+ scale_step_x = scale_step_vt[0],
+ scale_step_y = scale_step_vt[1],
+ twist_step = twist / len_path_pts_minus_one
+ )
+ [
+ for(i = [0:len_path_pts_minus_one])
+ [
+ for(p = sh_pts)
+ let(scaled_p = [p[0] * (1 + scale_step_x * i), p[1] * (1 + scale_step_y * i), p[2]])
+ rotate_p(
+ rotate_p(scaled_p, twist_step * i)
+ , angles[i]
+ ) + pth_pts[i]
+ ]
+ ];
+
+
+/**
+* shear.scad
*
* @copyright Justin Lin, 2019
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-bijection_offset.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shear.html
*
**/
-
-function _bijection_inward_edge_normal(edge) =
- let(
- pt1 = edge[0],
- pt2 = edge[1],
- dx = pt2[0] - pt1[0],
- dy = pt2[1] - pt1[1],
- edge_leng = norm([dx, dy])
- )
- [-dy / edge_leng, dx / edge_leng];
-function _bijection_outward_edge_normal(edge) = -1 * _bijection_inward_edge_normal(edge);
+module shear(sx = [0, 0], sy = [0, 0], sz = [0, 0]) {
+ multmatrix(__m_shearing(sx, sy, sz)) children();
+}
-function _bijection_offset_edge(edge, dx, dy) =
- let(
- pt1 = edge[0],
- pt2 = edge[1],
- dxy = [dx, dy]
- )
- [pt1 + dxy, pt2 + dxy];
+/**
+* bend.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-bend.html
+*
+**/
+
+
+module bend(size, angle, frags = 24) {
+ x = size[0];
+ y = size[1];
+ z = size[2];
+ frag_width = x / frags;
+ frag_angle = angle / frags;
+ half_frag_width = 0.5 * frag_width;
+ half_frag_angle = 0.5 * frag_angle;
+ r = half_frag_width / sin(half_frag_angle);
+ h = r * cos(half_frag_angle);
-function _bijection__bijection_offset_edges(edges, d) =
- [
- for(edge = edges)
- let(
- ow_normal = _bijection_outward_edge_normal(edge),
- dx = ow_normal[0] * d,
- dy = ow_normal[1] * d
- )
- _bijection_offset_edge(edge, dx, dy)
+ tri_frag_pts = [
+ [0, 0],
+ [half_frag_width, h],
+ [frag_width, 0],
+ [0, 0]
];
-function bijection_offset(pts, d, epsilon = 0.0001) =
- let(
- es = __lines_from(pts, true),
- offset_es = _bijection__bijection_offset_edges(es, d),
- leng = len(offset_es),
- last_p = __line_intersection(offset_es[leng - 1], offset_es[0], epsilon)
- )
- concat(
- [
- for(i = [0:leng - 2])
- let(
- this_edge = offset_es[i],
- next_edge = offset_es[i + 1],
- p = __line_intersection(this_edge, next_edge, epsilon)
- )
- // p == p to avoid [nan, nan], because [nan, nan] != [nan, nan]
- if(p != [] && p == p) p
- ],
- last_p != [] && last_p == last_p ? [last_p] : []
- );
+ module triangle_frag() {
+ translate([0, -z, 0])
+ linear_extrude(y)
+ polygon(tri_frag_pts);
+ }
+ module get_frag(i) {
+ translate([-frag_width * i - half_frag_width, -h + z, 0])
+ intersection() {
+ translate([frag_width * i, 0, 0])
+ triangle_frag();
+ rotate([90, 0, 0])
+ children();
+ }
+ }
+
+ rotate(90) for(i = [0 : frags - 1]) {
+ rotate(i * frag_angle + half_frag_angle)
+ get_frag(i)
+ children();
+ }
+
+ // hook for testing
+ test_bend_tri_frag(tri_frag_pts, frag_angle);
+}
+
+// override it to test
+module test_bend_tri_frag(points, angle) {
+
+}
+
+/**
+* helix.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-helix.html
+*
+**/
+
+
+function helix(radius, levels, level_dist, vt_dir = "SPI_DOWN", rt_dir = "CT_CLK") =
+ let(
+ is_flt = __is_float(radius),
+ r1 = is_flt ? radius : radius[0],
+ r2 = is_flt ? radius : radius[1],
+ init_r = vt_dir == "SPI_DOWN" ? r2 : r1,
+ _frags = __frags(init_r),
+ h = level_dist * levels,
+ vt_d = vt_dir == "SPI_DOWN" ? 1 : -1,
+ rt_d = rt_dir == "CT_CLK" ? 1 : -1,
+ r_diff = (r1 - r2) * vt_d,
+ h_step = level_dist / _frags * vt_d,
+ r_step = r_diff / (levels * _frags),
+ a_step = 360 / _frags * rt_d,
+ begin_r = vt_dir == "SPI_DOWN" ? r2 : r1,
+ begin_h = vt_dir == "SPI_DOWN" ? h : 0
+ )
+ [
+ for(i = [0:_frags * levels])
+ let(r = begin_r + r_step * i, a = a_step * i)
+ [r * cos(a), r * sin(a), begin_h - h_step * i]
+ ];
/**
* m_scaling.scad
@@ -204,6 +240,65 @@ function m_scaling(s) =
[0, 0, 0, 1]
];
+/**
+* bezier_curve.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-bezier_curve.html
+*
+**/
+
+
+function _combi(n, k) =
+ let(
+ bi_coef = [
+ [1], // n = 0: for padding
+ [1,1], // n = 1: for Linear curves, how about drawing a line directly?
+ [1,2,1], // n = 2: for Quadratic curves
+ [1,3,3,1] // n = 3: for Cubic Bézier curves
+ ]
+ )
+ n < len(bi_coef) ? bi_coef[n][k] : (
+ k == 0 ? 1 : (_combi(n, k - 1) * (n - k + 1) / k)
+ );
+
+function bezier_curve_coordinate(t, pn, n, i = 0) =
+ i == n + 1 ? 0 :
+ (_combi(n, i) * pn[i] * pow(1 - t, n - i) * pow(t, i) +
+ bezier_curve_coordinate(t, pn, n, i + 1));
+
+function _bezier_curve_point(t, points) =
+ let(n = len(points) - 1)
+ [
+ bezier_curve_coordinate(
+ t,
+ [for(p = points) p[0]],
+ n
+ ),
+ bezier_curve_coordinate(
+ t,
+ [for(p = points) p[1]],
+ n
+ ),
+ bezier_curve_coordinate(
+ t,
+ [for(p = points) p[2]],
+ n
+ )
+ ];
+
+function bezier_curve(t_step, points) =
+ let(
+ pts = concat([
+ for(t = [0: ceil(1 / t_step) - 1])
+ _bezier_curve_point(t * t_step, points)
+ ], [_bezier_curve_point(1, points)])
+ )
+ len(points[0]) == 3 ? pts : [for(pt = pts) __to2d(pt)];
+
+
/**
* m_shearing.scad
*
@@ -218,581 +313,161 @@ function m_scaling(s) =
function m_shearing(sx = [0, 0], sy = [0, 0], sz = [0, 0]) = __m_shearing(sx, sy, sz);
/**
-* shape_path_extend.scad
+* trim_shape.scad
*
-* @copyright Justin Lin, 2017
+* @copyright Justin Lin, 2019
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-path_extend.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-trim_shape.html
*
**/
-function _shape_path_extend_az(p1, p2) =
+function _trim_shape_any_intersection_sub(lines, line, lines_leng, i, epsilon) =
let(
- x1 = p1[0],
- y1 = p1[1],
- x2 = p2[0],
- y2 = p2[1]
- ) -90 + atan2((y2 - y1), (x2 - x1));
-
-function _shape_path_first_stroke(stroke_pts, path_pts) =
- let(
- p1 = path_pts[0],
- p2 = path_pts[1],
- a = _shape_path_extend_az(p1, p2)
+ p = __line_intersection(lines[i], line, epsilon)
)
- [
- for(p = stroke_pts)
- rotate_p(p, a) + p1
- ];
+ (p != [] && __in_line(line, p, epsilon) && __in_line(lines[i], p, epsilon)) ? [i, p] : _trim_shape_any_intersection(lines, line, lines_leng, i + 1, epsilon);
-function _shape_path_extend_stroke(stroke_pts, p1, p2, scale_step, i) =
+// return [idx, [x, y]] or []
+function _trim_shape_any_intersection(lines, line, lines_leng, i, epsilon) =
+ i == lines_leng ? [] : _trim_shape_any_intersection_sub(lines, line, lines_leng, i, epsilon);
+
+function _trim_sub(lines, leng, epsilon) =
let(
- leng = norm(__to3d(p2) - __to3d(p1)),
- a = _shape_path_extend_az(p1, p2)
+ current_line = lines[0],
+ next_line = lines[1],
+ lines_from_next = [for(j = [1 : leng - 1]) lines[j]],
+ lines_from_next2 = [for(j = [2 : leng - 1]) lines[j]],
+ current_p = current_line[0],
+ leng_lines_from_next2 = len(lines_from_next2),
+ inter_p = _trim_shape_any_intersection(lines_from_next2, current_line, leng_lines_from_next2, 0, epsilon)
)
- [
- for(p = stroke_pts)
- rotate_p(p * (1 + scale_step * i) + [0, leng], a) + p1
- ];
-
-function _shape_path_extend_inner(stroke_pts, path_pts, leng_path_pts, scale_step, index) =
- index == leng_path_pts ? [] :
- concat(
- [
- _shape_path_extend_stroke(
- stroke_pts,
- path_pts[index - 1],
- path_pts[index],
- scale_step,
- index
- )
- ],
- _shape_path_extend_inner(
- stroke_pts,
- path_pts,
- leng_path_pts,
- scale_step,
- index + 1
+ // no intersecting pt, collect current_p and trim remain lines
+ inter_p == [] ? (concat([current_p], _trim_shape_trim_lines(lines_from_next, epsilon))) : (
+ // collect current_p, intersecting pt and the last pt
+ (leng == 3 || (inter_p[0] == (leng_lines_from_next2 - 1))) ? [current_p, inter_p[1], lines[leng - 1]] : (
+ // collect current_p, intersecting pt and trim remain lines
+ concat([current_p, inter_p[1]],
+ _trim_shape_trim_lines([for(i = [inter_p[0] + 1 : leng_lines_from_next2 - 1]) lines_from_next2[i]], epsilon)
)
- );
-
-function shape_path_extend(stroke_pts, path_pts, scale = 1.0, closed = false) =
- let(
- leng_path_pts = len(path_pts),
- scale_step = (scale - 1) / (leng_path_pts - 1),
- strokes = _shape_path_extend_inner(stroke_pts, path_pts, leng_path_pts, scale_step, 1)
- )
- closed && path_pts[0] == path_pts[leng_path_pts - 1] ?
- __polytransversals(concat(strokes, [strokes[0]])) :
- __polytransversals(
- concat([_shape_path_first_stroke(stroke_pts, path_pts)], strokes)
- );
-
-
-
-/**
-* log.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-log.html
-*
-**/
-
-/*
- The accepted values are "OFF" (-1), "INFO" (20),
- "WARNING" (30), "ERROR" (40) or positive integers.
-*/
-$log_level = "INFO";
-
-module log(level = "INFO", level_color) {
- default_level_ints = [
- ["OFF", -1],
- ["INFO", 20],
- ["WARNING", 30],
- ["ERROR", 40]
- ];
+ )
+ );
- default_level_colors = [
- ["INFO", "green"],
- ["WARNING", "orange"],
- ["ERROR", "red"]
- ];
+function _trim_shape_trim_lines(lines, epsilon) =
+ let(leng = len(lines))
+ leng > 2 ? _trim_sub(lines, leng, epsilon) : _trim_shape_collect_pts_from(lines, leng);
- /*
- The built-in lookup function require integer keys.
- I overwrite it so that using string keys is ok.
- */
- function lookup(key, mappings, i = 0) =
- i == len(mappings) ? key : (
- key == mappings[i][0] ?
- mappings[i][1] :
- lookup(key, mappings, i + 1)
- );
+function _trim_shape_collect_pts_from(lines, leng) =
+ concat([for(line = lines) line[0]], [lines[leng - 1][1]]);
- if($log_level != "OFF") {
- argu_level = lookup(level, default_level_ints);
- golbal_level = lookup(
- $log_level == undef ? "INFO" : $log_level,
- default_level_ints
- );
+function trim_shape(shape_pts, from, to, epsilon = 0.0001) =
+ let(
+ pts = [for(i = [from:to]) shape_pts[i]],
+ trimmed = _trim_shape_trim_lines(__lines_from(pts), epsilon)
+ )
+ len(shape_pts) == len(trimmed) ? trimmed : trim_shape(trimmed, 0, len(trimmed) - 1, epsilon);
- if(argu_level >= golbal_level) {
- c = level_color == undef ? lookup(level, default_level_colors) : level_color;
- lv = str(len(level) == undef ? "LEVEL " : "", level);
-
- // level text
- echo(
- str(
- "",
- "",
- lv,
- "",
- ""
- )
- );
-
- // echo
- for(i = [0:$children - 1]) {
- children(i);
- }
- }
- }
-}
/**
-* archimedean_spiral.scad
-*
+* parse_number.scad
+*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-archimedean_spiral.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib2-parse_number.html
*
**/
-
-function _radian_step(b, theta, l) =
- let(r_square = pow(b * theta, 2))
- acos((2 * r_square - pow(l, 2)) / (2 * r_square)) / 180 * 3.14159;
-
-function _find_radians(b, point_distance, radians, n, count = 1) =
- let(pre_radians = radians[count - 1])
- count == n ? radians : (
- _find_radians(
- b,
- point_distance,
- concat(
- radians,
- [pre_radians + _radian_step(b, pre_radians, point_distance)]
- ),
- n,
- count + 1)
- );
-
-function archimedean_spiral(arm_distance, init_angle, point_distance, num_of_points, rt_dir = "CT_CLK") =
- let(b = arm_distance / 6.28318, init_radian = init_angle *3.14159 / 180)
- [
- for(theta = _find_radians(b, point_distance, [init_radian], num_of_points))
- let(r = b * theta, a = (rt_dir == "CT_CLK" ? 1 : -1) * theta * 57.2958)
- [[r * cos(a), r * sin(a)], a]
- ];
-
-function __reverse(vt) =
- let(leng = len(vt))
- [
- for(i = [0:leng - 1])
- vt[leng - 1 - i]
- ];
-
-function __tr__corner_t_leng_lt_zero(frags, t_sector_angle, l1, l2, h, round_r) =
- let(t_height = tan(t_sector_angle) * l1 - round_r / sin(90 - t_sector_angle) - h / 2)
- [
- for(pt = __pie_for_rounding(round_r, 90 - t_sector_angle, 90, frags * t_sector_angle / 180))
- [pt[0], pt[1] + t_height]
- ];
-
-function __tr_corner_t_leng_gt_or_eq_zero(frags, t_sector_angle, t_leng, h, round_r) =
- let(offset_y = h / 2 - round_r)
- [
- for(pt = __pie_for_rounding(round_r, 90 - t_sector_angle, 90, frags * t_sector_angle / 360))
- [pt[0] + t_leng, pt[1] + offset_y]
- ];
-
-function __tr_corner(frags, b_ang, l1, l2, h, round_r) =
- let(t_leng = l2 - round_r * tan(b_ang / 2))
- t_leng >= 0 ?
- __tr_corner_t_leng_gt_or_eq_zero(frags, b_ang, t_leng, h, round_r) :
- __tr__corner_t_leng_lt_zero(frags, b_ang, l1, l2, h, round_r);
-
-function __tr__corner_b_leng_lt_zero(frags, b_sector_angle, l1, l2, h, round_r) =
+
+function _str_to_int(t) =
let(
- reversed = __tr__corner_t_leng_lt_zero(frags, b_sector_angle, l2, l1, h, round_r),
- leng = len(reversed)
- )
- [
- for(i = [0:leng - 1])
- let(pt = reversed[leng - 1 - i])
- [pt[0], -pt[1]]
- ];
-
-function __br_corner_b_leng_gt_or_eq_zero(frags, b_sector_angle, l1, l2, b_leng, h, round_r) =
- let(half_h = h / 2)
- [
- for(pt = __pie_for_rounding(round_r, -90, -90 + b_sector_angle, frags * b_sector_angle / 360))
- [pt[0] + b_leng, pt[1] + round_r - half_h]
- ];
-
-function __br_corner(frags, b_ang, l1, l2, h, round_r) =
- let(b_leng = l1 - round_r / tan(b_ang / 2))
- b_leng >= 0 ?
- __br_corner_b_leng_gt_or_eq_zero(frags, 180 - b_ang, l1, l2, b_leng, h, round_r) :
- __tr__corner_b_leng_lt_zero(frags, 180 - b_ang, l1, l2, h, round_r);
-
-function __half_trapezium(length, h, round_r) =
- let(
- is_flt = __is_float(length),
- l1 = is_flt ? length : length[0],
- l2 = is_flt ? length : length[1],
- frags = __frags(round_r),
- b_ang = atan2(h, l1 - l2),
- br_corner = __br_corner(frags, b_ang, l1, l2, h, round_r),
- tr_corner = __tr_corner(frags, b_ang, l1, l2, h, round_r)
- )
- concat(
- br_corner,
- tr_corner
- );
-
-/**
-* m_cumulate.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-m_cumulate.html
-*
-**/
-
-function _m_cumulate(matrice, i) =
- i == len(matrice) - 2 ?
- matrice[i] * matrice[i + 1] :
- matrice[i] * _m_cumulate(matrice, i + 1);
-
-function m_cumulate(matrice) =
- len(matrice) == 1 ? matrice[0] : _m_cumulate(matrice, 0);
+ dict = [["0", 0], ["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5], ["6", 6], ["7", 7], ["8", 8], ["9", 9]],
+ n = dict[search(t, dict)[0]][1]
+ ) n;
+function _parse_positive_int(t, value = 0, i = 0) =
+ i == len(t) ? value : _parse_positive_int(t, value * pow(10, i) + _str_to_int(t[i]), i + 1);
+
+function _parse_positive_decimal(t, value = 0, i = 0) =
+ i == len(t) ? value : _parse_positive_decimal(t, value + _str_to_int(t[i]) * pow(10, -(i + 1)), i + 1);
+
+function _parse_positive_number(t) =
+ len(search(".", t)) == 0 ? _parse_positive_int(t) :
+ _parse_positive_int(split_str(t, ".")[0]) + _parse_positive_decimal(split_str(t, ".")[1]);
+
+function parse_number(t) =
+ t[0] == "-" ? -_parse_positive_number(sub_str(t, 1, len(t))) : _parse_positive_number(t);
+
/**
-* shape_trapezium.scad
+* polyline2d.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_trapezium.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-polyline2d.html
*
**/
+module polyline2d(points, width, startingStyle = "CAP_SQUARE", endingStyle = "CAP_SQUARE") {
+ leng_pts = len(points);
-function shape_trapezium(length, h, corner_r = 0) =
- __trapezium(
- length = length,
- h = h,
- round_r = corner_r
- );
-
+ module line_segment(index) {
+ styles = index == 1 ? [startingStyle, "CAP_ROUND"] : (
+ index == leng_pts - 1 ? ["CAP_BUTT", endingStyle] : [
+ "CAP_BUTT", "CAP_ROUND"
+ ]
+ );
-/**
-* ellipse_extrude.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-ellipse_extrude.html
-*
-**/
-
-module ellipse_extrude(semi_minor_axis, height, center = false, convexity = 10, twist = 0, slices = 20) {
- h = height == undef ? semi_minor_axis : (
- // `semi_minor_axis` is always equal to or greater than `height`.
- height > semi_minor_axis ? semi_minor_axis : height
- );
- angle = asin(h / semi_minor_axis) / slices;
-
- function f_extrude(i = 1) =
- i <= slices ?
- concat(
- [
- [
- cos(angle * i) / cos(angle * (i - 1)),
- semi_minor_axis * sin(angle * i)
- ]
- ],
- f_extrude(i + 1)
- ) : [];
-
- fzs = f_extrude();
- len_fzs = len(fzs);
-
- function accm_fs(pre_f = 1, i = 0) =
- i < len_fzs ?
- concat(
- [pre_f * fzs[i][0]],
- accm_fs(pre_f * fzs[i][0], i + 1)
- ) : [];
-
- child_fs = concat([1], accm_fs());
- pre_zs = concat(
- [0],
- [
- for(i = [0:len_fzs - 1])
- fzs[i][1]
- ]
- );
-
- module extrude() {
- for(i = [0:len_fzs - 1]) {
- f = fzs[i][0];
- z = fzs[i][1];
-
- translate([0, 0, pre_zs[i]])
- rotate(-twist / slices * i)
- linear_extrude(
- z - pre_zs[i],
- convexity = convexity,
- twist = twist / slices,
- slices = 1,
- scale = f
- ) scale(child_fs[i]) children();
- }
- }
-
- center_offset = [0, 0, center == true ? -h / 2 : 0];
- translate(center_offset)
- extrude()
- children();
-
- // hook for testing
- test_ellipse_extrude_fzc(child_fs, pre_zs, center_offset);
-}
-
-// override for testing
-module test_ellipse_extrude_fzc(child_fs, pre_zs, center_offset) {
-
-}
-
-/**
-* line3d.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-line3d.html
-*
-**/
-
-
-module line3d(p1, p2, thickness, p1Style = "CAP_CIRCLE", p2Style = "CAP_CIRCLE") {
- r = thickness / 2;
-
- frags = __nearest_multiple_of_4(__frags(r));
- half_fa = 180 / frags;
-
- dx = p2[0] - p1[0];
- dy = p2[1] - p1[1];
- dz = p2[2] - p1[2];
-
- length = sqrt(pow(dx, 2) + pow(dy, 2) + pow(dz, 2));
- ay = 90 - atan2(dz, sqrt(pow(dx, 2) + pow(dy, 2)));
- az = atan2(dy, dx);
-
- angles = [0, ay, az];
-
- module cap_with(p) {
- translate(p)
- rotate(angles)
- children();
- }
-
- module cap_butt() {
- cap_with(p1)
- linear_extrude(length)
- circle(r, $fn = frags);
+ p1 = points[index - 1];
+ p2 = points[index];
+ p1Style = styles[0];
+ p2Style = styles[1];
+ line2d(points[index - 1], points[index], width,
+ p1Style = p1Style, p2Style = p2Style);
+
// hook for testing
- test_line3d_butt(p1, r, frags, length, angles);
+ test_line_segment(index, p1, p2, width, p1Style, p2Style);
}
- module cap(p, style) {
- if(style == "CAP_CIRCLE") {
- cap_leng = r / 1.414;
- cap_with(p)
- linear_extrude(cap_leng * 2, center = true)
- circle(r, $fn = frags);
-
- // hook for testing
- test_line3d_cap(p, r, frags, cap_leng, angles);
- } else if(style == "CAP_SPHERE") {
- cap_leng = r / cos(half_fa);
- cap_with(p)
- sphere(cap_leng, $fn = frags);
-
- // hook for testing
- test_line3d_cap(p, r, frags, cap_leng, angles);
- }
+ module polyline2d_inner(index) {
+ if(index < leng_pts) {
+ line_segment(index);
+ polyline2d_inner(index + 1);
+ }
}
-
- cap_butt();
- cap(p1, p1Style);
- cap(p2, p2Style);
-}
-
-// Override them to test
-module test_line3d_butt(p, r, frags, length, angles) {
-
-}
-
-module test_line3d_cap(p, r, frags, cap_leng, angles) {
-
-}
-
-/**
-* arc_shape.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-arc_path.html
-*
-**/
-
-
-function arc_path(radius, angle) =
- let(
- frags = __frags(radius),
- a_step = 360 / frags,
- angles = __is_float(angle) ? [0, angle] : angle,
- m = floor(angles[0] / a_step) + 1,
- n = floor(angles[1] / a_step),
- points = concat([__ra_to_xy(__edge_r_begin(radius, angles[0], a_step, m), angles[0])],
- m > n ? [] : [
- for(i = [m:n])
- __ra_to_xy(radius, a_step * i)
- ],
- angles[1] == a_step * n ? [] : [__ra_to_xy(__edge_r_end(radius, angles[1], a_step, n), angles[1])])
- ) points;
-
-/**
-* box_extrude.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-box_extrude.html
-*
-**/
-
-module box_extrude(height, shell_thickness, offset_mode = "delta", chamfer = false) {
- linear_extrude(shell_thickness)
- children();
-
- linear_extrude(height)
- difference() {
- children();
- if(offset_mode == "delta") {
- offset(delta = -shell_thickness, chamfer = chamfer)
- children();
- } else {
- offset(r = -shell_thickness)
- children();
- }
- }
-}
-
-
-
-/**
-* m_translation.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-m_translation.html
-*
-**/
-
-
-function _to_3_elems_translation_vect(v) =
- let(leng = len(v))
- leng == 3 ? v : (
- leng == 2 ? [v[0], v[1], 0] : [v[0], 0, 0]
- );
-
-function _to_translation_vect(v) = __is_float(v) ? [v, 0, 0] : _to_3_elems_translation_vect(v);
-
-function m_translation(v) =
- let(vt = _to_translation_vect(v))
- [
- [1, 0, 0, vt[0]],
- [0, 1, 0, vt[1]],
- [0, 0, 1, vt[2]],
- [0, 0, 0, 1]
- ];
-
-/**
-* pie.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-pie.html
-*
-**/
-
-
-module pie(radius, angle) {
- polygon(__shape_pie(radius, angle));
-}
-
-/**
-* rounded_cylinder.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_cylinder.html
-*
-**/
-
-
-module rounded_cylinder(radius, h, round_r, convexity = 2, center = false) {
- r_corners = __half_trapezium(radius, h, round_r);
-
- shape_pts = concat(
- [[0, -h/2]],
- r_corners,
- [[0, h/2]]
- );
-
- center_pt = center ? [0, 0, 0] : [0, 0, h/2];
-
- translate(center ? [0, 0, 0] : [0, 0, h/2])
- rotate(180)
- rotate_extrude(convexity = convexity)
- polygon(shape_pts);
-
- // hook for testing
- test_center_half_trapezium(center_pt, shape_pts);
+ polyline2d_inner(1);
}
// override it to test
-module test_center_half_trapezium(center_pt, shape_pts) {
-
+module test_line_segment(index, point1, point2, width, p1Style, p2Style) {
+
}
+/**
+* midpt_smooth.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-midpt_smooth.html
+*
+**/
+
+function _midpt_smooth_sub(points, iend, i, closed = false) =
+ i == iend ? (
+ closed ? [(points[i] + points[0]) / 2]
+ : []
+ ) : concat([(points[i] + points[i + 1]) / 2], _midpt_smooth_sub(points, iend, i + 1, closed));
+
+function midpt_smooth(points, n, closed = false) =
+ let(
+ smoothed = _midpt_smooth_sub(points, len(points) - 1, 0, closed)
+ )
+ n == 1 ? smoothed : midpt_smooth(smoothed, n - 1, closed);
function __frags(radius) = $fn > 0 ?
($fn >= 3 ? $fn : 3) :
@@ -862,1494 +537,55 @@ function turtle2d(cmd, arg1, arg2, arg3) =
/**
-* shape_ellipse.scad
+* shape_cyclicpolygon.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_ellipse.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_cyclicpolygon.html
*
**/
-function shape_ellipse(axes) =
+function shape_cyclicpolygon(sides, circle_r, corner_r) =
let(
- frags = __frags(axes[0]),
- step_a = 360 / frags,
- shape_pts = [
- for(a = [0:step_a:360 - step_a])
- [axes[0] * cos(a), axes[1] * sin(a)]
+ frag_a = 360 / sides,
+ corner_a = (180 - frag_a),
+ corner_circle_a = 180 - corner_a,
+ half_corner_circle_a = corner_circle_a / 2,
+ corner_circle_center = circle_r - corner_r / sin(corner_a / 2),
+ first_corner = [
+ for(
+ pt = __pie_for_rounding(
+ corner_r,
+ -half_corner_circle_a,
+ half_corner_circle_a,
+ __frags(corner_r) * corner_circle_a / 360
+ )
+ )
+ [pt[0] + corner_circle_center, pt[1]]
]
- ) shape_pts;
-/**
-* shape_star.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_starburst.html
-*
-**/
-
-function __outer_points_shape_starburst(r1, r2, n) =
- let(
- a = 360 / n
)
- [for(i = [0:n-1]) [r1 * cos(a * i), r1 * sin(a * i)]];
-function __inner_points_shape_starburst(r1, r2, n) =
- let (
- a = 360 / n,
- half_a = a / 2
- )
- [for(i = [0:n-1]) [r2 * cos(a * i + half_a), r2 * sin(a * i + half_a)]];
-
-function __one_by_one_shape_starburst(outer_points, inner_points, i = 0) =
- len(outer_points) == i ? [] :
- concat([outer_points[i], inner_points[i]], __one_by_one_shape_starburst(outer_points, inner_points, i + 1));
-
-function shape_starburst(r1, r2, n) = __one_by_one_shape_starburst(
- __outer_points_shape_starburst(r1, r2, n), __inner_points_shape_starburst(r1, r2, n)
-);
-
-function __in_line(line_pts, pt, epsilon = 0.0001) =
- let(
- pts = len(line_pts[0]) == 2 ? [for(p = line_pts) __to3d(p)] : line_pts,
- pt3d = len(pt) == 2 ? __to3d(pt) : pt,
- v1 = pts[0] - pt3d,
- v2 = pts[1] - pt3d
- )
- (norm(cross(v1, v2)) < epsilon) && ((v1 * v2) <= epsilon);
-
-/**
-* polyline3d.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-polyline3d.html
-*
-**/
-
-module polyline3d(points, thickness, startingStyle = "CAP_CIRCLE", endingStyle = "CAP_CIRCLE") {
- leng_pts = len(points);
-
- module line_segment(index) {
- styles = index == 1 ? [startingStyle, "CAP_BUTT"] : (
- index == leng_pts - 1 ? ["CAP_SPHERE", endingStyle] : [
- "CAP_SPHERE", "CAP_BUTT"
- ]
- );
-
- p1 = points[index - 1];
- p2 = points[index];
- p1Style = styles[0];
- p2Style = styles[1];
-
- line3d(p1, p2, thickness,
- p1Style = p1Style, p2Style = p2Style);
-
- // hook for testing
- test_line3d_segment(index, p1, p2, thickness, p1Style, p2Style);
- }
-
- module polyline3d_inner(index) {
- if(index < leng_pts) {
- line_segment(index);
- polyline3d_inner(index + 1);
- }
- }
-
- polyline3d_inner(1);
-}
-
-// override it to test
-module test_line3d_segment(index, point1, point2, thickness, p1Style, p2Style) {
-
-}
-
-/**
-* turtle3d.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-turtle3d.html
-*
-**/
-
-function _turtle3d_x(pt) = pt[0];
-function _turtle3d_y(pt) = pt[1];
-function _turtle3d_z(pt) = pt[2];
-
-function _turtle3d_pt3D(x, y, z) = [x, y, z];
-
-function _turtle3d_create(pt, unit_vts) = [pt, unit_vts];
-function _turtle3d_create_default() = _turtle3d_create(
- _turtle3d_pt3D(0, 0, 0),
- // unit vectors from the turtle's viewpoint
- [_turtle3d_pt3D(1, 0, 0), _turtle3d_pt3D(0, 1, 0), _turtle3d_pt3D(0, 0, 1)]
-);
-
-function _turtle3d_plus(pt, n) =
- _turtle3d_pt3D(_turtle3d_x(pt) + n, _turtle3d_y(pt) + n, _turtle3d_z(pt) + n);
-function _turtle3d_minus(pt, n) =
- _turtle3d_pt3D(_turtle3d_x(pt) - n, _turtle3d_y(pt) - n, _turtle3d_z(pt) + n);
-function _turtle3d_mlt(pt, n) =
- _turtle3d_pt3D(_turtle3d_x(pt) * n, _turtle3d_y(pt) * n, _turtle3d_z(pt) * n);
-function _turtle3d_div(pt, n) =
- _turtle3d_pt3D(_turtle3d_x(pt) / n, _turtle3d_y(pt) / n, _turtle3d_z(pt) / n);
-function _turtle3d_neg(pt, n) =
- _turtle3d_mlt(pt, -1);
-
-function _turtle3d_ptPlus(pt1, pt2) =
- _turtle3d_pt3D(
- _turtle3d_x(pt1) + _turtle3d_x(pt2),
- _turtle3d_y(pt1) + _turtle3d_y(pt2),
- _turtle3d_z(pt1) + _turtle3d_z(pt2)
- );
-
-
-function _turtle3d_pt(turtle) = turtle[0];
-function _turtle3d_unit_vts(turtle) = turtle[1];
-
-// forward the turtle in the x' direction
-function _turtle3d_xu_move(turtle, leng) = _turtle3d_create(
- _turtle3d_ptPlus(_turtle3d_pt(turtle), _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], leng)),
- _turtle3d_unit_vts(turtle)
-);
-
-// forward the turtle in the y' direction
-function _turtle3d_yu_move(turtle, leng) = _turtle3d_create(
- _turtle3d_ptPlus(_turtle3d_pt(turtle), _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], leng)),
- _turtle3d_unit_vts(turtle)
-);
-
-// forward the turtle in the z' direction
-function _turtle3d_zu_move(turtle, leng) = _turtle3d_create(
- _turtle3d_ptPlus(
- _turtle3d_pt(turtle),
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], leng)
- ),
- _turtle3d_unit_vts(turtle)
-);
-
-// turn the turtle around the x'-axis
-// return a new unit vector
-function _turtle3d_xu_turn(turtle, a) =
- let(cosa = cos(a), sina = sin(a))
- _turtle3d_create(
- _turtle3d_pt(turtle),
+ concat(
+ first_corner,
[
- _turtle3d_unit_vts(turtle)[0],
- _turtle3d_ptPlus(
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], cosa),
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], sina)
- ),
- _turtle3d_ptPlus(
- _turtle3d_mlt(_turtle3d_neg(_turtle3d_unit_vts(turtle)[1]), sina),
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], cosa)
- )
+ for(side = [1:sides - 1])
+ for(pt = first_corner)
+ let(
+ a = frag_a * side,
+ x = pt[0],
+ y = pt[1],
+ sina = sin(a),
+ cosa = cos(a)
+ )
+ [
+ x * cosa - y * sina,
+ x * sina + y * cosa
+ ]
]
);
-// turn the turtle around the y'-axis
-// return a new unit vector
-function _turtle3d_yu_turn(turtle, a) =
- let(cosa = cos(a), sina = sin(a))
- _turtle3d_create(
- _turtle3d_pt(turtle),
- [
- _turtle3d_ptPlus(
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], cosa),
- _turtle3d_mlt(_turtle3d_neg(_turtle3d_unit_vts(turtle)[2]), sina)
- ),
- _turtle3d_unit_vts(turtle)[1],
- _turtle3d_ptPlus(
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], sina),
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], cosa)
- )
- ]
- );
-
-// turn the turtle around the z'-axis
-// return a new unit vector
-function _turtle3d_zu_turn(turtle, a) =
- let(cosa = cos(a), sina = sin(a))
- _turtle3d_create(
- _turtle3d_pt(turtle),
- [
- _turtle3d_ptPlus(
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], cosa),
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], sina)
- ),
- _turtle3d_ptPlus(
- _turtle3d_mlt(_turtle3d_neg(_turtle3d_unit_vts(turtle)[0]), sina),
- _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], cosa)
- ),
- _turtle3d_unit_vts(turtle)[2],
- ]
- );
-
-function _turtle3d_create_cmd(arg1, arg2) =
- (arg1 == undef && arg2 == undef) ? _turtle3d_create_default() : (
- (arg1 != undef && arg2 != undef) ? _turtle3d_create(arg1, arg2) : undef
- );
-
-function _turtle3d_chain_move(cmd, arg1, arg2) =
- cmd == "xu_move" ? _turtle3d_xu_move(arg1, arg2) : (
- cmd == "yu_move" ? _turtle3d_yu_move(arg1, arg2) : (
- cmd == "zu_move" ? _turtle3d_zu_move(arg1, arg2) : _turtle3d_chain_turn(cmd, arg1, arg2)
- )
- );
-
-function _turtle3d_chain_turn(cmd, arg1, arg2) =
- cmd == "xu_turn" ? _turtle3d_xu_turn(arg1, arg2) : (
- cmd == "yu_turn" ? _turtle3d_yu_turn(arg1, arg2) : (
- cmd == "zu_turn" ? _turtle3d_zu_turn(arg1, arg2) : _turtle3d_chain_one_arg(cmd, arg1)
- )
- );
-
-function _turtle3d_chain_one_arg(cmd, arg) =
- cmd == "pt" ? _turtle3d_pt(arg) : (
- cmd == "unit_vts" ? _turtle3d_unit_vts(arg) : undef
- );
-
-function turtle3d(cmd, arg1, arg2) =
- cmd == "create" ?
- _turtle3d_create_cmd(arg1, arg2) :
- _turtle3d_chain_move(cmd, arg1, arg2);
-
-/**
-* sub_str.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-sub_str.html
-*
-**/
-
-function sub_str(t, begin, end, result = "") =
- end == undef ? sub_str(t, begin, len(t)) : (
- begin == end ? result : sub_str(t, begin + 1, end, str(result, t[begin]))
- );
-
-
-/**
-* cross_sections.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-cross_sections.html
-*
-**/
-
-
-function cross_sections(shape_pts, path_pts, angles, twist = 0, scale = 1.0) =
- let(
- len_path_pts_minus_one = len(path_pts) - 1,
- sh_pts = len(shape_pts[0]) == 3 ? shape_pts : [for(p = shape_pts) __to3d(p)],
- pth_pts = len(path_pts[0]) == 3 ? path_pts : [for(p = path_pts) __to3d(p)],
- scale_step_vt = __is_float(scale) ?
- [(scale - 1) / len_path_pts_minus_one, (scale - 1) / len_path_pts_minus_one] :
- [(scale[0] - 1) / len_path_pts_minus_one, (scale[1] - 1) / len_path_pts_minus_one]
- ,
- scale_step_x = scale_step_vt[0],
- scale_step_y = scale_step_vt[1],
- twist_step = twist / len_path_pts_minus_one
- )
- [
- for(i = [0:len_path_pts_minus_one])
- [
- for(p = sh_pts)
- let(scaled_p = [p[0] * (1 + scale_step_x * i), p[1] * (1 + scale_step_y * i), p[2]])
- rotate_p(
- rotate_p(scaled_p, twist_step * i)
- , angles[i]
- ) + pth_pts[i]
- ]
- ];
-
-
-/**
-* archimedean_spiral_extrude.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-archimedean_spiral_extrude.html
-*
-**/
-
-module archimedean_spiral_extrude(shape_pts, arm_distance, init_angle, point_distance, num_of_points,
- rt_dir = "CT_CLK", twist = 0, scale = 1.0, triangles = "SOLID") {
- points_angles = archimedean_spiral(
- arm_distance = arm_distance,
- init_angle = init_angle,
- point_distance = point_distance,
- num_of_points = num_of_points,
- rt_dir = rt_dir
- );
-
- clk_a = rt_dir == "CT_CLK" ? 0 : 180;
-
- points = [for(pa = points_angles) pa[0]];
- angles = [
- for(pa = points_angles)
- [90, 0, pa[1] + clk_a]
- ];
-
- sections = cross_sections(shape_pts, points, angles, twist, scale);
-
- polysections(
- sections,
- triangles = triangles
- );
-
- // testing hook
- test_archimedean_spiral_extrude(sections);
-}
-
-// override it to test
-module test_archimedean_spiral_extrude(sections) {
-
-}
-
-/**
-* in_polyline.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-in_polyline.html
-*
-**/
-
-
-function _in_polyline_sub(pts, pt, epsilon, iend, i = 0) =
- i == iend ? false : (
- __in_line([pts[i], pts[i + 1]], pt, epsilon) ? true :
- _in_polyline_sub(pts, pt, epsilon, iend, i + 1)
- );
-
-function in_polyline(line_pts, pt, epsilon = 0.0001) =
- _in_polyline_sub(line_pts, pt, epsilon, len(line_pts) - 1);
-
-
-/**
-* voronoi3d.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-voronoi3d.html
-*
-**/
-
-
-// slow but workable
-
-module voronoi3d(points, space_size = "auto", spacing = 1) {
- xs = [for(p = points) p[0]];
- ys = [for(p = points) abs(p[1])];
- zs = [for(p = points) abs(p[2])];
-
- space_size = max([max(xs) - min(xs), max(ys) - min(ys), max(zs) - min(zs)]);
- half_space_size = 0.5 * space_size;
- offset_leng = spacing * 0.5 + half_space_size;
-
- function normalize(v) = v / norm(v);
-
- module space(pt) {
- intersection_for(p = points) {
- if(pt != p) {
- v = p - pt;
- ryz = __angy_angz(p, pt);
-
- translate((pt + p) / 2 - normalize(v) * offset_leng)
- rotate([0, -ryz[0], ryz[1]])
- cube([space_size, space_size * 2, space_size * 2], center = true);
- }
- }
- }
-
- for(p = points) {
- space(p);
- }
-}
-
-/**
-* crystal_ball.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-crystal_ball.html
-*
-**/
-
-
-module crystal_ball(radius, theta = 360, phi = 180) {
- phis = __is_float(phi) ? [0, phi] : phi;
-
- frags = __frags(radius);
-
- shape_pts = shape_pie(
- radius,
- [90 - phis[1], 90 - phis[0]],
- $fn = __nearest_multiple_of_4(frags)
- );
-
- // _hole_r = 0.0005 for avoiding warnings when using 2015.03
- // I downloaded 2017.01.20 and found that the problem is solved.
- _hole_r = version_num() >= 20170120 ? 0 : 0.0005;
- ring_extrude(
- shape_pts,
- angle = theta,
- radius = _hole_r,
- $fn = frags
- );
-
- // hook for testing
- test_crystal_ball_pie(shape_pts);
-}
-
-// override it to test
-module test_crystal_ball_pie(shape_pts) {
-
-}
-
-/**
-* trim_shape.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-trim_shape.html
-*
-**/
-
-
-function _trim_shape_any_intersection_sub(lines, line, lines_leng, i, epsilon) =
- let(
- p = __line_intersection(lines[i], line, epsilon)
- )
- (p != [] && __in_line(line, p, epsilon) && __in_line(lines[i], p, epsilon)) ? [i, p] : _trim_shape_any_intersection(lines, line, lines_leng, i + 1, epsilon);
-
-// return [idx, [x, y]] or []
-function _trim_shape_any_intersection(lines, line, lines_leng, i, epsilon) =
- i == lines_leng ? [] : _trim_shape_any_intersection_sub(lines, line, lines_leng, i, epsilon);
-
-function _trim_sub(lines, leng, epsilon) =
- let(
- current_line = lines[0],
- next_line = lines[1],
- lines_from_next = [for(j = [1 : leng - 1]) lines[j]],
- lines_from_next2 = [for(j = [2 : leng - 1]) lines[j]],
- current_p = current_line[0],
- leng_lines_from_next2 = len(lines_from_next2),
- inter_p = _trim_shape_any_intersection(lines_from_next2, current_line, leng_lines_from_next2, 0, epsilon)
- )
- // no intersecting pt, collect current_p and trim remain lines
- inter_p == [] ? (concat([current_p], _trim_shape_trim_lines(lines_from_next, epsilon))) : (
- // collect current_p, intersecting pt and the last pt
- (leng == 3 || (inter_p[0] == (leng_lines_from_next2 - 1))) ? [current_p, inter_p[1], lines[leng - 1]] : (
- // collect current_p, intersecting pt and trim remain lines
- concat([current_p, inter_p[1]],
- _trim_shape_trim_lines([for(i = [inter_p[0] + 1 : leng_lines_from_next2 - 1]) lines_from_next2[i]], epsilon)
- )
- )
- );
-
-function _trim_shape_trim_lines(lines, epsilon) =
- let(leng = len(lines))
- leng > 2 ? _trim_sub(lines, leng, epsilon) : _trim_shape_collect_pts_from(lines, leng);
-
-function _trim_shape_collect_pts_from(lines, leng) =
- concat([for(line = lines) line[0]], [lines[leng - 1][1]]);
-
-function trim_shape(shape_pts, from, to, epsilon = 0.0001) =
- let(
- pts = [for(i = [from:to]) shape_pts[i]],
- trimmed = _trim_shape_trim_lines(__lines_from(pts), epsilon)
- )
- len(shape_pts) == len(trimmed) ? trimmed : trim_shape(trimmed, 0, len(trimmed) - 1, epsilon);
-
-
-/**
-* stereographic_extrude.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-stereographic_extrude.html
-*
-**/
-
-module stereographic_extrude(shadow_side_leng) {
- half_side_length = shadow_side_leng / 2;
- outer_sphere_r = half_side_length / 3;
- a = atan(sqrt(2) * half_side_length / (2 * outer_sphere_r));
- inner_sphere_r = outer_sphere_r * sin(a);
-
- intersection() {
- translate([0, 0, outer_sphere_r]) difference() {
- sphere(outer_sphere_r);
- sphere(outer_sphere_r / 2 + inner_sphere_r / 2);
-
- translate([0, 0, outer_sphere_r / 2])
- linear_extrude(outer_sphere_r)
- circle(inner_sphere_r * cos(a));
- }
-
- linear_extrude(outer_sphere_r * 2, scale = 0.01)
- children();
- }
-
- // hook for testing
- test_stereographic_extrude_rs(outer_sphere_r, inner_sphere_r);
-}
-
-// override for testing
-module test_stereographic_extrude_rs(outer_sphere_r, inner_sphere_r) {
-
-}
-
-function __m_shearing(sx, sy, sz) =
- let(
- sx_along_y = sx[0],
- sx_along_z = sx[1],
- sy_along_x = sy[0],
- sy_along_z = sy[1],
- sz_along_x = sz[0],
- sz_along_y = sz[1]
- )
- [
- [1, sx_along_y, sx_along_z, 0],
- [sy_along_x, 1, sy_along_z, 0],
- [sz_along_x, sz_along_y, 1, 0],
- [0, 0, 0, 1]
- ];
-
-/**
-* polytransversals.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-polytransversals.html
-*
-**/
-
-
-module polytransversals(transversals) {
- polygon(
- __polytransversals(transversals)
- );
-}
-
-/**
-* hexagons.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-hexagons.html
-*
-**/
-
-module hexagons(radius, spacing, levels) {
- beginning_n = 2 * levels - 1;
- offset_x = radius * cos(30);
- offset_y = radius + radius * sin(30);
- r_hexagon = radius - spacing / 2;
- offset_step = 2 * offset_x;
- center_offset = 2 * (offset_x - offset_x * levels);
-
- module hexagon() {
- rotate(30)
- circle(r_hexagon, $fn = 6);
- }
-
- function hexagons_pts(hex_datum) =
- let(
- tx = hex_datum[0][0],
- ty = hex_datum[0][1],
- n = hex_datum[1],
- offset_xs = [for(i = [0:n - 1]) i * offset_step + center_offset]
- )
- [
- for(x = offset_xs) [x + tx, ty]
- ];
-
- module line_hexagons(hex_datum) {
- tx = hex_datum[0][0];
- ty = hex_datum[0][1];
- n = hex_datum[1];
-
- offset_xs = [for(i = [0:n - 1]) i * offset_step + center_offset];
- for(x = offset_xs) {
- p = [x + tx, ty, 0];
- translate(p)
- hexagon();
- }
- }
-
- upper_hex_data = levels > 1 ? [
- for(i = [1:beginning_n - levels])
- let(
- x = offset_x * i,
- y = offset_y * i,
- n = beginning_n - i
- ) [[x, y], n]
- ] : [];
-
- lower_hex_data = levels > 1 ? [
- for(hex_datum = upper_hex_data)
- [[hex_datum[0][0], -hex_datum[0][1]], hex_datum[1]]
- ] : [];
-
- total_hex_data = concat(
- [
- [[0, 0], beginning_n] // first line
- ],
- upper_hex_data,
- lower_hex_data
- );
-
- pts_all_lines = [
- for(hex_datum = total_hex_data)
- hexagons_pts(hex_datum)
- ];
-
- for(pts_one_line = pts_all_lines) {
- for(pt = pts_one_line) {
- translate(pt)
- hexagon();
- }
- }
-
- test_each_hexagon(r_hexagon, pts_all_lines);
-}
-
-// override it to test
-module test_each_hexagon(hex_r, pts_all_lines) {
-
-}
-
-/**
-* bend.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-bend.html
-*
-**/
-
-
-module bend(size, angle, frags = 24) {
- x = size[0];
- y = size[1];
- z = size[2];
- frag_width = x / frags;
- frag_angle = angle / frags;
- half_frag_width = 0.5 * frag_width;
- half_frag_angle = 0.5 * frag_angle;
- r = half_frag_width / sin(half_frag_angle);
- h = r * cos(half_frag_angle);
-
- tri_frag_pts = [
- [0, 0],
- [half_frag_width, h],
- [frag_width, 0],
- [0, 0]
- ];
-
- module triangle_frag() {
- translate([0, -z, 0])
- linear_extrude(y)
- polygon(tri_frag_pts);
- }
-
- module get_frag(i) {
- translate([-frag_width * i - half_frag_width, -h + z, 0])
- intersection() {
- translate([frag_width * i, 0, 0])
- triangle_frag();
- rotate([90, 0, 0])
- children();
- }
- }
-
- rotate(90) for(i = [0 : frags - 1]) {
- rotate(i * frag_angle + half_frag_angle)
- get_frag(i)
- children();
- }
-
- // hook for testing
- test_bend_tri_frag(tri_frag_pts, frag_angle);
-}
-
-// override it to test
-module test_bend_tri_frag(points, angle) {
-
-}
-
-function __to3d(p) = [p[0], p[1], 0];
-
-/**
-* hull_polyline3d.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/hull_polyline3d.html
-*
-**/
-
-module hull_polyline3d(points, thickness) {
- half_thickness = thickness / 2;
- leng = len(points);
-
- module hull_line3d(index) {
- point1 = points[index - 1];
- point2 = points[index];
-
- hull() {
- translate(point1)
- sphere(half_thickness);
- translate(point2)
- sphere(half_thickness);
- }
-
- // hook for testing
- test_line_segment(index, point1, point2, half_thickness);
- }
-
- module polyline3d_inner(index) {
- if(index < leng) {
- hull_line3d(index);
- polyline3d_inner(index + 1);
- }
- }
-
- polyline3d_inner(1);
-}
-
-// override it to test
-module test_line_segment(index, point1, point2, radius) {
-
-}
-
-/**
-* bezier_curve.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-bezier_curve.html
-*
-**/
-
-
-function _combi(n, k) =
- let(
- bi_coef = [
- [1], // n = 0: for padding
- [1,1], // n = 1: for Linear curves, how about drawing a line directly?
- [1,2,1], // n = 2: for Quadratic curves
- [1,3,3,1] // n = 3: for Cubic Bézier curves
- ]
- )
- n < len(bi_coef) ? bi_coef[n][k] : (
- k == 0 ? 1 : (_combi(n, k - 1) * (n - k + 1) / k)
- );
-
-function bezier_curve_coordinate(t, pn, n, i = 0) =
- i == n + 1 ? 0 :
- (_combi(n, i) * pn[i] * pow(1 - t, n - i) * pow(t, i) +
- bezier_curve_coordinate(t, pn, n, i + 1));
-
-function _bezier_curve_point(t, points) =
- let(n = len(points) - 1)
- [
- bezier_curve_coordinate(
- t,
- [for(p = points) p[0]],
- n
- ),
- bezier_curve_coordinate(
- t,
- [for(p = points) p[1]],
- n
- ),
- bezier_curve_coordinate(
- t,
- [for(p = points) p[2]],
- n
- )
- ];
-
-function bezier_curve(t_step, points) =
- let(
- pts = concat([
- for(t = [0: ceil(1 / t_step) - 1])
- _bezier_curve_point(t * t_step, points)
- ], [_bezier_curve_point(1, points)])
- )
- len(points[0]) == 3 ? pts : [for(pt = pts) __to2d(pt)];
-
-
-/**
-* multi_line_text.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-multi_line_text.html
-*
-**/
-module multi_line_text(lines, line_spacing = 15, size = 10, font = "Arial", halign = "left", valign = "baseline", direction = "ltr", language = "en", script = "latin"){
- to = len(lines) - 1;
- inc = line_spacing;
- offset_y = inc * to / 2;
- union() {
- for (i = [0 : to]) {
- translate([0 , -i * inc + offset_y, 0])
- text(lines[i], size, font = font, valign = valign, halign = halign, direction = direction, language = language, script = script);
- }
- }
-}
-
-/**
-* helix_extrude.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-helix_extrude.html
-*
-**/
-
-
-module helix_extrude(shape_pts, radius, levels, level_dist,
- vt_dir = "SPI_DOWN", rt_dir = "CT_CLK",
- twist = 0, scale = 1.0, triangles = "SOLID") {
-
- function reverse(vt) =
- let(leng = len(vt))
- [
- for(i = [0:leng - 1])
- vt[leng - 1 - i]
- ];
-
- is_flt = __is_float(radius);
- r1 = is_flt ? radius : radius[0];
- r2 = is_flt ? radius : radius[1];
-
- init_r = vt_dir == "SPI_DOWN" ? r2 : r1;
-
- frags = __frags(init_r);
-
- v_dir = vt_dir == "SPI_UP" ? 1 : -1;
- r_dir = rt_dir == "CT_CLK" ? 1 : -1;
-
- angle_step = 360 / frags * r_dir;
- initial_angle = atan2(level_dist / frags, 6.28318 * init_r / frags) * v_dir * r_dir;
-
- path_points = helix(
- radius = radius,
- levels = levels,
- level_dist = level_dist,
- vt_dir = vt_dir,
- rt_dir = rt_dir
- );
-
- clk_a = r_dir == 1 ? 0 : 180;
- angles = [for(i = [0:len(path_points) - 1]) [90 + initial_angle, 0, clk_a + angle_step * i]];
-
- sections = cross_sections(shape_pts, path_points, angles, twist, scale);
-
- polysections(
- sections,
- triangles = triangles
- );
-
- // hook for testing
- test_helix_extrude(sections);
-}
-
-// override it to test
-module test_helix_extrude(sections) {
-
-}
-
-function __is_float(value) = value + 0 != undef;
-
-/**
-* hollow_out.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-hollow_out.html
-*
-**/
-
-module hollow_out(shell_thickness) {
- difference() {
- children();
- offset(delta = -shell_thickness) children();
- }
-}
-
-/**
-* golden_spiral.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-golden_spiral.html
-*
-**/
-
-function _fast_fibonacci_sub(nth) =
- let(
- _f = _fast_fibonacci_2_elems(floor(nth / 2)),
- a = _f[0],
- b = _f[1],
- c = a * (b * 2 - a),
- d = a * a + b * b
- )
- nth % 2 == 0 ? [c, d] : [d, c + d];
-
-function _fast_fibonacci_2_elems(nth) =
- nth == 0 ? [0, 1] : _fast_fibonacci_sub(nth);
-
-function _fast_fibonacci(nth) =
- _fast_fibonacci_2_elems(nth)[0];
-
-function _remove_same_pts(pts1, pts2) =
- pts1[len(pts1) - 1] == pts2[0] ?
- concat(pts1, [for(i = [1:len(pts2) - 1]) pts2[i]]) :
- concat(pts1, pts2);
-
-function _golden_spiral_from_ls_or_eql_to(from, to, point_distance, rt_dir) =
- let(
- f1 = _fast_fibonacci(from),
- f2 = _fast_fibonacci(from + 1),
- fn = floor(f1 * 6.28312 / point_distance),
- $fn = fn + 4 - (fn % 4),
- circle_pts = circle_path(radius = f1, n = $fn / 4 + 1),
- len_pts = len(circle_pts),
- a_step = 360 / $fn * rt_dir,
- arc_points_angles = (rt_dir == 1 ? [
- for(i = [0:len_pts - 1])
- [circle_pts[i], a_step * i]
- ] : [
- for(i = [0:len_pts - 1]) let(idx = len_pts - i - 1)
- [circle_pts[idx], a_step * i]
- ]),
- offset = f2 - f1
- ) _remove_same_pts(
- arc_points_angles,
- [
- for(pt_a = _golden_spiral(from + 1, to, point_distance, rt_dir))
- [
- rotate_p(pt_a[0], [0, 0, 90 * rt_dir]) +
- (rt_dir == 1 ? [0, -offset, 0] : [-offset, 0, 0]),
- pt_a[1] + 90 * rt_dir
- ]
- ]
- );
-
-function _golden_spiral(from, to, point_distance, rt_dir) =
- from <= to ?
- _golden_spiral_from_ls_or_eql_to(from, to, point_distance, rt_dir) : [];
-
-function golden_spiral(from, to, point_distance, rt_dir = "CT_CLK") =
- _golden_spiral(from, to, point_distance, (rt_dir == "CT_CLK" ? 1 : -1));
-
-function __shape_arc(radius, angle, width, width_mode = "LINE_CROSS") =
- let(
- w_offset = width_mode == "LINE_CROSS" ? [width / 2, -width / 2] : (
- width_mode == "LINE_INWARD" ? [0, -width] : [width, 0]
- ),
- frags = __frags(radius),
- a_step = 360 / frags,
- half_a_step = a_step / 2,
- angles = __is_float(angle) ? [0, angle] : angle,
- m = floor(angles[0] / a_step) + 1,
- n = floor(angles[1] / a_step),
- r_outer = radius + w_offset[0],
- r_inner = radius + w_offset[1],
- points = concat(
- // outer arc path
- [__ra_to_xy(__edge_r_begin(r_outer, angles[0], a_step, m), angles[0])],
- m > n ? [] : [
- for(i = [m:n])
- __ra_to_xy(r_outer, a_step * i)
- ],
- angles[1] == a_step * n ? [] : [__ra_to_xy(__edge_r_end(r_outer, angles[1], a_step, n), angles[1])],
- // inner arc path
- angles[1] == a_step * n ? [] : [__ra_to_xy(__edge_r_end(r_inner, angles[1], a_step, n), angles[1])],
- m > n ? [] : [
- for(i = [m:n])
- let(idx = (n + (m - i)))
- __ra_to_xy(r_inner, a_step * idx)
-
- ],
- [__ra_to_xy(__edge_r_begin(r_inner, angles[0], a_step, m), angles[0])]
- )
- ) points;
-
-/**
-* sphere_spiral.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-sphere_spiral.html
-*
-**/
-
-function sphere_spiral(radius, za_step, z_circles = 1, begin_angle = 0, end_angle = 0, vt_dir = "SPI_DOWN", rt_dir = "CT_CLK") =
- [
- for(a = [begin_angle:za_step:90 * z_circles - end_angle])
- let(
- ya = vt_dir == "SPI_DOWN" ? (-90 + 2 * a / z_circles) : (90 + 2 * a / z_circles),
- za = (rt_dir == "CT_CLK" ? 1 : -1) * a,
- ra = [0, ya, za]
- )
- [rotate_p([radius, 0, 0], ra), ra]
- ];
-
-/**
-* hull_polyline2d.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/hull_polyline2d.html
-*
-**/
-
-module hull_polyline2d(points, width) {
- half_width = width / 2;
- leng = len(points);
-
- module hull_line2d(index) {
- point1 = points[index - 1];
- point2 = points[index];
-
- hull() {
- translate(point1)
- circle(half_width);
- translate(point2)
- circle(half_width);
- }
-
- // hook for testing
- test_line_segment(index, point1, point2, half_width);
- }
-
- module polyline2d_inner(index) {
- if(index < leng) {
- hull_line2d(index);
- polyline2d_inner(index + 1);
- }
- }
-
- polyline2d_inner(1);
-}
-
-// override it to test
-module test_line_segment(index, point1, point2, radius) {
-
-}
-
-function __polytransversals(transversals) =
- let(
- leng_trs = len(transversals),
- leng_tr = len(transversals[0]),
- lefts = [
- for(i = [1:leng_trs - 2])
- let(tr = transversals[leng_trs - i])
- tr[0]
- ],
- rights = [
- for(i = [1:leng_trs - 2])
- let(tr = transversals[i])
- tr[leng_tr - 1]
- ]
- ) concat(
- transversals[0],
- rights,
- __reverse(transversals[leng_trs - 1]),
- lefts
- );
-
-/**
-* bend_extrude.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-bend_extrude.html
-*
-**/
-
-module bend_extrude(size, thickness, angle, frags = 24) {
- x = size[0];
- y = size[1];
- frag_width = x / frags ;
- frag_angle = angle / frags;
- half_frag_width = 0.5 * frag_width;
- half_frag_angle = 0.5 * frag_angle;
- r = half_frag_width / sin(half_frag_angle);
- s = (r - thickness) / r;
-
- module get_frag(i) {
- offsetX = i * frag_width;
- linear_extrude(thickness, scale = [s, 1])
- translate([-offsetX - half_frag_width, 0, 0])
- intersection() {
- translate([x, 0, 0]) mirror([1, 0, 0]) children();
- translate([offsetX, 0, 0])
- square([frag_width, y]);
- }
- }
-
- offsetY = -r * cos(half_frag_angle) ;
- rotate([180, 0, 180]) for(i = [0 : frags - 1]) {
- rotate(i * frag_angle + half_frag_angle)
- translate([0, offsetY, 0])
- rotate([-90, 0, 0])
- get_frag(i)
- children();
- }
-
-}
-
-
-/**
-* helix.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-helix.html
-*
-**/
-
-
-function helix(radius, levels, level_dist, vt_dir = "SPI_DOWN", rt_dir = "CT_CLK") =
- let(
- is_flt = __is_float(radius),
- r1 = is_flt ? radius : radius[0],
- r2 = is_flt ? radius : radius[1],
- init_r = vt_dir == "SPI_DOWN" ? r2 : r1,
- _frags = __frags(init_r),
- h = level_dist * levels,
- vt_d = vt_dir == "SPI_DOWN" ? 1 : -1,
- rt_d = rt_dir == "CT_CLK" ? 1 : -1,
- r_diff = (r1 - r2) * vt_d,
- h_step = level_dist / _frags * vt_d,
- r_step = r_diff / (levels * _frags),
- a_step = 360 / _frags * rt_d,
- begin_r = vt_dir == "SPI_DOWN" ? r2 : r1,
- begin_h = vt_dir == "SPI_DOWN" ? h : 0
- )
- [
- for(i = [0:_frags * levels])
- let(r = begin_r + r_step * i, a = a_step * i)
- [r * cos(a), r * sin(a), begin_h - h_step * i]
- ];
-
-/**
-* polyline2d.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-polyline2d.html
-*
-**/
-
-module polyline2d(points, width, startingStyle = "CAP_SQUARE", endingStyle = "CAP_SQUARE") {
- leng_pts = len(points);
-
- module line_segment(index) {
- styles = index == 1 ? [startingStyle, "CAP_ROUND"] : (
- index == leng_pts - 1 ? ["CAP_BUTT", endingStyle] : [
- "CAP_BUTT", "CAP_ROUND"
- ]
- );
-
- p1 = points[index - 1];
- p2 = points[index];
- p1Style = styles[0];
- p2Style = styles[1];
-
- line2d(points[index - 1], points[index], width,
- p1Style = p1Style, p2Style = p2Style);
-
- // hook for testing
- test_line_segment(index, p1, p2, width, p1Style, p2Style);
- }
-
- module polyline2d_inner(index) {
- if(index < leng_pts) {
- line_segment(index);
- polyline2d_inner(index + 1);
- }
- }
-
- polyline2d_inner(1);
-}
-
-// override it to test
-module test_line_segment(index, point1, point2, width, p1Style, p2Style) {
-
-}
-
-/**
-* shape_pentagram.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_ellipse.html
-*
-**/
-
-function shape_pentagram(r) =
- [
- [0, 1], [-0.224514, 0.309017],
- [-0.951057, 0.309017], [-0.363271, -0.118034],
- [-0.587785, -0.809017], [0, -0.381966],
- [0.587785, -0.809017], [0.363271, -0.118034],
- [0.951057, 0.309017], [0.224514, 0.309017]
- ] * r;
-
-/**
-* along_with.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-along_with.html
-*
-**/
-
-
-// Becuase of improving the performance, this module requires m_rotation.scad which doesn't require in dotSCAD 1.0.
-// For backward compatibility, I directly include m_rotation here.
-
-module along_with(points, angles, twist = 0, scale = 1.0, method = "AXIS_ANGLE") {
- leng_points = len(points);
- leng_points_minus_one = leng_points - 1;
- twist_step_a = twist / leng_points;
-
- angles_defined = angles != undef;
-
- scale_step_vt = __is_float(scale) ?
- scale_step() :
- [
- (scale[0] - 1) / leng_points_minus_one,
- (scale[1] - 1) / leng_points_minus_one,
- scale[2] == undef ? 0 : (scale[2] - 1) / leng_points_minus_one
- ];
-
-
- function scale_step() =
- let(s = (scale - 1) / leng_points_minus_one)
- [s, s, s];
-
-
- /*
- Sadly, children(n) cannot be used with inner modules
- so I have to do things in the first level. Ugly!!
- */
-
- // >>> begin: modules and functions for "AXIS-ANGLE"
-
- // get rotation matrice for sections
- identity_matrix = [
- [1, 0, 0, 0],
- [0, 1, 0, 0],
- [0, 0, 1, 0],
- [0, 0, 0, 1]
- ];
-
- function axis_angle_local_ang_vects(j) =
- j == 0 ? [] : axis_angle_local_ang_vects_sub(j);
-
- function axis_angle_local_ang_vects_sub(j) =
- let(
- vt0 = points[j] - points[j - 1],
- vt1 = points[j + 1] - points[j],
- a = acos((vt0 * vt1) / (norm(vt0) * norm(vt1))),
- v = cross(vt0, vt1)
- )
- concat([[a, v]], axis_angle_local_ang_vects(j - 1));
-
- function axis_angle_cumulated_rot_matrice(i, rot_matrice) =
- let(
- leng_rot_matrice = len(rot_matrice),
- leng_rot_matrice_minus_one = leng_rot_matrice - 1,
- leng_rot_matrice_minus_two = leng_rot_matrice - 2
- )
- leng_rot_matrice == 0 ? [identity_matrix] : (
- leng_rot_matrice == 1 ? [rot_matrice[0], identity_matrix] : (
- i == leng_rot_matrice_minus_two ?
- [
- rot_matrice[leng_rot_matrice_minus_one],
- rot_matrice[leng_rot_matrice_minus_two] * rot_matrice[leng_rot_matrice_minus_one]
- ]
- : axis_angle_cumulated_rot_matrice_sub(i, rot_matrice)
- )
- );
-
- function axis_angle_cumulated_rot_matrice_sub(i, rot_matrice) =
- let(
- matrice = axis_angle_cumulated_rot_matrice(i + 1, rot_matrice),
- curr_matrix = rot_matrice[i],
- prev_matrix = matrice[len(matrice) - 1]
- )
- concat(matrice, [curr_matrix * prev_matrix]);
-
- // align modules
-
- module axis_angle_align_with_pts_angles(i) {
- translate(points[i])
- rotate(angles[i])
- rotate(twist_step_a * i)
- scale([1, 1, 1] + scale_step_vt * i)
- children(0);
- }
-
- module axis_angle_align_with_pts_init(a, s) {
- angleyz = __angy_angz(__to3d(points[0]), __to3d(points[1]));
-
- rotate([0, -angleyz[0], angleyz[1]])
- rotate([90, 0, -90])
- rotate(a)
- scale(s)
- children(0);
- }
-
- module axis_angle_align_with_pts_local_rotate(j, init_a, init_s, cumu_rot_matrice) {
- if(j == 0) { // first child
- axis_angle_align_with_pts_init(init_a, init_s)
- children(0);
- }
- else {
- multmatrix(cumu_rot_matrice[j - 1])
- axis_angle_align_with_pts_init(init_a, init_s)
- children(0);
- }
- }
-
- // <<< end: modules and functions for "AXIS-ANGLE"
-
-
- // >>> begin: modules and functions for "EULER-ANGLE"
-
- function _euler_angle_path_angles(pts, end_i, i = 0) =
- i == end_i ?
- [] :
- concat(
- [__angy_angz(pts[i], pts[i + 1])],
- _euler_angle_path_angles(pts, end_i, i + 1)
- );
-
- function euler_angle_path_angles(children) =
- let(
- pts = len(points[0]) == 3 ? points : [for(pt = points) __to3d(pt)],
- end_i = children == 1 ? leng_points_minus_one : children - 1,
- angs = _euler_angle_path_angles(pts, end_i)
- )
- concat(
- [[0, -angs[0][0], angs[0][1]]],
- [for(a = angs) [0, -a[0], a[1]]]
- );
-
- module euler_angle_align(i, angs) {
- translate(points[i])
- rotate(angs[i])
- rotate(angles_defined ? [0, 0, 0] : [90, 0, -90])
- rotate(twist_step_a * i)
- scale([1, 1, 1] + scale_step_vt * i)
- children(0);
- }
-
- // <<< end: modules and functions for "EULER-ANGLE"
-
- if(method == "AXIS_ANGLE") {
- if(angles_defined) {
- if($children == 1) {
- for(i = [0:leng_points_minus_one]) {
- axis_angle_align_with_pts_angles(i) children(0);
- }
- } else {
- for(i = [0:min(leng_points, $children) - 1]) {
- axis_angle_align_with_pts_angles(i) children(i);
- }
- }
- }
- else {
- cumu_rot_matrice = axis_angle_cumulated_rot_matrice(0, [
- for(ang_vect = axis_angle_local_ang_vects(leng_points - 2))
- m_rotation(ang_vect[0], ang_vect[1])
- ]);
-
- translate(points[0])
- axis_angle_align_with_pts_local_rotate(0, 0, [1, 1, 1], cumu_rot_matrice)
- children(0);
-
- if($children == 1) {
- for(i = [0:leng_points - 2]) {
- translate(points[i + 1])
- axis_angle_align_with_pts_local_rotate(i, i * twist_step_a, [1, 1, 1] + scale_step_vt * i, cumu_rot_matrice)
- children(0);
- }
- } else {
- for(i = [0:min(leng_points, $children) - 2]) {
- translate(points[i + 1])
- axis_angle_align_with_pts_local_rotate(i, i * twist_step_a, [1, 1, 1] + scale_step_vt * i, cumu_rot_matrice)
- children(i + 1);
- }
- }
- }
- }
- else if(method == "EULER_ANGLE") {
- angs = angles_defined ? angles : euler_angle_path_angles($children);
-
- if($children == 1) {
- for(i = [0:leng_points_minus_one]) {
- euler_angle_align(i, angs) children(0);
- }
-
- } else {
- for(i = [0:min(leng_points, $children) - 1]) {
- euler_angle_align(i, angs) children(i);
- }
- }
- }
-}
-
-function __nearest_multiple_of_4(n) =
- let(
- remain = n % 4
- )
- (remain / 4) > 0.5 ? n - remain + 4 : n - remain;
-
-
/**
* path_extrude.scad
*
@@ -2578,430 +814,157 @@ module test_path_extrude(sections) {
}
-function __to_3_elems_ang_vect(a) =
- let(leng = len(a))
- leng == 3 ? a : (
- leng == 2 ? [a[0], a[1], 0] : [a[0], 0, 0]
- );
+/**
+* bezier_surface.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-bezier_surface.html
+*
+**/
-function __to_ang_vect(a) = __is_float(a) ? [0, 0, a] : __to_3_elems_ang_vect(a);
+function bezier_surface(t_step, ctrl_pts) =
+ let(pts = [
+ for(i = [0:len(ctrl_pts) - 1])
+ bezier_curve(t_step, ctrl_pts[i])
+ ])
+ [for(x = [0:len(pts[0]) - 1])
+ bezier_curve(t_step,
+ [for(y = [0:len(pts) - 1]) pts[y][x]]
+ )
+ ];
-function __pie_for_rounding(r, begin_a, end_a, frags) =
+/**
+* torus_knot.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-torus_knot.html
+*
+**/
+
+function torus_knot(p, q, phi_step) = [
+ for(phi = [0:phi_step:6.28318])
let(
- sector_angle = end_a - begin_a,
- step_a = sector_angle / frags,
- is_integer = frags % 1 == 0
+ degree = phi * 180 / 3.14159,
+ r = cos(q * degree) + 2,
+ x = r * cos(p * degree),
+ y = r * sin(p * degree),
+ z = -sin(q * degree)
)
- r < 0.00005 ? [[0, 0]] : concat([
- for(ang = [begin_a:step_a:end_a])
- [
- r * cos(ang),
- r * sin(ang)
+ [x, y, z]
+];
+
+/**
+* rounded_square.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_square.html
+*
+**/
+
+
+module rounded_square(size, corner_r, center = false) {
+ is_flt = __is_float(size);
+ x = is_flt ? size : size[0];
+ y = is_flt ? size : size[1];
+
+ position = center ? [0, 0] : [x / 2, y / 2];
+ points = __trapezium(
+ length = x,
+ h = y,
+ round_r = corner_r
+ );
+
+ translate(position)
+ polygon(points);
+
+ // hook for testing
+ test_rounded_square(position, points);
+}
+
+// override it to test
+module test_rounded_square(position, points) {
+}
+
+/**
+* m_mirror.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-m_mirror.html
+*
+**/
+
+function m_mirror(v) =
+ let(
+ nv = v / norm(v),
+ txx = -2* nv[0] * nv[0],
+ txy = -2* nv[0] * nv[1],
+ txz = -2* nv[0] * nv[2],
+ tyy = -2* nv[1] * nv[1],
+ tyz = -2* nv[1] * nv[2],
+ tzz = -2* nv[2] * nv[2]
+ )
+ [
+ [1 + txx, txy, txz, 0],
+ [txy, 1 + tyy, tyz, 0],
+ [txz, tyz, 1 + tzz, 0],
+ [0, 0, 0, 1]
+ ];
+
+/**
+* polyline3d.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-polyline3d.html
+*
+**/
+
+module polyline3d(points, thickness, startingStyle = "CAP_CIRCLE", endingStyle = "CAP_CIRCLE") {
+ leng_pts = len(points);
+
+ module line_segment(index) {
+ styles = index == 1 ? [startingStyle, "CAP_BUTT"] : (
+ index == leng_pts - 1 ? ["CAP_SPHERE", endingStyle] : [
+ "CAP_SPHERE", "CAP_BUTT"
]
- ],
- is_integer ? [] : [[
- r * cos(end_a),
- r * sin(end_a)
- ]]
- );
-
-function __shape_pie(radius, angle) =
- let(
- frags = __frags(radius),
- a_step = 360 / frags,
- leng = radius * cos(a_step / 2),
- angles = __is_float(angle) ? [0:angle] : angle,
- m = floor(angles[0] / a_step) + 1,
- n = floor(angles[1] / a_step),
- edge_r_begin = leng / cos((m - 0.5) * a_step - angles[0]),
- edge_r_end = leng / cos((n + 0.5) * a_step - angles[1]),
- shape_pts = concat(
- [[0, 0], __ra_to_xy(edge_r_begin, angles[0])],
- m > n ? [] : [
- for(i = [m:n])
- let(a = a_step * i)
- __ra_to_xy(radius, a)
- ],
- angles[1] == a_step * n ? [] : [__ra_to_xy(edge_r_end, angles[1])]
- )
- ) shape_pts;
-
-function __to2d(p) = [p[0], p[1]];
-
-/**
-* parse_number.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-parse_number.html
-*
-**/
-
-function _str_to_int(t, i = 0, mapper = [["0", 0], ["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5], ["6", 6], ["7", 7], ["8", 8], ["9", 9]]) =
- i == len(mapper) ? -1 : (
- mapper[i][0] == t ? mapper[i][1] : _str_to_int(t, i + 1)
- );
-
-function _parse_positive_int(t, value = 0, i = 0) =
- i == len(t) ? value : _parse_positive_int(t, value * pow(10, i) + _str_to_int(t[i]), i + 1);
-
-function _parse_positive_decimal(t, value = 0, i = 0) =
- i == len(t) ? value : _parse_positive_decimal(t, value + _str_to_int(t[i]) * pow(10, -(i + 1)), i + 1);
-
-function _parse_positive_number(t) =
- len(search(".", t)) == 0 ? _parse_positive_int(t) :
- _parse_positive_int(split_str(t, ".")[0]) + _parse_positive_decimal(split_str(t, ".")[1]);
-
-function parse_number(t) =
- t[0] == "-" ? -_parse_positive_number(sub_str(t, 1, len(t))) : _parse_positive_number(t);
-
-
-
-/**
-* shape_superformula.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_superformula.html
-*
-**/
-
-
-function _superformula_r(angle, m1, m2, n1, n2 = 1, n3 = 1, a = 1, b = 1) =
- pow(
- pow(abs(cos(m1 * angle / 4) / a), n2) +
- pow(abs(sin(m2 * angle / 4) / b), n3),
- - 1 / n1
- );
-
-function shape_superformula(phi_step, m1, m2, n1, n2 = 1, n3 = 1, a = 1, b = 1) =
- [
- for(phi = [0:phi_step:6.28318])
- let(
- angle = __to_degree(phi),
- r = _superformula_r(angle, m1, m2, n1, n2, n3, a, b)
-
- )
- __ra_to_xy(r, angle)
- ];
-
-/**
-* bezier_smooth.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-bezier_curve.html
-*
-**/
-
-
-function _corner_ctrl_pts(round_d, p1, p2, p3) =
- let(
- _ya_za_1 = __angy_angz(p1, p2),
- _ya_za_2 = __angy_angz(p3, p2),
-
- dz1 = sin(_ya_za_1[0]) * round_d,
- dxy1 = cos(_ya_za_1[0]) * round_d,
- dy1 = sin(_ya_za_1[1]) * dxy1,
- dx1 = cos(_ya_za_1[1]) * dxy1,
-
- dz2 = sin(_ya_za_2[0]) * round_d,
- dxy2 = cos(_ya_za_2[0]) * round_d,
- dy2 = sin(_ya_za_2[1]) * dxy2,
- dx2 = cos(_ya_za_2[1]) * dxy2
- )
- [
- p2 - [dx1, dy1, dz1],
- p2,
- p2 - [dx2, dy2, dz2]
- ];
-
-
-function _bezier_corner(round_d, t_step, p1, p2, p3) =
- bezier_curve(t_step, _corner_ctrl_pts(round_d, p1, p2, p3));
-
-function _recursive_bezier_smooth(pts, round_d, t_step, leng, i = 0) =
- i <= leng - 3 ?
- concat(
- _bezier_corner(round_d, t_step, pts[i], pts[i + 1], pts[i + 2]),
- _recursive_bezier_smooth(pts, round_d, t_step, leng, i + 1)
- )
- : [];
-
-function bezier_smooth(path_pts, round_d, t_step = 0.1, closed = false) =
- let(
- pts = len(path_pts[0]) == 3 ? path_pts : [for(p = path_pts) __to3d(p)],
- leng = len(pts),
- middle_pts = _recursive_bezier_smooth(pts, round_d, t_step, leng),
- pth_pts = closed ?
- concat(
- _recursive_bezier_smooth(
- [pts[leng - 1], pts[0], pts[1]],
- round_d, t_step, 3
- ),
- middle_pts,
- _recursive_bezier_smooth(
- [pts[leng - 2], pts[leng - 1], pts[0]],
- round_d, t_step, 3
- )
- ) :
- concat(
- [pts[0]],
- middle_pts,
- [pts[leng - 1]]
- )
- )
- len(path_pts[0]) == 2 ? [for(p = pth_pts) __to2d(p)] : pth_pts;
-
-function __ra_to_xy(r, a) = [r * cos(a), r * sin(a)];
-
-function __line_intersection(line_pts1, line_pts2, epsilon = 0.0001) =
- let(
- a1 = line_pts1[0],
- a2 = line_pts1[1],
- b1 = line_pts2[0],
- b2 = line_pts2[1],
- a = a2 - a1,
- b = b2 - b1,
- s = b1 - a1
- )
- abs(cross(a, b)) < epsilon ? [] : // they are parallel or conincident edges
- a1 + a * cross(s, b) / cross(a, b);
-
-/**
-* shape_pie.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_pie.html
-*
-**/
-
-
-function shape_pie(radius, angle) =
- __shape_pie(radius, angle);
-
-
-/**
-* shape_glued2circles.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_glued2circles.html
-*
-**/
-
-function _glued2circles_pie_curve(radius, centre_dist, tangent_angle) =
- let(
- begin_ang = 90 + tangent_angle,
- shape_pts = shape_pie(radius, [-begin_ang, begin_ang])
- )
- [
- for(i = [1:len(shape_pts) - 1])
- shape_pts[i] + [centre_dist / 2, 0]
- ];
-
-function _glued2circles_bezier(radius, centre_dist, tangent_angle, t_step, ctrl_p1) =
- let(
- ctrl_p = rotate_p([radius * tan(tangent_angle), -radius], tangent_angle),
- ctrl_p2 = [-ctrl_p[0], ctrl_p[1]] + [centre_dist / 2, 0],
- ctrl_p3 = [-ctrl_p2[0], ctrl_p2[1]],
- ctrl_p4 = [-ctrl_p1[0], ctrl_p1[1]]
- )
- bezier_curve(
- t_step,
- [
- ctrl_p1,
- ctrl_p2,
- ctrl_p3,
- ctrl_p4
- ]
- );
-
-function _glued2circles_lower_half_curve(curve_pts, leng, i = 0) =
- i < leng ? (
- curve_pts[leng - 1 - i][0] >= 0 ?
- concat(
- [curve_pts[leng - 1 - i]],
- _glued2circles_lower_half_curve(curve_pts, leng, i + 1)
- ) :
- _glued2circles_lower_half_curve(curve_pts, leng, i + 1)
- ) : [];
-
-function _glued2circles_half_glued_circle(radius, centre_dist, tangent_angle, t_step) =
- let(
- pie_curve_pts = _glued2circles_pie_curve(radius, centre_dist, tangent_angle),
- curve_pts = _glued2circles_bezier(radius, centre_dist, tangent_angle, t_step, pie_curve_pts[0]),
- lower_curve_pts = _glued2circles_lower_half_curve(curve_pts, len(curve_pts)),
- leng_half_curve_pts = len(lower_curve_pts),
- upper_curve_pts = [
- for(i = [0:leng_half_curve_pts - 1])
- let(pt = lower_curve_pts[leng_half_curve_pts - 1 - i])
- [pt[0], -pt[1]]
- ]
- ) concat(
- lower_curve_pts,
- pie_curve_pts,
- upper_curve_pts
- );
-
-function shape_glued2circles(radius, centre_dist, tangent_angle = 30, t_step = 0.1) =
- let(
- half_glued_circles = _glued2circles_half_glued_circle(radius, centre_dist, tangent_angle, t_step),
- leng_half_glued_circles = len(half_glued_circles),
- left_half_glued_circles = [
- for(i = [0:leng_half_glued_circles - 1])
- let(pt = half_glued_circles[leng_half_glued_circles - 1 - i])
- [-pt[0], pt[1]]
- ]
- ) concat(half_glued_circles, left_half_glued_circles);
-
-/**
-* shape_cyclicpolygon.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_cyclicpolygon.html
-*
-**/
-
-
-function shape_cyclicpolygon(sides, circle_r, corner_r) =
- let(
- frag_a = 360 / sides,
- corner_a = (180 - frag_a),
- corner_circle_a = 180 - corner_a,
- half_corner_circle_a = corner_circle_a / 2,
- corner_circle_center = circle_r - corner_r / sin(corner_a / 2),
- first_corner = [
- for(
- pt = __pie_for_rounding(
- corner_r,
- -half_corner_circle_a,
- half_corner_circle_a,
- __frags(corner_r) * corner_circle_a / 360
- )
- )
- [pt[0] + corner_circle_center, pt[1]]
- ]
-
- )
- concat(
- first_corner,
- [
- for(side = [1:sides - 1])
- for(pt = first_corner)
- let(
- a = frag_a * side,
- x = pt[0],
- y = pt[1],
- sina = sin(a),
- cosa = cos(a)
- )
- [
- x * cosa - y * sina,
- x * sina + y * cosa
- ]
- ]
- );
-
-/**
-* ring_extrude.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-ring_extrude.html
-*
-**/
-
-
-module ring_extrude(shape_pts, radius, angle = 360, twist = 0, scale = 1.0, triangles = "SOLID") {
- if(angle == 360 && twist == 0 && scale == 1.0) {
- rotate_extrude()
- translate([radius, 0, 0])
- polygon(shape_pts);
- } else {
- a_step = 360 / __frags(radius);
-
- angles = __is_float(angle) ? [0, angle] : angle;
-
- m = floor(angles[0] / a_step) + 1;
- n = floor(angles[1] / a_step);
-
- leng = radius * cos(a_step / 2);
-
- begin_r =
- leng / cos((m - 0.5) * a_step - angles[0]);
-
- end_r =
- leng / cos((n + 0.5) * a_step - angles[1]);
-
- angs = concat(
- [[90, 0, angles[0]]],
- m > n ? [] : [for(i = [m:n]) [90, 0, a_step * i]]
);
- pts = concat(
- [__ra_to_xy(begin_r, angles[0])],
- m > n ? [] : [for(i = [m:n]) __ra_to_xy(radius, a_step * i)]
- );
- is_angle_frag_end = angs[len(angs) - 1][2] == angles[1];
+ p1 = points[index - 1];
+ p2 = points[index];
+ p1Style = styles[0];
+ p2Style = styles[1];
- all_angles = is_angle_frag_end ?
- angs :
- concat(angs, [[90, 0, angles[1]]]);
-
- all_points = is_angle_frag_end ?
- pts :
- concat(pts, [__ra_to_xy(end_r, angles[1])]);
-
- sections = cross_sections(shape_pts, all_points, all_angles, twist, scale);
-
- polysections(
- sections,
- triangles = triangles
- );
+ line3d(p1, p2, thickness,
+ p1Style = p1Style, p2Style = p2Style);
// hook for testing
- test_ring_extrude(sections);
+ test_line3d_segment(index, p1, p2, thickness, p1Style, p2Style);
}
+
+ module polyline3d_inner(index) {
+ if(index < leng_pts) {
+ line_segment(index);
+ polyline3d_inner(index + 1);
+ }
+ }
+
+ polyline3d_inner(1);
}
-// Override it to test
-module test_ring_extrude(sections) {
+// override it to test
+module test_line3d_segment(index, point1, point2, thickness, p1Style, p2Style) {
}
-function __trapezium(length, h, round_r) =
- let(
- r_half_trapezium = __half_trapezium(length / 2, h, round_r),
- to = len(r_half_trapezium) - 1,
- l_half_trapezium = [
- for(i = [0:to])
- let(pt = r_half_trapezium[to - i])
- [-pt[0], pt[1]]
- ]
- )
- concat(
- r_half_trapezium,
- l_half_trapezium
- );
-
-function __lines_from(pts, closed = false) =
- let(leng = len(pts))
- concat(
- [for(i = [0:leng - 2]) [pts[i], pts[i + 1]]],
- closed ? [[pts[len(pts) - 1], pts[0]]] : []
- );
-
-
/**
* polysections.scad
*
@@ -3253,6 +1216,20 @@ module test_polysections_solid(points, faces) {
}
+/**
+* shape_arc.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_arc.html
+*
+**/
+
+
+function shape_arc(radius, angle, width, width_mode = "LINE_CROSS") =
+ __shape_arc(radius, angle, width, width_mode);
+
/**
* starburst.scad
*
@@ -3296,6 +1273,286 @@ module starburst(r1, r2, n, height) {
}
}
+/**
+* archimedean_spiral.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-archimedean_spiral.html
+*
+**/
+
+function _radian_step(b, theta, l) =
+ let(r_square = pow(b * theta, 2))
+ acos((2 * r_square - pow(l, 2)) / (2 * r_square)) / 180 * 3.14159;
+
+function _find_radians(b, point_distance, radians, n, count = 1) =
+ let(pre_radians = radians[count - 1])
+ count == n ? radians : (
+ _find_radians(
+ b,
+ point_distance,
+ concat(
+ radians,
+ [pre_radians + _radian_step(b, pre_radians, point_distance)]
+ ),
+ n,
+ count + 1)
+ );
+
+function archimedean_spiral(arm_distance, init_angle, point_distance, num_of_points, rt_dir = "CT_CLK") =
+ let(b = arm_distance / 6.28318, init_radian = init_angle *3.14159 / 180)
+ [
+ for(theta = _find_radians(b, point_distance, [init_radian], num_of_points))
+ let(r = b * theta, a = (rt_dir == "CT_CLK" ? 1 : -1) * theta * 57.2958)
+ [[r * cos(a), r * sin(a)], a]
+ ];
+
+/**
+* helix_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-helix_extrude.html
+*
+**/
+
+
+module helix_extrude(shape_pts, radius, levels, level_dist,
+ vt_dir = "SPI_DOWN", rt_dir = "CT_CLK",
+ twist = 0, scale = 1.0, triangles = "SOLID") {
+
+ function reverse(vt) =
+ let(leng = len(vt))
+ [
+ for(i = [0:leng - 1])
+ vt[leng - 1 - i]
+ ];
+
+ is_flt = __is_float(radius);
+ r1 = is_flt ? radius : radius[0];
+ r2 = is_flt ? radius : radius[1];
+
+ init_r = vt_dir == "SPI_DOWN" ? r2 : r1;
+
+ frags = __frags(init_r);
+
+ v_dir = vt_dir == "SPI_UP" ? 1 : -1;
+ r_dir = rt_dir == "CT_CLK" ? 1 : -1;
+
+ angle_step = 360 / frags * r_dir;
+ initial_angle = atan2(level_dist / frags, 6.28318 * init_r / frags) * v_dir * r_dir;
+
+ path_points = helix(
+ radius = radius,
+ levels = levels,
+ level_dist = level_dist,
+ vt_dir = vt_dir,
+ rt_dir = rt_dir
+ );
+
+ clk_a = r_dir == 1 ? 0 : 180;
+ angles = [for(i = [0:len(path_points) - 1]) [90 + initial_angle, 0, clk_a + angle_step * i]];
+
+ sections = cross_sections(shape_pts, path_points, angles, twist, scale);
+
+ polysections(
+ sections,
+ triangles = triangles
+ );
+
+ // hook for testing
+ test_helix_extrude(sections);
+}
+
+// override it to test
+module test_helix_extrude(sections) {
+
+}
+
+/**
+* shape_square.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_square.html
+*
+**/
+
+
+function shape_square(size, corner_r = 0) =
+ let(
+ is_flt = __is_float(size),
+ x = is_flt ? size : size[0],
+ y = is_flt ? size : size[1]
+ )
+ __trapezium(
+ length = x,
+ h = y,
+ round_r = corner_r
+ );
+
+
+function __shape_arc(radius, angle, width, width_mode = "LINE_CROSS") =
+ let(
+ w_offset = width_mode == "LINE_CROSS" ? [width / 2, -width / 2] : (
+ width_mode == "LINE_INWARD" ? [0, -width] : [width, 0]
+ ),
+ frags = __frags(radius),
+ a_step = 360 / frags,
+ half_a_step = a_step / 2,
+ angles = __is_float(angle) ? [0, angle] : angle,
+ m = floor(angles[0] / a_step) + 1,
+ n = floor(angles[1] / a_step),
+ r_outer = radius + w_offset[0],
+ r_inner = radius + w_offset[1],
+ points = concat(
+ // outer arc path
+ [__ra_to_xy(__edge_r_begin(r_outer, angles[0], a_step, m), angles[0])],
+ m > n ? [] : [
+ for(i = [m:n])
+ __ra_to_xy(r_outer, a_step * i)
+ ],
+ angles[1] == a_step * n ? [] : [__ra_to_xy(__edge_r_end(r_outer, angles[1], a_step, n), angles[1])],
+ // inner arc path
+ angles[1] == a_step * n ? [] : [__ra_to_xy(__edge_r_end(r_inner, angles[1], a_step, n), angles[1])],
+ m > n ? [] : [
+ for(i = [m:n])
+ let(idx = (n + (m - i)))
+ __ra_to_xy(r_inner, a_step * idx)
+
+ ],
+ [__ra_to_xy(__edge_r_begin(r_inner, angles[0], a_step, m), angles[0])]
+ )
+ ) points;
+
+/**
+* arc.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-arc.html
+*
+**/
+
+
+module arc(radius, angle, width, width_mode = "LINE_CROSS") {
+ polygon(__shape_arc(radius, angle, width, width_mode));
+}
+
+/**
+* in_shape.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-in_shape.html
+*
+**/
+
+
+function _in_shape_in_line_equation(edge, pt) =
+ let(
+ x1 = edge[0][0],
+ y1 = edge[0][1],
+ x2 = edge[1][0],
+ y2 = edge[1][1],
+ a = (y2 - y1) / (x2 - x1),
+ b = y1 - a * x1
+ )
+ (pt[1] == a * pt[0] + b);
+
+function _in_shape_in_any_edges_sub(edges, leng, pt, i, epsilon) =
+ leng == i ? false : (
+ __in_line(edges[i], pt, epsilon) ? true : _in_shape_in_any_edges_sub(edges, leng, pt, i + 1, epsilon)
+ );
+
+function _in_shape_in_any_edges(edges, pt, epsilon) = _in_shape_in_any_edges_sub(edges, len(edges), pt, 0, epsilon);
+
+function _in_shape_interpolate_x(y, p1, p2) =
+ p1[1] == p2[1] ? p1[0] : (
+ p1[0] + (p2[0] - p1[0]) * (y - p1[1]) / (p2[1] - p1[1])
+ );
+
+function _in_shape_does_pt_cross(pts, i, j, pt) =
+ ((pts[i][1] > pt[1]) != (pts[j][1] > pt[1])) &&
+ (pt[0] < _in_shape_interpolate_x(pt[1], pts[i], pts[j]));
+
+
+function _in_shape_sub(shapt_pts, leng, pt, cond, i, j) =
+ j == leng ? cond : (
+ _in_shape_does_pt_cross(shapt_pts, i, j, pt) ?
+ _in_shape_sub(shapt_pts, leng, pt, !cond, j, j + 1) :
+ _in_shape_sub(shapt_pts, leng, pt, cond, j, j + 1)
+ );
+
+function in_shape(shapt_pts, pt, include_edge = false, epsilon = 0.0001) =
+ let(
+ leng = len(shapt_pts),
+ edges = __lines_from(shapt_pts, true)
+ )
+ _in_shape_in_any_edges(edges, pt, epsilon) ? include_edge :
+ _in_shape_sub(shapt_pts, leng, pt, false, leng - 1, 0);
+
+function __lines_from(pts, closed = false) =
+ let(leng = len(pts))
+ concat(
+ [for(i = [0:leng - 2]) [pts[i], pts[i + 1]]],
+ closed ? [[pts[len(pts) - 1], pts[0]]] : []
+ );
+
+
+/**
+* arc_shape.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-arc_path.html
+*
+**/
+
+
+function arc_path(radius, angle) =
+ let(
+ frags = __frags(radius),
+ a_step = 360 / frags,
+ angles = __is_float(angle) ? [0, angle] : angle,
+ m = floor(angles[0] / a_step) + 1,
+ n = floor(angles[1] / a_step),
+ points = concat([__ra_to_xy(__edge_r_begin(radius, angles[0], a_step, m), angles[0])],
+ m > n ? [] : [
+ for(i = [m:n])
+ __ra_to_xy(radius, a_step * i)
+ ],
+ angles[1] == a_step * n ? [] : [__ra_to_xy(__edge_r_end(radius, angles[1], a_step, n), angles[1])])
+ ) points;
+
+/**
+* sphere_spiral.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-sphere_spiral.html
+*
+**/
+
+function sphere_spiral(radius, za_step, z_circles = 1, begin_angle = 0, end_angle = 0, vt_dir = "SPI_DOWN", rt_dir = "CT_CLK") =
+ [
+ for(a = [begin_angle:za_step:90 * z_circles - end_angle])
+ let(
+ ya = vt_dir == "SPI_DOWN" ? (-90 + 2 * a / z_circles) : (90 + 2 * a / z_circles),
+ za = (rt_dir == "CT_CLK" ? 1 : -1) * a,
+ ra = [0, ya, za]
+ )
+ [rotate_p([radius, 0, 0], ra), ra]
+ ];
+
/**
* paths2sections.scad
*
@@ -3320,341 +1577,568 @@ function paths2sections(paths) =
];
/**
-* voronoi2d.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-voronoi2d.html
-*
-**/
-
-module voronoi2d(points, spacing = 1, r = 0, delta = 0, chamfer = false, region_type = "square") {
- xs = [for(p = points) p[0]];
- ys = [for(p = points) abs(p[1])];
-
- region_size = max([(max(xs) - min(xs) / 2), (max(ys) - min(ys)) / 2]);
- half_region_size = 0.5 * region_size;
- offset_leng = spacing * 0.5 + half_region_size;
-
- function normalize(v) = v / norm(v);
-
- module region(pt) {
- intersection_for(p = points) {
- if(pt != p) {
- v = p - pt;
- translate((pt + p) / 2 - normalize(v) * offset_leng)
- rotate(atan2(v[1], v[0]))
- if(region_type == "square") {
- square(region_size, center = true);
- }
- else if(region_type == "circle") {
- circle(region_size / 2);
- }
-
- }
- }
- }
-
- for(p = points) {
- if(r != 0) {
- offset(r) region(p);
- }
- else {
- offset(delta = delta, chamfer = chamfer) region(p);
- }
- }
-}
-
-/**
-* shear.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shear.html
-*
-**/
-
-
-module shear(sx = [0, 0], sy = [0, 0], sz = [0, 0]) {
- multmatrix(__m_shearing(sx, sy, sz)) children();
-}
-
-/**
-* rounded_extrude.scad
+* shape_trapezium.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_extrude.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_trapezium.html
*
**/
-module rounded_extrude(size, round_r, angle = 90, twist = 0, convexity = 10) {
-
- is_flt = __is_float(size);
- x = is_flt ? size : size[0];
- y = is_flt ? size : size[1];
+function shape_trapezium(length, h, corner_r = 0) =
+ __trapezium(
+ length = length,
+ h = h,
+ round_r = corner_r
+ );
- q_corner_frags = __frags(round_r) / 4;
-
- step_a = angle / q_corner_frags;
- twist_step = twist / q_corner_frags;
-
- module layers(pre_x, pre_y, pre_h = 0, i = 1) {
- module one_layer(current_a) {
- wx = pre_x;
- wy = pre_y;
-
- h = (round_r - pre_h) - round_r * cos(current_a);
-
- d_leng =
- round_r * (sin(current_a) - sin(step_a * (i - 1)));
-
- sx = (d_leng * 2 + wx) / wx;
- sy = (d_leng * 2 + wy) / wy;
-
- translate([0, 0, pre_h])
- rotate(-twist_step * (i - 1))
- linear_extrude(
- h,
- slices = 1,
- scale = [sx, sy],
- convexity = convexity,
- twist = twist_step
- ) scale([wx / x, wy / y])
- children();
-
- test_rounded_extrude_data(i, wx, wy, pre_h, sx, sy);
-
- layers(wx * sx, wy * sy, h + pre_h, i + 1)
- children();
-
- }
-
- if(i <= q_corner_frags) {
- one_layer(i * step_a)
- children();
- } else if(i - q_corner_frags < 1) {
- one_layer(q_corner_frags * step_a)
- children();
- }
- }
-
- layers(x, y)
- children();
-}
-
-module test_rounded_extrude_data(i, wx, wy, pre_h, sx, sy) {
-
-}
/**
-* arc.scad
+* split_str.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-arc.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-split_str.html
*
**/
+
+function _split_t_by(idxs, t, ts = [], i = -1) =
+ i == -1 ? _split_t_by(idxs, t, [sub_str(t, 0, idxs[0])], i + 1) : (
+ i == len(idxs) - 1 ? concat(ts, sub_str(t, idxs[i] + 1)) :
+ _split_t_by(idxs, t, concat(ts, sub_str(t, idxs[i] + 1, idxs[i + 1])), i + 1)
+ );
+function split_str(t, delimiter) =
+ len(search(delimiter, t)) == 0 ?
+ [t] : _split_t_by(search(delimiter, t, 0)[0], t);
-module arc(radius, angle, width, width_mode = "LINE_CROSS") {
- polygon(__shape_arc(radius, angle, width, width_mode));
-}
+/**
+* bijection_offset.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-bijection_offset.html
+*
+**/
-function __angy_angz(p1, p2) =
+
+function _bijection_inward_edge_normal(edge) =
let(
- dx = p2[0] - p1[0],
- dy = p2[1] - p1[1],
- dz = p2[2] - p1[2],
- ya = atan2(dz, sqrt(pow(dx, 2) + pow(dy, 2))),
- za = atan2(dy, dx)
- ) [ya, za];
+ pt1 = edge[0],
+ pt2 = edge[1],
+ dx = pt2[0] - pt1[0],
+ dy = pt2[1] - pt1[1],
+ edge_leng = norm([dx, dy])
+ )
+ [-dy / edge_leng, dx / edge_leng];
+
+function _bijection_outward_edge_normal(edge) = -1 * _bijection_inward_edge_normal(edge);
+
+function _bijection_offset_edge(edge, dx, dy) =
+ let(
+ pt1 = edge[0],
+ pt2 = edge[1],
+ dxy = [dx, dy]
+ )
+ [pt1 + dxy, pt2 + dxy];
+
+function _bijection__bijection_offset_edges(edges, d) =
+ [
+ for(edge = edges)
+ let(
+ ow_normal = _bijection_outward_edge_normal(edge),
+ dx = ow_normal[0] * d,
+ dy = ow_normal[1] * d
+ )
+ _bijection_offset_edge(edge, dx, dy)
+ ];
+
+function bijection_offset(pts, d, epsilon = 0.0001) =
+ let(
+ es = __lines_from(pts, true),
+ offset_es = _bijection__bijection_offset_edges(es, d),
+ leng = len(offset_es),
+ last_p = __line_intersection(offset_es[leng - 1], offset_es[0], epsilon)
+ )
+ concat(
+ [
+ for(i = [0:leng - 2])
+ let(
+ this_edge = offset_es[i],
+ next_edge = offset_es[i + 1],
+ p = __line_intersection(this_edge, next_edge, epsilon)
+ )
+ // p == p to avoid [nan, nan], because [nan, nan] != [nan, nan]
+ if(p != [] && p == p) p
+ ],
+ last_p != [] && last_p == last_p ? [last_p] : []
+ );
+
/**
-* shape_arc.scad
+* polytransversals.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_arc.html
-*
-**/
-
-
-function shape_arc(radius, angle, width, width_mode = "LINE_CROSS") =
- __shape_arc(radius, angle, width, width_mode);
-
-function __edge_r_begin(orig_r, a, a_step, m) =
- let(leng = orig_r * cos(a_step / 2))
- leng / cos((m - 0.5) * a_step - a);
-
-function __edge_r_end(orig_r, a, a_step, n) =
- let(leng = orig_r * cos(a_step / 2))
- leng / cos((n + 0.5) * a_step - a);
-
-/**
-* sphere_spiral_extrude.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-sphere_spiral_extrude.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-polytransversals.html
*
**/
-module sphere_spiral_extrude(shape_pts, radius, za_step,
- z_circles = 1, begin_angle = 0, end_angle = 0, vt_dir = "SPI_DOWN", rt_dir = "CT_CLK",
- twist = 0, scale = 1.0, triangles = "SOLID") {
- points_angles = sphere_spiral(
- radius = radius,
- za_step = za_step,
- z_circles = z_circles,
- begin_angle = begin_angle,
- end_angle = end_angle,
- vt_dir = vt_dir,
+module polytransversals(transversals) {
+ polygon(
+ __polytransversals(transversals)
+ );
+}
+
+/**
+* shape_pentagram.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_ellipse.html
+*
+**/
+
+function shape_pentagram(r) =
+ [
+ [0, 1], [-0.224514, 0.309017],
+ [-0.951057, 0.309017], [-0.363271, -0.118034],
+ [-0.587785, -0.809017], [0, -0.381966],
+ [0.587785, -0.809017], [0.363271, -0.118034],
+ [0.951057, 0.309017], [0.224514, 0.309017]
+ ] * r;
+
+/**
+* bend_extrude.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-bend_extrude.html
+*
+**/
+
+module bend_extrude(size, thickness, angle, frags = 24) {
+ x = size[0];
+ y = size[1];
+ frag_width = x / frags ;
+ frag_angle = angle / frags;
+ half_frag_width = 0.5 * frag_width;
+ half_frag_angle = 0.5 * frag_angle;
+ r = half_frag_width / sin(half_frag_angle);
+ s = (r - thickness) / r;
+
+ module get_frag(i) {
+ offsetX = i * frag_width;
+ linear_extrude(thickness, scale = [s, 1])
+ translate([-offsetX - half_frag_width, 0, 0])
+ intersection() {
+ translate([x, 0, 0]) mirror([1, 0, 0]) children();
+ translate([offsetX, 0, 0])
+ square([frag_width, y]);
+ }
+ }
+
+ offsetY = -r * cos(half_frag_angle) ;
+ rotate([180, 0, 180]) for(i = [0 : frags - 1]) {
+ rotate(i * frag_angle + half_frag_angle)
+ translate([0, offsetY, 0])
+ rotate([-90, 0, 0])
+ get_frag(i)
+ children();
+ }
+
+}
+
+
+/**
+* hexagons.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-hexagons.html
+*
+**/
+
+module hexagons(radius, spacing, levels) {
+ beginning_n = 2 * levels - 1;
+ offset_x = radius * cos(30);
+ offset_y = radius + radius * sin(30);
+ r_hexagon = radius - spacing / 2;
+ offset_step = 2 * offset_x;
+ center_offset = 2 * (offset_x - offset_x * levels);
+
+ module hexagon() {
+ rotate(30)
+ circle(r_hexagon, $fn = 6);
+ }
+
+ function hexagons_pts(hex_datum) =
+ let(
+ tx = hex_datum[0][0],
+ ty = hex_datum[0][1],
+ n = hex_datum[1],
+ offset_xs = [for(i = [0:n - 1]) i * offset_step + center_offset]
+ )
+ [
+ for(x = offset_xs) [x + tx, ty]
+ ];
+
+ module line_hexagons(hex_datum) {
+ tx = hex_datum[0][0];
+ ty = hex_datum[0][1];
+ n = hex_datum[1];
+
+ offset_xs = [for(i = [0:n - 1]) i * offset_step + center_offset];
+ for(x = offset_xs) {
+ p = [x + tx, ty, 0];
+ translate(p)
+ hexagon();
+ }
+ }
+
+ upper_hex_data = levels > 1 ? [
+ for(i = [1:beginning_n - levels])
+ let(
+ x = offset_x * i,
+ y = offset_y * i,
+ n = beginning_n - i
+ ) [[x, y], n]
+ ] : [];
+
+ lower_hex_data = levels > 1 ? [
+ for(hex_datum = upper_hex_data)
+ [[hex_datum[0][0], -hex_datum[0][1]], hex_datum[1]]
+ ] : [];
+
+ total_hex_data = concat(
+ [
+ [[0, 0], beginning_n] // first line
+ ],
+ upper_hex_data,
+ lower_hex_data
+ );
+
+ pts_all_lines = [
+ for(hex_datum = total_hex_data)
+ hexagons_pts(hex_datum)
+ ];
+
+ for(pts_one_line = pts_all_lines) {
+ for(pt = pts_one_line) {
+ translate(pt)
+ hexagon();
+ }
+ }
+
+ test_each_hexagon(r_hexagon, pts_all_lines);
+}
+
+// override it to test
+module test_each_hexagon(hex_r, pts_all_lines) {
+
+}
+
+/**
+* golden_spiral_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-golden_spiral_extrude.html
+*
+**/
+
+module golden_spiral_extrude(shape_pts, from, to, point_distance,
+ rt_dir = "CT_CLK", twist = 0, scale = 1.0, triangles = "SOLID") {
+
+ pts_angles = golden_spiral(
+ from = from,
+ to = to,
+ point_distance = point_distance,
rt_dir = rt_dir
);
- v_clk = vt_dir == "SPI_DOWN" ? 90 : -90;
- r_clk = rt_dir == "CT_CLK" ? 0 : 180;
-
- points = [for(pa = points_angles) pa[0]];
- angles = [for(pa = points_angles) [pa[1][0] + v_clk, pa[1][1], pa[1][2] + r_clk]];
+ pts = [for(pt_angle = pts_angles) pt_angle[0]];
+ angles = [
+ for(pt_angle = pts_angles)
+ [90, 0, pt_angle[1] + (rt_dir == "CT_CLK" ? 0 : -90)]
+ ];
sections = cross_sections(
- shape_pts, points, angles, twist = twist, scale = scale
+ shape_pts,
+ pts, angles,
+ twist = twist,
+ scale = scale
);
polysections(
- sections,
+ sections,
triangles = triangles
);
// testing hook
- test_sphere_spiral_extrude(sections);
+ test_golden_spiral_extrude(sections);
}
// override it to test
-module test_sphere_spiral_extrude(sections) {
+module test_golden_spiral_extrude(sections) {
}
/**
-* line2d.scad
+* shape_superformula.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-line2d.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_superformula.html
+*
+**/
+
+
+function _superformula_r(angle, m1, m2, n1, n2 = 1, n3 = 1, a = 1, b = 1) =
+ pow(
+ pow(abs(cos(m1 * angle / 4) / a), n2) +
+ pow(abs(sin(m2 * angle / 4) / b), n3),
+ - 1 / n1
+ );
+
+function shape_superformula(phi_step, m1, m2, n1, n2 = 1, n3 = 1, a = 1, b = 1) =
+ [
+ for(phi = [0:phi_step:6.28318])
+ let(
+ angle = __to_degree(phi),
+ r = _superformula_r(angle, m1, m2, n1, n2, n3, a, b)
+
+ )
+ __ra_to_xy(r, angle)
+ ];
+
+/**
+* archimedean_spiral_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-archimedean_spiral_extrude.html
*
**/
+module archimedean_spiral_extrude(shape_pts, arm_distance, init_angle, point_distance, num_of_points,
+ rt_dir = "CT_CLK", twist = 0, scale = 1.0, triangles = "SOLID") {
+ points_angles = archimedean_spiral(
+ arm_distance = arm_distance,
+ init_angle = init_angle,
+ point_distance = point_distance,
+ num_of_points = num_of_points,
+ rt_dir = rt_dir
+ );
-module line2d(p1, p2, width, p1Style = "CAP_SQUARE", p2Style = "CAP_SQUARE") {
- half_width = 0.5 * width;
+ clk_a = rt_dir == "CT_CLK" ? 0 : 180;
- atan_angle = atan2(p2[1] - p1[1], p2[0] - p1[0]);
- leng = sqrt(pow(p2[0] - p1[0], 2) + pow(p2[1] - p1[1], 2));
+ points = [for(pa = points_angles) pa[0]];
+ angles = [
+ for(pa = points_angles)
+ [90, 0, pa[1] + clk_a]
+ ];
- frags = __nearest_multiple_of_4(__frags(half_width));
-
- module square_end(point) {
- translate(point)
- rotate(atan_angle)
- square(width, center = true);
+ sections = cross_sections(shape_pts, points, angles, twist, scale);
- // hook for testing
- test_line2d_cap(point, "CAP_SQUARE");
- }
+ polysections(
+ sections,
+ triangles = triangles
+ );
- module round_end(point) {
- translate(point)
- rotate(atan_angle)
- circle(half_width, $fn = frags);
+ // testing hook
+ test_archimedean_spiral_extrude(sections);
+}
- // hook for testing
- test_line2d_cap(point, "CAP_ROUND");
- }
-
- if(p1Style == "CAP_SQUARE") {
- square_end(p1);
- } else if(p1Style == "CAP_ROUND") {
- round_end(p1);
- }
+// override it to test
+module test_archimedean_spiral_extrude(sections) {
- translate(p1)
- rotate(atan_angle)
- translate([0, -width / 2])
- square([leng, width]);
-
- if(p2Style == "CAP_SQUARE") {
- square_end(p2);
- } else if(p2Style == "CAP_ROUND") {
- round_end(p2);
- }
-
- // hook for testing
- test_line2d_line(atan_angle, leng, width, frags);
}
-// override them to test
-module test_line2d_cap(point, style) {
+function __is_float(value) = value + 0 != undef;
+
+/**
+* multi_line_text.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-multi_line_text.html
+*
+**/
+module multi_line_text(lines, line_spacing = 15, size = 10, font = "Arial", halign = "left", valign = "baseline", direction = "ltr", language = "en", script = "latin"){
+ to = len(lines) - 1;
+ inc = line_spacing;
+ offset_y = inc * to / 2;
+ union() {
+ for (i = [0 : to]) {
+ translate([0 , -i * inc + offset_y, 0])
+ text(lines[i], size, font = font, valign = valign, halign = halign, direction = direction, language = language, script = script);
+ }
+ }
}
-module test_line2d_line(angle, length, width, frags) {
-}
+function __pie_for_rounding(r, begin_a, end_a, frags) =
+ let(
+ sector_angle = end_a - begin_a,
+ step_a = sector_angle / frags,
+ is_integer = frags % 1 == 0
+ )
+ r < 0.00005 ? [[0, 0]] : concat([
+ for(ang = [begin_a:step_a:end_a])
+ [
+ r * cos(ang),
+ r * sin(ang)
+ ]
+ ],
+ is_integer ? [] : [[
+ r * cos(end_a),
+ r * sin(end_a)
+ ]]
+ );
+function __to_3_elems_ang_vect(a) =
+ let(leng = len(a))
+ leng == 3 ? a : (
+ leng == 2 ? [a[0], a[1], 0] : [a[0], 0, 0]
+ );
+
+function __to_ang_vect(a) = __is_float(a) ? [0, 0, a] : __to_3_elems_ang_vect(a);
+
+function __reverse(vt) =
+ let(leng = len(vt))
+ [
+ for(i = [0:leng - 1])
+ vt[leng - 1 - i]
+ ];
+
+function __to3d(p) = [p[0], p[1], 0];
/**
-* torus_knot.scad
+* shape_glued2circles.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_glued2circles.html
+*
+**/
+
+function _glued2circles_pie_curve(radius, centre_dist, tangent_angle) =
+ let(
+ begin_ang = 90 + tangent_angle,
+ shape_pts = shape_pie(radius, [-begin_ang, begin_ang])
+ )
+ [
+ for(i = [1:len(shape_pts) - 1])
+ shape_pts[i] + [centre_dist / 2, 0]
+ ];
+
+function _glued2circles_bezier(radius, centre_dist, tangent_angle, t_step, ctrl_p1) =
+ let(
+ ctrl_p = rotate_p([radius * tan(tangent_angle), -radius], tangent_angle),
+ ctrl_p2 = [-ctrl_p[0], ctrl_p[1]] + [centre_dist / 2, 0],
+ ctrl_p3 = [-ctrl_p2[0], ctrl_p2[1]],
+ ctrl_p4 = [-ctrl_p1[0], ctrl_p1[1]]
+ )
+ bezier_curve(
+ t_step,
+ [
+ ctrl_p1,
+ ctrl_p2,
+ ctrl_p3,
+ ctrl_p4
+ ]
+ );
+
+function _glued2circles_lower_half_curve(curve_pts, leng, i = 0) =
+ i < leng ? (
+ curve_pts[leng - 1 - i][0] >= 0 ?
+ concat(
+ [curve_pts[leng - 1 - i]],
+ _glued2circles_lower_half_curve(curve_pts, leng, i + 1)
+ ) :
+ _glued2circles_lower_half_curve(curve_pts, leng, i + 1)
+ ) : [];
+
+function _glued2circles_half_glued_circle(radius, centre_dist, tangent_angle, t_step) =
+ let(
+ pie_curve_pts = _glued2circles_pie_curve(radius, centre_dist, tangent_angle),
+ curve_pts = _glued2circles_bezier(radius, centre_dist, tangent_angle, t_step, pie_curve_pts[0]),
+ lower_curve_pts = _glued2circles_lower_half_curve(curve_pts, len(curve_pts)),
+ leng_half_curve_pts = len(lower_curve_pts),
+ upper_curve_pts = [
+ for(i = [0:leng_half_curve_pts - 1])
+ let(pt = lower_curve_pts[leng_half_curve_pts - 1 - i])
+ [pt[0], -pt[1]]
+ ]
+ ) concat(
+ lower_curve_pts,
+ pie_curve_pts,
+ upper_curve_pts
+ );
+
+function shape_glued2circles(radius, centre_dist, tangent_angle = 30, t_step = 0.1) =
+ let(
+ half_glued_circles = _glued2circles_half_glued_circle(radius, centre_dist, tangent_angle, t_step),
+ leng_half_glued_circles = len(half_glued_circles),
+ left_half_glued_circles = [
+ for(i = [0:leng_half_glued_circles - 1])
+ let(pt = half_glued_circles[leng_half_glued_circles - 1 - i])
+ [-pt[0], pt[1]]
+ ]
+ ) concat(half_glued_circles, left_half_glued_circles);
+
+/**
+* pie.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-pie.html
+*
+**/
+
+
+module pie(radius, angle) {
+ polygon(__shape_pie(radius, angle));
+}
+
+/**
+* in_polyline.scad
*
* @copyright Justin Lin, 2019
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-torus_knot.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-in_polyline.html
*
**/
-function torus_knot(p, q, phi_step) = [
- for(phi = [0:phi_step:6.28318])
- let(
- degree = phi * 180 / 3.14159,
- r = cos(q * degree) + 2,
- x = r * cos(p * degree),
- y = r * sin(p * degree),
- z = -sin(q * degree)
- )
- [x, y, z]
-];
-/**
-* midpt_smooth.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-midpt_smooth.html
-*
-**/
+function _in_polyline_sub(pts, pt, epsilon, iend, i = 0) =
+ i == iend ? false : (
+ __in_line([pts[i], pts[i + 1]], pt, epsilon) ? true :
+ _in_polyline_sub(pts, pt, epsilon, iend, i + 1)
+ );
-function _midpt_smooth_sub(points, iend, i, closed = false) =
- i == iend ? (
- closed ? [(points[i] + points[0]) / 2]
- : []
- ) : concat([(points[i] + points[i + 1]) / 2], _midpt_smooth_sub(points, iend, i + 1, closed));
+function in_polyline(line_pts, pt, epsilon = 0.0001) =
+ _in_polyline_sub(line_pts, pt, epsilon, len(line_pts) - 1);
+
-function midpt_smooth(points, n, closed = false) =
- let(
- smoothed = _midpt_smooth_sub(points, len(points) - 1, 0, closed)
- )
- n == 1 ? smoothed : midpt_smooth(smoothed, n - 1, closed);
+function __ra_to_xy(r, a) = [r * cos(a), r * sin(a)];
/**
* function_grapher.scad
@@ -3883,6 +2367,959 @@ module test_function_grapher_faces(points, faces) {
}
+/**
+* turtle3d.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-turtle3d.html
+*
+**/
+
+function _turtle3d_x(pt) = pt[0];
+function _turtle3d_y(pt) = pt[1];
+function _turtle3d_z(pt) = pt[2];
+
+function _turtle3d_pt3D(x, y, z) = [x, y, z];
+
+function _turtle3d_create(pt, unit_vts) = [pt, unit_vts];
+function _turtle3d_create_default() = _turtle3d_create(
+ _turtle3d_pt3D(0, 0, 0),
+ // unit vectors from the turtle's viewpoint
+ [_turtle3d_pt3D(1, 0, 0), _turtle3d_pt3D(0, 1, 0), _turtle3d_pt3D(0, 0, 1)]
+);
+
+function _turtle3d_plus(pt, n) =
+ _turtle3d_pt3D(_turtle3d_x(pt) + n, _turtle3d_y(pt) + n, _turtle3d_z(pt) + n);
+function _turtle3d_minus(pt, n) =
+ _turtle3d_pt3D(_turtle3d_x(pt) - n, _turtle3d_y(pt) - n, _turtle3d_z(pt) + n);
+function _turtle3d_mlt(pt, n) =
+ _turtle3d_pt3D(_turtle3d_x(pt) * n, _turtle3d_y(pt) * n, _turtle3d_z(pt) * n);
+function _turtle3d_div(pt, n) =
+ _turtle3d_pt3D(_turtle3d_x(pt) / n, _turtle3d_y(pt) / n, _turtle3d_z(pt) / n);
+function _turtle3d_neg(pt, n) =
+ _turtle3d_mlt(pt, -1);
+
+function _turtle3d_ptPlus(pt1, pt2) =
+ _turtle3d_pt3D(
+ _turtle3d_x(pt1) + _turtle3d_x(pt2),
+ _turtle3d_y(pt1) + _turtle3d_y(pt2),
+ _turtle3d_z(pt1) + _turtle3d_z(pt2)
+ );
+
+
+function _turtle3d_pt(turtle) = turtle[0];
+function _turtle3d_unit_vts(turtle) = turtle[1];
+
+// forward the turtle in the x' direction
+function _turtle3d_xu_move(turtle, leng) = _turtle3d_create(
+ _turtle3d_ptPlus(_turtle3d_pt(turtle), _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], leng)),
+ _turtle3d_unit_vts(turtle)
+);
+
+// forward the turtle in the y' direction
+function _turtle3d_yu_move(turtle, leng) = _turtle3d_create(
+ _turtle3d_ptPlus(_turtle3d_pt(turtle), _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], leng)),
+ _turtle3d_unit_vts(turtle)
+);
+
+// forward the turtle in the z' direction
+function _turtle3d_zu_move(turtle, leng) = _turtle3d_create(
+ _turtle3d_ptPlus(
+ _turtle3d_pt(turtle),
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], leng)
+ ),
+ _turtle3d_unit_vts(turtle)
+);
+
+// turn the turtle around the x'-axis
+// return a new unit vector
+function _turtle3d_xu_turn(turtle, a) =
+ let(cosa = cos(a), sina = sin(a))
+ _turtle3d_create(
+ _turtle3d_pt(turtle),
+ [
+ _turtle3d_unit_vts(turtle)[0],
+ _turtle3d_ptPlus(
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], cosa),
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], sina)
+ ),
+ _turtle3d_ptPlus(
+ _turtle3d_mlt(_turtle3d_neg(_turtle3d_unit_vts(turtle)[1]), sina),
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], cosa)
+ )
+ ]
+ );
+
+// turn the turtle around the y'-axis
+// return a new unit vector
+function _turtle3d_yu_turn(turtle, a) =
+ let(cosa = cos(a), sina = sin(a))
+ _turtle3d_create(
+ _turtle3d_pt(turtle),
+ [
+ _turtle3d_ptPlus(
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], cosa),
+ _turtle3d_mlt(_turtle3d_neg(_turtle3d_unit_vts(turtle)[2]), sina)
+ ),
+ _turtle3d_unit_vts(turtle)[1],
+ _turtle3d_ptPlus(
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], sina),
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[2], cosa)
+ )
+ ]
+ );
+
+// turn the turtle around the z'-axis
+// return a new unit vector
+function _turtle3d_zu_turn(turtle, a) =
+ let(cosa = cos(a), sina = sin(a))
+ _turtle3d_create(
+ _turtle3d_pt(turtle),
+ [
+ _turtle3d_ptPlus(
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[0], cosa),
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], sina)
+ ),
+ _turtle3d_ptPlus(
+ _turtle3d_mlt(_turtle3d_neg(_turtle3d_unit_vts(turtle)[0]), sina),
+ _turtle3d_mlt(_turtle3d_unit_vts(turtle)[1], cosa)
+ ),
+ _turtle3d_unit_vts(turtle)[2],
+ ]
+ );
+
+function _turtle3d_create_cmd(arg1, arg2) =
+ (arg1 == undef && arg2 == undef) ? _turtle3d_create_default() : (
+ (arg1 != undef && arg2 != undef) ? _turtle3d_create(arg1, arg2) : undef
+ );
+
+function _turtle3d_chain_move(cmd, arg1, arg2) =
+ cmd == "xu_move" ? _turtle3d_xu_move(arg1, arg2) : (
+ cmd == "yu_move" ? _turtle3d_yu_move(arg1, arg2) : (
+ cmd == "zu_move" ? _turtle3d_zu_move(arg1, arg2) : _turtle3d_chain_turn(cmd, arg1, arg2)
+ )
+ );
+
+function _turtle3d_chain_turn(cmd, arg1, arg2) =
+ cmd == "xu_turn" ? _turtle3d_xu_turn(arg1, arg2) : (
+ cmd == "yu_turn" ? _turtle3d_yu_turn(arg1, arg2) : (
+ cmd == "zu_turn" ? _turtle3d_zu_turn(arg1, arg2) : _turtle3d_chain_one_arg(cmd, arg1)
+ )
+ );
+
+function _turtle3d_chain_one_arg(cmd, arg) =
+ cmd == "pt" ? _turtle3d_pt(arg) : (
+ cmd == "unit_vts" ? _turtle3d_unit_vts(arg) : undef
+ );
+
+function turtle3d(cmd, arg1, arg2) =
+ cmd == "create" ?
+ _turtle3d_create_cmd(arg1, arg2) :
+ _turtle3d_chain_move(cmd, arg1, arg2);
+
+/**
+* shape_taiwan.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_taiwan.html
+*
+**/
+
+function shape_taiwan(h, distance = 0) =
+ let(tw = [[3.85724, 6.24078], [3.7902, 6.25779], [3.73596, 6.26288], [3.62807, 6.24587], [3.59581, 6.2602], [3.55429, 6.32386], [3.51698, 6.4571], [3.50768, 6.5361], [3.51188, 6.58284], [3.53917, 6.63708], [3.48981, 6.66934], [2.78286, 6.83905], [2.7218, 6.86112], [2.68441, 6.90272], [2.61063, 6.96209], [2.64288, 7.05549], [2.61062, 7.06568], [2.56396, 7.06058], [2.50281, 7.04109], [2.44849, 7.15647], [2.29319, 7.37123], [2.22438, 7.42555], [2.05644, 7.44248], [1.88674, 7.40509], [1.73901, 7.32879], [1.63795, 7.238], [1.58868, 7.14974], [1.54463, 7.0555], [1.5149, 7.0411], [1.48526, 7.01895], [1.53658, 6.9725], [1.58825, 6.92909], [1.63096, 6.86281], [1.61348, 6.80387], [1.57757, 6.83721], [1.53949, 6.89116], [1.48525, 6.87393], [1.43851, 6.87393], [1.40381, 6.86623], [1.37408, 6.85174], [1.28068, 6.77282], [1.20682, 6.7457], [0.723984, 6.65659], [0.586451, 6.60741], [0.5152, 6.57094], [0.458435, 6.52167], [0.393838, 6.47922], [0.108666, 6.38843], [-0.002506, 6.2925], [-0.110307, 6.16693], [-0.278327, 5.89785], [-0.312266, 5.80452], [-0.319942, 5.74076], [-0.35978, 5.72804], [-0.398773, 5.69411], [-0.431113, 5.64921], [-0.443073, 5.60508], [-0.45317, 5.54563], [-0.477762, 5.51429], [-0.504966, 5.48196], [-0.527025, 5.4379], [-0.511683, 5.32413], [-0.544022, 5.28008], [-0.630518, 5.10523], [-0.652506, 5.02118], [-0.721317, 4.96517], [-0.751047, 4.87851], [-0.807895, 4.81922], [-0.849502, 4.76313], [-0.835106, 4.67916], [-0.94535, 4.70123], [-1.03193, 4.67664], [-1.10318, 4.62476], [-1.16685, 4.55856], [-1.18724, 4.52125], [-1.20679, 4.47981], [-1.23643, 4.44755], [-1.33927, 4.41782], [-1.36462, 4.37621], [-1.38844, 4.28037], [-1.47747, 4.11993], [-1.51477, 4.03419], [-1.52674, 3.93321], [-1.55638, 3.87122], [-1.87389, 3.52162], [-1.92308, 3.43757], [-1.96721, 3.28479], [-2.11055, 3.03862], [-2.15224, 2.84946], [-2.18703, 2.75092], [-2.23874, 2.70864], [-2.2481, 2.65676], [-2.43313, 2.44284], [-2.47482, 2.40393], [-2.48998, 2.36148], [-2.49681, 2.20617], [-2.51727, 2.15951], [-2.59526, 2.09828], [-2.61312, 2.05583], [-2.62237, 2.00926], [-2.64705, 1.98728], [-2.68183, 1.97279], [-2.71915, 1.94559], [-2.76589, 1.90137], [-2.80741, 1.82768], [-2.89576, 1.58074], [-2.9601, 1.46267], [-3.00171, 1.34223], [-3.03396, 1.29978], [-3.14261, 1.19451], [-3.16223, 1.14945], [-3.18683, 1.04173], [-3.21142, 0.994987], [-3.30988, 0.906048], [-3.32504, 0.864443], [-3.42105, 0.738702], [-3.43797, 0.692212], [-3.47452, 0.558807], [-3.54409, 0.411083], [-3.62553, 0.283068], [-3.69434, 0.142925], [-3.72407, -0.047077], [-3.71634, -0.135425], [-3.67474, -0.317847], [-3.65948, -0.475761], [-3.62302, -0.539348], [-3.61029, -0.588533], [-3.62554, -0.628538], [-3.65703, -0.687068], [-3.61029, -0.73878], [-3.64002, -0.795629], [-3.57542, -0.918676], [-3.67902, -1.14784], [-3.62553, -1.18944], [-3.59589, -1.2046], [-3.60515, -1.24376], [-3.6476, -1.28781], [-3.68162, -1.37186], [-3.72138, -1.38205], [-3.74597, -1.40925], [-3.79785, -1.54426], [-3.80458, -1.58157], [-3.81731, -1.61121], [-3.84956, -1.65526], [-3.87677, -1.70958], [-3.8665, -1.75893], [-3.84714, -1.80812], [-3.83434, -1.86665], [-3.85397, -1.90666], [-3.89633, -1.91845], [-3.93532, -1.94052], [-3.94552, -2.0025], [-3.90315, -2.05169], [-3.83687, -2.10846], [-3.91452, -2.12525], [-3.92486, -2.16746], [-3.8021, -2.21718], [-3.78004, -2.24169], [-3.79537, -2.28599], [-3.84194, -2.30292], [-3.9014, -2.31052], [-3.94553, -2.3325], [-3.90902, -2.38984], [-3.81228, -2.42599], [-3.7487, -2.45311], [-3.7359, -2.49042], [-3.61032, -2.51249], [-3.51439, -2.52698], [-3.49737, -2.60598], [-3.53645, -2.69921], [-3.61031, -2.75101], [-3.55085, -2.813], [-3.52197, -2.86732], [-3.47455, -3.17194], [-3.46268, -3.20175], [-3.42108, -3.27805], [-3.36173, -3.36723], [-3.3667, -3.40353], [-3.39137, -3.45802], [-3.39137, -3.50468], [-3.37605, -3.57332], [-3.3048, -3.72879], [-3.22849, -3.84755], [-3.18689, -3.9332], [-3.16473, -4.02239], [-3.18175, -4.08606], [-3.21401, -4.15723], [-3.18942, -4.22848], [-3.1377, -4.29316], [-2.99756, -4.4237], [-2.91612, -4.51458], [-2.85675, -4.62575], [-2.84478, -4.76572], [-2.64282, -4.92119], [-2.55624, -4.90831], [-2.51219, -4.92877], [-2.47733, -4.95075], [-2.40355, -5.01441], [-2.33222, -5.06116], [-2.17691, -5.13485], [-2.10053, -5.19944], [-2.05126, -5.27328], [-2.00199, -5.30293], [-1.87641, -5.40147], [-1.84416, -5.44552], [-1.77804, -5.59577], [-1.69399, -5.70181], [-1.66687, -5.74611], [-1.35963, -6.53694], [-1.36899, -6.58351], [-1.4063, -6.638], [-1.41556, -6.68466], [-1.41556, -6.84915], [-1.42322, -6.93076], [-1.41556, -6.96992], [-1.33, -7.11073], [-1.34945, -7.21845], [-1.34692, -7.25315], [-1.28999, -7.29989], [-1.23921, -7.31522], [-1.20181, -7.27783], [-1.19675, -7.17685], [-1.07362, -7.20658], [-0.953101, -7.2609], [-0.849593, -7.33982], [-0.778259, -7.44248], [-0.775729, -7.35413], [-0.795186, -7.21416], [-0.797786, -7.08606], [-0.736641, -7.02913], [-0.685014, -6.99014], [-0.645009, -6.89851], [-0.620417, -6.78565], [-0.637343, -5.77062], [-0.612751, -5.52192], [-0.590692, -5.43534], [-0.443136, -5.1799], [-0.371802, -4.92362], [-0.341989, -4.73344], [-0.29036, -4.64283], [-0.16723, -4.48735], [-0.128151, -4.41105], [-0.046711, -4.20395], [-0.014539, -4.16235], [0.027066, -4.14297], [0.191803, -3.96039], [0.441265, -3.79565], [0.515127, -3.72608], [0.588904, -3.63285], [0.62032, -3.58105], [0.640788, -3.5317], [0.643228, -3.48757], [0.633124, -3.39173], [0.640794, -3.35189], [0.689979, -3.29336], [0.9039, -3.11338], [0.923353, -3.07093], [1.08363, -2.84522], [1.11605, -2.75342], [1.16262, -2.55407], [1.23396, -2.42344], [1.26116, -2.33518], [1.27809, -2.30031], [1.31035, -2.26637], [1.38421, -2.21213], [1.41638, -2.17987], [1.49024, -2.02701], [1.50464, -1.87935], [1.50212, -1.72405], [1.52671, -1.54937], [1.55897, -1.45589], [1.59897, -1.37942], [1.70669, -1.23616], [1.74147, -1.16044], [1.98243, 0.226167], [2.05637, 0.376416], [2.18195, 0.989965], [2.27788, 1.25316], [2.32959, 1.50186], [2.33469, 1.60107], [2.34741, 1.65447], [2.40173, 1.74366], [2.42118, 1.79798], [2.38391, 1.88363], [2.36176, 1.9185], [2.33473, 1.99994], [2.33213, 2.07136], [2.3542, 2.13747], [2.46798, 2.30473], [2.51203, 2.49221], [2.55625, 2.58553], [2.59356, 2.62284], [2.71147, 2.72138], [2.69968, 2.75364], [2.72175, 2.78497], [2.793, 2.8419], [2.89145, 2.97513], [2.98064, 3.06078], [2.97805, 3.26778], [2.98485, 3.3638], [2.99757, 3.41297], [3.01702, 3.44775], [3.03412, 3.48936], [3.02732, 3.59547], [3.03152, 3.64474], [3.14008, 3.76771], [3.23348, 3.82464], [3.27332, 3.8611], [3.26572, 3.90356], [3.23852, 3.94517], [3.22673, 3.98929], [3.23343, 4.02904], [3.32515, 4.08085], [3.33955, 4.16659], [3.31748, 4.26758], [3.2784, 4.34405], [3.3056, 4.3635], [3.37526, 4.38649], [3.39869, 4.42513], [3.32514, 4.42541], [3.22669, 4.4874], [3.17237, 4.58324], [3.16977, 4.70636], [3.19192, 4.7675], [3.19432, 4.8294], [3.14261, 5.06624], [3.14001, 5.17909], [3.14781, 5.29709], [3.1724, 5.41583], [3.20634, 5.51943], [3.25384, 5.59329], [3.54153, 5.94036], [3.62558, 6.01431], [3.94553, 6.17215], [3.91327, 6.21106]] / 15 * h)
+ distance == 0 ? tw : [for(i = [0 : len(tw) - 1]) if(norm(tw[i] - tw[i + 1]) > distance) tw[i]];
+
+
+function __line_intersection(line_pts1, line_pts2, epsilon = 0.0001) =
+ let(
+ a1 = line_pts1[0],
+ a2 = line_pts1[1],
+ b1 = line_pts2[0],
+ b2 = line_pts2[1],
+ a = a2 - a1,
+ b = b2 - b1,
+ s = b1 - a1
+ )
+ abs(cross(a, b)) < epsilon ? [] : // they are parallel or conincident edges
+ a1 + a * cross(s, b) / cross(a, b);
+
+/**
+* shape_star.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_starburst.html
+*
+**/
+
+function __outer_points_shape_starburst(r1, r2, n) =
+ let(
+ a = 360 / n
+ )
+ [for(i = [0:n-1]) [r1 * cos(a * i), r1 * sin(a * i)]];
+function __inner_points_shape_starburst(r1, r2, n) =
+ let (
+ a = 360 / n,
+ half_a = a / 2
+ )
+ [for(i = [0:n-1]) [r2 * cos(a * i + half_a), r2 * sin(a * i + half_a)]];
+
+function __one_by_one_shape_starburst(outer_points, inner_points, i = 0) =
+ len(outer_points) == i ? [] :
+ concat([outer_points[i], inner_points[i]], __one_by_one_shape_starburst(outer_points, inner_points, i + 1));
+
+function shape_starburst(r1, r2, n) = __one_by_one_shape_starburst(
+ __outer_points_shape_starburst(r1, r2, n), __inner_points_shape_starburst(r1, r2, n)
+);
+
+/**
+* line3d.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-line3d.html
+*
+**/
+
+
+module line3d(p1, p2, thickness, p1Style = "CAP_CIRCLE", p2Style = "CAP_CIRCLE") {
+ r = thickness / 2;
+
+ frags = __nearest_multiple_of_4(__frags(r));
+ half_fa = 180 / frags;
+
+ dx = p2[0] - p1[0];
+ dy = p2[1] - p1[1];
+ dz = p2[2] - p1[2];
+
+ length = sqrt(pow(dx, 2) + pow(dy, 2) + pow(dz, 2));
+ ay = 90 - atan2(dz, sqrt(pow(dx, 2) + pow(dy, 2)));
+ az = atan2(dy, dx);
+
+ angles = [0, ay, az];
+
+ module cap_with(p) {
+ translate(p)
+ rotate(angles)
+ children();
+ }
+
+ module cap_butt() {
+ cap_with(p1)
+ linear_extrude(length)
+ circle(r, $fn = frags);
+
+ // hook for testing
+ test_line3d_butt(p1, r, frags, length, angles);
+ }
+
+ module cap(p, style) {
+ if(style == "CAP_CIRCLE") {
+ cap_leng = r / 1.414;
+ cap_with(p)
+ linear_extrude(cap_leng * 2, center = true)
+ circle(r, $fn = frags);
+
+ // hook for testing
+ test_line3d_cap(p, r, frags, cap_leng, angles);
+ } else if(style == "CAP_SPHERE") {
+ cap_leng = r / cos(half_fa);
+ cap_with(p)
+ sphere(cap_leng, $fn = frags);
+
+ // hook for testing
+ test_line3d_cap(p, r, frags, cap_leng, angles);
+ }
+ }
+
+
+ cap_butt();
+ cap(p1, p1Style);
+ cap(p2, p2Style);
+}
+
+// Override them to test
+module test_line3d_butt(p, r, frags, length, angles) {
+
+}
+
+module test_line3d_cap(p, r, frags, cap_leng, angles) {
+
+}
+
+/**
+* golden_spiral.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-golden_spiral.html
+*
+**/
+
+function _fast_fibonacci_sub(nth) =
+ let(
+ _f = _fast_fibonacci_2_elems(floor(nth / 2)),
+ a = _f[0],
+ b = _f[1],
+ c = a * (b * 2 - a),
+ d = a * a + b * b
+ )
+ nth % 2 == 0 ? [c, d] : [d, c + d];
+
+function _fast_fibonacci_2_elems(nth) =
+ nth == 0 ? [0, 1] : _fast_fibonacci_sub(nth);
+
+function _fast_fibonacci(nth) =
+ _fast_fibonacci_2_elems(nth)[0];
+
+function _remove_same_pts(pts1, pts2) =
+ pts1[len(pts1) - 1] == pts2[0] ?
+ concat(pts1, [for(i = [1:len(pts2) - 1]) pts2[i]]) :
+ concat(pts1, pts2);
+
+function _golden_spiral_from_ls_or_eql_to(from, to, point_distance, rt_dir) =
+ let(
+ f1 = _fast_fibonacci(from),
+ f2 = _fast_fibonacci(from + 1),
+ fn = floor(f1 * 6.28312 / point_distance),
+ $fn = fn + 4 - (fn % 4),
+ circle_pts = circle_path(radius = f1, n = $fn / 4 + 1),
+ len_pts = len(circle_pts),
+ a_step = 360 / $fn * rt_dir,
+ arc_points_angles = (rt_dir == 1 ? [
+ for(i = [0:len_pts - 1])
+ [circle_pts[i], a_step * i]
+ ] : [
+ for(i = [0:len_pts - 1]) let(idx = len_pts - i - 1)
+ [circle_pts[idx], a_step * i]
+ ]),
+ offset = f2 - f1
+ ) _remove_same_pts(
+ arc_points_angles,
+ [
+ for(pt_a = _golden_spiral(from + 1, to, point_distance, rt_dir))
+ [
+ rotate_p(pt_a[0], [0, 0, 90 * rt_dir]) +
+ (rt_dir == 1 ? [0, -offset, 0] : [-offset, 0, 0]),
+ pt_a[1] + 90 * rt_dir
+ ]
+ ]
+ );
+
+function _golden_spiral(from, to, point_distance, rt_dir) =
+ from <= to ?
+ _golden_spiral_from_ls_or_eql_to(from, to, point_distance, rt_dir) : [];
+
+function golden_spiral(from, to, point_distance, rt_dir = "CT_CLK") =
+ _golden_spiral(from, to, point_distance, (rt_dir == "CT_CLK" ? 1 : -1));
+
+/**
+* m_cumulate.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-m_cumulate.html
+*
+**/
+
+function _m_cumulate(matrice, i) =
+ i == len(matrice) - 2 ?
+ matrice[i] * matrice[i + 1] :
+ matrice[i] * _m_cumulate(matrice, i + 1);
+
+function m_cumulate(matrice) =
+ len(matrice) == 1 ? matrice[0] : _m_cumulate(matrice, 0);
+
+
+
+/**
+* voronoi3d.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-voronoi3d.html
+*
+**/
+
+
+// slow but workable
+
+module voronoi3d(points, space_size = "auto", spacing = 1) {
+ xs = [for(p = points) p[0]];
+ ys = [for(p = points) abs(p[1])];
+ zs = [for(p = points) abs(p[2])];
+
+ space_size = max([max(xs) - min(xs), max(ys) - min(ys), max(zs) - min(zs)]);
+ half_space_size = 0.5 * space_size;
+ offset_leng = spacing * 0.5 + half_space_size;
+
+ function normalize(v) = v / norm(v);
+
+ module space(pt) {
+ intersection_for(p = points) {
+ if(pt != p) {
+ v = p - pt;
+ ryz = __angy_angz(p, pt);
+
+ translate((pt + p) / 2 - normalize(v) * offset_leng)
+ rotate([0, -ryz[0], ryz[1]])
+ cube([space_size, space_size * 2, space_size * 2], center = true);
+ }
+ }
+ }
+
+ for(p = points) {
+ space(p);
+ }
+}
+
+/**
+* crystal_ball.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-crystal_ball.html
+*
+**/
+
+
+module crystal_ball(radius, theta = 360, phi = 180) {
+ phis = __is_float(phi) ? [0, phi] : phi;
+
+ frags = __frags(radius);
+
+ shape_pts = shape_pie(
+ radius,
+ [90 - phis[1], 90 - phis[0]],
+ $fn = __nearest_multiple_of_4(frags)
+ );
+
+ // _hole_r = 0.0005 for avoiding warnings when using 2015.03
+ // I downloaded 2017.01.20 and found that the problem is solved.
+ _hole_r = version_num() >= 20170120 ? 0 : 0.0005;
+ ring_extrude(
+ shape_pts,
+ angle = theta,
+ radius = _hole_r,
+ $fn = frags
+ );
+
+ // hook for testing
+ test_crystal_ball_pie(shape_pts);
+}
+
+// override it to test
+module test_crystal_ball_pie(shape_pts) {
+
+}
+
+/**
+* rounded_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_extrude.html
+*
+**/
+
+
+module rounded_extrude(size, round_r, angle = 90, twist = 0, convexity = 10) {
+
+ is_flt = __is_float(size);
+ x = is_flt ? size : size[0];
+ y = is_flt ? size : size[1];
+
+ q_corner_frags = __frags(round_r) / 4;
+
+ step_a = angle / q_corner_frags;
+ twist_step = twist / q_corner_frags;
+
+ module layers(pre_x, pre_y, pre_h = 0, i = 1) {
+ module one_layer(current_a) {
+ wx = pre_x;
+ wy = pre_y;
+
+ h = (round_r - pre_h) - round_r * cos(current_a);
+
+ d_leng =
+ round_r * (sin(current_a) - sin(step_a * (i - 1)));
+
+ sx = (d_leng * 2 + wx) / wx;
+ sy = (d_leng * 2 + wy) / wy;
+
+ translate([0, 0, pre_h])
+ rotate(-twist_step * (i - 1))
+ linear_extrude(
+ h,
+ slices = 1,
+ scale = [sx, sy],
+ convexity = convexity,
+ twist = twist_step
+ ) scale([wx / x, wy / y])
+ children();
+
+ test_rounded_extrude_data(i, wx, wy, pre_h, sx, sy);
+
+ layers(wx * sx, wy * sy, h + pre_h, i + 1)
+ children();
+
+ }
+
+ if(i <= q_corner_frags) {
+ one_layer(i * step_a)
+ children();
+ } else if(i - q_corner_frags < 1) {
+ one_layer(q_corner_frags * step_a)
+ children();
+ }
+ }
+
+ layers(x, y)
+ children();
+}
+
+module test_rounded_extrude_data(i, wx, wy, pre_h, sx, sy) {
+
+}
+
+/**
+* along_with.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-along_with.html
+*
+**/
+
+
+// Becuase of improving the performance, this module requires m_rotation.scad which doesn't require in dotSCAD 1.0.
+// For backward compatibility, I directly include m_rotation here.
+
+module along_with(points, angles, twist = 0, scale = 1.0, method = "AXIS_ANGLE") {
+ leng_points = len(points);
+ leng_points_minus_one = leng_points - 1;
+ twist_step_a = twist / leng_points;
+
+ angles_defined = angles != undef;
+
+ scale_step_vt = __is_float(scale) ?
+ scale_step() :
+ [
+ (scale[0] - 1) / leng_points_minus_one,
+ (scale[1] - 1) / leng_points_minus_one,
+ scale[2] == undef ? 0 : (scale[2] - 1) / leng_points_minus_one
+ ];
+
+
+ function scale_step() =
+ let(s = (scale - 1) / leng_points_minus_one)
+ [s, s, s];
+
+
+ /*
+ Sadly, children(n) cannot be used with inner modules
+ so I have to do things in the first level. Ugly!!
+ */
+
+ // >>> begin: modules and functions for "AXIS-ANGLE"
+
+ // get rotation matrice for sections
+ identity_matrix = [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]
+ ];
+
+ function axis_angle_local_ang_vects(j) =
+ j == 0 ? [] : axis_angle_local_ang_vects_sub(j);
+
+ function axis_angle_local_ang_vects_sub(j) =
+ let(
+ vt0 = points[j] - points[j - 1],
+ vt1 = points[j + 1] - points[j],
+ a = acos((vt0 * vt1) / (norm(vt0) * norm(vt1))),
+ v = cross(vt0, vt1)
+ )
+ concat([[a, v]], axis_angle_local_ang_vects(j - 1));
+
+ function axis_angle_cumulated_rot_matrice(i, rot_matrice) =
+ let(
+ leng_rot_matrice = len(rot_matrice),
+ leng_rot_matrice_minus_one = leng_rot_matrice - 1,
+ leng_rot_matrice_minus_two = leng_rot_matrice - 2
+ )
+ leng_rot_matrice == 0 ? [identity_matrix] : (
+ leng_rot_matrice == 1 ? [rot_matrice[0], identity_matrix] : (
+ i == leng_rot_matrice_minus_two ?
+ [
+ rot_matrice[leng_rot_matrice_minus_one],
+ rot_matrice[leng_rot_matrice_minus_two] * rot_matrice[leng_rot_matrice_minus_one]
+ ]
+ : axis_angle_cumulated_rot_matrice_sub(i, rot_matrice)
+ )
+ );
+
+ function axis_angle_cumulated_rot_matrice_sub(i, rot_matrice) =
+ let(
+ matrice = axis_angle_cumulated_rot_matrice(i + 1, rot_matrice),
+ curr_matrix = rot_matrice[i],
+ prev_matrix = matrice[len(matrice) - 1]
+ )
+ concat(matrice, [curr_matrix * prev_matrix]);
+
+ // align modules
+
+ module axis_angle_align_with_pts_angles(i) {
+ translate(points[i])
+ rotate(angles[i])
+ rotate(twist_step_a * i)
+ scale([1, 1, 1] + scale_step_vt * i)
+ children(0);
+ }
+
+ module axis_angle_align_with_pts_init(a, s) {
+ angleyz = __angy_angz(__to3d(points[0]), __to3d(points[1]));
+
+ rotate([0, -angleyz[0], angleyz[1]])
+ rotate([90, 0, -90])
+ rotate(a)
+ scale(s)
+ children(0);
+ }
+
+ module axis_angle_align_with_pts_local_rotate(j, init_a, init_s, cumu_rot_matrice) {
+ if(j == 0) { // first child
+ axis_angle_align_with_pts_init(init_a, init_s)
+ children(0);
+ }
+ else {
+ multmatrix(cumu_rot_matrice[j - 1])
+ axis_angle_align_with_pts_init(init_a, init_s)
+ children(0);
+ }
+ }
+
+ // <<< end: modules and functions for "AXIS-ANGLE"
+
+
+ // >>> begin: modules and functions for "EULER-ANGLE"
+
+ function _euler_angle_path_angles(pts, end_i, i = 0) =
+ i == end_i ?
+ [] :
+ concat(
+ [__angy_angz(pts[i], pts[i + 1])],
+ _euler_angle_path_angles(pts, end_i, i + 1)
+ );
+
+ function euler_angle_path_angles(children) =
+ let(
+ pts = len(points[0]) == 3 ? points : [for(pt = points) __to3d(pt)],
+ end_i = children == 1 ? leng_points_minus_one : children - 1,
+ angs = _euler_angle_path_angles(pts, end_i)
+ )
+ concat(
+ [[0, -angs[0][0], angs[0][1]]],
+ [for(a = angs) [0, -a[0], a[1]]]
+ );
+
+ module euler_angle_align(i, angs) {
+ translate(points[i])
+ rotate(angs[i])
+ rotate(angles_defined ? [0, 0, 0] : [90, 0, -90])
+ rotate(twist_step_a * i)
+ scale([1, 1, 1] + scale_step_vt * i)
+ children(0);
+ }
+
+ // <<< end: modules and functions for "EULER-ANGLE"
+
+ if(method == "AXIS_ANGLE") {
+ if(angles_defined) {
+ if($children == 1) {
+ for(i = [0:leng_points_minus_one]) {
+ axis_angle_align_with_pts_angles(i) children(0);
+ }
+ } else {
+ for(i = [0:min(leng_points, $children) - 1]) {
+ axis_angle_align_with_pts_angles(i) children(i);
+ }
+ }
+ }
+ else {
+ cumu_rot_matrice = axis_angle_cumulated_rot_matrice(0, [
+ for(ang_vect = axis_angle_local_ang_vects(leng_points - 2))
+ m_rotation(ang_vect[0], ang_vect[1])
+ ]);
+
+ translate(points[0])
+ axis_angle_align_with_pts_local_rotate(0, 0, [1, 1, 1], cumu_rot_matrice)
+ children(0);
+
+ if($children == 1) {
+ for(i = [0:leng_points - 2]) {
+ translate(points[i + 1])
+ axis_angle_align_with_pts_local_rotate(i, i * twist_step_a, [1, 1, 1] + scale_step_vt * i, cumu_rot_matrice)
+ children(0);
+ }
+ } else {
+ for(i = [0:min(leng_points, $children) - 2]) {
+ translate(points[i + 1])
+ axis_angle_align_with_pts_local_rotate(i, i * twist_step_a, [1, 1, 1] + scale_step_vt * i, cumu_rot_matrice)
+ children(i + 1);
+ }
+ }
+ }
+ }
+ else if(method == "EULER_ANGLE") {
+ angs = angles_defined ? angles : euler_angle_path_angles($children);
+
+ if($children == 1) {
+ for(i = [0:leng_points_minus_one]) {
+ euler_angle_align(i, angs) children(0);
+ }
+
+ } else {
+ for(i = [0:min(leng_points, $children) - 1]) {
+ euler_angle_align(i, angs) children(i);
+ }
+ }
+ }
+}
+
+/**
+* stereographic_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-stereographic_extrude.html
+*
+**/
+
+module stereographic_extrude(shadow_side_leng) {
+ half_side_length = shadow_side_leng / 2;
+ outer_sphere_r = half_side_length / 3;
+ a = atan(sqrt(2) * half_side_length / (2 * outer_sphere_r));
+ inner_sphere_r = outer_sphere_r * sin(a);
+
+ intersection() {
+ translate([0, 0, outer_sphere_r]) difference() {
+ sphere(outer_sphere_r);
+ sphere(outer_sphere_r / 2 + inner_sphere_r / 2);
+
+ translate([0, 0, outer_sphere_r / 2])
+ linear_extrude(outer_sphere_r)
+ circle(inner_sphere_r * cos(a));
+ }
+
+ linear_extrude(outer_sphere_r * 2, scale = 0.01)
+ children();
+ }
+
+ // hook for testing
+ test_stereographic_extrude_rs(outer_sphere_r, inner_sphere_r);
+}
+
+// override for testing
+module test_stereographic_extrude_rs(outer_sphere_r, inner_sphere_r) {
+
+}
+
+/**
+* sub_str.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-sub_str.html
+*
+**/
+
+function _sub_str(t, begin, end) =
+ begin == end ? "" : str(t[begin], sub_str(t, begin + 1, end));
+
+function sub_str(t, begin, end) =
+ end == undef ? _sub_str(t, begin, len(t)) : _sub_str(t, begin, end);
+
+
+/**
+* hollow_out.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-hollow_out.html
+*
+**/
+
+module hollow_out(shell_thickness) {
+ difference() {
+ children();
+ offset(delta = -shell_thickness) children();
+ }
+}
+
+/**
+* voronoi2d.scad
+*
+* @copyright Justin Lin, 2019
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-voronoi2d.html
+*
+**/
+
+module voronoi2d(points, spacing = 1, r = 0, delta = 0, chamfer = false, region_type = "square") {
+ xs = [for(p = points) p[0]];
+ ys = [for(p = points) abs(p[1])];
+
+ region_size = max([(max(xs) - min(xs) / 2), (max(ys) - min(ys)) / 2]);
+ half_region_size = 0.5 * region_size;
+ offset_leng = spacing * 0.5 + half_region_size;
+
+ function normalize(v) = v / norm(v);
+
+ module region(pt) {
+ intersection_for(p = points) {
+ if(pt != p) {
+ v = p - pt;
+ translate((pt + p) / 2 - normalize(v) * offset_leng)
+ rotate(atan2(v[1], v[0]))
+ if(region_type == "square") {
+ square(region_size, center = true);
+ }
+ else if(region_type == "circle") {
+ circle(region_size / 2);
+ }
+
+ }
+ }
+ }
+
+ for(p = points) {
+ if(r != 0) {
+ offset(r) region(p);
+ }
+ else {
+ offset(delta = delta, chamfer = chamfer) region(p);
+ }
+ }
+}
+
+/**
+* sphere_spiral_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-sphere_spiral_extrude.html
+*
+**/
+
+module sphere_spiral_extrude(shape_pts, radius, za_step,
+ z_circles = 1, begin_angle = 0, end_angle = 0, vt_dir = "SPI_DOWN", rt_dir = "CT_CLK",
+ twist = 0, scale = 1.0, triangles = "SOLID") {
+
+ points_angles = sphere_spiral(
+ radius = radius,
+ za_step = za_step,
+ z_circles = z_circles,
+ begin_angle = begin_angle,
+ end_angle = end_angle,
+ vt_dir = vt_dir,
+ rt_dir = rt_dir
+ );
+
+ v_clk = vt_dir == "SPI_DOWN" ? 90 : -90;
+ r_clk = rt_dir == "CT_CLK" ? 0 : 180;
+
+ points = [for(pa = points_angles) pa[0]];
+ angles = [for(pa = points_angles) [pa[1][0] + v_clk, pa[1][1], pa[1][2] + r_clk]];
+
+ sections = cross_sections(
+ shape_pts, points, angles, twist = twist, scale = scale
+ );
+
+ polysections(
+ sections,
+ triangles = triangles
+ );
+
+ // testing hook
+ test_sphere_spiral_extrude(sections);
+}
+
+// override it to test
+module test_sphere_spiral_extrude(sections) {
+
+}
+
+/**
+* rounded_cylinder.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_cylinder.html
+*
+**/
+
+
+module rounded_cylinder(radius, h, round_r, convexity = 2, center = false) {
+ r_corners = __half_trapezium(radius, h, round_r);
+
+ shape_pts = concat(
+ [[0, -h/2]],
+ r_corners,
+ [[0, h/2]]
+ );
+
+ center_pt = center ? [0, 0, 0] : [0, 0, h/2];
+
+ translate(center ? [0, 0, 0] : [0, 0, h/2])
+ rotate(180)
+ rotate_extrude(convexity = convexity)
+ polygon(shape_pts);
+
+ // hook for testing
+ test_center_half_trapezium(center_pt, shape_pts);
+}
+
+// override it to test
+module test_center_half_trapezium(center_pt, shape_pts) {
+
+}
+
+
+function __trapezium(length, h, round_r) =
+ let(
+ r_half_trapezium = __half_trapezium(length / 2, h, round_r),
+ to = len(r_half_trapezium) - 1,
+ l_half_trapezium = [
+ for(i = [0:to])
+ let(pt = r_half_trapezium[to - i])
+ [-pt[0], pt[1]]
+ ]
+ )
+ concat(
+ r_half_trapezium,
+ l_half_trapezium
+ );
+
/**
* path_scaling_sections.scad
*
@@ -3916,95 +3353,612 @@ function path_scaling_sections(shape_pts, edge_path) =
function __to_degree(phi) = 180 / 3.14159 * phi;
/**
-* shape_square.scad
+* bezier_smooth.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_square.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-bezier_curve.html
*
-**/
+**/
-
-function shape_square(size, corner_r = 0) =
+
+function _corner_ctrl_pts(round_d, p1, p2, p3) =
let(
- is_flt = __is_float(size),
- x = is_flt ? size : size[0],
- y = is_flt ? size : size[1]
+ _ya_za_1 = __angy_angz(p1, p2),
+ _ya_za_2 = __angy_angz(p3, p2),
+
+ dz1 = sin(_ya_za_1[0]) * round_d,
+ dxy1 = cos(_ya_za_1[0]) * round_d,
+ dy1 = sin(_ya_za_1[1]) * dxy1,
+ dx1 = cos(_ya_za_1[1]) * dxy1,
+
+ dz2 = sin(_ya_za_2[0]) * round_d,
+ dxy2 = cos(_ya_za_2[0]) * round_d,
+ dy2 = sin(_ya_za_2[1]) * dxy2,
+ dx2 = cos(_ya_za_2[1]) * dxy2
)
- __trapezium(
- length = x,
- h = y,
- round_r = corner_r
- );
+ [
+ p2 - [dx1, dy1, dz1],
+ p2,
+ p2 - [dx2, dy2, dz2]
+ ];
+
+function _bezier_corner(round_d, t_step, p1, p2, p3) =
+ bezier_curve(t_step, _corner_ctrl_pts(round_d, p1, p2, p3));
+
+function _recursive_bezier_smooth(pts, round_d, t_step, leng, i = 0) =
+ i <= leng - 3 ?
+ concat(
+ _bezier_corner(round_d, t_step, pts[i], pts[i + 1], pts[i + 2]),
+ _recursive_bezier_smooth(pts, round_d, t_step, leng, i + 1)
+ )
+ : [];
+
+function bezier_smooth(path_pts, round_d, t_step = 0.1, closed = false) =
+ let(
+ pts = len(path_pts[0]) == 3 ? path_pts : [for(p = path_pts) __to3d(p)],
+ leng = len(pts),
+ middle_pts = _recursive_bezier_smooth(pts, round_d, t_step, leng),
+ pth_pts = closed ?
+ concat(
+ _recursive_bezier_smooth(
+ [pts[leng - 1], pts[0], pts[1]],
+ round_d, t_step, 3
+ ),
+ middle_pts,
+ _recursive_bezier_smooth(
+ [pts[leng - 2], pts[leng - 1], pts[0]],
+ round_d, t_step, 3
+ )
+ ) :
+ concat(
+ [pts[0]],
+ middle_pts,
+ [pts[leng - 1]]
+ )
+ )
+ len(path_pts[0]) == 2 ? [for(p = pth_pts) __to2d(p)] : pth_pts;
+
+function __in_line(line_pts, pt, epsilon = 0.0001) =
+ let(
+ pts = len(line_pts[0]) == 2 ? [for(p = line_pts) __to3d(p)] : line_pts,
+ pt3d = len(pt) == 2 ? __to3d(pt) : pt,
+ v1 = pts[0] - pt3d,
+ v2 = pts[1] - pt3d
+ )
+ (norm(cross(v1, v2)) < epsilon) && ((v1 * v2) <= epsilon);
+
+function __nearest_multiple_of_4(n) =
+ let(
+ remain = n % 4
+ )
+ (remain / 4) > 0.5 ? n - remain + 4 : n - remain;
+
/**
-* rounded_cube.scad
+* hull_polyline3d.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_cube.html
+* @see https://openhome.cc/eGossip/OpenSCAD/hull_polyline3d.html
*
**/
-
-module rounded_cube(size, corner_r, center = false) {
- is_flt = __is_float(size);
- x = is_flt ? size : size[0];
- y = is_flt ? size : size[1];
- z = is_flt ? size : size[2];
-
- corner_frags = __nearest_multiple_of_4(__frags(corner_r));
- edge_d = corner_r * cos(180 / corner_frags);
-
- half_x = x / 2;
- half_y = y / 2;
- half_z = z / 2;
+module hull_polyline3d(points, thickness) {
+ half_thickness = thickness / 2;
+ leng = len(points);
- half_l = half_x - edge_d;
- half_w = half_y - edge_d;
- half_h = half_z - edge_d;
-
- half_cube_leng = size / 2;
- half_leng = half_cube_leng - edge_d;
-
- corners = [
- for(z = [1, -1])
- for(y = [1, -1])
- for(x = [1, -1])
- [half_l * x, half_w * y, half_h * z]
- ];
+ module hull_line3d(index) {
+ point1 = points[index - 1];
+ point2 = points[index];
- module corner(i) {
- translate(corners[i])
- sphere(corner_r, $fn = corner_frags);
+ hull() {
+ translate(point1)
+ sphere(half_thickness);
+ translate(point2)
+ sphere(half_thickness);
+ }
+
+ // hook for testing
+ test_line_segment(index, point1, point2, half_thickness);
}
- center_pts = center ? [0, 0, 0] : [half_x, half_y, half_z];
-
- // Don't use `hull() for(...) {...}` because it's slow.
- translate(center_pts) hull() {
- corner(0);
- corner(1);
- corner(2);
- corner(3);
- corner(4);
- corner(5);
- corner(6);
- corner(7);
+ module polyline3d_inner(index) {
+ if(index < leng) {
+ hull_line3d(index);
+ polyline3d_inner(index + 1);
+ }
}
- // hook for testing
- test_rounded_edge_corner_center(corner_frags, corners, center_pts);
+ polyline3d_inner(1);
}
// override it to test
-module test_rounded_edge_corner_center(corner_frags, corners, center_pts) {
+module test_line_segment(index, point1, point2, radius) {
}
+/**
+* ellipse_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-ellipse_extrude.html
+*
+**/
+
+module ellipse_extrude(semi_minor_axis, height, center = false, convexity = 10, twist = 0, slices = 20) {
+ h = height == undef ? semi_minor_axis : (
+ // `semi_minor_axis` is always equal to or greater than `height`.
+ height > semi_minor_axis ? semi_minor_axis : height
+ );
+ angle = asin(h / semi_minor_axis) / slices;
+
+ function f_extrude(i = 1) =
+ i <= slices ?
+ concat(
+ [
+ [
+ cos(angle * i) / cos(angle * (i - 1)),
+ semi_minor_axis * sin(angle * i)
+ ]
+ ],
+ f_extrude(i + 1)
+ ) : [];
+
+ fzs = f_extrude();
+ len_fzs = len(fzs);
+
+ function accm_fs(pre_f = 1, i = 0) =
+ i < len_fzs ?
+ concat(
+ [pre_f * fzs[i][0]],
+ accm_fs(pre_f * fzs[i][0], i + 1)
+ ) : [];
+
+ child_fs = concat([1], accm_fs());
+ pre_zs = concat(
+ [0],
+ [
+ for(i = [0:len_fzs - 1])
+ fzs[i][1]
+ ]
+ );
+
+ module extrude() {
+ for(i = [0:len_fzs - 1]) {
+ f = fzs[i][0];
+ z = fzs[i][1];
+
+ translate([0, 0, pre_zs[i]])
+ rotate(-twist / slices * i)
+ linear_extrude(
+ z - pre_zs[i],
+ convexity = convexity,
+ twist = twist / slices,
+ slices = 1,
+ scale = f
+ ) scale(child_fs[i]) children();
+ }
+ }
+
+ center_offset = [0, 0, center == true ? -h / 2 : 0];
+ translate(center_offset)
+ extrude()
+ children();
+
+ // hook for testing
+ test_ellipse_extrude_fzc(child_fs, pre_zs, center_offset);
+}
+
+// override for testing
+module test_ellipse_extrude_fzc(child_fs, pre_zs, center_offset) {
+
+}
+
+/**
+* log.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-log.html
+*
+**/
+
+/*
+ The accepted values are "OFF" (-1), "INFO" (20),
+ "WARNING" (30), "ERROR" (40) or positive integers.
+*/
+$log_level = "INFO";
+
+module log(level = "INFO", level_color) {
+ default_level_ints = [
+ ["OFF", -1],
+ ["INFO", 20],
+ ["WARNING", 30],
+ ["ERROR", 40]
+ ];
+
+ default_level_colors = [
+ ["INFO", "green"],
+ ["WARNING", "orange"],
+ ["ERROR", "red"]
+ ];
+
+ /*
+ The built-in lookup function require integer keys.
+ I overwrite it so that using string keys is ok.
+ */
+ function lookup(key, mappings, i = 0) =
+ i == len(mappings) ? key : (
+ key == mappings[i][0] ?
+ mappings[i][1] :
+ lookup(key, mappings, i + 1)
+ );
+
+ if($log_level != "OFF") {
+ argu_level = lookup(level, default_level_ints);
+ golbal_level = lookup(
+ $log_level == undef ? "INFO" : $log_level,
+ default_level_ints
+ );
+
+ if(argu_level >= golbal_level) {
+ c = level_color == undef ? lookup(level, default_level_colors) : level_color;
+ lv = str(len(level) == undef ? "LEVEL " : "", level);
+
+ // level text
+ echo(
+ str(
+ "",
+ "",
+ lv,
+ "",
+ ""
+ )
+ );
+
+ // echo
+ for(i = [0:$children - 1]) {
+ children(i);
+ }
+ }
+ }
+}
+
+function __m_shearing(sx, sy, sz) =
+ let(
+ sx_along_y = sx[0],
+ sx_along_z = sx[1],
+ sy_along_x = sy[0],
+ sy_along_z = sy[1],
+ sz_along_x = sz[0],
+ sz_along_y = sz[1]
+ )
+ [
+ [1, sx_along_y, sx_along_z, 0],
+ [sy_along_x, 1, sy_along_z, 0],
+ [sz_along_x, sz_along_y, 1, 0],
+ [0, 0, 0, 1]
+ ];
+
+function __edge_r_begin(orig_r, a, a_step, m) =
+ let(leng = orig_r * cos(a_step / 2))
+ leng / cos((m - 0.5) * a_step - a);
+
+function __edge_r_end(orig_r, a, a_step, n) =
+ let(leng = orig_r * cos(a_step / 2))
+ leng / cos((n + 0.5) * a_step - a);
+
+/**
+* ring_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-ring_extrude.html
+*
+**/
+
+
+module ring_extrude(shape_pts, radius, angle = 360, twist = 0, scale = 1.0, triangles = "SOLID") {
+ if(angle == 360 && twist == 0 && scale == 1.0) {
+ rotate_extrude()
+ translate([radius, 0, 0])
+ polygon(shape_pts);
+ } else {
+ a_step = 360 / __frags(radius);
+
+ angles = __is_float(angle) ? [0, angle] : angle;
+
+ m = floor(angles[0] / a_step) + 1;
+ n = floor(angles[1] / a_step);
+
+ leng = radius * cos(a_step / 2);
+
+ begin_r =
+ leng / cos((m - 0.5) * a_step - angles[0]);
+
+ end_r =
+ leng / cos((n + 0.5) * a_step - angles[1]);
+
+ angs = concat(
+ [[90, 0, angles[0]]],
+ m > n ? [] : [for(i = [m:n]) [90, 0, a_step * i]]
+ );
+ pts = concat(
+ [__ra_to_xy(begin_r, angles[0])],
+ m > n ? [] : [for(i = [m:n]) __ra_to_xy(radius, a_step * i)]
+ );
+
+ is_angle_frag_end = angs[len(angs) - 1][2] == angles[1];
+
+ all_angles = is_angle_frag_end ?
+ angs :
+ concat(angs, [[90, 0, angles[1]]]);
+
+ all_points = is_angle_frag_end ?
+ pts :
+ concat(pts, [__ra_to_xy(end_r, angles[1])]);
+
+ sections = cross_sections(shape_pts, all_points, all_angles, twist, scale);
+
+ polysections(
+ sections,
+ triangles = triangles
+ );
+
+ // hook for testing
+ test_ring_extrude(sections);
+ }
+}
+
+// Override it to test
+module test_ring_extrude(sections) {
+
+}
+
+/**
+* shape_ellipse.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_ellipse.html
+*
+**/
+
+
+function shape_ellipse(axes) =
+ let(
+ frags = __frags(axes[0]),
+ step_a = 360 / frags,
+ shape_pts = [
+ for(a = [0:step_a:360 - step_a])
+ [axes[0] * cos(a), axes[1] * sin(a)]
+ ]
+ ) shape_pts;
+
+function __shape_pie(radius, angle) =
+ let(
+ frags = __frags(radius),
+ a_step = 360 / frags,
+ leng = radius * cos(a_step / 2),
+ angles = __is_float(angle) ? [0:angle] : angle,
+ m = floor(angles[0] / a_step) + 1,
+ n = floor(angles[1] / a_step),
+ edge_r_begin = leng / cos((m - 0.5) * a_step - angles[0]),
+ edge_r_end = leng / cos((n + 0.5) * a_step - angles[1]),
+ shape_pts = concat(
+ [[0, 0], __ra_to_xy(edge_r_begin, angles[0])],
+ m > n ? [] : [
+ for(i = [m:n])
+ let(a = a_step * i)
+ __ra_to_xy(radius, a)
+ ],
+ angles[1] == a_step * n ? [] : [__ra_to_xy(edge_r_end, angles[1])]
+ )
+ ) shape_pts;
+
+function __to2d(p) = [p[0], p[1]];
+
+/**
+* rotate_p.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-rotate_p.html
+*
+**/
+
+
+function _q_rotate_p_3d(p, a, v) =
+ let(
+ half_a = a / 2,
+ axis = v / norm(v),
+ s = sin(half_a),
+ x = s * axis[0],
+ y = s * axis[1],
+ z = s * axis[2],
+ w = cos(half_a),
+
+ x2 = x + x,
+ y2 = y + y,
+ z2 = z + z,
+
+ xx = x * x2,
+ yx = y * x2,
+ yy = y * y2,
+ zx = z * x2,
+ zy = z * y2,
+ zz = z * z2,
+ wx = w * x2,
+ wy = w * y2,
+ wz = w * z2
+ )
+ [
+ [1 - yy - zz, yx - wz, zx + wy] * p,
+ [yx + wz, 1 - xx - zz, zy - wx] * p,
+ [zx - wy, zy + wx, 1 - xx - yy] * p
+ ];
+
+function _rotx(pt, a) =
+ let(cosa = cos(a), sina = sin(a))
+ [
+ pt[0],
+ pt[1] * cosa - pt[2] * sina,
+ pt[1] * sina + pt[2] * cosa
+ ];
+
+function _roty(pt, a) =
+ let(cosa = cos(a), sina = sin(a))
+ [
+ pt[0] * cosa + pt[2] * sina,
+ pt[1],
+ -pt[0] * sina + pt[2] * cosa,
+ ];
+
+function _rotz(pt, a) =
+ let(cosa = cos(a), sina = sin(a))
+ [
+ pt[0] * cosa - pt[1] * sina,
+ pt[0] * sina + pt[1] * cosa,
+ pt[2]
+ ];
+
+function _rotate_p_3d(point, a) =
+ _rotz(_roty(_rotx(point, a[0]), a[1]), a[2]);
+
+function _rotate_p(p, a) =
+ let(angle = __to_ang_vect(a))
+ len(p) == 3 ?
+ _rotate_p_3d(p, angle) :
+ __to2d(
+ _rotate_p_3d(__to3d(p), angle)
+ );
+
+
+function _q_rotate_p(p, a, v) =
+ len(p) == 3 ?
+ _q_rotate_p_3d(p, a, v) :
+ __to2d(
+ _q_rotate_p_3d(__to3d(p), a, v)
+ );
+
+function rotate_p(point, a, v) =
+ v == undef ? _rotate_p(point, a) : _q_rotate_p(point, a, v);
+
+
+/**
+* shape_path_extend.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-path_extend.html
+*
+**/
+
+
+function _shape_path_extend_az(p1, p2) =
+ let(
+ x1 = p1[0],
+ y1 = p1[1],
+ x2 = p2[0],
+ y2 = p2[1]
+ ) -90 + atan2((y2 - y1), (x2 - x1));
+
+function _shape_path_first_stroke(stroke_pts, path_pts) =
+ let(
+ p1 = path_pts[0],
+ p2 = path_pts[1],
+ a = _shape_path_extend_az(p1, p2)
+ )
+ [
+ for(p = stroke_pts)
+ rotate_p(p, a) + p1
+ ];
+
+function _shape_path_extend_stroke(stroke_pts, p1, p2, scale_step, i) =
+ let(
+ leng = norm(__to3d(p2) - __to3d(p1)),
+ a = _shape_path_extend_az(p1, p2)
+ )
+ [
+ for(p = stroke_pts)
+ rotate_p(p * (1 + scale_step * i) + [0, leng], a) + p1
+ ];
+
+function _shape_path_extend_inner(stroke_pts, path_pts, leng_path_pts, scale_step, index) =
+ index == leng_path_pts ? [] :
+ concat(
+ [
+ _shape_path_extend_stroke(
+ stroke_pts,
+ path_pts[index - 1],
+ path_pts[index],
+ scale_step,
+ index
+ )
+ ],
+ _shape_path_extend_inner(
+ stroke_pts,
+ path_pts,
+ leng_path_pts,
+ scale_step,
+ index + 1
+ )
+ );
+
+function shape_path_extend(stroke_pts, path_pts, scale = 1.0, closed = false) =
+ let(
+ leng_path_pts = len(path_pts),
+ scale_step = (scale - 1) / (leng_path_pts - 1),
+ strokes = _shape_path_extend_inner(stroke_pts, path_pts, leng_path_pts, scale_step, 1)
+ )
+ closed && path_pts[0] == path_pts[leng_path_pts - 1] ?
+ __polytransversals(concat(strokes, [strokes[0]])) :
+ __polytransversals(
+ concat([_shape_path_first_stroke(stroke_pts, path_pts)], strokes)
+ );
+
+
+
+/**
+* box_extrude.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-box_extrude.html
+*
+**/
+
+module box_extrude(height, shell_thickness, offset_mode = "delta", chamfer = false) {
+ linear_extrude(shell_thickness)
+ children();
+
+ linear_extrude(height)
+ difference() {
+ children();
+ if(offset_mode == "delta") {
+ offset(delta = -shell_thickness, chamfer = chamfer)
+ children();
+ } else {
+ offset(r = -shell_thickness)
+ children();
+ }
+ }
+}
+
+
+
/**
* triangulate.scad
*
@@ -4110,18 +4064,137 @@ function triangulate(shape_pts, epsilon = 0.0001) =
/**
-* shape_taiwan.scad
+* line2d.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_taiwan.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-line2d.html
*
**/
-function shape_taiwan(h, distance = 0) =
- let(tw = [[3.85724, 6.24078], [3.7902, 6.25779], [3.73596, 6.26288], [3.62807, 6.24587], [3.59581, 6.2602], [3.55429, 6.32386], [3.51698, 6.4571], [3.50768, 6.5361], [3.51188, 6.58284], [3.53917, 6.63708], [3.48981, 6.66934], [2.78286, 6.83905], [2.7218, 6.86112], [2.68441, 6.90272], [2.61063, 6.96209], [2.64288, 7.05549], [2.61062, 7.06568], [2.56396, 7.06058], [2.50281, 7.04109], [2.44849, 7.15647], [2.29319, 7.37123], [2.22438, 7.42555], [2.05644, 7.44248], [1.88674, 7.40509], [1.73901, 7.32879], [1.63795, 7.238], [1.58868, 7.14974], [1.54463, 7.0555], [1.5149, 7.0411], [1.48526, 7.01895], [1.53658, 6.9725], [1.58825, 6.92909], [1.63096, 6.86281], [1.61348, 6.80387], [1.57757, 6.83721], [1.53949, 6.89116], [1.48525, 6.87393], [1.43851, 6.87393], [1.40381, 6.86623], [1.37408, 6.85174], [1.28068, 6.77282], [1.20682, 6.7457], [0.723984, 6.65659], [0.586451, 6.60741], [0.5152, 6.57094], [0.458435, 6.52167], [0.393838, 6.47922], [0.108666, 6.38843], [-0.002506, 6.2925], [-0.110307, 6.16693], [-0.278327, 5.89785], [-0.312266, 5.80452], [-0.319942, 5.74076], [-0.35978, 5.72804], [-0.398773, 5.69411], [-0.431113, 5.64921], [-0.443073, 5.60508], [-0.45317, 5.54563], [-0.477762, 5.51429], [-0.504966, 5.48196], [-0.527025, 5.4379], [-0.511683, 5.32413], [-0.544022, 5.28008], [-0.630518, 5.10523], [-0.652506, 5.02118], [-0.721317, 4.96517], [-0.751047, 4.87851], [-0.807895, 4.81922], [-0.849502, 4.76313], [-0.835106, 4.67916], [-0.94535, 4.70123], [-1.03193, 4.67664], [-1.10318, 4.62476], [-1.16685, 4.55856], [-1.18724, 4.52125], [-1.20679, 4.47981], [-1.23643, 4.44755], [-1.33927, 4.41782], [-1.36462, 4.37621], [-1.38844, 4.28037], [-1.47747, 4.11993], [-1.51477, 4.03419], [-1.52674, 3.93321], [-1.55638, 3.87122], [-1.87389, 3.52162], [-1.92308, 3.43757], [-1.96721, 3.28479], [-2.11055, 3.03862], [-2.15224, 2.84946], [-2.18703, 2.75092], [-2.23874, 2.70864], [-2.2481, 2.65676], [-2.43313, 2.44284], [-2.47482, 2.40393], [-2.48998, 2.36148], [-2.49681, 2.20617], [-2.51727, 2.15951], [-2.59526, 2.09828], [-2.61312, 2.05583], [-2.62237, 2.00926], [-2.64705, 1.98728], [-2.68183, 1.97279], [-2.71915, 1.94559], [-2.76589, 1.90137], [-2.80741, 1.82768], [-2.89576, 1.58074], [-2.9601, 1.46267], [-3.00171, 1.34223], [-3.03396, 1.29978], [-3.14261, 1.19451], [-3.16223, 1.14945], [-3.18683, 1.04173], [-3.21142, 0.994987], [-3.30988, 0.906048], [-3.32504, 0.864443], [-3.42105, 0.738702], [-3.43797, 0.692212], [-3.47452, 0.558807], [-3.54409, 0.411083], [-3.62553, 0.283068], [-3.69434, 0.142925], [-3.72407, -0.047077], [-3.71634, -0.135425], [-3.67474, -0.317847], [-3.65948, -0.475761], [-3.62302, -0.539348], [-3.61029, -0.588533], [-3.62554, -0.628538], [-3.65703, -0.687068], [-3.61029, -0.73878], [-3.64002, -0.795629], [-3.57542, -0.918676], [-3.67902, -1.14784], [-3.62553, -1.18944], [-3.59589, -1.2046], [-3.60515, -1.24376], [-3.6476, -1.28781], [-3.68162, -1.37186], [-3.72138, -1.38205], [-3.74597, -1.40925], [-3.79785, -1.54426], [-3.80458, -1.58157], [-3.81731, -1.61121], [-3.84956, -1.65526], [-3.87677, -1.70958], [-3.8665, -1.75893], [-3.84714, -1.80812], [-3.83434, -1.86665], [-3.85397, -1.90666], [-3.89633, -1.91845], [-3.93532, -1.94052], [-3.94552, -2.0025], [-3.90315, -2.05169], [-3.83687, -2.10846], [-3.91452, -2.12525], [-3.92486, -2.16746], [-3.8021, -2.21718], [-3.78004, -2.24169], [-3.79537, -2.28599], [-3.84194, -2.30292], [-3.9014, -2.31052], [-3.94553, -2.3325], [-3.90902, -2.38984], [-3.81228, -2.42599], [-3.7487, -2.45311], [-3.7359, -2.49042], [-3.61032, -2.51249], [-3.51439, -2.52698], [-3.49737, -2.60598], [-3.53645, -2.69921], [-3.61031, -2.75101], [-3.55085, -2.813], [-3.52197, -2.86732], [-3.47455, -3.17194], [-3.46268, -3.20175], [-3.42108, -3.27805], [-3.36173, -3.36723], [-3.3667, -3.40353], [-3.39137, -3.45802], [-3.39137, -3.50468], [-3.37605, -3.57332], [-3.3048, -3.72879], [-3.22849, -3.84755], [-3.18689, -3.9332], [-3.16473, -4.02239], [-3.18175, -4.08606], [-3.21401, -4.15723], [-3.18942, -4.22848], [-3.1377, -4.29316], [-2.99756, -4.4237], [-2.91612, -4.51458], [-2.85675, -4.62575], [-2.84478, -4.76572], [-2.64282, -4.92119], [-2.55624, -4.90831], [-2.51219, -4.92877], [-2.47733, -4.95075], [-2.40355, -5.01441], [-2.33222, -5.06116], [-2.17691, -5.13485], [-2.10053, -5.19944], [-2.05126, -5.27328], [-2.00199, -5.30293], [-1.87641, -5.40147], [-1.84416, -5.44552], [-1.77804, -5.59577], [-1.69399, -5.70181], [-1.66687, -5.74611], [-1.35963, -6.53694], [-1.36899, -6.58351], [-1.4063, -6.638], [-1.41556, -6.68466], [-1.41556, -6.84915], [-1.42322, -6.93076], [-1.41556, -6.96992], [-1.33, -7.11073], [-1.34945, -7.21845], [-1.34692, -7.25315], [-1.28999, -7.29989], [-1.23921, -7.31522], [-1.20181, -7.27783], [-1.19675, -7.17685], [-1.07362, -7.20658], [-0.953101, -7.2609], [-0.849593, -7.33982], [-0.778259, -7.44248], [-0.775729, -7.35413], [-0.795186, -7.21416], [-0.797786, -7.08606], [-0.736641, -7.02913], [-0.685014, -6.99014], [-0.645009, -6.89851], [-0.620417, -6.78565], [-0.637343, -5.77062], [-0.612751, -5.52192], [-0.590692, -5.43534], [-0.443136, -5.1799], [-0.371802, -4.92362], [-0.341989, -4.73344], [-0.29036, -4.64283], [-0.16723, -4.48735], [-0.128151, -4.41105], [-0.046711, -4.20395], [-0.014539, -4.16235], [0.027066, -4.14297], [0.191803, -3.96039], [0.441265, -3.79565], [0.515127, -3.72608], [0.588904, -3.63285], [0.62032, -3.58105], [0.640788, -3.5317], [0.643228, -3.48757], [0.633124, -3.39173], [0.640794, -3.35189], [0.689979, -3.29336], [0.9039, -3.11338], [0.923353, -3.07093], [1.08363, -2.84522], [1.11605, -2.75342], [1.16262, -2.55407], [1.23396, -2.42344], [1.26116, -2.33518], [1.27809, -2.30031], [1.31035, -2.26637], [1.38421, -2.21213], [1.41638, -2.17987], [1.49024, -2.02701], [1.50464, -1.87935], [1.50212, -1.72405], [1.52671, -1.54937], [1.55897, -1.45589], [1.59897, -1.37942], [1.70669, -1.23616], [1.74147, -1.16044], [1.98243, 0.226167], [2.05637, 0.376416], [2.18195, 0.989965], [2.27788, 1.25316], [2.32959, 1.50186], [2.33469, 1.60107], [2.34741, 1.65447], [2.40173, 1.74366], [2.42118, 1.79798], [2.38391, 1.88363], [2.36176, 1.9185], [2.33473, 1.99994], [2.33213, 2.07136], [2.3542, 2.13747], [2.46798, 2.30473], [2.51203, 2.49221], [2.55625, 2.58553], [2.59356, 2.62284], [2.71147, 2.72138], [2.69968, 2.75364], [2.72175, 2.78497], [2.793, 2.8419], [2.89145, 2.97513], [2.98064, 3.06078], [2.97805, 3.26778], [2.98485, 3.3638], [2.99757, 3.41297], [3.01702, 3.44775], [3.03412, 3.48936], [3.02732, 3.59547], [3.03152, 3.64474], [3.14008, 3.76771], [3.23348, 3.82464], [3.27332, 3.8611], [3.26572, 3.90356], [3.23852, 3.94517], [3.22673, 3.98929], [3.23343, 4.02904], [3.32515, 4.08085], [3.33955, 4.16659], [3.31748, 4.26758], [3.2784, 4.34405], [3.3056, 4.3635], [3.37526, 4.38649], [3.39869, 4.42513], [3.32514, 4.42541], [3.22669, 4.4874], [3.17237, 4.58324], [3.16977, 4.70636], [3.19192, 4.7675], [3.19432, 4.8294], [3.14261, 5.06624], [3.14001, 5.17909], [3.14781, 5.29709], [3.1724, 5.41583], [3.20634, 5.51943], [3.25384, 5.59329], [3.54153, 5.94036], [3.62558, 6.01431], [3.94553, 6.17215], [3.91327, 6.21106]] / 15 * h)
- distance == 0 ? tw : [for(i = [0 : len(tw) - 1]) if(norm(tw[i] - tw[i + 1]) > distance) tw[i]];
+
+module line2d(p1, p2, width, p1Style = "CAP_SQUARE", p2Style = "CAP_SQUARE") {
+ half_width = 0.5 * width;
+
+ atan_angle = atan2(p2[1] - p1[1], p2[0] - p1[0]);
+ leng = sqrt(pow(p2[0] - p1[0], 2) + pow(p2[1] - p1[1], 2));
+
+ frags = __nearest_multiple_of_4(__frags(half_width));
+
+ module square_end(point) {
+ translate(point)
+ rotate(atan_angle)
+ square(width, center = true);
+
+ // hook for testing
+ test_line2d_cap(point, "CAP_SQUARE");
+ }
+
+ module round_end(point) {
+ translate(point)
+ rotate(atan_angle)
+ circle(half_width, $fn = frags);
+
+ // hook for testing
+ test_line2d_cap(point, "CAP_ROUND");
+ }
+
+ if(p1Style == "CAP_SQUARE") {
+ square_end(p1);
+ } else if(p1Style == "CAP_ROUND") {
+ round_end(p1);
+ }
+
+ translate(p1)
+ rotate(atan_angle)
+ translate([0, -width / 2])
+ square([leng, width]);
+
+ if(p2Style == "CAP_SQUARE") {
+ square_end(p2);
+ } else if(p2Style == "CAP_ROUND") {
+ round_end(p2);
+ }
+
+ // hook for testing
+ test_line2d_line(atan_angle, leng, width, frags);
+}
+
+// override them to test
+module test_line2d_cap(point, style) {
+}
+
+module test_line2d_line(angle, length, width, frags) {
+}
+
+
+
+/**
+* hull_polyline2d.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/hull_polyline2d.html
+*
+**/
+
+module hull_polyline2d(points, width) {
+ half_width = width / 2;
+ leng = len(points);
+
+ module hull_line2d(index) {
+ point1 = points[index - 1];
+ point2 = points[index];
+
+ hull() {
+ translate(point1)
+ circle(half_width);
+ translate(point2)
+ circle(half_width);
+ }
+
+ // hook for testing
+ test_line_segment(index, point1, point2, half_width);
+ }
+
+ module polyline2d_inner(index) {
+ if(index < leng) {
+ hull_line2d(index);
+ polyline2d_inner(index + 1);
+ }
+ }
+
+ polyline2d_inner(1);
+}
+
+// override it to test
+module test_line_segment(index, point1, point2, radius) {
+
+}
+
+/**
+* circle_path.scad
+*
+* @copyright Justin Lin, 2017
+* @license https://opensource.org/licenses/lgpl-3.0.html
+*
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-circle_path.html
+*
+**/
+
+
+function circle_path(radius, n) =
+ let(
+ _frags = __frags(radius),
+ step_a = 360 / _frags,
+ end_a = 360 - step_a * ((n == undef || n > _frags) ? 1 : _frags - n + 1)
+ )
+ [
+ for(a = [0 : step_a : end_a])
+ [radius * cos(a), radius * sin(a)]
+ ];
/**
@@ -4205,213 +4278,142 @@ function m_rotation(a, v) =
[0, 0, 0, 1]
] : (v == undef ? _xyz_rotation(a) : _q_rotation(a, v));
+function __tr__corner_t_leng_lt_zero(frags, t_sector_angle, l1, l2, h, round_r) =
+ let(t_height = tan(t_sector_angle) * l1 - round_r / sin(90 - t_sector_angle) - h / 2)
+ [
+ for(pt = __pie_for_rounding(round_r, 90 - t_sector_angle, 90, frags * t_sector_angle / 180))
+ [pt[0], pt[1] + t_height]
+ ];
+
+function __tr_corner_t_leng_gt_or_eq_zero(frags, t_sector_angle, t_leng, h, round_r) =
+ let(offset_y = h / 2 - round_r)
+ [
+ for(pt = __pie_for_rounding(round_r, 90 - t_sector_angle, 90, frags * t_sector_angle / 360))
+ [pt[0] + t_leng, pt[1] + offset_y]
+ ];
+
+function __tr_corner(frags, b_ang, l1, l2, h, round_r) =
+ let(t_leng = l2 - round_r * tan(b_ang / 2))
+ t_leng >= 0 ?
+ __tr_corner_t_leng_gt_or_eq_zero(frags, b_ang, t_leng, h, round_r) :
+ __tr__corner_t_leng_lt_zero(frags, b_ang, l1, l2, h, round_r);
+
+function __tr__corner_b_leng_lt_zero(frags, b_sector_angle, l1, l2, h, round_r) =
+ let(
+ reversed = __tr__corner_t_leng_lt_zero(frags, b_sector_angle, l2, l1, h, round_r),
+ leng = len(reversed)
+ )
+ [
+ for(i = [0:leng - 1])
+ let(pt = reversed[leng - 1 - i])
+ [pt[0], -pt[1]]
+ ];
+
+function __br_corner_b_leng_gt_or_eq_zero(frags, b_sector_angle, l1, l2, b_leng, h, round_r) =
+ let(half_h = h / 2)
+ [
+ for(pt = __pie_for_rounding(round_r, -90, -90 + b_sector_angle, frags * b_sector_angle / 360))
+ [pt[0] + b_leng, pt[1] + round_r - half_h]
+ ];
+
+function __br_corner(frags, b_ang, l1, l2, h, round_r) =
+ let(b_leng = l1 - round_r / tan(b_ang / 2))
+ b_leng >= 0 ?
+ __br_corner_b_leng_gt_or_eq_zero(frags, 180 - b_ang, l1, l2, b_leng, h, round_r) :
+ __tr__corner_b_leng_lt_zero(frags, 180 - b_ang, l1, l2, h, round_r);
+
+function __half_trapezium(length, h, round_r) =
+ let(
+ is_flt = __is_float(length),
+ l1 = is_flt ? length : length[0],
+ l2 = is_flt ? length : length[1],
+ frags = __frags(round_r),
+ b_ang = atan2(h, l1 - l2),
+ br_corner = __br_corner(frags, b_ang, l1, l2, h, round_r),
+ tr_corner = __tr_corner(frags, b_ang, l1, l2, h, round_r)
+ )
+ concat(
+ br_corner,
+ tr_corner
+ );
+
/**
-* golden_spiral_extrude.scad
+* rounded_cube.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-golden_spiral_extrude.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-rounded_cube.html
*
**/
-module golden_spiral_extrude(shape_pts, from, to, point_distance,
- rt_dir = "CT_CLK", twist = 0, scale = 1.0, triangles = "SOLID") {
- pts_angles = golden_spiral(
- from = from,
- to = to,
- point_distance = point_distance,
- rt_dir = rt_dir
- );
+module rounded_cube(size, corner_r, center = false) {
+ is_flt = __is_float(size);
+ x = is_flt ? size : size[0];
+ y = is_flt ? size : size[1];
+ z = is_flt ? size : size[2];
- pts = [for(pt_angle = pts_angles) pt_angle[0]];
- angles = [
- for(pt_angle = pts_angles)
- [90, 0, pt_angle[1] + (rt_dir == "CT_CLK" ? 0 : -90)]
+ corner_frags = __nearest_multiple_of_4(__frags(corner_r));
+ edge_d = corner_r * cos(180 / corner_frags);
+
+ half_x = x / 2;
+ half_y = y / 2;
+ half_z = z / 2;
+
+ half_l = half_x - edge_d;
+ half_w = half_y - edge_d;
+ half_h = half_z - edge_d;
+
+ half_cube_leng = size / 2;
+ half_leng = half_cube_leng - edge_d;
+
+ corners = [
+ for(z = [1, -1])
+ for(y = [1, -1])
+ for(x = [1, -1])
+ [half_l * x, half_w * y, half_h * z]
];
- sections = cross_sections(
- shape_pts,
- pts, angles,
- twist = twist,
- scale = scale
- );
+ module corner(i) {
+ translate(corners[i])
+ sphere(corner_r, $fn = corner_frags);
+ }
- polysections(
- sections,
- triangles = triangles
- );
+ center_pts = center ? [0, 0, 0] : [half_x, half_y, half_z];
+
+ // Don't use `hull() for(...) {...}` because it's slow.
+ translate(center_pts) hull() {
+ corner(0);
+ corner(1);
+ corner(2);
+ corner(3);
+ corner(4);
+ corner(5);
+ corner(6);
+ corner(7);
+ }
- // testing hook
- test_golden_spiral_extrude(sections);
+ // hook for testing
+ test_rounded_edge_corner_center(corner_frags, corners, center_pts);
}
// override it to test
-module test_golden_spiral_extrude(sections) {
+module test_rounded_edge_corner_center(corner_frags, corners, center_pts) {
}
/**
-* bezier_surface.scad
+* shape_pie.scad
*
* @copyright Justin Lin, 2017
* @license https://opensource.org/licenses/lgpl-3.0.html
*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-bezier_surface.html
-*
-**/
-
-function bezier_surface(t_step, ctrl_pts) =
- let(pts = [
- for(i = [0:len(ctrl_pts) - 1])
- bezier_curve(t_step, ctrl_pts[i])
- ])
- [for(x = [0:len(pts[0]) - 1])
- bezier_curve(t_step,
- [for(y = [0:len(pts) - 1]) pts[y][x]]
- )
- ];
-
-/**
-* rotate_p.scad
-*
-* @copyright Justin Lin, 2017
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-rotate_p.html
-*
-**/
-
-
-function _q_rotate_p_3d(p, a, v) =
- let(
- half_a = a / 2,
- axis = v / norm(v),
- s = sin(half_a),
- x = s * axis[0],
- y = s * axis[1],
- z = s * axis[2],
- w = cos(half_a),
-
- x2 = x + x,
- y2 = y + y,
- z2 = z + z,
-
- xx = x * x2,
- yx = y * x2,
- yy = y * y2,
- zx = z * x2,
- zy = z * y2,
- zz = z * z2,
- wx = w * x2,
- wy = w * y2,
- wz = w * z2
- )
- [
- [1 - yy - zz, yx - wz, zx + wy] * p,
- [yx + wz, 1 - xx - zz, zy - wx] * p,
- [zx - wy, zy + wx, 1 - xx - yy] * p
- ];
-
-function _rotx(pt, a) =
- let(cosa = cos(a), sina = sin(a))
- [
- pt[0],
- pt[1] * cosa - pt[2] * sina,
- pt[1] * sina + pt[2] * cosa
- ];
-
-function _roty(pt, a) =
- let(cosa = cos(a), sina = sin(a))
- [
- pt[0] * cosa + pt[2] * sina,
- pt[1],
- -pt[0] * sina + pt[2] * cosa,
- ];
-
-function _rotz(pt, a) =
- let(cosa = cos(a), sina = sin(a))
- [
- pt[0] * cosa - pt[1] * sina,
- pt[0] * sina + pt[1] * cosa,
- pt[2]
- ];
-
-function _rotate_p_3d(point, a) =
- _rotz(_roty(_rotx(point, a[0]), a[1]), a[2]);
-
-function _rotate_p(p, a) =
- let(angle = __to_ang_vect(a))
- len(p) == 3 ?
- _rotate_p_3d(p, angle) :
- __to2d(
- _rotate_p_3d(__to3d(p), angle)
- );
-
-
-function _q_rotate_p(p, a, v) =
- len(p) == 3 ?
- _q_rotate_p_3d(p, a, v) :
- __to2d(
- _q_rotate_p_3d(__to3d(p), a, v)
- );
-
-function rotate_p(point, a, v) =
- v == undef ? _rotate_p(point, a) : _q_rotate_p(point, a, v);
-
-
-/**
-* in_shape.scad
-*
-* @copyright Justin Lin, 2019
-* @license https://opensource.org/licenses/lgpl-3.0.html
-*
-* @see https://openhome.cc/eGossip/OpenSCAD/lib-in_shape.html
+* @see https://openhome.cc/eGossip/OpenSCAD/lib-shape_pie.html
*
**/
-function _in_shape_in_line_equation(edge, pt) =
- let(
- x1 = edge[0][0],
- y1 = edge[0][1],
- x2 = edge[1][0],
- y2 = edge[1][1],
- a = (y2 - y1) / (x2 - x1),
- b = y1 - a * x1
- )
- (pt[1] == a * pt[0] + b);
-
-function _in_shape_in_any_edges_sub(edges, leng, pt, i, epsilon) =
- leng == i ? false : (
- __in_line(edges[i], pt, epsilon) ? true : _in_shape_in_any_edges_sub(edges, leng, pt, i + 1, epsilon)
- );
-
-function _in_shape_in_any_edges(edges, pt, epsilon) = _in_shape_in_any_edges_sub(edges, len(edges), pt, 0, epsilon);
-
-function _in_shape_interpolate_x(y, p1, p2) =
- p1[1] == p2[1] ? p1[0] : (
- p1[0] + (p2[0] - p1[0]) * (y - p1[1]) / (p2[1] - p1[1])
- );
-
-function _in_shape_does_pt_cross(pts, i, j, pt) =
- ((pts[i][1] > pt[1]) != (pts[j][1] > pt[1])) &&
- (pt[0] < _in_shape_interpolate_x(pt[1], pts[i], pts[j]));
-
-
-function _in_shape_sub(shapt_pts, leng, pt, cond, i, j) =
- j == leng ? cond : (
- _in_shape_does_pt_cross(shapt_pts, i, j, pt) ?
- _in_shape_sub(shapt_pts, leng, pt, !cond, j, j + 1) :
- _in_shape_sub(shapt_pts, leng, pt, cond, j, j + 1)
- );
-
-function in_shape(shapt_pts, pt, include_edge = false, epsilon = 0.0001) =
- let(
- leng = len(shapt_pts),
- edges = __lines_from(shapt_pts, true)
- )
- _in_shape_in_any_edges(edges, pt, epsilon) ? include_edge :
- _in_shape_sub(shapt_pts, leng, pt, false, leng - 1, 0);
+function shape_pie(radius, angle) =
+ __shape_pie(radius, angle);