From dcc63b4a34795c1d185f4c65d84b30e711c0eace Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 18 Oct 2023 21:34:16 +0900 Subject: [PATCH 01/74] Add triangle graph splitter class --- src/core/splitter/TriangleGraph.js | 75 ++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/core/splitter/TriangleGraph.js diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js new file mode 100644 index 00000000..a8a9555f --- /dev/null +++ b/src/core/splitter/TriangleGraph.js @@ -0,0 +1,75 @@ +import { Triangle, Matrix4 } from 'three'; + +class GraphTriangle { + + constructor() { + + this.edges = []; + this._triangle = new Triangle(); + + } + +} + +class GraphConnection { + + constructor() { + + this.start = - 1; + this.end = - 1; + this.required = false; + this.triangle1 = null; + this.triangle2 = null; + + } + +} + +export class TriangleGraph { + + constructor() { + + this.points = []; + this.connections = []; + this.triangles = []; + + this.frame = new Matrix4(); + + } + + initialize( tri ) { + + this.reset(); + + } + + reset() { + + // TODO: use a pool of objects here + this.points = []; + this.connections = []; + this.triangles = []; + + } + + splitBy( tri ) { + + // TODO + // - find intersection edge + // - transform tri or intersection edge into og tri frame w/ z up + // - which triangles the points line up on (or which edges they may lie on) to split and insert + // - possibly split the main triangle edges + // - triangulate the points + // - swap so the required edges are present + // - confirm on edges cross?? + // OR + // - just clip into separate shapes? Use earcut to triangulate? + + } + + forEachTriangle( cb ) { + + + } + +} From 6c224435cef79bbe0b939982fd6bc9df3734c794 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 20 Oct 2023 20:24:03 +0900 Subject: [PATCH 02/74] Add classes --- src/core/splitter/GraphConnection.js | 13 +++++++++++++ src/core/splitter/GraphTriangle.js | 9 +++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/core/splitter/GraphConnection.js create mode 100644 src/core/splitter/GraphTriangle.js diff --git a/src/core/splitter/GraphConnection.js b/src/core/splitter/GraphConnection.js new file mode 100644 index 00000000..e4dbbbd4 --- /dev/null +++ b/src/core/splitter/GraphConnection.js @@ -0,0 +1,13 @@ +export class GraphConnection { + + constructor() { + + this.start = - 1; + this.end = - 1; + this.required = false; + this.triangle1 = null; + this.triangle2 = null; + + } + +} diff --git a/src/core/splitter/GraphTriangle.js b/src/core/splitter/GraphTriangle.js new file mode 100644 index 00000000..3f82f4cf --- /dev/null +++ b/src/core/splitter/GraphTriangle.js @@ -0,0 +1,9 @@ +export class GraphTriangle { + + constructor() { + + this.edges = []; + + } + +} From c9a6980f700ab237c151977a39b73d24ac39e4ff Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 17:10:39 +0900 Subject: [PATCH 03/74] Updates --- src/core/splitter/TriangleGraph.js | 194 ++++++++++++++++++++++++---- src/core/splitter/utils.js | 195 +++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+), 25 deletions(-) create mode 100644 src/core/splitter/utils.js diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index a8a9555f..756b2de3 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -1,29 +1,7 @@ -import { Triangle, Matrix4 } from 'three'; +import { Triangle, Matrix4, Line3, Plane, Vector3 } from 'three'; +import { getIntersectedLine, transformToFrame } from './utils'; -class GraphTriangle { - - constructor() { - - this.edges = []; - this._triangle = new Triangle(); - - } - -} - -class GraphConnection { - - constructor() { - - this.start = - 1; - this.end = - 1; - this.required = false; - this.triangle1 = null; - this.triangle2 = null; - - } - -} +const EPSILON = 1e-10; export class TriangleGraph { @@ -33,7 +11,10 @@ export class TriangleGraph { this.connections = []; this.triangles = []; + this.initialTri = new Triangle(); + this.plane = new Plane(); this.frame = new Matrix4(); + this.invFrame = new Matrix4(); } @@ -41,6 +22,27 @@ export class TriangleGraph { this.reset(); + const norm = new Vector3(); + const right = new Vector3(); + const up = new Vector3(); + + tri.getNormal( norm ); + right.subVectors( tri.a, tri.b ).normalize(); + up.crossVectors( norm, right ); + + const { frame, invFrame, initialTri, points } = this; + frame.makeBasis( right, up, norm ).setPosition( tri.a ); + invFrame.copy( frame ).invert(); + + initialTri.copy( tri ); + transformToFrame( initialTri, frame ); + + points.push( + tri.a.clone(), + tri.b.clone(), + tri.c.clone(), + ); + } reset() { @@ -54,6 +56,127 @@ export class TriangleGraph { splitBy( tri ) { + const { plane, frame, initialTri } = this; + + tri = tri.clone(); + transformToFrame( tri, frame ); + + const line = new Line3(); + const hitPoint = new Vector3(); + const arr = [ tri.a, tri.b, tri.c ]; + const points = []; + let coplanarPoints = 0; + + for ( let i = 0; i < 3; i ++ ) { + + const ni = ( i + 1 ) % 3; + const p0 = arr[ i ]; + const p1 = arr[ ni ]; + const d0 = plane.distanceToPoint( p0 ); + + if ( d0 < EPSILON ) { + + coplanarPoints ++; + + } + + line.start.copy( p0 ); + line.end.copy( p1 ); + + if ( ! plane.intersectLine( line, hitPoint ) || hitPoint.distanceTo( p1 ) < EPSILON ) { + + if ( d0 < EPSILON ) { + + hitPoint.copy( p0 ); + + } else { + + continue; + + } + + } + + points.push( hitPoint.clone() ); + + } + + const edges = []; + if ( coplanarPoints === 3 ) { + + for ( let i = 0; i < 3; i ++ ) { + + const ni = ( i + 1 ) % 3; + const p0 = arr[ i ]; + const p1 = arr[ ni ]; + + const c0 = initialTri.containsPoint( p0 ); + const c1 = initialTri.containsPoint( p1 ); + if ( c0 && c1 ) { + + const line = new Line3(); + line.start.copy( p0 ); + line.end.copy( p1 ); + edges.push( line ); + + } else { + + const result = new Line3(); + const edge = new Line3(); + edge.start.copy( p0 ); + edge.end.copy( p1 ); + if ( getIntersectedLine( edge, initialTri, result ) ) { + + edges.push( result.clone() ); + + } + + } + + } + + } else { + + const result = new Line3(); + const edge = new Line3(); + edge.start.copy( points[ 0 ] ); + edge.end.copy( points[ 1 ] ); + if ( getIntersectedLine( edge, initialTri, result ) ) { + + edges.push( result.clone() ); + + } + + } + + // deduplicate and add edges + for ( let i = 0, l = edges.length; i < l; i ++ ) { + + const edge = edges[ i ]; + let startIndex = this.findClosestPointIndex( edge.start ); + if ( startIndex === null ) { + + startIndex = points.length; + points.push( edge.start.clone() ); + + } + + let endIndex = this.findClosestPointIndex( edge.start ); + if ( endIndex === null ) { + + endIndex = points.length; + points.push( edge.end.clone() ); + + } + + } + + // TODO: insert edges + // - detect crossings with other edges and swap them to insert + // - possibly need to store triangles to swap + + + // TODO // - find intersection edge // - transform tri or intersection edge into og tri frame w/ z up @@ -67,6 +190,27 @@ export class TriangleGraph { } + findClosestPointIndex( p ) { + + const points = this.points; + let closestIndex = null; + let closestDist = Infinity; + for ( let i = 0, l = points.length; i < l; i ++ ) { + + const d = p.distanceTo( points[ i ] ); + if ( d < EPSILON && d < closestDist ) { + + closestIndex = i; + closestDist = d; + + } + + } + + return closestIndex; + + } + forEachTriangle( cb ) { diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js new file mode 100644 index 00000000..1683b39a --- /dev/null +++ b/src/core/splitter/utils.js @@ -0,0 +1,195 @@ +import { Vector3, Line3 } from 'three'; + +export function transformToFrame( tri, frame ) { + + tri.a.applyMatrix4( frame ).z = 0; + tri.b.applyMatrix4( frame ).z = 0; + tri.c.applyMatrix4( frame ).z = 0; + return tri; + +} + +function isPositive( start, end, intersection ) { // all parameters are THREE.Vector3() + + let v1 = new Vector3().copy( end ).sub( start ); + let v2 = new Vector3().copy( intersection ).sub( start ); + return v1.dot( v2 ) >= 0; + +} + +// checks if two line segments will intersect on a point +// when traveling in one specific direction from start to end +// but not in the direction from end to start +// params (THREE.Line3, THREE.Line3) +export function getIntersectionOnAPoint( line1, line2 ) { + + let intersection = null; + let A = line1.start; + let B = line1.end; + let C = line2.start; + let D = line2.end; + + // Line AB represented as a1x + b1y = c1 + let a1 = B.y - A.y; + let b1 = A.x - B.x; + let c1 = a1 * ( A.x ) + b1 * ( A.y ); + + // Line CD represented as a2x + b2y = c2 + let a2 = D.y - C.y; + let b2 = C.x - D.x; + let c2 = a2 * ( C.x ) + b2 * ( C.y ); + + let determinant = a1 * b2 - a2 * b1; + + if ( determinant == 0 ) { + + // The lines are parallel. + + } else { + + let x = ( b2 * c1 - b1 * c2 ) / determinant; + let y = ( a1 * c2 - a2 * c1 ) / determinant; + intersection = new Vector3( x, y ); + + } + + // ??? + // if there is an intersection. verify intersection occurs on the two line segments + // when calculating from start to end + if ( intersection ) { + + let line1result = isPositive( line1.start, line1.end, intersection ); + let line2result = isPositive( line2.start, line2.end, intersection ); + if ( line1result && line2result ) { + + // do nothing when the intersection is not "false" as both results are "true" + + } else { + + // set intersection to null when the intersection is "false" as one of results is "false" + intersection = null; + + } + + } + + return intersection; + +} + + +// Determine the intersection point of two line segments +// Return FALSE if the lines don't intersect +// https://paulbourke.net/geometry/pointlineplane/ +export function lineIntersect( l1, l2, target ) { + + const x1 = l1.start.x; + const y1 = l1.start.y; + + const x2 = l1.end.x; + const y2 = l1.end.y; + + const x3 = l2.start.x; + const y3 = l2.start.y; + + const x4 = l2.end.x; + const y4 = l2.end.y; + + const EPS = 1e-10; + let mua, mub; + let denom, numera, numerb; + + denom = ( y4 - y3 ) * ( x2 - x1 ) - ( x4 - x3 ) * ( y2 - y1 ); + numera = ( x4 - x3 ) * ( y1 - y3 ) - ( y4 - y3 ) * ( x1 - x3 ); + numerb = ( x2 - x1 ) * ( y1 - y3 ) - ( y2 - y1 ) * ( x1 - x3 ); + + /* Are the line coincident? */ + if ( Math.abs( numera ) < EPS && Math.abs( numerb ) < EPS && Math.abs( denom ) < EPS ) { + + target.x = ( x1 + x2 ) / 2; + target.y = ( y1 + y2 ) / 2; + return true; + + } + + /* Are the line parallel */ + if ( Math.abs( denom ) < EPS ) { + + target.x = 0; + target.y = 0; + return false; + + } + + /* Is the intersection along the the segments */ + mua = numera / denom; + mub = numerb / denom; + if ( mua < 0 || mua > 1 || mub < 0 || mub > 1 ) { + + target.x = 0; + target.y = 0; + + return false; + + } + + target.x = x1 + mua * ( x2 - x1 ); + target.y = y1 + mua * ( y2 - y1 ); + + return true; + +} + +export function getIntersectedLine( line, tri, target ) { + + let setCount = 0; + const vec = new Vector3(); + const edge = new Line3(); + const arr = [ tri.a, tri.b, tri.c ]; + for ( let i = 0; i < 3; i ++ ) { + + const ni = ( i + 1 ) % 3; + edge.start.set( arr[ i ] ); + edge.end.set( arr[ ni ] ); + + if ( lineIntersect( edge, line, vec ) ) { + + if ( setCount === 2 ) { + + // UH OH + + } else if ( setCount === 1 ) { + + target.start.copy( vec ); + + } else { + + target.end.copy( vec ); + + } + + setCount ++; + + } + + } + + if ( setCount === 1 ) { + + if ( tri.containsPoint( line.start ) ) { + + target.end.copy( line.start ); + setCount ++; + + } else if ( tri.containsPoint( line.end ) ) { + + target.end.copy( line.end ); + setCount ++; + + } + + } + + return setCount === 2; + +} From 372466d2be82540e3e0f0a1a5f0a5eaee6ae0357 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 17:25:26 +0900 Subject: [PATCH 04/74] Fixed references --- src/core/splitter/TriangleGraph.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index 756b2de3..c8651683 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -64,7 +64,7 @@ export class TriangleGraph { const line = new Line3(); const hitPoint = new Vector3(); const arr = [ tri.a, tri.b, tri.c ]; - const points = []; + const planePoints = []; let coplanarPoints = 0; for ( let i = 0; i < 3; i ++ ) { @@ -97,7 +97,7 @@ export class TriangleGraph { } - points.push( hitPoint.clone() ); + planePoints.push( hitPoint.clone() ); } @@ -139,8 +139,8 @@ export class TriangleGraph { const result = new Line3(); const edge = new Line3(); - edge.start.copy( points[ 0 ] ); - edge.end.copy( points[ 1 ] ); + edge.start.copy( planePoints[ 0 ] ); + edge.end.copy( planePoints[ 1 ] ); if ( getIntersectedLine( edge, initialTri, result ) ) { edges.push( result.clone() ); @@ -169,8 +169,11 @@ export class TriangleGraph { } + connections.push( { start: startIndex, end: endIndex } ); + } + // TODO: insert edges // - detect crossings with other edges and swap them to insert // - possibly need to store triangles to swap From 845c0e6735cce1b8ef9f4ced4800c6da75533c9d Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 18:01:23 +0900 Subject: [PATCH 05/74] More utils --- src/core/splitter/EdgeGraph.js | 114 +++++++++++++++++++++++++++++ src/core/splitter/GraphTriangle.js | 9 --- src/core/splitter/TriangleGraph.js | 8 +- 3 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 src/core/splitter/EdgeGraph.js delete mode 100644 src/core/splitter/GraphTriangle.js diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js new file mode 100644 index 00000000..f7e1cd7d --- /dev/null +++ b/src/core/splitter/EdgeGraph.js @@ -0,0 +1,114 @@ +import { Vector3, Line3 } from 'three'; + +class IndexedLine3 extends Line3 { + + constructor( ...args ) { + + super( ...args ); + this.startIndex = - 1; + this.endIndex = - 1; + + } + +} + +const EPSILON = 1e-10; +export class EdgeGraph { + + constructor() { + + this.points = []; + this.edges = []; + + } + + insertEdge( edge ) { + + // TODO: check intersections? + + const { points, edges } = this; + const { start, end } = edge; + const i0 = this.insertPoint( start ); + const i1 = this.insertPoint( end ); + + const line = new IndexedLine3(); + line.start.copy( points[ i0 ] ); + line.startIndex = i0; + line.end.copy( points[ i1 ] ); + line.endIndex = i1; + + edges.push( line ); + + } + + insertPoint( point ) { + + const { edges, points } = this; + let index = this.findClosestPointIndex( point ); + if ( index === null ) { + + const vec = new Vector3(); + const intersectingEdge = edges.findIndex( e => { + + e.closestPointToPoint( point, vec ); + return vec.distanceTo( point ) < EPSILON; + + } ); + + if ( intersectingEdge === - 1 ) { + + index = points.length; + points.push( point.clone() ); + + } else { + + index = points.length; + points.push( point.clone() ); + + const e = edges[ intersectingEdge ]; + edges.splice( intersectingEdge, 1 ); + + const l0 = new IndexedLine3(); + l0.start.copy( e.start ); + l0.startIndex = e.startIndex; + l0.end.copy( point ); + l0.endIndex = index; + + const l1 = new IndexedLine3(); + l1.start.copy( point ); + l1.startIndex = index; + l1.end.copy( e.end ); + l1.endIndex = e.endIndex; + + edges.push( l0, l1 ); + + } + + } + + return index; + + } + + findClosestPointIndex( p ) { + + const points = this.points; + let closestIndex = null; + let closestDist = Infinity; + for ( let i = 0, l = points.length; i < l; i ++ ) { + + const d = p.distanceTo( points[ i ] ); + if ( d < EPSILON && d < closestDist ) { + + closestIndex = i; + closestDist = d; + + } + + } + + return closestIndex; + + } + +} diff --git a/src/core/splitter/GraphTriangle.js b/src/core/splitter/GraphTriangle.js deleted file mode 100644 index 3f82f4cf..00000000 --- a/src/core/splitter/GraphTriangle.js +++ /dev/null @@ -1,9 +0,0 @@ -export class GraphTriangle { - - constructor() { - - this.edges = []; - - } - -} diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index c8651683..b698e6d8 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -35,7 +35,7 @@ export class TriangleGraph { invFrame.copy( frame ).invert(); initialTri.copy( tri ); - transformToFrame( initialTri, frame ); + transformToFrame( initialTri, invFrame ); points.push( tri.a.clone(), @@ -56,10 +56,10 @@ export class TriangleGraph { splitBy( tri ) { - const { plane, frame, initialTri } = this; + const { plane, invFrame, initialTri, points, connections } = this; tri = tri.clone(); - transformToFrame( tri, frame ); + transformToFrame( tri, invFrame ); const line = new Line3(); const hitPoint = new Vector3(); @@ -67,6 +67,7 @@ export class TriangleGraph { const planePoints = []; let coplanarPoints = 0; + // find the points on the plane surface for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; @@ -101,6 +102,7 @@ export class TriangleGraph { } + // find the edges that intersect with the triangle itself const edges = []; if ( coplanarPoints === 3 ) { From 784bcd6afa0b1268abad12336c46efc74470969e Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 20:33:46 +0900 Subject: [PATCH 06/74] add fields --- src/core/splitter/EdgeGraph.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index f7e1cd7d..e076c02e 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -8,6 +8,11 @@ class IndexedLine3 extends Line3 { this.startIndex = - 1; this.endIndex = - 1; + this.positiveTriangle = - 1; + this.negativeTriangle = - 1; + + this.required = false; + } } @@ -73,12 +78,14 @@ export class EdgeGraph { l0.startIndex = e.startIndex; l0.end.copy( point ); l0.endIndex = index; + l0.required = true; const l1 = new IndexedLine3(); l1.start.copy( point ); l1.startIndex = index; l1.end.copy( e.end ); l1.endIndex = e.endIndex; + l1.required = true; edges.push( l0, l1 ); From 3639b602ce37667187af37ab5b18f2151f811a7f Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 20:38:13 +0900 Subject: [PATCH 07/74] Use the graph --- src/core/splitter/TriangleGraph.js | 83 +++++++----------------------- 1 file changed, 18 insertions(+), 65 deletions(-) diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index b698e6d8..5cc4099a 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -1,5 +1,6 @@ import { Triangle, Matrix4, Line3, Plane, Vector3 } from 'three'; import { getIntersectedLine, transformToFrame } from './utils'; +import { EdgeGraph } from './EdgeGraph'; const EPSILON = 1e-10; @@ -7,9 +8,7 @@ export class TriangleGraph { constructor() { - this.points = []; - this.connections = []; - this.triangles = []; + this.graph = new EdgeGraph(); this.initialTri = new Triangle(); this.plane = new Plane(); @@ -30,33 +29,37 @@ export class TriangleGraph { right.subVectors( tri.a, tri.b ).normalize(); up.crossVectors( norm, right ); - const { frame, invFrame, initialTri, points } = this; + const { frame, invFrame, initialTri, graph } = this; frame.makeBasis( right, up, norm ).setPosition( tri.a ); invFrame.copy( frame ).invert(); initialTri.copy( tri ); transformToFrame( initialTri, invFrame ); - points.push( - tri.a.clone(), - tri.b.clone(), - tri.c.clone(), - ); + const arr = [ tri.a, tri.b, tri.c ]; + for ( let i = 0; i < 3; i ++ ) { + + const line = new Line3(); + const ni = ( i + 1 ) % 3; + + line.start.copy( arr[ i ] ); + line.end.copy( arr[ ni ] ); + graph.insertEdge( line ); + + + } } reset() { - // TODO: use a pool of objects here - this.points = []; - this.connections = []; - this.triangles = []; + this.graph = new EdgeGraph(); } splitBy( tri ) { - const { plane, invFrame, initialTri, points, connections } = this; + const { plane, invFrame, initialTri, graph } = this; tri = tri.clone(); transformToFrame( tri, invFrame ); @@ -154,34 +157,10 @@ export class TriangleGraph { // deduplicate and add edges for ( let i = 0, l = edges.length; i < l; i ++ ) { - const edge = edges[ i ]; - let startIndex = this.findClosestPointIndex( edge.start ); - if ( startIndex === null ) { - - startIndex = points.length; - points.push( edge.start.clone() ); - - } - - let endIndex = this.findClosestPointIndex( edge.start ); - if ( endIndex === null ) { - - endIndex = points.length; - points.push( edge.end.clone() ); - - } - - connections.push( { start: startIndex, end: endIndex } ); + graph.insertEdge( edges[ i ] ); } - - // TODO: insert edges - // - detect crossings with other edges and swap them to insert - // - possibly need to store triangles to swap - - - // TODO // - find intersection edge // - transform tri or intersection edge into og tri frame w/ z up @@ -195,30 +174,4 @@ export class TriangleGraph { } - findClosestPointIndex( p ) { - - const points = this.points; - let closestIndex = null; - let closestDist = Infinity; - for ( let i = 0, l = points.length; i < l; i ++ ) { - - const d = p.distanceTo( points[ i ] ); - if ( d < EPSILON && d < closestDist ) { - - closestIndex = i; - closestDist = d; - - } - - } - - return closestIndex; - - } - - forEachTriangle( cb ) { - - - } - } From 6e0da7a1f8bb46b906015f4651cd073d747d5bf6 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 21:07:35 +0900 Subject: [PATCH 08/74] Fixes --- src/core/splitter/EdgeGraph.js | 2 +- src/core/splitter/GraphConnection.js | 13 ------------- src/core/splitter/TriangleGraph.js | 2 +- 3 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 src/core/splitter/GraphConnection.js diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index e076c02e..f47bada9 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -55,7 +55,7 @@ export class EdgeGraph { const vec = new Vector3(); const intersectingEdge = edges.findIndex( e => { - e.closestPointToPoint( point, vec ); + e.closestPointToPoint( point, true, vec ); return vec.distanceTo( point ) < EPSILON; } ); diff --git a/src/core/splitter/GraphConnection.js b/src/core/splitter/GraphConnection.js deleted file mode 100644 index e4dbbbd4..00000000 --- a/src/core/splitter/GraphConnection.js +++ /dev/null @@ -1,13 +0,0 @@ -export class GraphConnection { - - constructor() { - - this.start = - 1; - this.end = - 1; - this.required = false; - this.triangle1 = null; - this.triangle2 = null; - - } - -} diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index 5cc4099a..ae27321e 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -57,7 +57,7 @@ export class TriangleGraph { } - splitBy( tri ) { + splitByTriangle( tri ) { const { plane, invFrame, initialTri, graph } = this; From 16ccb1e1e29ce428454b512b32529f8b6710a804 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 21:08:15 +0900 Subject: [PATCH 09/74] Fixes --- src/core/splitter/TriangleGraph.js | 16 ++++++++++++---- src/core/splitter/utils.js | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index ae27321e..e67d461a 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -29,14 +29,15 @@ export class TriangleGraph { right.subVectors( tri.a, tri.b ).normalize(); up.crossVectors( norm, right ); - const { frame, invFrame, initialTri, graph } = this; + const { frame, invFrame, initialTri, graph, plane } = this; + tri.getPlane( plane ); frame.makeBasis( right, up, norm ).setPosition( tri.a ); invFrame.copy( frame ).invert(); initialTri.copy( tri ); transformToFrame( initialTri, invFrame ); - const arr = [ tri.a, tri.b, tri.c ]; + const arr = [ initialTri.a, initialTri.b, initialTri.c ]; for ( let i = 0; i < 3; i ++ ) { const line = new Line3(); @@ -59,10 +60,9 @@ export class TriangleGraph { splitByTriangle( tri ) { - const { plane, invFrame, initialTri, graph } = this; + const { plane, invFrame, frame, initialTri, graph } = this; tri = tri.clone(); - transformToFrame( tri, invFrame ); const line = new Line3(); const hitPoint = new Vector3(); @@ -105,6 +105,13 @@ export class TriangleGraph { } + planePoints.forEach( p => { + + p.applyMatrix4( invFrame ); + p.z = 0; + + } ); + // find the edges that intersect with the triangle itself const edges = []; if ( coplanarPoints === 3 ) { @@ -146,6 +153,7 @@ export class TriangleGraph { const edge = new Line3(); edge.start.copy( planePoints[ 0 ] ); edge.end.copy( planePoints[ 1 ] ); + if ( getIntersectedLine( edge, initialTri, result ) ) { edges.push( result.clone() ); diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index 1683b39a..2a0c9652 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -149,8 +149,8 @@ export function getIntersectedLine( line, tri, target ) { for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; - edge.start.set( arr[ i ] ); - edge.end.set( arr[ ni ] ); + edge.start.copy( arr[ i ] ); + edge.end.copy( arr[ ni ] ); if ( lineIntersect( edge, line, vec ) ) { From c5b5a068b233f5f80b82fe38fe40234b3f120b99 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 21:08:51 +0900 Subject: [PATCH 10/74] Add debug helper --- examples/triangleGraphSplit.html | 52 ++++++++++++ examples/triangleGraphSplit.js | 136 +++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 examples/triangleGraphSplit.html create mode 100644 examples/triangleGraphSplit.js diff --git a/examples/triangleGraphSplit.html b/examples/triangleGraphSplit.html new file mode 100644 index 00000000..a9b4f42d --- /dev/null +++ b/examples/triangleGraphSplit.html @@ -0,0 +1,52 @@ + + + + three-bvh-csg - test + + + + + +
+ + + diff --git a/examples/triangleGraphSplit.js b/examples/triangleGraphSplit.js new file mode 100644 index 00000000..e4d2e92a --- /dev/null +++ b/examples/triangleGraphSplit.js @@ -0,0 +1,136 @@ +import * as THREE from 'three'; +import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { EdgesHelper, PointsHelper, TriangleSetHelper } from '../src/index.js'; +import { TriangleGraph } from '../src/core/splitter/TriangleGraph.js'; + +let renderer, camera, scene; +let controls, transformControls; +let planeObject, planeHelper; +let splitter, clippedTris, initialTris, pointsHelper, edgesHelper; +let plane = new THREE.Plane(); +let _vec = new THREE.Vector3(); + +const ogTris = [ + new THREE.Triangle( + new THREE.Vector3( - 0.25, 1.25, 0.25 ), + new THREE.Vector3( - 0.25, 0.25, 0.25 ), + new THREE.Vector3( - 1.25, 1.25, 0.25 ), + ) +]; + +const tris = [ + // new THREE.Triangle( + // new THREE.Vector3( - 0.5, 0.5, - 0.5 ), + // new THREE.Vector3( - 0.5, - 0.5, - 0.5 ), + // new THREE.Vector3( - 0.5, 0.5, 0.5 ), + // ), + new THREE.Triangle( + new THREE.Vector3( - 0.5, 0.5, - 0.5 ), + new THREE.Vector3( - 0.5, 0.5, 0.5 ), + new THREE.Vector3( 0.5, 0.5, - 0.5 ), + ), + // new THREE.Triangle( + // new THREE.Vector3( -0.5, 0.5, 0.5 ), + // new THREE.Vector3( 0.5, 0.5, 0.5 ), + // new THREE.Vector3( 0.5, 0.5, -0.5 ), + // ), +]; + +init(); +render(); + +function init() { + + const bgColor = 0x111111; + + // renderer setup + renderer = new THREE.WebGLRenderer( { antialias: true } ); + renderer.setPixelRatio( window.devicePixelRatio ); + renderer.setSize( window.innerWidth, window.innerHeight ); + renderer.setClearColor( bgColor, 1 ); + document.body.appendChild( renderer.domElement ); + + // scene setup + scene = new THREE.Scene(); + scene.fog = new THREE.Fog( 0xffca28, 20, 60 ); + + const light = new THREE.DirectionalLight( 0xffffff, 3.5 ); + light.position.set( - 1, 2, 3 ); + scene.add( light ); + scene.add( new THREE.AmbientLight( 0xb0bec5, 0.35 ) ); + + // camera setup + camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 ); + camera.position.set( 1, 2, 4 ); + camera.far = 100; + camera.updateProjectionMatrix(); + + controls = new OrbitControls( camera, renderer.domElement ); + + transformControls = new TransformControls( camera, renderer.domElement ); + transformControls.addEventListener( 'dragging-changed', e => { + + controls.enabled = ! e.value; + + } ); + scene.add( transformControls ); + + planeObject = new THREE.Object3D(); + transformControls.attach( planeObject ); + scene.add( planeObject ); + planeObject.position.z = 0.5; + planeObject.rotation.y = Math.PI / 2; + + planeHelper = new THREE.PlaneHelper( plane ); + scene.add( planeHelper ); + + initialTris = new TriangleSetHelper(); + clippedTris = new TriangleSetHelper(); + pointsHelper = new PointsHelper(); + edgesHelper = new EdgesHelper(); + + splitter = new TriangleGraph(); + + scene.add( initialTris, clippedTris, pointsHelper, edgesHelper ); + + window.addEventListener( 'resize', function () { + + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + + renderer.setSize( window.innerWidth, window.innerHeight ); + + }, false ); + +} + +function render() { + + requestAnimationFrame( render ); + + _vec.set( 0, 0, 1 ).transformDirection( planeObject.matrixWorld ); + plane.setFromNormalAndCoplanarPoint( _vec, planeObject.position ); + + splitter.initialize( ogTris[ 0 ] ); + tris.forEach( t => { + + splitter.splitByTriangle( t ); + + } ); + + planeHelper.visible = false; + transformControls.visible = false; + transformControls.enabled = false; + + pointsHelper.setPoints( splitter.graph.points ); + edgesHelper.setEdges( splitter.graph.edges ); + + clippedTris.setTriangles( ogTris ); + initialTris.setTriangles( tris ); + // clippedTris.setTriangles( splitter.triangles ); + // initialTris.setTriangles( [ ...ogTris, ...tris ] ); + + renderer.render( scene, camera ); + +} From 07eacfd697ad3f5cf477d1800fa342a7ce50c29b Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 21:15:47 +0900 Subject: [PATCH 11/74] rename --- src/core/splitter/EdgeGraph.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index f47bada9..025ac411 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -8,8 +8,8 @@ class IndexedLine3 extends Line3 { this.startIndex = - 1; this.endIndex = - 1; - this.positiveTriangle = - 1; - this.negativeTriangle = - 1; + this.triangle = null; + this.reverseTriangle = null; this.required = false; From aad00b08963a821cbaf3fbac7721e04715643da3 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 3 Jan 2024 21:19:08 +0900 Subject: [PATCH 12/74] Classes --- src/core/splitter/EdgeGraph.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 025ac411..9b56a430 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -1,6 +1,21 @@ -import { Vector3, Line3 } from 'three'; +import { Vector3, Line3, Triangle } from 'three'; -class IndexedLine3 extends Line3 { +class GraphTriangle extends Triangle { + + constructor( ...args ) { + + super( ...args ); + + this.ab = null; + this.bc = null; + this.ca = null; + this.edges = [ null, null, null ]; + + } + +} + +class GraphEdge extends Line3 { constructor( ...args ) { @@ -29,14 +44,12 @@ export class EdgeGraph { insertEdge( edge ) { - // TODO: check intersections? - const { points, edges } = this; const { start, end } = edge; const i0 = this.insertPoint( start ); const i1 = this.insertPoint( end ); - const line = new IndexedLine3(); + const line = new GraphEdge(); line.start.copy( points[ i0 ] ); line.startIndex = i0; line.end.copy( points[ i1 ] ); @@ -44,6 +57,8 @@ export class EdgeGraph { edges.push( line ); + // TODO: check and swap intersections + } insertPoint( point ) { @@ -73,14 +88,14 @@ export class EdgeGraph { const e = edges[ intersectingEdge ]; edges.splice( intersectingEdge, 1 ); - const l0 = new IndexedLine3(); + const l0 = new GraphEdge(); l0.start.copy( e.start ); l0.startIndex = e.startIndex; l0.end.copy( point ); l0.endIndex = index; l0.required = true; - const l1 = new IndexedLine3(); + const l1 = new GraphEdge(); l1.start.copy( point ); l1.startIndex = index; l1.end.copy( e.end ); From 22c22bdc5ff119c290c4d71464fb7556bfb71e8a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 01:17:53 +0900 Subject: [PATCH 13/74] Get splitting working in one case --- src/core/splitter/EdgeGraph.js | 136 ++++++++++++++++++++++++++--- src/core/splitter/TriangleGraph.js | 13 +-- 2 files changed, 125 insertions(+), 24 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 9b56a430..21b3da4e 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -6,10 +6,39 @@ class GraphTriangle extends Triangle { super( ...args ); - this.ab = null; - this.bc = null; - this.ca = null; this.edges = [ null, null, null ]; + this.points = [ this.a, this.b, this.c ]; + + } + + setEdge( index, edge, reversed ) { + + this.edges[ index ] = { edge, reversed }; + + if ( reversed ) { + + edge.reverseTriangle = this; + this.points[ index ].copy( edge.end ); + + } else { + + edge.triangle = this; + this.points[ index ].copy( edge.start ); + + } + + } + + getEdgeIndex( edge ) { + + return this.edges.findIndex( info => info.edge === edge ); + + } + + getVertexIndex( index ) { + + const info = this.edges[ index ]; + return info.reversed ? info.edge.endIndex : info.edge.startIndex; } @@ -39,6 +68,35 @@ export class EdgeGraph { this.points = []; this.edges = []; + this.triangles = []; + + } + + initialize( tri ) { + + const arr = [ tri.a, tri.b, tri.c ]; + const { triangles, points, edges } = this; + + // initialize the first triangle if we find three points + const newTriangle = new GraphTriangle(); + for ( let i = 0; i < 3; i ++ ) { + + const ni = ( i + 1 ) % 3; + const p0 = arr[ i ]; + const p1 = arr[ ni ]; + const line = new GraphEdge(); + line.start.copy( p0 ); + line.startIndex = i; + line.end.copy( p1 ); + line.endIndex = ni; + + newTriangle.setEdge( i, line, false ); + edges.push( line ); + + } + + triangles.push( newTriangle ); + points.push( tri.a.clone(), tri.b.clone(), tri.c.clone() ); } @@ -55,15 +113,17 @@ export class EdgeGraph { line.end.copy( points[ i1 ] ); line.endIndex = i1; - edges.push( line ); + // edges.push( line ); - // TODO: check and swap intersections + // TODO: check for intersections and swap triangle orientations, then add + // a required edge + // TODO: after swapping and making way for new edges we may want to mark edges as "required" } insertPoint( point ) { - const { edges, points } = this; + const { edges, points, triangles } = this; let index = this.findClosestPointIndex( point ); if ( index === null ) { @@ -77,32 +137,78 @@ export class EdgeGraph { if ( intersectingEdge === - 1 ) { - index = points.length; - points.push( point.clone() ); + const containingTriangle = triangles.findIndex( t => t.containsPoint( point ) ); + if ( containingTriangle === - 1 ) { + + // TODO: this should never happen + index = points.length; + points.push( point.clone() ); + + } else { + + // TODO: split into three triangles + + } } else { index = points.length; points.push( point.clone() ); + // NOTE: if the edge is required here then we have a problem - it shouldn't have to be split const e = edges[ intersectingEdge ]; - edges.splice( intersectingEdge, 1 ); - const l0 = new GraphEdge(); l0.start.copy( e.start ); l0.startIndex = e.startIndex; l0.end.copy( point ); l0.endIndex = index; - l0.required = true; + l0.required = e.required; const l1 = new GraphEdge(); l1.start.copy( point ); l1.startIndex = index; l1.end.copy( e.end ); l1.endIndex = e.endIndex; - l1.required = true; + l1.required = e.required; edges.push( l0, l1 ); + edges.splice( intersectingEdge, 1 ); + + if ( e.triangle ) { + + const edgeIndex = e.triangle.getEdgeIndex( e ); + const nextEdgeIndex = ( edgeIndex + 2 ) % 3; + + const insertedEdge = new GraphEdge(); + insertedEdge.start.copy( point ); + insertedEdge.startIndex = index; + insertedEdge.end.copy( e.triangle.points[ nextEdgeIndex ] ); + insertedEdge.endIndex = e.triangle.getVertexIndex( nextEdgeIndex ); + edges.push( insertedEdge ); + + const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; + const newTri0 = new GraphTriangle(); + newTri0.setEdge( 0, l0, e.triangle.edges[ edgeIndex ].reversed ); + newTri0.setEdge( 1, insertedEdge, false ); + newTri0.setEdge( 2, e.triangle.edges[ finalEdgeIndex0 ].edge, e.triangle.edges[ finalEdgeIndex0 ].reversed ); + + const finalEdgeIndex1 = ( edgeIndex + 1 ) % 3; + const newTri1 = new GraphTriangle(); + newTri1.setEdge( 0, l1, e.triangle.edges[ edgeIndex ].reversed ); + newTri1.setEdge( 1, e.triangle.edges[ finalEdgeIndex1 ].edge, e.triangle.edges[ finalEdgeIndex1 ].reversed ); + newTri1.setEdge( 2, insertedEdge, true ); + + triangles.splice( triangles.indexOf( e.triangle ), 1 ); + triangles.push( newTri0, newTri1 ); + edges.push( insertedEdge ); + + } + + if ( e.reverseTriangle ) { + + // TODO: insert the other side + + } } @@ -133,4 +239,10 @@ export class EdgeGraph { } + validateState() { + + // TODO: validate state + + } + } diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index e67d461a..ead3caa0 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -37,18 +37,7 @@ export class TriangleGraph { initialTri.copy( tri ); transformToFrame( initialTri, invFrame ); - const arr = [ initialTri.a, initialTri.b, initialTri.c ]; - for ( let i = 0; i < 3; i ++ ) { - - const line = new Line3(); - const ni = ( i + 1 ) % 3; - - line.start.copy( arr[ i ] ); - line.end.copy( arr[ ni ] ); - graph.insertEdge( line ); - - - } + graph.initialize( initialTri ); } From f76f68eae50cfaae06f706b1c0c30ddcc902e25f Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 18:16:34 +0900 Subject: [PATCH 14/74] Add more triangle generation --- src/core/splitter/EdgeGraph.js | 92 +++++++++++++++++++++++++----- src/core/splitter/TriangleGraph.js | 3 +- src/core/splitter/utils.js | 7 ++- 3 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 21b3da4e..ea9f0ddb 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -107,11 +107,11 @@ export class EdgeGraph { const i0 = this.insertPoint( start ); const i1 = this.insertPoint( end ); - const line = new GraphEdge(); - line.start.copy( points[ i0 ] ); - line.startIndex = i0; - line.end.copy( points[ i1 ] ); - line.endIndex = i1; + // const line = new GraphEdge(); + // line.start.copy( points[ i0 ] ); + // line.startIndex = i0; + // line.end.copy( points[ i1 ] ); + // line.endIndex = i1; // edges.push( line ); @@ -125,6 +125,7 @@ export class EdgeGraph { const { edges, points, triangles } = this; let index = this.findClosestPointIndex( point ); + if ( index === null ) { const vec = new Vector3(); @@ -147,6 +148,41 @@ export class EdgeGraph { } else { // TODO: split into three triangles + const triangle = triangles[ containingTriangle ]; + const edges = [ null, null, null ]; + for ( let i = 0; i < 3; i ++ ) { + + const other = triangle.points[ i ]; + const edge = new GraphEdge(); + edge.start.copy( point ); + edge.startIndex = index; + edge.end.copy( other ); + edge.endIndex = triangle.getVertexIndex( i ); + + edges[ i ] = edge; + + } + + for ( let i = 0; i < 3; i ++ ) { + + const ni = ( i + 1 ) % 3; + const e0 = edges[ i ]; + const e1 = triangle.edges[ i ].edge; + const e2 = edges[ ni ]; + + const newTriangle = new GraphTriangle(); + const reversed = triangle.edges[ i ].reversed; + newTriangle.setEdge( 0, e0, reversed ); + newTriangle.setEdge( 1, e1, reversed ); + newTriangle.setEdge( 2, e2, ! reversed ); + + triangles.push( newTriangle ); + + } + + index = points.length; + points.push( point.clone() ); + triangles.splice( triangles.indexOf( triangle ), 1 ); } @@ -174,39 +210,67 @@ export class EdgeGraph { edges.push( l0, l1 ); edges.splice( intersectingEdge, 1 ); + // split the forward side triangle if ( e.triangle ) { - const edgeIndex = e.triangle.getEdgeIndex( e ); + const triangle = e.triangle; + const edgeIndex = triangle.getEdgeIndex( e ); const nextEdgeIndex = ( edgeIndex + 2 ) % 3; const insertedEdge = new GraphEdge(); insertedEdge.start.copy( point ); insertedEdge.startIndex = index; - insertedEdge.end.copy( e.triangle.points[ nextEdgeIndex ] ); - insertedEdge.endIndex = e.triangle.getVertexIndex( nextEdgeIndex ); + insertedEdge.end.copy( triangle.points[ nextEdgeIndex ] ); + insertedEdge.endIndex = triangle.getVertexIndex( nextEdgeIndex ); edges.push( insertedEdge ); const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; const newTri0 = new GraphTriangle(); - newTri0.setEdge( 0, l0, e.triangle.edges[ edgeIndex ].reversed ); + newTri0.setEdge( 0, l0, triangle.edges[ edgeIndex ].reversed ); newTri0.setEdge( 1, insertedEdge, false ); - newTri0.setEdge( 2, e.triangle.edges[ finalEdgeIndex0 ].edge, e.triangle.edges[ finalEdgeIndex0 ].reversed ); + newTri0.setEdge( 2, triangle.edges[ finalEdgeIndex0 ].edge, triangle.edges[ finalEdgeIndex0 ].reversed ); const finalEdgeIndex1 = ( edgeIndex + 1 ) % 3; const newTri1 = new GraphTriangle(); - newTri1.setEdge( 0, l1, e.triangle.edges[ edgeIndex ].reversed ); - newTri1.setEdge( 1, e.triangle.edges[ finalEdgeIndex1 ].edge, e.triangle.edges[ finalEdgeIndex1 ].reversed ); + newTri1.setEdge( 0, l1, triangle.edges[ edgeIndex ].reversed ); + newTri1.setEdge( 1, triangle.edges[ finalEdgeIndex1 ].edge, triangle.edges[ finalEdgeIndex1 ].reversed ); newTri1.setEdge( 2, insertedEdge, true ); - triangles.splice( triangles.indexOf( e.triangle ), 1 ); + triangles.splice( triangles.indexOf( triangle ), 1 ); triangles.push( newTri0, newTri1 ); edges.push( insertedEdge ); } + // split the reverse side triangle if ( e.reverseTriangle ) { - // TODO: insert the other side + const triangle = e.reverseTriangle; + const edgeIndex = triangle.getEdgeIndex( e ); + const nextEdgeIndex = ( edgeIndex + 2 ) % 3; + + const insertedEdge = new GraphEdge(); + insertedEdge.start.copy( point ); + insertedEdge.startIndex = index; + insertedEdge.end.copy( triangle.points[ nextEdgeIndex ] ); + insertedEdge.endIndex = triangle.getVertexIndex( nextEdgeIndex ); + edges.push( insertedEdge ); + + const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; + const newTri0 = new GraphTriangle(); + newTri0.setEdge( 0, l0, triangle.edges[ edgeIndex ].reversed ); + newTri0.setEdge( 1, insertedEdge, true ); + newTri0.setEdge( 2, triangle.edges[ finalEdgeIndex0 ].edge, triangle.edges[ finalEdgeIndex0 ].reversed ); + + const finalEdgeIndex1 = ( edgeIndex + 1 ) % 3; + const newTri1 = new GraphTriangle(); + newTri1.setEdge( 0, l1, triangle.edges[ edgeIndex ].reversed ); + newTri1.setEdge( 1, triangle.edges[ finalEdgeIndex1 ].edge, triangle.edges[ finalEdgeIndex1 ].reversed ); + newTri1.setEdge( 2, insertedEdge, true ); + + triangles.splice( triangles.indexOf( triangle ), 1 ); + triangles.push( newTri0, newTri1 ); + edges.push( insertedEdge ); } diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index ead3caa0..e8f0d6a1 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -49,7 +49,7 @@ export class TriangleGraph { splitByTriangle( tri ) { - const { plane, invFrame, frame, initialTri, graph } = this; + const { plane, invFrame, initialTri, graph } = this; tri = tri.clone(); @@ -142,7 +142,6 @@ export class TriangleGraph { const edge = new Line3(); edge.start.copy( planePoints[ 0 ] ); edge.end.copy( planePoints[ 1 ] ); - if ( getIntersectedLine( edge, initialTri, result ) ) { edges.push( result.clone() ); diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index 2a0c9652..89f0a161 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -156,7 +156,8 @@ export function getIntersectedLine( line, tri, target ) { if ( setCount === 2 ) { - // UH OH + // TODO + console.error( 'This shouldn\'t happen' ); } else if ( setCount === 1 ) { @@ -178,12 +179,12 @@ export function getIntersectedLine( line, tri, target ) { if ( tri.containsPoint( line.start ) ) { - target.end.copy( line.start ); + target.start.copy( line.start ); setCount ++; } else if ( tri.containsPoint( line.end ) ) { - target.end.copy( line.end ); + target.start.copy( line.end ); setCount ++; } From 889ef74a51f26ab02783df4a7f0601e3e3159950 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 18:35:31 +0900 Subject: [PATCH 15/74] Add a swap edge --- src/core/splitter/EdgeGraph.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index ea9f0ddb..d333e869 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -303,6 +303,36 @@ export class EdgeGraph { } + swapEdge( edge ) { + + const { triangle, reverseTriangle } = edge; + if ( ! triangle || ! reverseTriangle ) { + + return false; + + } + + const e0Index = triangle.getEdgeIndex( edge ); + const e1Index = reverseTriangle.getEdgeIndex( edge ); + + const v0Index = ( e0Index + 2 ) % 3; + const v1Index = ( e1Index + 2 ) % 3; + + const v0 = triangle.points[ v0Index ]; + const v1 = reverseTriangle.points[ v1Index ]; + + edge.start.copy( v0 ); + edge.startIndex = triangle.getVertexIndex( v0Index ); + edge.end.copy( v1 ); + edge.endIndex = reverseTriangle.getVertexIndex( v1Index ); + + triangle.setEdge( e0Index, edge, triangle.edges[ e0Index ].reversed ); + reverseTriangle.setEdge( e1Index, edge, reverseTriangle.edges[ e0Index ].reversed ); + + return true; + + } + validateState() { // TODO: validate state From 5306109eedd74398cd118528c90ad0c1bb7632ae Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 18:38:49 +0900 Subject: [PATCH 16/74] Add edge swap --- src/core/splitter/EdgeGraph.js | 61 ++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index d333e869..12fbf878 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -1,4 +1,5 @@ import { Vector3, Line3, Triangle } from 'three'; +import { lineIntersect } from './utils'; class GraphTriangle extends Triangle { @@ -104,20 +105,58 @@ export class EdgeGraph { const { points, edges } = this; const { start, end } = edge; - const i0 = this.insertPoint( start ); - const i1 = this.insertPoint( end ); + const startIndex = this.insertPoint( start ); + const endIndex = this.insertPoint( end ); - // const line = new GraphEdge(); - // line.start.copy( points[ i0 ] ); - // line.startIndex = i0; - // line.end.copy( points[ i1 ] ); - // line.endIndex = i1; + const inserting = new GraphEdge(); + inserting.start.copy( points[ startIndex ] ); + inserting.startIndex = startIndex; + inserting.end.copy( points[ endIndex ] ); + inserting.endIndex = endIndex; - // edges.push( line ); + for ( let i = 0, l = edges.length; i < l; i ++ ) { - // TODO: check for intersections and swap triangle orientations, then add - // a required edge - // TODO: after swapping and making way for new edges we may want to mark edges as "required" + // swap the edge if we don't emanate from the same point + const other = edges[ i ]; + if ( + other.startIndex !== inserting.startIndex && + other.startIndex !== inserting.endIndex && + other.endIndex !== inserting.startIndex && + other.endIndex !== inserting.endIndex + ) { + + const point = new Vector3(); + if ( lineIntersect( inserting, other, point ) ) { + + if ( other.required ) { + + // TODO + console.error( 'FAILURE' ); + + } else { + + this.swapEdge( other ); + + } + + } + + } + + // if we found the edge that matches the target edge then mark it as required and continue + if ( ( + other.startIndex === inserting.startIndex && + other.endIndex === inserting.endIndex + ) || ( + other.startIndex === inserting.endIndex && + other.endIndex === inserting.startIndex + ) ) { + + other.required = true; + + } + + } } From 74e501d0e3c3df72a4dad162a1500747074899d2 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 18:53:50 +0900 Subject: [PATCH 17/74] Confirm transformation --- examples/triangleGraphSplit.js | 23 +++++++++++++-------- src/core/splitter/EdgeGraph.js | 2 -- src/core/splitter/TriangleGraph.js | 33 +++++++++++++++++++++--------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/examples/triangleGraphSplit.js b/examples/triangleGraphSplit.js index e4d2e92a..e2026dc4 100644 --- a/examples/triangleGraphSplit.js +++ b/examples/triangleGraphSplit.js @@ -112,12 +112,21 @@ function render() { _vec.set( 0, 0, 1 ).transformDirection( planeObject.matrixWorld ); plane.setFromNormalAndCoplanarPoint( _vec, planeObject.position ); - splitter.initialize( ogTris[ 0 ] ); - tris.forEach( t => { + if ( ! window.UPDATED ) { - splitter.splitByTriangle( t ); + splitter.initialize( ogTris[ 0 ] ); + tris.forEach( t => { - } ); + splitter.splitByTriangle( t ); + + } ); + + splitter.complete(); + + window.UPDATED = true; + window.SPLITTER = splitter; + + } planeHelper.visible = false; transformControls.visible = false; @@ -126,10 +135,8 @@ function render() { pointsHelper.setPoints( splitter.graph.points ); edgesHelper.setEdges( splitter.graph.edges ); - clippedTris.setTriangles( ogTris ); - initialTris.setTriangles( tris ); - // clippedTris.setTriangles( splitter.triangles ); - // initialTris.setTriangles( [ ...ogTris, ...tris ] ); + clippedTris.setTriangles( splitter.graph.triangles ); + initialTris.setTriangles( [ ...ogTris, ...tris ] ); renderer.render( scene, camera ); diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 12fbf878..3b56cd34 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -261,7 +261,6 @@ export class EdgeGraph { insertedEdge.startIndex = index; insertedEdge.end.copy( triangle.points[ nextEdgeIndex ] ); insertedEdge.endIndex = triangle.getVertexIndex( nextEdgeIndex ); - edges.push( insertedEdge ); const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; const newTri0 = new GraphTriangle(); @@ -293,7 +292,6 @@ export class EdgeGraph { insertedEdge.startIndex = index; insertedEdge.end.copy( triangle.points[ nextEdgeIndex ] ); insertedEdge.endIndex = triangle.getVertexIndex( nextEdgeIndex ); - edges.push( insertedEdge ); const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; const newTri0 = new GraphTriangle(); diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index e8f0d6a1..42aaf0a0 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -157,16 +157,29 @@ export class TriangleGraph { } - // TODO - // - find intersection edge - // - transform tri or intersection edge into og tri frame w/ z up - // - which triangles the points line up on (or which edges they may lie on) to split and insert - // - possibly split the main triangle edges - // - triangulate the points - // - swap so the required edges are present - // - confirm on edges cross?? - // OR - // - just clip into separate shapes? Use earcut to triangulate? + } + + complete() { + + const { graph, frame } = this; + graph.points.forEach( v => { + + v.applyMatrix4( frame ); + + } ); + graph.edges.forEach( e => { + + e.start.applyMatrix4( frame ); + e.end.applyMatrix4( frame ); + + } ); + graph.triangles.forEach( t => { + + t.a.applyMatrix4( frame ); + t.b.applyMatrix4( frame ); + t.c.applyMatrix4( frame ); + + } ); } From 6ddbdc34b619ea87de404f362db4f2fbb338b2a2 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 20:24:56 +0900 Subject: [PATCH 18/74] Small fixes --- src/core/splitter/EdgeGraph.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 3b56cd34..87493e20 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -177,18 +177,19 @@ export class EdgeGraph { if ( intersectingEdge === - 1 ) { + index = points.length; + points.push( point.clone() ); + const containingTriangle = triangles.findIndex( t => t.containsPoint( point ) ); if ( containingTriangle === - 1 ) { // TODO: this should never happen - index = points.length; - points.push( point.clone() ); } else { // TODO: split into three triangles const triangle = triangles[ containingTriangle ]; - const edges = [ null, null, null ]; + const newEdges = [ null, null, null ]; for ( let i = 0; i < 3; i ++ ) { const other = triangle.points[ i ]; @@ -198,16 +199,16 @@ export class EdgeGraph { edge.end.copy( other ); edge.endIndex = triangle.getVertexIndex( i ); - edges[ i ] = edge; + newEdges[ i ] = edge; } for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; - const e0 = edges[ i ]; + const e0 = newEdges[ i ]; const e1 = triangle.edges[ i ].edge; - const e2 = edges[ ni ]; + const e2 = newEdges[ ni ]; const newTriangle = new GraphTriangle(); const reversed = triangle.edges[ i ].reversed; @@ -219,8 +220,7 @@ export class EdgeGraph { } - index = points.length; - points.push( point.clone() ); + edges.push( ...newEdges ); triangles.splice( triangles.indexOf( triangle ), 1 ); } From 540b3209cc61dcb2673dc573955720708e889915 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 20:59:52 +0900 Subject: [PATCH 19/74] Add function for validating triangle state, fix added edges --- src/core/splitter/EdgeGraph.js | 58 ++++++++++++++++++++++++++++-- src/core/splitter/TriangleGraph.js | 4 +++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 87493e20..3e91e318 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -370,9 +370,63 @@ export class EdgeGraph { } - validateState() { + validate() { - // TODO: validate state + const { points, edges, triangles } = this; + + edges.forEach( edge => { + + const { start, end, startIndex, endIndex } = edge; + if ( ! start.equals( points[ startIndex ] ) || ! end.equals( points[ endIndex ] ) ) { + + throw new Error( 'Edge indices do not match' ); + + } + + } ); + + const foundEdgeSet = new Set(); + triangles.forEach( triangle => { + + triangle.edges.forEach( ( info, i ) => { + + const { edge, reversed } = info; + + foundEdgeSet.add( edge ); + + if ( + reversed && edge.reverseTriangle !== triangle || + ! reversed && edge.triangle !== triangle + ) { + + throw new Error( 'Edge triangles do not match' ); + + } + + const ni = ( i + 1 ) % 3; + let start = triangle.points[ i ]; + let end = triangle.points[ ni ]; + if ( reversed ) { + + [ start, end ] = [ end, start ]; + + } + + if ( ! edge.start.equals( start ) || ! edge.end.equals( end ) ) { + + throw new Error( 'Edges incorrectly assigned' ); + + } + + } ); + + } ); + + if ( foundEdgeSet.size !== edges.length ) { + + throw new Error( 'Edge counts do not match' ); + + } } diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index 42aaf0a0..9cc827d6 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -146,6 +146,10 @@ export class TriangleGraph { edges.push( result.clone() ); + } else if ( initialTri.containsPoint( edge.start ) ) { + + edges.push( edge.clone() ); + } } From 663b0239b7c0f7672b0b63cdb0df7d6202c0fc1c Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 21:40:56 +0900 Subject: [PATCH 20/74] bug fix --- src/core/splitter/EdgeGraph.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 3e91e318..c23888e5 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -212,9 +212,9 @@ export class EdgeGraph { const newTriangle = new GraphTriangle(); const reversed = triangle.edges[ i ].reversed; - newTriangle.setEdge( 0, e0, reversed ); + newTriangle.setEdge( 0, e0, false ); newTriangle.setEdge( 1, e1, reversed ); - newTriangle.setEdge( 2, e2, ! reversed ); + newTriangle.setEdge( 2, e2, true ); triangles.push( newTriangle ); @@ -373,6 +373,8 @@ export class EdgeGraph { validate() { const { points, edges, triangles } = this; + const foundTriangleSet = new Set(); + const foundEdgeSet = new Set(); edges.forEach( edge => { @@ -383,9 +385,11 @@ export class EdgeGraph { } + foundTriangleSet.add( edge.triangle ); + foundTriangleSet.add( edge.reverseTriangle ); + } ); - const foundEdgeSet = new Set(); triangles.forEach( triangle => { triangle.edges.forEach( ( info, i ) => { @@ -428,6 +432,12 @@ export class EdgeGraph { } + if ( foundTriangleSet.size !== triangles.length ) { + + throw new Error( 'Triangle counts do not match' ); + + } + } } From 9ded1a11f6c96839958031cad4556ca48c6dc7a8 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 21:44:46 +0900 Subject: [PATCH 21/74] Fix validation --- src/core/splitter/EdgeGraph.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index c23888e5..9e0b5727 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -385,8 +385,17 @@ export class EdgeGraph { } - foundTriangleSet.add( edge.triangle ); - foundTriangleSet.add( edge.reverseTriangle ); + if ( edge.triangle ) { + + foundTriangleSet.add( edge.triangle ); + + } + + if ( edge.reverseTriangle ) { + + foundTriangleSet.add( edge.reverseTriangle ); + + } } ); From b01457fc4edd30d5ca735c8ab22bb769adb960af Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 22:00:52 +0900 Subject: [PATCH 22/74] Fix edge swap --- src/core/splitter/EdgeGraph.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 9e0b5727..176ce087 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -363,8 +363,28 @@ export class EdgeGraph { edge.end.copy( v1 ); edge.endIndex = reverseTriangle.getVertexIndex( v1Index ); - triangle.setEdge( e0Index, edge, triangle.edges[ e0Index ].reversed ); - reverseTriangle.setEdge( e1Index, edge, reverseTriangle.edges[ e0Index ].reversed ); + // TODO: we should be able to get by with only adjusting two edges per triangle + const t0a = edge; + const t0ar = false; + const t0b = reverseTriangle.edges[ ( e1Index + 2 ) % 3 ].edge; + const t0br = reverseTriangle.edges[ ( e1Index + 2 ) % 3 ].reversed; + const t0c = triangle.edges[ ( e0Index + 1 ) % 3 ].edge; + const t0cr = triangle.edges[ ( e0Index + 1 ) % 3 ].reversed; + + const t1a = edge; + const t1ar = true; + const t1b = triangle.edges[ ( e0Index + 2 ) % 3 ].edge; + const t1br = triangle.edges[ ( e0Index + 2 ) % 3 ].reversed; + const t1c = reverseTriangle.edges[ ( e1Index + 1 ) % 3 ].edge; + const t1cr = reverseTriangle.edges[ ( e1Index + 1 ) % 3 ].reversed; + + triangle.setEdge( 0, t0a, t0ar ); + triangle.setEdge( 1, t0b, t0br ); + triangle.setEdge( 2, t0c, t0cr ); + + reverseTriangle.setEdge( 0, t1a, t1ar ); + reverseTriangle.setEdge( 1, t1b, t1br ); + reverseTriangle.setEdge( 2, t1c, t1cr ); return true; From 55ec2cf640a60ac61c794cd30c7db45dc35a9e5f Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 22:06:21 +0900 Subject: [PATCH 23/74] Fix incorrect point --- src/core/splitter/TriangleGraph.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraph.js index 9cc827d6..b40f2ee0 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraph.js @@ -51,8 +51,6 @@ export class TriangleGraph { const { plane, invFrame, initialTri, graph } = this; - tri = tri.clone(); - const line = new Line3(); const hitPoint = new Vector3(); const arr = [ tri.a, tri.b, tri.c ]; @@ -65,7 +63,7 @@ export class TriangleGraph { const ni = ( i + 1 ) % 3; const p0 = arr[ i ]; const p1 = arr[ ni ]; - const d0 = plane.distanceToPoint( p0 ); + const d0 = Math.abs( plane.distanceToPoint( p0 ) ); if ( d0 < EPSILON ) { From 326aa066cc2339cc1945eb112c79247d05d1d48e Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 22:15:53 +0900 Subject: [PATCH 24/74] Rename, update api --- src/core/splitter/EdgeGraph.js | 8 ++++++++ .../{TriangleGraph.js => TriangleGraphSplitter.js} | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) rename src/core/splitter/{TriangleGraph.js => TriangleGraphSplitter.js} (95%) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 176ce087..fe55fb67 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -73,6 +73,14 @@ export class EdgeGraph { } + reset() { + + this.points.length = 0; + this.edges.length = 0; + this.triangles.length = 0; + + } + initialize( tri ) { const arr = [ tri.a, tri.b, tri.c ]; diff --git a/src/core/splitter/TriangleGraph.js b/src/core/splitter/TriangleGraphSplitter.js similarity index 95% rename from src/core/splitter/TriangleGraph.js rename to src/core/splitter/TriangleGraphSplitter.js index b40f2ee0..fd2f4752 100644 --- a/src/core/splitter/TriangleGraph.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -4,11 +4,12 @@ import { EdgeGraph } from './EdgeGraph'; const EPSILON = 1e-10; -export class TriangleGraph { +export class TriangleGraphSplitter { constructor() { this.graph = new EdgeGraph(); + this.coplanarTriangleUsed = false; this.initialTri = new Triangle(); this.plane = new Plane(); @@ -43,7 +44,8 @@ export class TriangleGraph { reset() { - this.graph = new EdgeGraph(); + this.graph.reset(); + this.coplanarTriangleUsed = false; } @@ -103,6 +105,7 @@ export class TriangleGraph { const edges = []; if ( coplanarPoints === 3 ) { + this.coplanarTriangleUsed = true; for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; From 6a5e9e84d7b1134b4d3e69604e062943b7fdfa3e Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 22:23:51 +0900 Subject: [PATCH 25/74] Fix coplanar points --- src/core/splitter/TriangleGraphSplitter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index fd2f4752..0afc9d04 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -109,8 +109,8 @@ export class TriangleGraphSplitter { for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; - const p0 = arr[ i ]; - const p1 = arr[ ni ]; + const p0 = planePoints[ i ]; + const p1 = planePoints[ ni ]; const c0 = initialTri.containsPoint( p0 ); const c1 = initialTri.containsPoint( p1 ); From 013ee9f13c1b3c2779e94161d06c763df349a1cf Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 22:35:03 +0900 Subject: [PATCH 26/74] Simplify edge swap --- src/core/splitter/EdgeGraph.js | 48 ++++++++++++++-------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index fe55fb67..f262bfdd 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -357,42 +357,34 @@ export class EdgeGraph { } - const e0Index = triangle.getEdgeIndex( edge ); - const e1Index = reverseTriangle.getEdgeIndex( edge ); + const t0EdgeIndex = triangle.getEdgeIndex( edge ); + const t1EdgeIndex = reverseTriangle.getEdgeIndex( edge ); - const v0Index = ( e0Index + 2 ) % 3; - const v1Index = ( e1Index + 2 ) % 3; + const t0SwapIndex = ( t0EdgeIndex + 2 ) % 3; + const t1SwapIndex = ( t1EdgeIndex + 2 ) % 3; - const v0 = triangle.points[ v0Index ]; - const v1 = reverseTriangle.points[ v1Index ]; + edge.start.copy( triangle.points[ t0SwapIndex ] ); + edge.startIndex = triangle.getVertexIndex( t0SwapIndex ); + edge.end.copy( reverseTriangle.points[ t1SwapIndex ] ); + edge.endIndex = reverseTriangle.getVertexIndex( t1SwapIndex ); - edge.start.copy( v0 ); - edge.startIndex = triangle.getVertexIndex( v0Index ); - edge.end.copy( v1 ); - edge.endIndex = reverseTriangle.getVertexIndex( v1Index ); - - // TODO: we should be able to get by with only adjusting two edges per triangle + // adjust both triangles in place const t0a = edge; const t0ar = false; - const t0b = reverseTriangle.edges[ ( e1Index + 2 ) % 3 ].edge; - const t0br = reverseTriangle.edges[ ( e1Index + 2 ) % 3 ].reversed; - const t0c = triangle.edges[ ( e0Index + 1 ) % 3 ].edge; - const t0cr = triangle.edges[ ( e0Index + 1 ) % 3 ].reversed; + const t0b = reverseTriangle.edges[ t1SwapIndex ].edge; + const t0br = reverseTriangle.edges[ t1SwapIndex ].reversed; const t1a = edge; const t1ar = true; - const t1b = triangle.edges[ ( e0Index + 2 ) % 3 ].edge; - const t1br = triangle.edges[ ( e0Index + 2 ) % 3 ].reversed; - const t1c = reverseTriangle.edges[ ( e1Index + 1 ) % 3 ].edge; - const t1cr = reverseTriangle.edges[ ( e1Index + 1 ) % 3 ].reversed; - - triangle.setEdge( 0, t0a, t0ar ); - triangle.setEdge( 1, t0b, t0br ); - triangle.setEdge( 2, t0c, t0cr ); - - reverseTriangle.setEdge( 0, t1a, t1ar ); - reverseTriangle.setEdge( 1, t1b, t1br ); - reverseTriangle.setEdge( 2, t1c, t1cr ); + const t1b = triangle.edges[ t0SwapIndex ].edge; + const t1br = triangle.edges[ t0SwapIndex ].reversed; + + // one edge on each triangle can remain in place + triangle.setEdge( t0SwapIndex, t0a, t0ar ); + triangle.setEdge( ( t0SwapIndex + 1 ) % 3, t0b, t0br ); + + reverseTriangle.setEdge( t1SwapIndex, t1a, t1ar ); + reverseTriangle.setEdge( ( t1SwapIndex + 1 ) % 3, t1b, t1br ); return true; From ab2c5fea594ef6bec0919e220a4bb3afc2ce9b9d Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 22:43:48 +0900 Subject: [PATCH 27/74] Fix API --- src/core/splitter/EdgeGraph.js | 1 + src/core/splitter/TriangleGraphSplitter.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index f262bfdd..9277ee2a 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -192,6 +192,7 @@ export class EdgeGraph { if ( containingTriangle === - 1 ) { // TODO: this should never happen + console.error( 'CANT FIND TRIANGLE' ); } else { diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 0afc9d04..aef920e6 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -6,6 +6,12 @@ const EPSILON = 1e-10; export class TriangleGraphSplitter { + get triangles() { + + return this.graph.triangles; + + } + constructor() { this.graph = new EdgeGraph(); From 3cc27e76fafe33092075b649bec63793854d26c3 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 23:13:45 +0900 Subject: [PATCH 28/74] Rename old splitter --- examples/triangleSplit.js | 4 ++-- src/core/Evaluator.js | 4 ++-- .../LegacyTriangleSplitter.js} | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/core/{TriangleSplitter.js => splitter/LegacyTriangleSplitter.js} (98%) diff --git a/examples/triangleSplit.js b/examples/triangleSplit.js index aae3bfc3..63cee859 100644 --- a/examples/triangleSplit.js +++ b/examples/triangleSplit.js @@ -1,7 +1,7 @@ import * as THREE from 'three'; import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; -import { TriangleSplitter, TriangleSetHelper } from '..'; +import { LegacyTriangleSplitter, TriangleSetHelper } from '..'; let renderer, camera, scene; let controls, transformControls; @@ -87,7 +87,7 @@ function init() { initialTris = new TriangleSetHelper(); clippedTris = new TriangleSetHelper(); - splitter = new TriangleSplitter(); + splitter = new LegacyTriangleSplitter(); scene.add( initialTris, clippedTris ); diff --git a/src/core/Evaluator.js b/src/core/Evaluator.js index ade3fdc6..9456bc0c 100644 --- a/src/core/Evaluator.js +++ b/src/core/Evaluator.js @@ -1,5 +1,5 @@ import { BufferAttribute } from 'three'; -import { TriangleSplitter } from './TriangleSplitter.js'; +import { LegacyTriangleSplitter } from './splitter/LegacyTriangleSplitter.js'; import { TypedAttributeData } from './TypedAttributeData.js'; import { OperationDebugData } from './debug/OperationDebugData.js'; import { performOperation } from './operations/operations.js'; @@ -188,7 +188,7 @@ export class Evaluator { constructor() { - this.triangleSplitter = new TriangleSplitter(); + this.triangleSplitter = new LegacyTriangleSplitter(); this.attributeData = []; this.attributes = [ 'position', 'uv', 'normal' ]; this.useGroups = true; diff --git a/src/core/TriangleSplitter.js b/src/core/splitter/LegacyTriangleSplitter.js similarity index 98% rename from src/core/TriangleSplitter.js rename to src/core/splitter/LegacyTriangleSplitter.js index eaa813b3..47ade32d 100644 --- a/src/core/TriangleSplitter.js +++ b/src/core/splitter/LegacyTriangleSplitter.js @@ -1,6 +1,6 @@ import { Triangle, Line3, Vector3, Plane } from 'three'; import { ExtendedTriangle } from 'three-mesh-bvh'; -import { isTriDegenerate } from './utils/triangleUtils.js'; +import { isTriDegenerate } from '../utils/triangleUtils.js'; // NOTE: these epsilons likely should all be the same since they're used to measure the // distance from a point to a plane which needs to be done consistently @@ -53,7 +53,7 @@ class TrianglePool { } // Utility class for splitting triangles -export class TriangleSplitter { +export class LegacyTriangleSplitter { constructor() { From bc17df0848b70d561392706d37475db959ec66a9 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 23:18:39 +0900 Subject: [PATCH 29/74] Add legacy splitter option --- examples/triangleGraphSplit.js | 4 ++-- src/core/Evaluator.js | 22 ++++++++++++++++++++-- src/core/operations/operations.js | 7 +++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/examples/triangleGraphSplit.js b/examples/triangleGraphSplit.js index e2026dc4..97774143 100644 --- a/examples/triangleGraphSplit.js +++ b/examples/triangleGraphSplit.js @@ -2,7 +2,7 @@ import * as THREE from 'three'; import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import { EdgesHelper, PointsHelper, TriangleSetHelper } from '../src/index.js'; -import { TriangleGraph } from '../src/core/splitter/TriangleGraph.js'; +import { TriangleGraphSplitter } from '../src/core/splitter/TriangleGraphSplitter.js'; let renderer, camera, scene; let controls, transformControls; @@ -90,7 +90,7 @@ function init() { pointsHelper = new PointsHelper(); edgesHelper = new EdgesHelper(); - splitter = new TriangleGraph(); + splitter = new TriangleGraphSplitter(); scene.add( initialTris, clippedTris, pointsHelper, edgesHelper ); diff --git a/src/core/Evaluator.js b/src/core/Evaluator.js index 9456bc0c..fae5ef87 100644 --- a/src/core/Evaluator.js +++ b/src/core/Evaluator.js @@ -1,5 +1,6 @@ -import { BufferAttribute } from 'three'; +import { BufferAttribute, Triangle } from 'three'; import { LegacyTriangleSplitter } from './splitter/LegacyTriangleSplitter.js'; +import { TriangleGraphSplitter } from './splitter/TriangleGraphSplitter.js'; import { TypedAttributeData } from './TypedAttributeData.js'; import { OperationDebugData } from './debug/OperationDebugData.js'; import { performOperation } from './operations/operations.js'; @@ -186,9 +187,26 @@ function getMaterialList( groups, materials ) { // Utility class for performing CSG operations export class Evaluator { + set useLegacySplitter( v ) { + + if ( this.useLegacySplitter !== v ) { + + this.triangleSplitter = v ? new LegacyTriangleSplitter() : new TriangleGraphSplitter(); + + } + + } + + get useLegacySplitter() { + + return this.triangleSplitter instanceof LegacyTriangleSplitter; + + } + constructor() { - this.triangleSplitter = new LegacyTriangleSplitter(); + this.useLegacySplitter = false; + this.triangleSplitter = new TriangleGraphSplitter(); this.attributeData = []; this.attributes = [ 'position', 'uv', 'normal' ]; this.useGroups = true; diff --git a/src/core/operations/operations.js b/src/core/operations/operations.js index bbf801c2..35499c81 100644 --- a/src/core/operations/operations.js +++ b/src/core/operations/operations.js @@ -140,6 +140,13 @@ function performSplitTriangleOperations( } + // TODO: remove the need to call "complete" + if ( splitter.complete ) { + + splitter.complete(); + + } + // for all triangles in the split result const triangles = splitter.triangles; for ( let ib = 0, l = triangles.length; ib < l; ib ++ ) { From be9b24065e449d65c972cf0fe87b2a66eaddf084 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 23:21:51 +0900 Subject: [PATCH 30/74] Remove unused import --- src/core/Evaluator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Evaluator.js b/src/core/Evaluator.js index fae5ef87..6919ed51 100644 --- a/src/core/Evaluator.js +++ b/src/core/Evaluator.js @@ -1,4 +1,4 @@ -import { BufferAttribute, Triangle } from 'three'; +import { BufferAttribute } from 'three'; import { LegacyTriangleSplitter } from './splitter/LegacyTriangleSplitter.js'; import { TriangleGraphSplitter } from './splitter/TriangleGraphSplitter.js'; import { TypedAttributeData } from './TypedAttributeData.js'; From f99336a19d37637e0b5e26285905731f27e5efc5 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 23:46:13 +0900 Subject: [PATCH 31/74] comments --- src/core/splitter/EdgeGraph.js | 77 +++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 9277ee2a..3b524506 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -7,35 +7,52 @@ class GraphTriangle extends Triangle { super( ...args ); + // the set of points and edge info associated with this triangle this.edges = [ null, null, null ]; this.points = [ this.a, this.b, this.c ]; } + // add the edge to the triangle at the given index and store whether + // this triangle is attached to the reversed half-edge setEdge( index, edge, reversed ) { - this.edges[ index ] = { edge, reversed }; + const { edges, points } = this; + if ( edges[ index ] === null ) { + + edges[ index ] = { edge, reversed }; + + } else { + + const info = edges[ index ]; + info.edge = edge; + info.reversed = reversed; + + } if ( reversed ) { edge.reverseTriangle = this; - this.points[ index ].copy( edge.end ); + points[ index ].copy( edge.end ); } else { edge.triangle = this; - this.points[ index ].copy( edge.start ); + points[ index ].copy( edge.start ); } } + // Returns the index of the given edge getEdgeIndex( edge ) { return this.edges.findIndex( info => info.edge === edge ); } + // Returns the vertex index associated with the vertex at the given + // point in the range [0, 2] getVertexIndex( index ) { const info = this.edges[ index ]; @@ -50,12 +67,16 @@ class GraphEdge extends Line3 { constructor( ...args ) { super( ...args ); + + // store the vertex index associated with the start, end points this.startIndex = - 1; this.endIndex = - 1; + // stores the half edge triangle connections this.triangle = null; this.reverseTriangle = null; + // whether this edge is required to stay in the graph this.required = false; } @@ -86,21 +107,21 @@ export class EdgeGraph { const arr = [ tri.a, tri.b, tri.c ]; const { triangles, points, edges } = this; - // initialize the first triangle if we find three points + // initialize the first triangle that we will be splitting const newTriangle = new GraphTriangle(); for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; const p0 = arr[ i ]; const p1 = arr[ ni ]; - const line = new GraphEdge(); - line.start.copy( p0 ); - line.startIndex = i; - line.end.copy( p1 ); - line.endIndex = ni; + const edge = new GraphEdge(); + edge.start.copy( p0 ); + edge.startIndex = i; + edge.end.copy( p1 ); + edge.endIndex = ni; - newTriangle.setEdge( i, line, false ); - edges.push( line ); + newTriangle.setEdge( i, edge, false ); + edges.push( edge ); } @@ -113,9 +134,12 @@ export class EdgeGraph { const { points, edges } = this; const { start, end } = edge; + + // insert the edge points into the graph const startIndex = this.insertPoint( start ); const endIndex = this.insertPoint( end ); + // the edge we're trying to insert const inserting = new GraphEdge(); inserting.start.copy( points[ startIndex ] ); inserting.startIndex = startIndex; @@ -139,7 +163,16 @@ export class EdgeGraph { if ( other.required ) { // TODO + // THESE ARE NOT INTERSECTING?! console.error( 'FAILURE' ); + console.log( + inserting.clone(), + other.clone(), + point.clone(), + + inserting.closestPointToPointParameter( point, false ), + other.closestPointToPointParameter( point, false ), + ); } else { @@ -171,10 +204,11 @@ export class EdgeGraph { insertPoint( point ) { const { edges, points, triangles } = this; - let index = this.findClosestPointIndex( point ); + let index = this.findMatchingPointIndex( point ); if ( index === null ) { + // if we haven't been able to match a point see if we can find an existing edge it sits on const vec = new Vector3(); const intersectingEdge = edges.findIndex( e => { @@ -185,6 +219,7 @@ export class EdgeGraph { if ( intersectingEdge === - 1 ) { + // if we didn't find an edge then try to find the triangle the point is in index = points.length; points.push( point.clone() ); @@ -196,9 +231,11 @@ export class EdgeGraph { } else { - // TODO: split into three triangles + // split into three triangles const triangle = triangles[ containingTriangle ]; const newEdges = [ null, null, null ]; + + // construct the new edges emanating from the point for ( let i = 0; i < 3; i ++ ) { const other = triangle.points[ i ]; @@ -212,6 +249,7 @@ export class EdgeGraph { } + // construct the triangles for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; @@ -236,11 +274,19 @@ export class EdgeGraph { } else { + // if we are sitting on an edge index = points.length; points.push( point.clone() ); // NOTE: if the edge is required here then we have a problem - it shouldn't have to be split const e = edges[ intersectingEdge ]; + if ( e.required ) { + + console.error( 'WE ARE ON A REQUIRED EDGE' ); + + } + + // construct the edges const l0 = new GraphEdge(); l0.start.copy( e.start ); l0.startIndex = e.startIndex; @@ -328,8 +374,9 @@ export class EdgeGraph { } - findClosestPointIndex( p ) { + findMatchingPointIndex( p ) { + // find the matching vertex for the give point if it exists in the graph const points = this.points; let closestIndex = null; let closestDist = Infinity; @@ -358,12 +405,14 @@ export class EdgeGraph { } + // get the vertices to swap to const t0EdgeIndex = triangle.getEdgeIndex( edge ); const t1EdgeIndex = reverseTriangle.getEdgeIndex( edge ); const t0SwapIndex = ( t0EdgeIndex + 2 ) % 3; const t1SwapIndex = ( t1EdgeIndex + 2 ) % 3; + // swap the edge direction edge.start.copy( triangle.points[ t0SwapIndex ] ); edge.startIndex = triangle.getVertexIndex( t0SwapIndex ); edge.end.copy( reverseTriangle.points[ t1SwapIndex ] ); From 4e054cbb981301275b3e17fae99ace3715baeb19 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 23:56:06 +0900 Subject: [PATCH 32/74] Comments --- src/core/splitter/TriangleGraphSplitter.js | 36 ++++++++++------------ src/index.js | 2 +- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index aef920e6..437274b1 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -82,8 +82,10 @@ export class TriangleGraphSplitter { line.start.copy( p0 ); line.end.copy( p1 ); + // consider the end point to be not hittable if ( ! plane.intersectLine( line, hitPoint ) || hitPoint.distanceTo( p1 ) < EPSILON ) { + // add buffer for the start point if ( d0 < EPSILON ) { hitPoint.copy( p0 ); @@ -114,30 +116,20 @@ export class TriangleGraphSplitter { this.coplanarTriangleUsed = true; for ( let i = 0; i < 3; i ++ ) { - const ni = ( i + 1 ) % 3; - const p0 = planePoints[ i ]; - const p1 = planePoints[ ni ]; - - const c0 = initialTri.containsPoint( p0 ); - const c1 = initialTri.containsPoint( p1 ); - if ( c0 && c1 ) { + const result = new Line3(); + const edge = new Line3(); - const line = new Line3(); - line.start.copy( p0 ); - line.end.copy( p1 ); - edges.push( line ); + const ni = ( i + 1 ) % 3; + edge.start.copy( planePoints[ i ] ); + edge.end.copy( planePoints[ ni ] ); - } else { + if ( getIntersectedLine( edge, initialTri, result ) ) { - const result = new Line3(); - const edge = new Line3(); - edge.start.copy( p0 ); - edge.end.copy( p1 ); - if ( getIntersectedLine( edge, initialTri, result ) ) { + edges.push( result.clone() ); - edges.push( result.clone() ); + } else if ( initialTri.containsPoint( edge.start ) || initialTri.containsPoint( edge.end ) ) { - } + edges.push( edge.clone() ); } @@ -147,13 +139,15 @@ export class TriangleGraphSplitter { const result = new Line3(); const edge = new Line3(); + edge.start.copy( planePoints[ 0 ] ); edge.end.copy( planePoints[ 1 ] ); + if ( getIntersectedLine( edge, initialTri, result ) ) { edges.push( result.clone() ); - } else if ( initialTri.containsPoint( edge.start ) ) { + } else if ( initialTri.containsPoint( edge.start ) || initialTri.containsPoint( edge.end ) ) { edges.push( edge.clone() ); @@ -178,12 +172,14 @@ export class TriangleGraphSplitter { v.applyMatrix4( frame ); } ); + graph.edges.forEach( e => { e.start.applyMatrix4( frame ); e.end.applyMatrix4( frame ); } ); + graph.triangles.forEach( t => { t.a.applyMatrix4( frame ); diff --git a/src/index.js b/src/index.js index a308a427..8f4995cd 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,7 @@ export * from './core/Brush.js'; export * from './core/Evaluator.js'; export * from './core/operations/Operation.js'; export * from './core/operations/OperationGroup.js'; -export * from './core/TriangleSplitter.js'; +export * from './core/splitter/LegacyTriangleSplitter.js'; export * from './core/HalfEdgeMap.js'; export * from './materials/GridMaterial.js'; From 3f740ae8413383efed7327e558c908fe88e0f9d2 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 4 Jan 2024 23:57:14 +0900 Subject: [PATCH 33/74] Update test --- tests/TriangleSplitter.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/TriangleSplitter.test.js b/tests/TriangleSplitter.test.js index 3ec20b75..56e3a75e 100644 --- a/tests/TriangleSplitter.test.js +++ b/tests/TriangleSplitter.test.js @@ -1,12 +1,12 @@ import { Vector3, Triangle } from 'three'; -import { TriangleSplitter } from '../src'; +import { LegacyTriangleSplitter } from '../src'; -describe( 'TriangleSplitter', () => { +describe( 'LegacyTriangleSplitter', () => { let splitter; beforeEach( () => { - splitter = new TriangleSplitter(); + splitter = new LegacyTriangleSplitter(); } ); From a3abea8ca71c6d0496a006fb20faad3135e91d63 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:08:27 +0900 Subject: [PATCH 34/74] Fix simple example vertex colors without groups --- examples/simple.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/simple.js b/examples/simple.js index 172944cc..81246e45 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -280,6 +280,9 @@ async function init() { brush2.material.vertexColors = v; brush2.material.needsUpdate = true; + originalMaterial.vertexColors = v; + originalMaterial.needsUpdate = true; + materialMap.forEach( m => { m.vertexColors = v; From a1101370a4a47bbddc4032b80b107b881ffa2660 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:20:27 +0900 Subject: [PATCH 35/74] Add pools --- src/core/splitter/EdgeGraph.js | 38 ++++++++++++++++++++++++++++++++- src/core/splitter/ObjectPool.js | 34 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/core/splitter/ObjectPool.js diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 3b524506..1192ce3d 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -1,5 +1,6 @@ import { Vector3, Line3, Triangle } from 'three'; -import { lineIntersect } from './utils'; +import { lineIntersect } from './utils.js'; +import { ObjectPool } from './ObjectPool.js'; class GraphTriangle extends Triangle { @@ -92,6 +93,41 @@ export class EdgeGraph { this.edges = []; this.triangles = []; + this.pointsPool = new ObjectPool( + () => new Vector3(), + ); + + this.edgesPool = new ObjectPool( + () => new GraphEdge(), + e => { + + e.startIndex = - 1; + e.endIndex = - 1; + + e.triangle = null; + e.reverseTriangle = null; + + e.required = false; + + }, + ); + + this.trianglePool = new ObjectPool( + () => new GraphTriangle(), + t => { + + const edges = t.edges; + for ( let i = 0; i < 3; i ++ ) { + + const info = edges[ i ]; + info.reversed = false; + info.edge = null; + + } + + }, + ); + } reset() { diff --git a/src/core/splitter/ObjectPool.js b/src/core/splitter/ObjectPool.js new file mode 100644 index 00000000..825543cb --- /dev/null +++ b/src/core/splitter/ObjectPool.js @@ -0,0 +1,34 @@ +export class ObjectPool { + + constructor( createCb, initCb = () => {} ) { + + this._createCb = createCb; + this._initCb = initCb; + + this._index = 0; + this._pool = []; + + } + + get() { + + const pool = this._pool; + if ( this._index === pool.length ) { + + pool.push( this._createCb() ); + + } + + const result = pool[ this._index ++ ]; + this._initCb( result ); + return result; + + } + + reset() { + + this._index = 0; + + } + +} From 6eaef3e56e478feb15ae287303ee6d07cbb01fe4 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:31:37 +0900 Subject: [PATCH 36/74] Add global fields --- src/core/splitter/TriangleGraphSplitter.js | 68 +++++++++++----------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 437274b1..597a7af1 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -1,9 +1,18 @@ import { Triangle, Matrix4, Line3, Plane, Vector3 } from 'three'; -import { getIntersectedLine, transformToFrame } from './utils'; -import { EdgeGraph } from './EdgeGraph'; +import { getIntersectedLine, transformToFrame } from './utils.js'; +import { EdgeGraph } from './EdgeGraph.js'; const EPSILON = 1e-10; +const _norm = new Vector3(); +const _right = new Vector3(); +const _up = new Vector3(); + +const _hitPoint = new Vector3(); + +const _result = new Line3(); +const _edge = new Line3(); + export class TriangleGraphSplitter { get triangles() { @@ -28,17 +37,14 @@ export class TriangleGraphSplitter { this.reset(); - const norm = new Vector3(); - const right = new Vector3(); - const up = new Vector3(); + const { frame, invFrame, initialTri, graph, plane } = this; - tri.getNormal( norm ); - right.subVectors( tri.a, tri.b ).normalize(); - up.crossVectors( norm, right ); + tri.getNormal( _norm ); + _right.subVectors( tri.a, tri.b ).normalize(); + _up.crossVectors( _norm, _right ); - const { frame, invFrame, initialTri, graph, plane } = this; tri.getPlane( plane ); - frame.makeBasis( right, up, norm ).setPosition( tri.a ); + frame.makeBasis( _right, _up, _norm ).setPosition( tri.a ); invFrame.copy( frame ).invert(); initialTri.copy( tri ); @@ -59,8 +65,6 @@ export class TriangleGraphSplitter { const { plane, invFrame, initialTri, graph } = this; - const line = new Line3(); - const hitPoint = new Vector3(); const arr = [ tri.a, tri.b, tri.c ]; const planePoints = []; let coplanarPoints = 0; @@ -79,16 +83,16 @@ export class TriangleGraphSplitter { } - line.start.copy( p0 ); - line.end.copy( p1 ); + _edge.start.copy( p0 ); + _edge.end.copy( p1 ); // consider the end point to be not hittable - if ( ! plane.intersectLine( line, hitPoint ) || hitPoint.distanceTo( p1 ) < EPSILON ) { + if ( ! plane.intersectLine( _edge, _hitPoint ) || _hitPoint.distanceTo( p1 ) < EPSILON ) { // add buffer for the start point if ( d0 < EPSILON ) { - hitPoint.copy( p0 ); + _hitPoint.copy( p0 ); } else { @@ -98,7 +102,7 @@ export class TriangleGraphSplitter { } - planePoints.push( hitPoint.clone() ); + planePoints.push( _hitPoint.clone() ); } @@ -116,20 +120,17 @@ export class TriangleGraphSplitter { this.coplanarTriangleUsed = true; for ( let i = 0; i < 3; i ++ ) { - const result = new Line3(); - const edge = new Line3(); - const ni = ( i + 1 ) % 3; - edge.start.copy( planePoints[ i ] ); - edge.end.copy( planePoints[ ni ] ); + _edge.start.copy( planePoints[ i ] ); + _edge.end.copy( planePoints[ ni ] ); - if ( getIntersectedLine( edge, initialTri, result ) ) { + if ( getIntersectedLine( _edge, initialTri, _result ) ) { - edges.push( result.clone() ); + edges.push( _result.clone() ); - } else if ( initialTri.containsPoint( edge.start ) || initialTri.containsPoint( edge.end ) ) { + } else if ( initialTri.containsPoint( _edge.start ) || initialTri.containsPoint( _edge.end ) ) { - edges.push( edge.clone() ); + edges.push( _edge.clone() ); } @@ -137,19 +138,16 @@ export class TriangleGraphSplitter { } else { - const result = new Line3(); - const edge = new Line3(); - - edge.start.copy( planePoints[ 0 ] ); - edge.end.copy( planePoints[ 1 ] ); + _edge.start.copy( planePoints[ 0 ] ); + _edge.end.copy( planePoints[ 1 ] ); - if ( getIntersectedLine( edge, initialTri, result ) ) { + if ( getIntersectedLine( _edge, initialTri, _result ) ) { - edges.push( result.clone() ); + edges.push( _result.clone() ); - } else if ( initialTri.containsPoint( edge.start ) || initialTri.containsPoint( edge.end ) ) { + } else if ( initialTri.containsPoint( _edge.start ) || initialTri.containsPoint( _edge.end ) ) { - edges.push( edge.clone() ); + edges.push( _edge.clone() ); } From 8ec4a6691c72bcd490d9d613eee797526007b5f7 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:40:58 +0900 Subject: [PATCH 37/74] Updates --- src/core/splitter/EdgeGraph.js | 16 ++++------------ src/core/splitter/ObjectPool.js | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 1192ce3d..6cc6f663 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -9,7 +9,7 @@ class GraphTriangle extends Triangle { super( ...args ); // the set of points and edge info associated with this triangle - this.edges = [ null, null, null ]; + this.edges = [ { edge: null, reversed: false }, { edge: null, reversed: false }, { edge: null, reversed: false } ]; this.points = [ this.a, this.b, this.c ]; } @@ -19,17 +19,9 @@ class GraphTriangle extends Triangle { setEdge( index, edge, reversed ) { const { edges, points } = this; - if ( edges[ index ] === null ) { - - edges[ index ] = { edge, reversed }; - - } else { - - const info = edges[ index ]; - info.edge = edge; - info.reversed = reversed; - - } + const info = edges[ index ]; + info.edge = edge; + info.reversed = reversed; if ( reversed ) { diff --git a/src/core/splitter/ObjectPool.js b/src/core/splitter/ObjectPool.js index 825543cb..cc8f20cb 100644 --- a/src/core/splitter/ObjectPool.js +++ b/src/core/splitter/ObjectPool.js @@ -10,7 +10,7 @@ export class ObjectPool { } - get() { + getInstance() { const pool = this._pool; if ( this._index === pool.length ) { From 1b9fa19f93ec514bdbc66b6cfb8bffae95bb0efb Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:41:09 +0900 Subject: [PATCH 38/74] Member name updates --- src/core/splitter/EdgeGraph.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 6cc6f663..c58a46ed 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -85,11 +85,11 @@ export class EdgeGraph { this.edges = []; this.triangles = []; - this.pointsPool = new ObjectPool( + this.pointPool = new ObjectPool( () => new Vector3(), ); - this.edgesPool = new ObjectPool( + this.edgePool = new ObjectPool( () => new GraphEdge(), e => { @@ -128,6 +128,10 @@ export class EdgeGraph { this.edges.length = 0; this.triangles.length = 0; + this.pointPool.reset(); + this.edgePool.reset(); + this.trianglePool.reset(); + } initialize( tri ) { From 23e40eb2715335f40010bfda0a5ed64ba8e02897 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:41:52 +0900 Subject: [PATCH 39/74] Update initialize function --- src/core/splitter/EdgeGraph.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index c58a46ed..08e9be66 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -137,16 +137,16 @@ export class EdgeGraph { initialize( tri ) { const arr = [ tri.a, tri.b, tri.c ]; - const { triangles, points, edges } = this; + const { triangles, points, edges, trianglePool, edgePool, pointPool } = this; // initialize the first triangle that we will be splitting - const newTriangle = new GraphTriangle(); + const newTriangle = trianglePool.getInstance(); for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; const p0 = arr[ i ]; const p1 = arr[ ni ]; - const edge = new GraphEdge(); + const edge = edgePool.getInstance(); edge.start.copy( p0 ); edge.startIndex = i; edge.end.copy( p1 ); @@ -158,7 +158,11 @@ export class EdgeGraph { } triangles.push( newTriangle ); - points.push( tri.a.clone(), tri.b.clone(), tri.c.clone() ); + points.push( + pointPool.getInstance().copy( tri.a ), + pointPool.getInstance().copy( tri.b ), + pointPool.getInstance().copy( tri.c ), + ); } From cd34e1709ff4cb8397396dafbc87c18068cf8f4b Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:43:02 +0900 Subject: [PATCH 40/74] Use the pools --- src/core/splitter/EdgeGraph.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 08e9be66..0b9da16d 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -168,7 +168,7 @@ export class EdgeGraph { insertEdge( edge ) { - const { points, edges } = this; + const { points, edges, edgePool, pointPool } = this; const { start, end } = edge; // insert the edge points into the graph @@ -176,7 +176,7 @@ export class EdgeGraph { const endIndex = this.insertPoint( end ); // the edge we're trying to insert - const inserting = new GraphEdge(); + const inserting = edgePool.getInstance(); inserting.start.copy( points[ startIndex ] ); inserting.startIndex = startIndex; inserting.end.copy( points[ endIndex ] ); @@ -193,7 +193,7 @@ export class EdgeGraph { other.endIndex !== inserting.endIndex ) { - const point = new Vector3(); + const point = pointPool.getInstance(); if ( lineIntersect( inserting, other, point ) ) { if ( other.required ) { @@ -239,13 +239,13 @@ export class EdgeGraph { insertPoint( point ) { - const { edges, points, triangles } = this; + const { edges, points, triangles, edgePool, pointPool, trianglePool } = this; let index = this.findMatchingPointIndex( point ); if ( index === null ) { // if we haven't been able to match a point see if we can find an existing edge it sits on - const vec = new Vector3(); + const vec = pointPool.getInstance(); const intersectingEdge = edges.findIndex( e => { e.closestPointToPoint( point, true, vec ); @@ -257,7 +257,7 @@ export class EdgeGraph { // if we didn't find an edge then try to find the triangle the point is in index = points.length; - points.push( point.clone() ); + points.push( pointPool.getInstance().copy( point ) ); const containingTriangle = triangles.findIndex( t => t.containsPoint( point ) ); if ( containingTriangle === - 1 ) { @@ -275,7 +275,7 @@ export class EdgeGraph { for ( let i = 0; i < 3; i ++ ) { const other = triangle.points[ i ]; - const edge = new GraphEdge(); + const edge = edgePool.getInstance(); edge.start.copy( point ); edge.startIndex = index; edge.end.copy( other ); @@ -293,7 +293,7 @@ export class EdgeGraph { const e1 = triangle.edges[ i ].edge; const e2 = newEdges[ ni ]; - const newTriangle = new GraphTriangle(); + const newTriangle = trianglePool.getInstance(); const reversed = triangle.edges[ i ].reversed; newTriangle.setEdge( 0, e0, false ); newTriangle.setEdge( 1, e1, reversed ); @@ -312,7 +312,7 @@ export class EdgeGraph { // if we are sitting on an edge index = points.length; - points.push( point.clone() ); + points.push( pointPool.getInstance().copy( point ) ); // NOTE: if the edge is required here then we have a problem - it shouldn't have to be split const e = edges[ intersectingEdge ]; @@ -323,14 +323,14 @@ export class EdgeGraph { } // construct the edges - const l0 = new GraphEdge(); + const l0 = edgePool.getInstance(); l0.start.copy( e.start ); l0.startIndex = e.startIndex; l0.end.copy( point ); l0.endIndex = index; l0.required = e.required; - const l1 = new GraphEdge(); + const l1 = edgePool.getInstance(); l1.start.copy( point ); l1.startIndex = index; l1.end.copy( e.end ); @@ -347,20 +347,20 @@ export class EdgeGraph { const edgeIndex = triangle.getEdgeIndex( e ); const nextEdgeIndex = ( edgeIndex + 2 ) % 3; - const insertedEdge = new GraphEdge(); + const insertedEdge = edgePool.getInstance(); insertedEdge.start.copy( point ); insertedEdge.startIndex = index; insertedEdge.end.copy( triangle.points[ nextEdgeIndex ] ); insertedEdge.endIndex = triangle.getVertexIndex( nextEdgeIndex ); const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; - const newTri0 = new GraphTriangle(); + const newTri0 = trianglePool.getInstance(); newTri0.setEdge( 0, l0, triangle.edges[ edgeIndex ].reversed ); newTri0.setEdge( 1, insertedEdge, false ); newTri0.setEdge( 2, triangle.edges[ finalEdgeIndex0 ].edge, triangle.edges[ finalEdgeIndex0 ].reversed ); const finalEdgeIndex1 = ( edgeIndex + 1 ) % 3; - const newTri1 = new GraphTriangle(); + const newTri1 = trianglePool.getInstance(); newTri1.setEdge( 0, l1, triangle.edges[ edgeIndex ].reversed ); newTri1.setEdge( 1, triangle.edges[ finalEdgeIndex1 ].edge, triangle.edges[ finalEdgeIndex1 ].reversed ); newTri1.setEdge( 2, insertedEdge, true ); @@ -378,20 +378,20 @@ export class EdgeGraph { const edgeIndex = triangle.getEdgeIndex( e ); const nextEdgeIndex = ( edgeIndex + 2 ) % 3; - const insertedEdge = new GraphEdge(); + const insertedEdge = edgePool.getInstance(); insertedEdge.start.copy( point ); insertedEdge.startIndex = index; insertedEdge.end.copy( triangle.points[ nextEdgeIndex ] ); insertedEdge.endIndex = triangle.getVertexIndex( nextEdgeIndex ); const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; - const newTri0 = new GraphTriangle(); + const newTri0 = trianglePool.getInstance(); newTri0.setEdge( 0, l0, triangle.edges[ edgeIndex ].reversed ); newTri0.setEdge( 1, insertedEdge, true ); newTri0.setEdge( 2, triangle.edges[ finalEdgeIndex0 ].edge, triangle.edges[ finalEdgeIndex0 ].reversed ); const finalEdgeIndex1 = ( edgeIndex + 1 ) % 3; - const newTri1 = new GraphTriangle(); + const newTri1 = trianglePool.getInstance(); newTri1.setEdge( 0, l1, triangle.edges[ edgeIndex ].reversed ); newTri1.setEdge( 1, triangle.edges[ finalEdgeIndex1 ].edge, triangle.edges[ finalEdgeIndex1 ].reversed ); newTri1.setEdge( 2, insertedEdge, true ); From 73278b3bebb1ac9d0f4c0dc274841cb7b85fa6da Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:44:42 +0900 Subject: [PATCH 41/74] small improvements --- src/core/splitter/EdgeGraph.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 0b9da16d..4746bc9c 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -2,6 +2,8 @@ import { Vector3, Line3, Triangle } from 'three'; import { lineIntersect } from './utils.js'; import { ObjectPool } from './ObjectPool.js'; +const _vec = new Vector3(); + class GraphTriangle extends Triangle { constructor( ...args ) { @@ -193,6 +195,7 @@ export class EdgeGraph { other.endIndex !== inserting.endIndex ) { + // TODO: remove this point? const point = pointPool.getInstance(); if ( lineIntersect( inserting, other, point ) ) { @@ -245,11 +248,10 @@ export class EdgeGraph { if ( index === null ) { // if we haven't been able to match a point see if we can find an existing edge it sits on - const vec = pointPool.getInstance(); const intersectingEdge = edges.findIndex( e => { - e.closestPointToPoint( point, true, vec ); - return vec.distanceTo( point ) < EPSILON; + e.closestPointToPoint( point, true, _vec ); + return _vec.distanceTo( point ) < EPSILON; } ); From d50ab071e759277559bc9774a23f055056ffb723 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 00:48:06 +0900 Subject: [PATCH 42/74] notes --- src/core/splitter/EdgeGraph.js | 1 + src/core/splitter/TriangleGraphSplitter.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 4746bc9c..4307e232 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -270,6 +270,7 @@ export class EdgeGraph { } else { // split into three triangles + // TODO: remove this array instantiation? const triangle = triangles[ containingTriangle ]; const newEdges = [ null, null, null ]; diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 597a7af1..b91df01a 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -65,6 +65,7 @@ export class TriangleGraphSplitter { const { plane, invFrame, initialTri, graph } = this; + // TODO: remove array instantiations const arr = [ tri.a, tri.b, tri.c ]; const planePoints = []; let coplanarPoints = 0; @@ -102,6 +103,7 @@ export class TriangleGraphSplitter { } + // TODO: use a pool for these points? planePoints.push( _hitPoint.clone() ); } @@ -114,6 +116,7 @@ export class TriangleGraphSplitter { } ); // find the edges that intersect with the triangle itself + // TODO: just insert the edges immediately so we don't have to clone the points const edges = []; if ( coplanarPoints === 3 ) { From 514626306c97c42c251504370fea8051a25249c6 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 12:42:03 +0900 Subject: [PATCH 43/74] Function rename --- src/core/splitter/TriangleGraphSplitter.js | 6 +++--- src/core/splitter/utils.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index b91df01a..ad0bb589 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -1,5 +1,5 @@ import { Triangle, Matrix4, Line3, Plane, Vector3 } from 'three'; -import { getIntersectedLine, transformToFrame } from './utils.js'; +import { getTriangleLineIntersection, transformToFrame } from './utils.js'; import { EdgeGraph } from './EdgeGraph.js'; const EPSILON = 1e-10; @@ -127,7 +127,7 @@ export class TriangleGraphSplitter { _edge.start.copy( planePoints[ i ] ); _edge.end.copy( planePoints[ ni ] ); - if ( getIntersectedLine( _edge, initialTri, _result ) ) { + if ( getTriangleLineIntersection( _edge, initialTri, _result ) ) { edges.push( _result.clone() ); @@ -144,7 +144,7 @@ export class TriangleGraphSplitter { _edge.start.copy( planePoints[ 0 ] ); _edge.end.copy( planePoints[ 1 ] ); - if ( getIntersectedLine( _edge, initialTri, _result ) ) { + if ( getTriangleLineIntersection( _edge, initialTri, _result ) ) { edges.push( _result.clone() ); diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index 89f0a161..b1f66f29 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -140,7 +140,7 @@ export function lineIntersect( l1, l2, target ) { } -export function getIntersectedLine( line, tri, target ) { +export function getTriangleLineIntersection( line, tri, target ) { let setCount = 0; const vec = new Vector3(); From 15c7ae582387f83d28be3335c66309b7d356ab82 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 13:08:55 +0900 Subject: [PATCH 44/74] Support parallel lines in the triangle / edge function --- src/core/splitter/utils.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index b1f66f29..58150c05 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -78,6 +78,17 @@ export function getIntersectionOnAPoint( line1, line2 ) { } +const _delta1 = new Vector3(); +const _delta2 = new Vector3(); +export function areEdgesParallel( l1, l2 ) { + + const EPS = 1e-10; + l1.delta( _delta1 ).normalize(); + l2.delta( _delta2 ).normalize(); + return Math.abs( _delta1.dot( _delta2 ) ) > 1 - EPS; + +} + // Determine the intersection point of two line segments // Return FALSE if the lines don't intersect // https://paulbourke.net/geometry/pointlineplane/ @@ -152,7 +163,26 @@ export function getTriangleLineIntersection( line, tri, target ) { edge.start.copy( arr[ i ] ); edge.end.copy( arr[ ni ] ); - if ( lineIntersect( edge, line, vec ) ) { + if ( areEdgesParallel( edge, line ) ) { + + let sp = edge.closestPointToPointParameter( line.start, false ); + let ep = edge.closestPointToPointParameter( line.end, false ); + if ( ! ( + sp < 0 && ep < 0 || + sp > 1 && ep > 1 + ) ) { + + sp = MathUtils.clamp( sp, 0, 1 ); + ep = MathUtils.clamp( ep, 0, 1 ); + + edge.at( sp, target.start ); + edge.at( ep, target.end ); + + return true; + + } + + } else if ( lineIntersect( edge, line, vec ) ) { if ( setCount === 2 ) { From c12c26a5995bb0c2c4f4533b7a6b821f61a8adc8 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 13:29:07 +0900 Subject: [PATCH 45/74] Update triangle line interscetion --- src/core/splitter/TriangleGraphSplitter.js | 8 --- src/core/splitter/utils.js | 71 ++++++++++++++++------ 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index ad0bb589..0a5f807f 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -131,10 +131,6 @@ export class TriangleGraphSplitter { edges.push( _result.clone() ); - } else if ( initialTri.containsPoint( _edge.start ) || initialTri.containsPoint( _edge.end ) ) { - - edges.push( _edge.clone() ); - } } @@ -148,10 +144,6 @@ export class TriangleGraphSplitter { edges.push( _result.clone() ); - } else if ( initialTri.containsPoint( _edge.start ) || initialTri.containsPoint( _edge.end ) ) { - - edges.push( _edge.clone() ); - } } diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index 58150c05..a02173f9 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -165,22 +165,22 @@ export function getTriangleLineIntersection( line, tri, target ) { if ( areEdgesParallel( edge, line ) ) { - let sp = edge.closestPointToPointParameter( line.start, false ); - let ep = edge.closestPointToPointParameter( line.end, false ); - if ( ! ( - sp < 0 && ep < 0 || - sp > 1 && ep > 1 - ) ) { + // let sp = edge.closestPointToPointParameter( line.start, false ); + // let ep = edge.closestPointToPointParameter( line.end, false ); + // if ( ! ( + // sp < 0 && ep < 0 || + // sp > 1 && ep > 1 + // ) ) { - sp = MathUtils.clamp( sp, 0, 1 ); - ep = MathUtils.clamp( ep, 0, 1 ); + // sp = MathUtils.clamp( sp, 0, 1 ); + // ep = MathUtils.clamp( ep, 0, 1 ); - edge.at( sp, target.start ); - edge.at( ep, target.end ); + // edge.at( sp, target.start ); + // edge.at( ep, target.end ); - return true; + // return true; - } + // } } else if ( lineIntersect( edge, line, vec ) ) { @@ -205,17 +205,50 @@ export function getTriangleLineIntersection( line, tri, target ) { } - if ( setCount === 1 ) { + if ( setCount === 0 || setCount === 1 ) { - if ( tri.containsPoint( line.start ) ) { + const cs = tri.containsPoint( line.start ); + const ce = tri.containsPoint( line.end ); + if ( setCount === 0 ) { - target.start.copy( line.start ); - setCount ++; + if ( cs && ce ) { - } else if ( tri.containsPoint( line.end ) ) { + target.copy( line ); + setCount += 2; - target.start.copy( line.end ); - setCount ++; + } else { + + console.error('UH OH'); + + } + + } + + if ( setCount === 1 ) { + + if ( cs && ce ) { + + if ( line.start.distanceTo( target.end ) > line.end.distanceTo( target.end ) ) { + + target.start.copy( line.start ); + + } else { + + target.start.copy( line.end ); + + } + + } else if ( cs ) { + + target.start.copy( line.start ); + setCount ++; + + } else if ( ce ) { + + target.start.copy( line.end ); + setCount ++; + + } } From 13e0f207ede6e1ca18b61c958b80d5c516371848 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 13:39:25 +0900 Subject: [PATCH 46/74] debug example changes --- examples/triangleGraphSplit.js | 45 +++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/examples/triangleGraphSplit.js b/examples/triangleGraphSplit.js index 97774143..b0b3c413 100644 --- a/examples/triangleGraphSplit.js +++ b/examples/triangleGraphSplit.js @@ -20,20 +20,28 @@ const ogTris = [ ]; const tris = [ - // new THREE.Triangle( - // new THREE.Vector3( - 0.5, 0.5, - 0.5 ), - // new THREE.Vector3( - 0.5, - 0.5, - 0.5 ), - // new THREE.Vector3( - 0.5, 0.5, 0.5 ), - // ), + + new THREE.Triangle( + new THREE.Vector3( - 0.35, 0.5, - 0.5 ), + new THREE.Vector3( - 0.35, - 0.5, - 0.5 ), + new THREE.Vector3( - 0.35, 0.5, 0.5 ), + ), new THREE.Triangle( - new THREE.Vector3( - 0.5, 0.5, - 0.5 ), - new THREE.Vector3( - 0.5, 0.5, 0.5 ), - new THREE.Vector3( 0.5, 0.5, - 0.5 ), + new THREE.Vector3( - 0.35, 0.5, - 0.5 ), + new THREE.Vector3( - 0.35, 0.5, 0.5 ), + new THREE.Vector3( 0.35, 0.5, - 0.5 ), ), + new THREE.Triangle( + new THREE.Vector3( - 0.5, 0.7, - 0.5 ), + new THREE.Vector3( - 0.5, - 0.5, - 0.5 ), + new THREE.Vector3( - 0.5, 0.7, 0.5 ), + ), + + // new THREE.Triangle( - // new THREE.Vector3( -0.5, 0.5, 0.5 ), - // new THREE.Vector3( 0.5, 0.5, 0.5 ), - // new THREE.Vector3( 0.5, 0.5, -0.5 ), + // new THREE.Vector3( -0.5, 0.75, 0.5 ), + // new THREE.Vector3( 0.5, 0.75, 0.5 ), + // new THREE.Vector3( 0.5, 0.75, -0.5 ), // ), ]; @@ -62,7 +70,7 @@ function init() { // camera setup camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 ); - camera.position.set( 1, 2, 4 ); + camera.position.set( - 1.5, 2, - 2 ); camera.far = 100; camera.updateProjectionMatrix(); @@ -115,9 +123,9 @@ function render() { if ( ! window.UPDATED ) { splitter.initialize( ogTris[ 0 ] ); - tris.forEach( t => { + tris.forEach( ( t, i ) => { - splitter.splitByTriangle( t ); + splitter.splitByTriangle( t, i === 2 ); } ); @@ -128,15 +136,18 @@ function render() { } + splitter.graph.validate(); + planeHelper.visible = false; transformControls.visible = false; transformControls.enabled = false; - pointsHelper.setPoints( splitter.graph.points ); - edgesHelper.setEdges( splitter.graph.edges ); + // pointsHelper.setPoints( splitter.graph.points ); + // edgesHelper.setEdges( splitter.graph.edges ); + clippedTris.color.set( 0x00ff00 ) clippedTris.setTriangles( splitter.graph.triangles ); - initialTris.setTriangles( [ ...ogTris, ...tris ] ); + initialTris.setTriangles( [ ...tris ] ); renderer.render( scene, camera ); From 421ce89a236a13fc80810b27e1038e30edceea51 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 13:47:45 +0900 Subject: [PATCH 47/74] Ensure caches are reset --- src/core/Evaluator.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/Evaluator.js b/src/core/Evaluator.js index 6919ed51..4130a184 100644 --- a/src/core/Evaluator.js +++ b/src/core/Evaluator.js @@ -156,6 +156,8 @@ function assignBufferData( geometry, attributeData, groupOrder ) { // TODO: can we have this dispose in the same way that a brush does? // TODO: why are half edges and group indices not removed here? geometry.boundsTree = null; + geometry.halfEdges = null; + geometry.groupIndices = null; if ( needsDisposal ) { From a3a54047c476c35d164c4e5c75df583a6384b594 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 14:34:27 +0900 Subject: [PATCH 48/74] Fix edge cases --- src/core/splitter/EdgeGraph.js | 39 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 4307e232..95551e46 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -340,9 +340,6 @@ export class EdgeGraph { l1.endIndex = e.endIndex; l1.required = e.required; - edges.push( l0, l1 ); - edges.splice( intersectingEdge, 1 ); - // split the forward side triangle if ( e.triangle ) { @@ -358,13 +355,13 @@ export class EdgeGraph { const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; const newTri0 = trianglePool.getInstance(); - newTri0.setEdge( 0, l0, triangle.edges[ edgeIndex ].reversed ); + newTri0.setEdge( 0, l0, false ); newTri0.setEdge( 1, insertedEdge, false ); newTri0.setEdge( 2, triangle.edges[ finalEdgeIndex0 ].edge, triangle.edges[ finalEdgeIndex0 ].reversed ); const finalEdgeIndex1 = ( edgeIndex + 1 ) % 3; const newTri1 = trianglePool.getInstance(); - newTri1.setEdge( 0, l1, triangle.edges[ edgeIndex ].reversed ); + newTri1.setEdge( 0, l1, false ); newTri1.setEdge( 1, triangle.edges[ finalEdgeIndex1 ].edge, triangle.edges[ finalEdgeIndex1 ].reversed ); newTri1.setEdge( 2, insertedEdge, true ); @@ -387,17 +384,17 @@ export class EdgeGraph { insertedEdge.end.copy( triangle.points[ nextEdgeIndex ] ); insertedEdge.endIndex = triangle.getVertexIndex( nextEdgeIndex ); - const finalEdgeIndex0 = ( edgeIndex + 2 ) % 3; + const finalEdgeIndex0 = ( edgeIndex + 1 ) % 3; const newTri0 = trianglePool.getInstance(); - newTri0.setEdge( 0, l0, triangle.edges[ edgeIndex ].reversed ); - newTri0.setEdge( 1, insertedEdge, true ); - newTri0.setEdge( 2, triangle.edges[ finalEdgeIndex0 ].edge, triangle.edges[ finalEdgeIndex0 ].reversed ); + newTri0.setEdge( 0, l0, true ); + newTri0.setEdge( 1, triangle.edges[ finalEdgeIndex0 ].edge, triangle.edges[ finalEdgeIndex0 ].reversed ); + newTri0.setEdge( 2, insertedEdge, true ); - const finalEdgeIndex1 = ( edgeIndex + 1 ) % 3; + const finalEdgeIndex1 = ( edgeIndex + 2 ) % 3; const newTri1 = trianglePool.getInstance(); - newTri1.setEdge( 0, l1, triangle.edges[ edgeIndex ].reversed ); - newTri1.setEdge( 1, triangle.edges[ finalEdgeIndex1 ].edge, triangle.edges[ finalEdgeIndex1 ].reversed ); - newTri1.setEdge( 2, insertedEdge, true ); + newTri1.setEdge( 0, l1, true ); + newTri1.setEdge( 1, insertedEdge, false ); + newTri1.setEdge( 2, triangle.edges[ finalEdgeIndex1 ].edge, triangle.edges[ finalEdgeIndex1 ].reversed ); triangles.splice( triangles.indexOf( triangle ), 1 ); triangles.push( newTri0, newTri1 ); @@ -405,6 +402,9 @@ export class EdgeGraph { } + edges.push( l0, l1 ); + edges.splice( intersectingEdge, 1 ); + } } @@ -484,13 +484,14 @@ export class EdgeGraph { const { points, edges, triangles } = this; const foundTriangleSet = new Set(); const foundEdgeSet = new Set(); + const messages = []; edges.forEach( edge => { const { start, end, startIndex, endIndex } = edge; if ( ! start.equals( points[ startIndex ] ) || ! end.equals( points[ endIndex ] ) ) { - throw new Error( 'Edge indices do not match' ); + messages.push( 'Edge indices do not match' ); } @@ -521,7 +522,7 @@ export class EdgeGraph { ! reversed && edge.triangle !== triangle ) { - throw new Error( 'Edge triangles do not match' ); + messages.push( 'Edge triangles do not match' ); } @@ -536,7 +537,7 @@ export class EdgeGraph { if ( ! edge.start.equals( start ) || ! edge.end.equals( end ) ) { - throw new Error( 'Edges incorrectly assigned' ); + messages.push( 'Edges incorrectly assigned' ); } @@ -546,16 +547,18 @@ export class EdgeGraph { if ( foundEdgeSet.size !== edges.length ) { - throw new Error( 'Edge counts do not match' ); + messages.push( 'Edge counts do not match' ); } if ( foundTriangleSet.size !== triangles.length ) { - throw new Error( 'Triangle counts do not match' ); + messages.push( 'Triangle counts do not match' ); } + return messages; + } } From 89f7fa18ea3e4d4058b48fb63ab9fd9eb0ce9dc2 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 14:56:24 +0900 Subject: [PATCH 49/74] Updates --- src/core/splitter/TriangleGraphSplitter.js | 7 +++++++ src/core/splitter/utils.js | 21 ++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 0a5f807f..b4a3ef41 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -181,6 +181,13 @@ export class TriangleGraphSplitter { } ); + const issues = this.graph.validate(); + if ( issues.length ) { + + issues.forEach( msg => console.warn( msg ) ); + + } + } } diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index a02173f9..f9f927ad 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -1,4 +1,5 @@ import { Vector3, Line3 } from 'three'; +import { closestPointsSegmentToSegment } from 'three-mesh-bvh/src/math/MathUtilities.js'; export function transformToFrame( tri, frame ) { @@ -77,7 +78,6 @@ export function getIntersectionOnAPoint( line1, line2 ) { } - const _delta1 = new Vector3(); const _delta2 = new Vector3(); export function areEdgesParallel( l1, l2 ) { @@ -89,11 +89,18 @@ export function areEdgesParallel( l1, l2 ) { } -// Determine the intersection point of two line segments -// Return FALSE if the lines don't intersect -// https://paulbourke.net/geometry/pointlineplane/ export function lineIntersect( l1, l2, target ) { + const tg1 = new Vector3(); + const tg2 = new Vector3(); + closestPointsSegmentToSegment( l1, l2, tg1, tg2 ); + target.copy( tg1 ); + + return tg1.distanceTo( tg2 ) < 1e-10; + + // Determine the intersection point of two line segments + // Return FALSE if the lines don't intersect + // https://paulbourke.net/geometry/pointlineplane/ const x1 = l1.start.x; const y1 = l1.start.y; @@ -165,6 +172,9 @@ export function getTriangleLineIntersection( line, tri, target ) { if ( areEdgesParallel( edge, line ) ) { + // TODO: we need to check if the lines are actually on each other + continue; + // let sp = edge.closestPointToPointParameter( line.start, false ); // let ep = edge.closestPointToPointParameter( line.end, false ); // if ( ! ( @@ -218,7 +228,8 @@ export function getTriangleLineIntersection( line, tri, target ) { } else { - console.error('UH OH'); + // TODO + console.error( 'This shouldn\'t happen' ); } From a194eae0e8f30c2c732fdbe16abb83b1c937be83 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 14:57:55 +0900 Subject: [PATCH 50/74] Dispose the cache data --- src/core/Evaluator.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/core/Evaluator.js b/src/core/Evaluator.js index 4130a184..d30fe929 100644 --- a/src/core/Evaluator.js +++ b/src/core/Evaluator.js @@ -152,13 +152,6 @@ function assignBufferData( geometry, attributeData, groupOrder ) { // update the draw range geometry.setDrawRange( 0, drawRange ); - // remove the bounds tree if it exists because its now out of date - // TODO: can we have this dispose in the same way that a brush does? - // TODO: why are half edges and group indices not removed here? - geometry.boundsTree = null; - geometry.halfEdges = null; - geometry.groupIndices = null; - if ( needsDisposal ) { geometry.dispose(); @@ -365,6 +358,9 @@ export class Evaluator { } + // dispose the cached data if it exists because its now out of date + brush.disposeCacheData(); + } ); return wasArray ? targetBrushes : targetBrushes[ 0 ]; From 893f49372c745fa7fc48b5998f622b817fc56c62 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:02:54 +0900 Subject: [PATCH 51/74] Fix some todos --- src/core/Evaluator.js | 6 +++++- src/core/splitter/TriangleGraphSplitter.js | 14 ++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/core/Evaluator.js b/src/core/Evaluator.js index d30fe929..7551416b 100644 --- a/src/core/Evaluator.js +++ b/src/core/Evaluator.js @@ -359,7 +359,11 @@ export class Evaluator { } // dispose the cached data if it exists because its now out of date - brush.disposeCacheData(); + if ( brush instanceof Brush ) { + + brush.disposeCacheData(); + + } } ); diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index b4a3ef41..1cc2966f 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -65,7 +65,6 @@ export class TriangleGraphSplitter { const { plane, invFrame, initialTri, graph } = this; - // TODO: remove array instantiations const arr = [ tri.a, tri.b, tri.c ]; const planePoints = []; let coplanarPoints = 0; @@ -116,8 +115,6 @@ export class TriangleGraphSplitter { } ); // find the edges that intersect with the triangle itself - // TODO: just insert the edges immediately so we don't have to clone the points - const edges = []; if ( coplanarPoints === 3 ) { this.coplanarTriangleUsed = true; @@ -129,7 +126,7 @@ export class TriangleGraphSplitter { if ( getTriangleLineIntersection( _edge, initialTri, _result ) ) { - edges.push( _result.clone() ); + graph.insertEdge( _result ); } @@ -142,19 +139,12 @@ export class TriangleGraphSplitter { if ( getTriangleLineIntersection( _edge, initialTri, _result ) ) { - edges.push( _result.clone() ); + graph.insertEdge( _result ); } } - // deduplicate and add edges - for ( let i = 0, l = edges.length; i < l; i ++ ) { - - graph.insertEdge( edges[ i ] ); - - } - } complete() { From 235b7b4e306947ea12a18494074128bce5af3c14 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:21:50 +0900 Subject: [PATCH 52/74] Fixes --- src/core/splitter/EdgeGraph.js | 11 ++++------- src/core/splitter/TriangleGraphSplitter.js | 6 +++++- src/core/splitter/utils.js | 2 ++ tests/TriangleSplitter.test.js | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 95551e46..57afc7dc 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -195,9 +195,7 @@ export class EdgeGraph { other.endIndex !== inserting.endIndex ) { - // TODO: remove this point? - const point = pointPool.getInstance(); - if ( lineIntersect( inserting, other, point ) ) { + if ( lineIntersect( inserting, other, _vec ) ) { if ( other.required ) { @@ -207,10 +205,10 @@ export class EdgeGraph { console.log( inserting.clone(), other.clone(), - point.clone(), + _vec.clone(), - inserting.closestPointToPointParameter( point, false ), - other.closestPointToPointParameter( point, false ), + inserting.closestPointToPointParameter( _vec, false ), + other.closestPointToPointParameter( _vec, false ), ); } else { @@ -270,7 +268,6 @@ export class EdgeGraph { } else { // split into three triangles - // TODO: remove this array instantiation? const triangle = triangles[ containingTriangle ]; const newEdges = [ null, null, null ]; diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 1cc2966f..a36a5a22 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -132,7 +132,7 @@ export class TriangleGraphSplitter { } - } else { + } else if ( planePoints.length >= 2 ) { _edge.start.copy( planePoints[ 0 ] ); _edge.end.copy( planePoints[ 1 ] ); @@ -143,6 +143,10 @@ export class TriangleGraphSplitter { } + } else { + + // we only touch at a single vertex so do nothing + } } diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index f9f927ad..5c01f7e8 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -249,6 +249,8 @@ export function getTriangleLineIntersection( line, tri, target ) { } + setCount ++; + } else if ( cs ) { target.start.copy( line.start ); diff --git a/tests/TriangleSplitter.test.js b/tests/TriangleSplitter.test.js index 56e3a75e..9602137f 100644 --- a/tests/TriangleSplitter.test.js +++ b/tests/TriangleSplitter.test.js @@ -1,12 +1,12 @@ import { Vector3, Triangle } from 'three'; -import { LegacyTriangleSplitter } from '../src'; +import { TriangleGraphSplitter } from '../src'; -describe( 'LegacyTriangleSplitter', () => { +describe( 'TriangleGraphSplitter', () => { let splitter; beforeEach( () => { - splitter = new LegacyTriangleSplitter(); + splitter = new TriangleGraphSplitter(); } ); From e074adcbb90991ea1ddfb7f99b26757d794a01e2 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:22:17 +0900 Subject: [PATCH 53/74] Update index --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index 8f4995cd..24f58086 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ export * from './core/Evaluator.js'; export * from './core/operations/Operation.js'; export * from './core/operations/OperationGroup.js'; export * from './core/splitter/LegacyTriangleSplitter.js'; +export * from './core/splitter/TriangleGraphSplitter.js'; export * from './core/HalfEdgeMap.js'; export * from './materials/GridMaterial.js'; From c8607cce4ddf815d6649e89f779ba0daee6b344d Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:27:11 +0900 Subject: [PATCH 54/74] Lint fixes --- examples/triangleGraphSplit.js | 17 ++++++++++++++--- src/core/splitter/EdgeGraph.js | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/triangleGraphSplit.js b/examples/triangleGraphSplit.js index b0b3c413..95ce51ce 100644 --- a/examples/triangleGraphSplit.js +++ b/examples/triangleGraphSplit.js @@ -125,10 +125,22 @@ function render() { splitter.initialize( ogTris[ 0 ] ); tris.forEach( ( t, i ) => { - splitter.splitByTriangle( t, i === 2 ); + splitter.splitByTriangle( t, i === 1 ); } ); + const errors = splitter.graph.validate(); + if ( errors.length > 0 ) { + + console.error( 'INVALID' ); + errors.forEach( e => { + + console.error( e ); + + } ); + + } + splitter.complete(); window.UPDATED = true; @@ -136,7 +148,6 @@ function render() { } - splitter.graph.validate(); planeHelper.visible = false; transformControls.visible = false; @@ -145,7 +156,7 @@ function render() { // pointsHelper.setPoints( splitter.graph.points ); // edgesHelper.setEdges( splitter.graph.edges ); - clippedTris.color.set( 0x00ff00 ) + clippedTris.color.set( 0x00ff00 ); clippedTris.setTriangles( splitter.graph.triangles ); initialTris.setTriangles( [ ...tris ] ); diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 57afc7dc..9e5a978a 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -170,7 +170,7 @@ export class EdgeGraph { insertEdge( edge ) { - const { points, edges, edgePool, pointPool } = this; + const { points, edges, edgePool } = this; const { start, end } = edge; // insert the edge points into the graph From ffd7e407973fc6b5248bd790156f8c4f627e6919 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:37:50 +0900 Subject: [PATCH 55/74] Reuse arrays --- src/core/splitter/EdgeGraph.js | 21 +++++++++++++-------- src/core/splitter/TriangleGraphSplitter.js | 11 ++++++++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 9e5a978a..ef85d45b 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -3,6 +3,8 @@ import { lineIntersect } from './utils.js'; import { ObjectPool } from './ObjectPool.js'; const _vec = new Vector3(); +const _triangleVertices = new Array( 3 ); +const _edgesToAdd = new Array( 3 ); class GraphTriangle extends Triangle { @@ -138,16 +140,19 @@ export class EdgeGraph { initialize( tri ) { - const arr = [ tri.a, tri.b, tri.c ]; const { triangles, points, edges, trianglePool, edgePool, pointPool } = this; + _triangleVertices[ 0 ] = tri.a; + _triangleVertices[ 1 ] = tri.b; + _triangleVertices[ 2 ] = tri.c; + // initialize the first triangle that we will be splitting const newTriangle = trianglePool.getInstance(); for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; - const p0 = arr[ i ]; - const p1 = arr[ ni ]; + const p0 = _triangleVertices[ i ]; + const p1 = _triangleVertices[ ni ]; const edge = edgePool.getInstance(); edge.start.copy( p0 ); edge.startIndex = i; @@ -269,7 +274,7 @@ export class EdgeGraph { // split into three triangles const triangle = triangles[ containingTriangle ]; - const newEdges = [ null, null, null ]; + _edgesToAdd.fill( null ); // construct the new edges emanating from the point for ( let i = 0; i < 3; i ++ ) { @@ -281,7 +286,7 @@ export class EdgeGraph { edge.end.copy( other ); edge.endIndex = triangle.getVertexIndex( i ); - newEdges[ i ] = edge; + _edgesToAdd[ i ] = edge; } @@ -289,9 +294,9 @@ export class EdgeGraph { for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; - const e0 = newEdges[ i ]; + const e0 = _edgesToAdd[ i ]; const e1 = triangle.edges[ i ].edge; - const e2 = newEdges[ ni ]; + const e2 = _edgesToAdd[ ni ]; const newTriangle = trianglePool.getInstance(); const reversed = triangle.edges[ i ].reversed; @@ -303,7 +308,7 @@ export class EdgeGraph { } - edges.push( ...newEdges ); + edges.push( ..._edgesToAdd ); triangles.splice( triangles.indexOf( triangle ), 1 ); } diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index a36a5a22..d7bb4c1b 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -13,6 +13,8 @@ const _hitPoint = new Vector3(); const _result = new Line3(); const _edge = new Line3(); +const _triangleVertices = new Array( 3 ); + export class TriangleGraphSplitter { get triangles() { @@ -65,16 +67,19 @@ export class TriangleGraphSplitter { const { plane, invFrame, initialTri, graph } = this; - const arr = [ tri.a, tri.b, tri.c ]; const planePoints = []; let coplanarPoints = 0; + _triangleVertices[ 0 ] = tri.a; + _triangleVertices[ 1 ] = tri.b; + _triangleVertices[ 2 ] = tri.c; + // find the points on the plane surface for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; - const p0 = arr[ i ]; - const p1 = arr[ ni ]; + const p0 = _triangleVertices[ i ]; + const p1 = _triangleVertices[ ni ]; const d0 = Math.abs( plane.distanceToPoint( p0 ) ); if ( d0 < EPSILON ) { From b9f7709d8e3da415ca50c074fc3f2871c9d6c2eb Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:43:55 +0900 Subject: [PATCH 56/74] README and dts update --- README.md | 8 ++++++++ src/index.d.ts | 13 +------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fc00ad6d..9d9b06f2 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,14 @@ A class with the same interface as `THREE.Group` but used to group a list of Ope ## Evaluator +### .useLegacyTriangleSplitter + +```js +useLegacyTriangleSplitter = false : Boolean +``` + +Whether to use the legacy splitter. Set to true if triangulation behavior looks incorrect. Please report errors if this seems to be necessary. + ### .useGroups ```js diff --git a/src/index.d.ts b/src/index.d.ts index 6d29a306..7d00af57 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -73,23 +73,12 @@ export class CullableTriangle extends Triangle { } -export class TrianglePool { +export class TriangleGraphSplitter { - getTriangle(): Triangle; - clear(): void; - reset(): void; - -} - -export class TriangleSplitter { - - trianglePool: TrianglePool; triangles: Triangle[]; - normal: Vector3; initialize( tri: Triangle ): void; splitByTriangle( triangle: Triangle ): void; - splitByPlane( plane: Plane, triangle: Triangle, coplanarIndex: number ): void; reset(): void; } From 1abcd171c746936d3c381c2cc1130a4fb6ab1255 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:44:39 +0900 Subject: [PATCH 57/74] IMPL updates --- IMPLEMENTATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index 81d50bc2..c7d3c4ba 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -100,7 +100,7 @@ A map encoding a map of each triangle edge to another triangle edge in a typed a A map used for storing the set of intersections from one triangle index in the first geometry to the other. -`src/core/TriangleSplitter.js` +`src/core/splitter/TriangleGraphSplitter.js` A utility for taking a triangle splitting it by planes or other triangles into a smaller subset of triangles. From 8bba87bc4098b453d8f4415bbcb382775b05af31 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:50:44 +0900 Subject: [PATCH 58/74] Fix field name --- src/core/Evaluator.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/Evaluator.js b/src/core/Evaluator.js index 7551416b..3812dd87 100644 --- a/src/core/Evaluator.js +++ b/src/core/Evaluator.js @@ -182,9 +182,9 @@ function getMaterialList( groups, materials ) { // Utility class for performing CSG operations export class Evaluator { - set useLegacySplitter( v ) { + set useLegacyTriangleSplitter( v ) { - if ( this.useLegacySplitter !== v ) { + if ( this.useLegacyTriangleSplitter !== v ) { this.triangleSplitter = v ? new LegacyTriangleSplitter() : new TriangleGraphSplitter(); @@ -192,7 +192,7 @@ export class Evaluator { } - get useLegacySplitter() { + get useLegacyTriangleSplitter() { return this.triangleSplitter instanceof LegacyTriangleSplitter; @@ -200,7 +200,7 @@ export class Evaluator { constructor() { - this.useLegacySplitter = false; + this.useLegacyTriangleSplitter = false; this.triangleSplitter = new TriangleGraphSplitter(); this.attributeData = []; this.attributes = [ 'position', 'uv', 'normal' ]; From 5a99cfd9a79716fe6925d660c886bd0fa9c18ff5 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 15:57:47 +0900 Subject: [PATCH 59/74] update three.js --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index bdbf2fa4..96910240 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "jest": "^27.2.4", "parcel": "^2.6.0", "rollup": "^2.70.0", - "three": "^0.155.0", + "three": "^0.160.0", "three-csg-ts": "^3.1.10", "three-mesh-bvh": "^0.6.6", "typescript": "^4.8.2" @@ -8370,9 +8370,9 @@ "dev": true }, "node_modules/three": { - "version": "0.155.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.155.0.tgz", - "integrity": "sha512-sNgCYmDijnIqkD/bMfk+1pHg3YzsxW7V2ChpuP6HCQ8NiZr3RufsXQr8M3SSUMjW4hG+sUk7YbyuY0DncaDTJQ==", + "version": "0.160.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.160.0.tgz", + "integrity": "sha512-DLU8lc0zNIPkM7rH5/e1Ks1Z8tWCGRq6g8mPowdDJpw1CFBJMU7UoJjC6PefXW7z//SSl0b2+GCw14LB+uDhng==", "dev": true }, "node_modules/three-csg-ts": { @@ -14868,9 +14868,9 @@ "dev": true }, "three": { - "version": "0.155.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.155.0.tgz", - "integrity": "sha512-sNgCYmDijnIqkD/bMfk+1pHg3YzsxW7V2ChpuP6HCQ8NiZr3RufsXQr8M3SSUMjW4hG+sUk7YbyuY0DncaDTJQ==", + "version": "0.160.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.160.0.tgz", + "integrity": "sha512-DLU8lc0zNIPkM7rH5/e1Ks1Z8tWCGRq6g8mPowdDJpw1CFBJMU7UoJjC6PefXW7z//SSl0b2+GCw14LB+uDhng==", "dev": true }, "three-csg-ts": { diff --git a/package.json b/package.json index 4cfc97c7..06514618 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "jest": "^27.2.4", "parcel": "^2.6.0", "rollup": "^2.70.0", - "three": "^0.155.0", + "three": "^0.160.0", "three-csg-ts": "^3.1.10", "three-mesh-bvh": "^0.6.6", "typescript": "^4.8.2" From 374e7d434e0cce62d39e093942b2a5cbbebfefe7 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 16:10:29 +0900 Subject: [PATCH 60/74] Improve parallel line condition --- src/core/splitter/utils.js | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index 5c01f7e8..a072a483 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -1,4 +1,4 @@ -import { Vector3, Line3 } from 'three'; +import { Vector3, Line3, MathUtils } from 'three'; import { closestPointsSegmentToSegment } from 'three-mesh-bvh/src/math/MathUtilities.js'; export function transformToFrame( tri, frame ) { @@ -164,6 +164,7 @@ export function getTriangleLineIntersection( line, tri, target ) { const vec = new Vector3(); const edge = new Line3(); const arr = [ tri.a, tri.b, tri.c ]; + const EPS = 1e-10; for ( let i = 0; i < 3; i ++ ) { const ni = ( i + 1 ) % 3; @@ -173,24 +174,30 @@ export function getTriangleLineIntersection( line, tri, target ) { if ( areEdgesParallel( edge, line ) ) { // TODO: we need to check if the lines are actually on each other - continue; + edge.closestPointToPoint( line.start, false, vec ); + if ( line.start.distanceTo( vec ) < EPS ) { - // let sp = edge.closestPointToPointParameter( line.start, false ); - // let ep = edge.closestPointToPointParameter( line.end, false ); - // if ( ! ( - // sp < 0 && ep < 0 || - // sp > 1 && ep > 1 - // ) ) { + let sp = edge.closestPointToPointParameter( line.start, false ); + let ep = edge.closestPointToPointParameter( line.end, false ); + if ( ! ( + sp < 0 && ep < 0 || + sp > 1 && ep > 1 + ) ) { - // sp = MathUtils.clamp( sp, 0, 1 ); - // ep = MathUtils.clamp( ep, 0, 1 ); + sp = MathUtils.clamp( sp, 0, 1 ); + ep = MathUtils.clamp( ep, 0, 1 ); - // edge.at( sp, target.start ); - // edge.at( ep, target.end ); + edge.at( sp, target.start ); + edge.at( ep, target.end ); - // return true; + return true; + + } + + } + + continue; - // } } else if ( lineIntersect( edge, line, vec ) ) { @@ -226,7 +233,7 @@ export function getTriangleLineIntersection( line, tri, target ) { target.copy( line ); setCount += 2; - } else { + } else if ( cs || ce ) { // TODO console.error( 'This shouldn\'t happen' ); From a06199784ace8044adb4f5cccb3f66c0092eeb9d Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 16:14:25 +0900 Subject: [PATCH 61/74] Clean up logs --- src/core/splitter/utils.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index a072a483..5345e152 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -204,20 +204,30 @@ export function getTriangleLineIntersection( line, tri, target ) { if ( setCount === 2 ) { // TODO - console.error( 'This shouldn\'t happen' ); + const d0 = target.start.distanceTo( vec ); + const d1 = target.end.distanceTo( vec ); + if ( d0 > EPS && d1 > EPS ) { + + console.error( 'This shouldn\'t happen' ); + + } } else if ( setCount === 1 ) { - target.start.copy( vec ); + if ( vec.distanceTo( target.end ) > EPS ) { + + target.start.copy( vec ); + setCount ++; + + } } else { target.end.copy( vec ); + setCount ++; } - setCount ++; - } } From 00b145ea3c579f3dd4ce556949b76e2eebd34c31 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 18:04:36 +0900 Subject: [PATCH 62/74] Fix swap iterations --- src/core/splitter/EdgeGraph.js | 148 +++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 51 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index ef85d45b..3e60b274 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -1,10 +1,20 @@ -import { Vector3, Line3, Triangle } from 'three'; -import { lineIntersect } from './utils.js'; +import { Vector3, Line3, Triangle, Line } from 'three'; +import { areEdgesParallel, lineIntersect } from './utils.js'; import { ObjectPool } from './ObjectPool.js'; const _vec = new Vector3(); const _triangleVertices = new Array( 3 ); const _edgesToAdd = new Array( 3 ); +const _edgesToSwap = []; +const SWAP_ITERATIONS = 3; + +function doEdgesMatch( a, b ) { + + const forwardMatch = b.startIndex === a.startIndex && b.endIndex === a.endIndex; + const reverseMatch = b.startIndex === a.endIndex && b.endIndex === a.startIndex; + return forwardMatch || reverseMatch; + +} class GraphTriangle extends Triangle { @@ -175,7 +185,7 @@ export class EdgeGraph { insertEdge( edge ) { - const { points, edges, edgePool } = this; + const { points, edgePool } = this; const { start, end } = edge; // insert the edge points into the graph @@ -189,55 +199,11 @@ export class EdgeGraph { inserting.end.copy( points[ endIndex ] ); inserting.endIndex = endIndex; - for ( let i = 0, l = edges.length; i < l; i ++ ) { - - // swap the edge if we don't emanate from the same point - const other = edges[ i ]; - if ( - other.startIndex !== inserting.startIndex && - other.startIndex !== inserting.endIndex && - other.endIndex !== inserting.startIndex && - other.endIndex !== inserting.endIndex - ) { - - if ( lineIntersect( inserting, other, _vec ) ) { - - if ( other.required ) { - - // TODO - // THESE ARE NOT INTERSECTING?! - console.error( 'FAILURE' ); - console.log( - inserting.clone(), - other.clone(), - _vec.clone(), - - inserting.closestPointToPointParameter( _vec, false ), - other.closestPointToPointParameter( _vec, false ), - ); - - } else { + // swap the edges + if ( ! this.markMatchingEdgeRequired( inserting ) ) { - this.swapEdge( other ); - - } - - } - - } - - // if we found the edge that matches the target edge then mark it as required and continue - if ( ( - other.startIndex === inserting.startIndex && - other.endIndex === inserting.endIndex - ) || ( - other.startIndex === inserting.endIndex && - other.endIndex === inserting.startIndex - ) ) { - - other.required = true; - - } + // TODO + console.error( 'Matching edge could not be found' ); } @@ -437,6 +403,86 @@ export class EdgeGraph { } + markMatchingEdgeRequired( inserting ) { + + const { edges } = this; + + _edgesToSwap.length = 0; + for ( let i = 0, l = edges.length; i < l; i ++ ) { + + // swap the edge if we don't emanate from the same point + const other = edges[ i ]; + if ( + other.startIndex !== inserting.startIndex && + other.startIndex !== inserting.endIndex && + other.endIndex !== inserting.startIndex && + other.endIndex !== inserting.endIndex + ) { + + // TODO + // if ( areEdgesParallel( inserting, other ) ) { + + // let sp = inserting.closestPointToPointParameter( other.start, false ); + // let ep = inserting.closestPointToPointParameter( other.end, false ); + + // if ( ! ( + // sp < 0 && ep < 0 || + // sp > 1 && ep > 1 + // ) ) { + + // // TODO: but do they overlap + // // console.log( 'THEYRE PARALLEL', sp, ep ); + + // } + + // } + + if ( lineIntersect( inserting, other, _vec ) ) { + + _edgesToSwap.push( other ); + + } + + } else { + + // if we found an edge that matches without swapping then theres no need + // to continue + if ( doEdgesMatch( inserting, other ) ) { + + other.required = true; + return true; + + } + + } + + } + + // try for a few iterations to swap edges until they work + for ( let i = 0; i < SWAP_ITERATIONS; i ++ ) { + + for ( let j = 0, l = _edgesToSwap.length; j < l; j ++ ) { + + const other = _edgesToSwap[ j ]; + + this.swapEdge( other ); + + // check if the edge swapped into the form we needed + if ( doEdgesMatch( inserting, other ) ) { + + other.required = true; + return true; + + } + + } + + } + + return false; + + } + swapEdge( edge ) { const { triangle, reverseTriangle } = edge; From 5a03e39ff02a6816347446ffbebd74bdf19a735c Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 21:25:46 +0900 Subject: [PATCH 63/74] some improvements --- src/core/splitter/EdgeGraph.js | 9 ++++++++- src/core/splitter/utils.js | 9 ++++++++- src/objects/PointsHelper.js | 7 +++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 3e60b274..e49f8931 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -192,6 +192,13 @@ export class EdgeGraph { const startIndex = this.insertPoint( start ); const endIndex = this.insertPoint( end ); + // we've aligned on the same point + if ( startIndex === endIndex ) { + + return; + + } + // the edge we're trying to insert const inserting = edgePool.getInstance(); inserting.start.copy( points[ startIndex ] ); @@ -437,6 +444,7 @@ export class EdgeGraph { // } + // TODO: make sure we account for parallel scenarios if ( lineIntersect( inserting, other, _vec ) ) { _edgesToSwap.push( other ); @@ -464,7 +472,6 @@ export class EdgeGraph { for ( let j = 0, l = _edgesToSwap.length; j < l; j ++ ) { const other = _edgesToSwap[ j ]; - this.swapEdge( other ); // check if the edge swapped into the form we needed diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index 5345e152..334ffc4c 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -198,7 +198,6 @@ export function getTriangleLineIntersection( line, tri, target ) { continue; - } else if ( lineIntersect( edge, line, vec ) ) { if ( setCount === 2 ) { @@ -236,6 +235,7 @@ export function getTriangleLineIntersection( line, tri, target ) { const cs = tri.containsPoint( line.start ); const ce = tri.containsPoint( line.end ); + if ( setCount === 0 ) { if ( cs && ce ) { @@ -284,6 +284,13 @@ export function getTriangleLineIntersection( line, tri, target ) { } + // if the final edge is degenerate then we haven't intersected + if ( target.distance() < EPS ) { + + return false; + + } + return setCount === 2; } diff --git a/src/objects/PointsHelper.js b/src/objects/PointsHelper.js index 8a8addab..cf89757a 100644 --- a/src/objects/PointsHelper.js +++ b/src/objects/PointsHelper.js @@ -9,6 +9,13 @@ export class PointsHelper extends InstancedMesh { } + set radius( v ) { + + this.geometry.dispose(); + this.geometry = new SphereGeometry( v ); + + } + constructor( count = 1000, points = [] ) { super( new SphereGeometry( 0.025 ), new MeshBasicMaterial(), count ); From 59c54c063aa04a3f7de203b5d942ee5a4f6b3913 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 21:29:32 +0900 Subject: [PATCH 64/74] Remove need for 2d transformations --- src/core/splitter/TriangleGraphSplitter.js | 38 ++-------------------- 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index d7bb4c1b..e8fc8f2c 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -30,8 +30,6 @@ export class TriangleGraphSplitter { this.initialTri = new Triangle(); this.plane = new Plane(); - this.frame = new Matrix4(); - this.invFrame = new Matrix4(); } @@ -39,18 +37,15 @@ export class TriangleGraphSplitter { this.reset(); - const { frame, invFrame, initialTri, graph, plane } = this; + const { initialTri, graph, plane } = this; tri.getNormal( _norm ); _right.subVectors( tri.a, tri.b ).normalize(); _up.crossVectors( _norm, _right ); tri.getPlane( plane ); - frame.makeBasis( _right, _up, _norm ).setPosition( tri.a ); - invFrame.copy( frame ).invert(); initialTri.copy( tri ); - transformToFrame( initialTri, invFrame ); graph.initialize( initialTri ); @@ -65,7 +60,7 @@ export class TriangleGraphSplitter { splitByTriangle( tri ) { - const { plane, invFrame, initialTri, graph } = this; + const { plane, initialTri, graph } = this; const planePoints = []; let coplanarPoints = 0; @@ -112,13 +107,6 @@ export class TriangleGraphSplitter { } - planePoints.forEach( p => { - - p.applyMatrix4( invFrame ); - p.z = 0; - - } ); - // find the edges that intersect with the triangle itself if ( coplanarPoints === 3 ) { @@ -158,28 +146,6 @@ export class TriangleGraphSplitter { complete() { - const { graph, frame } = this; - graph.points.forEach( v => { - - v.applyMatrix4( frame ); - - } ); - - graph.edges.forEach( e => { - - e.start.applyMatrix4( frame ); - e.end.applyMatrix4( frame ); - - } ); - - graph.triangles.forEach( t => { - - t.a.applyMatrix4( frame ); - t.b.applyMatrix4( frame ); - t.c.applyMatrix4( frame ); - - } ); - const issues = this.graph.validate(); if ( issues.length ) { From 5e09b97dc497a633558122558bb51124dfd617ca Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 21:33:28 +0900 Subject: [PATCH 65/74] comment --- src/core/splitter/EdgeGraph.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index e49f8931..03bbf972 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -292,13 +292,9 @@ export class EdgeGraph { index = points.length; points.push( pointPool.getInstance().copy( point ) ); - // NOTE: if the edge is required here then we have a problem - it shouldn't have to be split + // NOTE: it's possible for use to land on a required edge here to split but this is the sad reality of + // floating point math :( const e = edges[ intersectingEdge ]; - if ( e.required ) { - - console.error( 'WE ARE ON A REQUIRED EDGE' ); - - } // construct the edges const l0 = edgePool.getInstance(); From 444f53d4a8f57069874367196c370c1354932e93 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 22:38:26 +0900 Subject: [PATCH 66/74] Fix splitter --- src/core/splitter/TriangleGraphSplitter.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index e8fc8f2c..de1b41f8 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -86,8 +86,7 @@ export class TriangleGraphSplitter { _edge.start.copy( p0 ); _edge.end.copy( p1 ); - // consider the end point to be not hittable - if ( ! plane.intersectLine( _edge, _hitPoint ) || _hitPoint.distanceTo( p1 ) < EPSILON ) { + if ( ! plane.intersectLine( _edge, _hitPoint ) ) { // add buffer for the start point if ( d0 < EPSILON ) { @@ -100,6 +99,11 @@ export class TriangleGraphSplitter { } + } else if ( _hitPoint.distanceTo( p1 ) < EPSILON ) { + + // consider the end point to be not hittable + continue; + } // TODO: use a pool for these points? From 82713cbf287cd95a91f818ba548096e9814ee325 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 5 Jan 2024 22:38:45 +0900 Subject: [PATCH 67/74] Revert "Remove need for 2d transformations" This reverts commit 59c54c063aa04a3f7de203b5d942ee5a4f6b3913. --- src/core/splitter/TriangleGraphSplitter.js | 38 ++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index de1b41f8..6e082e56 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -30,6 +30,8 @@ export class TriangleGraphSplitter { this.initialTri = new Triangle(); this.plane = new Plane(); + this.frame = new Matrix4(); + this.invFrame = new Matrix4(); } @@ -37,15 +39,18 @@ export class TriangleGraphSplitter { this.reset(); - const { initialTri, graph, plane } = this; + const { frame, invFrame, initialTri, graph, plane } = this; tri.getNormal( _norm ); _right.subVectors( tri.a, tri.b ).normalize(); _up.crossVectors( _norm, _right ); tri.getPlane( plane ); + frame.makeBasis( _right, _up, _norm ).setPosition( tri.a ); + invFrame.copy( frame ).invert(); initialTri.copy( tri ); + transformToFrame( initialTri, invFrame ); graph.initialize( initialTri ); @@ -60,7 +65,7 @@ export class TriangleGraphSplitter { splitByTriangle( tri ) { - const { plane, initialTri, graph } = this; + const { plane, invFrame, initialTri, graph } = this; const planePoints = []; let coplanarPoints = 0; @@ -111,6 +116,13 @@ export class TriangleGraphSplitter { } + planePoints.forEach( p => { + + p.applyMatrix4( invFrame ); + p.z = 0; + + } ); + // find the edges that intersect with the triangle itself if ( coplanarPoints === 3 ) { @@ -150,6 +162,28 @@ export class TriangleGraphSplitter { complete() { + const { graph, frame } = this; + graph.points.forEach( v => { + + v.applyMatrix4( frame ); + + } ); + + graph.edges.forEach( e => { + + e.start.applyMatrix4( frame ); + e.end.applyMatrix4( frame ); + + } ); + + graph.triangles.forEach( t => { + + t.a.applyMatrix4( frame ); + t.b.applyMatrix4( frame ); + t.c.applyMatrix4( frame ); + + } ); + const issues = this.graph.validate(); if ( issues.length ) { From 354ae1eda8cc3e0152f58a8c499591a1d60a2720 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 6 Jan 2024 00:09:34 +0900 Subject: [PATCH 68/74] improvements --- src/core/splitter/EdgeGraph.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 03bbf972..eda1a1a1 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -168,6 +168,7 @@ export class EdgeGraph { edge.startIndex = i; edge.end.copy( p1 ); edge.endIndex = ni; + edge.required = true; newTriangle.setEdge( i, edge, false ); edges.push( edge ); @@ -237,7 +238,11 @@ export class EdgeGraph { index = points.length; points.push( pointPool.getInstance().copy( point ) ); - const containingTriangle = triangles.findIndex( t => t.containsPoint( point ) ); + const containingTriangle = triangles.findIndex( t => { + + return t.getArea() !== 0 && t.containsPoint( point ); + + } ); if ( containingTriangle === - 1 ) { // TODO: this should never happen From d10f2b46725efe3407e3c3216d51e807fc3b0b29 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 6 Jan 2024 13:40:25 +0900 Subject: [PATCH 69/74] Improvements --- src/core/splitter/EdgeGraph.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index eda1a1a1..0a33d6ce 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -1,5 +1,5 @@ -import { Vector3, Line3, Triangle, Line } from 'three'; -import { areEdgesParallel, lineIntersect } from './utils.js'; +import { Vector3, Line3, Triangle } from 'three'; +import { lineIntersect } from './utils.js'; import { ObjectPool } from './ObjectPool.js'; const _vec = new Vector3(); @@ -228,7 +228,14 @@ export class EdgeGraph { const intersectingEdge = edges.findIndex( e => { e.closestPointToPoint( point, true, _vec ); - return _vec.distanceTo( point ) < EPSILON; + const found = _vec.distanceTo( point ) < EPSILON; + if ( found ) { + + point.copy( _vec ); + + } + + return found; } ); @@ -555,12 +562,24 @@ export class EdgeGraph { foundTriangleSet.add( edge.triangle ); + if ( triangles.indexOf( edge.triangle ) === - 1 ) { + + messages.push( 'Incorrect triangle assigned.' ); + + } + } if ( edge.reverseTriangle ) { foundTriangleSet.add( edge.reverseTriangle ); + if ( triangles.indexOf( edge.reverseTriangle ) === - 1 ) { + + messages.push( 'Incorrect triangle assigned.' ); + + } + } } ); From 6bf56c084dba2a35e26b80faef950f52887c8b43 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 6 Jan 2024 13:42:34 +0900 Subject: [PATCH 70/74] Improvements --- src/core/splitter/TriangleGraphSplitter.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 6e082e56..9cbf8dd0 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -91,7 +91,7 @@ export class TriangleGraphSplitter { _edge.start.copy( p0 ); _edge.end.copy( p1 ); - if ( ! plane.intersectLine( _edge, _hitPoint ) ) { + if ( ! plane.intersectLine( _edge, _hitPoint ) || _hitPoint.distanceTo( p1 ) < EPSILON ) { // add buffer for the start point if ( d0 < EPSILON ) { @@ -104,11 +104,6 @@ export class TriangleGraphSplitter { } - } else if ( _hitPoint.distanceTo( p1 ) < EPSILON ) { - - // consider the end point to be not hittable - continue; - } // TODO: use a pool for these points? From c67e7fa0a305a1b17b5c05a191ad4fa49e723e56 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 6 Jan 2024 13:50:28 +0900 Subject: [PATCH 71/74] Remove need for basis --- src/core/splitter/TriangleGraphSplitter.js | 33 ++-------------------- src/core/splitter/utils.js | 9 ------ 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 9cbf8dd0..8fed9574 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -1,5 +1,5 @@ import { Triangle, Matrix4, Line3, Plane, Vector3 } from 'three'; -import { getTriangleLineIntersection, transformToFrame } from './utils.js'; +import { getTriangleLineIntersection } from './utils.js'; import { EdgeGraph } from './EdgeGraph.js'; const EPSILON = 1e-10; @@ -39,19 +39,15 @@ export class TriangleGraphSplitter { this.reset(); - const { frame, invFrame, initialTri, graph, plane } = this; + const { initialTri, graph, plane } = this; tri.getNormal( _norm ); _right.subVectors( tri.a, tri.b ).normalize(); _up.crossVectors( _norm, _right ); tri.getPlane( plane ); - frame.makeBasis( _right, _up, _norm ).setPosition( tri.a ); - invFrame.copy( frame ).invert(); initialTri.copy( tri ); - transformToFrame( initialTri, invFrame ); - graph.initialize( initialTri ); } @@ -113,8 +109,7 @@ export class TriangleGraphSplitter { planePoints.forEach( p => { - p.applyMatrix4( invFrame ); - p.z = 0; + plane.projectPoint( p, p ); } ); @@ -157,28 +152,6 @@ export class TriangleGraphSplitter { complete() { - const { graph, frame } = this; - graph.points.forEach( v => { - - v.applyMatrix4( frame ); - - } ); - - graph.edges.forEach( e => { - - e.start.applyMatrix4( frame ); - e.end.applyMatrix4( frame ); - - } ); - - graph.triangles.forEach( t => { - - t.a.applyMatrix4( frame ); - t.b.applyMatrix4( frame ); - t.c.applyMatrix4( frame ); - - } ); - const issues = this.graph.validate(); if ( issues.length ) { diff --git a/src/core/splitter/utils.js b/src/core/splitter/utils.js index 334ffc4c..f3bdbdc6 100644 --- a/src/core/splitter/utils.js +++ b/src/core/splitter/utils.js @@ -1,15 +1,6 @@ import { Vector3, Line3, MathUtils } from 'three'; import { closestPointsSegmentToSegment } from 'three-mesh-bvh/src/math/MathUtilities.js'; -export function transformToFrame( tri, frame ) { - - tri.a.applyMatrix4( frame ).z = 0; - tri.b.applyMatrix4( frame ).z = 0; - tri.c.applyMatrix4( frame ).z = 0; - return tri; - -} - function isPositive( start, end, intersection ) { // all parameters are THREE.Vector3() let v1 = new Vector3().copy( end ).sub( start ); From 9d42cd1f53fc73a5db51264194cf608b1b5610b6 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 6 Jan 2024 13:50:57 +0900 Subject: [PATCH 72/74] Remove unused fields --- src/core/splitter/TriangleGraphSplitter.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/splitter/TriangleGraphSplitter.js b/src/core/splitter/TriangleGraphSplitter.js index 8fed9574..54c564ee 100644 --- a/src/core/splitter/TriangleGraphSplitter.js +++ b/src/core/splitter/TriangleGraphSplitter.js @@ -1,4 +1,4 @@ -import { Triangle, Matrix4, Line3, Plane, Vector3 } from 'three'; +import { Triangle, Line3, Plane, Vector3 } from 'three'; import { getTriangleLineIntersection } from './utils.js'; import { EdgeGraph } from './EdgeGraph.js'; @@ -30,8 +30,6 @@ export class TriangleGraphSplitter { this.initialTri = new Triangle(); this.plane = new Plane(); - this.frame = new Matrix4(); - this.invFrame = new Matrix4(); } @@ -61,7 +59,7 @@ export class TriangleGraphSplitter { splitByTriangle( tri ) { - const { plane, invFrame, initialTri, graph } = this; + const { plane, initialTri, graph } = this; const planePoints = []; let coplanarPoints = 0; From 999ee18323ada179f580b087d4a425089983ff37 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 6 Jan 2024 14:59:32 +0900 Subject: [PATCH 73/74] Updates --- src/core/splitter/EdgeGraph.js | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/core/splitter/EdgeGraph.js b/src/core/splitter/EdgeGraph.js index 0a33d6ce..c1d59427 100644 --- a/src/core/splitter/EdgeGraph.js +++ b/src/core/splitter/EdgeGraph.js @@ -186,6 +186,12 @@ export class EdgeGraph { insertEdge( edge ) { + if ( edge.distance() < EPSILON ) { + + return; + + } + const { points, edgePool } = this; const { start, end } = edge; @@ -636,4 +642,47 @@ export class EdgeGraph { } + removeTriangle( t ) { + + const { points, edges, triangles } = this; + triangles.splice( triangles.indexOf( t ), 1 ); + + triangles.edges.forEach( info => { + + if ( info.reversed ) info.edge.reverseTriangle = null; + else info.edge.triangle = null; + + if ( info.edge.triangle === null && info.edge.reverseTriangle ) { + + edges.splice( edges.indexOf( info.edge ), 1 ); + + } + + } ); + + } + + sync() { + + const { points, edges, triangles } = this; + + edges.forEach( e => { + + e.start.copy( points[ e.startIndex ] ); + e.end.copy( points[ e.endIndex ] ); + + } ); + + triangles.forEach( t => { + + t.edges.forEach( ( info, i ) => { + + t.setEdge( i, info.edge, info.reversed ); + + } ); + + } ); + + } + } From 8d28a2e419c5fd27b54fe804354de39ab34c4fba Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 6 Jan 2024 18:09:39 +0900 Subject: [PATCH 74/74] Add legacy splitter toggle to example --- examples/simple.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/simple.js b/examples/simple.js index 81246e45..85f546dd 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -54,6 +54,7 @@ const params = { brush2Color: '#E91E63', operation: SUBTRACTION, + legacySplitter: false, wireframe: false, displayBrushes: true, displayControls: true, @@ -267,6 +268,12 @@ async function init() { } + } ); + gui.add( params, 'legacySplitter' ).onChange( v => { + + csgEvaluator.useLegacyTriangleSplitter = v; + needsUpdate = true; + } ); gui.add( params, 'displayBrushes' ); gui.add( params, 'displayControls' );