larb 1.0.0 → 1.0.1

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.
data/ext/larb/mat4.c CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "mat4.h"
2
+ #include "matrix_utils.h"
2
3
 
3
4
  #include <math.h>
4
5
 
@@ -23,11 +24,6 @@ static VALUE cVec3 = Qnil;
23
24
  static VALUE cVec4 = Qnil;
24
25
  static VALUE cQuat = Qnil;
25
26
 
26
- static double value_to_double(VALUE value) {
27
- VALUE coerced = rb_funcall(value, rb_intern("to_f"), 0);
28
- return NUM2DBL(coerced);
29
- }
30
-
31
27
  static Mat4Data *mat4_get(VALUE obj) {
32
28
  Mat4Data *data = NULL;
33
29
  TypedData_Get_Struct(obj, Mat4Data, &mat4_type, data);
@@ -53,10 +49,11 @@ static VALUE mat4_build16(VALUE klass, double v0, double v1, double v2,
53
49
  }
54
50
 
55
51
  static inline void vec3_normalize(double *x, double *y, double *z) {
56
- double len = sqrt((*x) * (*x) + (*y) * (*y) + (*z) * (*z));
57
- *x /= len;
58
- *y /= len;
59
- *z /= len;
52
+ double values[3] = {*x, *y, *z};
53
+ larb_normalize(values, 3);
54
+ *x = values[0];
55
+ *y = values[1];
56
+ *z = values[2];
60
57
  }
61
58
 
62
59
  static inline void vec3_cross(double ax, double ay, double az, double bx,
@@ -80,30 +77,34 @@ VALUE mat4_alloc(VALUE klass) {
80
77
  }
81
78
 
82
79
  VALUE mat4_initialize(int argc, VALUE *argv, VALUE self) {
80
+ rb_check_frozen(self);
83
81
  VALUE data_arg = Qnil;
84
- Mat4Data *data = mat4_get(self);
85
-
82
+ Mat4Data values = {{1.0, 0.0, 0.0, 0.0,
83
+ 0.0, 1.0, 0.0, 0.0,
84
+ 0.0, 0.0, 1.0, 0.0,
85
+ 0.0, 0.0, 0.0, 1.0}};
86
86
  rb_scan_args(argc, argv, "01", &data_arg);
87
- if (NIL_P(data_arg)) {
87
+ if (argc != 0) {
88
+ VALUE ary = rb_check_array_type(data_arg);
89
+ if (NIL_P(ary)) {
90
+ rb_raise(rb_eTypeError, "expected Array");
91
+ }
92
+ if (RARRAY_LEN(ary) != 16) {
93
+ rb_raise(rb_eArgError, "expected 16 elements");
94
+ }
88
95
  for (int i = 0; i < 16; i++) {
89
- data->data[i] = 0.0;
96
+ values.data[i] = NUM2DBL(rb_ary_entry(ary, i));
90
97
  }
91
- data->data[0] = 1.0;
92
- data->data[5] = 1.0;
93
- data->data[10] = 1.0;
94
- data->data[15] = 1.0;
95
- return self;
96
- }
97
-
98
- VALUE ary = rb_check_array_type(data_arg);
99
- if (NIL_P(ary)) {
100
- rb_raise(rb_eTypeError, "expected Array");
101
- }
102
-
103
- for (int i = 0; i < 16; i++) {
104
- data->data[i] = value_to_double(rb_ary_entry(ary, i));
105
98
  }
99
+ rb_check_frozen(self);
100
+ *mat4_get(self) = values;
101
+ return self;
102
+ }
106
103
 
104
+ static VALUE mat4_initialize_copy(VALUE self, VALUE other) {
105
+ if (self == other) return self;
106
+ rb_obj_init_copy(self, other);
107
+ *mat4_get(self) = *mat4_get(other);
107
108
  return self;
108
109
  }
109
110
 
@@ -125,15 +126,15 @@ static VALUE mat4_class_translation(VALUE klass, VALUE x, VALUE y, VALUE z) {
125
126
  }
126
127
 
127
128
  static VALUE mat4_class_scaling(VALUE klass, VALUE x, VALUE y, VALUE z) {
128
- double sx = value_to_double(x);
129
- double sy = value_to_double(y);
130
- double sz = value_to_double(z);
129
+ double sx = NUM2DBL(x);
130
+ double sy = NUM2DBL(y);
131
+ double sz = NUM2DBL(z);
131
132
  return mat4_build16(klass, sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0,
132
133
  sz, 0.0, 0.0, 0.0, 0.0, 1.0);
133
134
  }
134
135
 
135
136
  static VALUE mat4_class_rotation_x(VALUE klass, VALUE radians) {
136
- double r = value_to_double(radians);
137
+ double r = NUM2DBL(radians);
137
138
  double c = cos(r);
138
139
  double s = sin(r);
139
140
  return mat4_build16(klass, 1.0, 0.0, 0.0, 0.0, 0.0, c, s, 0.0, 0.0, -s, c,
@@ -141,7 +142,7 @@ static VALUE mat4_class_rotation_x(VALUE klass, VALUE radians) {
141
142
  }
142
143
 
143
144
  static VALUE mat4_class_rotation_y(VALUE klass, VALUE radians) {
144
- double r = value_to_double(radians);
145
+ double r = NUM2DBL(radians);
145
146
  double c = cos(r);
146
147
  double s = sin(r);
147
148
  return mat4_build16(klass, c, 0.0, -s, 0.0, 0.0, 1.0, 0.0, 0.0, s, 0.0, c,
@@ -149,7 +150,7 @@ static VALUE mat4_class_rotation_y(VALUE klass, VALUE radians) {
149
150
  }
150
151
 
151
152
  static VALUE mat4_class_rotation_z(VALUE klass, VALUE radians) {
152
- double r = value_to_double(radians);
153
+ double r = NUM2DBL(radians);
153
154
  double c = cos(r);
154
155
  double s = sin(r);
155
156
  return mat4_build16(klass, c, s, 0.0, 0.0, -s, c, 0.0, 0.0, 0.0, 0.0, 1.0,
@@ -158,10 +159,10 @@ static VALUE mat4_class_rotation_z(VALUE klass, VALUE radians) {
158
159
 
159
160
  static VALUE mat4_class_rotation(VALUE klass, VALUE axis, VALUE radians) {
160
161
  VALUE normalized = rb_funcall(axis, rb_intern("normalize"), 0);
161
- double x = value_to_double(rb_funcall(normalized, rb_intern("x"), 0));
162
- double y = value_to_double(rb_funcall(normalized, rb_intern("y"), 0));
163
- double z = value_to_double(rb_funcall(normalized, rb_intern("z"), 0));
164
- double r = value_to_double(radians);
162
+ double x = NUM2DBL(rb_funcall(normalized, rb_intern("x"), 0));
163
+ double y = NUM2DBL(rb_funcall(normalized, rb_intern("y"), 0));
164
+ double z = NUM2DBL(rb_funcall(normalized, rb_intern("z"), 0));
165
+ double r = NUM2DBL(radians);
165
166
  double c = cos(r);
166
167
  double s = sin(r);
167
168
  double t = 1.0 - c;
@@ -175,21 +176,22 @@ static VALUE mat4_class_rotation(VALUE klass, VALUE axis, VALUE radians) {
175
176
 
176
177
  static VALUE mat4_class_look_at(VALUE klass, VALUE eye, VALUE target,
177
178
  VALUE up) {
178
- double ex = value_to_double(rb_funcall(eye, rb_intern("x"), 0));
179
- double ey = value_to_double(rb_funcall(eye, rb_intern("y"), 0));
180
- double ez = value_to_double(rb_funcall(eye, rb_intern("z"), 0));
181
- double tx = value_to_double(rb_funcall(target, rb_intern("x"), 0));
182
- double ty = value_to_double(rb_funcall(target, rb_intern("y"), 0));
183
- double tz = value_to_double(rb_funcall(target, rb_intern("z"), 0));
184
- double ux = value_to_double(rb_funcall(up, rb_intern("x"), 0));
185
- double uy = value_to_double(rb_funcall(up, rb_intern("y"), 0));
186
- double uz = value_to_double(rb_funcall(up, rb_intern("z"), 0));
179
+ double ex = NUM2DBL(rb_funcall(eye, rb_intern("x"), 0));
180
+ double ey = NUM2DBL(rb_funcall(eye, rb_intern("y"), 0));
181
+ double ez = NUM2DBL(rb_funcall(eye, rb_intern("z"), 0));
182
+ double tx = NUM2DBL(rb_funcall(target, rb_intern("x"), 0));
183
+ double ty = NUM2DBL(rb_funcall(target, rb_intern("y"), 0));
184
+ double tz = NUM2DBL(rb_funcall(target, rb_intern("z"), 0));
185
+ double ux = NUM2DBL(rb_funcall(up, rb_intern("x"), 0));
186
+ double uy = NUM2DBL(rb_funcall(up, rb_intern("y"), 0));
187
+ double uz = NUM2DBL(rb_funcall(up, rb_intern("z"), 0));
187
188
 
188
189
  double fx = tx - ex;
189
190
  double fy = ty - ey;
190
191
  double fz = tz - ez;
191
192
  vec3_normalize(&fx, &fy, &fz);
192
193
 
194
+ vec3_normalize(&ux, &uy, &uz);
193
195
  double rx, ry, rz;
194
196
  vec3_cross(fx, fy, fz, ux, uy, uz, &rx, &ry, &rz);
195
197
  vec3_normalize(&rx, &ry, &rz);
@@ -205,11 +207,11 @@ static VALUE mat4_class_look_at(VALUE klass, VALUE eye, VALUE target,
205
207
 
206
208
  static VALUE mat4_class_perspective(VALUE klass, VALUE fov_y, VALUE aspect,
207
209
  VALUE near, VALUE far) {
208
- double f = 1.0 / tan(value_to_double(fov_y) / 2.0);
209
- double nf = 1.0 / (value_to_double(near) - value_to_double(far));
210
- double a = value_to_double(aspect);
211
- double n = value_to_double(near);
212
- double fr = value_to_double(far);
210
+ double f = 1.0 / tan(NUM2DBL(fov_y) / 2.0);
211
+ double nf = 1.0 / (NUM2DBL(near) - NUM2DBL(far));
212
+ double a = NUM2DBL(aspect);
213
+ double n = NUM2DBL(near);
214
+ double fr = NUM2DBL(far);
213
215
 
214
216
  return mat4_build16(klass, f / a, 0.0, 0.0, 0.0, 0.0, f, 0.0, 0.0, 0.0, 0.0,
215
217
  (fr + n) * nf, -1.0, 0.0, 0.0, 2.0 * fr * n * nf, 0.0);
@@ -218,15 +220,15 @@ static VALUE mat4_class_perspective(VALUE klass, VALUE fov_y, VALUE aspect,
218
220
  static VALUE mat4_class_orthographic(VALUE klass, VALUE left, VALUE right,
219
221
  VALUE bottom, VALUE top, VALUE near,
220
222
  VALUE far) {
221
- double rl = 1.0 / (value_to_double(right) - value_to_double(left));
222
- double tb = 1.0 / (value_to_double(top) - value_to_double(bottom));
223
- double fn = 1.0 / (value_to_double(far) - value_to_double(near));
224
- double r = value_to_double(right);
225
- double l = value_to_double(left);
226
- double t = value_to_double(top);
227
- double b = value_to_double(bottom);
228
- double f = value_to_double(far);
229
- double n = value_to_double(near);
223
+ double rl = 1.0 / (NUM2DBL(right) - NUM2DBL(left));
224
+ double tb = 1.0 / (NUM2DBL(top) - NUM2DBL(bottom));
225
+ double fn = 1.0 / (NUM2DBL(far) - NUM2DBL(near));
226
+ double r = NUM2DBL(right);
227
+ double l = NUM2DBL(left);
228
+ double t = NUM2DBL(top);
229
+ double b = NUM2DBL(bottom);
230
+ double f = NUM2DBL(far);
231
+ double n = NUM2DBL(near);
230
232
 
231
233
  return mat4_build16(klass, 2 * rl, 0.0, 0.0, 0.0, 0.0, 2 * tb, 0.0, 0.0,
232
234
  0.0, 0.0, -2 * fn, 0.0, -(r + l) * rl, -(t + b) * tb,
@@ -236,15 +238,15 @@ static VALUE mat4_class_orthographic(VALUE klass, VALUE left, VALUE right,
236
238
  static VALUE mat4_class_frustum(VALUE klass, VALUE left, VALUE right,
237
239
  VALUE bottom, VALUE top, VALUE near,
238
240
  VALUE far) {
239
- double rl = 1.0 / (value_to_double(right) - value_to_double(left));
240
- double tb = 1.0 / (value_to_double(top) - value_to_double(bottom));
241
- double nf = 1.0 / (value_to_double(near) - value_to_double(far));
242
- double r = value_to_double(right);
243
- double l = value_to_double(left);
244
- double t = value_to_double(top);
245
- double b = value_to_double(bottom);
246
- double n = value_to_double(near);
247
- double f = value_to_double(far);
241
+ double rl = 1.0 / (NUM2DBL(right) - NUM2DBL(left));
242
+ double tb = 1.0 / (NUM2DBL(top) - NUM2DBL(bottom));
243
+ double nf = 1.0 / (NUM2DBL(near) - NUM2DBL(far));
244
+ double r = NUM2DBL(right);
245
+ double l = NUM2DBL(left);
246
+ double t = NUM2DBL(top);
247
+ double b = NUM2DBL(bottom);
248
+ double n = NUM2DBL(near);
249
+ double f = NUM2DBL(far);
248
250
 
249
251
  return mat4_build16(klass, 2 * n * rl, 0.0, 0.0, 0.0, 0.0, 2 * n * tb, 0.0,
250
252
  0.0, (r + l) * rl, (t + b) * tb, (f + n) * nf, -1.0, 0.0,
@@ -252,10 +254,10 @@ static VALUE mat4_class_frustum(VALUE klass, VALUE left, VALUE right,
252
254
  }
253
255
 
254
256
  static VALUE mat4_class_from_quaternion(VALUE klass, VALUE quat) {
255
- double x = value_to_double(rb_funcall(quat, rb_intern("x"), 0));
256
- double y = value_to_double(rb_funcall(quat, rb_intern("y"), 0));
257
- double z = value_to_double(rb_funcall(quat, rb_intern("z"), 0));
258
- double w = value_to_double(rb_funcall(quat, rb_intern("w"), 0));
257
+ double x = NUM2DBL(rb_funcall(quat, rb_intern("x"), 0));
258
+ double y = NUM2DBL(rb_funcall(quat, rb_intern("y"), 0));
259
+ double z = NUM2DBL(rb_funcall(quat, rb_intern("z"), 0));
260
+ double w = NUM2DBL(rb_funcall(quat, rb_intern("w"), 0));
259
261
 
260
262
  double x2 = x + x;
261
263
  double y2 = y + y;
@@ -278,17 +280,17 @@ static VALUE mat4_class_from_quaternion(VALUE klass, VALUE quat) {
278
280
  static VALUE mat4_class_trs(VALUE klass, VALUE translation, VALUE rotation,
279
281
  VALUE scale) {
280
282
  VALUE rot = mat4_class_from_quaternion(klass, rotation);
281
- double sx = value_to_double(rb_funcall(scale, rb_intern("x"), 0));
282
- double sy = value_to_double(rb_funcall(scale, rb_intern("y"), 0));
283
- double sz = value_to_double(rb_funcall(scale, rb_intern("z"), 0));
283
+ double sx = NUM2DBL(rb_funcall(scale, rb_intern("x"), 0));
284
+ double sy = NUM2DBL(rb_funcall(scale, rb_intern("y"), 0));
285
+ double sz = NUM2DBL(rb_funcall(scale, rb_intern("z"), 0));
284
286
  VALUE scale_m = mat4_class_scaling(klass, DBL2NUM(sx), DBL2NUM(sy), DBL2NUM(sz));
285
- double tx = value_to_double(rb_funcall(translation, rb_intern("x"), 0));
286
- double ty = value_to_double(rb_funcall(translation, rb_intern("y"), 0));
287
- double tz = value_to_double(rb_funcall(translation, rb_intern("z"), 0));
287
+ double tx = NUM2DBL(rb_funcall(translation, rb_intern("x"), 0));
288
+ double ty = NUM2DBL(rb_funcall(translation, rb_intern("y"), 0));
289
+ double tz = NUM2DBL(rb_funcall(translation, rb_intern("z"), 0));
288
290
  VALUE trans_m =
289
291
  mat4_class_translation(klass, DBL2NUM(tx), DBL2NUM(ty), DBL2NUM(tz));
290
292
  VALUE tmp = mat4_mul(rot, scale_m);
291
- return mat4_mul(tmp, trans_m);
293
+ return mat4_mul(trans_m, tmp);
292
294
  }
293
295
 
294
296
  VALUE mat4_aref(VALUE self, VALUE index) {
@@ -301,12 +303,15 @@ VALUE mat4_aref(VALUE self, VALUE index) {
301
303
  }
302
304
 
303
305
  VALUE mat4_aset(VALUE self, VALUE index, VALUE value) {
306
+ rb_check_frozen(self);
304
307
  Mat4Data *data = mat4_get(self);
305
308
  long idx = NUM2LONG(index);
306
309
  if (idx < 0 || idx > 15) {
307
310
  rb_raise(rb_eIndexError, "index %ld out of range", idx);
308
311
  }
309
- data->data[idx] = value_to_double(value);
312
+ double component = NUM2DBL(value);
313
+ rb_check_frozen(self);
314
+ data->data[idx] = component;
310
315
  return value;
311
316
  }
312
317
 
@@ -342,10 +347,10 @@ VALUE mat4_mul(VALUE self, VALUE other) {
342
347
  }
343
348
 
344
349
  if (rb_obj_is_kind_of(other, cVec4)) {
345
- double x = value_to_double(rb_funcall(other, rb_intern("x"), 0));
346
- double y = value_to_double(rb_funcall(other, rb_intern("y"), 0));
347
- double z = value_to_double(rb_funcall(other, rb_intern("z"), 0));
348
- double w = value_to_double(rb_funcall(other, rb_intern("w"), 0));
350
+ double x = NUM2DBL(rb_funcall(other, rb_intern("x"), 0));
351
+ double y = NUM2DBL(rb_funcall(other, rb_intern("y"), 0));
352
+ double z = NUM2DBL(rb_funcall(other, rb_intern("z"), 0));
353
+ double w = NUM2DBL(rb_funcall(other, rb_intern("w"), 0));
349
354
  VALUE vec4_class = rb_const_get(mLarb, rb_intern("Vec4"));
350
355
  return rb_funcall(
351
356
  vec4_class, rb_intern("new"), 4,
@@ -364,7 +369,16 @@ VALUE mat4_mul(VALUE self, VALUE other) {
364
369
  return mat4_mul(self, vec4);
365
370
  }
366
371
 
367
- return Qnil;
372
+ if (rb_obj_is_kind_of(other, rb_cNumeric)) {
373
+ double scalar = NUM2DBL(other);
374
+ double values[16];
375
+ for (int i = 0; i < 16; i++) {
376
+ values[i] = a->data[i] * scalar;
377
+ }
378
+ return mat4_build(rb_obj_class(self), values);
379
+ }
380
+
381
+ rb_raise(rb_eTypeError, "unsupported operand for Mat4 multiplication");
368
382
  }
369
383
 
370
384
  VALUE mat4_transpose(VALUE self) {
@@ -377,72 +391,9 @@ VALUE mat4_transpose(VALUE self) {
377
391
  }
378
392
 
379
393
  VALUE mat4_inverse(VALUE self) {
380
- Mat4Data *a = mat4_get(self);
381
- const double *m = a->data;
382
- const double m0 = m[0];
383
- const double m1 = m[1];
384
- const double m2 = m[2];
385
- const double m3 = m[3];
386
- const double m4 = m[4];
387
- const double m5 = m[5];
388
- const double m6 = m[6];
389
- const double m7 = m[7];
390
- const double m8 = m[8];
391
- const double m9 = m[9];
392
- const double m10 = m[10];
393
- const double m11 = m[11];
394
- const double m12 = m[12];
395
- const double m13 = m[13];
396
- const double m14 = m[14];
397
- const double m15 = m[15];
398
- double inv[16];
399
-
400
- inv[0] = m5 * m10 * m15 - m5 * m11 * m14 - m9 * m6 * m15 +
401
- m9 * m7 * m14 + m13 * m6 * m11 - m13 * m7 * m10;
402
- inv[4] = -m4 * m10 * m15 + m4 * m11 * m14 + m8 * m6 * m15 -
403
- m8 * m7 * m14 - m12 * m6 * m11 + m12 * m7 * m10;
404
- inv[8] = m4 * m9 * m15 - m4 * m11 * m13 - m8 * m5 * m15 +
405
- m8 * m7 * m13 + m12 * m5 * m11 - m12 * m7 * m9;
406
- inv[12] = -m4 * m9 * m14 + m4 * m10 * m13 + m8 * m5 * m14 -
407
- m8 * m6 * m13 - m12 * m5 * m10 + m12 * m6 * m9;
408
-
409
- inv[1] = -m1 * m10 * m15 + m1 * m11 * m14 + m9 * m2 * m15 -
410
- m9 * m3 * m14 - m13 * m2 * m11 + m13 * m3 * m10;
411
- inv[5] = m0 * m10 * m15 - m0 * m11 * m14 - m8 * m2 * m15 +
412
- m8 * m3 * m14 + m12 * m2 * m11 - m12 * m3 * m10;
413
- inv[9] = -m0 * m9 * m15 + m0 * m11 * m13 + m8 * m1 * m15 -
414
- m8 * m3 * m13 - m12 * m1 * m11 + m12 * m3 * m9;
415
- inv[13] = m0 * m9 * m14 - m0 * m10 * m13 - m8 * m1 * m14 +
416
- m8 * m2 * m13 + m12 * m1 * m10 - m12 * m2 * m9;
417
-
418
- inv[2] = m1 * m6 * m15 - m1 * m7 * m14 - m5 * m2 * m15 +
419
- m5 * m3 * m14 + m13 * m2 * m7 - m13 * m3 * m6;
420
- inv[6] = -m0 * m6 * m15 + m0 * m7 * m14 + m4 * m2 * m15 -
421
- m4 * m3 * m14 - m12 * m2 * m7 + m12 * m3 * m6;
422
- inv[10] = m0 * m5 * m15 - m0 * m7 * m13 - m4 * m1 * m15 +
423
- m4 * m3 * m13 + m12 * m1 * m7 - m12 * m3 * m5;
424
- inv[14] = -m0 * m5 * m14 + m0 * m6 * m13 + m4 * m1 * m14 -
425
- m4 * m2 * m13 - m12 * m1 * m6 + m12 * m2 * m5;
426
-
427
- inv[3] = -m1 * m6 * m11 + m1 * m7 * m10 + m5 * m2 * m11 -
428
- m5 * m3 * m10 - m9 * m2 * m7 + m9 * m3 * m6;
429
- inv[7] = m0 * m6 * m11 - m0 * m7 * m10 - m4 * m2 * m11 +
430
- m4 * m3 * m10 + m8 * m2 * m7 - m8 * m3 * m6;
431
- inv[11] = -m0 * m5 * m11 + m0 * m7 * m9 + m4 * m1 * m11 -
432
- m4 * m3 * m9 - m8 * m1 * m7 + m8 * m3 * m5;
433
- inv[15] = m0 * m5 * m10 - m0 * m6 * m9 - m4 * m1 * m10 +
434
- m4 * m2 * m9 + m8 * m1 * m6 - m8 * m2 * m5;
435
-
436
- double det = m0 * inv[0] + m1 * inv[4] + m2 * inv[8] + m3 * inv[12];
437
- if (fabs(det) < 1e-10) {
438
- rb_raise(rb_eRuntimeError, "Matrix is not invertible");
439
- }
440
-
441
- det = 1.0 / det;
442
- for (int i = 0; i < 16; i++) {
443
- inv[i] *= det;
444
- }
445
- return mat4_build(rb_obj_class(self), inv);
394
+ double inverse[16];
395
+ larb_matrix_inverse(mat4_get(self)->data, inverse, 4);
396
+ return mat4_build(rb_obj_class(self), inverse);
446
397
  }
447
398
 
448
399
  VALUE mat4_to_a(VALUE self) {
@@ -522,10 +473,13 @@ VALUE mat4_near(int argc, VALUE *argv, VALUE self) {
522
473
  rb_scan_args(argc, argv, "11", &other, &epsilon);
523
474
  Mat4Data *a = mat4_get(self);
524
475
  Mat4Data *b = mat4_get(other);
525
- double eps = NIL_P(epsilon) ? 1e-6 : value_to_double(epsilon);
476
+ double eps = argc < 2 ? 1e-6 : NUM2DBL(epsilon);
477
+ if (!isfinite(eps) || eps <= 0.0) {
478
+ rb_raise(rb_eArgError, "epsilon must be finite and positive");
479
+ }
526
480
 
527
481
  for (int i = 0; i < 16; i++) {
528
- if (fabs(a->data[i] - b->data[i]) >= eps) {
482
+ if (!(fabs(a->data[i] - b->data[i]) < eps)) {
529
483
  return Qfalse;
530
484
  }
531
485
  }
@@ -539,63 +493,88 @@ VALUE mat4_extract_translation(VALUE self) {
539
493
  DBL2NUM(a->data[13]), DBL2NUM(a->data[14]));
540
494
  }
541
495
 
496
+ static void mat4_decompose(const Mat4Data *a, double *scale, double *rotation) {
497
+ for (int i = 0; i < 16; i++) {
498
+ if (!isfinite(a->data[i])) {
499
+ rb_raise(rb_eArgError, "Cannot decompose non-finite components");
500
+ }
501
+ }
502
+ if (a->data[3] != 0.0 || a->data[7] != 0.0 || a->data[11] != 0.0 ||
503
+ a->data[15] != 1.0) {
504
+ rb_raise(rb_eArgError, "Cannot decompose a perspective matrix");
505
+ }
506
+ for (int col = 0; col < 3; col++) {
507
+ const double *v = a->data + col * 4;
508
+ scale[col] = hypot(hypot(v[0], v[1]), v[2]);
509
+ if (scale[col] == 0.0 || !isfinite(scale[col])) {
510
+ rb_raise(rb_eArgError, "Cannot decompose zero or non-finite scale");
511
+ }
512
+ for (int row = 0; row < 3; row++) {
513
+ rotation[col * 3 + row] = v[row] / scale[col];
514
+ }
515
+ }
516
+ for (int col = 0; col < 3; col++) {
517
+ for (int other = col + 1; other < 3; other++) {
518
+ double dot = 0.0;
519
+ for (int row = 0; row < 3; row++) {
520
+ dot += rotation[col * 3 + row] * rotation[other * 3 + row];
521
+ }
522
+ if (fabs(dot) > 1e-6) {
523
+ rb_raise(rb_eArgError, "Cannot decompose shear");
524
+ }
525
+ }
526
+ }
527
+ double det = rotation[0] * (rotation[4] * rotation[8] - rotation[5] * rotation[7]) -
528
+ rotation[3] * (rotation[1] * rotation[8] - rotation[2] * rotation[7]) +
529
+ rotation[6] * (rotation[1] * rotation[5] - rotation[2] * rotation[4]);
530
+ if (det < 0.0) {
531
+ scale[0] = -scale[0];
532
+ for (int row = 0; row < 3; row++) rotation[row] = -rotation[row];
533
+ }
534
+ }
535
+
542
536
  VALUE mat4_extract_scale(VALUE self) {
543
- Mat4Data *a = mat4_get(self);
544
- double sx = sqrt(a->data[0] * a->data[0] + a->data[1] * a->data[1] +
545
- a->data[2] * a->data[2]);
546
- double sy = sqrt(a->data[4] * a->data[4] + a->data[5] * a->data[5] +
547
- a->data[6] * a->data[6]);
548
- double sz = sqrt(a->data[8] * a->data[8] + a->data[9] * a->data[9] +
549
- a->data[10] * a->data[10]);
550
- VALUE vec3_class = rb_const_get(mLarb, rb_intern("Vec3"));
551
- return rb_funcall(vec3_class, rb_intern("new"), 3, DBL2NUM(sx), DBL2NUM(sy),
552
- DBL2NUM(sz));
537
+ double scale[3], rotation[9];
538
+ mat4_decompose(mat4_get(self), scale, rotation);
539
+ return rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(scale[0]),
540
+ DBL2NUM(scale[1]), DBL2NUM(scale[2]));
553
541
  }
554
542
 
555
543
  VALUE mat4_extract_rotation(VALUE self) {
556
- Mat4Data *a = mat4_get(self);
557
- VALUE scale = mat4_extract_scale(self);
558
- double sx = value_to_double(rb_funcall(scale, rb_intern("x"), 0));
559
- double sy = value_to_double(rb_funcall(scale, rb_intern("y"), 0));
560
- double sz = value_to_double(rb_funcall(scale, rb_intern("z"), 0));
561
-
562
- double m00 = a->data[0] / sx;
563
- double m01 = a->data[1] / sx;
564
- double m02 = a->data[2] / sx;
565
- double m10 = a->data[4] / sy;
566
- double m11 = a->data[5] / sy;
567
- double m12 = a->data[6] / sy;
568
- double m20 = a->data[8] / sz;
569
- double m21 = a->data[9] / sz;
570
- double m22 = a->data[10] / sz;
571
-
544
+ double scale[3], rotation[9], q[4];
545
+ mat4_decompose(mat4_get(self), scale, rotation);
546
+ double m00 = rotation[0], m01 = rotation[1], m02 = rotation[2];
547
+ double m10 = rotation[3], m11 = rotation[4], m12 = rotation[5];
548
+ double m20 = rotation[6], m21 = rotation[7], m22 = rotation[8];
572
549
  double trace = m00 + m11 + m22;
573
550
  if (trace > 0.0) {
574
551
  double s = 0.5 / sqrt(trace + 1.0);
575
- return rb_funcall(cQuat, rb_intern("new"), 4,
576
- DBL2NUM((m12 - m21) * s),
577
- DBL2NUM((m20 - m02) * s),
578
- DBL2NUM((m01 - m10) * s), DBL2NUM(0.25 / s));
579
- }
580
- if (m00 > m11 && m00 > m22) {
552
+ q[0] = (m12 - m21) * s;
553
+ q[1] = (m20 - m02) * s;
554
+ q[2] = (m01 - m10) * s;
555
+ q[3] = 0.25 / s;
556
+ } else if (m00 > m11 && m00 > m22) {
581
557
  double s = 2.0 * sqrt(1.0 + m00 - m11 - m22);
582
- return rb_funcall(cQuat, rb_intern("new"), 4, DBL2NUM(0.25 * s),
583
- DBL2NUM((m10 + m01) / s),
584
- DBL2NUM((m20 + m02) / s),
585
- DBL2NUM((m12 - m21) / s));
586
- }
587
- if (m11 > m22) {
558
+ q[0] = 0.25 * s;
559
+ q[1] = (m10 + m01) / s;
560
+ q[2] = (m20 + m02) / s;
561
+ q[3] = (m12 - m21) / s;
562
+ } else if (m11 > m22) {
588
563
  double s = 2.0 * sqrt(1.0 + m11 - m00 - m22);
589
- return rb_funcall(cQuat, rb_intern("new"), 4,
590
- DBL2NUM((m10 + m01) / s), DBL2NUM(0.25 * s),
591
- DBL2NUM((m21 + m12) / s),
592
- DBL2NUM((m20 - m02) / s));
564
+ q[0] = (m10 + m01) / s;
565
+ q[1] = 0.25 * s;
566
+ q[2] = (m21 + m12) / s;
567
+ q[3] = (m20 - m02) / s;
568
+ } else {
569
+ double s = 2.0 * sqrt(1.0 + m22 - m00 - m11);
570
+ q[0] = (m20 + m02) / s;
571
+ q[1] = (m21 + m12) / s;
572
+ q[2] = 0.25 * s;
573
+ q[3] = (m01 - m10) / s;
593
574
  }
594
- double s = 2.0 * sqrt(1.0 + m22 - m00 - m11);
575
+ larb_normalize(q, 4);
595
576
  return rb_funcall(cQuat, rb_intern("new"), 4,
596
- DBL2NUM((m20 + m02) / s),
597
- DBL2NUM((m21 + m12) / s), DBL2NUM(0.25 * s),
598
- DBL2NUM((m01 - m10) / s));
577
+ DBL2NUM(q[0]), DBL2NUM(q[1]), DBL2NUM(q[2]), DBL2NUM(q[3]));
599
578
  }
600
579
 
601
580
  static VALUE mat4_format_value(double value) {
@@ -634,6 +613,7 @@ void Init_mat4(VALUE module) {
634
613
 
635
614
  rb_define_alloc_func(cMat4, mat4_alloc);
636
615
  rb_define_method(cMat4, "initialize", mat4_initialize, -1);
616
+ rb_define_method(cMat4, "initialize_copy", mat4_initialize_copy, 1);
637
617
 
638
618
  rb_define_singleton_method(cMat4, "identity", mat4_class_identity, 0);
639
619
  rb_define_singleton_method(cMat4, "zero", mat4_class_zero, 0);
@@ -0,0 +1,97 @@
1
+ #ifndef LARB_MATRIX_UTILS_H
2
+ #define LARB_MATRIX_UTILS_H
3
+
4
+ #include "larb.h"
5
+ #include <float.h>
6
+ #include <limits.h>
7
+ #include <math.h>
8
+
9
+ /* Column-major matrices of size 2 through 4. Combine row and column exponents
10
+ * before scaling, so neither intermediate division loses small components. */
11
+ static inline void larb_matrix_inverse(const double *values, double *inverse,
12
+ int size) {
13
+ double rows[4][8] = {{0.0}};
14
+ int row_exponents[4], column_exponents[4];
15
+ double row_scales[4] = {0.0};
16
+ for (int col = 0; col < size; col++) {
17
+ for (int row = 0; row < size; row++) {
18
+ double value = values[col * size + row];
19
+ if (!isfinite(value)) {
20
+ rb_raise(rb_eRuntimeError, "Matrix is not invertible");
21
+ }
22
+ row_scales[row] = fmax(row_scales[row], fabs(value));
23
+ }
24
+ }
25
+ for (int row = 0; row < size; row++) {
26
+ if (row_scales[row] == 0.0) {
27
+ rb_raise(rb_eRuntimeError, "Matrix is not invertible");
28
+ }
29
+ row_exponents[row] = ilogb(row_scales[row]);
30
+ row_scales[row] = 0.0;
31
+ }
32
+ for (int col = 0; col < size; col++) {
33
+ int exponent = INT_MIN;
34
+ for (int row = 0; row < size; row++) {
35
+ double value = values[col * size + row];
36
+ if (value != 0.0) {
37
+ int adjusted = ilogb(fabs(value)) - row_exponents[row];
38
+ if (adjusted > exponent) exponent = adjusted;
39
+ }
40
+ }
41
+ if (exponent == INT_MIN) {
42
+ rb_raise(rb_eRuntimeError, "Matrix is not invertible");
43
+ }
44
+ column_exponents[col] = exponent;
45
+ for (int row = 0; row < size; row++) {
46
+ rows[row][col] = scalbn(values[col * size + row],
47
+ -(row_exponents[row] + exponent));
48
+ row_scales[row] = fmax(row_scales[row], fabs(rows[row][col]));
49
+ }
50
+ rows[col][size + col] = 1.0;
51
+ }
52
+
53
+ for (int col = 0; col < size; col++) {
54
+ int pivot = col;
55
+ for (int row = col + 1; row < size; row++) {
56
+ if (fabs(rows[row][col]) / row_scales[row] >
57
+ fabs(rows[pivot][col]) / row_scales[pivot]) {
58
+ pivot = row;
59
+ }
60
+ }
61
+ if (fabs(rows[pivot][col]) / row_scales[pivot] <= size * DBL_EPSILON) {
62
+ rb_raise(rb_eRuntimeError, "Matrix is not invertible");
63
+ }
64
+ double row_scale = row_scales[col];
65
+ row_scales[col] = row_scales[pivot];
66
+ row_scales[pivot] = row_scale;
67
+ for (int i = 0; i < size * 2; i++) {
68
+ double tmp = rows[col][i];
69
+ rows[col][i] = rows[pivot][i];
70
+ rows[pivot][i] = tmp;
71
+ }
72
+ double divisor = rows[col][col];
73
+ for (int i = 0; i < size * 2; i++) {
74
+ rows[col][i] /= divisor;
75
+ }
76
+ for (int row = 0; row < size; row++) {
77
+ if (row == col) continue;
78
+ double factor = rows[row][col];
79
+ for (int i = 0; i < size * 2; i++) {
80
+ rows[row][i] -= factor * rows[col][i];
81
+ }
82
+ }
83
+ }
84
+
85
+ for (int col = 0; col < size; col++) {
86
+ for (int row = 0; row < size; row++) {
87
+ double value = scalbn(rows[row][size + col],
88
+ -(column_exponents[row] + row_exponents[col]));
89
+ if (!isfinite(value)) {
90
+ rb_raise(rb_eRuntimeError, "Matrix is not invertible");
91
+ }
92
+ inverse[col * size + row] = value;
93
+ }
94
+ }
95
+ }
96
+
97
+ #endif