larb 0.1.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -1
  3. data/README.md +63 -1
  4. data/ext/larb/color.c +449 -0
  5. data/ext/larb/color.h +35 -0
  6. data/ext/larb/extconf.rb +11 -0
  7. data/ext/larb/larb.c +27 -0
  8. data/ext/larb/larb.h +31 -0
  9. data/ext/larb/mat2.c +303 -0
  10. data/ext/larb/mat2.h +30 -0
  11. data/ext/larb/mat2d.c +397 -0
  12. data/ext/larb/mat2d.h +35 -0
  13. data/ext/larb/mat3.c +455 -0
  14. data/ext/larb/mat3.h +33 -0
  15. data/ext/larb/mat4.c +651 -0
  16. data/ext/larb/mat4.h +31 -0
  17. data/ext/larb/matrix_utils.h +97 -0
  18. data/ext/larb/quat.c +567 -0
  19. data/ext/larb/quat.h +39 -0
  20. data/ext/larb/quat2.c +511 -0
  21. data/ext/larb/quat2.h +39 -0
  22. data/ext/larb/vec2.c +365 -0
  23. data/ext/larb/vec2.h +43 -0
  24. data/ext/larb/vec3.c +538 -0
  25. data/ext/larb/vec3.h +52 -0
  26. data/ext/larb/vec4.c +361 -0
  27. data/ext/larb/vec4.h +38 -0
  28. data/lib/larb/version.rb +5 -0
  29. data/lib/larb.rb +2 -14
  30. data/test/larb/color_test.rb +299 -0
  31. data/test/larb/mat2_test.rb +144 -0
  32. data/test/larb/mat2d_test.rb +197 -0
  33. data/test/larb/mat3_test.rb +147 -0
  34. data/test/larb/mat4_test.rb +347 -0
  35. data/test/larb/matrix_test.rb +135 -0
  36. data/test/larb/native_value_test.rb +157 -0
  37. data/test/larb/quat2_test.rb +254 -0
  38. data/test/larb/quat_test.rb +284 -0
  39. data/test/larb/vec2_test.rb +274 -0
  40. data/test/larb/vec3_test.rb +373 -0
  41. data/test/larb/vec4_test.rb +196 -0
  42. data/test/test_helper.rb +4 -0
  43. metadata +60 -18
  44. data/Rakefile +0 -11
  45. data/lib/larb/color.rb +0 -148
  46. data/lib/larb/mat2.rb +0 -119
  47. data/lib/larb/mat2d.rb +0 -180
  48. data/lib/larb/mat3.rb +0 -238
  49. data/lib/larb/mat4.rb +0 -329
  50. data/lib/larb/quat.rb +0 -238
  51. data/lib/larb/quat2.rb +0 -193
  52. data/lib/larb/vec2.rb +0 -150
  53. data/lib/larb/vec3.rb +0 -218
  54. data/lib/larb/vec4.rb +0 -125
data/ext/larb/mat4.c ADDED
@@ -0,0 +1,651 @@
1
+ #include "mat4.h"
2
+ #include "matrix_utils.h"
3
+
4
+ #include <math.h>
5
+
6
+ static void mat4_free(void *ptr) {
7
+ xfree(ptr);
8
+ }
9
+
10
+ static size_t mat4_memsize(const void *ptr) {
11
+ return sizeof(Mat4Data);
12
+ }
13
+
14
+ static const rb_data_type_t mat4_type = {
15
+ "Mat4",
16
+ {0, mat4_free, mat4_memsize},
17
+ 0,
18
+ 0,
19
+ RUBY_TYPED_FREE_IMMEDIATELY,
20
+ };
21
+
22
+ static VALUE cMat4 = Qnil;
23
+ static VALUE cVec3 = Qnil;
24
+ static VALUE cVec4 = Qnil;
25
+ static VALUE cQuat = Qnil;
26
+
27
+ static Mat4Data *mat4_get(VALUE obj) {
28
+ Mat4Data *data = NULL;
29
+ TypedData_Get_Struct(obj, Mat4Data, &mat4_type, data);
30
+ return data;
31
+ }
32
+
33
+ static VALUE mat4_build(VALUE klass, const double *values) {
34
+ VALUE obj = mat4_alloc(klass);
35
+ Mat4Data *data = mat4_get(obj);
36
+ for (int i = 0; i < 16; i++) {
37
+ data->data[i] = values[i];
38
+ }
39
+ return obj;
40
+ }
41
+
42
+ static VALUE mat4_build16(VALUE klass, double v0, double v1, double v2,
43
+ double v3, double v4, double v5, double v6, double v7,
44
+ double v8, double v9, double v10, double v11,
45
+ double v12, double v13, double v14, double v15) {
46
+ double values[16] = {v0, v1, v2, v3, v4, v5, v6, v7,
47
+ v8, v9, v10, v11, v12, v13, v14, v15};
48
+ return mat4_build(klass, values);
49
+ }
50
+
51
+ static inline void vec3_normalize(double *x, double *y, double *z) {
52
+ double values[3] = {*x, *y, *z};
53
+ larb_normalize(values, 3);
54
+ *x = values[0];
55
+ *y = values[1];
56
+ *z = values[2];
57
+ }
58
+
59
+ static inline void vec3_cross(double ax, double ay, double az, double bx,
60
+ double by, double bz, double *rx, double *ry,
61
+ double *rz) {
62
+ *rx = ay * bz - az * by;
63
+ *ry = az * bx - ax * bz;
64
+ *rz = ax * by - ay * bx;
65
+ }
66
+
67
+ VALUE mat4_alloc(VALUE klass) {
68
+ Mat4Data *data = ALLOC(Mat4Data);
69
+ for (int i = 0; i < 16; i++) {
70
+ data->data[i] = 0.0;
71
+ }
72
+ data->data[0] = 1.0;
73
+ data->data[5] = 1.0;
74
+ data->data[10] = 1.0;
75
+ data->data[15] = 1.0;
76
+ return TypedData_Wrap_Struct(klass, &mat4_type, data);
77
+ }
78
+
79
+ VALUE mat4_initialize(int argc, VALUE *argv, VALUE self) {
80
+ rb_check_frozen(self);
81
+ VALUE data_arg = Qnil;
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
+ rb_scan_args(argc, argv, "01", &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
+ }
95
+ for (int i = 0; i < 16; i++) {
96
+ values.data[i] = NUM2DBL(rb_ary_entry(ary, i));
97
+ }
98
+ }
99
+ rb_check_frozen(self);
100
+ *mat4_get(self) = values;
101
+ return self;
102
+ }
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);
108
+ return self;
109
+ }
110
+
111
+ static VALUE mat4_class_identity(VALUE klass) {
112
+ return mat4_alloc(klass);
113
+ }
114
+
115
+ static VALUE mat4_class_zero(VALUE klass) {
116
+ return mat4_build16(klass, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
117
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
118
+ }
119
+
120
+ static VALUE mat4_class_translation(VALUE klass, VALUE x, VALUE y, VALUE z) {
121
+ VALUE m = mat4_class_identity(klass);
122
+ mat4_aset(m, INT2NUM(12), x);
123
+ mat4_aset(m, INT2NUM(13), y);
124
+ mat4_aset(m, INT2NUM(14), z);
125
+ return m;
126
+ }
127
+
128
+ static VALUE mat4_class_scaling(VALUE klass, VALUE x, VALUE y, VALUE z) {
129
+ double sx = NUM2DBL(x);
130
+ double sy = NUM2DBL(y);
131
+ double sz = NUM2DBL(z);
132
+ return mat4_build16(klass, sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0,
133
+ sz, 0.0, 0.0, 0.0, 0.0, 1.0);
134
+ }
135
+
136
+ static VALUE mat4_class_rotation_x(VALUE klass, VALUE radians) {
137
+ double r = NUM2DBL(radians);
138
+ double c = cos(r);
139
+ double s = sin(r);
140
+ return mat4_build16(klass, 1.0, 0.0, 0.0, 0.0, 0.0, c, s, 0.0, 0.0, -s, c,
141
+ 0.0, 0.0, 0.0, 0.0, 1.0);
142
+ }
143
+
144
+ static VALUE mat4_class_rotation_y(VALUE klass, VALUE radians) {
145
+ double r = NUM2DBL(radians);
146
+ double c = cos(r);
147
+ double s = sin(r);
148
+ return mat4_build16(klass, c, 0.0, -s, 0.0, 0.0, 1.0, 0.0, 0.0, s, 0.0, c,
149
+ 0.0, 0.0, 0.0, 0.0, 1.0);
150
+ }
151
+
152
+ static VALUE mat4_class_rotation_z(VALUE klass, VALUE radians) {
153
+ double r = NUM2DBL(radians);
154
+ double c = cos(r);
155
+ double s = sin(r);
156
+ return mat4_build16(klass, c, s, 0.0, 0.0, -s, c, 0.0, 0.0, 0.0, 0.0, 1.0,
157
+ 0.0, 0.0, 0.0, 0.0, 1.0);
158
+ }
159
+
160
+ static VALUE mat4_class_rotation(VALUE klass, VALUE axis, VALUE radians) {
161
+ VALUE normalized = rb_funcall(axis, rb_intern("normalize"), 0);
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);
166
+ double c = cos(r);
167
+ double s = sin(r);
168
+ double t = 1.0 - c;
169
+
170
+ return mat4_build16(klass, t * x * x + c, t * x * y + s * z,
171
+ t * x * z - s * y, 0.0, t * x * y - s * z,
172
+ t * y * y + c, t * y * z + s * x, 0.0,
173
+ t * x * z + s * y, t * y * z - s * x,
174
+ t * z * z + c, 0.0, 0.0, 0.0, 0.0, 1.0);
175
+ }
176
+
177
+ static VALUE mat4_class_look_at(VALUE klass, VALUE eye, VALUE target,
178
+ VALUE up) {
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));
188
+
189
+ double fx = tx - ex;
190
+ double fy = ty - ey;
191
+ double fz = tz - ez;
192
+ vec3_normalize(&fx, &fy, &fz);
193
+
194
+ vec3_normalize(&ux, &uy, &uz);
195
+ double rx, ry, rz;
196
+ vec3_cross(fx, fy, fz, ux, uy, uz, &rx, &ry, &rz);
197
+ vec3_normalize(&rx, &ry, &rz);
198
+
199
+ double ux2, uy2, uz2;
200
+ vec3_cross(rx, ry, rz, fx, fy, fz, &ux2, &uy2, &uz2);
201
+
202
+ return mat4_build16(klass, rx, ux2, -fx, 0.0, ry, uy2, -fy, 0.0, rz, uz2,
203
+ -fz, 0.0, -(rx * ex + ry * ey + rz * ez),
204
+ -(ux2 * ex + uy2 * ey + uz2 * ez),
205
+ (fx * ex + fy * ey + fz * ez), 1.0);
206
+ }
207
+
208
+ static VALUE mat4_class_perspective(VALUE klass, VALUE fov_y, VALUE aspect,
209
+ VALUE near, VALUE 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);
215
+
216
+ return mat4_build16(klass, f / a, 0.0, 0.0, 0.0, 0.0, f, 0.0, 0.0, 0.0, 0.0,
217
+ (fr + n) * nf, -1.0, 0.0, 0.0, 2.0 * fr * n * nf, 0.0);
218
+ }
219
+
220
+ static VALUE mat4_class_orthographic(VALUE klass, VALUE left, VALUE right,
221
+ VALUE bottom, VALUE top, VALUE near,
222
+ VALUE far) {
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);
232
+
233
+ return mat4_build16(klass, 2 * rl, 0.0, 0.0, 0.0, 0.0, 2 * tb, 0.0, 0.0,
234
+ 0.0, 0.0, -2 * fn, 0.0, -(r + l) * rl, -(t + b) * tb,
235
+ -(f + n) * fn, 1.0);
236
+ }
237
+
238
+ static VALUE mat4_class_frustum(VALUE klass, VALUE left, VALUE right,
239
+ VALUE bottom, VALUE top, VALUE near,
240
+ VALUE 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);
250
+
251
+ return mat4_build16(klass, 2 * n * rl, 0.0, 0.0, 0.0, 0.0, 2 * n * tb, 0.0,
252
+ 0.0, (r + l) * rl, (t + b) * tb, (f + n) * nf, -1.0, 0.0,
253
+ 0.0, 2 * f * n * nf, 0.0);
254
+ }
255
+
256
+ static VALUE mat4_class_from_quaternion(VALUE klass, VALUE quat) {
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));
261
+
262
+ double x2 = x + x;
263
+ double y2 = y + y;
264
+ double z2 = z + z;
265
+ double xx = x * x2;
266
+ double xy = x * y2;
267
+ double xz = x * z2;
268
+ double yy = y * y2;
269
+ double yz = y * z2;
270
+ double zz = z * z2;
271
+ double wx = w * x2;
272
+ double wy = w * y2;
273
+ double wz = w * z2;
274
+
275
+ return mat4_build16(klass, 1 - (yy + zz), xy + wz, xz - wy, 0.0, xy - wz,
276
+ 1 - (xx + zz), yz + wx, 0.0, xz + wy, yz - wx,
277
+ 1 - (xx + yy), 0.0, 0.0, 0.0, 0.0, 1.0);
278
+ }
279
+
280
+ static VALUE mat4_class_trs(VALUE klass, VALUE translation, VALUE rotation,
281
+ VALUE scale) {
282
+ VALUE rot = mat4_class_from_quaternion(klass, rotation);
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));
286
+ VALUE scale_m = mat4_class_scaling(klass, DBL2NUM(sx), DBL2NUM(sy), DBL2NUM(sz));
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));
290
+ VALUE trans_m =
291
+ mat4_class_translation(klass, DBL2NUM(tx), DBL2NUM(ty), DBL2NUM(tz));
292
+ VALUE tmp = mat4_mul(rot, scale_m);
293
+ return mat4_mul(trans_m, tmp);
294
+ }
295
+
296
+ VALUE mat4_aref(VALUE self, VALUE index) {
297
+ Mat4Data *data = mat4_get(self);
298
+ long idx = NUM2LONG(index);
299
+ if (idx < 0 || idx > 15) {
300
+ return Qnil;
301
+ }
302
+ return DBL2NUM(data->data[idx]);
303
+ }
304
+
305
+ VALUE mat4_aset(VALUE self, VALUE index, VALUE value) {
306
+ rb_check_frozen(self);
307
+ Mat4Data *data = mat4_get(self);
308
+ long idx = NUM2LONG(index);
309
+ if (idx < 0 || idx > 15) {
310
+ rb_raise(rb_eIndexError, "index %ld out of range", idx);
311
+ }
312
+ double component = NUM2DBL(value);
313
+ rb_check_frozen(self);
314
+ data->data[idx] = component;
315
+ return value;
316
+ }
317
+
318
+ VALUE mat4_mul(VALUE self, VALUE other) {
319
+ Mat4Data *a = mat4_get(self);
320
+
321
+ if (rb_obj_is_kind_of(other, cMat4)) {
322
+ Mat4Data *b = mat4_get(other);
323
+ double result[16];
324
+ const double *ad = a->data;
325
+ const double *bd = b->data;
326
+
327
+ result[0] = ad[0] * bd[0] + ad[4] * bd[1] + ad[8] * bd[2] + ad[12] * bd[3];
328
+ result[1] = ad[1] * bd[0] + ad[5] * bd[1] + ad[9] * bd[2] + ad[13] * bd[3];
329
+ result[2] = ad[2] * bd[0] + ad[6] * bd[1] + ad[10] * bd[2] + ad[14] * bd[3];
330
+ result[3] = ad[3] * bd[0] + ad[7] * bd[1] + ad[11] * bd[2] + ad[15] * bd[3];
331
+
332
+ result[4] = ad[0] * bd[4] + ad[4] * bd[5] + ad[8] * bd[6] + ad[12] * bd[7];
333
+ result[5] = ad[1] * bd[4] + ad[5] * bd[5] + ad[9] * bd[6] + ad[13] * bd[7];
334
+ result[6] = ad[2] * bd[4] + ad[6] * bd[5] + ad[10] * bd[6] + ad[14] * bd[7];
335
+ result[7] = ad[3] * bd[4] + ad[7] * bd[5] + ad[11] * bd[6] + ad[15] * bd[7];
336
+
337
+ result[8] = ad[0] * bd[8] + ad[4] * bd[9] + ad[8] * bd[10] + ad[12] * bd[11];
338
+ result[9] = ad[1] * bd[8] + ad[5] * bd[9] + ad[9] * bd[10] + ad[13] * bd[11];
339
+ result[10] = ad[2] * bd[8] + ad[6] * bd[9] + ad[10] * bd[10] + ad[14] * bd[11];
340
+ result[11] = ad[3] * bd[8] + ad[7] * bd[9] + ad[11] * bd[10] + ad[15] * bd[11];
341
+
342
+ result[12] = ad[0] * bd[12] + ad[4] * bd[13] + ad[8] * bd[14] + ad[12] * bd[15];
343
+ result[13] = ad[1] * bd[12] + ad[5] * bd[13] + ad[9] * bd[14] + ad[13] * bd[15];
344
+ result[14] = ad[2] * bd[12] + ad[6] * bd[13] + ad[10] * bd[14] + ad[14] * bd[15];
345
+ result[15] = ad[3] * bd[12] + ad[7] * bd[13] + ad[11] * bd[14] + ad[15] * bd[15];
346
+ return mat4_build(rb_obj_class(self), result);
347
+ }
348
+
349
+ if (rb_obj_is_kind_of(other, cVec4)) {
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));
354
+ VALUE vec4_class = rb_const_get(mLarb, rb_intern("Vec4"));
355
+ return rb_funcall(
356
+ vec4_class, rb_intern("new"), 4,
357
+ DBL2NUM(a->data[0] * x + a->data[4] * y + a->data[8] * z +
358
+ a->data[12] * w),
359
+ DBL2NUM(a->data[1] * x + a->data[5] * y + a->data[9] * z +
360
+ a->data[13] * w),
361
+ DBL2NUM(a->data[2] * x + a->data[6] * y + a->data[10] * z +
362
+ a->data[14] * w),
363
+ DBL2NUM(a->data[3] * x + a->data[7] * y + a->data[11] * z +
364
+ a->data[15] * w));
365
+ }
366
+
367
+ if (rb_obj_is_kind_of(other, cVec3)) {
368
+ VALUE vec4 = rb_funcall(other, rb_intern("to_vec4"), 0);
369
+ return mat4_mul(self, vec4);
370
+ }
371
+
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");
382
+ }
383
+
384
+ VALUE mat4_transpose(VALUE self) {
385
+ Mat4Data *a = mat4_get(self);
386
+ return mat4_build16(rb_obj_class(self), a->data[0], a->data[4], a->data[8],
387
+ a->data[12], a->data[1], a->data[5], a->data[9],
388
+ a->data[13], a->data[2], a->data[6], a->data[10],
389
+ a->data[14], a->data[3], a->data[7], a->data[11],
390
+ a->data[15]);
391
+ }
392
+
393
+ VALUE mat4_inverse(VALUE self) {
394
+ double inverse[16];
395
+ larb_matrix_inverse(mat4_get(self)->data, inverse, 4);
396
+ return mat4_build(rb_obj_class(self), inverse);
397
+ }
398
+
399
+ VALUE mat4_to_a(VALUE self) {
400
+ Mat4Data *a = mat4_get(self);
401
+ VALUE ary = rb_ary_new_capa(16);
402
+ for (int i = 0; i < 16; i++) {
403
+ rb_ary_push(ary, DBL2NUM(a->data[i]));
404
+ }
405
+ return ary;
406
+ }
407
+
408
+ VALUE mat4_data(VALUE self) {
409
+ return mat4_to_a(self);
410
+ }
411
+
412
+ VALUE mat4_determinant(VALUE self) {
413
+ Mat4Data *a = mat4_get(self);
414
+ double *m = a->data;
415
+ double det =
416
+ m[0] *
417
+ (m[5] * (m[10] * m[15] - m[11] * m[14]) -
418
+ m[9] * (m[6] * m[15] - m[7] * m[14]) +
419
+ m[13] * (m[6] * m[11] - m[7] * m[10])) -
420
+ m[4] *
421
+ (m[1] * (m[10] * m[15] - m[11] * m[14]) -
422
+ m[9] * (m[2] * m[15] - m[3] * m[14]) +
423
+ m[13] * (m[2] * m[11] - m[3] * m[10])) +
424
+ m[8] *
425
+ (m[1] * (m[6] * m[15] - m[7] * m[14]) -
426
+ m[5] * (m[2] * m[15] - m[3] * m[14]) +
427
+ m[13] * (m[2] * m[7] - m[3] * m[6])) -
428
+ m[12] *
429
+ (m[1] * (m[6] * m[11] - m[7] * m[10]) -
430
+ m[5] * (m[2] * m[11] - m[3] * m[10]) +
431
+ m[9] * (m[2] * m[7] - m[3] * m[6]));
432
+ return DBL2NUM(det);
433
+ }
434
+
435
+ VALUE mat4_add(VALUE self, VALUE other) {
436
+ Mat4Data *a = mat4_get(self);
437
+ Mat4Data *b = mat4_get(other);
438
+ double values[16];
439
+ for (int i = 0; i < 16; i++) {
440
+ values[i] = a->data[i] + b->data[i];
441
+ }
442
+ return mat4_build(rb_obj_class(self), values);
443
+ }
444
+
445
+ VALUE mat4_sub(VALUE self, VALUE other) {
446
+ Mat4Data *a = mat4_get(self);
447
+ Mat4Data *b = mat4_get(other);
448
+ double values[16];
449
+ for (int i = 0; i < 16; i++) {
450
+ values[i] = a->data[i] - b->data[i];
451
+ }
452
+ return mat4_build(rb_obj_class(self), values);
453
+ }
454
+
455
+ VALUE mat4_equal(VALUE self, VALUE other) {
456
+ if (!rb_obj_is_kind_of(other, cMat4)) {
457
+ return Qfalse;
458
+ }
459
+ Mat4Data *a = mat4_get(self);
460
+ Mat4Data *b = mat4_get(other);
461
+ for (int i = 0; i < 16; i++) {
462
+ if (a->data[i] != b->data[i]) {
463
+ return Qfalse;
464
+ }
465
+ }
466
+ return Qtrue;
467
+ }
468
+
469
+ VALUE mat4_near(int argc, VALUE *argv, VALUE self) {
470
+ VALUE other = Qnil;
471
+ VALUE epsilon = Qnil;
472
+
473
+ rb_scan_args(argc, argv, "11", &other, &epsilon);
474
+ Mat4Data *a = mat4_get(self);
475
+ Mat4Data *b = mat4_get(other);
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
+ }
480
+
481
+ for (int i = 0; i < 16; i++) {
482
+ if (!(fabs(a->data[i] - b->data[i]) < eps)) {
483
+ return Qfalse;
484
+ }
485
+ }
486
+ return Qtrue;
487
+ }
488
+
489
+ VALUE mat4_extract_translation(VALUE self) {
490
+ Mat4Data *a = mat4_get(self);
491
+ VALUE vec3_class = rb_const_get(mLarb, rb_intern("Vec3"));
492
+ return rb_funcall(vec3_class, rb_intern("new"), 3, DBL2NUM(a->data[12]),
493
+ DBL2NUM(a->data[13]), DBL2NUM(a->data[14]));
494
+ }
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
+
536
+ VALUE mat4_extract_scale(VALUE self) {
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]));
541
+ }
542
+
543
+ VALUE mat4_extract_rotation(VALUE self) {
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];
549
+ double trace = m00 + m11 + m22;
550
+ if (trace > 0.0) {
551
+ double s = 0.5 / sqrt(trace + 1.0);
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) {
557
+ double s = 2.0 * sqrt(1.0 + m00 - 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) {
563
+ double s = 2.0 * sqrt(1.0 + m11 - m00 - m22);
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;
574
+ }
575
+ larb_normalize(q, 4);
576
+ return rb_funcall(cQuat, rb_intern("new"), 4,
577
+ DBL2NUM(q[0]), DBL2NUM(q[1]), DBL2NUM(q[2]), DBL2NUM(q[3]));
578
+ }
579
+
580
+ static VALUE mat4_format_value(double value) {
581
+ return rb_funcall(rb_mKernel, rb_intern("format"), 2,
582
+ rb_str_new_cstr("%8.4f"), DBL2NUM(value));
583
+ }
584
+
585
+ static VALUE mat4_row_string(const double *values, int offset) {
586
+ VALUE str = mat4_format_value(values[offset]);
587
+ for (int i = 1; i < 4; i++) {
588
+ rb_str_cat_cstr(str, ", ");
589
+ rb_str_concat(str, mat4_format_value(values[offset + i]));
590
+ }
591
+ return str;
592
+ }
593
+
594
+ VALUE mat4_inspect(VALUE self) {
595
+ Mat4Data *a = mat4_get(self);
596
+ VALUE str = rb_str_new_cstr("Mat4[\n ");
597
+ rb_str_concat(str, mat4_row_string(a->data, 0));
598
+ rb_str_cat_cstr(str, "\n ");
599
+ rb_str_concat(str, mat4_row_string(a->data, 4));
600
+ rb_str_cat_cstr(str, "\n ");
601
+ rb_str_concat(str, mat4_row_string(a->data, 8));
602
+ rb_str_cat_cstr(str, "\n ");
603
+ rb_str_concat(str, mat4_row_string(a->data, 12));
604
+ rb_str_cat_cstr(str, "\n]");
605
+ return str;
606
+ }
607
+
608
+ void Init_mat4(VALUE module) {
609
+ cMat4 = rb_define_class_under(module, "Mat4", rb_cObject);
610
+ cVec3 = rb_const_get(mLarb, rb_intern("Vec3"));
611
+ cVec4 = rb_const_get(mLarb, rb_intern("Vec4"));
612
+ cQuat = rb_const_get(mLarb, rb_intern("Quat"));
613
+
614
+ rb_define_alloc_func(cMat4, mat4_alloc);
615
+ rb_define_method(cMat4, "initialize", mat4_initialize, -1);
616
+ rb_define_method(cMat4, "initialize_copy", mat4_initialize_copy, 1);
617
+
618
+ rb_define_singleton_method(cMat4, "identity", mat4_class_identity, 0);
619
+ rb_define_singleton_method(cMat4, "zero", mat4_class_zero, 0);
620
+ rb_define_singleton_method(cMat4, "translation", mat4_class_translation, 3);
621
+ rb_define_singleton_method(cMat4, "scaling", mat4_class_scaling, 3);
622
+ rb_define_singleton_method(cMat4, "rotation_x", mat4_class_rotation_x, 1);
623
+ rb_define_singleton_method(cMat4, "rotation_y", mat4_class_rotation_y, 1);
624
+ rb_define_singleton_method(cMat4, "rotation_z", mat4_class_rotation_z, 1);
625
+ rb_define_singleton_method(cMat4, "rotation", mat4_class_rotation, 2);
626
+ rb_define_singleton_method(cMat4, "look_at", mat4_class_look_at, 3);
627
+ rb_define_singleton_method(cMat4, "perspective", mat4_class_perspective, 4);
628
+ rb_define_singleton_method(cMat4, "orthographic", mat4_class_orthographic,
629
+ 6);
630
+ rb_define_singleton_method(cMat4, "frustum", mat4_class_frustum, 6);
631
+ rb_define_singleton_method(cMat4, "from_quaternion",
632
+ mat4_class_from_quaternion, 1);
633
+ rb_define_singleton_method(cMat4, "trs", mat4_class_trs, 3);
634
+
635
+ rb_define_method(cMat4, "data", mat4_data, 0);
636
+ rb_define_method(cMat4, "[]", mat4_aref, 1);
637
+ rb_define_method(cMat4, "[]=", mat4_aset, 2);
638
+ rb_define_method(cMat4, "*", mat4_mul, 1);
639
+ rb_define_method(cMat4, "transpose", mat4_transpose, 0);
640
+ rb_define_method(cMat4, "inverse", mat4_inverse, 0);
641
+ rb_define_method(cMat4, "to_a", mat4_to_a, 0);
642
+ rb_define_method(cMat4, "determinant", mat4_determinant, 0);
643
+ rb_define_method(cMat4, "+", mat4_add, 1);
644
+ rb_define_method(cMat4, "-", mat4_sub, 1);
645
+ rb_define_method(cMat4, "==", mat4_equal, 1);
646
+ rb_define_method(cMat4, "near?", mat4_near, -1);
647
+ rb_define_method(cMat4, "extract_translation", mat4_extract_translation, 0);
648
+ rb_define_method(cMat4, "extract_scale", mat4_extract_scale, 0);
649
+ rb_define_method(cMat4, "extract_rotation", mat4_extract_rotation, 0);
650
+ rb_define_method(cMat4, "inspect", mat4_inspect, 0);
651
+ }
data/ext/larb/mat4.h ADDED
@@ -0,0 +1,31 @@
1
+ #ifndef MAT4_H
2
+ #define MAT4_H
3
+
4
+ #include "larb.h"
5
+
6
+ typedef struct {
7
+ double data[16];
8
+ } Mat4Data;
9
+
10
+ void Init_mat4(VALUE module);
11
+ VALUE mat4_alloc(VALUE klass);
12
+ VALUE mat4_initialize(int argc, VALUE *argv, VALUE self);
13
+
14
+ VALUE mat4_aref(VALUE self, VALUE index);
15
+ VALUE mat4_aset(VALUE self, VALUE index, VALUE value);
16
+ VALUE mat4_mul(VALUE self, VALUE other);
17
+ VALUE mat4_transpose(VALUE self);
18
+ VALUE mat4_inverse(VALUE self);
19
+ VALUE mat4_to_a(VALUE self);
20
+ VALUE mat4_determinant(VALUE self);
21
+ VALUE mat4_add(VALUE self, VALUE other);
22
+ VALUE mat4_sub(VALUE self, VALUE other);
23
+ VALUE mat4_equal(VALUE self, VALUE other);
24
+ VALUE mat4_near(int argc, VALUE *argv, VALUE self);
25
+ VALUE mat4_extract_translation(VALUE self);
26
+ VALUE mat4_extract_scale(VALUE self);
27
+ VALUE mat4_extract_rotation(VALUE self);
28
+ VALUE mat4_inspect(VALUE self);
29
+ VALUE mat4_data(VALUE self);
30
+
31
+ #endif