move legacy 3d path generator to geo.paths
This commit is contained in:
parent
1b283d5bb6
commit
bcc64baef8
3 changed files with 160 additions and 153 deletions
|
|
@ -356,9 +356,105 @@ function pathTo3D(path, height, z) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// produces indexed geometry which isn't ideal for rendering because
|
||||||
|
// the default threejs generated vertex normals aren't accurate
|
||||||
|
function shapeToPath(shape, points, closed) {
|
||||||
|
closed = closed !== undefined ? closed : true;
|
||||||
|
|
||||||
|
const profileGeometry = new THREE.ShapeGeometry(shape);
|
||||||
|
profileGeometry.rotateX(Math.PI * .5);
|
||||||
|
|
||||||
|
const profile = profileGeometry.attributes.position;
|
||||||
|
const faces = new Float32Array(profile.count * points.length * 3);
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const v1 = new THREE.Vector2().subVectors(points[i - 1 < 0 ? points.length - 1 : i - 1], points[i]);
|
||||||
|
const v2 = new THREE.Vector2().subVectors(points[i + 1 == points.length ? 0 : i + 1], points[i]);
|
||||||
|
const angle = v2.angle() - v1.angle();
|
||||||
|
const halfAngle = angle * .5;
|
||||||
|
let hA = halfAngle;
|
||||||
|
let tA = v2.angle() + Math.PI * .5;
|
||||||
|
|
||||||
|
if (!closed){
|
||||||
|
if (i == 0 || i == points.length - 1) {hA = Math.PI * .5;}
|
||||||
|
if (i == points.length - 1) {tA = v1.angle() - Math.PI * .5;}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shift = Math.tan(hA - Math.PI * .5);
|
||||||
|
const shiftMatrix = new THREE.Matrix4().set(
|
||||||
|
1, 0, 0, 0,
|
||||||
|
-shift, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
);
|
||||||
|
|
||||||
|
const tempAngle = tA;
|
||||||
|
const rotationMatrix = new THREE.Matrix4().set(
|
||||||
|
Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0,
|
||||||
|
Math.sin(tempAngle), Math.cos(tempAngle), 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
);
|
||||||
|
|
||||||
|
const translationMatrix = new THREE.Matrix4().set(
|
||||||
|
1, 0, 0, points[i].x,
|
||||||
|
0, 1, 0, points[i].y,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const cloneProfile = profile.clone();
|
||||||
|
cloneProfile.applyMatrix4(shiftMatrix);
|
||||||
|
cloneProfile.applyMatrix4(rotationMatrix);
|
||||||
|
cloneProfile.applyMatrix4(translationMatrix);
|
||||||
|
|
||||||
|
faces.set(cloneProfile.array, cloneProfile.count * i * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = [];
|
||||||
|
const lastCorner = closed == false ? points.length - 1: points.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < lastCorner; i++) {
|
||||||
|
for (let j = 0; j < profile.count; j++) {
|
||||||
|
const currCorner = i;
|
||||||
|
const nextCorner = i + 1 == points.length ? 0 : i + 1;
|
||||||
|
const currPoint = j;
|
||||||
|
const nextPoint = j + 1 == profile.count ? 0 : j + 1;
|
||||||
|
|
||||||
|
const a = nextPoint + profile.count * currCorner;
|
||||||
|
const b = currPoint + profile.count * currCorner;
|
||||||
|
const c = currPoint + profile.count * nextCorner;
|
||||||
|
const d = nextPoint + profile.count * nextCorner;
|
||||||
|
|
||||||
|
index.push(a, b, d);
|
||||||
|
index.push(b, c, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!closed) {
|
||||||
|
// cheating because we know the profile length is 4 (for now)
|
||||||
|
const p1 = 0 + profile.count * 0;
|
||||||
|
const p2 = 1 + profile.count * 0;
|
||||||
|
const p3 = 2 + profile.count * 0;
|
||||||
|
const p4 = 3 + profile.count * 0;
|
||||||
|
index.push(p1, p2, p3);
|
||||||
|
index.push(p1, p3, p4);
|
||||||
|
const lc = lastCorner;
|
||||||
|
const p5 = 0 + profile.count * lc;
|
||||||
|
const p6 = 1 + profile.count * lc;
|
||||||
|
const p7 = 2 + profile.count * lc;
|
||||||
|
const p8 = 3 + profile.count * lc;
|
||||||
|
index.push(p7, p6, p5);
|
||||||
|
index.push(p8, p7, p5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {index, faces};
|
||||||
|
}
|
||||||
|
|
||||||
base.paths = {
|
base.paths = {
|
||||||
poly2polyEmit,
|
poly2polyEmit,
|
||||||
tip2tipEmit,
|
tip2tipEmit,
|
||||||
|
shapeToPath,
|
||||||
pointsToPath,
|
pointsToPath,
|
||||||
pathTo3D
|
pathTo3D
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// dep: geo.base
|
// dep: geo.base
|
||||||
|
// dep: geo.paths
|
||||||
// dep: geo.polygon
|
// dep: geo.polygon
|
||||||
// dep: geo.polygons
|
// dep: geo.polygons
|
||||||
gapp.register("kiri.layers", [], (root, exports) => {
|
gapp.register("kiri.layers", [], (root, exports) => {
|
||||||
|
|
@ -219,7 +220,7 @@ class Layers {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const z = opts.z || poly.getZ();
|
const z = opts.z || poly.getZ();
|
||||||
const faces = poly.toPath3D(offset * 1, height, 0);
|
const faces = poly.toPath3D(offset, height, 0);
|
||||||
const cur = this.current;
|
const cur = this.current;
|
||||||
const one = cur.paths[0];
|
const one = cur.paths[0];
|
||||||
if (one) {
|
if (one) {
|
||||||
|
|
@ -251,65 +252,65 @@ class Layers {
|
||||||
}
|
}
|
||||||
this.stats.contour++;
|
this.stats.contour++;
|
||||||
}
|
}
|
||||||
return this;
|
} else {
|
||||||
}
|
const profiles = this.profiles;
|
||||||
|
const prokey = `${offset}x${height}`;
|
||||||
const profiles = this.profiles;
|
if (!profiles[prokey]) {
|
||||||
const prokey = `${offset}x${height}`;
|
const profile = new THREE.Shape();
|
||||||
if (!profiles[prokey]) {
|
profile.moveTo(-offset, -height);
|
||||||
const profile = new THREE.Shape();
|
profile.lineTo(-offset, height);
|
||||||
profile.moveTo(-offset, -height);
|
profile.lineTo( offset, height);
|
||||||
profile.lineTo(-offset, height);
|
profile.lineTo( offset, -height);
|
||||||
profile.lineTo( offset, height);
|
profiles[prokey] = profile;
|
||||||
profile.lineTo( offset, -height);
|
|
||||||
profiles[prokey] = profile;
|
|
||||||
}
|
|
||||||
const profile = profiles[prokey].clone();
|
|
||||||
|
|
||||||
polys.forEach(poly => {
|
|
||||||
const contour = [];
|
|
||||||
poly = poly.debur(0.05);
|
|
||||||
if (!poly) return;
|
|
||||||
poly = poly.miter();
|
|
||||||
poly.points.forEach(p => {
|
|
||||||
contour.push(new THREE.Vector2(p.x, p.y));
|
|
||||||
});
|
|
||||||
const {index, faces} = ProfiledContourGeometry(profile, contour, poly.isClosed());
|
|
||||||
const cur = this.current;
|
|
||||||
const one = cur.paths[0];
|
|
||||||
if (one) {
|
|
||||||
// merge all contour geometry for massive speed gain
|
|
||||||
// todo: recode to concat all geometries at once, and not incrementally
|
|
||||||
const add = one.faces.length / 3;
|
|
||||||
for (let i=0; i<index.length; i++) {
|
|
||||||
index[i] += add;
|
|
||||||
}
|
|
||||||
const feces = new Float32Array(one.faces.length + faces.length);
|
|
||||||
const indln = one.index.length || one.faces.length / 3;
|
|
||||||
feces.set(one.faces);
|
|
||||||
feces.set(faces, one.faces.length);
|
|
||||||
one.faces = feces;
|
|
||||||
one.index.appendAll(index);
|
|
||||||
// allow changing colors
|
|
||||||
if (opts.color) {
|
|
||||||
if (!cur.cpath) {
|
|
||||||
cur.cpath = [ Object.assign({ start: 0, count: indln - 1 }, cur.color) ];
|
|
||||||
}
|
|
||||||
// rewrite last color count if color or opacity have changed
|
|
||||||
const pc = cur.cpath[cur.cpath.length - 1];
|
|
||||||
if (pc.face !== opts.color.face || pc.opacity !== opts.color.opacity) {
|
|
||||||
pc.count = indln;
|
|
||||||
cur.cpath.push(Object.assign({ start: indln, count: Infinity }, opts.color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cur.paths.push({ index, faces, z: opts.z || poly.getZ() });
|
|
||||||
if (opts.color) {
|
|
||||||
cur.cpath = [ Object.assign({ start: 0, count: Infinity }, opts.color) ];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.stats.contour++;
|
const profile = profiles[prokey].clone();
|
||||||
});
|
|
||||||
|
polys.forEach(poly => {
|
||||||
|
const contour = [];
|
||||||
|
poly = poly.debur(0.05);
|
||||||
|
if (!poly) return;
|
||||||
|
poly = poly.miter();
|
||||||
|
poly.points.forEach(p => {
|
||||||
|
contour.push(new THREE.Vector2(p.x, p.y));
|
||||||
|
});
|
||||||
|
const {index, faces} = base.paths.shapeToPath(profile, contour, poly.isClosed());
|
||||||
|
const cur = this.current;
|
||||||
|
const one = cur.paths[0];
|
||||||
|
if (one) {
|
||||||
|
// merge all contour geometry for massive speed gain
|
||||||
|
// todo: recode to concat all geometries at once, and not incrementally
|
||||||
|
const add = one.faces.length / 3;
|
||||||
|
for (let i=0; i<index.length; i++) {
|
||||||
|
index[i] += add;
|
||||||
|
}
|
||||||
|
const feces = new Float32Array(one.faces.length + faces.length);
|
||||||
|
const indln = one.index.length || one.faces.length / 3;
|
||||||
|
feces.set(one.faces);
|
||||||
|
feces.set(faces, one.faces.length);
|
||||||
|
one.faces = feces;
|
||||||
|
one.index.appendAll(index);
|
||||||
|
// allow changing colors
|
||||||
|
if (opts.color) {
|
||||||
|
if (!cur.cpath) {
|
||||||
|
cur.cpath = [ Object.assign({ start: 0, count: indln - 1 }, cur.color) ];
|
||||||
|
}
|
||||||
|
// rewrite last color count if color or opacity have changed
|
||||||
|
const pc = cur.cpath[cur.cpath.length - 1];
|
||||||
|
if (pc.face !== opts.color.face || pc.opacity !== opts.color.opacity) {
|
||||||
|
pc.count = indln;
|
||||||
|
cur.cpath.push(Object.assign({ start: indln, count: Infinity }, opts.color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur.paths.push({ index, faces, z: opts.z || poly.getZ() });
|
||||||
|
if (opts.color) {
|
||||||
|
cur.cpath = [ Object.assign({ start: 0, count: Infinity }, opts.color) ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.stats.contour++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -322,100 +323,6 @@ function flat(polys) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProfiledContourGeometry(profileShape, contour, contourClosed) {
|
|
||||||
|
|
||||||
contourClosed = contourClosed !== undefined ? contourClosed : true;
|
|
||||||
|
|
||||||
const profileGeometry = new THREE.ShapeGeometry(profileShape);
|
|
||||||
profileGeometry.rotateX(Math.PI * .5);
|
|
||||||
|
|
||||||
const profile = profileGeometry.attributes.position;
|
|
||||||
const faces = new Float32Array(profile.count * contour.length * 3);
|
|
||||||
|
|
||||||
for (let i = 0; i < contour.length; i++) {
|
|
||||||
const v1 = new THREE.Vector2().subVectors(contour[i - 1 < 0 ? contour.length - 1 : i - 1], contour[i]);
|
|
||||||
const v2 = new THREE.Vector2().subVectors(contour[i + 1 == contour.length ? 0 : i + 1], contour[i]);
|
|
||||||
const angle = v2.angle() - v1.angle();
|
|
||||||
const halfAngle = angle * .5;
|
|
||||||
let hA = halfAngle;
|
|
||||||
let tA = v2.angle() + Math.PI * .5;
|
|
||||||
|
|
||||||
if (!contourClosed){
|
|
||||||
if (i == 0 || i == contour.length - 1) {hA = Math.PI * .5;}
|
|
||||||
if (i == contour.length - 1) {tA = v1.angle() - Math.PI * .5;}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shift = Math.tan(hA - Math.PI * .5);
|
|
||||||
const shiftMatrix = new THREE.Matrix4().set(
|
|
||||||
1, 0, 0, 0,
|
|
||||||
-shift, 1, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1
|
|
||||||
);
|
|
||||||
|
|
||||||
const tempAngle = tA;
|
|
||||||
const rotationMatrix = new THREE.Matrix4().set(
|
|
||||||
Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0,
|
|
||||||
Math.sin(tempAngle), Math.cos(tempAngle), 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1
|
|
||||||
);
|
|
||||||
|
|
||||||
const translationMatrix = new THREE.Matrix4().set(
|
|
||||||
1, 0, 0, contour[i].x,
|
|
||||||
0, 1, 0, contour[i].y,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
const cloneProfile = profile.clone();
|
|
||||||
cloneProfile.applyMatrix4(shiftMatrix);
|
|
||||||
cloneProfile.applyMatrix4(rotationMatrix);
|
|
||||||
cloneProfile.applyMatrix4(translationMatrix);
|
|
||||||
|
|
||||||
faces.set(cloneProfile.array, cloneProfile.count * i * 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = [];
|
|
||||||
const lastCorner = contourClosed == false ? contour.length - 1: contour.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < lastCorner; i++) {
|
|
||||||
for (let j = 0; j < profile.count; j++) {
|
|
||||||
const currCorner = i;
|
|
||||||
const nextCorner = i + 1 == contour.length ? 0 : i + 1;
|
|
||||||
const currPoint = j;
|
|
||||||
const nextPoint = j + 1 == profile.count ? 0 : j + 1;
|
|
||||||
|
|
||||||
const a = nextPoint + profile.count * currCorner;
|
|
||||||
const b = currPoint + profile.count * currCorner;
|
|
||||||
const c = currPoint + profile.count * nextCorner;
|
|
||||||
const d = nextPoint + profile.count * nextCorner;
|
|
||||||
|
|
||||||
index.push(a, b, d);
|
|
||||||
index.push(b, c, d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!contourClosed) {
|
|
||||||
// cheating because we know the profile length is 4 (for now)
|
|
||||||
const p1 = 0 + profile.count * 0;
|
|
||||||
const p2 = 1 + profile.count * 0;
|
|
||||||
const p3 = 2 + profile.count * 0;
|
|
||||||
const p4 = 3 + profile.count * 0;
|
|
||||||
index.push(p1, p2, p3);
|
|
||||||
index.push(p1, p3, p4);
|
|
||||||
const lc = lastCorner;
|
|
||||||
const p5 = 0 + profile.count * lc;
|
|
||||||
const p6 = 1 + profile.count * lc;
|
|
||||||
const p7 = 2 + profile.count * lc;
|
|
||||||
const p8 = 3 + profile.count * lc;
|
|
||||||
index.push(p7, p6, p5);
|
|
||||||
index.push(p8, p7, p5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {index, faces};
|
|
||||||
}
|
|
||||||
|
|
||||||
kiri.Layers = Layers;
|
kiri.Layers = Layers;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1814,6 +1814,12 @@ th, tr, td, label {
|
||||||
#stats:hover #rnfo {
|
#stats:hover #rnfo {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
.dark #rnfo {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
.dark #rnfo label {
|
||||||
|
color: #ada;
|
||||||
|
}
|
||||||
#rnfo {
|
#rnfo {
|
||||||
display: none;
|
display: none;
|
||||||
grid-template-columns: min-content 1fr;
|
grid-template-columns: min-content 1fr;
|
||||||
|
|
@ -1821,12 +1827,10 @@ th, tr, td, label {
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
gap: 3px;
|
gap: 3px;
|
||||||
right: 0;
|
right: 0;
|
||||||
color: #ddd;
|
|
||||||
background-color: rgba(255,255,255,0.05);
|
background-color: rgba(255,255,255,0.05);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
x-white-space: pre;
|
|
||||||
}
|
}
|
||||||
#rnfo label {
|
#rnfo label {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue