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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +49 -0
- data/ext/larb/color.c +85 -82
- data/ext/larb/extconf.rb +1 -1
- data/ext/larb/larb.h +23 -0
- data/ext/larb/mat2.c +50 -47
- data/ext/larb/mat2d.c +84 -67
- data/ext/larb/mat3.c +73 -87
- data/ext/larb/mat4.c +180 -200
- data/ext/larb/matrix_utils.h +97 -0
- data/ext/larb/quat.c +110 -66
- data/ext/larb/quat2.c +123 -85
- data/ext/larb/vec2.c +52 -29
- data/ext/larb/vec3.c +106 -71
- data/ext/larb/vec4.c +55 -34
- data/lib/larb/version.rb +1 -1
- data/test/larb/color_test.rb +21 -0
- data/test/larb/mat2d_test.rb +25 -0
- data/test/larb/mat4_test.rb +77 -0
- data/test/larb/matrix_test.rb +135 -0
- data/test/larb/native_value_test.rb +157 -0
- data/test/larb/quat2_test.rb +93 -0
- data/test/larb/quat_test.rb +60 -0
- data/test/larb/vec2_test.rb +23 -0
- data/test/larb/vec3_test.rb +63 -0
- data/test/larb/vec4_test.rb +8 -1
- metadata +8 -5
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
|
|
57
|
-
|
|
58
|
-
*
|
|
59
|
-
*
|
|
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
|
|
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 (
|
|
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
|
|
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 =
|
|
129
|
-
double sy =
|
|
130
|
-
double sz =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
162
|
-
double y =
|
|
163
|
-
double z =
|
|
164
|
-
double r =
|
|
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 =
|
|
179
|
-
double ey =
|
|
180
|
-
double ez =
|
|
181
|
-
double tx =
|
|
182
|
-
double ty =
|
|
183
|
-
double tz =
|
|
184
|
-
double ux =
|
|
185
|
-
double uy =
|
|
186
|
-
double uz =
|
|
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(
|
|
209
|
-
double nf = 1.0 / (
|
|
210
|
-
double a =
|
|
211
|
-
double n =
|
|
212
|
-
double fr =
|
|
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 / (
|
|
222
|
-
double tb = 1.0 / (
|
|
223
|
-
double fn = 1.0 / (
|
|
224
|
-
double r =
|
|
225
|
-
double l =
|
|
226
|
-
double t =
|
|
227
|
-
double b =
|
|
228
|
-
double f =
|
|
229
|
-
double n =
|
|
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 / (
|
|
240
|
-
double tb = 1.0 / (
|
|
241
|
-
double nf = 1.0 / (
|
|
242
|
-
double r =
|
|
243
|
-
double l =
|
|
244
|
-
double t =
|
|
245
|
-
double b =
|
|
246
|
-
double n =
|
|
247
|
-
double f =
|
|
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 =
|
|
256
|
-
double y =
|
|
257
|
-
double z =
|
|
258
|
-
double w =
|
|
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 =
|
|
282
|
-
double sy =
|
|
283
|
-
double sz =
|
|
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 =
|
|
286
|
-
double ty =
|
|
287
|
-
double tz =
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
346
|
-
double y =
|
|
347
|
-
double z =
|
|
348
|
-
double w =
|
|
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
|
-
|
|
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
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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 =
|
|
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])
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
557
|
-
|
|
558
|
-
double
|
|
559
|
-
double
|
|
560
|
-
double
|
|
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
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
575
|
+
larb_normalize(q, 4);
|
|
595
576
|
return rb_funcall(cQuat, rb_intern("new"), 4,
|
|
596
|
-
DBL2NUM((
|
|
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
|