Skip to content

Commit

Permalink
Merge pull request mrdoob#14377 from mquander/triangle-point-voronoi
Browse files Browse the repository at this point in the history
Make Triangle.closestPointToPoint much faster
  • Loading branch information
mrdoob authored Jul 1, 2018
2 parents 5739377 + 2eb9a12 commit 036d9a8
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 70 deletions.
91 changes: 62 additions & 29 deletions src/math/Triangle.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Vector3 } from './Vector3.js';
import { Line3 } from './Line3.js';
import { Plane } from './Plane.js';

/**
* @author bhouston / http://clara.io
Expand Down Expand Up @@ -218,12 +216,14 @@ Object.assign( Triangle.prototype, {

closestPointToPoint: function () {

var plane = new Plane();
var edgeList = [ new Line3(), new Line3(), new Line3() ];
var projectedPoint = new Vector3();
var closestPoint = new Vector3();
var vab = new Vector3();
var vac = new Vector3();
var vbc = new Vector3();
var vap = new Vector3();
var vbp = new Vector3();
var vcp = new Vector3();

return function closestPointToPoint( point, target ) {
return function closestPointToPoint( p, target ) {

if ( target === undefined ) {

Expand All @@ -232,48 +232,81 @@ Object.assign( Triangle.prototype, {

}

var minDistance = Infinity;
var a = this.a, b = this.b, c = this.c;
var v, w;

// project the point onto the plane of the triangle
// algorithm thanks to Real-Time Collision Detection by Christer Ericson,
// published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc.,
// under the accompanying license; see chapter 5.1.5 for detailed explanation.
// basically, we're distinguishing which of the voronoi regions of the triangle
// the point lies in with the minimum amount of redundant computation.

plane.setFromCoplanarPoints( this.a, this.b, this.c );
plane.projectPoint( point, projectedPoint );
vab.subVectors( b, a );
vac.subVectors( c, a );
vap.subVectors( p, a );
var d1 = vab.dot( vap );
var d2 = vac.dot( vap );
if ( d1 <= 0 && d2 <= 0 ) {

// check if the projection lies within the triangle
// vertex region of A; barycentric coords (1, 0, 0)
return target.copy( a );

if ( this.containsPoint( projectedPoint ) === true ) {
}

vbp.subVectors( p, b );
var d3 = vab.dot( vbp );
var d4 = vac.dot( vbp );
if ( d3 >= 0 && d4 <= d3 ) {

// if so, this is the closest point
// vertex region of B; barycentric coords (0, 1, 0)
return target.copy( b );

target.copy( projectedPoint );
}

} else {
var vc = d1 * d4 - d3 * d2;
if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) {

// if not, the point falls outside the triangle. the target is the closest point to the triangle's edges or vertices
v = d1 / ( d1 - d3 );
// edge region of AB; barycentric coords (1-v, v, 0)
return target.copy( a ).addScaledVector( vab, v );

edgeList[ 0 ].set( this.a, this.b );
edgeList[ 1 ].set( this.b, this.c );
edgeList[ 2 ].set( this.c, this.a );
}

for ( var i = 0; i < edgeList.length; i ++ ) {
vcp.subVectors( p, c );
var d5 = vab.dot( vcp );
var d6 = vac.dot( vcp );
if ( d6 >= 0 && d5 <= d6 ) {

edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint );
// vertex region of C; barycentric coords (0, 0, 1)
return target.copy( c );

var distance = projectedPoint.distanceToSquared( closestPoint );
}

if ( distance < minDistance ) {
var vb = d5 * d2 - d1 * d6;
if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) {

minDistance = distance;
w = d2 / ( d2 - d6 );
// edge region of AC; barycentric coords (1-w, 0, w)
return target.copy( a ).addScaledVector( vac, w );

target.copy( closestPoint );
}

}
var va = d3 * d6 - d5 * d4;
if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) {

}
vbc.subVectors( c, b );
w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) );
// edge region of BC; barycentric coords (0, 1-w, w)
return target.copy( b ).addScaledVector( vbc, w ); // edge region of BC

}

return target;
// face region
var denom = 1 / ( va + vb + vc );
// u = va * denom
v = vb * denom;
w = vc * denom;
return target.copy( a ).addScaledVector( vab, v ).addScaledVector( vac, w );

};

Expand Down
83 changes: 42 additions & 41 deletions test/benchmark/benchmarks.html
Original file line number Diff line number Diff line change
@@ -1,50 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ThreeJS Benchmark Tests - Using Files in /src</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:700" rel="stylesheet" type="text/css">
<link href="normalize.css" rel="stylesheet" type="text/css">
<link href="style.css" rel="stylesheet" type="text/css">
<script src="../../build/three.min.js"></script>
<script src="vendor/lodash.min.js"></script>
<script src="vendor/benchmark-2.1.0.min.js"></script>
<script src="benchmark.js"></script>
<head>
<meta charset="utf-8">
<title>ThreeJS Benchmark Tests - Using Files in /src</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:700" rel="stylesheet" type="text/css">
<link href="normalize.css" rel="stylesheet" type="text/css">
<link href="style.css" rel="stylesheet" type="text/css">
<script src="../../build/three.min.js"></script>
<script src="vendor/lodash.min.js"></script>
<script src="vendor/benchmark-2.1.0.min.js"></script>
<script src="benchmark.js"></script>

<script src="core/Vector3Components.js"></script>
<script src="core/Vector3Storage.js"></script>
<script src="core/Vector3Length.js"></script>
<script src="core/Float32Array.js"></script>
<script src="core/UpdateMatrixWorld.js"></script>
</head>
<body>
<header>
<h1>Three JS Benchmarks Suite</h1>
</header>
<section>
</section>
<script src="core/Vector3Components.js"></script>
<script src="core/Vector3Storage.js"></script>
<script src="core/Vector3Length.js"></script>
<script src="core/Float32Array.js"></script>
<script src="core/UpdateMatrixWorld.js"></script>
<script src="core/TriangleClosestPoint.js"></script>
</head>
<body>
<header>
<h1>Three JS Benchmarks Suite</h1>
</header>
<section>
</section>

<template id="suite">
<article>
<header>
<h2></h2>
<h3>Start</h3>
</header>
<div class="results">
<div class"head">
<p class="name">Name</p>
<p class="ops">Ops / Sec</p>
<p class="desv">±</p>
<template id="suite">
<article>
<header>
<h2></h2>
<h3>Start</h3>
</header>
<div class="results">
<div class"head">
<p class="name">Name</p>
<p class="ops">Ops / Sec</p>
<p class="desv">±</p>
</div>
</div>
</div>
</article>
</template>
</article>
</template>

<template id="suite-test">
<div>
<p class="name"></p>
<p class="ops"></p>
<p class="desv"></p>
<template id="suite-test">
<div>
<p class="name"></p>
<p class="ops"></p>
<p class="desv"></p>
</div>
</template>

Expand Down
61 changes: 61 additions & 0 deletions test/benchmark/core/TriangleClosestPoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
(function() {

THREE = Bench.THREE;

// these vertices and triangles are those of a unit icosahedron centered at the origin
var phi = 1.618;
var verts = [
[phi, 1, 0], [-phi, 1, 0], [phi, -1, 0], [-phi, -1, 0],
[1, 0, phi], [1, 0, -phi], [-1, 0, phi], [-1, 0, -phi],
[0, phi, 1], [0, -phi, 1], [0, phi, -1], [0, -phi, -1],
];
var createVertex = function(c) {
return new THREE.Vector3(c[0], c[1], c[2]);
};
var createTriangle = function(i0, i1, i2) {
return new THREE.Triangle(createVertex(verts[i0]), createVertex(verts[i1]), createVertex(verts[i2]));
};
var triangles = [
createTriangle(0, 8, 4),
createTriangle(0, 5, 10),
createTriangle(2, 4, 9),
createTriangle(2, 11, 5),
createTriangle(1, 6, 8),
createTriangle(1, 10, 7),
createTriangle(3, 9, 6),
createTriangle(3, 7, 11),
createTriangle(0, 10, 8),
createTriangle(1, 8, 10),
createTriangle(2, 9, 11),
createTriangle(3, 9, 11),
createTriangle(4, 2, 0),
createTriangle(5, 0, 2),
createTriangle(6, 1, 3),
createTriangle(7, 3, 1),
createTriangle(8, 6, 4),
createTriangle(9, 4, 6),
createTriangle(10, 5, 7),
createTriangle(11, 7, 5),
];
// test a variety of points all in and around the icosahedron
var testPoints = [];
for (var x = -2; x <= 2; x += 0.5) {
for (var y = -2; y <= 2; y += 0.5) {
for (var z = -2; z <= 2; z += 0.5) {
testPoints.push(new THREE.Vector3(x, y, z));
}
}
}

var s = Bench.newSuite("Clamping point into triangles");

s.add('9^3 points, 20 triangles', function() {
var target = new THREE.Vector3();
for (var tidx = 0; tidx < triangles.length; tidx++) {
var triangle = triangles[tidx];
for (var pidx = 0; pidx < testPoints.length; pidx++) {
triangle.closestPointToPoint(testPoints[pidx], target);
}
}
});
})();

0 comments on commit 036d9a8

Please sign in to comment.