From 5e557e583d8df3f14821878ebebb75e27b38a927 Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Mon, 15 Sep 2025 16:38:16 +0530 Subject: [PATCH 1/7] Fix textToModel face normals for extruded text --- src/type/p5.Font.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 83c30fee82..5201a4770c 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -591,7 +591,7 @@ export class Font { if (extrude !== 0) { geom.computeNormals(); for (const face of geom.faces) { - if (face.every(idx => geom.vertices[idx].z <= -extrude * 0.5 + 0.1)) { + if (face.every(idx => geom.vertices[idx].z <= -extrude * 0.5 + 0.1 || geom.vertices[idx].z >= extrude * 0.5 - 0.1)) { for (const idx of face) geom.vertexNormals[idx].set(0, 0, -1); face.reverse(); } From 3e567bf894bda0d4b0e0806cb431936fc730ee0e Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Mon, 29 Sep 2025 14:51:56 +0530 Subject: [PATCH 2/7] Fix textToModel extrusion and degenerate face issues --- src/type/p5.Font.js | 157 +++++++++++++++++++++++++++++++------------- 1 file changed, 112 insertions(+), 45 deletions(-) diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 7d669db11a..1475697b49 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -541,63 +541,130 @@ export class Font { textToModel(str, x, y, width, height, options) { ({ width, height, options } = this._parseArgs(width, height, options)); const extrude = options?.extrude || 0; - const contours = this.textToContours(str, x, y, width, height, options); + // Step 1: generate glyph contours + let contours = this.textToContours(str, x, y, width, height, options); + if (!Array.isArray(contours[0][0])) { + contours = [contours]; + } + // Step 2: build base flat geometry const geom = this._pInst.buildGeometry(() => { - if (extrude === 0) { - const prevValidateFaces = this._pInst._renderer._validateFaces; - this._pInst._renderer._validateFaces = true; + const prevValidateFaces = this._pInst._renderer._validateFaces; + this._pInst._renderer._validateFaces = true; + + contours.forEach(glyphContours => { this._pInst.beginShape(); - this._pInst.normal(0, 0, 1); - for (const contour of contours) { + const outer = glyphContours[0]; + outer.forEach(({ x, y }) => this._pInst.vertex(x, y, 0)); + + for (let i = 1; i < glyphContours.length; i++) { this._pInst.beginContour(); - for (const { x, y } of contour) { - this._pInst.vertex(x, y); - } + glyphContours[i].forEach(({ x, y }) => this._pInst.vertex(x, y, 0)); this._pInst.endContour(this._pInst.CLOSE); } - this._pInst.endShape(); - this._pInst._renderer._validateFaces = prevValidateFaces; + + this._pInst.endShape(this._pInst.CLOSE); + }); + + this._pInst._renderer._validateFaces = prevValidateFaces; + }); + + if (extrude === 0) { + console.log('No extrusion'); + return geom; + } + + // Step 3: Create extruded geometry with UNSHARED vertices for flat shading + const extruded = this._pInst.buildGeometry(() => {}); + const half = extrude * 0.5; + + extruded.vertices = []; + extruded.vertexNormals = []; + extruded.faces = []; + + let vertexIndex = 0; + + // Helper to add a triangle with flat normal + const addTriangle = (v0, v1, v2) => { + const edge1 = p5.Vector.sub(v1, v0); + const edge2 = p5.Vector.sub(v2, v0); + const normal = p5.Vector.cross(edge1, edge2); + if (normal.magSq() > 0.0001) { + normal.normalize(); } else { - const prevValidateFaces = this._pInst._renderer._validateFaces; - this._pInst._renderer._validateFaces = true; - - // Draw front faces - for (const side of [1, -1]) { - this._pInst.beginShape(); - for (const contour of contours) { - this._pInst.beginContour(); - for (const { x, y } of contour) { - this._pInst.vertex(x, y, side * extrude * 0.5); - } - this._pInst.endContour(this._pInst.CLOSE); - } - this._pInst.endShape(); - } - this._pInst._renderer._validateFaces = prevValidateFaces; - - // Draw sides - for (const contour of contours) { - this._pInst.beginShape(this._pInst.QUAD_STRIP); - for (const v of contour) { - for (const side of [-1, 1]) { - this._pInst.vertex(v.x, v.y, side * extrude * 0.5); - } - } - this._pInst.endShape(); - } + normal.set(0, 0, 1); } - }); - if (extrude !== 0) { - geom.computeNormals(); + + // Add vertices (unshared - each triangle gets its own copies) + extruded.vertices.push(v0.copy(), v1.copy(), v2.copy()); + extruded.vertexNormals.push(normal.copy(), normal.copy(), normal.copy()); + extruded.faces.push([vertexIndex, vertexIndex + 1, vertexIndex + 2]); + vertexIndex += 3; + }; + + for (const face of geom.faces) { + if (face.length < 3) continue; + const v0 = geom.vertices[face[0]]; + for (let i = 1; i < face.length - 1; i++) { + const v1 = geom.vertices[face[i]]; + const v2 = geom.vertices[face[i + 1]]; + addTriangle( + new p5.Vector(v0.x, v0.y, v0.z + half), + new p5.Vector(v1.x, v1.y, v1.z + half), + new p5.Vector(v2.x, v2.y, v2.z + half) + ); + } + } + + for (const face of geom.faces) { + if (face.length < 3) continue; + const v0 = geom.vertices[face[0]]; + for (let i = 1; i < face.length - 1; i++) { + const v1 = geom.vertices[face[i]]; + const v2 = geom.vertices[face[i + 1]]; + addTriangle( + new p5.Vector(v0.x, v0.y, v0.z - half), + new p5.Vector(v2.x, v2.y, v2.z - half), + new p5.Vector(v1.x, v1.y, v1.z - half) + ); + } + } + + // Side faces from edges + let edges = geom.edges; + if (!edges || !Array.isArray(edges)) { + edges = []; + const edgeSet = new Set(); for (const face of geom.faces) { - if (face.every(idx => geom.vertices[idx].z <= -extrude * 0.5 + 0.1 || geom.vertices[idx].z >= extrude * 0.5 - 0.1)) { - for (const idx of face) geom.vertexNormals[idx].set(0, 0, -1); - face.reverse(); + for (let i = 0; i < face.length; i++) { + const a = face[i]; + const b = face[(i + 1) % face.length]; + if (a === b) continue; + const key = a < b ? `${a},${b}` : `${b},${a}`; + if (!edgeSet.has(key)) { + edgeSet.add(key); + edges.push([a, b]); + } } } } - return geom; + + const validEdges = edges.filter(([a, b]) => a !== b); + + for (const [a, b] of validEdges) { + const v0 = geom.vertices[a]; + const v1 = geom.vertices[b]; + + const vFront0 = new p5.Vector(v0.x, v0.y, v0.z + half); + const vFront1 = new p5.Vector(v1.x, v1.y, v1.z + half); + const vBack0 = new p5.Vector(v0.x, v0.y, v0.z - half); + const vBack1 = new p5.Vector(v1.x, v1.y, v1.z - half); + + // Two triangles forming the side quad + addTriangle(vFront0, vFront1, vBack1); + addTriangle(vFront0, vBack1, vBack0); + } + return extruded; } variations() { From 4c91d6e075d0a2f71ac1d8e407d24beb0e0dd4f4 Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Mon, 29 Sep 2025 15:21:50 +0530 Subject: [PATCH 3/7] Fix textToModel extrusion and degenerate face issues --- src/type/p5.Font.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 1475697b49..0921f7f968 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -583,12 +583,12 @@ export class Font { extruded.faces = []; let vertexIndex = 0; - + const Vector = this._pInst.constructor.Vector; // Helper to add a triangle with flat normal const addTriangle = (v0, v1, v2) => { - const edge1 = p5.Vector.sub(v1, v0); - const edge2 = p5.Vector.sub(v2, v0); - const normal = p5.Vector.cross(edge1, edge2); + const edge1 = Vector.sub(v1, v0); + const edge2 = Vector.sub(v2, v0); + const normal = Vector.cross(edge1, edge2); if (normal.magSq() > 0.0001) { normal.normalize(); } else { @@ -609,9 +609,9 @@ export class Font { const v1 = geom.vertices[face[i]]; const v2 = geom.vertices[face[i + 1]]; addTriangle( - new p5.Vector(v0.x, v0.y, v0.z + half), - new p5.Vector(v1.x, v1.y, v1.z + half), - new p5.Vector(v2.x, v2.y, v2.z + half) + new Vector(v0.x, v0.y, v0.z + half), + new Vector(v1.x, v1.y, v1.z + half), + new Vector(v2.x, v2.y, v2.z + half) ); } } @@ -623,9 +623,9 @@ export class Font { const v1 = geom.vertices[face[i]]; const v2 = geom.vertices[face[i + 1]]; addTriangle( - new p5.Vector(v0.x, v0.y, v0.z - half), - new p5.Vector(v2.x, v2.y, v2.z - half), - new p5.Vector(v1.x, v1.y, v1.z - half) + new Vector(v0.x, v0.y, v0.z - half), + new Vector(v2.x, v2.y, v2.z - half), + new Vector(v1.x, v1.y, v1.z - half) ); } } @@ -655,14 +655,14 @@ export class Font { const v0 = geom.vertices[a]; const v1 = geom.vertices[b]; - const vFront0 = new p5.Vector(v0.x, v0.y, v0.z + half); - const vFront1 = new p5.Vector(v1.x, v1.y, v1.z + half); - const vBack0 = new p5.Vector(v0.x, v0.y, v0.z - half); - const vBack1 = new p5.Vector(v1.x, v1.y, v1.z - half); + const vFront0 = new Vector(v0.x, v0.y, v0.z + half); + const vFront1 = new Vector(v1.x, v1.y, v1.z + half); + const vBack0 = new Vector(v0.x, v0.y, v0.z - half); + const vBack1 = new Vector(v1.x, v1.y, v1.z - half); // Two triangles forming the side quad - addTriangle(vFront0, vFront1, vBack1); - addTriangle(vFront0, vBack1, vBack0); + addTriangle(vFront0, vBack0, vBack1); + addTriangle(vFront0, vBack1, vFront1); } return extruded; } From a68bd7b214f0b31a1016d03e412d2f77fac00773 Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Tue, 28 Oct 2025 23:28:28 +0530 Subject: [PATCH 4/7] Fix text extrusion with proper vertex deduplication, edge detection, and normal calculation --- src/type/p5.Font.js | 187 ++++++++++++++++++++++++-------------------- 1 file changed, 102 insertions(+), 85 deletions(-) diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 0921f7f968..542f15a907 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -6,6 +6,7 @@ import { textCoreConstants } from './textCore'; import * as constants from '../core/constants'; import { UnicodeRange } from '@japont/unicode-range'; import { unicodeRanges } from './unicodeRanges'; +import { Vector } from '../math/p5.Vector'; /* API: @@ -541,129 +542,145 @@ export class Font { textToModel(str, x, y, width, height, options) { ({ width, height, options } = this._parseArgs(width, height, options)); const extrude = options?.extrude || 0; - // Step 1: generate glyph contours + let contours = this.textToContours(str, x, y, width, height, options); if (!Array.isArray(contours[0][0])) { contours = [contours]; } - // Step 2: build base flat geometry const geom = this._pInst.buildGeometry(() => { const prevValidateFaces = this._pInst._renderer._validateFaces; this._pInst._renderer._validateFaces = true; + this._pInst.push(); + this._pInst.stroke(0); contours.forEach(glyphContours => { this._pInst.beginShape(); - const outer = glyphContours[0]; - outer.forEach(({ x, y }) => this._pInst.vertex(x, y, 0)); - - for (let i = 1; i < glyphContours.length; i++) { + for (const contour of glyphContours) { this._pInst.beginContour(); - glyphContours[i].forEach(({ x, y }) => this._pInst.vertex(x, y, 0)); + contour.forEach(({ x, y }) => this._pInst.vertex(x, y, 0)); this._pInst.endContour(this._pInst.CLOSE); } - this._pInst.endShape(this._pInst.CLOSE); }); - + this._pInst.pop(); this._pInst._renderer._validateFaces = prevValidateFaces; }); if (extrude === 0) { - console.log('No extrusion'); return geom; } - // Step 3: Create extruded geometry with UNSHARED vertices for flat shading + const vertexIndices = {}; + const vertexId = v => `${v.x.toFixed(6)}-${v.y.toFixed(6)}-${v.z.toFixed(6)}`; + const newVertices = []; + const newVertexIndex = []; + + for (const v of geom.vertices) { + const id = vertexId(v); + if (!(id in vertexIndices)) { + const index = newVertices.length; + vertexIndices[id] = index; + newVertices.push(v.copy()); + } + newVertexIndex.push(vertexIndices[id]); + } + + // Remap faces to use deduplicated vertices + const newFaces = geom.faces.map(f => f.map(i => newVertexIndex[i])); + + //Find outer edges (edges that appear in only one face) + const seen = {}; + for (const face of newFaces) { + for (let off = 0; off < face.length; off++) { + const a = face[off]; + const b = face[(off + 1) % face.length]; + const id = `${Math.min(a, b)}-${Math.max(a, b)}`; + if (!seen[id]) seen[id] = []; + seen[id].push([a, b]); + } + } + const validEdges = []; + for (const key in seen) { + if (seen[key].length === 1) { + validEdges.push(seen[key][0]); + } + } + + console.log(`Found ${validEdges.length} outer edges from ${Object.keys(seen).length} total edges`); + + // Step 5: Create extruded geometry const extruded = this._pInst.buildGeometry(() => {}); const half = extrude * 0.5; - extruded.vertices = []; - extruded.vertexNormals = []; extruded.faces = []; + extruded.edges = []; // INITIALIZE EDGES ARRAY - let vertexIndex = 0; - const Vector = this._pInst.constructor.Vector; - // Helper to add a triangle with flat normal - const addTriangle = (v0, v1, v2) => { - const edge1 = Vector.sub(v1, v0); - const edge2 = Vector.sub(v2, v0); - const normal = Vector.cross(edge1, edge2); - if (normal.magSq() > 0.0001) { - normal.normalize(); - } else { - normal.set(0, 0, 1); - } - - // Add vertices (unshared - each triangle gets its own copies) - extruded.vertices.push(v0.copy(), v1.copy(), v2.copy()); - extruded.vertexNormals.push(normal.copy(), normal.copy(), normal.copy()); - extruded.faces.push([vertexIndex, vertexIndex + 1, vertexIndex + 2]); - vertexIndex += 3; - }; - - for (const face of geom.faces) { - if (face.length < 3) continue; - const v0 = geom.vertices[face[0]]; - for (let i = 1; i < face.length - 1; i++) { - const v1 = geom.vertices[face[i]]; - const v2 = geom.vertices[face[i + 1]]; - addTriangle( - new Vector(v0.x, v0.y, v0.z + half), - new Vector(v1.x, v1.y, v1.z + half), - new Vector(v2.x, v2.y, v2.z + half) - ); - } + // Add side face vertices (separate for each edge for flat shading) + for (const [a, b] of validEdges) { + const vA = newVertices[a]; + const vB = newVertices[b]; + // Skip if vertices are too close (degenerate edge) + const dist = Math.sqrt( + Math.pow(vB.x - vA.x, 2) + + Math.pow(vB.y - vA.y, 2) + + Math.pow(vB.z - vA.z, 2) + ); + if (dist < 0.0001) continue; + // Front face vertices + const frontA = extruded.vertices.length; + extruded.vertices.push(new Vector(vA.x, vA.y, vA.z + half)); + const frontB = extruded.vertices.length; + extruded.vertices.push(new Vector(vB.x, vB.y, vB.z + half)); + const backA = extruded.vertices.length; + extruded.vertices.push(new Vector(vA.x, vA.y, vA.z - half)); + const backB = extruded.vertices.length; + extruded.vertices.push(new Vector(vB.x, vB.y, vB.z - half)); + + extruded.faces.push([frontA, backA, backB]); + extruded.faces.push([frontA, backB, frontB]); + extruded.edges.push([frontA, frontB]); + extruded.edges.push([backA, backB]); + extruded.edges.push([frontA, backA]); + extruded.edges.push([frontB, backB]); } - for (const face of geom.faces) { - if (face.length < 3) continue; - const v0 = geom.vertices[face[0]]; - for (let i = 1; i < face.length - 1; i++) { - const v1 = geom.vertices[face[i]]; - const v2 = geom.vertices[face[i + 1]]; - addTriangle( - new Vector(v0.x, v0.y, v0.z - half), - new Vector(v2.x, v2.y, v2.z - half), - new Vector(v1.x, v1.y, v1.z - half) - ); - } + // Add front face (with unshared vertices for flat shading) + const frontVertexOffset = extruded.vertices.length; + for (const v of newVertices) { + extruded.vertices.push(new Vector(v.x, v.y, v.z + half)); } + for (const face of newFaces) { + if (face.length < 3) continue; + const mappedFace = face.map(i => i + frontVertexOffset); + extruded.faces.push(mappedFace); - // Side faces from edges - let edges = geom.edges; - if (!edges || !Array.isArray(edges)) { - edges = []; - const edgeSet = new Set(); - for (const face of geom.faces) { - for (let i = 0; i < face.length; i++) { - const a = face[i]; - const b = face[(i + 1) % face.length]; - if (a === b) continue; - const key = a < b ? `${a},${b}` : `${b},${a}`; - if (!edgeSet.has(key)) { - edgeSet.add(key); - edges.push([a, b]); - } - } + // ADD EDGES FOR FRONT FACE + for (let i = 0; i < mappedFace.length; i++) { + const nextIndex = (i + 1) % mappedFace.length; + extruded.edges.push([mappedFace[i], mappedFace[nextIndex]]); } } - const validEdges = edges.filter(([a, b]) => a !== b); - - for (const [a, b] of validEdges) { - const v0 = geom.vertices[a]; - const v1 = geom.vertices[b]; + // Add back face (reversed winding order) + const backVertexOffset = extruded.vertices.length; + for (const v of newVertices) { + extruded.vertices.push(new Vector(v.x, v.y, v.z - half)); + } - const vFront0 = new Vector(v0.x, v0.y, v0.z + half); - const vFront1 = new Vector(v1.x, v1.y, v1.z + half); - const vBack0 = new Vector(v0.x, v0.y, v0.z - half); - const vBack1 = new Vector(v1.x, v1.y, v1.z - half); + for (const face of newFaces) { + if (face.length < 3) continue; + const mappedFace = [...face].reverse().map(i => i + backVertexOffset); + extruded.faces.push(mappedFace); - // Two triangles forming the side quad - addTriangle(vFront0, vBack0, vBack1); - addTriangle(vFront0, vBack1, vFront1); + // ADD EDGES FOR BACK FACE + for (let i = 0; i < mappedFace.length; i++) { + const nextIndex = (i + 1) % mappedFace.length; + extruded.edges.push([mappedFace[i], mappedFace[nextIndex]]); + } } + + extruded.computeNormals(); return extruded; } From 1a582515c54c3f62f8fb00f1c46655d6fb895f02 Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Thu, 30 Oct 2025 10:24:05 +0530 Subject: [PATCH 5/7] Fix text extrusion with proper vertex deduplication, edge detection, and normal calculation with recomended chnages --- src/type/p5.Font.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 4ebf202639..9bde21d7da 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -549,22 +549,24 @@ export class Font { contours = [contours]; } + // Step 2: build base flat geometry - single shape const geom = this._pInst.buildGeometry(() => { const prevValidateFaces = this._pInst._renderer._validateFaces; this._pInst._renderer._validateFaces = true; - this._pInst.push(); - this._pInst.stroke(0); - contours.forEach(glyphContours => { - this._pInst.beginShape(); + this._pInst.beginShape(); + for (const glyphContours of contours) { for (const contour of glyphContours) { this._pInst.beginContour(); - contour.forEach(({ x, y }) => this._pInst.vertex(x, y, 0)); + for (const pt of contour) { + this._pInst.vertex(pt.x, pt.y, 0); + } this._pInst.endContour(this._pInst.CLOSE); } - this._pInst.endShape(this._pInst.CLOSE); - }); - this._pInst.pop(); + } + + this._pInst.endShape(this._pInst.CLOSE); + this._pInst._renderer._validateFaces = prevValidateFaces; }); @@ -572,6 +574,11 @@ export class Font { return geom; } + // The tessellation process creates separate vertices for each triangle, + // even when they share the same position. We need to deduplicate them + // to find which faces are actually connected, so we can identify the + // outer edges for extrusion. + const vertexIndices = {}; const vertexId = v => `${v.x.toFixed(6)}-${v.y.toFixed(6)}-${v.z.toFixed(6)}`; const newVertices = []; @@ -608,8 +615,6 @@ export class Font { } } - console.log(`Found ${validEdges.length} outer edges from ${Object.keys(seen).length} total edges`); - // Step 5: Create extruded geometry const extruded = this._pInst.buildGeometry(() => {}); const half = extrude * 0.5; @@ -622,11 +627,11 @@ export class Font { const vA = newVertices[a]; const vB = newVertices[b]; // Skip if vertices are too close (degenerate edge) - const dist = Math.sqrt( - Math.pow(vB.x - vA.x, 2) + - Math.pow(vB.y - vA.y, 2) + - Math.pow(vB.z - vA.z, 2) - ); + // Check if the cross product of edge vectors has sufficient magnitude + const edgeVector = new Vector(vB.x - vA.x, vB.y - vA.y, vB.z - vA.z); + const extrudeVector = new Vector(0, 0, extrude); + const crossProduct = p5.Vector.cross(edgeVector, extrudeVector); + if (crossProduct.mag() < 0.0001) continue; if (dist < 0.0001) continue; // Front face vertices const frontA = extruded.vertices.length; From 6894f2d1aa9a276bff9722867420da374da0c976 Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Thu, 30 Oct 2025 11:43:40 +0530 Subject: [PATCH 6/7] Fix text extrusion with proper vertex deduplication, edge detection, and normal calculation with recomended chnages --- src/type/p5.Font.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 9bde21d7da..59ff7905fe 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -630,7 +630,7 @@ export class Font { // Check if the cross product of edge vectors has sufficient magnitude const edgeVector = new Vector(vB.x - vA.x, vB.y - vA.y, vB.z - vA.z); const extrudeVector = new Vector(0, 0, extrude); - const crossProduct = p5.Vector.cross(edgeVector, extrudeVector); + const crossProduct = Vector.cross(edgeVector, extrudeVector); if (crossProduct.mag() < 0.0001) continue; if (dist < 0.0001) continue; // Front face vertices From 0b31fa5e456a086f3d525c834c3b363678f33cac Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Thu, 30 Oct 2025 11:52:06 +0530 Subject: [PATCH 7/7] Fix text extrusion with proper vertex deduplication, edge detection, and normal calculation with recomended chnages --- src/type/p5.Font.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 59ff7905fe..67d7a8f538 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -631,8 +631,8 @@ export class Font { const edgeVector = new Vector(vB.x - vA.x, vB.y - vA.y, vB.z - vA.z); const extrudeVector = new Vector(0, 0, extrude); const crossProduct = Vector.cross(edgeVector, extrudeVector); - if (crossProduct.mag() < 0.0001) continue; - if (dist < 0.0001) continue; + const dist = edgeVector.mag(); + if (crossProduct.mag() < 0.0001 || dist < 0.0001) continue; // Front face vertices const frontA = extruded.vertices.length; extruded.vertices.push(new Vector(vA.x, vA.y, vA.z + half));