-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsliding_constraints.ts
101 lines (84 loc) · 2.47 KB
/
sliding_constraints.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import * as vec3 from 'toybox/math/vec3';
import {EPSILON} from 'toybox/math/constants';
let tmp = vec3.newZero();
/**
* Implements constraints for sliding an object along a surface.
* Similar in principle to the method described in:
* http://arxiv.org/pdf/1211.0059.pdf
* But fixes common cases where the method in the paper gets stuck.
*/
export class SlidingConstraints {
private v = vec3.newZero();
private normals: vec3.Type[] = [];
constructor(v: vec3.Type) {
this.reset(v);
}
reset(v: vec3.Type) {
vec3.setFromVec(this.v, v);
}
add(n: vec3.Type) {
let unique = true;
for (let i = 0; i < this.normals.length; ++i) {
if (vec3.distanceSqr(this.normals[i], n) <= EPSILON * EPSILON) {
unique = false;
break;
}
}
if (unique) {
this.normals.push(vec3.newFromVec(n));
}
this.updateConstraints_();
}
apply(v: vec3.Type) {
let n = this.normals;
switch (n.length) {
case 0:
// No constraints: nothing to do.
break;
case 1:
// One constraint: slide the vector along the surface normal.
vec3.add(v, v, vec3.scale(tmp, -vec3.dot(n[0], v), n[0]));
break;
case 2:
// Two constraints: slide along the crease they form.
vec3.cross(tmp, n[0], n[1]);
vec3.normalize(tmp, tmp);
vec3.scale(v, vec3.dot(tmp, v), tmp);
break;
default:
// Three or more constraints: we're stuck.
vec3.setFromValues(v, 0, 0, 0);
break;
}
}
private updateConstraints_() {
let bestNormals = null;
let bestMaxDot = -Infinity;
// TODO(tom): remove temp allocation.
let projected = vec3.newZero();
for (let i = 0; i < this.normals.length; ++i) {
let n = this.normals[i];
let d = -vec3.dot(n, this.v);
vec3.add(projected, this.v, vec3.scale(projected, d, n));
let candidates = [n];
let maxDot = -1;
for (let j = 0; j < this.normals.length; ++j) {
if (i == j) {
continue;
}
let dot = vec3.dot(projected, this.normals[j]);
maxDot = Math.max(dot, maxDot);
if (dot <= 0) {
candidates.push(this.normals[j]);
}
}
if (bestNormals == null ||
candidates.length < bestNormals.length ||
candidates.length == bestNormals.length && maxDot > bestMaxDot) {
bestMaxDot = maxDot;
bestNormals = candidates;
}
}
this.normals = bestNormals || [];
}
}