update code style for geo classes

This commit is contained in:
Stewart Allen 2022-01-31 20:50:19 -05:00
commit 7b8a5be1c5
11 changed files with 2777 additions and 3147 deletions

9
app.js
View file

@ -309,15 +309,13 @@ const script = {
"add/array", "add/array",
"add/three", "add/three",
"geo/base", "geo/base",
// "geo/render",
"geo/point", "geo/point",
"geo/points", "geo/points",
"geo/slope", "geo/slope",
"geo/line", "geo/line",
"geo/bounds", "geo/bounds",
"geo/polygon",
"geo/polygons", "geo/polygons",
// "geo/gyroid", "geo/polygon",
"geo/mesh", "geo/mesh",
"data/local", "data/local",
"data/index", "data/index",
@ -383,8 +381,8 @@ const script = {
"geo/slope", "geo/slope",
"geo/line", "geo/line",
"geo/bounds", "geo/bounds",
"geo/polygon",
"geo/polygons", "geo/polygons",
"geo/polygon",
"geo/gyroid", "geo/gyroid",
"geo/mesh", "geo/mesh",
// "moto/broker", // "moto/broker",
@ -432,8 +430,8 @@ const script = {
"geo/slope", "geo/slope",
"geo/line", "geo/line",
"geo/bounds", "geo/bounds",
"geo/polygon",
"geo/polygons", "geo/polygons",
"geo/polygon",
"geo/gyroid", "geo/gyroid",
"kiri-mode/fdm/driver", "kiri-mode/fdm/driver",
"kiri-mode/fdm/slice", "kiri-mode/fdm/slice",
@ -453,7 +451,6 @@ const script = {
"add/array", "add/array",
"add/three", "add/three",
"geo/base", "geo/base",
// "geo/render",
"geo/point", "geo/point",
"geo/points", "geo/points",
"geo/slope", "geo/slope",

View file

@ -4,20 +4,21 @@
(function() { (function() {
if (self.base.gyroid) return; if (self.base.gyroid) return;
const base = self.base; const base = self.base;
const PI2 = Math.PI * 2; const PI2 = Math.PI * 2;
let cache = {};
let lastVal;
let lastRes = 0;
let lastSlice = 0;
/** let cache = {};
let lastVal;
let lastRes = 0;
let lastSlice = 0;
/**
* @param off {number} z offset value from 0-1 * @param off {number} z offset value from 0-1
* @param res {number} resolution (pixels/slices per side) * @param res {number} resolution (pixels/slices per side)
*/ */
function slice(off, res, val) { function slice(off, res, val) {
// auto clear cach if it hasn't been hit in the last 20 seconds // auto clear cach if it hasn't been hit in the last 20 seconds
// or the requested resolution or tip values have changed // or the requested resolution or tip values have changed
let now = Date.now(); let now = Date.now();
@ -168,10 +169,10 @@
let slice = {edge, points, dir, polys: psimple}; let slice = {edge, points, dir, polys: psimple};
cache[key] = slice; cache[key] = slice;
return slice; return slice;
} }
// merge co-linear and distance threshold // merge co-linear and distance threshold
function filter(poly, inc) { function filter(poly, inc) {
if (poly.length <= 2) { if (poly.length <= 2) {
return poly; return poly;
} }
@ -203,17 +204,17 @@
} }
nupoly.push(poly[poly.length-1]); nupoly.push(poly[poly.length-1]);
return nupoly; return nupoly;
} }
function distTo(a, b, dir) { function distTo(a, b, dir) {
let dx = a.x - b.x; let dx = a.x - b.x;
let dy = a.y - b.y; let dy = a.y - b.y;
// bias distance by prevailing direction of discovery to join stragglers // bias distance by prevailing direction of discovery to join stragglers
if (dir === 'lr') dx = dx / 2; if (dir === 'lr') dx = dx / 2;
if (dir === 'td') dy = dy / 2; if (dir === 'td') dy = dy / 2;
return Math.sqrt(dx * dx + dy * dy); return Math.sqrt(dx * dx + dy * dy);
} }
base.gyroid = { slice }; base.gyroid = { slice };
})(); })();

View file

@ -4,79 +4,45 @@
(function() { (function() {
if (self.base.Line) return; if (self.base.Line) return;
const BASE = self.base, PRO = Line.prototype; const base = self.base;
BASE.Line = Line; class Line {
BASE.newLine = newLine; constructor(p1, p2, key) {
BASE.newOrderedLine = newOrderedLine;
/**
*
* @param {Point} p1
* @param {Point} p2
* @param {String} [key]
* @constructor
*/
function Line(p1, p2, key) {
if (!key) key = [p1.key, p2.key].join(';'); if (!key) key = [p1.key, p2.key].join(';');
this.p1 = p1; this.p1 = p1;
this.p2 = p2; this.p2 = p2;
this.key = key; this.key = key;
this.coplanar = false;
this.edge = false;
this.del = false;
} }
/** ****************************************************************** length() {
* Line Prototype Functions
******************************************************************* */
/**
* @returns {number}
*/
PRO.length = function() {
return Math.sqrt(this.length2()); return Math.sqrt(this.length2());
}; }
/** /**
* @returns {number} square of length * @returns {number} square of length
*/ */
PRO.length2 = function() { length2() {
return this.p1.distToSq2D(this.p2); return this.p1.distToSq2D(this.p2);
}; }
/** slope() {
* @returns {Slope} return base.newSlope(this.p1.slopeTo(this.p2));
*/ }
PRO.slope = function() {
return BASE.newSlope(this.p1.slopeTo(this.p2));
};
/** reverse() {
* @returns {Line}
*/
PRO.reverse = function() {
let t = this.p1; let t = this.p1;
this.p1 = this.p2; this.p1 = this.p2;
this.p2 = t; this.p2 = t;
return this; return this;
}; }
/** midpoint() {
* @returns {Point}
*/
PRO.midpoint = function() {
return this.p1.midPointTo(this.p2); return this.p1.midPointTo(this.p2);
}; }
/** isCollinear(line) {
* faulty when line doubles back at 180?
* @param {Line} line
* @returns {boolean}
*/
PRO.isCollinear = function(line) {
let p1 = this.p1, let p1 = this.p1,
p2 = this.p2, p2 = this.p2,
p3 = line.p1, p3 = line.p1,
@ -85,27 +51,20 @@
d1y = (p2.y - p1.y), d1y = (p2.y - p1.y),
d2x = (p4.x - p3.x), d2x = (p4.x - p3.x),
d2y = (p4.y - p3.y); d2y = (p4.y - p3.y);
return Math.abs( (d2y * d1x) - (d2x * d1y) ) < 0.0001; return Math.abs( (d2y * d1x) - (d2x * d1y) ) < 0.0001;
}; }
}
/** ****************************************************************** function newLine(p1, p2, key) {
* Connect to base and Helpers
******************************************************************* */
/**
*
* @param {Point} p1
* @param {Point} p2
* @param {String} [key]
* @returns {Line}
*/
function newLine(p1, p2, key) {
return new Line(p1, p2, key); return new Line(p1, p2, key);
} }
function newOrderedLine(p1, p2, key) { function newOrderedLine(p1, p2, key) {
return p1.key < p2.key ? newLine(p1,p2,key) : newLine(p2,p1,key); return p1.key < p2.key ? newLine(p1,p2,key) : newLine(p2,p1,key);
} }
base.Line = Line;
base.newLine = newLine;
base.newOrderedLine = newOrderedLine;
})(); })();

View file

@ -4,16 +4,14 @@
(function() { (function() {
if (self.base.Point) return; if (self.base.Point) return;
const BASE = self.base, const base = self.base;
UTIL = BASE.util, const { util, config, key } = base;
CONF = BASE.config, const { round } = util;
KEYS = BASE.key,
ROUND = UTIL.round;
class Point { class Point {
constructor(x,y,z,key) { constructor(x, y, z, key) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z || 0; this.z = z || 0;
@ -32,104 +30,71 @@
((this.z * 100000) | 0) ((this.z * 100000) | 0)
].join(''); ].join('');
} }
}
const PRO = Point.prototype; toClipper() {
BASE.Point = Point;
BASE.newPoint = newPoint;
BASE.pointFromClipper = function(cp, z) {
return newPoint(cp.X / CONF.clipper, cp.Y / CONF.clipper, z);
};
/** ******************************************************************
* Point Prototype Functions
******************************************************************* */
PRO.toClipper = function() {
return { return {
X: this.x * CONF.clipper, X: this.x * config.clipper,
Y: this.y * CONF.clipper Y: this.y * config.clipper
}; };
} }
PRO.setZ = function(z) { setZ(z) {
this.z = z; this.z = z;
return this; return this;
} }
PRO.swapXZ = function() { swapXZ() {
let p = this, let p = this,
t = p.x; t = p.x;
p.x = p.z; p.x = p.z;
p.z = t; p.z = t;
return this; return this;
}; }
PRO.swapYZ = function() { swapYZ() {
let p = this, let p = this,
t = p.y; t = p.y;
p.y = p.z; p.y = p.z;
p.z = t; p.z = t;
return this; return this;
}; }
PRO.round = function(precision) { round(precision) {
return newPoint( return newPoint(
this.x.round(precision), this.x.round(precision),
this.y.round(precision), this.y.round(precision),
this.z.round(precision)); this.z.round(precision));
}; }
PRO.addFacet = function(facet) { addFacet(facet) {
if (!this.group) this.group = []; if (!this.group) this.group = [];
this.group.push(facet); this.group.push(facet);
return this; return this;
}; }
PRO.rekey = function() { rekey() {
this._key = undefined; this._key = undefined;
}; }
PRO.toString = function() { toString() {
return this.key; return this.key;
}; }
/** clone() {
* @returns {Point}
*/
PRO.clone = function() {
return newPoint(this.x, this.y, this.z, this._key); return newPoint(this.x, this.y, this.z, this._key);
}; }
/** slopeTo(p) {
* @param {Point} p return base.newSlope(this, p);
* @returns {Slope} }
*/
PRO.slopeTo = function(p) {
return BASE.newSlope(this, p);
};
/** lineTo(p, k) {
* return base.newLine(this, p, k);
* @param {Point} p }
* @param {String} [k]
* @returns {Line}
*/
PRO.lineTo = function(p, k) {
return BASE.newLine(this, p, k);
};
/** isNear(p, dist) {
* @param {Point} p return util.isCloseTo(this.x, p.x, dist) && util.isCloseTo(this.y, p.y, dist);
* @param {number} [dist] }
* @returns {boolean}
*/
PRO.isNear = function(p, dist) {
return UTIL.isCloseTo(this.x, p.x, dist) && UTIL.isCloseTo(this.y, p.y, dist);
};
/** /**
* return distance to line connecting points p1, p2 * return distance to line connecting points p1, p2
@ -139,10 +104,9 @@
* @param {Point} p2 * @param {Point} p2
* @returns {number} * @returns {number}
*/ */
PRO.distToLine = function(p1, p2) { distToLine(p1, p2) {
// return p2l(this, p1, p2);
return Math.sqrt(this.distToLineSq(p1, p2)); return Math.sqrt(this.distToLineSq(p1, p2));
}; }
/** /**
* used exclusively in new fill code. output does not agree with * used exclusively in new fill code. output does not agree with
@ -151,69 +115,27 @@
* offset clipping. both need to be investigated and a single line * offset clipping. both need to be investigated and a single line
* normal distance needs to be formulated to replace both functions. * normal distance needs to be formulated to replace both functions.
*/ */
PRO.distToLineNew = function(p1, p2) { distToLineNew(p1, p2) {
return p2l(this, p1, p2); return p2l(this, p1, p2);
// return Math.sqrt(this.distToLineSq(p1, p2)); }
};
/** /**
* return square of distance to line connecting points p1, p2 * return square of distance to line connecting points p1, p2
* distance is calculated on the perpendicular (normal) to line * distance is calculated on the perpendicular (normal) to line
*
* @param {Point} p1
* @param {Point} p2
* @returns {number}
*/ */
PRO.distToLineSq = function(p1, p2) { distToLineSq(p1, p2) {
let p = this, let p = this,
d = UTIL.distSq(p1, p2); d = util.distSq(p1, p2);
let t = ((p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y) * (p2.y - p1.y)) / d; let t = ((p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y) * (p2.y - p1.y)) / d;
if (t < 0) return UTIL.distSq(p, p1); if (t < 0) return util.distSq(p, p1);
if (t > 1) return UTIL.distSq(p, p2); if (t > 1) return util.distSq(p, p2);
return UTIL.distSqv2(p.x, p.y, p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y)); return util.distSqv2(p.x, p.y, p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y));
};
// ---( begin fix distToLine )---
function dot(u, v) {
return u.x * v.x + u.y * v.y;
} }
function norm(v) { withinDist2(p1, p2, dist2) {
return Math.sqrt(dot(v,v));
}
function d(u, v) {
return norm({x: u.x - v.x, y: u.y - v.y});
}
function p2l(p, l1, l2) {
let v = {x: l2.x - l1.x, y: l2.y - l1.y};
let w = {x: p.x - l1.x, y: p.y - l1.y};
let c1 = dot(w, v);
let c2 = dot(v, v);
let b = c1 / c2;
if (isNaN(b)) {
// console.log('nan', {p, l1, l2, v, w, c1, c2});
b = 0;
}
let pb = {x: l1.x + b * v.x, y: l1.y + b * v.y};
return d(p, pb);
}
// ---( end fix distToLine )---
/**
*
* @param {Point} p1
* @param {Point} p2
* @param {number} dist2
* @returns {boolean}
*/
PRO.withinDist2 = function(p1, p2, dist2) {
let ll2 = p1.distToSq2D(p2), let ll2 = p1.distToSq2D(p2),
dp1 = this.distToSq2D(p1), dp1 = this.distToSq2D(p1),
dp2 = this.distToSq2D(p2); dp2 = this.distToSq2D(p2);
@ -231,71 +153,53 @@
// dist2 to the described line segment. // dist2 to the described line segment.
if (dp1 > ll2 && dp2 > ll2) return false; if (dp1 > ll2 && dp2 > ll2) return false;
return this.distToLineSq(p1, p2) < dist2; return this.distToLineSq(p1, p2) < dist2;
}; }
/** midPointTo(p2) {
* @param {Point} p2 return newPoint((this.x + p2.x) / 2, (this.y + p2.y) / 2, this.z);
* @returns {Point} }
*/
PRO.midPointTo = function(p2) {
return newPoint((this.x + p2.x)/2, (this.y + p2.y)/2, this.z);
};
/** midPointTo3D(p2) {
* @param {Point} p2
* @returns {Point}
*/
PRO.midPointTo3D = function(p2) {
return newPoint( return newPoint(
(this.x + p2.x)/2, (this.x + p2.x) / 2,
(this.y + p2.y)/2, (this.y + p2.y) / 2,
(this.z + p2.z)/2 (this.z + p2.z) / 2
); );
}; }
/** /**
* non-scale corrected version of follow() * non-scale corrected version of follow()
*
* @param slope
* @param mult
* @returns {Point}
*/ */
PRO.projectOnSlope = function(slope, mult) { projectOnSlope(slope, mult) {
return newPoint( return newPoint(
this.x + slope.dx * mult, this.x + slope.dx * mult,
this.y + slope.dy * mult, this.y + slope.dy * mult,
this.z); this.z);
}; }
PRO.followTo = function(point, mult) { followTo(point, mult) {
return this.follow(this.slopeTo(point), mult); return this.follow(this.slopeTo(point), mult);
}; }
/** /**
* return a point along the line this from point to p2 offset * return a point along the line this from point to p2 offset
* by a distance. positive distances are closer to this point. * by a distance. positive distances are closer to this point.
*
* @param p2
* @param dist
*/ */
PRO.offsetPointFrom = function(p2, dist) { offsetPointFrom(p2, dist) {
let p1 = this, let p1 = this,
dx = p2.x - p1.x, dx = p2.x - p1.x,
dy = p2.y - p1.y, dy = p2.y - p1.y,
ls = dist / Math.sqrt(dx * dx + dy * dy), ls = dist / Math.sqrt(dx * dx + dy * dy),
ox = dx * ls, ox = dx * ls,
oy = dy * ls; oy = dy * ls;
return newPoint(p2.x - ox, p2.y - oy, p2.z, KEYS.NONE); return newPoint(p2.x - ox, p2.y - oy, p2.z, key.NONE);
}; }
/** /**
* return a point along the line this from point to p2 offset * return a point along the line this from point to p2 offset
* by a distance. positive distances are farther from this point. * by a distance. positive distances are farther from this point.
*
* @param p2
* @param dist
*/ */
PRO.offsetPointTo = function(p2, dist) { offsetPointTo(p2, dist) {
let p1 = this, let p1 = this,
dx = p2.x - p1.x, dx = p2.x - p1.x,
dy = p2.y - p1.y; dy = p2.y - p1.y;
@ -306,93 +210,82 @@
ox = dx * ls, ox = dx * ls,
oy = dy * ls; oy = dy * ls;
return newPoint(p1.x + ox, p1.y + oy, p2.z, KEYS.NONE); return newPoint(p1.x + ox, p1.y + oy, p2.z, key.NONE);
}; }
/** offsetLineTo(p2, offset) {
* @param {Point} p2
* @param {number} offset
* @returns {Line}
*/
PRO.offsetLineTo = function(p2, offset) {
let p1 = this, let p1 = this,
dx = p2.x - p1.x, dx = p2.x - p1.x,
dy = p2.y - p1.y, dy = p2.y - p1.y,
ls = offset / Math.sqrt(dx * dx + dy * dy), ls = offset / Math.sqrt(dx * dx + dy * dy),
ox = dx * ls, ox = dx * ls,
oy = dy * ls, oy = dy * ls,
np1 = newPoint(p1.x - oy, p1.y + ox, p1.z, KEYS.NONE), np1 = newPoint(p1.x - oy, p1.y + ox, p1.z, key.NONE),
np2 = newPoint(p2.x - oy, p2.y + ox, p2.z, KEYS.NONE); np2 = newPoint(p2.x - oy, p2.y + ox, p2.z, key.NONE);
np1.op = p1; np1.op = p1;
np2.op = p2; np2.op = p2;
return BASE.newLine(np1, np2, KEYS.NONE); return base.newLine(np1, np2, key.NONE);
}; }
PRO.offset = function(x, y, z) { offset(x, y, z) {
return newPoint(this.x + x, this.y + y, this.z + z); return newPoint(this.x + x, this.y + y, this.z + z);
}; }
/** /**
* checks if a point is inside of a polygon * checks if a point is inside of a polygon
* does not check children/holes * does not check children/holes
*
* @param {Polygon} poly
* @returns {boolean}
*/ */
PRO.inPolygon = function(poly) { inPolygon(poly) {
if (!poly.bounds.containsXY(this.x, this.y)) return false; if (!poly.bounds.containsXY(this.x, this.y)) return false;
let p = poly.points, pl = p.length, p1, p2, i, inside = false; let p = poly.points,
pl = p.length,
p1, p2, i, inside = false;
for (i=0; i<pl; i++) { for (i = 0; i < pl; i++) {
p1 = p[i]; p1 = p[i];
p2 = p[(i+1)%pl]; p2 = p[(i + 1) % pl];
if ((p1.y >= this.y) != (p2.y >= this.y) && if ((p1.y >= this.y) != (p2.y >= this.y) &&
(this.x <= (p2.x - p1.x) * (this.y - p1.y) / (p2.y - p1.y) + p1.x)) (this.x <= (p2.x - p1.x) * (this.y - p1.y) / (p2.y - p1.y) + p1.x)) {
{
inside = !inside; inside = !inside;
} }
} }
return inside; return inside;
}; }
/** /**
* returns true if the point is inside of a polygon but * returns true if the point is inside of a polygon but
* not inside any of it's children * not inside any of it's children
*
* @param {Polygon | Polygon[]} poly
* @return {boolean} true if inside outer but not inner
*/ */
PRO.isInPolygon = function(poly) { isInPolygon(poly) {
let point = this, i; let point = this,
i;
if (Array.isArray(poly)) { if (Array.isArray(poly)) {
for (i=0; i<poly.length; i++) { for (i = 0; i < poly.length; i++) {
if (point.isInPolygon(poly[i])) return true; if (point.isInPolygon(poly[i])) return true;
} }
return false; return false;
} }
let holes = poly.inner; let holes = poly.inner;
if (point.inPolygon(poly) || point.nearPolygon(poly, CONF.precision_merge_sq)) { if (point.inPolygon(poly) || point.nearPolygon(poly, config.precision_merge_sq)) {
for (i=0; holes && i < holes.length; i++) { for (i = 0; holes && i < holes.length; i++) {
if (point.inPolygon(holes[i]) && !point.nearPolygon(holes[i], CONF.precision_merge_sq)) return false; if (point.inPolygon(holes[i]) && !point.nearPolygon(holes[i], config.precision_merge_sq)) return false;
} }
return true; return true;
} }
return false; return false;
}; }
/** /**
* returns true if the point is inside of a polygon but * returns true if the point is inside of a polygon but
* not inside any of it's children * not inside any of it's children
*
* @param {Polygon | Polygon[]} poly
* @return {boolean} true if inside outer but not inner
*/ */
PRO.isInPolygonOnly = function(poly) { isInPolygonOnly(poly) {
let point = this, i; let point = this,
i;
if (Array.isArray(poly)) { if (Array.isArray(poly)) {
for (i=0; i<poly.length; i++) { for (i = 0; i < poly.length; i++) {
if (point.isInPolygonOnly(poly[i])) { if (point.isInPolygonOnly(poly[i])) {
return true; return true;
} }
@ -401,129 +294,96 @@
} }
let holes = poly.inner; let holes = poly.inner;
if (point.inPolygon(poly)) { if (point.inPolygon(poly)) {
for (i=0; holes && i < holes.length; i++) { for (i = 0; holes && i < holes.length; i++) {
if (point.inPolygon(holes[i])) return false; if (point.inPolygon(holes[i])) return false;
} }
return true; return true;
} }
return false; return false;
}; }
/** /**
* checks if point is near polygon edge. distance is squared. * checks if point is near polygon edge. distance is squared.
*
* @param {Polygon} poly
* @param {number} dist2
* @param {boolean} [inner] process inner polygons * @param {boolean} [inner] process inner polygons
* @returns {boolean}
*/ */
PRO.nearPolygon = function(poly, dist2, inner) { nearPolygon(poly, dist2, inner) {
// throw new Error("nearPolygon"); // throw new Error("nearPolygon");
for (let i=0, p=poly.points, pl=p.length ; i<pl; i++) { for (let i = 0, p = poly.points, pl = p.length; i < pl; i++) {
if (this.withinDist2(p[i], p[(i+1)%pl], dist2)) { if (this.withinDist2(p[i], p[(i + 1) % pl], dist2)) {
return true; return true;
} }
} }
if (inner && poly.inner) { if (inner && poly.inner) {
for (let i=0; i<poly.inner.length; i++) { for (let i = 0; i < poly.inner.length; i++) {
if (this.nearPolygon(poly.inner[i], dist2)) return true; if (this.nearPolygon(poly.inner[i], dist2)) return true;
} }
} }
return false; return false;
}; }
/** /**
* returns true if point will not be trimmed later * returns true if point will not be trimmed later
*
* @param {Polygon} poly
* @param {number} offset
* @param {number} mindist2
* @returns {boolean}
*/ */
PRO.insideOffset = function(poly, offset, mindist2) { insideOffset(poly, offset, mindist2) {
return this.inPolygon(poly) === (offset > 0) && !this.nearPolygon(poly, mindist2); return this.inPolygon(poly) === (offset > 0) && !this.nearPolygon(poly, mindist2);
}; }
/** /**
* returns a new point following given slope for given distance * returns a new point following given slope for given distance
* same as projectOnSlope() but scaled * same as projectOnSlope() but scaled
*
* @param {Slope} slope
* @param {number} distance
* @returns {Point}
*/ */
PRO.follow = function(slope, distance) { follow(slope, distance) {
let ls = distance / Math.sqrt(slope.dx * slope.dx + slope.dy * slope.dy); let ls = distance / Math.sqrt(slope.dx * slope.dx + slope.dy * slope.dy);
return newPoint(this.x + slope.dx * ls, this.y + slope.dy * ls, this.z); return newPoint(this.x + slope.dx * ls, this.y + slope.dy * ls, this.z);
}; }
/** /**
* for point, return z-plane intersecting point on line to next point * for point, return intersecting point on z to next point if points
* * are on either size of z
* @param {Point} p
* @param {number} z
* @returns {Point}
*/ */
PRO.intersectZ = function(p, z) { intersectZ(p, z) {
let dx = p.x - this.x, let dx = p.x - this.x,
dy = p.y - this.y, dy = p.y - this.y,
dz = p.z - this.z, dz = p.z - this.z,
pct = 1 - ((p.z - z) / dz); pct = 1 - ((p.z - z) / dz);
return newPoint(this.x + dx * pct, this.y + dy * pct, this.z + dz * pct); return newPoint(this.x + dx * pct, this.y + dy * pct, this.z + dz * pct);
}; }
/** isEqual2D(p) {
* @param {Point} p
* @returns {boolean}
*/
PRO.isEqual2D = function(p) {
return this === p || (this.x === p.x && this.y === p.y); return this === p || (this.x === p.x && this.y === p.y);
}; }
/** /**
* returns true if points are close enough to be considered equivalent * returns true if points are close enough to be considered equivalent
*
* @param {Point} p
* @returns {boolean}
*/ */
PRO.isMergable2D = function(p) { isMergable2D(p) {
return this.isEqual2D(p) || (this.distToSq2D(p) < CONF.precision_merge_sq); return this.isEqual2D(p) || (this.distToSq2D(p) < config.precision_merge_sq);
}; }
/** /**
* compares 3D point * compares 3D point
*
* @param {Point} p
* @returns {boolean}
*/ */
PRO.isEqual = function(p) { isEqual(p) {
return this === p || (this.x === p.x && this.y === p.y && this.z === p.z); return this === p || (this.x === p.x && this.y === p.y && this.z === p.z);
}; }
PRO.isEqual2D = function(p) { isEqual2D(p) {
return this === p || (this.x === p.x && this.y === p.y); return this === p || (this.x === p.x && this.y === p.y);
}; }
/** /**
* returns true if points are close enough to be considered equivalent * returns true if points are close enough to be considered equivalent
*
* @param {Point} p
* @returns {boolean}
*/ */
PRO.isMergable3D = function(p) { isMergable3D(p) {
return this.isEqual(p) || (this.distToSq3D(p) < CONF.precision_merge_sq); return this.isEqual(p) || (this.distToSq3D(p) < config.precision_merge_sq);
}; }
/** /**
* return true if point is inside 2D square size dist*2 around p * return true if point is inside 2D square size dist*2 around p
*
* @param {Point} p
* @param {number} dist
* @returns {boolean}
*/ */
PRO.isInBox = function(p, dist) { isInBox(p, dist) {
return Math.abs(this.x - p.x) < dist && Math.abs(this.y - p.y) < dist; return Math.abs(this.x - p.x) < dist && Math.abs(this.y - p.y) < dist;
}; }
/** /**
* return min distance from point to a polygon * return min distance from point to a polygon
@ -532,7 +392,7 @@
* @param {Polygon} poly * @param {Polygon} poly
* @param {number} [threshold] stop looking if under threshold * @param {number} [threshold] stop looking if under threshold
*/ */
PRO.distToPolySegments = function(poly, threshold) { distToPolySegments(poly, threshold) {
let point = this, let point = this,
mindist = Infinity; mindist = Infinity;
poly.forEachSegment(function(p1, p2) { poly.forEachSegment(function(p1, p2) {
@ -542,32 +402,31 @@
if (mindist <= threshold) return true; if (mindist <= threshold) return true;
}); });
return mindist; return mindist;
}; }
/** /**
* @param {Polygon} poly * @param {Polygon} poly
* @param {number} [threshold] stop looking if under threshold * @param {number} [threshold] stop looking if under threshold
*/ */
PRO.distToPolyPoints = function(poly, threshold) { distToPolyPoints(poly, threshold) {
let point = this, mindist = Infinity; let point = this,
mindist = Infinity;
poly.forEachPoint(function(pp) { poly.forEachPoint(function(pp) {
mindist = Math.min(mindist, point.distTo2D(pp)); mindist = Math.min(mindist, point.distTo2D(pp));
if (mindist < threshold) return true; if (mindist < threshold) return true;
}); });
return mindist; return mindist;
}; }
/** /**
* @param {Point[]} points
* @param {number} max
* @returns {Point} nearest point (less than max) from array to this point * @returns {Point} nearest point (less than max) from array to this point
*/ */
PRO.nearestTo = function(points, max) { nearestTo(points, max) {
if (!max) throw "missing max"; if (!max) throw "missing max";
let mind = Infinity, let mind = Infinity,
minp = null, minp = null,
i, p, d; i, p, d;
for (i=0; i<points.length; i++) { for (i = 0; i < points.length; i++) {
p = points[i]; p = points[i];
if (p === this || p.del) continue; if (p === this || p.del) continue;
d = this.distToSq2D(p); d = this.distToSq2D(p);
@ -577,14 +436,16 @@
} }
} }
return minp; return minp;
}; }
/** /**
* @param {Point[]} points * @param {Point[]} points
* @return {number} average square dist to cloud of points * @return {number} average square dist to cloud of points
*/ */
PRO.averageDistTo = function(points) { averageDistTo(points) {
let sum = 0.0, count = 0, i; let sum = 0.0,
count = 0,
i;
for (i = 0; i < points.length; i++) { for (i = 0; i < points.length; i++) {
if (points[i] != this) { if (points[i] != this) {
sum += this.distToSq2D(points[i]); sum += this.distToSq2D(points[i]);
@ -592,125 +453,125 @@
} }
} }
return sum / count; return sum / count;
}; }
/** /**
* dist to point in 2D * dist to point in 2D
*
* @param {Point} p
* @returns {number}
*/ */
PRO.distTo2D = function(p) { distTo2D(p) {
let dx = this.x - p.x, let dx = this.x - p.x,
dy = this.y - p.y; dy = this.y - p.y;
return Math.sqrt(dx * dx + dy * dy); return Math.sqrt(dx * dx + dy * dy);
}; }
/** /**
* square of distance in 2D * square of distance in 2D
*
* @param {Point} p
* @returns {number}
*/ */
PRO.distToSq2D = function(p) { distToSq2D(p) {
let dx = this.x - p.x, let dx = this.x - p.x,
dy = this.y - p.y; dy = this.y - p.y;
return dx * dx + dy * dy; return dx * dx + dy * dy;
}; }
PRO.distTo3D = function(p) { distTo3D(p) {
let dx = this.x - p.x, let dx = this.x - p.x,
dy = this.y - p.y, dy = this.y - p.y,
dz = this.z - p.z; dz = this.z - p.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz); return Math.sqrt(dx * dx + dy * dy + dz * dz);
}; }
/** /**
* square of distance in 3D * square of distance in 3D
*
* @param {Point} p
* @returns {number}
*/ */
PRO.distToSq3D = function(p) { distToSq3D(p) {
let dx = this.x - p.x, let dx = this.x - p.x,
dy = this.y - p.y, dy = this.y - p.y,
dz = this.z - p.z; dz = this.z - p.z;
return dx * dx + dy * dy + dz * dz; return dx * dx + dy * dy + dz * dz;
}; }
/** /**
* returns true if point is inside triangle described by three points * returns true if point is inside triangle described by three points
*
* @param {Point} a
* @param {Point} b
* @param {Point} c
* @returns {boolean}
*/ */
PRO.inTriangle = function(a, b, c) { inTriangle(a, b, c) {
let as_x = this.x - a.x, let as_x = this.x - a.x,
as_y = this.y - a.y, as_y = this.y - a.y,
s_ab = (b.x - a.x) * as_y - (b.y - a.y) * as_x > 0; s_ab = (b.x - a.x) * as_y - (b.y - a.y) * as_x > 0;
if ((c.x - a.x) * as_y - (c.y - a.y) * as_x > 0 == s_ab) return false; if ((c.x - a.x) * as_y - (c.y - a.y) * as_x > 0 == s_ab) return false;
if ((c.x - b.x) * (this.y - b.y) - (c.y - b.y) * (this.x - b.x) > 0 != s_ab) return false; if ((c.x - b.x) * (this.y - b.y) - (c.y - b.y) * (this.x - b.x) > 0 != s_ab) return false;
return true; return true;
}; }
/** /**
* returns true if point is on a line described by two points. * returns true if point is on a line described by two points.
* test sum of distances p1->this + this->p2 ~= p1->p2 whens * test sum of distances p1->this + this->p2 ~= p1->p2 whens
* slopes from p1->this same as this->p2 * slopes from p1->this same as this->p2
*
* @param {Point} p1
* @param {Point} p2
* @returns {boolean}
*/ */
PRO.onLine = function(p1, p2) { onLine(p1, p2) {
return this.distToLine(p1, p2) < CONF.precision_point_on_line; return this.distToLine(p1, p2) < config.precision_point_on_line;
}; }
/** add(delta) {
*
* @param {THREE.Vector3} delta
* @return {Point} new offset point
*/
PRO.add = function(delta) {
return newPoint(this.x + delta.x, this.y + delta.y, this.z + delta.z); return newPoint(this.x + delta.x, this.y + delta.y, this.z + delta.z);
}; }
/** sub(delta) {
*
* @param {THREE.Vector3} delta
* @return {Point} new offset point
*/
PRO.sub = function(delta) {
return newPoint(this.x - delta.x, this.y - delta.y, this.z - delta.z); return newPoint(this.x - delta.x, this.y - delta.y, this.z - delta.z);
}; }
/** move(delta) {
*
* @param {THREE.Vector3} delta
*/
PRO.move = function(delta) {
this.x += delta.x; this.x += delta.x;
this.y += delta.y; this.y += delta.y;
this.z += delta.z; this.z += delta.z;
return this; return this;
};
/** ******************************************************************
* Connect to base and Helpers
******************************************************************* */
/**
*
* @param {number} x
* @param {number} y
* @param {number} z
* @param {String} [key]
* @returns {Point}
*/
function newPoint(x, y, z, key) {
return new Point(x, y, z, key);
} }
}
function dot(u, v) {
return u.x * v.x + u.y * v.y;
}
function norm(v) {
return Math.sqrt(dot(v, v));
}
function d(u, v) {
return norm({
x: u.x - v.x,
y: u.y - v.y
});
}
function p2l(p, l1, l2) {
let v = {
x: l2.x - l1.x,
y: l2.y - l1.y
};
let w = {
x: p.x - l1.x,
y: p.y - l1.y
};
let c1 = dot(w, v);
let c2 = dot(v, v);
let b = c1 / c2;
if (isNaN(b)) {
// console.log('nan', {p, l1, l2, v, w, c1, c2});
b = 0;
}
let pb = {
x: l1.x + b * v.x,
y: l1.y + b * v.y
};
return d(p, pb);
}
function newPoint(x, y, z, key) {
return new Point(x, y, z, key);
}
base.Point = Point;
base.newPoint = newPoint;
base.pointFromClipper = function(cp, z) {
return newPoint(cp.X / config.clipper, cp.Y / config.clipper, z);
};
})(); })();

View file

@ -4,20 +4,17 @@
(function() { (function() {
const BASE = self.base; const base = self.base;
const CONF = BASE.config; const { config } = base;
BASE.verticesToPoints = verticesToPoints; base.verticesToPoints = verticesToPoints;
BASE.pointsToVertices = pointsToVertices; base.pointsToVertices = pointsToVertices;
/** /**
* converts a geometry point array into a kiri point array * converts a geometry point array into a kiri point array
* with auto-decimation * with auto-decimation
*
* @param {Float32Array} array
* @returns {Array}
*/ */
function verticesToPoints(array, options) { function verticesToPoints(array, options) {
let parr = new Array(array.length / 3), let parr = new Array(array.length / 3),
i = 0, i = 0,
j = 0, j = 0,
@ -31,7 +28,7 @@
// replace point objects with their equivalents // replace point objects with their equivalents
while (i < array.length) { while (i < array.length) {
let p = BASE.newPoint(array[i++], array[i++], array[i++]), let p = base.newPoint(array[i++], array[i++], array[i++]),
k = p.key, k = p.key,
m = hash[k]; m = hash[k];
if (!m) { if (!m) {
@ -46,8 +43,8 @@
// threshold = point count for triggering decimation // threshold = point count for triggering decimation
// precision = under which points are merged // precision = under which points are merged
// maxpass = max number of decimations // maxpass = max number of decimations
threshold = threshold > 0 ? threshold : CONF.decimate_threshold; threshold = threshold > 0 ? threshold : config.decimate_threshold;
precision = precision >= 0 ? precision : CONF.precision_decimate; precision = precision >= 0 ? precision : config.precision_decimate;
maxpass = maxpass >= 0 ? maxpass : 10; maxpass = maxpass >= 0 ? maxpass : 10;
// decimate until all point spacing > precision // decimate until all point spacing > precision
@ -115,9 +112,9 @@
}); });
return parr; return parr;
} }
function pointsToVertices(points) { function pointsToVertices(points) {
let vertices = new Float32Array(points.length * 3), let vertices = new Float32Array(points.length * 3),
i = 0, vi = 0; i = 0, vi = 0;
while (i < points.length) { while (i < points.length) {
@ -126,6 +123,6 @@
vertices[vi++] = points[i++].z; vertices[vi++] = points[i++].z;
} }
return vertices; return vertices;
} }
})(); })();

File diff suppressed because it is too large Load diff

View file

@ -4,21 +4,24 @@
(function() { (function() {
if (!self.base) self.base = {}; const base = self.base;
if (self.base.polygons) return; if (base.polygons) return;
const BASE = self.base, const { util, config, newPoint } = base;
UTIL = BASE.util, const { sqr, numOrDefault } = util;
CONF = BASE.config,
DEG2RAD = Math.PI / 180, const DEG2RAD = Math.PI / 180,
ABS = Math.abs,
SQRT = Math.sqrt, SQRT = Math.sqrt,
SQR = UTIL.sqr, SQR = util.sqr,
NOKEY = BASE.key.NONE, ABS = Math.abs;
newPoint = BASE.newPoint,
numOrDefault = UTIL.numOrDefault;
const POLYS = BASE.polygons = { const clib = self.ClipperLib,
clip = clib.Clipper,
ctyp = clib.ClipType,
ptyp = clib.PolyType,
cfil = clib.PolyFillType;
const POLYS = base.polygons = {
rayIntersect, rayIntersect,
alignWindings, alignWindings,
setWinding, setWinding,
@ -44,37 +47,33 @@
cleanClipperTree, cleanClipperTree,
fingerprintCompare, fingerprintCompare,
fingerprint fingerprint
}; };
/** ****************************************************************** function setZ(polys, z) {
* Polygon array utility functions
******************************************************************* */
function setZ(polys, z) {
for (let poly of polys) { for (let poly of polys) {
poly.setZ(z); poly.setZ(z);
} }
return polys; return polys;
} }
function toClipper(polys = []) { function toClipper(polys = []) {
let out = []; let out = [];
for (let poly of polys) { for (let poly of polys) {
poly.toClipper(out); poly.toClipper(out);
} }
return out; return out;
} }
function fromClipperNode(tnode, z) { function fromClipperNode(tnode, z) {
let poly = BASE.newPolygon(); let poly = base.newPolygon();
for (let point of tnode.m_polygon) { for (let point of tnode.m_polygon) {
poly.push(BASE.pointFromClipper(point, z)); poly.push(base.pointFromClipper(point, z));
} }
poly.open = tnode.IsOpen; poly.open = tnode.IsOpen;
return poly; return poly;
}; };
function fromClipperTree(tnode, z, tops, parent, minarea) { function fromClipperTree(tnode, z, tops, parent, minarea) {
let poly, let poly,
polys = tops || [], polys = tops || [],
min = numOrDefault(minarea, 0.1); min = numOrDefault(minarea, 0.1);
@ -96,9 +95,9 @@
} }
return polys; return polys;
}; };
function fromClipperTreeUnion(tnode, z, minarea, tops, parent) { function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
let polys = tops || [], poly; let polys = tops || [], poly;
for (let child of tnode.m_Childs) { for (let child of tnode.m_Childs) {
@ -117,22 +116,19 @@
} }
return polys; return polys;
}; };
function cleanClipperTree(tree) {
let clib = self.ClipperLib,
clip = clib.Clipper;
function cleanClipperTree(tree) {
if (tree.m_Childs) if (tree.m_Childs)
for (let child of tree.m_Childs) { for (let child of tree.m_Childs) {
child.m_polygon = clip.CleanPolygon(child.m_polygon, CONF.clipperClean); child.m_polygon = clip.CleanPolygon(child.m_polygon, config.clipperClean);
cleanClipperTree(child.m_Childs); cleanClipperTree(child.m_Childs);
} }
return tree; return tree;
}; };
function filter(array, output, fn) { function filter(array, output, fn) {
for (let poly of array) { for (let poly of array) {
poly = fn(poly); poly = fn(poly);
if (poly) { if (poly) {
@ -144,13 +140,13 @@
} }
} }
return output; return output;
} }
function points(polys) { function points(polys) {
return polys.length ? polys.map(p => p.deepLength).reduce((a,v) => a+v) : 0; return polys.length ? polys.map(p => p.deepLength).reduce((a,v) => a+v) : 0;
} }
/** /**
* todo use clipper polytree? * todo use clipper polytree?
* *
* use bounding boxes and sliceIntersection * use bounding boxes and sliceIntersection
@ -162,7 +158,7 @@
* @param {boolean} opentop prevent open polygons from having inners * @param {boolean} opentop prevent open polygons from having inners
* @returns {Polygon[]} top level parent polygons * @returns {Polygon[]} top level parent polygons
*/ */
function nest(polygons, deep, opentop) { function nest(polygons, deep, opentop) {
if (!polygons) { if (!polygons) {
return polygons; return polygons;
} }
@ -216,9 +212,9 @@
} }
} }
return tops; return tops;
} }
/** /**
* sets windings for parents one way * sets windings for parents one way
* and children in opposition * and children in opposition
* *
@ -226,7 +222,7 @@
* @param {boolean} CW * @param {boolean} CW
* @param {boolean} [recurse] * @param {boolean} [recurse]
*/ */
function setWinding(array, CW, recurse) { function setWinding(array, CW, recurse) {
if (!array) return; if (!array) return;
let poly, i = 0; let poly, i = 0;
while (i < array.length) { while (i < array.length) {
@ -234,16 +230,16 @@
if (poly.isClockwise() !== CW) poly.reverse(); if (poly.isClockwise() !== CW) poly.reverse();
if (recurse && poly.inner) setWinding(poly.inner, !CW, false); if (recurse && poly.inner) setWinding(poly.inner, !CW, false);
} }
} }
/** /**
* ensure all polygons have the same winding direction. * ensure all polygons have the same winding direction.
* try to use reversals that touch the fewest nodes. * try to use reversals that touch the fewest nodes.
* *
* @param {Polygon[]} polys * @param {Polygon[]} polys
* @return {boolean} true if aligned clockwise * @return {boolean} true if aligned clockwise
*/ */
function alignWindings(polys) { function alignWindings(polys) {
let len = polys.length, let len = polys.length,
fwd = 0, fwd = 0,
pts = 0, pts = 0,
@ -262,25 +258,25 @@
if (poly.isClockwise() != setCW) poly.reverse(); if (poly.isClockwise() != setCW) poly.reverse();
} }
return setCW; return setCW;
} }
function setContains(setA, poly) { function setContains(setA, poly) {
for (let i=0; i<setA.length; i++) { for (let i=0; i<setA.length; i++) {
if (setA[i].contains(poly)) return true; if (setA[i].contains(poly)) return true;
} }
return false; return false;
} }
function flatten(polys, to, crush) { function flatten(polys, to, crush) {
to = to || []; to = to || [];
polys.forEach(function(poly) { polys.forEach(function(poly) {
poly.flattenTo(to); poly.flattenTo(to);
if (crush) poly.inner = null; if (crush) poly.inner = null;
}); });
return to; return to;
} }
/** /**
* Diff two sets of polygons and return A-B, B-A. * Diff two sets of polygons and return A-B, B-A.
* no polygons in a given set can overlap ... only between sets * no polygons in a given set can overlap ... only between sets
* *
@ -292,7 +288,7 @@
* @param {number} [minArea] * @param {number} [minArea]
* @returns {Polygon[]} out * @returns {Polygon[]} out
*/ */
function subtract(setA, setB, outA, outB, z, minArea, opt = {}) { function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
let min = minArea || 0.1, let min = minArea || 0.1,
out = []; out = [];
@ -326,11 +322,7 @@
outB.appendAll(filter(oB)); outB.appendAll(filter(oB));
} }
} else { } else {
let clib = self.ClipperLib, let clip = new clib.Clipper(),
ctyp = clib.ClipType,
ptyp = clib.PolyType,
cfil = clib.PolyFillType,
clip = new clib.Clipper(),
ctre = new clib.PolyTree(), ctre = new clib.PolyTree(),
sp1 = toClipper(setA), sp1 = toClipper(setA),
sp2 = toClipper(setB); sp2 = toClipper(setB);
@ -364,9 +356,9 @@
} }
return out; return out;
} }
/** /**
* Slice.doProjectedFills() * Slice.doProjectedFills()
* Print.init w/ brims * Print.init w/ brims
* *
@ -419,16 +411,12 @@
return uset; return uset;
} }
/** /**
* @param {Polygon} poly clipping mask * @param {Polygon} poly clipping mask
* @returns {?Polygon[]} * @returns {?Polygon[]}
*/ */
function diff(setA, setB, z) { function diff(setA, setB, z) {
let clib = self.ClipperLib, let clip = new clib.Clipper(),
ctyp = clib.ClipType,
ptyp = clib.PolyType,
cfil = clib.PolyFillType,
clip = new clib.Clipper(),
ctre = new clib.PolyTree(), ctre = new clib.PolyTree(),
sp1 = toClipper(setA), sp1 = toClipper(setA),
sp2 = toClipper(setB); sp2 = toClipper(setB);
@ -441,45 +429,44 @@
} else { } else {
return null; return null;
} }
}; };
/** /**
* Slice.doProjectedFills() * Slice.doProjectedFills()
* *
* @param {Polygon[]} setA source set * @param {Polygon[]} setA source set
* @param {Polygon[]} setB mask set * @param {Polygon[]} setB mask set
* @returns {Polygon[]} * @returns {Polygon[]}
*/ */
function trimTo(setA, setB) { function trimTo(setA, setB) {
// handle null/empty slices // handle null/empty slices
if (setA === setB || setA === null || setB === null) return null; if (setA === setB || setA === null || setB === null) return null;
let out = [], tmp; let out = [], tmp;
UTIL.doCombinations(setA, setB, {}, function(a, b) { util.doCombinations(setA, setB, {}, function(a, b) {
if (tmp = a.mask(b)) { if (tmp = a.mask(b)) {
out.appendAll(tmp); out.appendAll(tmp);
} }
}); });
return out; return out;
} }
function sumCirc(polys) { function sumCirc(polys) {
let sum = 0.0; let sum = 0.0;
polys.forEach(function(poly) { polys.forEach(function(poly) {
sum += poly.circularityDeep(); sum += poly.circularityDeep();
}); });
return sum; return sum;
} }
/** /**
* @param {Polygon[]} polys * @param {Polygon[]} polys
* @param {number} distance offset * @param {number} distance offset
* @param {number} [z] defaults to 0 * @param {number} [z] defaults to 0
*/ */
function expand_lines(poly, distance, z) { function expand_lines(poly, distance, z) {
let fact = CONF.clipper, let fact = config.clipper,
clib = self.ClipperLib,
cjnt = clib.JoinType, cjnt = clib.JoinType,
cety = clib.EndType, cety = clib.EndType,
coff = new clib.ClipperOffset(), coff = new clib.ClipperOffset(),
@ -489,9 +476,9 @@
coff.Execute(ctre, distance * fact); coff.Execute(ctre, distance * fact);
return fromClipperTree(ctre, z, null, null, 0); return fromClipperTree(ctre, z, null, null, 0);
} }
/** /**
* @param {Polygon[]} polys * @param {Polygon[]} polys
* @param {number} distance offset * @param {number} distance offset
* @param {number} [z] defaults to 0 * @param {number} [z] defaults to 0
@ -501,18 +488,18 @@
* @param {Function} [collector] receives output of each pass * @param {Function} [collector] receives output of each pass
* @returns {Polygon[]} last offset * @returns {Polygon[]} last offset
*/ */
function expand(polys, distance, z, out, count, distance2, collector, min) { function expand(polys, distance, z, out, count, distance2, collector, min) {
return offset(polys, [distance, distance2 || distance], { return offset(polys, [distance, distance2 || distance], {
z, outs: out, call: collector, minArea: min, count, flat: true z, outs: out, call: collector, minArea: min, count, flat: true
}); });
} }
/** /**
* offset an array of polygons by distance with options to recurse * offset an array of polygons by distance with options to recurse
* and return resulting gaps from offsets for thin wall detection in * and return resulting gaps from offsets for thin wall detection in
* in FDM mode and uncleared areas in CAM mode. * in FDM mode and uncleared areas in CAM mode.
*/ */
function offset(polys, dist, opts = {}) { function offset(polys, dist, opts = {}) {
// do not offset open lines // do not offset open lines
polys = polys.filter(p => !p.open); polys = polys.filter(p => !p.open);
@ -529,9 +516,9 @@
depth = numOrDefault(opts.depth, 0), depth = numOrDefault(opts.depth, 0),
clean = opts.clean !== false, clean = opts.clean !== false,
simple = opts.simple !== false, simple = opts.simple !== false,
fill = numOrDefault(opts.fill, ClipperLib.PolyFillType.pftNonZero), fill = numOrDefault(opts.fill, clib.PolyFillType.pftNonZero),
join = numOrDefault(opts.join, ClipperLib.JoinType.jtMiter), join = numOrDefault(opts.join, clib.JoinType.jtMiter),
type = numOrDefault(opts.type, ClipperLib.EndType.etClosedPolygon), type = numOrDefault(opts.type, clib.EndType.etClosedPolygon),
// if dist is array with values, shift out next offset // if dist is array with values, shift out next offset
offs = Array.isArray(dist) ? (dist.length > 1 ? dist.shift() : dist[0]) : dist, offs = Array.isArray(dist) ? (dist.length > 1 ? dist.shift() : dist[0]) : dist,
mina = numOrDefault(opts.minArea, 0.1), mina = numOrDefault(opts.minArea, 0.1),
@ -539,26 +526,26 @@
if (opts.wasm && geo.wasm) { if (opts.wasm && geo.wasm) {
try { try {
polys = geo.wasm.js.offset(polys, offs, zed, clean ? CONF.clipperClean : 0, simple ? 1 : 0); polys = geo.wasm.js.offset(polys, offs, zed, clean ? config.clipperClean : 0, simple ? 1 : 0);
} catch (e) { } catch (e) {
console.log('wasm error', e.message || e); console.log('wasm error', e.message || e);
opts.wasm = false; opts.wasm = false;
return offset(polys, dist, opts); return offset(polys, dist, opts);
} }
} else { } else {
let coff = new ClipperLib.ClipperOffset(opts.miter, opts.arc), let coff = new clib.ClipperOffset(opts.miter, opts.arc),
ctre = new ClipperLib.PolyTree(); ctre = new clib.PolyTree();
// setup offset // setup offset
for (let poly of polys) { for (let poly of polys) {
// convert to clipper format // convert to clipper format
poly = poly.toClipper(); poly = poly.toClipper();
if (clean) poly = ClipperLib.Clipper.CleanPolygons(poly, CONF.clipperClean); if (clean) poly = clib.Clipper.CleanPolygons(poly, config.clipperClean);
if (simple) poly = ClipperLib.Clipper.SimplifyPolygons(poly, fill); if (simple) poly = clib.Clipper.SimplifyPolygons(poly, fill);
coff.AddPaths(poly, join, type); coff.AddPaths(poly, join, type);
} }
// perform offset // perform offset
coff.Execute(ctre, offs * CONF.clipper); coff.Execute(ctre, offs * config.clipper);
// convert back from clipper output format // convert back from clipper output format
polys = fromClipperTree(ctre, zed, null, null, mina); polys = fromClipperTree(ctre, zed, null, null, mina);
} }
@ -600,14 +587,14 @@
} }
return opts.flat ? opts.outs : polys; return opts.flat ? opts.outs : polys;
} }
/** /**
* progressive insetting that does inset + outset to debur as well * progressive insetting that does inset + outset to debur as well
* as performing subtractive analysis between initial layer shell (ref) * as performing subtractive analysis between initial layer shell (ref)
* and last offset (cmp) to produce gap candidates (for thinfill) * and last offset (cmp) to produce gap candidates (for thinfill)
*/ */
function inset(polys, dist, count, z, wasm) { function inset(polys, dist, count, z, wasm) {
let total = count; let total = count;
let layers = []; let layers = [];
let ref = polys; let ref = polys;
@ -637,9 +624,9 @@
ref = off; ref = off;
} }
return layers; return layers;
} }
/** /**
* todo use clipper opne poly clipping? * todo use clipper opne poly clipping?
* *
* @param {Polygon[]} polys * @param {Polygon[]} polys
@ -650,7 +637,7 @@
* @param {number} [maxLen] * @param {number} [maxLen]
* @returns {Point[]} supplied output or new array * @returns {Point[]} supplied output or new array
*/ */
function fillArea(polys, angle, spacing, output, minLen, maxLen) { function fillArea(polys, angle, spacing, output, minLen, maxLen) {
if (polys.length === 0) return; if (polys.length === 0) return;
let i = 1, let i = 1,
@ -665,7 +652,7 @@
while (angle > 90) angle -= 180; while (angle > 90) angle -= 180;
// X,Y ray slope derived from angle // X,Y ray slope derived from angle
raySlope = BASE.newSlope(0,0, raySlope = base.newSlope(0,0,
Math.cos(angle * DEG2RAD) * spacing, Math.cos(angle * DEG2RAD) * spacing,
Math.sin(angle * DEG2RAD) * spacing Math.sin(angle * DEG2RAD) * spacing
); );
@ -685,14 +672,10 @@
step = SQRT(SQR(stepX) + SQR(stepY)), step = SQRT(SQR(stepX) + SQR(stepY)),
steps = dist / step, steps = dist / step,
start = angle < 0 ? { x:bounds.minx, y:bounds.miny, z:zpos } : { x:bounds.maxx, y:bounds.miny, z:zpos }, start = angle < 0 ? { x:bounds.minx, y:bounds.miny, z:zpos } : { x:bounds.maxx, y:bounds.miny, z:zpos },
clib = self.ClipperLib,
ctyp = clib.ClipType,
ptyp = clib.PolyType,
cfil = clib.PolyFillType,
clip = new clib.Clipper(), clip = new clib.Clipper(),
ctre = new clib.PolyTree(), ctre = new clib.PolyTree(),
minlen = BASE.config.clipper * (minLen || 0), minlen = base.config.clipper * (minLen || 0),
maxlen = BASE.config.clipper * (maxLen || 0), maxlen = base.config.clipper * (maxLen || 0),
lines = []; lines = [];
// store origin as start/affinity point for fill // store origin as start/affinity point for fill
@ -701,11 +684,11 @@
for (i = 0; i < steps; i++) { for (i = 0; i < steps; i++) {
lines.push([ lines.push([
{ {
X: (start.x - raySlope.dx * 1000) * CONF.clipper, X: (start.x - raySlope.dx * 1000) * config.clipper,
Y: (start.y - raySlope.dy * 1000) * CONF.clipper Y: (start.y - raySlope.dy * 1000) * config.clipper
},{ },{
X: (start.x + raySlope.dx * 1000) * CONF.clipper, X: (start.x + raySlope.dx * 1000) * config.clipper,
Y: (start.y + raySlope.dy * 1000) * CONF.clipper Y: (start.y + raySlope.dy * 1000) * config.clipper
} }
]); ]);
start.x += stepX; start.x += stepX;
@ -724,8 +707,8 @@
if (minlen && plen < minlen) continue; if (minlen && plen < minlen) continue;
if (maxlen && plen > maxlen) continue; if (maxlen && plen > maxlen) continue;
} }
let p1 = BASE.pointFromClipper(poly.m_polygon[0], zpos); let p1 = base.pointFromClipper(poly.m_polygon[0], zpos);
let p2 = BASE.pointFromClipper(poly.m_polygon[1], zpos); let p2 = base.pointFromClipper(poly.m_polygon[1], zpos);
let od = rayint.origin.distToLineNew(p1,p2) / spacing; let od = rayint.origin.distToLineNew(p1,p2) / spacing;
lines.push([p1, p2, od]); lines.push([p1, p2, od]);
} }
@ -744,9 +727,9 @@
} }
return rayint; return rayint;
} }
/** /**
* tracing a ray through a slice's polygons, find and return * tracing a ray through a slice's polygons, find and return
* a sorted list of all intersecting points. * a sorted list of all intersecting points.
* *
@ -756,11 +739,11 @@
* @param {boolean} [for_fill] * @param {boolean} [for_fill]
* @returns {Point[]} * @returns {Point[]}
*/ */
function rayIntersect(start, slope, polygons, for_fill) { function rayIntersect(start, slope, polygons, for_fill) {
let i = 0, let i = 0,
flat = [], flat = [],
points = [], points = [],
conf = BASE.config, conf = base.config,
merge_dist = for_fill ? conf.precision_fill_merge : conf.precision_merge; merge_dist = for_fill ? conf.precision_fill_merge : conf.precision_merge;
// todo use new flatten() function above // todo use new flatten() function above
polygons.forEach(function(p) { polygons.forEach(function(p) {
@ -773,7 +756,7 @@
pl = pp.length;; pl = pp.length;;
for (let j = 0; j < pl; j++) { for (let j = 0; j < pl; j++) {
let j2 = (j + 1) % pl, let j2 = (j + 1) % pl,
ip = UTIL.intersectRayLine(start, slope, pp[j], pp[j2]); ip = util.intersectRayLine(start, slope, pp[j], pp[j2]);
if (ip) { if (ip) {
// add group object to point for cull detection // add group object to point for cull detection
ip.group = polygon; ip.group = polygon;
@ -819,7 +802,7 @@
* and test intersect the ray with that line. if it intersects, the points are * and test intersect the ray with that line. if it intersects, the points are
* inside and we keep one of them. otherwise, they are outside and we drop both. * inside and we keep one of them. otherwise, they are outside and we drop both.
*/ */
if (!UTIL.intersectRayLine(start, slope, line[0], line[1])) { if (!util.intersectRayLine(start, slope, line[0], line[1])) {
del = true; del = true;
p1.del = true; p1.del = true;
p2.del = true; p2.del = true;
@ -883,9 +866,9 @@
} }
} }
return points; return points;
} }
function fingerprint(polys) { function fingerprint(polys) {
let finger = []; let finger = [];
flatten(polys).sort((a,b) => { flatten(polys).sort((a,b) => {
return a.area() > b.area(); return a.area() > b.area();
@ -897,10 +880,10 @@
}); });
}); });
return finger; return finger;
} }
// compare fingerprint arrays // compare fingerprint arrays
function fingerprintCompare(a, b) { function fingerprintCompare(a, b) {
// true if array is the same object // true if array is the same object
if (a === b) { if (a === b) {
return true; return true;
@ -930,11 +913,11 @@
} }
} }
return true; return true;
} }
// plan a route through an array of polygon center points // plan a route through an array of polygon center points
// starting with the polygon center closest to "start" // starting with the polygon center closest to "start"
function route(polys, start) { function route(polys, start) {
let centers = []; let centers = [];
let first, minDist = Infinity; let first, minDist = Infinity;
for (let poly of polys) { for (let poly of polys) {
@ -969,6 +952,6 @@
} }
} }
return routed.map(r => r.poly); return routed.map(r => r.poly);
} }
})(); })();

View file

@ -1,104 +0,0 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
"use strict";
(function() {
if (!self.base) self.base = {};
if (self.base.render) return;
const BASE = self.base,
materialCache = {},
line_width = 1;
BASE.render = {
wireframe : wireframe
};
/** ******************************************************************
* Render Functions
******************************************************************* */
/**
* render triangle array as line segments. use of {@link newOrderedLine}
* allows lines to be cached by normalizing key order.
*
* @param {THREE.Group} group
* @param {Point[]} points
* @param {number} color
* @returns {THREE.Line}
*/
function wireframe(group, points, color) {
if (points.length % 3 != 0) throw "invalid line : "+points.length;
let lines = new THREE.BufferGeometry(),
hash = {},
added = 0,
vertices = [];
for (let i = 0; i < points.length; i += 3) {
let p1 = points[i],
p2 = points[i + 1],
p3 = points[i + 2],
l1 = newOrderedLine(p1, p2),
l2 = newOrderedLine(p2, p3),
l3 = newOrderedLine(p3, p1);
if (!hash[l1.key]) {
vertices.appendAll([
p1.x, p1.y, p1.z,
p2.x, p2.y, p2.z
]);
hash[l1.key] = ++added;
}
if (!hash[l2.key]) {
vertices.appendAll([
p2.x, p2.y, p2.z,
p3.x, p3.y, p3.z
]);
hash[l2.key] = ++added;
}
if (!hash[l3.key]) {
vertices.appendAll([
p3.x, p3.y, p3.z,
p1.x, p1.y, p1.z
]);
hash[l3.key] = ++added;
}
}
lines.setAttribute('position', new THREE.BufferAttribute( vertices.toFloat32(), 3 ) );
let mesh = new THREE.LineSegments(lines, getMaterial(color));
group.add(mesh);
return mesh;
}
/** ******************************************************************
* Connect to base and Helpers
******************************************************************* */
/**
* @param {number} color
* @returns {THREE.LineBasicMaterial}
*/
function getMaterial(color) {
let material = materialCache[color];
if (!material) {
material = new THREE.LineBasicMaterial({
fog:false,
color: color,
linewidth: line_width
});
materialCache[color] = material;
}
return material;
}
/**
* required for line caching in {@link base.render.wireframe}
*
* @param {Point} p1
* @param {Point} p2
* @returns {Line}
*/
function newOrderedLine(p1, p2) {
return p1.key < p2.key ? BASE.newLine(p1, p2) : BASE.newLine(p2, p1);
}
})();

View file

@ -8,17 +8,17 @@
*/ */
(function() { (function() {
let base = self.base; const base = self.base;
if (base.slice) return; if (base.slice) return;
let { config, util, polygons } = base const { config, util, polygons } = base
let { newOrderedLine, newPolygon, newPoint } = base; const { newOrderedLine, newPolygon, newPoint } = base;
function dval(v, dv) { function dval(v, dv) {
return v !== undefined ? v : dv; return v !== undefined ? v : dv;
} }
/** /**
* Given an array of points as triples, a bounding box and a set of * Given an array of points as triples, a bounding box and a set of
* slicing controls, emit an array of Slice objects to the ondone() * slicing controls, emit an array of Slice objects to the ondone()
* function. onupdate() will be called with two parameters (% completion * function. onupdate() will be called with two parameters (% completion
@ -27,7 +27,7 @@
* @param {Point[]} points vertex array * @param {Point[]} points vertex array
* @param {Object} options slicing parameters * @param {Object} options slicing parameters
*/ */
async function slice(points, options = {}) { async function slice(points, options = {}) {
let zMin = options.zmin || 0, let zMin = options.zmin || 0,
zMax = options.zmax || 0, zMax = options.zmax || 0,
zInc = options.zinc || 0, zInc = options.zinc || 0,
@ -205,9 +205,9 @@
slices = slices.sort((a,b) => a.z - b.z); slices = slices.sort((a,b) => a.z - b.z);
return { slices, points, zMin, zMax, zIndexes, zFlat }; return { slices, points, zMin, zMax, zIndexes, zFlat };
} }
/** /**
* given a point, append to the correct * given a point, append to the correct
* 'where' objec tarray (on, over or under) * 'where' objec tarray (on, over or under)
* *
@ -215,7 +215,7 @@
* @param {number} z offset * @param {number} z offset
* @param {Obejct} where * @param {Obejct} where
*/ */
function checkUnderOverOn(p, z, where) { function checkUnderOverOn(p, z, where) {
let delta = p.z - z; let delta = p.z - z;
if (Math.abs(delta) < config.precision_slice_z) { // on if (Math.abs(delta) < config.precision_slice_z) { // on
where.on.push(p); where.on.push(p);
@ -224,9 +224,9 @@
} else { // over } else { // over
where.over.push(p); where.over.push(p);
} }
} }
/** /**
* Given a point over and under a z offset, calculate * Given a point over and under a z offset, calculate
* and return the intersection point on that z plane * and return the intersection point on that z plane
* *
@ -235,7 +235,7 @@
* @param {number} z offset * @param {number} z offset
* @returns {Point} intersection point * @returns {Point} intersection point
*/ */
function intersectPoints(over, under, z) { function intersectPoints(over, under, z) {
let ip = []; let ip = [];
for (let i = 0; i < over.length; i++) { for (let i = 0; i < over.length; i++) {
for (let j = 0; j < under.length; j++) { for (let j = 0; j < under.length; j++) {
@ -243,21 +243,21 @@
} }
} }
return ip; return ip;
} }
/** /**
* Ensure points are unique with a cache/key algorithm * Ensure points are unique with a cache/key algorithm
*/ */
function getCachedPoint(phash, p) { function getCachedPoint(phash, p) {
let cached = phash[p.key]; let cached = phash[p.key];
if (!cached) { if (!cached) {
phash[p.key] = p; phash[p.key] = p;
return p; return p;
} }
return cached; return cached;
} }
/** /**
* Given two points and hints about their edges, * Given two points and hints about their edges,
* return a new Line object with points sorted * return a new Line object with points sorted
* lexicographically by key. This allows for future * lexicographically by key. This allows for future
@ -270,22 +270,22 @@
* @param {boolean} [edge] * @param {boolean} [edge]
* @returns {Line} * @returns {Line}
*/ */
function makeZLine(phash, p1, p2, coplanar, edge) { function makeZLine(phash, p1, p2, coplanar, edge) {
p1 = getCachedPoint(phash, p1); p1 = getCachedPoint(phash, p1);
p2 = getCachedPoint(phash, p2); p2 = getCachedPoint(phash, p2);
let line = newOrderedLine(p1,p2); let line = newOrderedLine(p1,p2);
line.coplanar = coplanar || false; line.coplanar = coplanar || false;
line.edge = edge || false; line.edge = edge || false;
return line; return line;
} }
/** /**
* process a single z-slice on a single mesh and * process a single z-slice on a single mesh and
* add to slices array * add to slices array
* *
* @param {number} z * @param {number} z
*/ */
async function sliceZ(z, points, options = {}) { async function sliceZ(z, points, options = {}) {
let { zMin, zMax, under, over, both } = options, let { zMin, zMax, under, over, both } = options,
groupFn = dval(options.groupr, both ? null : sliceConnect), groupFn = dval(options.groupr, both ? null : sliceConnect),
phash = {}, phash = {},
@ -347,9 +347,9 @@
if (groupFn) rval.groups = groupFn(lines, z, options); if (groupFn) rval.groups = groupFn(lines, z, options);
return rval; return rval;
} }
/** /**
* Given an array of input lines (line soup), find the path through * Given an array of input lines (line soup), find the path through
* joining line ends that encompasses the greatest area without self * joining line ends that encompasses the greatest area without self
* interesection. Eliminate used points and repeat. Unjoined lines * interesection. Eliminate used points and repeat. Unjoined lines
@ -359,7 +359,7 @@
* @param {number} [index] * @param {number} [index]
* @returns {Array} * @returns {Array}
*/ */
function sliceConnect(input, z, opt = {}) { function sliceConnect(input, z, opt = {}) {
let { debug, both } = opt; let { debug, both } = opt;
if (both) { if (both) {
@ -669,9 +669,9 @@
if (debug && emitted < points.length) console.log({ leftovers:points.length - emitted }); if (debug && emitted < points.length) console.log({ leftovers:points.length - emitted });
return output; return output;
} }
/** /**
* eliminate duplicate lines and interior-only lines (coplanar) * eliminate duplicate lines and interior-only lines (coplanar)
* *
* lines are sorted using lexicographic point keys such that * lines are sorted using lexicographic point keys such that
@ -682,7 +682,7 @@
* @param {Line[]} lines * @param {Line[]} lines
* @returns {Line[]} * @returns {Line[]}
*/ */
function removeDuplicateLines(lines) { function removeDuplicateLines(lines) {
let output = [], let output = [],
tmplines = [], tmplines = [],
points = [], points = [],
@ -771,9 +771,9 @@
} }
return output; return output;
} }
base.slice = slice; base.slice = slice;
base.sliceZ = sliceZ; base.sliceZ = sliceZ;
})(); })();

View file

@ -4,147 +4,105 @@
(function() { (function() {
if (self.base.Slope) return; const base = self.base;
if (base.Slope) return;
const BASE = self.base, const { config } = base;
CONF = BASE.config, const ABS = Math.abs,
ABS = Math.abs,
PRO = Slope.prototype,
DEG2RAD = Math.PI / 180, DEG2RAD = Math.PI / 180,
RAD2DEG = 180 / Math.PI; RAD2DEG = 180 / Math.PI;
BASE.Slope = Slope; class Slope {
BASE.newSlope = newSlope; constructor(p1, p2, dx, dy) {
BASE.newSlopeFromAngle = function(angle) {
return newSlope(0,0,
Math.cos(angle * DEG2RAD),
Math.sin(angle * DEG2RAD)
);
};
/**
*
* @param p1
* @param p2
* @param dx
* @param dy
* @constructor
*/
function Slope(p1, p2, dx, dy) {
this.dx = p1 && p2 ? p2.x - p1.x : dx; this.dx = p1 && p2 ? p2.x - p1.x : dx;
this.dy = p1 && p2 ? p2.y - p1.y : dy; this.dy = p1 && p2 ? p2.y - p1.y : dy;
this.angle = Math.atan2(this.dy, this.dx) * RAD2DEG; this.angle = Math.atan2(this.dy, this.dx) * RAD2DEG;
} }
/** ****************************************************************** toString() {
* Slope Prototype Functions
******************************************************************* */
PRO.toString = function() {
return [this.dx, this.dy, this.angle].join(','); return [this.dx, this.dy, this.angle].join(',');
}; }
PRO.clone = function() { clone() {
return new Slope(null, null, this.dx, this.dy); return new Slope(null, null, this.dx, this.dy);
}; }
/** isSame(s) {
* @param {Slope} s
* @returns {boolean}
*/
PRO.isSame = function(s) {
// if very close to vertical or horizontal, they're the same // if very close to vertical or horizontal, they're the same
if (ABS(this.dx) <= CONF.precision_merge && ABS(s.dx) <= CONF.precision_merge) return true; if (ABS(this.dx) <= config.precision_merge && ABS(s.dx) <= config.precision_merge) return true;
if (ABS(this.dy) <= CONF.precision_merge && ABS(s.dy) <= CONF.precision_merge) return true; if (ABS(this.dy) <= config.precision_merge && ABS(s.dy) <= config.precision_merge) return true;
// check angle within a range // check angle within a range
let prec = Math.min(1/Math.sqrt(this.dx * this.dx + this.dy * this.dy), CONF.precision_slope_merge); let prec = Math.min(1/Math.sqrt(this.dx * this.dx + this.dy * this.dy), config.precision_slope_merge);
return angleWithinDelta(this.angle, s.angle, prec || CONF.precision_slope); return angleWithinDelta(this.angle, s.angle, prec || config.precision_slope);
}; }
/** normal() {
* turn slope 90 degrees
*
* @returns {Slope}
*/
PRO.normal = function() {
let t = this.dx; let t = this.dx;
this.dx = -this.dy; this.dx = -this.dy;
this.dy = t; this.dy = t;
this.angle = Math.atan2(this.dy, this.dx) * RAD2DEG; this.angle = Math.atan2(this.dy, this.dx) * RAD2DEG;
return this; return this;
}; }
PRO.invert = function() { invert() {
this.dx = -this.dx; this.dx = -this.dx;
this.dy = -this.dy; this.dy = -this.dy;
this.angle = Math.atan2(this.dy, this.dx) * RAD2DEG; this.angle = Math.atan2(this.dy, this.dx) * RAD2DEG;
return this; return this;
} }
PRO.toUnit = function() { toUnit() {
let max = Math.max(ABS(this.dx), ABS(this.dy)); let max = Math.max(ABS(this.dx), ABS(this.dy));
this.dx = this.dx / max; this.dx = this.dx / max;
this.dy = this.dy / max; this.dy = this.dy / max;
return this; return this;
}; }
PRO.factor = function(f) { factor(f) {
this.dx *= f; this.dx *= f;
this.dy *= f; this.dy *= f;
return this; return this;
}; }
/** invert() {
* reverse (180 degree) slope
*
* @returns {Slope}
*/
PRO.invert = function() {
this.dx = -this.dx; this.dx = -this.dx;
this.dy = -this.dy; this.dy = -this.dy;
this.angle = 180 - this.angle; this.angle = 180 - this.angle;
return this; return this;
}; }
PRO.angleDiff = function(s2,sign) { angleDiff(s2,sign) {
const n1 = this.angle; const n1 = this.angle;
const n2 = s2.angle; const n2 = s2.angle;
let diff = n2 - n1; let diff = n2 - n1;
while (diff < -180) diff += 360; while (diff < -180) diff += 360;
while (diff > 180) diff -= 360; while (diff > 180) diff -= 360;
return sign ? diff : Math.abs(diff); return sign ? diff : Math.abs(diff);
}; }
}
/** ****************************************************************** /**
* Connect to base and Helpers
******************************************************************* */
/**
* returns true if the difference between a & b is less than v * returns true if the difference between a & b is less than v
*
* @param {number} a
* @param {number} b
* @param {number} v
* @returns {boolean}
*/ */
function minDeltaABS(a,b,v) { function minDeltaABS(a,b,v) {
return ABS(a-b) < v; return ABS(a-b) < v;
} }
function angleWithinDelta(a1, a2, delta) { function angleWithinDelta(a1, a2, delta) {
return (ABS(a1-a2) <= delta || 360-ABS(a1-a2) <= delta); return (ABS(a1-a2) <= delta || 360-ABS(a1-a2) <= delta);
} }
/** function newSlope(p1, p2, dx, dy) {
*
* @param p1
* @param p2
* @param dx
* @param dy
* @returns {Slope}
*/
function newSlope(p1, p2, dx, dy) {
return new Slope(p1, p2, dx, dy); return new Slope(p1, p2, dx, dy);
} }
base.Slope = Slope;
base.newSlope = newSlope;
base.newSlopeFromAngle = function(angle) {
return newSlope(0,0,
Math.cos(angle * DEG2RAD),
Math.sin(angle * DEG2RAD)
);
};
})(); })();

View file

@ -5,7 +5,7 @@
// only active in workers // only active in workers
if (!self.window) (function() { if (!self.window) (function() {
if (!self.geo) self.geo = { if (!self.geo) self.geo = {
enable, enable,
disable, disable,
count: { count: {
@ -13,24 +13,24 @@ if (!self.window) (function() {
union: 0, union: 0,
diff: 0 diff: 0
} }
}; };
const factor = self.base.config.clipper; const factor = self.base.config.clipper;
const geo = self.geo; const geo = self.geo;
function log() { function log() {
console.log(...arguments); console.log(...arguments);
} }
function writePolys(view, polys) { function writePolys(view, polys) {
let pcount = 0; let pcount = 0;
for (let poly of polys) { for (let poly of polys) {
pcount += writePoly(view, poly); pcount += writePoly(view, poly);
} }
return pcount; return pcount;
} }
function writePoly(view, poly, inner) { function writePoly(view, poly, inner) {
if (inner) { if (inner) {
poly.setCounterClockwise(); poly.setCounterClockwise();
} else { } else {
@ -51,9 +51,9 @@ if (!self.window) (function() {
} }
} }
return count; return count;
} }
function readPoly(view, z) { function readPoly(view, z) {
let points = view.readU16(true); let points = view.readU16(true);
if (points === 0) return; if (points === 0) return;
let poly = self.base.newPolygon(); let poly = self.base.newPolygon();
@ -61,9 +61,9 @@ if (!self.window) (function() {
poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0); poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0);
} }
return poly; return poly;
} }
function readPolys(view, z, out = []) { function readPolys(view, z, out = []) {
for (;;) { for (;;) {
let poly = readPoly(view, z); let poly = readPoly(view, z);
if (poly) { if (poly) {
@ -73,9 +73,9 @@ if (!self.window) (function() {
} }
} }
return out; return out;
} }
function polyOffset(polys, offset, z, clean, simple) { function polyOffset(polys, offset, z, clean, simple) {
geo.count.offset++; geo.count.offset++;
let wasm = geo.wasm, let wasm = geo.wasm,
buffer = geo.wasm.shared, buffer = geo.wasm.shared,
@ -83,9 +83,9 @@ if (!self.window) (function() {
resat = wasm.fn.offset(buffer, pcount, offset * factor, clean, simple), resat = wasm.fn.offset(buffer, pcount, offset * factor, clean, simple),
out = readPolys(new DataReader(wasm.heap, resat), z); out = readPolys(new DataReader(wasm.heap, resat), z);
return polyNest(out); return polyNest(out);
} }
function polyUnion(polys, z) { function polyUnion(polys, z) {
geo.count.union++; geo.count.union++;
let wasm = geo.wasm, let wasm = geo.wasm,
buffer = geo.wasm.shared, buffer = geo.wasm.shared,
@ -93,9 +93,9 @@ if (!self.window) (function() {
resat = wasm.fn.union(buffer, pcount), resat = wasm.fn.union(buffer, pcount),
out = readPolys(new DataReader(wasm.heap, resat), z); out = readPolys(new DataReader(wasm.heap, resat), z);
return polyNest(out); return polyNest(out);
} }
function polyDiff(polysA, polysB, z, AB, BA) { function polyDiff(polysA, polysB, z, AB, BA) {
geo.count.diff++; geo.count.diff++;
let wasm = geo.wasm, let wasm = geo.wasm,
buffer = geo.wasm.shared, buffer = geo.wasm.shared,
@ -110,10 +110,10 @@ if (!self.window) (function() {
if (BA) { if (BA) {
BA.appendAll(polyNest(readPolys(reader, z))); BA.appendAll(polyNest(readPolys(reader, z)));
} }
} }
// nest closed polygons without existing parent / child relationships // nest closed polygons without existing parent / child relationships
function polyNest(polys) { function polyNest(polys) {
polys.sort((a,b) => { polys.sort((a,b) => {
return b.bounds.minx - a.bounds.minx; return b.bounds.minx - a.bounds.minx;
}); });
@ -138,18 +138,18 @@ if (!self.window) (function() {
} }
} }
return tops; return tops;
} }
function readString(pos, len) { function readString(pos, len) {
let view = new DataReader(geo.wasm.heap, pos); let view = new DataReader(geo.wasm.heap, pos);
let out = []; let out = [];
while (len-- > 0) { while (len-- > 0) {
out.push(String.fromCharCode(view.readU8())); out.push(String.fromCharCode(view.readU8()));
} }
return out.join(''); return out.join('');
} }
function enable() { function enable() {
if (geo.wasm || geo._wasm) { if (geo.wasm || geo._wasm) {
return; return;
} }
@ -194,13 +194,13 @@ if (!self.window) (function() {
offset: polyOffset offset: polyOffset
}; };
}); });
} }
function disable() { function disable() {
if (geo.wasm) { if (geo.wasm) {
delete geo.wasm; delete geo.wasm;
// console.log({disabled: geo}); // console.log({disabled: geo});
} }
} }
})(); })();