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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/reflex/application.cpp +19 -1
  3. data/.doc/ext/reflex/chase_constraint.cpp +84 -0
  4. data/.doc/ext/reflex/clipboard.cpp +65 -0
  5. data/.doc/ext/reflex/constraint.cpp +135 -0
  6. data/.doc/ext/reflex/key_event.cpp +6 -6
  7. data/.doc/ext/reflex/link_constraint.cpp +178 -0
  8. data/.doc/ext/reflex/menu.cpp +302 -0
  9. data/.doc/ext/reflex/native.cpp +22 -4
  10. data/.doc/ext/reflex/pin.cpp +156 -0
  11. data/.doc/ext/reflex/pointer.cpp +2 -0
  12. data/.doc/ext/reflex/pointer_event.cpp +1 -1
  13. data/.doc/ext/reflex/reflex.cpp +42 -7
  14. data/.doc/ext/reflex/selector.cpp +4 -4
  15. data/.doc/ext/reflex/snap_constraint.cpp +125 -0
  16. data/.doc/ext/reflex/style_length.cpp +3 -2
  17. data/.doc/ext/reflex/view.cpp +96 -23
  18. data/.doc/ext/reflex/wheel_constraint.cpp +142 -0
  19. data/.doc/ext/reflex/window.cpp +59 -17
  20. data/.github/workflows/release-gem.yml +10 -1
  21. data/.github/workflows/test.yml +9 -0
  22. data/ChangeLog.md +30 -0
  23. data/README.md +1 -0
  24. data/VERSION +1 -1
  25. data/ext/reflex/application.cpp +21 -1
  26. data/ext/reflex/chase_constraint.cpp +89 -0
  27. data/ext/reflex/clipboard.cpp +69 -0
  28. data/ext/reflex/constraint.cpp +146 -0
  29. data/ext/reflex/key_event.cpp +6 -6
  30. data/ext/reflex/link_constraint.cpp +194 -0
  31. data/ext/reflex/menu.cpp +330 -0
  32. data/ext/reflex/native.cpp +22 -4
  33. data/ext/reflex/pin.cpp +165 -0
  34. data/ext/reflex/pointer.cpp +2 -0
  35. data/ext/reflex/pointer_event.cpp +1 -1
  36. data/ext/reflex/reflex.cpp +42 -7
  37. data/ext/reflex/selector.cpp +4 -4
  38. data/ext/reflex/selector.h +1 -1
  39. data/ext/reflex/snap_constraint.cpp +135 -0
  40. data/ext/reflex/style_length.cpp +3 -2
  41. data/ext/reflex/view.cpp +117 -37
  42. data/ext/reflex/wheel_constraint.cpp +154 -0
  43. data/ext/reflex/window.cpp +65 -18
  44. data/include/reflex/application.h +9 -0
  45. data/include/reflex/clipboard.h +53 -0
  46. data/include/reflex/constraint.h +275 -0
  47. data/include/reflex/defs.h +204 -195
  48. data/include/reflex/event.h +2 -0
  49. data/include/reflex/menu.h +120 -0
  50. data/include/reflex/pin.h +68 -0
  51. data/include/reflex/pointer.h +4 -0
  52. data/include/reflex/ruby/clipboard.h +23 -0
  53. data/include/reflex/ruby/constraint.h +94 -0
  54. data/include/reflex/ruby/menu.h +103 -0
  55. data/include/reflex/ruby/pin.h +40 -0
  56. data/include/reflex/ruby/view.h +36 -0
  57. data/include/reflex/ruby/window.h +36 -18
  58. data/include/reflex/view.h +39 -12
  59. data/include/reflex/window.h +17 -4
  60. data/include/reflex.h +5 -0
  61. data/lib/reflex/chase_constraint.rb +15 -0
  62. data/lib/reflex/clipboard.rb +19 -0
  63. data/lib/reflex/constraint.rb +29 -0
  64. data/lib/reflex/extension.rb +1 -1
  65. data/lib/reflex/helper.rb +28 -0
  66. data/lib/reflex/key_event.rb +7 -12
  67. data/lib/reflex/link_constraint.rb +31 -0
  68. data/lib/reflex/menu.rb +76 -0
  69. data/lib/reflex/pin.rb +39 -0
  70. data/lib/reflex/pointer.rb +18 -0
  71. data/lib/reflex/pointer_event.rb +1 -1
  72. data/lib/reflex/snap_constraint.rb +28 -0
  73. data/lib/reflex/view.rb +56 -4
  74. data/lib/reflex/wheel_constraint.rb +28 -0
  75. data/lib/reflex/wheel_event.rb +9 -0
  76. data/lib/reflex.rb +15 -6
  77. data/pod.rake +4 -2
  78. data/reflex.gemspec +3 -3
  79. data/samples/constraint.rb +152 -0
  80. data/samples/menu.rb +29 -0
  81. data/src/application.cpp +21 -2
  82. data/src/application.h +5 -0
  83. data/src/clipboard.cpp +81 -0
  84. data/src/constraint.cpp +1662 -0
  85. data/src/constraint.h +64 -0
  86. data/src/event.cpp +7 -1
  87. data/src/event.h +1 -2
  88. data/src/ios/application.mm +5 -0
  89. data/src/ios/clipboard.mm +44 -0
  90. data/src/ios/event.h +6 -2
  91. data/src/ios/event.mm +31 -12
  92. data/src/ios/menu.mm +36 -0
  93. data/src/ios/view_controller.mm +45 -12
  94. data/src/ios/window.mm +6 -0
  95. data/src/menu.cpp +325 -0
  96. data/src/menu.h +65 -0
  97. data/src/osx/app_delegate.mm +74 -125
  98. data/src/osx/application.mm +16 -0
  99. data/src/osx/clipboard.mm +45 -0
  100. data/src/osx/event.mm +40 -10
  101. data/src/osx/menu.h +25 -0
  102. data/src/osx/menu.mm +372 -0
  103. data/src/osx/native_window.mm +27 -0
  104. data/src/osx/opengl_view.mm +51 -0
  105. data/src/osx/window.mm +11 -0
  106. data/src/pin.cpp +137 -0
  107. data/src/pointer.cpp +27 -16
  108. data/src/pointer.h +6 -0
  109. data/src/sdl/application.cpp +5 -0
  110. data/src/sdl/clipboard.cpp +39 -0
  111. data/src/sdl/event.cpp +20 -3
  112. data/src/sdl/event.h +6 -3
  113. data/src/sdl/menu.cpp +35 -0
  114. data/src/sdl/window.cpp +26 -8
  115. data/src/view.cpp +313 -52
  116. data/src/view.h +18 -2
  117. data/src/win32/application.cpp +5 -0
  118. data/src/win32/clipboard.cpp +210 -0
  119. data/src/win32/event.cpp +11 -1
  120. data/src/win32/event.h +2 -0
  121. data/src/win32/menu.cpp +352 -0
  122. data/src/win32/menu.h +27 -0
  123. data/src/win32/window.cpp +62 -0
  124. data/src/win32/window.h +3 -0
  125. data/src/window.cpp +235 -22
  126. data/src/window.h +13 -2
  127. data/src/world.cpp +77 -14
  128. data/src/world.h +9 -2
  129. data/test/helper.rb +7 -0
  130. data/test/test_application.rb +11 -0
  131. data/test/test_chase_constraint.rb +124 -0
  132. data/test/test_clipboard.rb +54 -0
  133. data/test/test_constraint.rb +186 -0
  134. data/test/test_key_event.rb +6 -0
  135. data/test/test_link_constraint.rb +239 -0
  136. data/test/test_menu.rb +207 -0
  137. data/test/test_pin.rb +65 -0
  138. data/test/test_pointer.rb +23 -9
  139. data/test/test_pointer_event.rb +1 -1
  140. data/test/test_snap_constraint.rb +142 -0
  141. data/test/test_view.rb +113 -23
  142. data/test/test_wheel_constraint.rb +127 -0
  143. data/test/test_wheel_event.rb +15 -9
  144. data/test/test_window.rb +10 -0
  145. metadata +82 -8
@@ -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