numerix 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +5 -0
  4. data/.yardopts +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +66 -0
  9. data/Rakefile +18 -0
  10. data/TODO.txt +25 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/ext/numerix/common.h +107 -0
  14. data/ext/numerix/extconf.rb +3 -0
  15. data/ext/numerix/matrix3x2.c +638 -0
  16. data/ext/numerix/matrix3x2.h +52 -0
  17. data/ext/numerix/matrix4x4.c +1807 -0
  18. data/ext/numerix/matrix4x4.h +90 -0
  19. data/ext/numerix/matrix_base.c +307 -0
  20. data/ext/numerix/matrix_base.h +70 -0
  21. data/ext/numerix/numerix.c +33 -0
  22. data/ext/numerix/numerix.h +19 -0
  23. data/ext/numerix/plane.c +311 -0
  24. data/ext/numerix/plane.h +34 -0
  25. data/ext/numerix/quaternion.c +712 -0
  26. data/ext/numerix/quaternion.h +53 -0
  27. data/ext/numerix/structure.c +154 -0
  28. data/ext/numerix/structure.h +24 -0
  29. data/ext/numerix/vector.c +326 -0
  30. data/ext/numerix/vector.h +57 -0
  31. data/ext/numerix/vector2.c +641 -0
  32. data/ext/numerix/vector2.h +64 -0
  33. data/ext/numerix/vector3.c +805 -0
  34. data/ext/numerix/vector3.h +68 -0
  35. data/ext/numerix/vector4.c +727 -0
  36. data/ext/numerix/vector4.h +63 -0
  37. data/ext/numerix/vector_base.c +94 -0
  38. data/ext/numerix/vector_base.h +30 -0
  39. data/extra/numerix128.png +0 -0
  40. data/extra/numerix24.png +0 -0
  41. data/extra/numerix32.png +0 -0
  42. data/extra/numerix320.png +0 -0
  43. data/extra/numerix48.png +0 -0
  44. data/extra/numerix96.png +0 -0
  45. data/lib/numerix/error.rb +36 -0
  46. data/lib/numerix/matrix3x2.rb +420 -0
  47. data/lib/numerix/matrix4x4.rb +676 -0
  48. data/lib/numerix/matrix_base.rb +14 -0
  49. data/lib/numerix/plane.rb +154 -0
  50. data/lib/numerix/quaternion.rb +355 -0
  51. data/lib/numerix/structure.rb +124 -0
  52. data/lib/numerix/vector.rb +13 -0
  53. data/lib/numerix/vector2.rb +534 -0
  54. data/lib/numerix/vector3.rb +572 -0
  55. data/lib/numerix/vector4.rb +551 -0
  56. data/lib/numerix/vector_base.rb +14 -0
  57. data/lib/numerix/version.rb +6 -0
  58. data/lib/numerix.rb +10 -0
  59. data/numerix.gemspec +30 -0
  60. metadata +167 -0
@@ -0,0 +1,712 @@
1
+
2
+ #include "quaternion.h"
3
+
4
+ void Init_quaternion(VALUE outer) {
5
+ rb_define_alloc_func(rb_cQuaternion, rb_quaternion_alloc);
6
+ rb_define_method(rb_cQuaternion, "initialize", rb_quaternion_initialize, -1);
7
+
8
+ // Instance
9
+ rb_define_method(rb_cQuaternion, "identity?", rb_quaternion_identity_p, 0);
10
+ rb_define_method(rb_cQuaternion, "length", rb_quaternion_length, 0);
11
+ rb_define_method(rb_cQuaternion, "length_squared", rb_quaternion_length_squared, 0);
12
+ rb_define_method(rb_cQuaternion, "normalize", rb_quaternion_normalize, 0);
13
+ rb_define_method(rb_cQuaternion, "normalize!", rb_quaternion_normalize_bang, 0);
14
+ rb_define_method(rb_cQuaternion, "conjugate", rb_quaternion_conjugate, 0);
15
+ rb_define_method(rb_cQuaternion, "conjugate!", rb_quaternion_conjugate_bang, 0);
16
+ rb_define_method(rb_cQuaternion, "inverse", rb_quaternion_inverse, 0);
17
+ rb_define_method(rb_cQuaternion, "dot", rb_quaternion_dot, 1);
18
+ rb_define_method(rb_cQuaternion, "concatenate", rb_quaternion_concatenate, 1);
19
+ rb_define_method(rb_cQuaternion, "concatenate!", rb_quaternion_concatenate_bang, 1);
20
+ rb_define_method(rb_cQuaternion, "lerp", rb_quaternion_lerp, 2);
21
+ rb_define_method(rb_cQuaternion, "lerp!", rb_quaternion_lerp_bang, 2);
22
+ rb_define_method(rb_cQuaternion, "slerp", rb_quaternion_slerp, 2);
23
+ rb_define_method(rb_cQuaternion, "slerp!", rb_quaternion_slerp_bang, 2);
24
+
25
+ // Conversion
26
+ rb_define_method(rb_cQuaternion, "to_s", rb_quaternion_to_s, 0);
27
+ rb_define_method(rb_cQuaternion, "to_a", rb_quaternion_to_a, 0);
28
+ rb_define_method(rb_cQuaternion, "to_h", rb_quaternion_to_h, 0);
29
+ rb_define_method(rb_cQuaternion, "to_vec4", rb_quaternion_to_vec4, 0);
30
+
31
+ // Operators
32
+ rb_define_method(rb_cQuaternion, "-@", rb_quaternion_negate, 0);
33
+ rb_define_method(rb_cQuaternion, "+", rb_quaternion_add, 1);
34
+ rb_define_method(rb_cQuaternion, "-", rb_quaternion_subtract, 1);
35
+ rb_define_method(rb_cQuaternion, "*", rb_quaternion_multiply, 1);
36
+ rb_define_method(rb_cQuaternion, "/", rb_quaternion_divide, 1);
37
+ rb_define_method(rb_cQuaternion, "==", rb_quaternion_equal, 1);
38
+
39
+ // Class
40
+ rb_define_singleton_method(rb_cQuaternion, "identity", rb_quaternion_identity, 0);
41
+ rb_define_singleton_method(rb_cQuaternion, "from_axis_angle", rb_quaternion_from_axis_angle, 2);
42
+ rb_define_singleton_method(rb_cQuaternion, "from_yaw_pitch_roll", rb_quaternion_from_yaw_pitch_roll, 3);
43
+ rb_define_singleton_method(rb_cQuaternion, "from_rotation_matrix", rb_quaternion_from_rotation_matrix, 1);
44
+ rb_define_singleton_method(rb_cQuaternion, "slerp", rb_quaternion_slerp_s, 3);
45
+ rb_define_singleton_method(rb_cQuaternion, "lerp", rb_quaternion_lerp_s, 3);
46
+ }
47
+
48
+ VALUE rb_quaternion_alloc(VALUE klass) {
49
+ Quaternion *q = ALLOC(Quaternion);
50
+ memset(q, 0, sizeof(Quaternion));
51
+ return NUMERIX_WRAP(klass, q);
52
+ }
53
+
54
+ VALUE rb_quaternion_initialize(int argc, VALUE *argv, VALUE self) {
55
+ QUATERNION();
56
+ switch (argc) {
57
+ case 0:
58
+ break;
59
+ case 2: {
60
+ Vector3 *v;
61
+ Data_Get_Struct(argv[0], Vector3, v);
62
+ q->x = v->x;
63
+ q->y = v->y;
64
+ q->z = v->z;
65
+ q->w = NUM2FLT(argv[1]);
66
+ break;
67
+ }
68
+ case 4: {
69
+ q->x = NUM2FLT(argv[0]);
70
+ q->y = NUM2FLT(argv[1]);
71
+ q->z = NUM2FLT(argv[2]);
72
+ q->w = NUM2FLT(argv[3]);
73
+ break;
74
+ }
75
+ default:
76
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 0, 2, 4)", argc);
77
+ break;
78
+ }
79
+ return Qnil;
80
+ }
81
+
82
+ VALUE rb_quaternion_identity_p(VALUE self) {
83
+ QUATERNION();
84
+ return q->x == 0.0f && q->y == 0.0f && q->z == 0.0f && q->w == 1.0f ? Qtrue : Qfalse;
85
+ }
86
+
87
+ VALUE rb_quaternion_length(VALUE self) {
88
+ QUATERNION();
89
+ return DBL2NUM(sqrtf(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w));
90
+ }
91
+
92
+ VALUE rb_quaternion_length_squared(VALUE self) {
93
+ QUATERNION();
94
+ return DBL2NUM(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w);
95
+ }
96
+
97
+ VALUE rb_quaternion_negate(VALUE self) {
98
+ QUATERNION();
99
+ Quaternion *result = ALLOC(Quaternion);
100
+
101
+ result->x = -q->x;
102
+ result->y = -q->y;
103
+ result->z = -q->z;
104
+ result->w = -q->w;
105
+
106
+ return NUMERIX_WRAP(CLASS_OF(self), result);
107
+ }
108
+
109
+ VALUE rb_quaternion_add(VALUE self, VALUE other) {
110
+ Quaternion *q1, *q2, *result;
111
+ Data_Get_Struct(self, Quaternion, q1);
112
+ Data_Get_Struct(other, Quaternion, q2);
113
+ result = ALLOC(Quaternion);
114
+
115
+ result->x = q1->x + q2->x;
116
+ result->y = q1->y + q2->y;
117
+ result->z = q1->z + q2->z;
118
+ result->w = q1->w + q2->w;
119
+
120
+ return NUMERIX_WRAP(CLASS_OF(self), result);
121
+ }
122
+
123
+ VALUE rb_quaternion_subtract(VALUE self, VALUE other) {
124
+ Quaternion *q1, *q2, *result;
125
+ Data_Get_Struct(self, Quaternion, q1);
126
+ Data_Get_Struct(other, Quaternion, q2);
127
+ result = ALLOC(Quaternion);
128
+
129
+ result->x = q1->x - q2->x;
130
+ result->y = q1->y - q2->y;
131
+ result->z = q1->z - q2->z;
132
+ result->w = q1->w - q2->w;
133
+
134
+ return NUMERIX_WRAP(CLASS_OF(self), result);
135
+ }
136
+
137
+ VALUE rb_quaternion_multiply(VALUE self, VALUE other) {
138
+ Quaternion *q1, *result;
139
+ Data_Get_Struct(self, Quaternion, q1);
140
+ result = ALLOC(Quaternion);
141
+
142
+ if (NUMERIX_TYPE_P(other, rb_cQuaternion)) {
143
+ Quaternion *q2;
144
+ Data_Get_Struct(other, Quaternion, q2);
145
+
146
+ float q1x = q1->x;
147
+ float q1y = q1->y;
148
+ float q1z = q1->z;
149
+ float q1w = q1->w;
150
+
151
+ float q2x = q2->x;
152
+ float q2y = q2->y;
153
+ float q2z = q2->z;
154
+ float q2w = q2->w;
155
+
156
+ // cross(av, bv)
157
+ float cx = q1y * q2z - q1z * q2y;
158
+ float cy = q1z * q2x - q1x * q2z;
159
+ float cz = q1x * q2y - q1y * q2x;
160
+
161
+ float dot = q1x * q2x + q1y * q2y + q1z * q2z;
162
+
163
+ result->x = q1x * q2w + q2x * q1w + cx;
164
+ result->y = q1y * q2w + q2y * q1w + cy;
165
+ result->z = q1z * q2w + q2z * q1w + cz;
166
+ result->w = q1w * q2w - dot;
167
+ } else {
168
+ float scalar = NUM2FLT(other);
169
+ result->x = q1->x * scalar;
170
+ result->y = q1->y * scalar;
171
+ result->z = q1->z * scalar;
172
+ result->w = q1->w * scalar;
173
+ }
174
+
175
+ return NUMERIX_WRAP(CLASS_OF(self), result);
176
+ }
177
+
178
+ VALUE rb_quaternion_divide(VALUE self, VALUE other) {
179
+ Quaternion *q1, *q2, *result;
180
+ Data_Get_Struct(self, Quaternion, q1);
181
+ Data_Get_Struct(other, Quaternion, q2);
182
+ result = ALLOC(Quaternion);
183
+
184
+ float q1x = q1->x;
185
+ float q1y = q1->y;
186
+ float q1z = q1->z;
187
+ float q1w = q1->w;
188
+
189
+ //-------------------------------------
190
+ // Inverse part.
191
+ float ls = q2->x * q2->x + q2->y * q2->y + q2->z * q2->z + q2->w * q2->w;
192
+ float invNorm = 1.0f / ls;
193
+
194
+ float q2x = -q2->x * invNorm;
195
+ float q2y = -q2->y * invNorm;
196
+ float q2z = -q2->z * invNorm;
197
+ float q2w = q2->w * invNorm;
198
+
199
+ //-------------------------------------
200
+ // Multiply part.
201
+
202
+ // cross(av, bv)
203
+ float cx = q1y * q2z - q1z * q2y;
204
+ float cy = q1z * q2x - q1x * q2z;
205
+ float cz = q1x * q2y - q1y * q2x;
206
+
207
+ float dot = q1x * q2x + q1y * q2y + q1z * q2z;
208
+
209
+ result->x = q1x * q2w + q2x * q1w + cx;
210
+ result->y = q1y * q2w + q2y * q1w + cy;
211
+ result->z = q1z * q2w + q2z * q1w + cz;
212
+ result->w = q1w * q2w - dot;
213
+
214
+ return NUMERIX_WRAP(CLASS_OF(self), result);
215
+ }
216
+
217
+ VALUE rb_quaternion_equal(VALUE self, VALUE other) {
218
+ if (CLASS_OF(other) != CLASS_OF(self))
219
+ return Qfalse;
220
+ Quaternion *q1, *q2;
221
+ Data_Get_Struct(self, Quaternion, q1);
222
+ Data_Get_Struct(other, Quaternion, q2);
223
+ return FLT_EQUAL(q1->x, q2->x) && FLT_EQUAL(q1->y, q2->y) && FLT_EQUAL(q1->z, q2->z) && FLT_EQUAL(q1->w, q2->w) ? Qtrue : Qfalse;
224
+ }
225
+
226
+ VALUE rb_quaternion_to_s(VALUE self) {
227
+ QUATERNION();
228
+ return rb_sprintf("{%f, %f, %f, %f}", q->x, q->y, q->z, q->w);
229
+ }
230
+
231
+ VALUE rb_quaternion_to_a(VALUE self) {
232
+ QUATERNION();
233
+ VALUE ary = rb_ary_new_capa(4);
234
+ rb_ary_store(ary, 0, DBL2NUM(q->x));
235
+ rb_ary_store(ary, 1, DBL2NUM(q->y));
236
+ rb_ary_store(ary, 2, DBL2NUM(q->z));
237
+ rb_ary_store(ary, 3, DBL2NUM(q->w));
238
+ return ary;
239
+ }
240
+
241
+ VALUE rb_quaternion_to_h(VALUE self) {
242
+ QUATERNION();
243
+ VALUE hash = rb_hash_new();
244
+ rb_hash_aset(hash, ID2SYM(rb_intern("x")), DBL2NUM(q->x));
245
+ rb_hash_aset(hash, ID2SYM(rb_intern("y")), DBL2NUM(q->y));
246
+ rb_hash_aset(hash, ID2SYM(rb_intern("z")), DBL2NUM(q->z));
247
+ rb_hash_aset(hash, ID2SYM(rb_intern("w")), DBL2NUM(q->w));
248
+ return hash;
249
+ }
250
+
251
+ VALUE rb_quaternion_to_vec4(VALUE self) {
252
+ QUATERNION();
253
+ Vector4 *v = ALLOC(Vector4);
254
+ memcpy(v, q, sizeof(Quaternion));
255
+ return NUMERIX_WRAP(rb_cVector4, v);
256
+ }
257
+
258
+ VALUE rb_quaternion_normalize(VALUE self) {
259
+ QUATERNION();
260
+ Quaternion *result = ALLOC(Quaternion);
261
+
262
+ float invNorm = 1.0f / sqrtf(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w);
263
+ result->x = q->x * invNorm;
264
+ result->y = q->y * invNorm;
265
+ result->z = q->z * invNorm;
266
+ result->w = q->w * invNorm;
267
+
268
+ return NUMERIX_WRAP(CLASS_OF(self), result);
269
+ }
270
+
271
+ VALUE rb_quaternion_normalize_bang(VALUE self) {
272
+ QUATERNION();
273
+
274
+ float invNorm = 1.0f / sqrtf(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w);
275
+ q->x = q->x * invNorm;
276
+ q->y = q->y * invNorm;
277
+ q->z = q->z * invNorm;
278
+ q->w = q->w * invNorm;
279
+
280
+ return self;
281
+ }
282
+
283
+ VALUE rb_quaternion_conjugate(VALUE self) {
284
+ QUATERNION();
285
+ Quaternion *result = ALLOC(Quaternion);
286
+
287
+ result->x = -q->x;
288
+ result->y = -q->y;
289
+ result->z = -q->z;
290
+ result->w = q->w;
291
+
292
+ return NUMERIX_WRAP(CLASS_OF(self), result);
293
+ }
294
+
295
+ VALUE rb_quaternion_conjugate_bang(VALUE self) {
296
+ QUATERNION();
297
+
298
+ q->x = -q->x;
299
+ q->y = -q->y;
300
+ q->z = -q->z;
301
+ q->w = q->w;
302
+
303
+ return self;
304
+ }
305
+
306
+ VALUE rb_quaternion_dot(VALUE self, VALUE other) {
307
+ Quaternion *q1, *q2;
308
+ Data_Get_Struct(self, Quaternion, q1);
309
+ Data_Get_Struct(other, Quaternion, q2);
310
+
311
+ return DBL2NUM(q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w);
312
+ }
313
+
314
+ VALUE rb_quaternion_identity(VALUE klass) {
315
+ Quaternion *q = ALLOC(Quaternion);
316
+ q->x = 0.0f;
317
+ q->y = 0.0f;
318
+ q->z = 0.0f;
319
+ q->w = 1.0f;
320
+ return NUMERIX_WRAP(klass, q);
321
+ }
322
+
323
+ VALUE rb_quaternion_inverse(VALUE self) {
324
+ QUATERNION();
325
+ Quaternion *result = ALLOC(Quaternion);
326
+
327
+ // -1 ( a -v )
328
+ // q = ( ------------- ------------- )
329
+ // ( a^2 + |v|^2 , a^2 + |v|^2 )
330
+
331
+ float ls = q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w;
332
+ float invNorm = 1.0f / ls;
333
+
334
+ result->x = -q->x * invNorm;
335
+ result->y = -q->y * invNorm;
336
+ result->z = -q->z * invNorm;
337
+ result->w = q->w * invNorm;
338
+
339
+ return NUMERIX_WRAP(CLASS_OF(self), result);
340
+ }
341
+
342
+ VALUE rb_quaternion_concatenate(VALUE self, VALUE other) {
343
+ Quaternion *q1, *q2, *result;
344
+ Data_Get_Struct(self, Quaternion, q1);
345
+ Data_Get_Struct(other, Quaternion, q2);
346
+ result = ALLOC(Quaternion);
347
+
348
+ // Concatenate rotation is actually q2 * q1 instead of q1 * q2.
349
+ // So that's why q2 goes q1 and q1 goes q2.
350
+ float q1x = q2->x;
351
+ float q1y = q2->y;
352
+ float q1z = q2->z;
353
+ float q1w = q2->w;
354
+
355
+ float q2x = q1->x;
356
+ float q2y = q1->y;
357
+ float q2z = q1->z;
358
+ float q2w = q1->w;
359
+
360
+ // cross(av, bv)
361
+ float cx = q1y * q2z - q1z * q2y;
362
+ float cy = q1z * q2x - q1x * q2z;
363
+ float cz = q1x * q2y - q1y * q2x;
364
+
365
+ float dot = q1x * q2x + q1y * q2y + q1z * q2z;
366
+
367
+ result->x = q1x * q2w + q2x * q1w + cx;
368
+ result->y = q1y * q2w + q2y * q1w + cy;
369
+ result->z = q1z * q2w + q2z * q1w + cz;
370
+ result->w = q1w * q2w - dot;
371
+
372
+ return NUMERIX_WRAP(CLASS_OF(self), result);
373
+ }
374
+
375
+ VALUE rb_quaternion_concatenate_bang(VALUE self, VALUE other) {
376
+ Quaternion *q1, *q2;
377
+ Data_Get_Struct(self, Quaternion, q1);
378
+ Data_Get_Struct(other, Quaternion, q2);
379
+
380
+ // Concatenate rotation is actually q2 * q1 instead of q1 * q2.
381
+ // So that's why q2 goes q1 and q1 goes q2.
382
+ float q1x = q2->x;
383
+ float q1y = q2->y;
384
+ float q1z = q2->z;
385
+ float q1w = q2->w;
386
+
387
+ float q2x = q1->x;
388
+ float q2y = q1->y;
389
+ float q2z = q1->z;
390
+ float q2w = q1->w;
391
+
392
+ // cross(av, bv)
393
+ float cx = q1y * q2z - q1z * q2y;
394
+ float cy = q1z * q2x - q1x * q2z;
395
+ float cz = q1x * q2y - q1y * q2x;
396
+
397
+ float dot = q1x * q2x + q1y * q2y + q1z * q2z;
398
+
399
+ q1->x = q1x * q2w + q2x * q1w + cx;
400
+ q1->y = q1y * q2w + q2y * q1w + cy;
401
+ q1->z = q1z * q2w + q2z * q1w + cz;
402
+ q1->w = q1w * q2w - dot;
403
+
404
+ return self;
405
+ }
406
+
407
+ VALUE rb_quaternion_lerp(VALUE self, VALUE other, VALUE amount) {
408
+ Quaternion *q1, *q2, *result;
409
+ Data_Get_Struct(self, Quaternion, q1);
410
+ Data_Get_Struct(other, Quaternion, q2);
411
+ result = ALLOC(Quaternion);
412
+
413
+ float t = amount;
414
+ float t1 = 1.0f - t;
415
+ float dot = q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w;
416
+
417
+ if (dot >= 0.0f) {
418
+ result->x = t1 * q1->x + t * q2->x;
419
+ result->y = t1 * q1->y + t * q2->y;
420
+ result->z = t1 * q1->z + t * q2->z;
421
+ result->w = t1 * q1->w + t * q2->w;
422
+ } else {
423
+ result->x = t1 * q1->x - t * q2->x;
424
+ result->y = t1 * q1->y - t * q2->y;
425
+ result->z = t1 * q1->z - t * q2->z;
426
+ result->w = t1 * q1->w - t * q2->w;
427
+ }
428
+
429
+ // Normalize it.
430
+ float ls = result->x * result->x + result->y * result->y + result->z * result->z + result->w * result->w;
431
+ float invNorm = 1.0f / sqrtf(ls);
432
+
433
+ result->x *= invNorm;
434
+ result->y *= invNorm;
435
+ result->z *= invNorm;
436
+ result->w *= invNorm;
437
+
438
+ return NUMERIX_WRAP(CLASS_OF(self), result);
439
+ }
440
+
441
+ VALUE rb_quaternion_lerp_bang(VALUE self, VALUE other, VALUE amount) {
442
+ Quaternion *q1, *q2;
443
+ Data_Get_Struct(self, Quaternion, q1);
444
+ Data_Get_Struct(other, Quaternion, q2);
445
+
446
+ float t = amount;
447
+ float t1 = 1.0f - t;
448
+ float dot = q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w;
449
+
450
+ if (dot >= 0.0f) {
451
+ q1->x = t1 * q1->x + t * q2->x;
452
+ q1->y = t1 * q1->y + t * q2->y;
453
+ q1->z = t1 * q1->z + t * q2->z;
454
+ q1->w = t1 * q1->w + t * q2->w;
455
+ } else {
456
+ q1->x = t1 * q1->x - t * q2->x;
457
+ q1->y = t1 * q1->y - t * q2->y;
458
+ q1->z = t1 * q1->z - t * q2->z;
459
+ q1->w = t1 * q1->w - t * q2->w;
460
+ }
461
+
462
+ // Normalize it.
463
+ float ls = q1->x * q1->x + q1->y * q1->y + q1->z * q1->z + q1->w * q1->w;
464
+ float invNorm = 1.0f / sqrtf(ls);
465
+
466
+ q1->x *= invNorm;
467
+ q1->y *= invNorm;
468
+ q1->z *= invNorm;
469
+ q1->w *= invNorm;
470
+
471
+ return self;
472
+ }
473
+
474
+ VALUE rb_quaternion_slerp(VALUE self, VALUE other, VALUE amount) {
475
+ Quaternion *q1, *q2, *result;
476
+ Data_Get_Struct(self, Quaternion, q1);
477
+ Data_Get_Struct(other, Quaternion, q2);
478
+
479
+ const float epsilon = 1e-6f;
480
+
481
+ float t = amount;
482
+ float cosOmega = q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w;
483
+ int flip = 0;
484
+ float s1, s2;
485
+
486
+ if (cosOmega < 0.0f) {
487
+ flip = 1;
488
+ cosOmega = -cosOmega;
489
+ }
490
+
491
+ if (cosOmega > (1.0f - epsilon)) {
492
+ // Too close, do straight linear interpolation.
493
+ s1 = 1.0f - t;
494
+ s2 = (flip) ? -t : t;
495
+ } else {
496
+ float omega = acosf(cosOmega);
497
+ float invSinOmega = 1.0f / sinf(omega);
498
+
499
+ s1 = sinf((1.0f - t) * omega) * invSinOmega;
500
+ s2 = (flip)
501
+ ? -sinf(t * omega) * invSinOmega
502
+ : sinf(t * omega) * invSinOmega;
503
+ }
504
+
505
+ result = ALLOC(Quaternion);
506
+ result->x = s1 * q1->x + s2 * q2->x;
507
+ result->y = s1 * q1->y + s2 * q2->y;
508
+ result->z = s1 * q1->z + s2 * q2->z;
509
+ result->w = s1 * q1->w + s2 * q2->w;
510
+
511
+ return NUMERIX_WRAP(CLASS_OF(self), result);
512
+ }
513
+
514
+ VALUE rb_quaternion_slerp_bang(VALUE self, VALUE other, VALUE amount) {
515
+ Quaternion *q1, *q2;
516
+ Data_Get_Struct(self, Quaternion, q1);
517
+ Data_Get_Struct(other, Quaternion, q2);
518
+
519
+ const float epsilon = 1e-6f;
520
+
521
+ float t = amount;
522
+ float cosOmega = q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w;
523
+ int flip = 0;
524
+ float s1, s2;
525
+
526
+ if (cosOmega < 0.0f) {
527
+ flip = 1;
528
+ cosOmega = -cosOmega;
529
+ }
530
+
531
+ if (cosOmega > (1.0f - epsilon)) {
532
+ // Too close, do straight linear interpolation.
533
+ s1 = 1.0f - t;
534
+ s2 = (flip) ? -t : t;
535
+ } else {
536
+ float omega = acosf(cosOmega);
537
+ float invSinOmega = 1.0f / sinf(omega);
538
+
539
+ s1 = sinf((1.0f - t) * omega) * invSinOmega;
540
+ s2 = (flip)
541
+ ? -sinf(t * omega) * invSinOmega
542
+ : sinf(t * omega) * invSinOmega;
543
+ }
544
+
545
+ q1->x = s1 * q1->x + s2 * q2->x;
546
+ q1->y = s1 * q1->y + s2 * q2->y;
547
+ q1->z = s1 * q1->z + s2 * q2->z;
548
+ q1->w = s1 * q1->w + s2 * q2->w;
549
+
550
+ return self;
551
+ }
552
+
553
+ VALUE rb_quaternion_from_axis_angle(VALUE klass, VALUE vec3, VALUE angle) {
554
+ Vector3 *axis;
555
+ Data_Get_Struct(vec3, Vector3, axis);
556
+ Quaternion *result = ALLOC(Quaternion);
557
+
558
+ float half = NUM2FLT(angle) * 0.5f;
559
+ float s = sinf(half);
560
+
561
+ result->x = axis->x * s;
562
+ result->y = axis->y * s;
563
+ result->z = axis->z * s;
564
+ result->w = cosf(half);
565
+
566
+ return NUMERIX_WRAP(klass, result);
567
+ }
568
+
569
+ VALUE rb_quaternion_from_yaw_pitch_roll(VALUE klass, VALUE yaw, VALUE pitch, VALUE roll) {
570
+ // Roll first, about axis the object is facing, then
571
+ // pitch upward, then yaw to face into the new heading
572
+
573
+ float sr, cr, sp, cp, sy, cy;
574
+
575
+ float halfRoll = roll * 0.5f;
576
+ sr = sinf(halfRoll);
577
+ cr = cosf(halfRoll);
578
+
579
+ float halfPitch = pitch * 0.5f;
580
+ sp = sinf(halfPitch);
581
+ cp = cosf(halfPitch);
582
+
583
+ float halfYaw = yaw * 0.5f;
584
+ sy = sinf(halfYaw);
585
+ cy = cosf(halfYaw);
586
+
587
+ Quaternion *result = ALLOC(Quaternion);
588
+
589
+ result->x = cy * sp * cr + sy * cp * sr;
590
+ result->y = sy * cp * cr - cy * sp * sr;
591
+ result->z = cy * cp * sr - sy * sp * cr;
592
+ result->w = cy * cp * cr + sy * sp * sr;
593
+
594
+ return NUMERIX_WRAP(klass, result);
595
+ }
596
+
597
+ VALUE rb_quaternion_from_rotation_matrix(VALUE klass, VALUE matrix) {
598
+ Matrix4x4 *m;
599
+ Data_Get_Struct(matrix, Matrix4x4, m);
600
+ Quaternion *q = ALLOC(Quaternion);
601
+
602
+ float trace = m->m11 + m->m22 + m->m33;
603
+ float s;
604
+ if (trace > 0.0f) {
605
+ s = sqrtf(trace + 1.0f);
606
+ q->w = s * 0.5f;
607
+ s = 0.5f / s;
608
+ q->x = (m->m23 - m->m32) * s;
609
+ q->y = (m->m31 - m->m13) * s;
610
+ q->z = (m->m12 - m->m21) * s;
611
+ } else {
612
+ float invS;
613
+ if (m->m11 >= m->m22 && m->m11 >= m->m33) {
614
+ s = sqrtf(1.0f + m->m11 - m->m22 - m->m33);
615
+ invS = 0.5f / s;
616
+ q->x = 0.5f * s;
617
+ q->y = (m->m12 + m->m21) * invS;
618
+ q->z = (m->m13 + m->m31) * invS;
619
+ q->w = (m->m23 - m->m32) * invS;
620
+ } else if (m->m22 > m->m33) {
621
+ s = sqrtf(1.0f + m->m22 - m->m11 - m->m33);
622
+ invS = 0.5f / s;
623
+ q->x = (m->m21 + m->m12) * invS;
624
+ q->y = 0.5f * s;
625
+ q->z = (m->m32 + m->m23) * invS;
626
+ q->w = (m->m31 - m->m13) * invS;
627
+ } else {
628
+ s = sqrtf(1.0f + m->m33 - m->m11 - m->m22);
629
+ invS = 0.5f / s;
630
+ q->x = (m->m31 + m->m13) * invS;
631
+ q->y = (m->m32 + m->m23) * invS;
632
+ q->z = 0.5f * s;
633
+ q->w = (m->m12 - m->m21) * invS;
634
+ }
635
+ }
636
+
637
+ return NUMERIX_WRAP(klass, q);
638
+ }
639
+
640
+ static inline VALUE rb_quaternion_slerp_s(VALUE klass, VALUE quaternion1, VALUE quaternion2, VALUE amount) {
641
+ Quaternion *q1, *q2, *result;
642
+ Data_Get_Struct(quaternion1, Quaternion, q1);
643
+ Data_Get_Struct(quaternion2, Quaternion, q2);
644
+
645
+ const float epsilon = 1e-6f;
646
+
647
+ float t = amount;
648
+ float cosOmega = q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w;
649
+ int flip = 0;
650
+ float s1, s2;
651
+
652
+ if (cosOmega < 0.0f) {
653
+ flip = 1;
654
+ cosOmega = -cosOmega;
655
+ }
656
+
657
+ if (cosOmega > (1.0f - epsilon)) {
658
+ // Too close, do straight linear interpolation.
659
+ s1 = 1.0f - t;
660
+ s2 = (flip) ? -t : t;
661
+ } else {
662
+ float omega = acosf(cosOmega);
663
+ float invSinOmega = 1.0f / sinf(omega);
664
+
665
+ s1 = sinf((1.0f - t) * omega) * invSinOmega;
666
+ s2 = (flip)
667
+ ? -sinf(t * omega) * invSinOmega
668
+ : sinf(t * omega) * invSinOmega;
669
+ }
670
+
671
+ result = ALLOC(Quaternion);
672
+ result->x = s1 * q1->x + s2 * q2->x;
673
+ result->y = s1 * q1->y + s2 * q2->y;
674
+ result->z = s1 * q1->z + s2 * q2->z;
675
+ result->w = s1 * q1->w + s2 * q2->w;
676
+
677
+ return NUMERIX_WRAP(klass, result);
678
+ }
679
+
680
+ static inline VALUE rb_quaternion_lerp_s(VALUE klass, VALUE quaternion1, VALUE quaternion2, VALUE amount) {
681
+ Quaternion *q1, *q2, *result;
682
+ Data_Get_Struct(quaternion1, Quaternion, q1);
683
+ Data_Get_Struct(quaternion2, Quaternion, q2);
684
+ result = ALLOC(Quaternion);
685
+
686
+ float t = amount;
687
+ float t1 = 1.0f - t;
688
+ float dot = q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w;
689
+
690
+ if (dot >= 0.0f) {
691
+ result->x = t1 * q1->x + t * q2->x;
692
+ result->y = t1 * q1->y + t * q2->y;
693
+ result->z = t1 * q1->z + t * q2->z;
694
+ result->w = t1 * q1->w + t * q2->w;
695
+ } else {
696
+ result->x = t1 * q1->x - t * q2->x;
697
+ result->y = t1 * q1->y - t * q2->y;
698
+ result->z = t1 * q1->z - t * q2->z;
699
+ result->w = t1 * q1->w - t * q2->w;
700
+ }
701
+
702
+ // Normalize it.
703
+ float ls = result->x * result->x + result->y * result->y + result->z * result->z + result->w * result->w;
704
+ float invNorm = 1.0f / sqrtf(ls);
705
+
706
+ result->x *= invNorm;
707
+ result->y *= invNorm;
708
+ result->z *= invNorm;
709
+ result->w *= invNorm;
710
+
711
+ return NUMERIX_WRAP(klass, result);
712
+ }