Doodle3D-Slicer/src/sliceActions/intersectionsToShapes.js

163 lines
4.9 KiB
JavaScript

import { subtract, normal, normalize, dot, almostEquals } from './helpers/vector2.js';
export default function intersectionsToShapes(layerPoints, layerFaceIndexes, faces, openObjectIndexes, settings) {
const layers = [];
for (let layer = 0; layer < layerPoints.length; layer ++) {
const fillShapes = [];
const lineShapesOpen = [];
const lineShapesClosed = [];
const points = layerPoints[layer];
const faceIndexes = layerFaceIndexes[layer];
if (faceIndexes.length === 0) continue;
const shapes = {};
const startConnects = {};
const endConnects = {};
for (let i = 0; i < faceIndexes.length; i ++) {
const faceIndex = faceIndexes[i];
const { lineIndexes, flatNormal, objectIndex } = faces[faceIndex];
const a = lineIndexes[0];
const b = lineIndexes[1];
const c = lineIndexes[2];
let pointA;
let pointB;
if (points[a] && points[b]) {
pointA = a;
pointB = b;
} else if (points[b] && points[c]) {
pointA = b;
pointB = c;
} else if (points[c] && points[a]) {
pointA = c;
pointB = a;
} else {
// should never happen
continue;
}
const segmentNormal = normalize(normal(subtract(points[pointA], points[pointB])));
if (dot(segmentNormal, flatNormal) < 0) {
const temp = pointB;
pointB = pointA;
pointA = temp;
}
if (endConnects[pointA]) {
const lineSegment = endConnects[pointA];
delete endConnects[pointA];
if (startConnects[pointB]) {
if (startConnects[pointB] === lineSegment) {
delete startConnects[pointB];
lineSegment.push(pointB);
} else {
lineSegment.push(...startConnects[pointB]);
endConnects[lineSegment[lineSegment.length - 1]] = lineSegment;
}
} else {
lineSegment.push(pointB);
endConnects[pointB] = lineSegment;
}
} else if (startConnects[pointB]) {
const lineSegment = startConnects[pointB];
delete startConnects[pointB];
if (endConnects[pointA]) {
lineSegment.unshift(...endConnects[pointA]);
startConnects[lineSegment[0]] = lineSegment;
} else {
lineSegment.unshift(pointA);
startConnects[pointA] = lineSegment;
}
} else {
const lineSegment = [pointA, pointB];
startConnects[pointA] = lineSegment;
endConnects[pointB] = lineSegment;
if (!shapes[objectIndex]) shapes[objectIndex] = [];
shapes[objectIndex].push(lineSegment);
}
}
for (const objectIndex in shapes) {
const shape = shapes[objectIndex]
.map(lineSegment => lineSegment.map(pointIndex => points[pointIndex]))
.filter(lineSegment => lineSegment.some(i => !almostEquals(lineSegment[0], lineSegment[1])));
const openShape = openObjectIndexes[objectIndex];
const connectPoints = [];
for (let pathIndex = 0; pathIndex < shape.length; pathIndex ++) {
const path = shape[pathIndex];
if (almostEquals(path[0], path[path.length - 1])) {
if (openShape) {
lineShapesClosed.push(path);
} else {
fillShapes.push(path);
}
continue;
}
let shapeStartPoint = path[0];
const connectNext = connectPoints.find(({ point }) => almostEquals(point, shapeStartPoint));
if (connectNext) {
connectNext.next = pathIndex;
} else {
connectPoints.push({ point: shapeStartPoint, next: pathIndex, previous: -1 });
}
let shapeEndPoint = path[path.length - 1];
const connectPrevious = connectPoints.find(({ point }) => almostEquals(point, shapeEndPoint));
if (connectPrevious) {
connectPrevious.previous = pathIndex;
} else {
connectPoints.push({ point: shapeEndPoint, next: -1, previous: pathIndex });
}
}
connectPoints.sort(({ previous }) => -previous);
while (connectPoints.length !== 0) {
let { next, previous } = connectPoints.pop();
const line = [];
if (previous !== -1) line.push(...shape[previous]);
while (true) {
const pointIndex = connectPoints.findIndex(point => point.previous === next);
if (pointIndex === -1) break;
const point = connectPoints[pointIndex];
line.push(...shape[point.previous]);
connectPoints.splice(pointIndex, 1);
if (point.next === -1) break;
if (point.next === previous) break;
next = point.next;
}
if (openShape) {
if (almostEquals(line[0], line[line.length - 1])) {
lineShapesClosed.push(line);
} else {
lineShapesOpen.push(line);
}
} else {
fillShapes.push(line);
}
}
}
layers.push({ fillShapes, lineShapesOpen, lineShapesClosed });
}
return layers;
}