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/mat2d.c CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "mat2d.h"
2
+ #include "matrix_utils.h"
2
3
 
3
4
  #include <math.h>
4
5
 
@@ -22,11 +23,6 @@ static VALUE cMat2d = Qnil;
22
23
  static VALUE cVec2 = Qnil;
23
24
  static VALUE cMat3 = Qnil;
24
25
 
25
- static double value_to_double(VALUE value) {
26
- VALUE coerced = rb_funcall(value, rb_intern("to_f"), 0);
27
- return NUM2DBL(coerced);
28
- }
29
-
30
26
  static Mat2dData *mat2d_get(VALUE obj) {
31
27
  Mat2dData *data = NULL;
32
28
  TypedData_Get_Struct(obj, Mat2dData, &mat2d_type, data);
@@ -60,29 +56,31 @@ VALUE mat2d_alloc(VALUE klass) {
60
56
  }
61
57
 
62
58
  VALUE mat2d_initialize(int argc, VALUE *argv, VALUE self) {
59
+ rb_check_frozen(self);
63
60
  VALUE data_arg = Qnil;
64
- Mat2dData *data = mat2d_get(self);
65
-
61
+ Mat2dData values = {{1.0, 0.0, 0.0, 1.0, 0.0, 0.0}};
66
62
  rb_scan_args(argc, argv, "01", &data_arg);
67
- if (NIL_P(data_arg)) {
68
- data->data[0] = 1.0;
69
- data->data[1] = 0.0;
70
- data->data[2] = 0.0;
71
- data->data[3] = 1.0;
72
- data->data[4] = 0.0;
73
- data->data[5] = 0.0;
74
- return self;
75
- }
76
-
77
- VALUE ary = rb_check_array_type(data_arg);
78
- if (NIL_P(ary)) {
79
- rb_raise(rb_eTypeError, "expected Array");
80
- }
81
-
82
- for (int i = 0; i < 6; i++) {
83
- data->data[i] = value_to_double(rb_ary_entry(ary, i));
63
+ if (argc != 0) {
64
+ VALUE ary = rb_check_array_type(data_arg);
65
+ if (NIL_P(ary)) {
66
+ rb_raise(rb_eTypeError, "expected Array");
67
+ }
68
+ if (RARRAY_LEN(ary) != 6) {
69
+ rb_raise(rb_eArgError, "expected 6 elements");
70
+ }
71
+ for (int i = 0; i < 6; i++) {
72
+ values.data[i] = NUM2DBL(rb_ary_entry(ary, i));
73
+ }
84
74
  }
75
+ rb_check_frozen(self);
76
+ *mat2d_get(self) = values;
77
+ return self;
78
+ }
85
79
 
80
+ static VALUE mat2d_initialize_copy(VALUE self, VALUE other) {
81
+ if (self == other) return self;
82
+ rb_obj_init_copy(self, other);
83
+ *mat2d_get(self) = *mat2d_get(other);
86
84
  return self;
87
85
  }
88
86
 
@@ -95,33 +93,33 @@ static VALUE mat2d_class_zero(VALUE klass) {
95
93
  }
96
94
 
97
95
  static VALUE mat2d_class_translation(VALUE klass, VALUE x, VALUE y) {
98
- return mat2d_build6(klass, 1.0, 0.0, 0.0, 1.0, value_to_double(x),
99
- value_to_double(y));
96
+ return mat2d_build6(klass, 1.0, 0.0, 0.0, 1.0, NUM2DBL(x),
97
+ NUM2DBL(y));
100
98
  }
101
99
 
102
100
  static VALUE mat2d_class_rotation(VALUE klass, VALUE radians) {
103
- double r = value_to_double(radians);
101
+ double r = NUM2DBL(radians);
104
102
  double c = cos(r);
105
103
  double s = sin(r);
106
104
  return mat2d_build6(klass, c, s, -s, c, 0.0, 0.0);
107
105
  }
108
106
 
109
107
  static VALUE mat2d_class_scaling(VALUE klass, VALUE x, VALUE y) {
110
- return mat2d_build6(klass, value_to_double(x), 0.0, 0.0,
111
- value_to_double(y), 0.0, 0.0);
108
+ return mat2d_build6(klass, NUM2DBL(x), 0.0, 0.0,
109
+ NUM2DBL(y), 0.0, 0.0);
112
110
  }
113
111
 
114
112
  static VALUE mat2d_class_from_rotation_translation_scale(VALUE klass,
115
113
  VALUE rotation,
116
114
  VALUE translation,
117
115
  VALUE scale) {
118
- double r = value_to_double(rotation);
116
+ double r = NUM2DBL(rotation);
119
117
  double c = cos(r);
120
118
  double s = sin(r);
121
- double sx = value_to_double(rb_funcall(scale, rb_intern("x"), 0));
122
- double sy = value_to_double(rb_funcall(scale, rb_intern("y"), 0));
123
- double tx = value_to_double(rb_funcall(translation, rb_intern("x"), 0));
124
- double ty = value_to_double(rb_funcall(translation, rb_intern("y"), 0));
119
+ double sx = NUM2DBL(rb_funcall(scale, rb_intern("x"), 0));
120
+ double sy = NUM2DBL(rb_funcall(scale, rb_intern("y"), 0));
121
+ double tx = NUM2DBL(rb_funcall(translation, rb_intern("x"), 0));
122
+ double ty = NUM2DBL(rb_funcall(translation, rb_intern("y"), 0));
125
123
 
126
124
  return mat2d_build6(klass, c * sx, s * sx, -s * sy, c * sy, tx, ty);
127
125
  }
@@ -136,12 +134,15 @@ VALUE mat2d_aref(VALUE self, VALUE index) {
136
134
  }
137
135
 
138
136
  VALUE mat2d_aset(VALUE self, VALUE index, VALUE value) {
137
+ rb_check_frozen(self);
139
138
  Mat2dData *data = mat2d_get(self);
140
139
  long idx = NUM2LONG(index);
141
140
  if (idx < 0 || idx > 5) {
142
141
  rb_raise(rb_eIndexError, "index %ld out of range", idx);
143
142
  }
144
- data->data[idx] = value_to_double(value);
143
+ double component = NUM2DBL(value);
144
+ rb_check_frozen(self);
145
+ data->data[idx] = component;
145
146
  return value;
146
147
  }
147
148
 
@@ -161,8 +162,8 @@ VALUE mat2d_mul(VALUE self, VALUE other) {
161
162
  }
162
163
 
163
164
  if (rb_obj_is_kind_of(other, cVec2)) {
164
- double x = value_to_double(rb_funcall(other, rb_intern("x"), 0));
165
- double y = value_to_double(rb_funcall(other, rb_intern("y"), 0));
165
+ double x = NUM2DBL(rb_funcall(other, rb_intern("x"), 0));
166
+ double y = NUM2DBL(rb_funcall(other, rb_intern("y"), 0));
166
167
  VALUE vec2_class = rb_const_get(mLarb, rb_intern("Vec2"));
167
168
  return rb_funcall(vec2_class, rb_intern("new"), 2,
168
169
  DBL2NUM(a->data[0] * x + a->data[2] * y + a->data[4]),
@@ -170,13 +171,13 @@ VALUE mat2d_mul(VALUE self, VALUE other) {
170
171
  }
171
172
 
172
173
  if (rb_obj_is_kind_of(other, rb_cNumeric)) {
173
- double s = value_to_double(other);
174
+ double s = NUM2DBL(other);
174
175
  return mat2d_build6(rb_obj_class(self), a->data[0] * s, a->data[1] * s,
175
176
  a->data[2] * s, a->data[3] * s, a->data[4] * s,
176
177
  a->data[5] * s);
177
178
  }
178
179
 
179
- return Qnil;
180
+ rb_raise(rb_eTypeError, "unsupported operand for Mat2d multiplication");
180
181
  }
181
182
 
182
183
  VALUE mat2d_add(VALUE self, VALUE other) {
@@ -203,25 +204,18 @@ VALUE mat2d_determinant(VALUE self) {
203
204
  }
204
205
 
205
206
  VALUE mat2d_inverse(VALUE self) {
206
- Mat2dData *a = mat2d_get(self);
207
- double det = a->data[0] * a->data[3] - a->data[1] * a->data[2];
208
- if (fabs(det) < 1e-10) {
209
- rb_raise(rb_eRuntimeError, "Matrix is not invertible");
210
- }
211
- double inv_det = 1.0 / det;
212
- return mat2d_build6(rb_obj_class(self), a->data[3] * inv_det,
213
- -a->data[1] * inv_det, -a->data[2] * inv_det,
214
- a->data[0] * inv_det,
215
- (a->data[2] * a->data[5] - a->data[3] * a->data[4]) *
216
- inv_det,
217
- (a->data[1] * a->data[4] - a->data[0] * a->data[5]) *
218
- inv_det);
207
+ const double *a = mat2d_get(self)->data;
208
+ double values[9] = {a[0], a[1], 0.0, a[2], a[3], 0.0, a[4], a[5], 1.0};
209
+ double inverse[9];
210
+ larb_matrix_inverse(values, inverse, 3);
211
+ return mat2d_build6(rb_obj_class(self), inverse[0], inverse[1], inverse[3],
212
+ inverse[4], inverse[6], inverse[7]);
219
213
  }
220
214
 
221
215
  VALUE mat2d_translate(VALUE self, VALUE x, VALUE y) {
222
216
  Mat2dData *a = mat2d_get(self);
223
- double dx = value_to_double(x);
224
- double dy = value_to_double(y);
217
+ double dx = NUM2DBL(x);
218
+ double dy = NUM2DBL(y);
225
219
  return mat2d_build6(rb_obj_class(self), a->data[0], a->data[1], a->data[2],
226
220
  a->data[3], a->data[0] * dx + a->data[2] * dy + a->data[4],
227
221
  a->data[1] * dx + a->data[3] * dy + a->data[5]);
@@ -234,8 +228,8 @@ VALUE mat2d_rotate(VALUE self, VALUE radians) {
234
228
 
235
229
  VALUE mat2d_scale(VALUE self, VALUE x, VALUE y) {
236
230
  Mat2dData *a = mat2d_get(self);
237
- double sx = value_to_double(x);
238
- double sy = value_to_double(y);
231
+ double sx = NUM2DBL(x);
232
+ double sy = NUM2DBL(y);
239
233
  return mat2d_build6(rb_obj_class(self), a->data[0] * sx, a->data[1] * sx,
240
234
  a->data[2] * sy, a->data[3] * sy, a->data[4], a->data[5]);
241
235
  }
@@ -247,26 +241,45 @@ VALUE mat2d_extract_translation(VALUE self) {
247
241
  DBL2NUM(a->data[5]));
248
242
  }
249
243
 
244
+ static void mat2d_decompose(const Mat2dData *a, double *sx, double *sy) {
245
+ for (int i = 0; i < 6; i++) {
246
+ if (!isfinite(a->data[i])) {
247
+ rb_raise(rb_eArgError, "Cannot decompose non-finite components");
248
+ }
249
+ }
250
+ *sx = hypot(a->data[0], a->data[1]);
251
+ *sy = hypot(a->data[2], a->data[3]);
252
+ if (*sx == 0.0 || *sy == 0.0 || !isfinite(*sx) || !isfinite(*sy)) {
253
+ rb_raise(rb_eArgError, "Cannot decompose zero or non-finite scale");
254
+ }
255
+ double x0 = a->data[0] / *sx, y0 = a->data[1] / *sx;
256
+ double x1 = a->data[2] / *sy, y1 = a->data[3] / *sy;
257
+ if (fabs(x0 * x1 + y0 * y1) > 1e-6) {
258
+ rb_raise(rb_eArgError, "Cannot decompose shear");
259
+ }
260
+ if (x0 * y1 - y0 * x1 < 0.0) *sx = -*sx;
261
+ }
262
+
250
263
  VALUE mat2d_extract_rotation(VALUE self) {
251
264
  Mat2dData *a = mat2d_get(self);
252
- return DBL2NUM(atan2(a->data[1], a->data[0]));
265
+ double sx, sy;
266
+ mat2d_decompose(a, &sx, &sy);
267
+ return DBL2NUM(atan2(a->data[1] / sx, a->data[0] / sx));
253
268
  }
254
269
 
255
270
  VALUE mat2d_extract_scale(VALUE self) {
256
- Mat2dData *a = mat2d_get(self);
257
- double sx = sqrt(a->data[0] * a->data[0] + a->data[1] * a->data[1]);
258
- double sy = sqrt(a->data[2] * a->data[2] + a->data[3] * a->data[3]);
259
- VALUE vec2_class = rb_const_get(mLarb, rb_intern("Vec2"));
260
- return rb_funcall(vec2_class, rb_intern("new"), 2, DBL2NUM(sx), DBL2NUM(sy));
271
+ double sx, sy;
272
+ mat2d_decompose(mat2d_get(self), &sx, &sy);
273
+ return rb_funcall(cVec2, rb_intern("new"), 2, DBL2NUM(sx), DBL2NUM(sy));
261
274
  }
262
275
 
263
276
  VALUE mat2d_frobenius_norm(VALUE self) {
264
277
  Mat2dData *a = mat2d_get(self);
265
- double sum = 0.0;
278
+ double norm = 0.0;
266
279
  for (int i = 0; i < 6; i++) {
267
- sum += a->data[i] * a->data[i];
280
+ norm = hypot(norm, a->data[i]);
268
281
  }
269
- return DBL2NUM(sqrt(sum));
282
+ return DBL2NUM(norm);
270
283
  }
271
284
 
272
285
  VALUE mat2d_to_mat3(VALUE self) {
@@ -314,10 +327,13 @@ VALUE mat2d_near(int argc, VALUE *argv, VALUE self) {
314
327
  rb_scan_args(argc, argv, "11", &other, &epsilon);
315
328
  Mat2dData *a = mat2d_get(self);
316
329
  Mat2dData *b = mat2d_get(other);
317
- double eps = NIL_P(epsilon) ? 1e-6 : value_to_double(epsilon);
330
+ double eps = argc < 2 ? 1e-6 : NUM2DBL(epsilon);
331
+ if (!isfinite(eps) || eps <= 0.0) {
332
+ rb_raise(rb_eArgError, "epsilon must be finite and positive");
333
+ }
318
334
 
319
335
  for (int i = 0; i < 6; i++) {
320
- if (fabs(a->data[i] - b->data[i]) >= eps) {
336
+ if (!(fabs(a->data[i] - b->data[i]) < eps)) {
321
337
  return Qfalse;
322
338
  }
323
339
  }
@@ -346,6 +362,7 @@ void Init_mat2d(VALUE module) {
346
362
 
347
363
  rb_define_alloc_func(cMat2d, mat2d_alloc);
348
364
  rb_define_method(cMat2d, "initialize", mat2d_initialize, -1);
365
+ rb_define_method(cMat2d, "initialize_copy", mat2d_initialize_copy, 1);
349
366
 
350
367
  rb_define_singleton_method(cMat2d, "identity", mat2d_class_identity, 0);
351
368
  rb_define_singleton_method(cMat2d, "zero", mat2d_class_zero, 0);
data/ext/larb/mat3.c CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "mat3.h"
2
+ #include "matrix_utils.h"
2
3
 
3
4
  #include <math.h>
4
5
 
@@ -21,11 +22,6 @@ static const rb_data_type_t mat3_type = {
21
22
  static VALUE cMat3 = Qnil;
22
23
  static VALUE cVec3 = Qnil;
23
24
 
24
- static double value_to_double(VALUE value) {
25
- VALUE coerced = rb_funcall(value, rb_intern("to_f"), 0);
26
- return NUM2DBL(coerced);
27
- }
28
-
29
25
  static Mat3Data *mat3_get(VALUE obj) {
30
26
  Mat3Data *data = NULL;
31
27
  TypedData_Get_Struct(obj, Mat3Data, &mat3_type, data);
@@ -63,32 +59,31 @@ VALUE mat3_alloc(VALUE klass) {
63
59
  }
64
60
 
65
61
  VALUE mat3_initialize(int argc, VALUE *argv, VALUE self) {
62
+ rb_check_frozen(self);
66
63
  VALUE data_arg = Qnil;
67
- Mat3Data *data = mat3_get(self);
68
-
64
+ Mat3Data values = {{1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}};
69
65
  rb_scan_args(argc, argv, "01", &data_arg);
70
- if (NIL_P(data_arg)) {
71
- data->data[0] = 1.0;
72
- data->data[1] = 0.0;
73
- data->data[2] = 0.0;
74
- data->data[3] = 0.0;
75
- data->data[4] = 1.0;
76
- data->data[5] = 0.0;
77
- data->data[6] = 0.0;
78
- data->data[7] = 0.0;
79
- data->data[8] = 1.0;
80
- return self;
81
- }
82
-
83
- VALUE ary = rb_check_array_type(data_arg);
84
- if (NIL_P(ary)) {
85
- rb_raise(rb_eTypeError, "expected Array");
86
- }
87
-
88
- for (int i = 0; i < 9; i++) {
89
- data->data[i] = value_to_double(rb_ary_entry(ary, i));
66
+ if (argc != 0) {
67
+ VALUE ary = rb_check_array_type(data_arg);
68
+ if (NIL_P(ary)) {
69
+ rb_raise(rb_eTypeError, "expected Array");
70
+ }
71
+ if (RARRAY_LEN(ary) != 9) {
72
+ rb_raise(rb_eArgError, "expected 9 elements");
73
+ }
74
+ for (int i = 0; i < 9; i++) {
75
+ values.data[i] = NUM2DBL(rb_ary_entry(ary, i));
76
+ }
90
77
  }
78
+ rb_check_frozen(self);
79
+ *mat3_get(self) = values;
80
+ return self;
81
+ }
91
82
 
83
+ static VALUE mat3_initialize_copy(VALUE self, VALUE other) {
84
+ if (self == other) return self;
85
+ rb_obj_init_copy(self, other);
86
+ *mat3_get(self) = *mat3_get(other);
92
87
  return self;
93
88
  }
94
89
 
@@ -102,37 +97,37 @@ static VALUE mat3_class_zero(VALUE klass) {
102
97
 
103
98
  static VALUE mat3_class_from_mat4(VALUE klass, VALUE mat4) {
104
99
  double values[9];
105
- values[0] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(0)));
106
- values[1] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(1)));
107
- values[2] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(2)));
108
- values[3] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(4)));
109
- values[4] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(5)));
110
- values[5] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(6)));
111
- values[6] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(8)));
112
- values[7] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(9)));
113
- values[8] = value_to_double(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(10)));
100
+ values[0] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(0)));
101
+ values[1] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(1)));
102
+ values[2] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(2)));
103
+ values[3] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(4)));
104
+ values[4] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(5)));
105
+ values[5] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(6)));
106
+ values[6] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(8)));
107
+ values[7] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(9)));
108
+ values[8] = NUM2DBL(rb_funcall(mat4, rb_intern("[]"), 1, INT2NUM(10)));
114
109
  return mat3_build(klass, values);
115
110
  }
116
111
 
117
112
  static VALUE mat3_class_from_mat2d(VALUE klass, VALUE mat2d) {
118
113
  double values[9];
119
- values[0] = value_to_double(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(0)));
120
- values[1] = value_to_double(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(1)));
114
+ values[0] = NUM2DBL(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(0)));
115
+ values[1] = NUM2DBL(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(1)));
121
116
  values[2] = 0.0;
122
- values[3] = value_to_double(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(2)));
123
- values[4] = value_to_double(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(3)));
117
+ values[3] = NUM2DBL(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(2)));
118
+ values[4] = NUM2DBL(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(3)));
124
119
  values[5] = 0.0;
125
- values[6] = value_to_double(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(4)));
126
- values[7] = value_to_double(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(5)));
120
+ values[6] = NUM2DBL(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(4)));
121
+ values[7] = NUM2DBL(rb_funcall(mat2d, rb_intern("[]"), 1, INT2NUM(5)));
127
122
  values[8] = 1.0;
128
123
  return mat3_build(klass, values);
129
124
  }
130
125
 
131
126
  static VALUE mat3_class_from_quaternion(VALUE klass, VALUE quat) {
132
- double x = value_to_double(rb_funcall(quat, rb_intern("x"), 0));
133
- double y = value_to_double(rb_funcall(quat, rb_intern("y"), 0));
134
- double z = value_to_double(rb_funcall(quat, rb_intern("z"), 0));
135
- double w = value_to_double(rb_funcall(quat, rb_intern("w"), 0));
127
+ double x = NUM2DBL(rb_funcall(quat, rb_intern("x"), 0));
128
+ double y = NUM2DBL(rb_funcall(quat, rb_intern("y"), 0));
129
+ double z = NUM2DBL(rb_funcall(quat, rb_intern("z"), 0));
130
+ double w = NUM2DBL(rb_funcall(quat, rb_intern("w"), 0));
136
131
  double x2 = x + x;
137
132
  double y2 = y + y;
138
133
  double z2 = z + z;
@@ -158,27 +153,27 @@ static VALUE mat3_class_normal_from_mat4(VALUE klass, VALUE mat4) {
158
153
  }
159
154
 
160
155
  static VALUE mat3_class_projection(VALUE klass, VALUE width, VALUE height) {
161
- double w = value_to_double(width);
162
- double h = value_to_double(height);
156
+ double w = NUM2DBL(width);
157
+ double h = NUM2DBL(height);
163
158
  return mat3_build9(klass, 2.0 / w, 0.0, 0.0, 0.0, -2.0 / h, 0.0, -1.0, 1.0,
164
159
  1.0);
165
160
  }
166
161
 
167
162
  static VALUE mat3_class_translation(VALUE klass, VALUE x, VALUE y) {
168
- return mat3_build9(klass, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, value_to_double(x),
169
- value_to_double(y), 1.0);
163
+ return mat3_build9(klass, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, NUM2DBL(x),
164
+ NUM2DBL(y), 1.0);
170
165
  }
171
166
 
172
167
  static VALUE mat3_class_rotation(VALUE klass, VALUE radians) {
173
- double r = value_to_double(radians);
168
+ double r = NUM2DBL(radians);
174
169
  double c = cos(r);
175
170
  double s = sin(r);
176
171
  return mat3_build9(klass, c, s, 0.0, -s, c, 0.0, 0.0, 0.0, 1.0);
177
172
  }
178
173
 
179
174
  static VALUE mat3_class_scaling(VALUE klass, VALUE x, VALUE y) {
180
- return mat3_build9(klass, value_to_double(x), 0.0, 0.0, 0.0,
181
- value_to_double(y), 0.0, 0.0, 0.0, 1.0);
175
+ return mat3_build9(klass, NUM2DBL(x), 0.0, 0.0, 0.0,
176
+ NUM2DBL(y), 0.0, 0.0, 0.0, 1.0);
182
177
  }
183
178
 
184
179
  VALUE mat3_aref(VALUE self, VALUE index) {
@@ -191,12 +186,15 @@ VALUE mat3_aref(VALUE self, VALUE index) {
191
186
  }
192
187
 
193
188
  VALUE mat3_aset(VALUE self, VALUE index, VALUE value) {
189
+ rb_check_frozen(self);
194
190
  Mat3Data *data = mat3_get(self);
195
191
  long idx = NUM2LONG(index);
196
192
  if (idx < 0 || idx > 8) {
197
193
  rb_raise(rb_eIndexError, "index %ld out of range", idx);
198
194
  }
199
- data->data[idx] = value_to_double(value);
195
+ double component = NUM2DBL(value);
196
+ rb_check_frozen(self);
197
+ data->data[idx] = component;
200
198
  return value;
201
199
  }
202
200
 
@@ -228,9 +226,9 @@ VALUE mat3_mul(VALUE self, VALUE other) {
228
226
  }
229
227
 
230
228
  if (rb_obj_is_kind_of(other, cVec3)) {
231
- double x = value_to_double(rb_funcall(other, rb_intern("x"), 0));
232
- double y = value_to_double(rb_funcall(other, rb_intern("y"), 0));
233
- double z = value_to_double(rb_funcall(other, rb_intern("z"), 0));
229
+ double x = NUM2DBL(rb_funcall(other, rb_intern("x"), 0));
230
+ double y = NUM2DBL(rb_funcall(other, rb_intern("y"), 0));
231
+ double z = NUM2DBL(rb_funcall(other, rb_intern("z"), 0));
234
232
  VALUE vec3_class = rb_const_get(mLarb, rb_intern("Vec3"));
235
233
  return rb_funcall(vec3_class, rb_intern("new"), 3,
236
234
  DBL2NUM(a->data[0] * x + a->data[3] * y +
@@ -242,14 +240,14 @@ VALUE mat3_mul(VALUE self, VALUE other) {
242
240
  }
243
241
 
244
242
  if (rb_obj_is_kind_of(other, rb_cNumeric)) {
245
- double s = value_to_double(other);
243
+ double s = NUM2DBL(other);
246
244
  return mat3_build9(rb_obj_class(self), a->data[0] * s, a->data[1] * s,
247
245
  a->data[2] * s, a->data[3] * s, a->data[4] * s,
248
246
  a->data[5] * s, a->data[6] * s, a->data[7] * s,
249
247
  a->data[8] * s);
250
248
  }
251
249
 
252
- return Qnil;
250
+ rb_raise(rb_eTypeError, "unsupported operand for Mat3 multiplication");
253
251
  }
254
252
 
255
253
  VALUE mat3_add(VALUE self, VALUE other) {
@@ -281,25 +279,9 @@ VALUE mat3_determinant(VALUE self) {
281
279
  }
282
280
 
283
281
  VALUE mat3_inverse(VALUE self) {
284
- Mat3Data *a = mat3_get(self);
285
- double det = a->data[0] * (a->data[4] * a->data[8] - a->data[5] * a->data[7]) -
286
- a->data[3] * (a->data[1] * a->data[8] - a->data[2] * a->data[7]) +
287
- a->data[6] * (a->data[1] * a->data[5] - a->data[2] * a->data[4]);
288
- if (fabs(det) < 1e-10) {
289
- rb_raise(rb_eRuntimeError, "Matrix is not invertible");
290
- }
291
- double inv_det = 1.0 / det;
292
- return mat3_build9(
293
- rb_obj_class(self),
294
- (a->data[4] * a->data[8] - a->data[5] * a->data[7]) * inv_det,
295
- (a->data[2] * a->data[7] - a->data[1] * a->data[8]) * inv_det,
296
- (a->data[1] * a->data[5] - a->data[2] * a->data[4]) * inv_det,
297
- (a->data[5] * a->data[6] - a->data[3] * a->data[8]) * inv_det,
298
- (a->data[0] * a->data[8] - a->data[2] * a->data[6]) * inv_det,
299
- (a->data[2] * a->data[3] - a->data[0] * a->data[5]) * inv_det,
300
- (a->data[3] * a->data[7] - a->data[4] * a->data[6]) * inv_det,
301
- (a->data[1] * a->data[6] - a->data[0] * a->data[7]) * inv_det,
302
- (a->data[0] * a->data[4] - a->data[1] * a->data[3]) * inv_det);
282
+ double inverse[9];
283
+ larb_matrix_inverse(mat3_get(self)->data, inverse, 3);
284
+ return mat3_build(rb_obj_class(self), inverse);
303
285
  }
304
286
 
305
287
  VALUE mat3_transpose(VALUE self) {
@@ -325,17 +307,17 @@ VALUE mat3_adjoint(VALUE self) {
325
307
 
326
308
  VALUE mat3_frobenius_norm(VALUE self) {
327
309
  Mat3Data *a = mat3_get(self);
328
- double sum = 0.0;
310
+ double norm = 0.0;
329
311
  for (int i = 0; i < 9; i++) {
330
- sum += a->data[i] * a->data[i];
312
+ norm = hypot(norm, a->data[i]);
331
313
  }
332
- return DBL2NUM(sqrt(sum));
314
+ return DBL2NUM(norm);
333
315
  }
334
316
 
335
317
  VALUE mat3_translate(VALUE self, VALUE x, VALUE y) {
336
318
  Mat3Data *a = mat3_get(self);
337
- double dx = value_to_double(x);
338
- double dy = value_to_double(y);
319
+ double dx = NUM2DBL(x);
320
+ double dy = NUM2DBL(y);
339
321
  return mat3_build9(rb_obj_class(self), a->data[0], a->data[1], a->data[2],
340
322
  a->data[3], a->data[4], a->data[5],
341
323
  dx * a->data[0] + dy * a->data[3] + a->data[6],
@@ -350,8 +332,8 @@ VALUE mat3_rotate(VALUE self, VALUE radians) {
350
332
 
351
333
  VALUE mat3_scale(VALUE self, VALUE x, VALUE y) {
352
334
  Mat3Data *a = mat3_get(self);
353
- double sx = value_to_double(x);
354
- double sy = value_to_double(y);
335
+ double sx = NUM2DBL(x);
336
+ double sy = NUM2DBL(y);
355
337
  return mat3_build9(rb_obj_class(self), a->data[0] * sx, a->data[1] * sx,
356
338
  a->data[2] * sx, a->data[3] * sy, a->data[4] * sy,
357
339
  a->data[5] * sy, a->data[6], a->data[7], a->data[8]);
@@ -391,10 +373,13 @@ VALUE mat3_near(int argc, VALUE *argv, VALUE self) {
391
373
  rb_scan_args(argc, argv, "11", &other, &epsilon);
392
374
  Mat3Data *a = mat3_get(self);
393
375
  Mat3Data *b = mat3_get(other);
394
- double eps = NIL_P(epsilon) ? 1e-6 : value_to_double(epsilon);
376
+ double eps = argc < 2 ? 1e-6 : NUM2DBL(epsilon);
377
+ if (!isfinite(eps) || eps <= 0.0) {
378
+ rb_raise(rb_eArgError, "epsilon must be finite and positive");
379
+ }
395
380
 
396
381
  for (int i = 0; i < 9; i++) {
397
- if (fabs(a->data[i] - b->data[i]) >= eps) {
382
+ if (!(fabs(a->data[i] - b->data[i]) < eps)) {
398
383
  return Qfalse;
399
384
  }
400
385
  }
@@ -433,6 +418,7 @@ void Init_mat3(VALUE module) {
433
418
 
434
419
  rb_define_alloc_func(cMat3, mat3_alloc);
435
420
  rb_define_method(cMat3, "initialize", mat3_initialize, -1);
421
+ rb_define_method(cMat3, "initialize_copy", mat3_initialize_copy, 1);
436
422
 
437
423
  rb_define_singleton_method(cMat3, "identity", mat3_class_identity, 0);
438
424
  rb_define_singleton_method(cMat3, "zero", mat3_class_zero, 0);