303 lines
9.4 KiB
JavaScript
303 lines
9.4 KiB
JavaScript
"use strict";
|
|
var __defProp = Object.defineProperty;
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField = (obj, key, value) => {
|
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
const THREE = require("three");
|
|
const BufferGeometryUtils = require("../utils/BufferGeometryUtils.cjs");
|
|
const cb = /* @__PURE__ */ new THREE.Vector3();
|
|
const ab = /* @__PURE__ */ new THREE.Vector3();
|
|
function pushIfUnique(array, object) {
|
|
if (array.indexOf(object) === -1)
|
|
array.push(object);
|
|
}
|
|
function removeFromArray(array, object) {
|
|
const k = array.indexOf(object);
|
|
if (k > -1)
|
|
array.splice(k, 1);
|
|
}
|
|
class Vertex {
|
|
constructor(v, id) {
|
|
__publicField(this, "position");
|
|
__publicField(this, "id");
|
|
__publicField(this, "faces");
|
|
__publicField(this, "neighbors");
|
|
__publicField(this, "collapseCost");
|
|
__publicField(this, "collapseNeighbor");
|
|
__publicField(this, "minCost", 0);
|
|
__publicField(this, "totalCost", 0);
|
|
__publicField(this, "costCount", 0);
|
|
this.position = v;
|
|
this.id = id;
|
|
this.faces = [];
|
|
this.neighbors = [];
|
|
this.collapseCost = 0;
|
|
this.collapseNeighbor = null;
|
|
}
|
|
addUniqueNeighbor(vertex) {
|
|
pushIfUnique(this.neighbors, vertex);
|
|
}
|
|
removeIfNonNeighbor(n) {
|
|
const neighbors = this.neighbors;
|
|
const faces = this.faces;
|
|
const offset = neighbors.indexOf(n);
|
|
if (offset === -1)
|
|
return;
|
|
for (let i = 0; i < faces.length; i++) {
|
|
if (faces[i].hasVertex(n))
|
|
return;
|
|
}
|
|
neighbors.splice(offset, 1);
|
|
}
|
|
}
|
|
class Triangle {
|
|
constructor(v1, v2, v3, a, b, c) {
|
|
__publicField(this, "a");
|
|
__publicField(this, "b");
|
|
__publicField(this, "c");
|
|
__publicField(this, "v1");
|
|
__publicField(this, "v2");
|
|
__publicField(this, "v3");
|
|
__publicField(this, "normal", new THREE.Vector3());
|
|
this.a = a;
|
|
this.b = b;
|
|
this.c = c;
|
|
this.v1 = v1;
|
|
this.v2 = v2;
|
|
this.v3 = v3;
|
|
this.computeNormal();
|
|
v1.faces.push(this);
|
|
v1.addUniqueNeighbor(v2);
|
|
v1.addUniqueNeighbor(v3);
|
|
v2.faces.push(this);
|
|
v2.addUniqueNeighbor(v1);
|
|
v2.addUniqueNeighbor(v3);
|
|
v3.faces.push(this);
|
|
v3.addUniqueNeighbor(v1);
|
|
v3.addUniqueNeighbor(v2);
|
|
}
|
|
computeNormal() {
|
|
const vA = this.v1.position;
|
|
const vB = this.v2.position;
|
|
const vC = this.v3.position;
|
|
cb.subVectors(vC, vB);
|
|
ab.subVectors(vA, vB);
|
|
cb.cross(ab).normalize();
|
|
this.normal.copy(cb);
|
|
}
|
|
hasVertex(v) {
|
|
return v === this.v1 || v === this.v2 || v === this.v3;
|
|
}
|
|
replaceVertex(oldv, newv) {
|
|
if (oldv === this.v1)
|
|
this.v1 = newv;
|
|
else if (oldv === this.v2)
|
|
this.v2 = newv;
|
|
else if (oldv === this.v3)
|
|
this.v3 = newv;
|
|
removeFromArray(oldv.faces, this);
|
|
newv.faces.push(this);
|
|
oldv.removeIfNonNeighbor(this.v1);
|
|
this.v1.removeIfNonNeighbor(oldv);
|
|
oldv.removeIfNonNeighbor(this.v2);
|
|
this.v2.removeIfNonNeighbor(oldv);
|
|
oldv.removeIfNonNeighbor(this.v3);
|
|
this.v3.removeIfNonNeighbor(oldv);
|
|
this.v1.addUniqueNeighbor(this.v2);
|
|
this.v1.addUniqueNeighbor(this.v3);
|
|
this.v2.addUniqueNeighbor(this.v1);
|
|
this.v2.addUniqueNeighbor(this.v3);
|
|
this.v3.addUniqueNeighbor(this.v1);
|
|
this.v3.addUniqueNeighbor(this.v2);
|
|
this.computeNormal();
|
|
}
|
|
}
|
|
class SimplifyModifier {
|
|
constructor() {
|
|
__publicField(this, "computeEdgeCollapseCost", (u, v) => {
|
|
const edgelength = v.position.distanceTo(u.position);
|
|
let curvature = 0;
|
|
const sideFaces = [];
|
|
let i, il = u.faces.length, face, sideFace;
|
|
for (i = 0; i < il; i++) {
|
|
face = u.faces[i];
|
|
if (face.hasVertex(v)) {
|
|
sideFaces.push(face);
|
|
}
|
|
}
|
|
for (i = 0; i < il; i++) {
|
|
let minCurvature = 1;
|
|
face = u.faces[i];
|
|
for (let j = 0; j < sideFaces.length; j++) {
|
|
sideFace = sideFaces[j];
|
|
const dotProd = face.normal.dot(sideFace.normal);
|
|
minCurvature = Math.min(minCurvature, (1.001 - dotProd) / 2);
|
|
}
|
|
curvature = Math.max(curvature, minCurvature);
|
|
}
|
|
const borders = 0;
|
|
if (sideFaces.length < 2) {
|
|
curvature = 1;
|
|
}
|
|
const amt = edgelength * curvature + borders;
|
|
return amt;
|
|
});
|
|
__publicField(this, "computeEdgeCostAtVertex", (v) => {
|
|
if (v.neighbors.length === 0) {
|
|
v.collapseNeighbor = null;
|
|
v.collapseCost = -0.01;
|
|
return;
|
|
}
|
|
v.collapseCost = 1e5;
|
|
v.collapseNeighbor = null;
|
|
for (let i = 0; i < v.neighbors.length; i++) {
|
|
const collapseCost = this.computeEdgeCollapseCost(v, v.neighbors[i]);
|
|
if (!v.collapseNeighbor) {
|
|
v.collapseNeighbor = v.neighbors[i];
|
|
v.collapseCost = collapseCost;
|
|
v.minCost = collapseCost;
|
|
v.totalCost = 0;
|
|
v.costCount = 0;
|
|
}
|
|
v.costCount++;
|
|
v.totalCost += collapseCost;
|
|
if (collapseCost < v.minCost) {
|
|
v.collapseNeighbor = v.neighbors[i];
|
|
v.minCost = collapseCost;
|
|
}
|
|
}
|
|
v.collapseCost = v.totalCost / v.costCount;
|
|
});
|
|
__publicField(this, "removeFace", (f, faces) => {
|
|
removeFromArray(faces, f);
|
|
if (f.v1)
|
|
removeFromArray(f.v1.faces, f);
|
|
if (f.v2)
|
|
removeFromArray(f.v2.faces, f);
|
|
if (f.v3)
|
|
removeFromArray(f.v3.faces, f);
|
|
const vs = [f.v1, f.v2, f.v3];
|
|
let v1, v2;
|
|
for (let i = 0; i < 3; i++) {
|
|
v1 = vs[i];
|
|
v2 = vs[(i + 1) % 3];
|
|
if (!v1 || !v2)
|
|
continue;
|
|
v1.removeIfNonNeighbor(v2);
|
|
v2.removeIfNonNeighbor(v1);
|
|
}
|
|
});
|
|
__publicField(this, "collapse", (vertices, faces, u, v) => {
|
|
if (!v) {
|
|
this.removeVertex(u, vertices);
|
|
return;
|
|
}
|
|
let i;
|
|
const tmpVertices = [];
|
|
for (i = 0; i < u.neighbors.length; i++) {
|
|
tmpVertices.push(u.neighbors[i]);
|
|
}
|
|
for (i = u.faces.length - 1; i >= 0; i--) {
|
|
if (u.faces[i].hasVertex(v)) {
|
|
this.removeFace(u.faces[i], faces);
|
|
}
|
|
}
|
|
for (i = u.faces.length - 1; i >= 0; i--) {
|
|
u.faces[i].replaceVertex(u, v);
|
|
}
|
|
this.removeVertex(u, vertices);
|
|
for (i = 0; i < tmpVertices.length; i++) {
|
|
this.computeEdgeCostAtVertex(tmpVertices[i]);
|
|
}
|
|
});
|
|
__publicField(this, "minimumCostEdge", (vertices) => {
|
|
let least = vertices[0];
|
|
for (let i = 0; i < vertices.length; i++) {
|
|
if (vertices[i].collapseCost < least.collapseCost) {
|
|
least = vertices[i];
|
|
}
|
|
}
|
|
return least;
|
|
});
|
|
__publicField(this, "modify", (geometry, count) => {
|
|
geometry = geometry.clone();
|
|
const attributes = geometry.attributes;
|
|
for (let name in attributes) {
|
|
if (name !== "position")
|
|
geometry.deleteAttribute(name);
|
|
}
|
|
geometry = BufferGeometryUtils.mergeVertices(geometry);
|
|
const vertices = [];
|
|
const faces = [];
|
|
const positionAttribute = geometry.getAttribute("position");
|
|
for (let i = 0; i < positionAttribute.count; i++) {
|
|
const v = new THREE.Vector3().fromBufferAttribute(positionAttribute, i);
|
|
const vertex = new Vertex(v, i);
|
|
vertices.push(vertex);
|
|
}
|
|
const geomIndex = geometry.getIndex();
|
|
if (geomIndex !== null) {
|
|
for (let i = 0; i < geomIndex.count; i += 3) {
|
|
const a = geomIndex.getX(i);
|
|
const b = geomIndex.getX(i + 1);
|
|
const c = geomIndex.getX(i + 2);
|
|
const triangle = new Triangle(vertices[a], vertices[b], vertices[c], a, b, c);
|
|
faces.push(triangle);
|
|
}
|
|
} else {
|
|
for (let i = 0; i < positionAttribute.count; i += 3) {
|
|
const a = i;
|
|
const b = i + 1;
|
|
const c = i + 2;
|
|
const triangle = new Triangle(vertices[a], vertices[b], vertices[c], a, b, c);
|
|
faces.push(triangle);
|
|
}
|
|
}
|
|
for (let i = 0, il = vertices.length; i < il; i++) {
|
|
this.computeEdgeCostAtVertex(vertices[i]);
|
|
}
|
|
let nextVertex;
|
|
let z = count;
|
|
while (z--) {
|
|
nextVertex = this.minimumCostEdge(vertices);
|
|
if (!nextVertex) {
|
|
console.log("THREE.SimplifyModifier: No next vertex");
|
|
break;
|
|
} else {
|
|
this.collapse(vertices, faces, nextVertex, nextVertex.collapseNeighbor);
|
|
}
|
|
}
|
|
const simplifiedGeometry = new THREE.BufferGeometry();
|
|
const position = [];
|
|
let index = [];
|
|
for (let i = 0; i < vertices.length; i++) {
|
|
const vertex = vertices[i].position;
|
|
position.push(vertex.x, vertex.y, vertex.z);
|
|
}
|
|
for (let i = 0; i < faces.length; i++) {
|
|
const face = faces[i];
|
|
const a = vertices.indexOf(face.v1);
|
|
const b = vertices.indexOf(face.v2);
|
|
const c = vertices.indexOf(face.v3);
|
|
index.push(a, b, c);
|
|
}
|
|
simplifiedGeometry.setAttribute("position", new THREE.Float32BufferAttribute(position, 3));
|
|
simplifiedGeometry.setIndex(index);
|
|
return simplifiedGeometry;
|
|
});
|
|
}
|
|
removeVertex(v, vertices) {
|
|
console.assert(v.faces.length === 0);
|
|
while (v.neighbors.length) {
|
|
const n = v.neighbors.pop();
|
|
removeFromArray(n.neighbors, v);
|
|
}
|
|
removeFromArray(vertices, v);
|
|
}
|
|
}
|
|
exports.SimplifyModifier = SimplifyModifier;
|
|
//# sourceMappingURL=SimplifyModifier.cjs.map
|