135 function _polygon_is_ear
144 ip = (i == 0) ? n-1 : i-1,
145 in_ = (i == n-1) ? 0 : i+1,
163 if (j != ip && j != i && j != in_)
169 convex && no_interior;
209 function _polygon_clip_ears
215 let ( n = len(path) )
217 ? concat(tris, [[path[0], path[1], path[2]]])
222 [for (i = [0 : n-1]) if (_polygon_is_ear(c, path, i)) i]
228 _ = echo("WARNING: _polygon_clip_ears: no ear found — path may be self-intersecting or degenerate. Triangulation is incomplete.")
233 ip = (ear_i == 0) ? n-1 : ear_i-1,
234 in_ = (ear_i == n-1) ? 0 : ear_i+1,
236 tri = [path[ip], path[ear_i], path[in_]],
237 new_path = [for (j = [0 : n-1]) if (j != ear_i) path[j]]
239 _polygon_clip_ears(c, new_path, concat(tris, [tri]));
275 function _polygon_cap_triangles
285 norm_path = (cw == true) ?
reverse(path) : path,
287 raw_tris = _polygon_clip_ears(c, norm_path),
292 let (t = flip ? [tri[2], tri[1], tri[0]] : tri)
293 [t[0] + offset, t[1] + offset, t[2] + offset]
296 (len(raw_tris) == 0) ?
empty_lst : out_tris;
324 function _polygon_edge_indices
332 let (j = (i == 0) ? n-1 : i-1)
364 function _polygon_vertex_indices
374 prev = (i == 0) ? n-1 : i-1,
375 next = (i == n-1) ? 0 : i+1
377 [k[prev], k[i], k[next]]
465 ai = (cw == true) ? ao - i*step : ao + i*step,
466 v = o + s * [cos(ai), sin(ai)]
468 is_undef(vr) ? v : v - vr/cos(180/n) *
unit_l(v - o)
542 s = (tp[a] > ip[a]) ? +1 : -1,
549 [for (i=is_list(r) ? r : [r]) (i*(tp[a]-ip[a])+ip[a])]
558 [for (i=[ip[a] : s*sx : tp[a]]) i]
567 co = ( abs(tp[a]-ip[a]) - sx*floor(abs(tp[a]-ip[a])/sx) )/2
569 [ip[a], for (i=[ip[a] + s*co : s*sx : tp[a]]) i, tp[a]]
572 : [for (i=[0:fn]) (i*(tp[a]-ip[a])/fn+ip[a])]
634 va1 =
is_number(v1) ? [cos(v1), sin(v1)] : v1,
635 va2 =
is_number(v2) ? [cos(v2), sin(v2)] : v2,
645 sas = (((cw == true) ? 0 : 360) - vap)/naf
649 let (aa = iap + as * sas)
650 o + r * [cos(aa), sin(aa)]
709 d1 =
unit_l(
is_number(v1) ? [cos(v1), sin(v1)] : (len(v1) == 2 && is_list(v1[0])) ? v1[1] - v1[0] : v1 ),
710 d2 =
unit_l(
is_number(v2) ? [cos(v2), sin(v2)] : (len(v2) == 2 && is_list(v2[0])) ? v2[1] - v2[0] : v2 ),
713 sin_half = sqrt( max(0, (1 - cos_full) / 2) ),
721 bisector =
unit_l(d1 + d2),
724 dist_to_center = r / sin_half,
727 fc = o + dist_to_center * bisector,
730 cos_half = sqrt( max(0, (1 + cos_full) / 2) ),
731 tan_half = sin_half / cos_half,
733 tp1 = o + (r / tan_half) * d1,
734 tp2 = o + (r / tan_half) * d2,
888 va3 = (va1 == va2) ? va2+360 : va2,
894 as = (va3 > va1) ? [af:-1:0] : [0:af],
901 let (pa = ((af-i)*va1 + i*va3) / af)
902 o + [rx*cos(pa), ry*sin(pa)]
905 (cw == true) ? pp :
reverse(pp);
967 "given h, the
angle a is restricted to the range [45, 135]."
976 p2 = o + (is_undef(h) ? l*[cos(a), sin(a)] : h*[cos(2*a - 90), 1]),
981 pp = [p1, p2, p3, p4]
983 (cw == true) ? pp :
reverse(pp);
1049 chord =
sum( [for (i = [0 : len(c)-2])
distance_pp( c[i], c[i+1] )] ),
1056 (len(cp) == 1) ? cp[0]
1059 [for (i = [0 : len(cp)-2]) (1-t)*cp[i] + t*cp[i+1]],
1066 de_casteljau( c, i / nf )
1069 (cw == true) ? pp :
reverse(pp);
1136 concat( [c[n-1]], c, [c[0]], [c[1]] )
1137 : concat( [2*c[0] - c[1]], c, [2*c[n-1] - c[n-2]] ),
1140 ns = closed ? n : n - 1,
1147 function (kp, seg, nf, last_seg)
1155 end_i = last_seg ? nf : nf - 1
1158 for (i = [0 : end_i])
1168 + (2*p0 - 5*p1 + 4*p2 - p3) * t2
1169 + (-p0 + 3*p1 - 3*p2 + p3) * t3
1175 for (seg = [0 : ns-1])
1178 chord =
distance_pp( c[seg % n], c[(seg+1) % n] ),
1180 last_s = (!closed) && (seg == ns-1)
1182 for (pt = cr_seg(kp, seg, nf, last_s))
1186 (cw == true) ? pp :
reverse(pp);
1255 ) =
is_defined(r) ? pow(r, 2) * n * sin(360/n) / 2
1288 ei = _polygon_edge_indices(pm)
1328 ei = _polygon_edge_indices(pm),
1333 (c[e[0]][0] + c[e[1]][0]) * (c[e[1]][1] - c[e[0]][1])
1337 (s == false) ? abs(sa) : sa;
1385 nv =
defined_or(n,
cross_ll([c[pm[0][0]], c[pm[0][1]]], [c[pm[0][0]], c[pm[0][2]]])),
1397 " cannot determine polygon plane. Supply an explicit normal n."
1401 ac = [abs(nv[0]), abs(nv[1]), abs(nv[2])],
1403 ai = (am == ac[2]) ? 2 : (am == ac[1]) ? 1 : 0,
1406 for (k = pm) let (m = len(k))
1408 c[k[i%m]][(ai+1)%3] * (c[k[(i+1)%m]][(ai+2)%3] - c[k[(i-1)%m]][(ai+2)%3])
1451 ei = _polygon_edge_indices(pm),
1456 xc = c[e[0]][0], yc = c[e[0]][1],
1457 xn = c[e[1]][0], yn = c[e[1]][1],
1460 [(xc + xn) * cd, (yc + yn) * cd]
1518 ei = _polygon_edge_indices(pm),
1521 (c[e[0]][1] <= t[1] && c[e[1]][1] > t[1] &&
is_left_ppp(c[e[0]], c[e[1]], t) > 0) ? 1
1522 : (c[e[0]][1] > t[1] && c[e[1]][1] <= t[1] &&
is_left_ppp(c[e[0]], c[e[1]], t) < 0) ? -1
1616 ) = is_undef(c) ? undef
1617 : len(c) < 3 ? undef
1622 vi = _polygon_vertex_indices(pm),
1629 [c[triple[0]], c[triple[1]]],
1630 [c[triple[1]], c[triple[2]]]
1715 for (k = pm) let (n = len(k))
1719 j = (i == 0) ? n-1 : i-1
1721 angle_ll([t, c[k[i]]], [t, c[k[j]]])
1811 pn = len([for (
pi = pm) for (ci =
pi) 1]),
1814 zr = (center == true) ? [-h/2, h/2] : [0, h],
1819 for (i = [0 : len(pm)-1])
1820 len([for (j = [0 : i-1]) for (ci = pm[j]) 1])
1834 "degenerate path (zero
signed area) — winding cannot be determined."
1842 for (zi = zr) for (
pi = pm)
1843 for (ci =
pi) concat(c[ci] - po, zi)
1849 for (k = [0 : len(pm)-1])
1850 for (tri = _polygon_cap_triangles
1854 cw = cw_per_path[k],
1856 offset = po_offsets[k]
1861 for (k = [0 : len(pm)-1])
1862 for (tri = _polygon_cap_triangles
1866 cw = cw_per_path[k],
1868 offset = po_offsets[k] + pn
1873 for (k = [0 : len(pm)-1])
1874 let (
pi = pm[k], pl = len(
pi), base = po_offsets[k])
1875 for (li = [0 : pl-1])
1876 let (ci = base + li, ni = base + (li+1)%pl)
1877 (cw_per_path[k] == true) ?
1878 [ci, ci+pn, ni+pn, ni]
1879 : [ci, ni, ni+pn, ci+pn]
1950 va1 =
is_number(v1) ? [cos(v1), sin(v1)] : v1,
1951 va2 =
is_number(v2) ? [cos(v2), sin(v2)] : v2,
1963 vim = (m == 1) ? sqrt( pow(
distance_pp(o, tcc),2) - pow(r,2) ) : r,
1966 tc1 = o + vim*
unit_l(va1),
1967 tc2 = o + vim*
unit_l(va2),
1970 vpl = (m == 1) ?
polygon_arc_sweep_p(r=r, o=tcc, v1=[tcc, tc1], v2=[tcc, tc2], fn=fn, cw=true)
1971 : (m == 2) ?
polygon_arc_sweep_p(r=r, o=tcc, v1=[tcc, tc1], v2=[tcc, tc2], fn=fn, cw=false)
1975 pp = concat([tc1], vpl, [tc2])
1977 (cw == true) ? pp :
reverse(pp);
2092 cp = (cw == true) ? c :
reverse(c),
2100 for ( i = [0 : len(avl)-1] )
2123 : (rm == 1 || rm == 2) ?
2133 : (rm == 1 || rm == 2) ?
2148 tc1 = (rm == 0 || rm > 10) ?
origin2d
2149 : (rm == 3 || rm == 7 || rm == 9) ?
2150 vc + vim *
unit_l([vp, vc])
2151 : vc + vim *
unit_l([vc, vp]),
2153 tc2 = (rm == 0 || rm > 10) ?
origin2d
2154 : (rm == 4 || rm == 8 || rm == 10) ?
2155 vc + vim *
unit_l([vn, vc])
2156 : vc + vim *
unit_l([vc, vn]),
2159 vpl = (rm == 0 || rm > 10) ? [vc]
2162 : (rm == 2 || rm == 3 || rm == 4) ?
2164 : (rm == 6 || rm == 7 || rm == 8) ?
2174 (cw == true) ? pp :
reverse(pp);
2435 len = sqrt(dx*dx + dy*dy),
2438 steps = ceil( (len * (t_max - t_min) / wp) * line_fn ),
2451 safe_s = (remap == 2 || remap == 3) ?
2454 log_s = (remap == 2) ? log(0.5) / log(safe_s) : 0,
2455 log_1ms = (remap == 2) ? log(0.5) / log(1 - safe_s) : 0,
2459 blend_m_phase = (remap == 3) ?
defined_e_or(m, 1, 1/2) : 0,
2460 blend_m_skew = (remap == 3) ?
defined_e_or(m, 2, 1/2) : 0,
2461 blend_m_blend = (remap == 3) ?
defined_e_or(m, 3, 1/2) : 0,
2462 blend_safe_skew = (remap == 3) ?
2465 blend_log_s = (remap == 3) ? log(0.5) / log(blend_safe_skew) : 0,
2466 blend_log_1ms = (remap == 3) ? log(0.5) / log(1 - blend_safe_skew) : 0
2470 for (i = [0 : steps])
2474 tp = t_min + (t_max - t_min) * (i / steps),
2479 u_raw = (arc / wp) - floor(arc / wp),
2480 u = u_raw < 0 ? u_raw + 1 : u_raw,
2490 (u + m_shift) - floor(u + m_shift)
2494 0.5 * pow(u / safe_s, log_s)
2495 : 0.5 + 0.5 * pow((u - safe_s) / (1 - safe_s), log_1ms)
2500 u_phase = (u + blend_m_phase) - floor(u + blend_m_phase),
2501 u_skew = u < blend_safe_skew ?
2502 0.5 * pow(u / blend_safe_skew, blend_log_s)
2503 : 0.5 + 0.5 * pow((u - blend_safe_skew) / (1 - blend_safe_skew), blend_log_1ms)
2505 (1 - blend_m_blend) * u_phase + blend_m_blend * u_skew
2511 let ( v = u_remapped )
2517 v < 0.25 ? 4 * v : v < 0.75 ? 2 - 4 * v : 4 * v - 4
2542 v < w_rise ? -1 + 2 * (v / w_rise)
2543 : v < w_rise + w_hold ? 1
2544 : v < w_rise + w_hold + w_fall ? 1 - 2 * ((v - w_rise - w_hold) / w_fall)
2548 2 * abs(sin(180 * v)) - 1
2551 v < 0.5 ? 2 * sin(360 * v) - 1 : -1
2561 i0 = floor(scaled) %
count,
2562 i1 = (i0 + 1) %
count,
2563 sf = scaled - floor(scaled),
2571 signv = wave < 0 ? -1 : (wave > 0 ? 1 : 0),
2572 offset = oa + ma * signv * pow(abs(wave), 1 / safe_n)
2574 [ p1.x + arc * tx + offset * nx, p1.y + arc * ty + offset * ny ]
origin2d
<point-2d> The origin point coordinate in 2d Euclidean space.
x_axis2d_uv
<vector-2d> The unit vector of the positive x-axis in 2d Euclidean space.
y_axis2d_uv
<vector-2d> The unit vector of the positive y-axis in 2d Euclidean space.
pi
<decimal> The ratio of a circle's circumference to its diameter.
empty_lst
<list> A list with no values (the empty list).
grid_fine
OpenSCAD fine grid limit.
function line_tp(l)
Return the terminal point of a line or vector.
function angle_ll(l1, l2, s=true)
Compute the angle between two lines or vectors in a 3d or 2d-space.
function line_ip(l)
Return the initial point of a line or vector.
function cross_ll(l1, l2)
Compute the cross product of two lines or vectors in a 3d or 2d-space.
function is_left_ppp(p1, p2, p3)
Test if a point is left, on, or right of an infinite line in a 2d-space.
function unit_l(l)
Compute the normalized unit vector of a line or vector.
function interpolate2d_l_pp(p1, p2, x, y)
Linearly interpolate along a line established by two points in 2d.
function distance_pp(p1, p2)
Compute the distance between two points.
function unique(v)
Return a list of the unique elements of an iterable value.
function defined_e_or(v, i, d)
Returns an element from an iterable if it exists, or a default value if not.
function third(v)
Return the third element of an iterable value.
function count(mv, v, s=true, i)
Count all occurrences of a match value in an iterable value.
function second(v)
Return the second element of an iterable value.
function defined_eon_or(v, i, d)
Returns the list element or scalar numeric value if defined, otherwise returns the default value.
function first(v)
Return the first element of an iterable value.
function sequence_ns(v, n=1, s=1, w=false)
Return a list of all n-element sequential-subsets of an iterable value.
function reverse(v)
Reverse the elements of an iterable value.
function all_len(v, l)
Test if all elements of an iterable value are iterable with a fixed length.
function any_equal(v, cv)
Test if any element of an iterable value equal a comparison value.
function almost_eq_nv(v1, v2, p=6)
Test to see if two numerical vectors are sufficiently equal.
function consts(l, v, u=false)
Create a list of constant or incrementing elements.
function strl(v)
Convert a list of values to a concatenated string.
function merge_s(v, r=false)
Serially merge the elements of a list.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function defined_or(v, d)
Return given value, if defined, or a secondary value, if primary is not defined.
function is_scalar(v)
Test if a value is a single non-iterable value.
function is_between(v, l, u)
Test if a numerical value is between an upper and lower bounds.
function is_defined(v)
Test if a value is defined.
function is_number(v)
Test if a value is a number.
function polygon_arc_sweep_p(r=1, o=origin2d, v1=x_axis2d_uv, v2=x_axis2d_uv, fn, cw=true)
Compute coordinates of an arc with constant radius between two vectors in 2D.
function polygon_regular_perimeter(n, r, a)
Compute the perimeter of an n-sided regular polygon in 2D.
function polygon3d_area(c, p, n)
Compute the area of a polygon in a Euclidean 3d-space.
function polygon_bezier_p(c, fn, cw=true)
Compute coordinates for a degree-n Bézier curve in 2D.
function polygon_regular_area(n, r, a)
Compute the area of an n-sided regular polygon in 2D.
function polygon_elliptical_sector_p(r=1, o=origin2d, v1=x_axis2d_uv, v2=x_axis2d_uv, s=true, fn, cw=true)
Compute coordinates for an elliptical sector in 2D.
function polygon_winding(c, p, t)
Compute the winding number of a polygon about a point in a Euclidean 2d-space.
function polygon_trapezoid_p(b=1, h, l=1, a=90, o=origin2d, cw=true)
Compute the coordinates for a rounded trapezoid in 2D space.
function polygon_linear_extrude_pf(c, p, h=1, centroid=false, center=false)
Convert a polygon in 2D to a polyhedron by adding a height dimension.
function polygon_as_is_p_inside(c, p, t)
Test if a point is inside a polygon in a Euclidean 2d-space using angle summation.
function polygon_round_eve_all_p(c, vr=0, vrm=1, vfn, w=true, cw=true)
Compute coordinates that round all of the vertices between each adjacent edges in 2D.
function polygon_regular_p(n, r, a, o=origin2d, ao=0, vr, cw=true)
Compute coordinates for an n-sided regular polygon in 2D.
function polygon_is_convex(c, p)
Test the convexity of a polygon in a Euclidean 2d-space.
function polygon_round_eve_p(r=1, m=1, o=origin2d, v1=x_axis2d_uv, v2=y_axis2d_uv, fn, cw=true)
Compute coordinates for a constant radius vertex round between two edge vectors in 2D.
function polygon_spline_p(c, fn, closed=false, cw=true)
Compute coordinates for a Catmull-Rom spline through a list of knot points in 2D.
function polygon_wn_is_p_inside(c, p, t)
Test if a point is inside a polygon in a Euclidean 2d-space using winding number.
function polygon_line_wave_p(p1=origin2d, p2=x_axis2d_uv, p=1, a=1, w=1, m=0, t, fn)
Generate 2D coordinate points along a line with periodic waveform lateral displacement.
function polygon_line_p(p1=origin2d, p2=x_axis2d_uv, l, x, y, r, fs, ft, fn=1)
Compute coordinates along a line in 2D.
function polygon_arc_blend_p(p1, p2, p3, r=1, fn, cw=true)
Compute coordinates for a circular arc blend between two line segments in 2D.
function polygon_centroid(c, p)
Compute the center of mass of a polygon in a Euclidean 2d-space.
function polygon_arc_fillet_p(r=1, o=origin2d, v1=x_axis2d_uv, v2=y_axis2d_uv, fn, cw=true)
Compute coordinates for a circular fillet arc between two rays in 2D.
function polygon_area(c, p, s=false)
Compute the signed area of a polygon in a Euclidean 2d-space.
function polygon_perimeter(c, p)
Calculate the perimeter length of a polygon in 2d.
function polygon_is_clockwise(c, p)
Test the vertex ordering of a polygon in a Euclidean 2d-space.
function triangle2d_exradius(c, vi=1)
Compute the exradius of a triangle's excircle in 2D.
function triangle2d_excenter(c, vi=1)
Compute the center coordinate of a triangle's excircle in 2D.
function triangle2d_inradius(c)
Compute the inradius of a triangle's incircle in 2D.
function triangle2d_incenter(c)
Compute the center coordinate of a triangle's incircle in 2D.
function get_fn(r=0)
Return facets number for the given arc radius.
function angle(a, from=angle_unit_default, to=angle_unit_base)
Convert an angle value from one unit to another.