add new path generator. let it bake for a while before removing the old one
This commit is contained in:
parent
956d1b1af0
commit
1d47ec9473
4 changed files with 328 additions and 25 deletions
242
src/geo/paths.js
242
src/geo/paths.js
|
|
@ -105,9 +105,249 @@ function poly2polyEmit(array, startPoint, emitter, opt = {}) {
|
|||
return startPoint;
|
||||
}
|
||||
|
||||
function calc_normal(p1, p2) {
|
||||
let dx = p2.x - p1.x;
|
||||
let dy = p2.y - p1.y;
|
||||
let len = Math.sqrt(dx * dx + dy * dy);
|
||||
let mn = (1 / len);
|
||||
dx *= mn;
|
||||
dy *= mn;
|
||||
return({ dx: dy, dy: -dx, p1, p2, len });
|
||||
}
|
||||
|
||||
function end_vertex(n1, n2, off, start) {
|
||||
let dx, dy;
|
||||
if (start) {
|
||||
dx = n2.dx * off;
|
||||
dy = n2.dy * off;
|
||||
} else {
|
||||
dx = n1.dx * off;
|
||||
dy = n1.dy * off;
|
||||
}
|
||||
return { dx, dy, vp: n1.p2 };
|
||||
}
|
||||
|
||||
function calc_vertex(n1, n2, off, vp) {
|
||||
let dx, dy, io, vl, q, r;
|
||||
r = 1 + (n1.dx * n2.dx + n1.dy * n2.dy);
|
||||
q = off / r;
|
||||
// handle spurs that switch back 180 degrees
|
||||
if (q === Infinity) {
|
||||
q = 0;
|
||||
}
|
||||
dx = (n1.dx + n2.dx) * q;
|
||||
dy = (n1.dy + n2.dy) * q;
|
||||
// io tells us whether we're turning left or right
|
||||
io = (n1.dx * n2.dy - n2.dx * n1.dy);
|
||||
// vertex length can be compared to the previons and next
|
||||
// segment lengths to see if we're highly acute
|
||||
vl = Math.sqrt(dx * dx + dy * dy);
|
||||
return { dx, dy, vp: vp || n1.p2, io, vl };
|
||||
}
|
||||
|
||||
function v2pl(rec) {
|
||||
let p = rec.vp.clone();
|
||||
p.x += rec.dx;
|
||||
p.y += rec.dy;
|
||||
p.vp = rec.vp;
|
||||
return p;
|
||||
}
|
||||
|
||||
function v2pr(rec) {
|
||||
let p = rec.vp.clone();
|
||||
p.x -= rec.dx;
|
||||
p.y -= rec.dy;
|
||||
p.vp = rec.vp;
|
||||
return p;
|
||||
}
|
||||
|
||||
function pointsToPath(points, offset, open, miter = 1.5) {
|
||||
const absoff = Math.abs(offset);
|
||||
// calculate segment normals which are used to calculate vertex normals
|
||||
// next segment info is associated with the current point
|
||||
const nupoints = [];
|
||||
for (let i=0, l=points.length; i<l; i++) {
|
||||
let p1 = points[i];
|
||||
let p2 = points[(i+1)%l];
|
||||
p1.normal = calc_normal(p1, p2);
|
||||
// drop next duplicate point if segment length is 0
|
||||
// is possible there are 3 dups in a row and this is
|
||||
// not handled. could make if a while, but that could
|
||||
// end up in a loop without additional checks
|
||||
if (p1.normal.len === 0) {
|
||||
p1.normal = calc_normal(p1, points[(i+2)%l]);
|
||||
i++;
|
||||
}
|
||||
nupoints.push(p1);
|
||||
}
|
||||
// when points are dropped, we need the new array
|
||||
points = nupoints;
|
||||
// generate left / right paths and triangle faces
|
||||
const left = [];
|
||||
const right = [];
|
||||
const faces = [];
|
||||
// calculate vertex normals from segments normals
|
||||
// vertex info is associated with the origin point
|
||||
let fl, fr;
|
||||
for (let i=0, l=points.length; i<l; i++) {
|
||||
let n1 = points[(i+l-1)%l].normal;
|
||||
let n2 = points[(i+l)%l].normal;
|
||||
let vn = open && (i === 0 || i === l-1) ?
|
||||
end_vertex(n1, n2, offset, i === 0) :
|
||||
calc_vertex(n1, n2, offset);
|
||||
let { p1, p2 } = n2;
|
||||
let { io, vl } = vn;
|
||||
if (offset < 0) {
|
||||
io = -io;
|
||||
}
|
||||
let split_left = false, split_right = false;
|
||||
if (io > 0) { // right
|
||||
split_left = vl > absoff * miter;
|
||||
split_right = vl > Math.min(n1.len, n2.len) + absoff;
|
||||
} else { // left
|
||||
split_right = vl > absoff * miter;
|
||||
split_left = vl > Math.min(n1.len, n2.len) + absoff;
|
||||
}
|
||||
let l0 = left.peek(1);
|
||||
let r0 = right.peek(1);
|
||||
if (split_left || split_right) {
|
||||
// shorten each leg and insert new point
|
||||
let delta = 0.1;
|
||||
let np1 = p1.clone().move({ x: n1.dy * delta, y: -n1.dx * delta, z: 0 });
|
||||
let np2 = p1.clone().move({ x:-n2.dy * delta, y: n2.dx * delta, z: 0 });
|
||||
let sn1 = np1.normal = calc_normal(np1, np2);
|
||||
let sn2 = np2.normal = p1.normal;
|
||||
let nv1 = calc_vertex(n1.p1.normal, sn1, offset, np1);
|
||||
let nv2 = calc_vertex(sn1, sn2, offset, np2);
|
||||
if (split_right) {
|
||||
right.push(v2pr(nv1), v2pr(nv2));
|
||||
} else {
|
||||
right.push(v2pr(vn));
|
||||
}
|
||||
if (split_left) {
|
||||
left.push(v2pl(nv1), v2pl(nv2));
|
||||
} else {
|
||||
left.push(v2pl(vn));
|
||||
}
|
||||
if (faces) {
|
||||
let l1 = left.peek(1);
|
||||
let r1 = right.peek(1);
|
||||
let l2 = left.peek(2);
|
||||
let r2 = right.peek(2);
|
||||
if (split_left && split_right) {
|
||||
faces.push(l1, l2, r1);
|
||||
faces.push(r2, r1, l2);
|
||||
fl = fl || l1;
|
||||
fr = fr || r2;
|
||||
} else if (split_left) {
|
||||
faces.push(l1, l2, r1);
|
||||
fl = fl || l2;
|
||||
fr = fr || r1;
|
||||
} else { // split right
|
||||
faces.push(r2, r1, l1);
|
||||
fl = fl || l1;
|
||||
fr = fr || r2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
left.push(v2pl(vn));
|
||||
right.push(v2pr(vn));
|
||||
fl = fl || left.peek(1);
|
||||
fr = fr || right.peek(1);
|
||||
}
|
||||
if (faces && l0 && r0) {
|
||||
let l1 = left.peek(split_left ? 2 : 1);
|
||||
let r1 = right.peek(split_right ? 2 : 1);
|
||||
faces.push(l1, l0, r1);
|
||||
faces.push(r0, r1, l0);
|
||||
}
|
||||
}
|
||||
if (open) {
|
||||
// move open ends by offset length
|
||||
const p0 = points[0].normal;
|
||||
const l0 = left[0];
|
||||
const r0 = right[0];
|
||||
l0.x += p0.dy * offset;
|
||||
l0.y -= p0.dx * offset;
|
||||
r0.x += p0.dy * offset;
|
||||
r0.y -= p0.dx * offset;
|
||||
const pn = points.peek(2).normal;
|
||||
const ln = left.peek();
|
||||
const rn = right.peek();
|
||||
ln.x -= pn.dy * offset;
|
||||
ln.y += pn.dx * offset;
|
||||
rn.x -= pn.dy * offset;
|
||||
rn.y += pn.dx * offset;
|
||||
}
|
||||
if (!open && faces) {
|
||||
let l1 = left.peek(1);
|
||||
let r1 = right.peek(1);
|
||||
faces.push(fl, l1, fr);
|
||||
faces.push(r1, fr, l1);
|
||||
}
|
||||
return { left, right, faces, open };
|
||||
}
|
||||
|
||||
function pathTo3D(path, height, z) {
|
||||
const { faces, left, right, open } = path;
|
||||
const out = [];
|
||||
if (z !== undefined) {
|
||||
for (let p of faces) {
|
||||
p.z = z;
|
||||
}
|
||||
}
|
||||
for (let p of faces) {
|
||||
out.push(p.x, p.y, p.z - height);
|
||||
}
|
||||
for (let p of faces.slice().reverse()) {
|
||||
out.push(p.x, p.y, p.z + height);
|
||||
}
|
||||
for (let i=0, l=left.length, tl = open ? l-1 : l; i<tl; i++) {
|
||||
let p0 = left[i];
|
||||
let p1 = left[(i+1)%l];
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
out.push(p0.x, p0.y, p0.z - height);
|
||||
out.push(p1.x, p1.y, p1.z - height);
|
||||
out.push(p1.x, p1.y, p1.z - height);
|
||||
out.push(p1.x, p1.y, p1.z + height);
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
}
|
||||
for (let i=0, l=right.length, tl = open ? l-1 : l; i<tl; i++) {
|
||||
let p0 = right[i];
|
||||
let p1 = right[(i+1)%l];
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
out.push(p1.x, p1.y, p1.z - height);
|
||||
out.push(p0.x, p0.y, p0.z - height);
|
||||
out.push(p1.x, p1.y, p1.z - height);
|
||||
out.push(p0.x, p0.y, p0.z + height);
|
||||
out.push(p1.x, p1.y, p1.z + height);
|
||||
}
|
||||
if (open) {
|
||||
let l0 = left[0];
|
||||
let r0 = right[0];
|
||||
out.push(l0.x, l0.y, l0.z + height);
|
||||
out.push(r0.x, r0.y, r0.z - height);
|
||||
out.push(l0.x, l0.y, l0.z - height);
|
||||
out.push(r0.x, r0.y, r0.z + height);
|
||||
out.push(r0.x, r0.y, r0.z - height);
|
||||
out.push(l0.x, l0.y, l0.z + height);
|
||||
let ln = left.peek();
|
||||
let rn = right.peek();
|
||||
out.push(ln.x, ln.y, ln.z + height);
|
||||
out.push(ln.x, ln.y, ln.z - height);
|
||||
out.push(rn.x, rn.y, rn.z - height);
|
||||
out.push(rn.x, rn.y, rn.z + height);
|
||||
out.push(ln.x, ln.y, ln.z + height);
|
||||
out.push(rn.x, rn.y, rn.z - height);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
base.paths = {
|
||||
poly2polyEmit,
|
||||
tip2tipEmit
|
||||
tip2tipEmit,
|
||||
pointsToPath,
|
||||
pathTo3D
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue