reflexion 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.doc/ext/reflex/application.cpp +19 -1
- data/.doc/ext/reflex/chase_constraint.cpp +84 -0
- data/.doc/ext/reflex/clipboard.cpp +65 -0
- data/.doc/ext/reflex/constraint.cpp +135 -0
- data/.doc/ext/reflex/key_event.cpp +6 -6
- data/.doc/ext/reflex/link_constraint.cpp +178 -0
- data/.doc/ext/reflex/menu.cpp +302 -0
- data/.doc/ext/reflex/native.cpp +22 -4
- data/.doc/ext/reflex/pin.cpp +156 -0
- data/.doc/ext/reflex/pointer.cpp +2 -0
- data/.doc/ext/reflex/pointer_event.cpp +1 -1
- data/.doc/ext/reflex/reflex.cpp +42 -7
- data/.doc/ext/reflex/selector.cpp +4 -4
- data/.doc/ext/reflex/snap_constraint.cpp +125 -0
- data/.doc/ext/reflex/style_length.cpp +3 -2
- data/.doc/ext/reflex/view.cpp +96 -23
- data/.doc/ext/reflex/wheel_constraint.cpp +142 -0
- data/.doc/ext/reflex/window.cpp +59 -17
- data/.github/workflows/release-gem.yml +10 -1
- data/.github/workflows/test.yml +9 -0
- data/ChangeLog.md +30 -0
- data/README.md +1 -0
- data/VERSION +1 -1
- data/ext/reflex/application.cpp +21 -1
- data/ext/reflex/chase_constraint.cpp +89 -0
- data/ext/reflex/clipboard.cpp +69 -0
- data/ext/reflex/constraint.cpp +146 -0
- data/ext/reflex/key_event.cpp +6 -6
- data/ext/reflex/link_constraint.cpp +194 -0
- data/ext/reflex/menu.cpp +330 -0
- data/ext/reflex/native.cpp +22 -4
- data/ext/reflex/pin.cpp +165 -0
- data/ext/reflex/pointer.cpp +2 -0
- data/ext/reflex/pointer_event.cpp +1 -1
- data/ext/reflex/reflex.cpp +42 -7
- data/ext/reflex/selector.cpp +4 -4
- data/ext/reflex/selector.h +1 -1
- data/ext/reflex/snap_constraint.cpp +135 -0
- data/ext/reflex/style_length.cpp +3 -2
- data/ext/reflex/view.cpp +117 -37
- data/ext/reflex/wheel_constraint.cpp +154 -0
- data/ext/reflex/window.cpp +65 -18
- data/include/reflex/application.h +9 -0
- data/include/reflex/clipboard.h +53 -0
- data/include/reflex/constraint.h +275 -0
- data/include/reflex/defs.h +204 -195
- data/include/reflex/event.h +2 -0
- data/include/reflex/menu.h +120 -0
- data/include/reflex/pin.h +68 -0
- data/include/reflex/pointer.h +4 -0
- data/include/reflex/ruby/clipboard.h +23 -0
- data/include/reflex/ruby/constraint.h +94 -0
- data/include/reflex/ruby/menu.h +103 -0
- data/include/reflex/ruby/pin.h +40 -0
- data/include/reflex/ruby/view.h +36 -0
- data/include/reflex/ruby/window.h +36 -18
- data/include/reflex/view.h +39 -12
- data/include/reflex/window.h +17 -4
- data/include/reflex.h +5 -0
- data/lib/reflex/chase_constraint.rb +15 -0
- data/lib/reflex/clipboard.rb +19 -0
- data/lib/reflex/constraint.rb +29 -0
- data/lib/reflex/extension.rb +1 -1
- data/lib/reflex/helper.rb +28 -0
- data/lib/reflex/key_event.rb +7 -12
- data/lib/reflex/link_constraint.rb +31 -0
- data/lib/reflex/menu.rb +76 -0
- data/lib/reflex/pin.rb +39 -0
- data/lib/reflex/pointer.rb +18 -0
- data/lib/reflex/pointer_event.rb +1 -1
- data/lib/reflex/snap_constraint.rb +28 -0
- data/lib/reflex/view.rb +56 -4
- data/lib/reflex/wheel_constraint.rb +28 -0
- data/lib/reflex/wheel_event.rb +9 -0
- data/lib/reflex.rb +15 -6
- data/pod.rake +4 -2
- data/reflex.gemspec +3 -3
- data/samples/constraint.rb +152 -0
- data/samples/menu.rb +29 -0
- data/src/application.cpp +21 -2
- data/src/application.h +5 -0
- data/src/clipboard.cpp +81 -0
- data/src/constraint.cpp +1662 -0
- data/src/constraint.h +64 -0
- data/src/event.cpp +7 -1
- data/src/event.h +1 -2
- data/src/ios/application.mm +5 -0
- data/src/ios/clipboard.mm +44 -0
- data/src/ios/event.h +6 -2
- data/src/ios/event.mm +31 -12
- data/src/ios/menu.mm +36 -0
- data/src/ios/view_controller.mm +45 -12
- data/src/ios/window.mm +6 -0
- data/src/menu.cpp +325 -0
- data/src/menu.h +65 -0
- data/src/osx/app_delegate.mm +74 -125
- data/src/osx/application.mm +16 -0
- data/src/osx/clipboard.mm +45 -0
- data/src/osx/event.mm +40 -10
- data/src/osx/menu.h +25 -0
- data/src/osx/menu.mm +372 -0
- data/src/osx/native_window.mm +27 -0
- data/src/osx/opengl_view.mm +51 -0
- data/src/osx/window.mm +11 -0
- data/src/pin.cpp +137 -0
- data/src/pointer.cpp +27 -16
- data/src/pointer.h +6 -0
- data/src/sdl/application.cpp +5 -0
- data/src/sdl/clipboard.cpp +39 -0
- data/src/sdl/event.cpp +20 -3
- data/src/sdl/event.h +6 -3
- data/src/sdl/menu.cpp +35 -0
- data/src/sdl/window.cpp +26 -8
- data/src/view.cpp +313 -52
- data/src/view.h +18 -2
- data/src/win32/application.cpp +5 -0
- data/src/win32/clipboard.cpp +210 -0
- data/src/win32/event.cpp +11 -1
- data/src/win32/event.h +2 -0
- data/src/win32/menu.cpp +352 -0
- data/src/win32/menu.h +27 -0
- data/src/win32/window.cpp +62 -0
- data/src/win32/window.h +3 -0
- data/src/window.cpp +235 -22
- data/src/window.h +13 -2
- data/src/world.cpp +77 -14
- data/src/world.h +9 -2
- data/test/helper.rb +7 -0
- data/test/test_application.rb +11 -0
- data/test/test_chase_constraint.rb +124 -0
- data/test/test_clipboard.rb +54 -0
- data/test/test_constraint.rb +186 -0
- data/test/test_key_event.rb +6 -0
- data/test/test_link_constraint.rb +239 -0
- data/test/test_menu.rb +207 -0
- data/test/test_pin.rb +65 -0
- data/test/test_pointer.rb +23 -9
- data/test/test_pointer_event.rb +1 -1
- data/test/test_snap_constraint.rb +142 -0
- data/test/test_view.rb +113 -23
- data/test/test_wheel_constraint.rb +127 -0
- data/test/test_wheel_event.rb +15 -9
- data/test/test_window.rb +10 -0
- metadata +82 -8
data/src/constraint.cpp
ADDED
|
@@ -0,0 +1,1662 @@
|
|
|
1
|
+
#include "constraint.h"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
#include <box2d/box2d.h>
|
|
5
|
+
#include "reflex/pin.h"
|
|
6
|
+
#include "reflex/exception.h"
|
|
7
|
+
#include "view.h"
|
|
8
|
+
#include "selector.h"
|
|
9
|
+
#include "body.h"
|
|
10
|
+
#include "world.h"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
namespace Reflex
|
|
14
|
+
{
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
static constexpr float DEFAULT_DAMPING = 0.7f;
|
|
18
|
+
|
|
19
|
+
static constexpr float DEFAULT_CHASE_SPRING = 5;
|
|
20
|
+
|
|
21
|
+
static constexpr float FORCE_PER_MASS = 1000;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
static float
|
|
25
|
+
default_max_force (b2BodyId body)
|
|
26
|
+
{
|
|
27
|
+
float mass = b2Body_GetMass(body);
|
|
28
|
+
return FORCE_PER_MASS * (mass > 0 ? mass : 1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static Point
|
|
32
|
+
view_center (const View* view)
|
|
33
|
+
{
|
|
34
|
+
return view ? view->frame().size() / 2 : Point(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static float
|
|
38
|
+
relative_angle (b2BodyId body0, b2BodyId body1)
|
|
39
|
+
{
|
|
40
|
+
return
|
|
41
|
+
b2Rot_GetAngle(b2Body_GetRotation(body0)) -
|
|
42
|
+
b2Rot_GetAngle(b2Body_GetRotation(body1));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
struct Constraint::Data
|
|
47
|
+
{
|
|
48
|
+
|
|
49
|
+
Pin pins[2];
|
|
50
|
+
|
|
51
|
+
b2JointId b2joint = b2_nullJointId;
|
|
52
|
+
|
|
53
|
+
World* world = NULL;
|
|
54
|
+
|
|
55
|
+
float spring = 0;
|
|
56
|
+
|
|
57
|
+
float damping = DEFAULT_DAMPING;
|
|
58
|
+
|
|
59
|
+
bool collide = false;
|
|
60
|
+
|
|
61
|
+
bool removed = false;
|
|
62
|
+
|
|
63
|
+
bool resolved = false;
|
|
64
|
+
|
|
65
|
+
float resolved_ppm = 0;
|
|
66
|
+
|
|
67
|
+
b2Vec2 anchor0 = {0, 0};
|
|
68
|
+
|
|
69
|
+
b2Vec2 anchor1 = {0, 0};
|
|
70
|
+
|
|
71
|
+
float ref_angle = 0;
|
|
72
|
+
|
|
73
|
+
SelectorPtr pselector;
|
|
74
|
+
|
|
75
|
+
virtual ~Data ()
|
|
76
|
+
{
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
virtual b2JointId create_joint (
|
|
80
|
+
b2WorldId world, b2BodyId body0, b2BodyId body1, float ppm) = 0;
|
|
81
|
+
|
|
82
|
+
virtual void apply_params (float ppm) = 0;
|
|
83
|
+
|
|
84
|
+
virtual void on_world_update (float ppm)
|
|
85
|
+
{
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
bool is_valid () const
|
|
89
|
+
{
|
|
90
|
+
return b2Joint_IsValid(b2joint);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
float ppm () const
|
|
94
|
+
{
|
|
95
|
+
assert(world);
|
|
96
|
+
return world->meter2pixel();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void resolve_anchors (b2BodyId body0, b2BodyId body1, float ppm)
|
|
100
|
+
{
|
|
101
|
+
if (resolved)
|
|
102
|
+
return rescale_resolved_anchors(ppm);
|
|
103
|
+
|
|
104
|
+
const Point* pos0 = pins[0].position();
|
|
105
|
+
const Point* pos1 = pins[1].position();
|
|
106
|
+
|
|
107
|
+
if (pos0 && pos1)
|
|
108
|
+
{
|
|
109
|
+
anchor0 = to_b2vec2(*pos0, ppm);
|
|
110
|
+
anchor1 = to_b2vec2(*pos1, ppm);
|
|
111
|
+
}
|
|
112
|
+
else if (pos1)
|
|
113
|
+
{
|
|
114
|
+
anchor1 = to_b2vec2(*pos1, ppm);
|
|
115
|
+
anchor0 = b2Body_GetLocalPoint(body0, b2Body_GetWorldPoint(body1, anchor1));
|
|
116
|
+
}
|
|
117
|
+
else
|
|
118
|
+
{
|
|
119
|
+
anchor0 = to_b2vec2(pos0 ? *pos0 : view_center(pins[0].view()), ppm);
|
|
120
|
+
anchor1 = b2Body_GetLocalPoint(body1, b2Body_GetWorldPoint(body0, anchor0));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
ref_angle = relative_angle(body0, body1);
|
|
124
|
+
resolved = true;
|
|
125
|
+
resolved_ppm = ppm;
|
|
126
|
+
write_back_resolved_pins();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void rescale_resolved_anchors (float ppm)
|
|
130
|
+
{
|
|
131
|
+
// the anchors cache the pinned positions in meters, so moving to
|
|
132
|
+
// a world with another pixels-per-meter only needs unit rescaling,
|
|
133
|
+
// never a re-derivation
|
|
134
|
+
|
|
135
|
+
if (resolved_ppm == ppm) return;
|
|
136
|
+
|
|
137
|
+
anchor0 = to_b2vec2(*pins[0].position(), ppm);
|
|
138
|
+
anchor1 = to_b2vec2(*pins[1].position(), ppm);
|
|
139
|
+
resolved_ppm = ppm;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
void write_back_resolved_pins ()
|
|
143
|
+
{
|
|
144
|
+
if (!pins[0].position())
|
|
145
|
+
pins[0] = Pin(pins[0].view(), to_point(anchor0, resolved_ppm));
|
|
146
|
+
if (!pins[1].position())
|
|
147
|
+
pins[1] = Pin(pins[1].view(), to_point(anchor1, resolved_ppm));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
void update_params ()
|
|
151
|
+
{
|
|
152
|
+
if (!is_valid() || !world) return;
|
|
153
|
+
|
|
154
|
+
b2Joint_SetCollideConnected(b2joint, collide);
|
|
155
|
+
apply_params(ppm());
|
|
156
|
+
b2Joint_WakeBodies(b2joint);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
};// Constraint::Data
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
static void
|
|
163
|
+
reactivate_constraint (Constraint* constraint)
|
|
164
|
+
{
|
|
165
|
+
assert(constraint);
|
|
166
|
+
|
|
167
|
+
if (!constraint->self->is_valid()) return;
|
|
168
|
+
|
|
169
|
+
Constraint_deactivate(constraint);
|
|
170
|
+
Constraint_activate(constraint);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
void
|
|
174
|
+
Constraint_set_pins (
|
|
175
|
+
Constraint* constraint,
|
|
176
|
+
View* view0, const Point* position0,
|
|
177
|
+
View* view1, const Point* position1)
|
|
178
|
+
{
|
|
179
|
+
if (!constraint || !view0)
|
|
180
|
+
argument_error(__FILE__, __LINE__);
|
|
181
|
+
|
|
182
|
+
if (view0 == view1)
|
|
183
|
+
argument_error(__FILE__, __LINE__, "can not constrain a view to itself");
|
|
184
|
+
|
|
185
|
+
Constraint::Data* self = constraint->self.get();
|
|
186
|
+
|
|
187
|
+
if (self->pins[0].view())
|
|
188
|
+
invalid_state_error(__FILE__, __LINE__, "constraint already has pins");
|
|
189
|
+
|
|
190
|
+
self->pins[0] = position0 ? Pin(view0, *position0) : Pin(view0);
|
|
191
|
+
self->pins[1] = position1 ? Pin(view1, *position1) : Pin(view1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static bool
|
|
195
|
+
get_view_body_and_world (b2BodyId* pid, World** ppworld, View* view)
|
|
196
|
+
{
|
|
197
|
+
if (!view)
|
|
198
|
+
return false;
|
|
199
|
+
|
|
200
|
+
Body* body = View_get_body(view);
|
|
201
|
+
if (!body || Body_is_temporary(*body))
|
|
202
|
+
return false;
|
|
203
|
+
|
|
204
|
+
World* world = Body_get_world(body);
|
|
205
|
+
if (!world)
|
|
206
|
+
return false;
|
|
207
|
+
|
|
208
|
+
*ppworld = world;
|
|
209
|
+
*pid = Body_get_id(body);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
bool
|
|
214
|
+
Constraint_activate (Constraint* constraint)
|
|
215
|
+
{
|
|
216
|
+
if (!constraint)
|
|
217
|
+
argument_error(__FILE__, __LINE__);
|
|
218
|
+
|
|
219
|
+
Constraint::Data* self = constraint->self.get();
|
|
220
|
+
|
|
221
|
+
if (self->removed || self->is_valid())
|
|
222
|
+
return false;
|
|
223
|
+
|
|
224
|
+
b2BodyId id0 = b2_nullBodyId;
|
|
225
|
+
b2BodyId id1 = b2_nullBodyId;
|
|
226
|
+
|
|
227
|
+
World* world0 = NULL;
|
|
228
|
+
if (!get_view_body_and_world(&id0, &world0, self->pins[0].view()))
|
|
229
|
+
return false;
|
|
230
|
+
|
|
231
|
+
View* view1 = self->pins[1].view();
|
|
232
|
+
if (view1)
|
|
233
|
+
{
|
|
234
|
+
World* world1 = NULL;
|
|
235
|
+
if (!get_view_body_and_world(&id1, &world1, view1))
|
|
236
|
+
return false;
|
|
237
|
+
|
|
238
|
+
if (world1 != world0)
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
else
|
|
242
|
+
id1 = Body_get_id(World_get_ground(world0));
|
|
243
|
+
|
|
244
|
+
if (World_is_stepping(world0))
|
|
245
|
+
physics_error(__FILE__, __LINE__, "world is stepping now");
|
|
246
|
+
|
|
247
|
+
b2JointId joint =
|
|
248
|
+
self->create_joint(World_get_id(world0), id0, id1, world0->meter2pixel());
|
|
249
|
+
if (!b2Joint_IsValid(joint))
|
|
250
|
+
physics_error(__FILE__, __LINE__);
|
|
251
|
+
|
|
252
|
+
self->b2joint = joint;
|
|
253
|
+
self->world = world0;
|
|
254
|
+
World_add_constraint(world0, constraint);
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
void
|
|
259
|
+
Constraint_deactivate (Constraint* constraint)
|
|
260
|
+
{
|
|
261
|
+
if (!constraint)
|
|
262
|
+
argument_error(__FILE__, __LINE__);
|
|
263
|
+
|
|
264
|
+
Constraint::Data* self = constraint->self.get();
|
|
265
|
+
|
|
266
|
+
if (self->world)
|
|
267
|
+
{
|
|
268
|
+
if (self->is_valid())
|
|
269
|
+
{
|
|
270
|
+
if (World_is_stepping(self->world))
|
|
271
|
+
physics_error(__FILE__, __LINE__, "world is stepping now");
|
|
272
|
+
|
|
273
|
+
b2DestroyJoint(self->b2joint);
|
|
274
|
+
}
|
|
275
|
+
World_remove_constraint(self->world, constraint);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
self->b2joint = b2_nullJointId;
|
|
279
|
+
self->world = NULL;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
void
|
|
283
|
+
Constraint_sever (Constraint* constraint)
|
|
284
|
+
{
|
|
285
|
+
if (!constraint)
|
|
286
|
+
argument_error(__FILE__, __LINE__);
|
|
287
|
+
|
|
288
|
+
Constraint::Data* self = constraint->self.get();
|
|
289
|
+
|
|
290
|
+
if (self->removed) return;
|
|
291
|
+
|
|
292
|
+
Constraint::Ref guard(constraint);
|
|
293
|
+
|
|
294
|
+
Constraint_deactivate(constraint);
|
|
295
|
+
self->removed = true;
|
|
296
|
+
|
|
297
|
+
View* view0 = self->pins[0].view();
|
|
298
|
+
View* view1 = self->pins[1].view();
|
|
299
|
+
if (view0) View_remove_constraint(view0, constraint);
|
|
300
|
+
if (view1) View_remove_constraint(view1, constraint);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
void
|
|
304
|
+
Constraint_on_world_destroyed (Constraint* constraint)
|
|
305
|
+
{
|
|
306
|
+
if (!constraint)
|
|
307
|
+
argument_error(__FILE__, __LINE__);
|
|
308
|
+
|
|
309
|
+
Constraint::Data* self = constraint->self.get();
|
|
310
|
+
|
|
311
|
+
self->b2joint = b2_nullJointId;
|
|
312
|
+
self->world = NULL;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
void
|
|
316
|
+
Constraint_on_world_update (Constraint* constraint)
|
|
317
|
+
{
|
|
318
|
+
if (!constraint)
|
|
319
|
+
argument_error(__FILE__, __LINE__);
|
|
320
|
+
|
|
321
|
+
Constraint::Data* self = constraint->self.get();
|
|
322
|
+
if (!self->world) return;
|
|
323
|
+
|
|
324
|
+
self->on_world_update(self->ppm());
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
bool
|
|
328
|
+
Constraint_has_world_mismatch (const Constraint* constraint)
|
|
329
|
+
{
|
|
330
|
+
if (!constraint)
|
|
331
|
+
argument_error(__FILE__, __LINE__);
|
|
332
|
+
|
|
333
|
+
const Constraint::Data* self = constraint->self.get();
|
|
334
|
+
|
|
335
|
+
const View* view0 = self->pins[0].view();
|
|
336
|
+
const View* view1 = self->pins[1].view();
|
|
337
|
+
if (!view0 || !view1) return false;
|
|
338
|
+
|
|
339
|
+
const Body* body0 = View_get_body(view0);
|
|
340
|
+
const Body* body1 = View_get_body(view1);
|
|
341
|
+
if (!body0 || Body_is_temporary(*body0)) return false;
|
|
342
|
+
if (!body1 || Body_is_temporary(*body1)) return false;
|
|
343
|
+
|
|
344
|
+
return Body_get_world(body0) != Body_get_world(body1);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
Constraint::Constraint (Data* data)
|
|
349
|
+
: self(data)
|
|
350
|
+
{
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
Constraint::~Constraint ()
|
|
354
|
+
{
|
|
355
|
+
Constraint_deactivate(this);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
void
|
|
359
|
+
Constraint::remove ()
|
|
360
|
+
{
|
|
361
|
+
Constraint_sever(this);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const Pin&
|
|
365
|
+
Constraint::pin (size_t index) const
|
|
366
|
+
{
|
|
367
|
+
if (index > 1)
|
|
368
|
+
argument_error(__FILE__, __LINE__);
|
|
369
|
+
|
|
370
|
+
return self->pins[index];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
View*
|
|
374
|
+
Constraint::view (size_t index)
|
|
375
|
+
{
|
|
376
|
+
if (index > 1)
|
|
377
|
+
argument_error(__FILE__, __LINE__);
|
|
378
|
+
|
|
379
|
+
return self->pins[index].view();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const View*
|
|
383
|
+
Constraint::view (size_t index) const
|
|
384
|
+
{
|
|
385
|
+
return const_cast<Constraint*>(this)->view(index);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
void
|
|
389
|
+
Constraint::set_spring (float hertz)
|
|
390
|
+
{
|
|
391
|
+
if (hertz < 0)
|
|
392
|
+
argument_error(__FILE__, __LINE__);
|
|
393
|
+
|
|
394
|
+
self->spring = hertz;
|
|
395
|
+
self->update_params();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
float
|
|
399
|
+
Constraint::spring () const
|
|
400
|
+
{
|
|
401
|
+
return self->spring;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
void
|
|
405
|
+
Constraint::set_damping (float ratio)
|
|
406
|
+
{
|
|
407
|
+
if (ratio < 0)
|
|
408
|
+
argument_error(__FILE__, __LINE__);
|
|
409
|
+
|
|
410
|
+
self->damping = ratio;
|
|
411
|
+
self->update_params();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
float
|
|
415
|
+
Constraint::damping () const
|
|
416
|
+
{
|
|
417
|
+
return self->damping;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
void
|
|
421
|
+
Constraint::set_collide (bool state)
|
|
422
|
+
{
|
|
423
|
+
self->collide = state;
|
|
424
|
+
self->update_params();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
bool
|
|
428
|
+
Constraint::can_collide () const
|
|
429
|
+
{
|
|
430
|
+
return self->collide;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
bool
|
|
434
|
+
Constraint::is_removed () const
|
|
435
|
+
{
|
|
436
|
+
return self->removed;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
Constraint::operator bool () const
|
|
440
|
+
{
|
|
441
|
+
return self->is_valid();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
bool
|
|
445
|
+
Constraint::operator ! () const
|
|
446
|
+
{
|
|
447
|
+
return !operator bool();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
SelectorPtr*
|
|
451
|
+
Constraint::get_selector_ptr ()
|
|
452
|
+
{
|
|
453
|
+
return &self->pselector;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
struct SnapConstraintData : public Constraint::Data
|
|
458
|
+
{
|
|
459
|
+
|
|
460
|
+
bool has_angle = false;
|
|
461
|
+
|
|
462
|
+
float angle_min = 0;
|
|
463
|
+
|
|
464
|
+
float angle_max = 0;
|
|
465
|
+
|
|
466
|
+
bool has_motor = false;
|
|
467
|
+
|
|
468
|
+
float motor_speed = 0;
|
|
469
|
+
|
|
470
|
+
bool has_force = false;
|
|
471
|
+
|
|
472
|
+
float force = 0;
|
|
473
|
+
|
|
474
|
+
bool use_weld () const
|
|
475
|
+
{
|
|
476
|
+
return has_angle && angle_min == angle_max;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
b2JointId create_joint (
|
|
480
|
+
b2WorldId world, b2BodyId body0, b2BodyId body1, float ppm) override
|
|
481
|
+
{
|
|
482
|
+
resolve_anchors(body0, body1, ppm);
|
|
483
|
+
|
|
484
|
+
// the declaring side goes to bodyB because the mouse joint
|
|
485
|
+
// assumes bodyA is static
|
|
486
|
+
if (use_weld())
|
|
487
|
+
{
|
|
488
|
+
b2WeldJointDef def = b2DefaultWeldJointDef();
|
|
489
|
+
def.bodyIdA = body1;
|
|
490
|
+
def.bodyIdB = body0;
|
|
491
|
+
def.localAnchorA = anchor1;
|
|
492
|
+
def.localAnchorB = anchor0;
|
|
493
|
+
def.referenceAngle = ref_angle + Xot::deg2rad(angle_min);
|
|
494
|
+
def.linearHertz = spring;
|
|
495
|
+
def.angularHertz = spring;
|
|
496
|
+
def.linearDampingRatio = damping;
|
|
497
|
+
def.angularDampingRatio = damping;
|
|
498
|
+
def.collideConnected = collide;
|
|
499
|
+
return b2CreateWeldJoint(world, &def);
|
|
500
|
+
}
|
|
501
|
+
else
|
|
502
|
+
{
|
|
503
|
+
b2RevoluteJointDef def = b2DefaultRevoluteJointDef();
|
|
504
|
+
def.bodyIdA = body1;
|
|
505
|
+
def.bodyIdB = body0;
|
|
506
|
+
def.localAnchorA = anchor1;
|
|
507
|
+
def.localAnchorB = anchor0;
|
|
508
|
+
def.referenceAngle = ref_angle;
|
|
509
|
+
def.enableSpring = spring > 0;
|
|
510
|
+
def.hertz = spring;
|
|
511
|
+
def.dampingRatio = damping;
|
|
512
|
+
def.enableLimit = has_angle;
|
|
513
|
+
def.lowerAngle = Xot::deg2rad(angle_min);
|
|
514
|
+
def.upperAngle = Xot::deg2rad(angle_max);
|
|
515
|
+
def.enableMotor = has_motor;
|
|
516
|
+
def.motorSpeed = Xot::deg2rad(motor_speed);
|
|
517
|
+
def.maxMotorTorque = has_force ? force : default_max_force(body0);
|
|
518
|
+
def.collideConnected = collide;
|
|
519
|
+
return b2CreateRevoluteJoint(world, &def);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
void apply_params (float ppm) override
|
|
524
|
+
{
|
|
525
|
+
if (b2Joint_GetType(b2joint) == b2_weldJoint)
|
|
526
|
+
{
|
|
527
|
+
float min = Xot::deg2rad(angle_min);
|
|
528
|
+
b2Joint_SetReferenceAngle( b2joint, ref_angle + min);
|
|
529
|
+
b2WeldJoint_SetLinearHertz( b2joint, spring);
|
|
530
|
+
b2WeldJoint_SetAngularHertz( b2joint, spring);
|
|
531
|
+
b2WeldJoint_SetLinearDampingRatio( b2joint, damping);
|
|
532
|
+
b2WeldJoint_SetAngularDampingRatio(b2joint, damping);
|
|
533
|
+
}
|
|
534
|
+
else
|
|
535
|
+
{
|
|
536
|
+
b2RevoluteJoint_EnableSpring( b2joint, spring > 0);
|
|
537
|
+
b2RevoluteJoint_SetSpringHertz( b2joint, spring);
|
|
538
|
+
b2RevoluteJoint_SetSpringDampingRatio(b2joint, damping);
|
|
539
|
+
b2RevoluteJoint_EnableLimit( b2joint, has_angle);
|
|
540
|
+
if (has_angle)
|
|
541
|
+
{
|
|
542
|
+
float min = Xot::deg2rad(angle_min), max = Xot::deg2rad(angle_max);
|
|
543
|
+
b2RevoluteJoint_SetLimits( b2joint, min, max);
|
|
544
|
+
}
|
|
545
|
+
b2RevoluteJoint_EnableMotor( b2joint, has_motor);
|
|
546
|
+
b2RevoluteJoint_SetMotorSpeed( b2joint, Xot::deg2rad(motor_speed));
|
|
547
|
+
if (has_force)
|
|
548
|
+
b2RevoluteJoint_SetMaxMotorTorque( b2joint, force);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
};// SnapConstraintData
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
static SnapConstraintData&
|
|
556
|
+
get_data (SnapConstraint& constraint)
|
|
557
|
+
{
|
|
558
|
+
return (SnapConstraintData&) *constraint.self;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
static const SnapConstraintData&
|
|
562
|
+
get_data (const SnapConstraint& constraint)
|
|
563
|
+
{
|
|
564
|
+
return get_data(const_cast<SnapConstraint&>(constraint));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
static SnapConstraint_CreateFun snap_constraint_create_fun = NULL;
|
|
569
|
+
|
|
570
|
+
void
|
|
571
|
+
SnapConstraint_set_create_fun (SnapConstraint_CreateFun fun)
|
|
572
|
+
{
|
|
573
|
+
snap_constraint_create_fun = fun;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
SnapConstraint*
|
|
577
|
+
SnapConstraint_create ()
|
|
578
|
+
{
|
|
579
|
+
return snap_constraint_create_fun
|
|
580
|
+
? snap_constraint_create_fun()
|
|
581
|
+
: new SnapConstraint();
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
SnapConstraint::SnapConstraint ()
|
|
586
|
+
: Super(new SnapConstraintData)
|
|
587
|
+
{
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
SnapConstraint::~SnapConstraint ()
|
|
591
|
+
{
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
static void
|
|
595
|
+
update_angle (SnapConstraint* c, bool has_angle, float min, float max)
|
|
596
|
+
{
|
|
597
|
+
if (min > max)
|
|
598
|
+
argument_error(__FILE__, __LINE__);
|
|
599
|
+
|
|
600
|
+
SnapConstraintData& self = get_data(*c);
|
|
601
|
+
|
|
602
|
+
bool weld = self.use_weld();
|
|
603
|
+
self.has_angle = has_angle;
|
|
604
|
+
self.angle_min = min;
|
|
605
|
+
self.angle_max = max;
|
|
606
|
+
|
|
607
|
+
if (self.is_valid() && weld != self.use_weld())
|
|
608
|
+
reactivate_constraint(c);
|
|
609
|
+
else
|
|
610
|
+
self.update_params();
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
void
|
|
614
|
+
SnapConstraint::set_angle (float min_degree, float max_degree)
|
|
615
|
+
{
|
|
616
|
+
update_angle(this, true, min_degree, max_degree);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
void
|
|
620
|
+
SnapConstraint::clear_angle ()
|
|
621
|
+
{
|
|
622
|
+
update_angle(this, false, 0, 0);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
float
|
|
626
|
+
SnapConstraint::angle_min () const
|
|
627
|
+
{
|
|
628
|
+
return get_data(*this).angle_min;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
float
|
|
632
|
+
SnapConstraint::angle_max () const
|
|
633
|
+
{
|
|
634
|
+
return get_data(*this).angle_max;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
bool
|
|
638
|
+
SnapConstraint::has_angle () const
|
|
639
|
+
{
|
|
640
|
+
return get_data(*this).has_angle;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
static void
|
|
644
|
+
update_motor (SnapConstraint* c, bool has_motor, float speed)
|
|
645
|
+
{
|
|
646
|
+
SnapConstraintData& self = get_data(*c);
|
|
647
|
+
|
|
648
|
+
self.has_motor = has_motor;
|
|
649
|
+
self.motor_speed = speed;
|
|
650
|
+
self.update_params();
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
void
|
|
654
|
+
SnapConstraint::set_motor (float degrees_per_second)
|
|
655
|
+
{
|
|
656
|
+
update_motor(this, true, degrees_per_second);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
void
|
|
660
|
+
SnapConstraint::clear_motor ()
|
|
661
|
+
{
|
|
662
|
+
update_motor(this, false, 0);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
float
|
|
666
|
+
SnapConstraint::motor () const
|
|
667
|
+
{
|
|
668
|
+
return get_data(*this).motor_speed;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
bool
|
|
672
|
+
SnapConstraint::has_motor () const
|
|
673
|
+
{
|
|
674
|
+
return get_data(*this).has_motor;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
void
|
|
678
|
+
SnapConstraint::set_force (float max_torque)
|
|
679
|
+
{
|
|
680
|
+
if (max_torque < 0)
|
|
681
|
+
argument_error(__FILE__, __LINE__);
|
|
682
|
+
|
|
683
|
+
SnapConstraintData& self = get_data(*this);
|
|
684
|
+
|
|
685
|
+
self.has_force = true;
|
|
686
|
+
self.force = max_torque;
|
|
687
|
+
self.update_params();
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
void
|
|
691
|
+
SnapConstraint::clear_force ()
|
|
692
|
+
{
|
|
693
|
+
SnapConstraintData& self = get_data(*this);
|
|
694
|
+
|
|
695
|
+
self.has_force = false;
|
|
696
|
+
self.force = 0;
|
|
697
|
+
|
|
698
|
+
if (
|
|
699
|
+
self.is_valid() && self.world &&
|
|
700
|
+
b2Joint_GetType(self.b2joint) == b2_revoluteJoint)
|
|
701
|
+
{
|
|
702
|
+
View* view = self.pins[0].view();
|
|
703
|
+
Body* body = view ? View_get_body(view, false) : NULL;
|
|
704
|
+
if (body)
|
|
705
|
+
{
|
|
706
|
+
b2RevoluteJoint_SetMaxMotorTorque(
|
|
707
|
+
self.b2joint, default_max_force(Body_get_id(body)));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
float
|
|
713
|
+
SnapConstraint::force () const
|
|
714
|
+
{
|
|
715
|
+
const SnapConstraintData& self = get_data(*this);
|
|
716
|
+
|
|
717
|
+
if (self.has_force)
|
|
718
|
+
return self.force;
|
|
719
|
+
|
|
720
|
+
if (
|
|
721
|
+
self.is_valid() && self.world &&
|
|
722
|
+
b2Joint_GetType(self.b2joint) == b2_revoluteJoint)
|
|
723
|
+
{
|
|
724
|
+
return b2RevoluteJoint_GetMaxMotorTorque(self.b2joint);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return 0;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
bool
|
|
731
|
+
SnapConstraint::has_force () const
|
|
732
|
+
{
|
|
733
|
+
return get_data(*this).has_force;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
struct LinkConstraintData : public Constraint::Data
|
|
738
|
+
{
|
|
739
|
+
|
|
740
|
+
bool has_axis = false;
|
|
741
|
+
|
|
742
|
+
Point axis = Point(1, 0);
|
|
743
|
+
|
|
744
|
+
bool has_distance = false;
|
|
745
|
+
|
|
746
|
+
coord distance = 0;
|
|
747
|
+
|
|
748
|
+
float b2distance = 0;
|
|
749
|
+
|
|
750
|
+
bool has_range = false;
|
|
751
|
+
|
|
752
|
+
coord range_min = 0;
|
|
753
|
+
|
|
754
|
+
coord range_max = 0;
|
|
755
|
+
|
|
756
|
+
bool has_motor = false;
|
|
757
|
+
|
|
758
|
+
coord motor_speed = 0;
|
|
759
|
+
|
|
760
|
+
bool has_force = false;
|
|
761
|
+
|
|
762
|
+
float force = 0;
|
|
763
|
+
|
|
764
|
+
b2JointId create_joint (
|
|
765
|
+
b2WorldId world, b2BodyId body0, b2BodyId body1, float ppm) override
|
|
766
|
+
{
|
|
767
|
+
// the declaring side goes to bodyB because the mouse joint
|
|
768
|
+
// assumes bodyA is static
|
|
769
|
+
if (has_axis)
|
|
770
|
+
{
|
|
771
|
+
// an axis turns the link into a rail: the anchors coincide and
|
|
772
|
+
// the separation is measured along the axis, not radially
|
|
773
|
+
resolve_anchors(body0, body1, ppm);
|
|
774
|
+
|
|
775
|
+
b2PrismaticJointDef def = b2DefaultPrismaticJointDef();
|
|
776
|
+
def.bodyIdA = body1;
|
|
777
|
+
def.bodyIdB = body0;
|
|
778
|
+
def.localAnchorA = anchor1;
|
|
779
|
+
def.localAnchorB = anchor0;
|
|
780
|
+
def.localAxisA = b2axis();
|
|
781
|
+
def.referenceAngle = ref_angle;
|
|
782
|
+
def.targetTranslation = has_distance ? to_b2coord(distance, ppm) : 0;
|
|
783
|
+
def.enableSpring = spring > 0;
|
|
784
|
+
def.hertz = spring;
|
|
785
|
+
def.dampingRatio = damping;
|
|
786
|
+
def.enableLimit = has_range;
|
|
787
|
+
def.lowerTranslation = to_b2coord(range_min, ppm);
|
|
788
|
+
def.upperTranslation = to_b2coord(range_max, ppm);
|
|
789
|
+
def.enableMotor = has_motor;
|
|
790
|
+
def.motorSpeed = to_b2coord(motor_speed, ppm);
|
|
791
|
+
def.maxMotorForce = has_force ? force : default_max_force(body0);
|
|
792
|
+
def.collideConnected = collide;
|
|
793
|
+
return b2CreatePrismaticJoint(world, &def);
|
|
794
|
+
}
|
|
795
|
+
else
|
|
796
|
+
{
|
|
797
|
+
resolve_link_anchors(body0, body1, ppm);
|
|
798
|
+
|
|
799
|
+
b2DistanceJointDef def = b2DefaultDistanceJointDef();
|
|
800
|
+
def.bodyIdA = body1;
|
|
801
|
+
def.bodyIdB = body0;
|
|
802
|
+
def.localAnchorA = anchor1;
|
|
803
|
+
def.localAnchorB = anchor0;
|
|
804
|
+
def.length = b2distance;
|
|
805
|
+
def.enableSpring = spring > 0;
|
|
806
|
+
def.hertz = spring;
|
|
807
|
+
def.dampingRatio = damping;
|
|
808
|
+
def.enableLimit = has_range;
|
|
809
|
+
def.minLength = to_b2coord(range_min, ppm);
|
|
810
|
+
def.maxLength = to_b2coord(range_max, ppm);
|
|
811
|
+
def.enableMotor = has_motor;
|
|
812
|
+
def.motorSpeed = to_b2coord(motor_speed, ppm);
|
|
813
|
+
def.maxMotorForce = has_force ? force : default_max_force(body0);
|
|
814
|
+
def.collideConnected = collide;
|
|
815
|
+
return b2CreateDistanceJoint(world, &def);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
void resolve_link_anchors (b2BodyId body0, b2BodyId body1, float ppm)
|
|
820
|
+
{
|
|
821
|
+
// unlike coincidence type constraints, unspecified link anchors
|
|
822
|
+
// default to the view centers because the relation to be kept is
|
|
823
|
+
// the distance, not the anchor placement
|
|
824
|
+
|
|
825
|
+
if (resolved)
|
|
826
|
+
{
|
|
827
|
+
if (has_distance)
|
|
828
|
+
b2distance = to_b2coord(distance, ppm);
|
|
829
|
+
else if (resolved_ppm != ppm)
|
|
830
|
+
b2distance = to_b2coord(to_coord(b2distance, resolved_ppm), ppm);
|
|
831
|
+
|
|
832
|
+
return rescale_resolved_anchors(ppm);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const Point* pos0 = pins[0].position();
|
|
836
|
+
const Point* pos1 = pins[1].position();
|
|
837
|
+
|
|
838
|
+
anchor0 = to_b2vec2(pos0 ? *pos0 : view_center(pins[0].view()), ppm);
|
|
839
|
+
if (pos1)
|
|
840
|
+
anchor1 = to_b2vec2(*pos1, ppm);
|
|
841
|
+
else if (pins[1].view())
|
|
842
|
+
anchor1 = to_b2vec2(view_center(pins[1].view()), ppm);
|
|
843
|
+
else
|
|
844
|
+
anchor1 = b2Body_GetLocalPoint(body1, b2Body_GetWorldPoint(body0, anchor0));
|
|
845
|
+
|
|
846
|
+
ref_angle = relative_angle(body0, body1);
|
|
847
|
+
resolved = true;
|
|
848
|
+
resolved_ppm = ppm;
|
|
849
|
+
b2distance = has_distance
|
|
850
|
+
? to_b2coord(distance, ppm)
|
|
851
|
+
: b2Distance(b2Body_GetWorldPoint(body0, anchor0), b2Body_GetWorldPoint(body1, anchor1));
|
|
852
|
+
|
|
853
|
+
write_back_resolved_pins();
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
void apply_params (float ppm) override
|
|
857
|
+
{
|
|
858
|
+
if (b2Joint_GetType(b2joint) == b2_prismaticJoint)
|
|
859
|
+
{
|
|
860
|
+
b2Joint_SetLocalAxisA(b2joint, b2axis());
|
|
861
|
+
if (has_distance)
|
|
862
|
+
b2PrismaticJoint_SetTargetTranslation(b2joint, to_b2coord(distance, ppm));
|
|
863
|
+
b2PrismaticJoint_EnableSpring( b2joint, spring > 0);
|
|
864
|
+
b2PrismaticJoint_SetSpringHertz( b2joint, spring);
|
|
865
|
+
b2PrismaticJoint_SetSpringDampingRatio(b2joint, damping);
|
|
866
|
+
b2PrismaticJoint_EnableLimit( b2joint, has_range);
|
|
867
|
+
if (has_range)
|
|
868
|
+
{
|
|
869
|
+
float min = to_b2coord(range_min, ppm), max = to_b2coord(range_max, ppm);
|
|
870
|
+
b2PrismaticJoint_SetLimits( b2joint, min, max);
|
|
871
|
+
}
|
|
872
|
+
b2PrismaticJoint_EnableMotor( b2joint, has_motor);
|
|
873
|
+
b2PrismaticJoint_SetMotorSpeed( b2joint, to_b2coord(motor_speed, ppm));
|
|
874
|
+
if (has_force)
|
|
875
|
+
b2PrismaticJoint_SetMaxMotorForce( b2joint, force);
|
|
876
|
+
}
|
|
877
|
+
else
|
|
878
|
+
{
|
|
879
|
+
if (has_distance)
|
|
880
|
+
b2DistanceJoint_SetLength( b2joint, to_b2coord(distance, ppm));
|
|
881
|
+
b2DistanceJoint_EnableSpring( b2joint, spring > 0);
|
|
882
|
+
b2DistanceJoint_SetSpringHertz( b2joint, spring);
|
|
883
|
+
b2DistanceJoint_SetSpringDampingRatio(b2joint, damping);
|
|
884
|
+
b2DistanceJoint_EnableLimit( b2joint, has_range);
|
|
885
|
+
if (has_range)
|
|
886
|
+
{
|
|
887
|
+
float min = to_b2coord(range_min, ppm), max = to_b2coord(range_max, ppm);
|
|
888
|
+
b2DistanceJoint_SetLengthRange( b2joint, min, max);
|
|
889
|
+
}
|
|
890
|
+
b2DistanceJoint_EnableMotor( b2joint, has_motor);
|
|
891
|
+
b2DistanceJoint_SetMotorSpeed( b2joint, to_b2coord(motor_speed, ppm));
|
|
892
|
+
if (has_force)
|
|
893
|
+
b2DistanceJoint_SetMaxMotorForce( b2joint, force);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
b2Vec2 b2axis () const
|
|
898
|
+
{
|
|
899
|
+
return b2Normalize(b2Vec2(axis.x, axis.y));
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
};// LinkConstraintData
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
static LinkConstraintData&
|
|
906
|
+
get_data (LinkConstraint& constraint)
|
|
907
|
+
{
|
|
908
|
+
return (LinkConstraintData&) *constraint.self;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
static const LinkConstraintData&
|
|
912
|
+
get_data (const LinkConstraint& constraint)
|
|
913
|
+
{
|
|
914
|
+
return get_data(const_cast<LinkConstraint&>(constraint));
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
static LinkConstraint_CreateFun link_constraint_create_fun = NULL;
|
|
919
|
+
|
|
920
|
+
void
|
|
921
|
+
LinkConstraint_set_create_fun (LinkConstraint_CreateFun fun)
|
|
922
|
+
{
|
|
923
|
+
link_constraint_create_fun = fun;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
LinkConstraint*
|
|
927
|
+
LinkConstraint_create ()
|
|
928
|
+
{
|
|
929
|
+
return link_constraint_create_fun
|
|
930
|
+
? link_constraint_create_fun()
|
|
931
|
+
: new LinkConstraint();
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
LinkConstraint::LinkConstraint ()
|
|
936
|
+
: Super(new LinkConstraintData)
|
|
937
|
+
{
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
LinkConstraint::~LinkConstraint ()
|
|
941
|
+
{
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
static void
|
|
945
|
+
update_axis (LinkConstraint* c, bool has_axis, const Point& axis)
|
|
946
|
+
{
|
|
947
|
+
LinkConstraintData& self = get_data(*c);
|
|
948
|
+
|
|
949
|
+
bool was_axis = self.has_axis;
|
|
950
|
+
self.has_axis = has_axis;
|
|
951
|
+
if (has_axis) self.axis = axis;
|
|
952
|
+
|
|
953
|
+
if (self.is_valid() && was_axis != has_axis)
|
|
954
|
+
reactivate_constraint(c); // distance joint <-> prismatic joint
|
|
955
|
+
else
|
|
956
|
+
self.update_params();
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
void
|
|
960
|
+
LinkConstraint::set_axis (coord x, coord y)
|
|
961
|
+
{
|
|
962
|
+
set_axis(Point(x, y));
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
void
|
|
966
|
+
LinkConstraint::set_axis (const Point& direction)
|
|
967
|
+
{
|
|
968
|
+
if (direction.x == 0 && direction.y == 0)
|
|
969
|
+
argument_error(__FILE__, __LINE__);
|
|
970
|
+
|
|
971
|
+
update_axis(this, true, direction);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
void
|
|
975
|
+
LinkConstraint::clear_axis ()
|
|
976
|
+
{
|
|
977
|
+
update_axis(this, false, Point(0));
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
const Point&
|
|
981
|
+
LinkConstraint::axis () const
|
|
982
|
+
{
|
|
983
|
+
return get_data(*this).axis;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
bool
|
|
987
|
+
LinkConstraint::has_axis () const
|
|
988
|
+
{
|
|
989
|
+
return get_data(*this).has_axis;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
static void
|
|
993
|
+
update_distance (LinkConstraint* c, bool has_distance, coord distance)
|
|
994
|
+
{
|
|
995
|
+
// distance is a radial length (>= 0) for a distance joint, or a signed
|
|
996
|
+
// target translation along the axis for a rail, so it is not clamped
|
|
997
|
+
|
|
998
|
+
LinkConstraintData& self = get_data(*c);
|
|
999
|
+
|
|
1000
|
+
self.has_distance = has_distance;
|
|
1001
|
+
self.distance = distance;
|
|
1002
|
+
self.update_params();
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
void
|
|
1006
|
+
LinkConstraint::set_distance (coord distance)
|
|
1007
|
+
{
|
|
1008
|
+
update_distance(this, true, distance);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
void
|
|
1012
|
+
LinkConstraint::clear_distance ()
|
|
1013
|
+
{
|
|
1014
|
+
update_distance(this, false, 0);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
coord
|
|
1018
|
+
LinkConstraint::distance () const
|
|
1019
|
+
{
|
|
1020
|
+
const LinkConstraintData& self = get_data(*this);
|
|
1021
|
+
|
|
1022
|
+
if (self.has_distance)
|
|
1023
|
+
return self.distance;
|
|
1024
|
+
|
|
1025
|
+
if (self.is_valid() && self.world)
|
|
1026
|
+
return to_coord(
|
|
1027
|
+
self.has_axis
|
|
1028
|
+
? b2PrismaticJoint_GetTargetTranslation(self.b2joint)
|
|
1029
|
+
: b2DistanceJoint_GetLength(self.b2joint),
|
|
1030
|
+
self.ppm());
|
|
1031
|
+
|
|
1032
|
+
return 0;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
bool
|
|
1036
|
+
LinkConstraint::has_distance () const
|
|
1037
|
+
{
|
|
1038
|
+
return get_data(*this).has_distance;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
coord
|
|
1042
|
+
LinkConstraint::current_distance () const
|
|
1043
|
+
{
|
|
1044
|
+
const LinkConstraintData& self = get_data(*this);
|
|
1045
|
+
|
|
1046
|
+
if (!self.is_valid() || !self.world) return 0;
|
|
1047
|
+
|
|
1048
|
+
return to_coord(
|
|
1049
|
+
self.has_axis
|
|
1050
|
+
? b2PrismaticJoint_GetTranslation(self.b2joint)
|
|
1051
|
+
: b2DistanceJoint_GetCurrentLength(self.b2joint),
|
|
1052
|
+
self.ppm());
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
static void
|
|
1056
|
+
update_range (LinkConstraint* c, bool has_range, coord min, coord max)
|
|
1057
|
+
{
|
|
1058
|
+
// range bounds are radial lengths (>= 0) for a distance joint, or signed
|
|
1059
|
+
// translations along the axis for a rail, so only the order is checked
|
|
1060
|
+
|
|
1061
|
+
if (min > max)
|
|
1062
|
+
argument_error(__FILE__, __LINE__);
|
|
1063
|
+
|
|
1064
|
+
LinkConstraintData& self = get_data(*c);
|
|
1065
|
+
|
|
1066
|
+
self.has_range = has_range;
|
|
1067
|
+
self.range_min = min;
|
|
1068
|
+
self.range_max = max;
|
|
1069
|
+
self.update_params();
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
void
|
|
1073
|
+
LinkConstraint::set_range (coord min, coord max)
|
|
1074
|
+
{
|
|
1075
|
+
update_range(this, true, min, max);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
void
|
|
1079
|
+
LinkConstraint::clear_range ()
|
|
1080
|
+
{
|
|
1081
|
+
update_range(this, false, 0, 0);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
coord
|
|
1085
|
+
LinkConstraint::range_min () const
|
|
1086
|
+
{
|
|
1087
|
+
return get_data(*this).range_min;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
coord
|
|
1091
|
+
LinkConstraint::range_max () const
|
|
1092
|
+
{
|
|
1093
|
+
return get_data(*this).range_max;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
bool
|
|
1097
|
+
LinkConstraint::has_range () const
|
|
1098
|
+
{
|
|
1099
|
+
return get_data(*this).has_range;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
static void
|
|
1103
|
+
update_motor (LinkConstraint* c, bool has_motor, coord speed)
|
|
1104
|
+
{
|
|
1105
|
+
LinkConstraintData& self = get_data(*c);
|
|
1106
|
+
|
|
1107
|
+
self.has_motor = has_motor;
|
|
1108
|
+
self.motor_speed = speed;
|
|
1109
|
+
self.update_params();
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
void
|
|
1113
|
+
LinkConstraint::set_motor (coord pixels_per_second)
|
|
1114
|
+
{
|
|
1115
|
+
update_motor(this, true, pixels_per_second);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
void
|
|
1119
|
+
LinkConstraint::clear_motor ()
|
|
1120
|
+
{
|
|
1121
|
+
update_motor(this, false, 0);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
coord
|
|
1125
|
+
LinkConstraint::motor () const
|
|
1126
|
+
{
|
|
1127
|
+
return get_data(*this).motor_speed;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
bool
|
|
1131
|
+
LinkConstraint::has_motor () const
|
|
1132
|
+
{
|
|
1133
|
+
return get_data(*this).has_motor;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
void
|
|
1137
|
+
LinkConstraint::set_force (float max_force)
|
|
1138
|
+
{
|
|
1139
|
+
if (max_force < 0)
|
|
1140
|
+
argument_error(__FILE__, __LINE__);
|
|
1141
|
+
|
|
1142
|
+
LinkConstraintData& self = get_data(*this);
|
|
1143
|
+
|
|
1144
|
+
self.has_force = true;
|
|
1145
|
+
self.force = max_force;
|
|
1146
|
+
self.update_params();
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
void
|
|
1150
|
+
LinkConstraint::clear_force ()
|
|
1151
|
+
{
|
|
1152
|
+
LinkConstraintData& self = get_data(*this);
|
|
1153
|
+
|
|
1154
|
+
self.has_force = false;
|
|
1155
|
+
self.force = 0;
|
|
1156
|
+
|
|
1157
|
+
if (self.is_valid() && self.world)
|
|
1158
|
+
{
|
|
1159
|
+
View* view = self.pins[0].view();
|
|
1160
|
+
Body* body = view ? View_get_body(view, false) : NULL;
|
|
1161
|
+
if (body)
|
|
1162
|
+
{
|
|
1163
|
+
float force = default_max_force(Body_get_id(body));
|
|
1164
|
+
if (self.has_axis)
|
|
1165
|
+
b2PrismaticJoint_SetMaxMotorForce(self.b2joint, force);
|
|
1166
|
+
else
|
|
1167
|
+
b2DistanceJoint_SetMaxMotorForce( self.b2joint, force);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
float
|
|
1173
|
+
LinkConstraint::force () const
|
|
1174
|
+
{
|
|
1175
|
+
const LinkConstraintData& self = get_data(*this);
|
|
1176
|
+
|
|
1177
|
+
if (self.has_force)
|
|
1178
|
+
return self.force;
|
|
1179
|
+
|
|
1180
|
+
if (self.is_valid() && self.world)
|
|
1181
|
+
return self.has_axis
|
|
1182
|
+
? b2PrismaticJoint_GetMaxMotorForce(self.b2joint)
|
|
1183
|
+
: b2DistanceJoint_GetMaxMotorForce( self.b2joint);
|
|
1184
|
+
|
|
1185
|
+
return 0;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
bool
|
|
1189
|
+
LinkConstraint::has_force () const
|
|
1190
|
+
{
|
|
1191
|
+
return get_data(*this).has_force;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
struct WheelConstraintData : public Constraint::Data
|
|
1196
|
+
{
|
|
1197
|
+
|
|
1198
|
+
Point axis = Point(0, 1);
|
|
1199
|
+
|
|
1200
|
+
bool has_range = false;
|
|
1201
|
+
|
|
1202
|
+
coord range_min = 0;
|
|
1203
|
+
|
|
1204
|
+
coord range_max = 0;
|
|
1205
|
+
|
|
1206
|
+
bool has_motor = false;
|
|
1207
|
+
|
|
1208
|
+
float motor_speed = 0;// degree/sec
|
|
1209
|
+
|
|
1210
|
+
bool has_force = false;
|
|
1211
|
+
|
|
1212
|
+
float force = 0;
|
|
1213
|
+
|
|
1214
|
+
b2JointId create_joint (
|
|
1215
|
+
b2WorldId world, b2BodyId body0, b2BodyId body1, float ppm) override
|
|
1216
|
+
{
|
|
1217
|
+
resolve_anchors(body0, body1, ppm);
|
|
1218
|
+
|
|
1219
|
+
// the declaring side goes to bodyB because the mouse joint
|
|
1220
|
+
// assumes bodyA is static
|
|
1221
|
+
b2WheelJointDef def = b2DefaultWheelJointDef();
|
|
1222
|
+
def.bodyIdA = body1;
|
|
1223
|
+
def.bodyIdB = body0;
|
|
1224
|
+
def.localAnchorA = anchor1;
|
|
1225
|
+
def.localAnchorB = anchor0;
|
|
1226
|
+
def.localAxisA = b2axis();
|
|
1227
|
+
def.enableSpring = spring > 0;
|
|
1228
|
+
def.hertz = spring;
|
|
1229
|
+
def.dampingRatio = damping;
|
|
1230
|
+
def.enableLimit = has_range;
|
|
1231
|
+
def.lowerTranslation = to_b2coord(range_min, ppm);
|
|
1232
|
+
def.upperTranslation = to_b2coord(range_max, ppm);
|
|
1233
|
+
def.enableMotor = has_motor;
|
|
1234
|
+
def.motorSpeed = Xot::deg2rad(motor_speed);
|
|
1235
|
+
def.maxMotorTorque = has_force ? force : default_max_force(body0);
|
|
1236
|
+
def.collideConnected = collide;
|
|
1237
|
+
return b2CreateWheelJoint(world, &def);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
void apply_params (float ppm) override
|
|
1241
|
+
{
|
|
1242
|
+
b2Joint_SetLocalAxisA(b2joint, b2axis());
|
|
1243
|
+
b2WheelJoint_EnableSpring( b2joint, spring > 0);
|
|
1244
|
+
b2WheelJoint_SetSpringHertz( b2joint, spring);
|
|
1245
|
+
b2WheelJoint_SetSpringDampingRatio(b2joint, damping);
|
|
1246
|
+
b2WheelJoint_EnableLimit( b2joint, has_range);
|
|
1247
|
+
if (has_range)
|
|
1248
|
+
{
|
|
1249
|
+
float min = to_b2coord(range_min, ppm), max = to_b2coord(range_max, ppm);
|
|
1250
|
+
b2WheelJoint_SetLimits( b2joint, min, max);
|
|
1251
|
+
}
|
|
1252
|
+
b2WheelJoint_EnableMotor( b2joint, has_motor);
|
|
1253
|
+
b2WheelJoint_SetMotorSpeed( b2joint, Xot::deg2rad(motor_speed));
|
|
1254
|
+
if (has_force)
|
|
1255
|
+
b2WheelJoint_SetMaxMotorTorque( b2joint, force);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
b2Vec2 b2axis () const
|
|
1259
|
+
{
|
|
1260
|
+
return b2Normalize(b2Vec2(axis.x, axis.y));
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
};// WheelConstraintData
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
static WheelConstraintData&
|
|
1267
|
+
get_data (WheelConstraint& constraint)
|
|
1268
|
+
{
|
|
1269
|
+
return (WheelConstraintData&) *constraint.self;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
static const WheelConstraintData&
|
|
1273
|
+
get_data (const WheelConstraint& constraint)
|
|
1274
|
+
{
|
|
1275
|
+
return get_data(const_cast<WheelConstraint&>(constraint));
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
static WheelConstraint_CreateFun wheel_constraint_create_fun = NULL;
|
|
1280
|
+
|
|
1281
|
+
void
|
|
1282
|
+
WheelConstraint_set_create_fun (WheelConstraint_CreateFun fun)
|
|
1283
|
+
{
|
|
1284
|
+
wheel_constraint_create_fun = fun;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
WheelConstraint*
|
|
1288
|
+
WheelConstraint_create ()
|
|
1289
|
+
{
|
|
1290
|
+
return wheel_constraint_create_fun
|
|
1291
|
+
? wheel_constraint_create_fun()
|
|
1292
|
+
: new WheelConstraint();
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
|
|
1296
|
+
WheelConstraint::WheelConstraint ()
|
|
1297
|
+
: Super(new WheelConstraintData)
|
|
1298
|
+
{
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
WheelConstraint::~WheelConstraint ()
|
|
1302
|
+
{
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
void
|
|
1306
|
+
WheelConstraint::set_axis (coord x, coord y)
|
|
1307
|
+
{
|
|
1308
|
+
set_axis(Point(x, y));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
void
|
|
1312
|
+
WheelConstraint::set_axis (const Point& direction)
|
|
1313
|
+
{
|
|
1314
|
+
if (direction.x == 0 && direction.y == 0)
|
|
1315
|
+
argument_error(__FILE__, __LINE__);
|
|
1316
|
+
|
|
1317
|
+
WheelConstraintData& self = get_data(*this);
|
|
1318
|
+
|
|
1319
|
+
self.axis = direction;
|
|
1320
|
+
self.update_params();
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
const Point&
|
|
1324
|
+
WheelConstraint::axis () const
|
|
1325
|
+
{
|
|
1326
|
+
return get_data(*this).axis;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
static void
|
|
1330
|
+
update_range (WheelConstraint* c, bool has_range, float min, float max)
|
|
1331
|
+
{
|
|
1332
|
+
if (min > max)
|
|
1333
|
+
argument_error(__FILE__, __LINE__);
|
|
1334
|
+
|
|
1335
|
+
WheelConstraintData& self = get_data(*c);
|
|
1336
|
+
|
|
1337
|
+
self.has_range = has_range;
|
|
1338
|
+
self.range_min = min;
|
|
1339
|
+
self.range_max = max;
|
|
1340
|
+
self.update_params();
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
void
|
|
1344
|
+
WheelConstraint::set_range (coord min, coord max)
|
|
1345
|
+
{
|
|
1346
|
+
update_range(this, true, min, max);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
void
|
|
1350
|
+
WheelConstraint::clear_range ()
|
|
1351
|
+
{
|
|
1352
|
+
update_range(this, false, 0, 0);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
coord
|
|
1356
|
+
WheelConstraint::range_min () const
|
|
1357
|
+
{
|
|
1358
|
+
return get_data(*this).range_min;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
coord
|
|
1362
|
+
WheelConstraint::range_max () const
|
|
1363
|
+
{
|
|
1364
|
+
return get_data(*this).range_max;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
bool
|
|
1368
|
+
WheelConstraint::has_range () const
|
|
1369
|
+
{
|
|
1370
|
+
return get_data(*this).has_range;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
static void
|
|
1374
|
+
update_motor (WheelConstraint* c, bool has_motor, float speed)
|
|
1375
|
+
{
|
|
1376
|
+
WheelConstraintData& self = get_data(*c);
|
|
1377
|
+
|
|
1378
|
+
self.has_motor = has_motor;
|
|
1379
|
+
self.motor_speed = speed;
|
|
1380
|
+
self.update_params();
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
void
|
|
1384
|
+
WheelConstraint::set_motor (float degrees_per_second)
|
|
1385
|
+
{
|
|
1386
|
+
update_motor(this, true, degrees_per_second);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
void
|
|
1390
|
+
WheelConstraint::clear_motor ()
|
|
1391
|
+
{
|
|
1392
|
+
update_motor(this, false, 0);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
float
|
|
1396
|
+
WheelConstraint::motor () const
|
|
1397
|
+
{
|
|
1398
|
+
return get_data(*this).motor_speed;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
bool
|
|
1402
|
+
WheelConstraint::has_motor () const
|
|
1403
|
+
{
|
|
1404
|
+
return get_data(*this).has_motor;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
void
|
|
1408
|
+
WheelConstraint::set_force (float max_torque)
|
|
1409
|
+
{
|
|
1410
|
+
if (max_torque < 0)
|
|
1411
|
+
argument_error(__FILE__, __LINE__);
|
|
1412
|
+
|
|
1413
|
+
WheelConstraintData& self = get_data(*this);
|
|
1414
|
+
|
|
1415
|
+
self.has_force = true;
|
|
1416
|
+
self.force = max_torque;
|
|
1417
|
+
self.update_params();
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
void
|
|
1421
|
+
WheelConstraint::clear_force ()
|
|
1422
|
+
{
|
|
1423
|
+
WheelConstraintData& self = get_data(*this);
|
|
1424
|
+
|
|
1425
|
+
self.has_force = false;
|
|
1426
|
+
self.force = 0;
|
|
1427
|
+
|
|
1428
|
+
if (self.is_valid() && self.world)
|
|
1429
|
+
{
|
|
1430
|
+
View* view = self.pins[0].view();
|
|
1431
|
+
Body* body = view ? View_get_body(view, false) : NULL;
|
|
1432
|
+
if (body)
|
|
1433
|
+
{
|
|
1434
|
+
b2WheelJoint_SetMaxMotorTorque(
|
|
1435
|
+
self.b2joint, default_max_force(Body_get_id(body)));
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
float
|
|
1441
|
+
WheelConstraint::force () const
|
|
1442
|
+
{
|
|
1443
|
+
const WheelConstraintData& self = get_data(*this);
|
|
1444
|
+
|
|
1445
|
+
if (self.has_force)
|
|
1446
|
+
return self.force;
|
|
1447
|
+
|
|
1448
|
+
if (self.is_valid() && self.world)
|
|
1449
|
+
return b2WheelJoint_GetMaxMotorTorque(self.b2joint);
|
|
1450
|
+
|
|
1451
|
+
return 0;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
bool
|
|
1455
|
+
WheelConstraint::has_force () const
|
|
1456
|
+
{
|
|
1457
|
+
return get_data(*this).has_force;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
struct ChaseConstraintData : public Constraint::Data
|
|
1462
|
+
{
|
|
1463
|
+
|
|
1464
|
+
Pin target;
|
|
1465
|
+
|
|
1466
|
+
bool has_force = false;
|
|
1467
|
+
|
|
1468
|
+
float force = 0;
|
|
1469
|
+
|
|
1470
|
+
ChaseConstraintData ()
|
|
1471
|
+
{
|
|
1472
|
+
spring = DEFAULT_CHASE_SPRING;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
b2JointId create_joint (
|
|
1476
|
+
b2WorldId world, b2BodyId body0, b2BodyId body1, float ppm) override
|
|
1477
|
+
{
|
|
1478
|
+
Point pos0 = pins[0].position() ? *pins[0].position() : view_center(pins[0].view());
|
|
1479
|
+
|
|
1480
|
+
// the declaring side goes to bodyB because the mouse joint
|
|
1481
|
+
// assumes bodyA is static
|
|
1482
|
+
b2MouseJointDef def = b2DefaultMouseJointDef();
|
|
1483
|
+
def.bodyIdA = body1;
|
|
1484
|
+
def.bodyIdB = body0;
|
|
1485
|
+
def.target = b2Body_GetWorldPoint(body0, to_b2vec2(pos0, ppm));
|
|
1486
|
+
def.hertz = spring;
|
|
1487
|
+
def.dampingRatio = damping;
|
|
1488
|
+
def.maxForce = has_force ? force : default_max_force(body0);
|
|
1489
|
+
def.collideConnected = collide;
|
|
1490
|
+
|
|
1491
|
+
b2JointId id = b2CreateMouseJoint(world, &def);
|
|
1492
|
+
if (b2Joint_IsValid(id))
|
|
1493
|
+
{
|
|
1494
|
+
b2Vec2 target_pos;
|
|
1495
|
+
if (get_target_pos(&target_pos, ppm))
|
|
1496
|
+
b2MouseJoint_SetTarget(id, target_pos);
|
|
1497
|
+
}
|
|
1498
|
+
return id;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
void apply_params (float ppm) override
|
|
1502
|
+
{
|
|
1503
|
+
b2MouseJoint_SetSpringHertz( b2joint, spring);
|
|
1504
|
+
b2MouseJoint_SetSpringDampingRatio(b2joint, damping);
|
|
1505
|
+
if (has_force)
|
|
1506
|
+
b2MouseJoint_SetMaxForce( b2joint, force);
|
|
1507
|
+
|
|
1508
|
+
b2Vec2 target_pos;
|
|
1509
|
+
if (get_target_pos(&target_pos, ppm))
|
|
1510
|
+
b2MouseJoint_SetTarget( b2joint, target_pos);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
void on_world_update (float ppm) override
|
|
1514
|
+
{
|
|
1515
|
+
if (!is_valid() || !target.view())
|
|
1516
|
+
return;
|
|
1517
|
+
|
|
1518
|
+
b2Vec2 pos;
|
|
1519
|
+
if (get_target_pos(&pos, ppm))
|
|
1520
|
+
{
|
|
1521
|
+
b2MouseJoint_SetTarget(b2joint, pos);
|
|
1522
|
+
b2Joint_WakeBodies(b2joint);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
bool get_target_pos (b2Vec2* pos, float ppm)
|
|
1527
|
+
{
|
|
1528
|
+
assert(pos);
|
|
1529
|
+
|
|
1530
|
+
View* view = target.view();
|
|
1531
|
+
if (view)
|
|
1532
|
+
{
|
|
1533
|
+
Body* body = View_get_body(view, false);
|
|
1534
|
+
if (!body || Body_is_temporary(*body))
|
|
1535
|
+
return false;
|
|
1536
|
+
|
|
1537
|
+
Point p = target.position() ? *target.position() : view_center(view);
|
|
1538
|
+
*pos = b2Body_GetWorldPoint(Body_get_id(body), to_b2vec2(p, ppm));
|
|
1539
|
+
return true;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
if (target.position())
|
|
1543
|
+
{
|
|
1544
|
+
*pos = to_b2vec2(*target.position(), ppm);
|
|
1545
|
+
return true;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
return false;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
};// ChaseConstraintData
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
static ChaseConstraintData&
|
|
1555
|
+
get_data (ChaseConstraint& constraint)
|
|
1556
|
+
{
|
|
1557
|
+
return (ChaseConstraintData&) *constraint.self;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
static const ChaseConstraintData&
|
|
1561
|
+
get_data (const ChaseConstraint& constraint)
|
|
1562
|
+
{
|
|
1563
|
+
return get_data(const_cast<ChaseConstraint&>(constraint));
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
|
|
1567
|
+
static ChaseConstraint_CreateFun chase_constraint_create_fun = NULL;
|
|
1568
|
+
|
|
1569
|
+
void
|
|
1570
|
+
ChaseConstraint_set_create_fun (ChaseConstraint_CreateFun fun)
|
|
1571
|
+
{
|
|
1572
|
+
chase_constraint_create_fun = fun;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
ChaseConstraint*
|
|
1576
|
+
ChaseConstraint_create ()
|
|
1577
|
+
{
|
|
1578
|
+
return chase_constraint_create_fun
|
|
1579
|
+
? chase_constraint_create_fun()
|
|
1580
|
+
: new ChaseConstraint();
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
|
|
1584
|
+
ChaseConstraint::ChaseConstraint ()
|
|
1585
|
+
: Super(new ChaseConstraintData)
|
|
1586
|
+
{
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
ChaseConstraint::~ChaseConstraint ()
|
|
1590
|
+
{
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
void
|
|
1594
|
+
ChaseConstraint::set_target (const Pin& target)
|
|
1595
|
+
{
|
|
1596
|
+
ChaseConstraintData& self = get_data(*this);
|
|
1597
|
+
|
|
1598
|
+
if (target.view() && target.view() == self.pins[0].view())
|
|
1599
|
+
argument_error(__FILE__, __LINE__, "can not chase itself");
|
|
1600
|
+
|
|
1601
|
+
self.target = target;
|
|
1602
|
+
self.update_params();
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
const Pin&
|
|
1606
|
+
ChaseConstraint::target () const
|
|
1607
|
+
{
|
|
1608
|
+
return get_data(*this).target;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
void
|
|
1612
|
+
ChaseConstraint::set_force (float max_force)
|
|
1613
|
+
{
|
|
1614
|
+
if (max_force < 0)
|
|
1615
|
+
argument_error(__FILE__, __LINE__);
|
|
1616
|
+
|
|
1617
|
+
ChaseConstraintData& self = get_data(*this);
|
|
1618
|
+
|
|
1619
|
+
self.has_force = true;
|
|
1620
|
+
self.force = max_force;
|
|
1621
|
+
self.update_params();
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
void
|
|
1625
|
+
ChaseConstraint::clear_force ()
|
|
1626
|
+
{
|
|
1627
|
+
ChaseConstraintData& self = get_data(*this);
|
|
1628
|
+
|
|
1629
|
+
self.has_force = false;
|
|
1630
|
+
self.force = 0;
|
|
1631
|
+
|
|
1632
|
+
if (self.is_valid() && self.world)
|
|
1633
|
+
{
|
|
1634
|
+
View* view = self.pins[0].view();
|
|
1635
|
+
Body* body = view ? View_get_body(view, false) : NULL;
|
|
1636
|
+
if (body)
|
|
1637
|
+
b2MouseJoint_SetMaxForce(self.b2joint, default_max_force(Body_get_id(body)));
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
float
|
|
1642
|
+
ChaseConstraint::force () const
|
|
1643
|
+
{
|
|
1644
|
+
const ChaseConstraintData& self = get_data(*this);
|
|
1645
|
+
|
|
1646
|
+
if (self.has_force)
|
|
1647
|
+
return self.force;
|
|
1648
|
+
|
|
1649
|
+
if (self.is_valid() && self.world)
|
|
1650
|
+
return b2MouseJoint_GetMaxForce(self.b2joint);
|
|
1651
|
+
|
|
1652
|
+
return 0;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
bool
|
|
1656
|
+
ChaseConstraint::has_force () const
|
|
1657
|
+
{
|
|
1658
|
+
return get_data(*this).has_force;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
|
|
1662
|
+
}// Reflex
|