Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/core/Evaluator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CDTTriangleSplitter } from './CDTTriangleSplitter.js';
import { PolygonSplitter } from './PolygonSplitter.js';
import { LegacyTriangleSplitter } from './LegacyTriangleSplitter.js';
import { OperationDebugData } from './debug/OperationDebugData.js';
import { performOperation } from './operations/operations.js';
Expand All @@ -11,15 +11,15 @@ export class Evaluator {

get useCDTClipping() {

return this.triangleSplitter instanceof CDTTriangleSplitter;
return this.triangleSplitter instanceof PolygonSplitter;

}

set useCDTClipping( v ) {

if ( v !== this.useCDTClipping ) {

this.triangleSplitter = v ? new CDTTriangleSplitter() : new LegacyTriangleSplitter();
this.triangleSplitter = v ? new PolygonSplitter() : new LegacyTriangleSplitter();

}

Expand Down
54 changes: 53 additions & 1 deletion src/core/CDTTriangleSplitter.js → src/core/PolygonSplitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function edgesToIndices( edges, outputVertices, outputIndices, epsilonScale ) {

}

export class CDTTriangleSplitter {
export class PolygonSplitter {

constructor() {

Expand Down Expand Up @@ -327,6 +327,58 @@ export class CDTTriangleSplitter {

}

getPolygonRegions() {

const { triangles, triangleConnectivity } = this;
const regions = [];
const visited = new Set();

for ( let i = 0, l = triangles.length; i < l; i ++ ) {

if ( visited.has( i ) ) continue;

const region = {
triangleIndices: [],
midpoint: new Vector3(),
};

// flood-fill connected sub-triangles via non-constraint edges
const stack = [ i ];
while ( stack.length > 0 ) {

const idx = stack.pop();
if ( visited.has( idx ) ) continue;
visited.add( idx );

region.triangleIndices.push( idx );

const connected = triangleConnectivity[ idx ];
if ( connected ) {

for ( let c = 0, cl = connected.length; c < cl; c ++ ) {

if ( ! visited.has( connected[ c ] ) ) {

stack.push( connected[ c ] );

}

}

}

}

// use the first sub-triangle's midpoint as the representative point
triangles[ region.triangleIndices[ 0 ] ].getMidpoint( region.midpoint );
regions.push( region );

}

return regions;

}

reset() {

this.trianglePool.clear();
Expand Down
140 changes: 62 additions & 78 deletions src/core/operations/operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ const _tri = new Triangle();
const _barycoordTri = new Triangle();
const _actions = [];
const _builders = [];
const _traversed = new Set();
const _midpoint = new Vector3();
const _normal = new Vector3();
const _coplanarTrianglePool = new Pool( () => new Triangle() );
const _coplanarNormal = new Vector3();
Expand Down Expand Up @@ -262,38 +260,50 @@ function performSplitTriangleOperations(


// cache all the attribute data in origA's local frame
const { triangles, triangleIndices = [], triangleConnectivity = [] } = splitter;
const { triangles, triangleIndices = [] } = splitter;
for ( let i = 0, l = builders.length; i < l; i ++ ) {

builders[ i ].initInterpolatedAttributeData( a.geometry, _builderMatrix, _normalMatrix, ia0, ia1, ia2 );

}

// for all triangles in the split result
_traversed.clear();
for ( let ib = 0, l = triangles.length; ib < l; ib ++ ) {
// get polygon regions from the splitter if it supports them,
// otherwise treat each triangle as its own region
let regions;
if ( splitter.getPolygonRegions ) {

// skip the triangle if we've already traversed
if ( _traversed.has( ib ) ) {
regions = splitter.getPolygonRegions();

continue;
} else {

}
regions = triangles.map( ( tri, idx ) => {

const mp = new Vector3();
tri.getMidpoint( mp );
return {
triangleIndices: [ idx ],
midpoint: mp,
};

} );

}

// classify and add triangles per polygon region
for ( let ri = 0, rl = regions.length; ri < rl; ri ++ ) {

// try to use the side derived from the clipping but if it turns out to be
// uncertain then fall back to the raycasting approach.
// If checking the sided ness against brush B's BVH then we need to transform
// into the appropriate frame
const clippedTri = triangles[ ib ];
const region = regions[ ri ];
const { triangleIndices: regionTriIndices, midpoint: regionMidpoint } = region;

// determine the hit side for this entire region using the representative midpoint
const raycastMatrix = invert ? null : _matrix;
let hitSide = null;

// check against the set of coplanar triangles to see if we can easily determine what to do
clippedTri.getMidpoint( _midpoint );
// check coplanar triangles first
for ( let cp = 0, cpl = _coplanarTriangles.length; cp < cpl; cp ++ ) {

const cpt = _coplanarTriangles[ cp ];
if ( cpt.containsPoint( _midpoint ) ) {
if ( cpt.containsPoint( regionMidpoint ) ) {

cpt.getNormal( _coplanarNormal );
hitSide = _normal.dot( _coplanarNormal ) > 0 ? COPLANAR_ALIGNED : COPLANAR_OPPOSITE;
Expand All @@ -303,17 +313,18 @@ function performSplitTriangleOperations(

}

// if the clipped triangle is no coplanar then fall back to raycasting
if ( hitSide === null ) {

hitSide = getHitSide( clippedTri, bBVH, raycastMatrix );
// use the first triangle in the region for raycasting
const firstTriIdx = regionTriIndices[ 0 ];
hitSide = getHitSide( triangles[ firstTriIdx ], bBVH, raycastMatrix );

}

// determine actions for each builder
_actions.length = 0;
_builders.length = 0;

// determine action to take for each builder
for ( let o = 0, lo = operations.length; o < lo; o ++ ) {

const op = getOperationAction( operations[ o ], hitSide, invert );
Expand All @@ -326,75 +337,48 @@ function performSplitTriangleOperations(

}

if ( _builders.length !== 0 ) {

// traverse the connectivity of the triangles to add them to the geometry
const stack = [ ib ];
while ( stack.length > 0 ) {

const index = stack.pop();
if ( _traversed.has( index ) ) {

continue;

}

// mark this triangle as traversed
_traversed.add( index );

// TODO: this is being skipped for now due to the connectivity graph not
// including small connections due to floating point error. Adding support
// for symmetric vertices across half edges may help this.
// push the connected triangle ids onto the stack
// const connected = triangleConnectivity[ index ] || [];
// for ( let c = 0, l = connected.length; c < l; c ++ ) {

// const connectedIndex = connected[ c ];
// if ( triangles[ connectedIndex ] !== null ) {

// stack.push( connectedIndex );
if ( _builders.length === 0 ) continue;

// }
// add all triangles in this region to the geometry
for ( let ti = 0, tl = regionTriIndices.length; ti < tl; ti ++ ) {

// }
const index = regionTriIndices[ ti ];
const tri = triangles[ index ];

// get the triangle indices
const indices = triangleIndices[ index ];
let t0 = null, t1 = null, t2 = null;
if ( indices ) {
// get the triangle indices
const indices = triangleIndices[ index ];
let t0 = null, t1 = null, t2 = null;
if ( indices ) {

t0 = indices[ 0 ];
t1 = indices[ 1 ];
t2 = indices[ 2 ];
t0 = indices[ 0 ];
t1 = indices[ 1 ];
t2 = indices[ 2 ];

}

// get the barycentric coordinates relative to the base triangle
const tri = triangles[ index ];
_triA.getBarycoord( tri.a, _barycoordTri.a );
_triA.getBarycoord( tri.b, _barycoordTri.b );
_triA.getBarycoord( tri.c, _barycoordTri.c );
}

// append the triangle to all builders
for ( let k = 0, lk = _builders.length; k < lk; k ++ ) {
// get the barycentric coordinates relative to the base triangle
_triA.getBarycoord( tri.a, _barycoordTri.a );
_triA.getBarycoord( tri.b, _barycoordTri.b );
_triA.getBarycoord( tri.c, _barycoordTri.c );

const builder = _builders[ k ];
const action = _actions[ k ];
const invertTri = action === INVERT_TRI;
const invert = invertedGeometry !== invertTri;
// append the triangle to all builders
for ( let k = 0, lk = _builders.length; k < lk; k ++ ) {

builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.a, t0, invert );
if ( invert ) {
const builder = _builders[ k ];
const action = _actions[ k ];
const invertTri = action === INVERT_TRI;
const invert = invertedGeometry !== invertTri;

builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.c, t2, invert );
builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.b, t1, invert );
builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.a, t0, invert );
if ( invert ) {

} else {
builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.c, t2, invert );
builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.b, t1, invert );

builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.b, t1, invert );
builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.c, t2, invert );
} else {

}
builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.b, t1, invert );
builder.appendInterpolatedAttributeData( groupIndex, _barycoordTri.c, t2, invert );

}

Expand Down
9 changes: 4 additions & 5 deletions src/core/operations/operationsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,10 @@ export function collectIntersectingTriangles( a, b ) {

} else {

// non-coplanar
const ea = _edgePool.getInstance().copy( _edge );
const eb = _edgePool.getInstance().copy( _edge );
aIntersections.addIntersectionEdge( va, ea );
bIntersections.addIntersectionEdge( vb, eb );
// non-coplanar — share the same edge instance for symmetric splitting
const e = _edgePool.getInstance().copy( _edge );
aIntersections.addIntersectionEdge( va, e );
bIntersections.addIntersectionEdge( vb, e );

}

Expand Down
16 changes: 16 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ export class LegacyTriangleSplitter {

}

export class PolygonSplitter {

trianglePool: TrianglePool;
triangles: Triangle[];
triangleIndices: Array<Array<number | string>>;
triangleConnectivity: Array<Array<number>>;
normal: Vector3;

initialize( tri: Triangle, i0?: number, i1?: number, i2?: number ): void;
addConstraintEdge( edge: Line3 ): void;
triangulate(): void;
getPolygonRegions(): Array<{ triangleIndices: number[], midpoint: Vector3 }>;
reset(): void;

}

export class HalfEdgeMap {

constructor( geometry?: BufferGeometry );
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export * from './core/Evaluator.js';
export * from './core/operations/Operation.js';
export * from './core/operations/OperationGroup.js';
export * from './core/LegacyTriangleSplitter.js';
export * from './core/CDTTriangleSplitter.js';
export * from './core/PolygonSplitter.js';
export * from './core/HalfEdgeMap.js';
export * from './materials/GridMaterial.js';

Expand Down
Loading