move legacy 3d path generator to geo.paths

This commit is contained in:
Stewart Allen 2022-10-04 20:10:50 -04:00
commit bcc64baef8
3 changed files with 160 additions and 153 deletions

View file

@ -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
}; };

View file

@ -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,9 +252,7 @@ class Layers {
} }
this.stats.contour++; this.stats.contour++;
} }
return this; } else {
}
const profiles = this.profiles; const profiles = this.profiles;
const prokey = `${offset}x${height}`; const prokey = `${offset}x${height}`;
if (!profiles[prokey]) { if (!profiles[prokey]) {
@ -274,7 +273,7 @@ class Layers {
poly.points.forEach(p => { poly.points.forEach(p => {
contour.push(new THREE.Vector2(p.x, p.y)); contour.push(new THREE.Vector2(p.x, p.y));
}); });
const {index, faces} = ProfiledContourGeometry(profile, contour, poly.isClosed()); const {index, faces} = base.paths.shapeToPath(profile, contour, poly.isClosed());
const cur = this.current; const cur = this.current;
const one = cur.paths[0]; const one = cur.paths[0];
if (one) { if (one) {
@ -310,6 +309,8 @@ class Layers {
} }
this.stats.contour++; 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;
}); });

View file

@ -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;