import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
function computeFacesUV(vertices, iA, iB, iC) { const ax = vertices[iA * 3]; const ay = vertices[iA * 3 + 1]; const bx = vertices[iB * 3]; const by = vertices[iB * 3 + 1]; const cx = vertices[iC * 3]; const cy = vertices[iC * 3 + 1]; return [ [ax, ay], [bx, by], [cx, cy], ]; }
function computeSidesUV(vertices, iA, iB, iC, iD) { const ax = vertices[iA * 3]; const ay = vertices[iA * 3 + 1]; const az = vertices[iA * 3 + 2]; const bx = vertices[iB * 3]; const by = vertices[iB * 3 + 1]; const bz = vertices[iB * 3 + 2]; const cx = vertices[iC * 3]; const cy = vertices[iC * 3 + 1]; const cz = vertices[iC * 3 + 2]; const dx = vertices[iD * 3]; const dy = vertices[iD * 3 + 1]; const dz = vertices[iD * 3 + 2];
if (Math.abs(ay - by) < Math.abs(ax - bx)) { return [ [ax, 1 - az], [bx, 1 - bz], [cx, 1 - cz], [dx, 1 - dz], ]; } return [ [ay, 1 - az], [by, 1 - bz], [cy, 1 - cz], [dy, 1 - dz], ]; }
function createShapePath() { const curves = []; const currentPoint = [0, 0]; const holes = [];
return { curves, currentPoint, holes, moveTo(x, y) { currentPoint[0] = x; currentPoint[1] = y; }, lineTo(x, y) { const start = [...currentPoint]; const end = [x, y]; curves.push({ curveType: 'LineCurve', start, end, getPointAt(t) { return [ start[0] + t * (end[0] - start[0]), start[1] + t * (end[1] - start[1]), ]; }, getPoints(resolution) { const points = []; for (let i = 0; i <= resolution; i++) { points.push(this.getPointAt(i / resolution)); } return points; }, }); currentPoint[0] = x; currentPoint[1] = y; }, quadraticCurveTo(cpX, cpY, x, y) { const start = [...currentPoint]; const end = [x, y]; const cp = [cpX, cpY]; curves.push({ curveType: 'QuadraticBezierCurve', cp, start, end, getPointAt(t) { const oneMinusT = 1 - t; return [ oneMinusT * oneMinusT * start[0] + 2 * oneMinusT * t * cp[0] + t * t * end[0], oneMinusT * oneMinusT * start[1] + 2 * oneMinusT * t * cp[1] + t * t * end[1], ]; }, getPoints(resolution) { const points = []; for (let i = 0; i <= resolution; i++) { points.push(this.getPointAt(i / resolution)); } return points; }, }); currentPoint[0] = x; currentPoint[1] = y; }, bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, x, y) { const start = [...currentPoint]; const end = [x, y]; const cp1 = [cp1X, cp1Y]; const cp2 = [cp2X, cp2Y]; curves.push({ curveType: 'BezierCurve', cp1, cp2, start, end, getPointAt(t) { const oneMinusT = 1 - t; return [ oneMinusT * oneMinusT * oneMinusT * start[0] + 3 * oneMinusT * oneMinusT * t * cp1[0] + 3 * oneMinusT * t * t * cp2[0] + t * t * t * end[0], oneMinusT * oneMinusT * oneMinusT * start[1] + 3 * oneMinusT * oneMinusT * t * cp1[1] + 3 * oneMinusT * t * t * cp2[1] + t * t * t * end[1], ]; }, getPoints(resolution) { const points = []; for (let i = 0; i <= resolution; i++) { points.push(this.getPointAt(i / resolution)); } return points; }, }); currentPoint[0] = x; currentPoint[1] = y; },
getPoints(divisions) { let last; const points = [];
for (let i = 0; i < curves.length; i++) { const curve = curves[i]; let resolution = divisions;
if (curve.curveType === 'EllipseCurve') { resolution = divisions * 2; } else if (curve.curveType === 'LineCurve') { resolution = 1; }
const pts = curve.getPoints(resolution);
for (let j = 0; j < pts.length; j++) { const point = pts[j]; if (last && vtkMath.areEquals(last, point)) continue; points.push(point); last = point; } }
return points; },
extractPoints(divisions) { const points = this.getPoints(divisions); const holesPoints = this.holes.map((hole) => hole.getPoints(divisions)); return { shape: points, holes: holesPoints }; },
isPointInside(point, polygon) { const x = point[0]; const y = point[1]; let isInside = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { const xi = polygon[i][0]; const yi = polygon[i][1]; const xj = polygon[j][0]; const yj = polygon[j][1];
const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) isInside = !isInside; }
return isInside; }, isIntersect(path) { const pathA = this.getPoints(1, curves, false); const pathB = path.getPoints(1); return this.isPointInside(pathB[0], pathA); }, }; }
function getBoundingSize(shapes, depth, curveSegments) { const minPoint = [Infinity, Infinity, depth > 0 ? 0 : depth]; const maxPoint = [-Infinity, -Infinity, depth < 0 ? 0 : depth];
for (let i = 0; i < shapes.length; i++) { const shape = shapes[i]; const shapePoints = shape.extractPoints(curveSegments);
for (let j = 0; j < shapePoints.shape.length; j++) { const p = shapePoints.shape[j]; if (p[0] < minPoint[0]) minPoint[0] = p[0]; if (p[1] < minPoint[1]) minPoint[1] = p[1]; if (p[0] > maxPoint[0]) maxPoint[0] = p[0]; if (p[1] > maxPoint[1]) maxPoint[1] = p[1]; } }
return { min: minPoint, max: maxPoint }; }
function removeDupEndPoints(points) { const l = points.length; const isEqual = vtkMath.areEquals(points[l - 1], points[0]); if (l > 2 && isEqual) { points.pop(); } }
function isClockWise(points) { let sum = 0.0; const n = points.length; for (let p = n - 1, q = 0; q < n; p = q++) { sum += points[p][0] * points[q][1] - points[q][0] * points[p][1]; } return sum * 0.5 < 0; }
function computeBevelVector(pt, prev, next) { const vPrevX = pt[0] - prev[0]; const vPrevY = pt[1] - prev[1]; const vNextX = next[0] - pt[0]; const vNextY = next[1] - pt[1];
const cross = vPrevX * vNextY - vPrevY * vNextX; let tx; let ty; let shrinkBy;
if (Math.abs(cross) > Number.EPSILON) { const lenPrev = Math.hypot(vPrevX, vPrevY); const lenNext = Math.hypot(vNextX, vNextY);
const prevShiftX = prev[0] - vPrevY / lenPrev; const prevShiftY = prev[1] + vPrevX / lenPrev; const nextShiftX = next[0] - vNextY / lenNext; const nextShiftY = next[1] + vNextX / lenNext;
const sf = ((nextShiftX - prevShiftX) * vNextY - (nextShiftY - prevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
tx = prevShiftX + vPrevX * sf - pt[0]; ty = prevShiftY + vPrevY * sf - pt[1];
const lensq = tx * tx + ty * ty; if (lensq <= 2) { return [tx, ty]; } shrinkBy = Math.sqrt(lensq / 2); } else { const sameDir = (vPrevX > 0 && vNextX > 0) || (vPrevX < 0 && vNextX < 0) || Math.sign(vPrevY) === Math.sign(vNextY);
if (sameDir) { tx = -vPrevY; ty = vPrevX; shrinkBy = Math.hypot(vPrevX, vPrevY); } else { tx = vPrevX; ty = vPrevY; shrinkBy = Math.sqrt((vPrevX * vPrevX + vPrevY * vPrevY) / 2); } }
return [tx / shrinkBy, ty / shrinkBy]; }
function triangulateShape(earcut, contour, holes) { const faces = []; const vertices = []; const holeIndices = [];
removeDupEndPoints(contour);
for (let i = 0; i < contour.length; i++) { vertices.push(contour[i][0], contour[i][1]); }
let holeIndex = contour.length; holes.forEach(removeDupEndPoints);
for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex);
const hole = holes[i]; holeIndex += hole.length;
for (let j = 0; j < hole.length; j++) { vertices.push(hole[j][0], hole[j][1]); } }
const triangles = earcut(vertices, holeIndices); for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); }
return faces; }
function scalePoint(pt, vec, size) { const rt = [pt[0], pt[1]]; rt[0] += vec[0] * size; rt[1] += vec[1] * size; return rt; }
function addTriangle( layers, a, b, c, verticesArray, uvArray, colorArray, color ) { const tri = [a, c, b]; tri.forEach((i) => { verticesArray.push(layers[i * 3], layers[i * 3 + 1], layers[i * 3 + 2]); });
const nextIndex = verticesArray.length / 3; const uvs = computeFacesUV( verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
uvs.forEach((uv) => { uvArray.push(uv[0], uv[1]); }); if (colorArray && color) { for (let i = 0; i < 3; ++i) colorArray.push(color[0] * 255, color[1] * 255, color[2] * 255); } }
function addQuad( layers, a, b, c, d, verticesArray, uvArray, colorArray, color ) { const quad = [a, d, b, b, d, c]; quad.forEach((i) => verticesArray.push(layers[i * 3], layers[i * 3 + 1], layers[i * 3 + 2]) );
const nextIndex = verticesArray.length / 3; const uvs = computeSidesUV( verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
uvArray.push(uvs[0][0], uvs[0][1]); uvArray.push(uvs[1][0], uvs[1][1]); uvArray.push(uvs[3][0], uvs[3][1]);
uvArray.push(uvs[1][0], uvs[1][1]); uvArray.push(uvs[2][0], uvs[2][1]); uvArray.push(uvs[3][0], uvs[3][1]);
if (colorArray && color) { for (let i = 0; i < 6; ++i) colorArray.push(color[0] * 255, color[1] * 255, color[2] * 255); } }
function buildLidFaces( layers, faces, vlen, steps, bevelEnabled, bevelSegments, verticesArray, uvArray, colorArray, color ) { if (bevelEnabled) { let layer = 0; let offset = vlen * layer; faces.forEach(([a, b, c]) => { addTriangle( layers, c + offset, b + offset, a + offset, verticesArray, uvArray, colorArray, color ); });
layer = steps + bevelSegments * 2; offset = vlen * layer;
faces.forEach(([a, b, c]) => { addTriangle( layers, a + offset, b + offset, c + offset, verticesArray, uvArray, colorArray, color ); }); } else { faces.forEach(([a, b, c]) => { addTriangle(layers, c, b, a, verticesArray, uvArray, colorArray, color); });
const offset = vlen * steps; faces.forEach(([a, b, c]) => { addTriangle( layers, a + offset, b + offset, c + offset, verticesArray, uvArray, colorArray, color ); }); } }
function buildWalls( layers, contour, layerOffset, vlen, steps, bevelSegments, verticesArray, uvArray, colorArray, color ) { const totalLayers = steps + bevelSegments * 2; for (let i = 0; i < contour.length; i++) { const j = i; const k = i === 0 ? contour.length - 1 : i - 1;
for (let s = 0; s < totalLayers; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1);
const a = layerOffset + j + slen1; const b = layerOffset + k + slen1; const c = layerOffset + k + slen2; const d = layerOffset + j + slen2;
addQuad(layers, a, b, c, d, verticesArray, uvArray, colorArray, color); } } }
function buildSideFaces( layers, contour, holes, vlen, steps, bevelSegments, verticesArray, uvArray, colorArray, color ) { let layerOffset = 0; buildWalls( layers, contour, layerOffset, vlen, steps, bevelSegments, verticesArray, uvArray, colorArray, color ); layerOffset += contour.length;
for (let i = 0; i < holes.length; i++) { const ahole = holes[i]; buildWalls( layers, ahole, layerOffset, vlen, steps, bevelSegments, verticesArray, uvArray, colorArray, color ); layerOffset += ahole.length; } }
export { addTriangle, addQuad, buildLidFaces, buildWalls, buildSideFaces, computeBevelVector, computeFacesUV, computeSidesUV, createShapePath, getBoundingSize, isClockWise, scalePoint, triangulateShape, };
|