-
Notifications
You must be signed in to change notification settings - Fork 27
/
Constraints.hx
160 lines (130 loc) · 3.74 KB
/
Constraints.hx
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package echo.util.verlet;
import echo.math.Vector2;
abstract class Constraint {
public var active:Bool = true;
public abstract function step(dt:Float):Void;
public abstract function position_count():Int;
public abstract function get_position(i:Int):Vector2;
public inline function iterator() {
return new ConstraintIterator(this);
}
public inline function get_positions():Array<Vector2> {
return [for (p in iterator()) p];
}
}
class ConstraintIterator {
var c:Constraint;
var i:Int;
public inline function new(c:Constraint) {
this.c = c;
i = 0;
}
public inline function hasNext() {
return i < c.position_count();
}
public inline function next() {
return c.get_position(i++);
}
}
class DistanceConstraint extends Constraint {
public var a:Dot;
public var b:Dot;
public var stiffness:Float;
public var distance:Float = 0;
public function new(a:Dot, b:Dot, stiffness:Float, ?distance:Float) {
if (a == b) {
trace("Can't constrain a particle to itself!");
return;
}
this.a = a;
this.b = b;
this.stiffness = stiffness;
if (distance != null) this.distance = distance;
else this.distance = a.get_position().distance(b.get_position());
}
public function step(dt:Float) {
var ap = a.get_position();
var bp = b.get_position();
var normal = ap - bp;
var m = normal.length_sq;
var n = normal * (((distance * distance - m) / m) * stiffness * dt);
a.set_position(ap + n);
b.set_position(bp - n);
}
public inline function position_count() return 2;
public inline function get_position(i:Int):Vector2 {
switch (i) {
case 0:
return a.get_position();
case 1:
return b.get_position();
}
throw 'Constraint has no position at index $i.';
}
}
class PinConstraint extends Constraint {
public var a:Dot;
public var x:Float;
public var y:Float;
public function new(a:Dot, ?x:Float, ?y:Float) {
this.x = a.x = x == null ? a.x : x;
this.y = a.y = y == null ? a.y : y;
this.a = a;
}
public function step(dt:Float) {
a.x = x;
a.y = y;
}
public inline function position_count():Int return 2;
public inline function get_position(i:Int):Vector2 {
switch (i) {
case 0:
return a.get_position();
case 1:
return new Vector2(x, y);
}
throw 'Constraint has no position at index $i.';
}
}
class RotationConstraint extends Constraint {
public var a:Dot;
public var b:Dot;
public var c:Dot;
public var radians:Float;
public var stiffness:Float;
public function new(a:Dot, b:Dot, c:Dot, stiffness:Float) {
this.a = a;
this.b = b;
this.c = c;
this.stiffness = stiffness;
radians = b.get_position().radians_between(a.get_position(), c.get_position());
}
public function step(dt:Float) {
var a_pos = a.get_position();
var b_pos = b.get_position();
var c_pos = c.get_position();
var angle_between = b_pos.radians_between(a_pos, c_pos);
var diff = angle_between - radians;
if (diff <= -Math.PI) diff += 2 * Math.PI;
else if (diff >= Math.PI) diff -= 2 * Math.PI;
diff *= dt * stiffness;
a.set_position((a_pos - b_pos).rotate(diff) + b_pos);
c.set_position((c_pos - b_pos).rotate(-diff) + b_pos);
a_pos.set(a.x, a.y);
c_pos.set(c.x, c.y);
b.set_position((b_pos - a_pos).rotate(diff) + a_pos);
b.set_position((b.get_position() - c_pos).rotate(-diff) + c_pos);
}
public inline function position_count() return 3;
public inline function get_position(i:Int):Vector2 {
switch (i) {
case 0:
return a.get_position();
case 1:
return b.get_position();
case 2:
return c.get_position();
}
throw 'Constraint has no position at index $i.';
}
}