larb 0.1.0 → 1.0.0
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 +4 -0
- data/README.md +14 -1
- data/ext/larb/color.c +446 -0
- data/ext/larb/color.h +35 -0
- data/ext/larb/extconf.rb +11 -0
- data/ext/larb/larb.c +27 -0
- data/ext/larb/larb.h +8 -0
- data/ext/larb/mat2.c +300 -0
- data/ext/larb/mat2.h +30 -0
- data/ext/larb/mat2d.c +380 -0
- data/ext/larb/mat2d.h +35 -0
- data/ext/larb/mat3.c +469 -0
- data/ext/larb/mat3.h +33 -0
- data/ext/larb/mat4.c +671 -0
- data/ext/larb/mat4.h +31 -0
- data/ext/larb/quat.c +523 -0
- data/ext/larb/quat.h +39 -0
- data/ext/larb/quat2.c +473 -0
- data/ext/larb/quat2.h +39 -0
- data/ext/larb/vec2.c +342 -0
- data/ext/larb/vec2.h +43 -0
- data/ext/larb/vec3.c +503 -0
- data/ext/larb/vec3.h +52 -0
- data/ext/larb/vec4.c +340 -0
- data/ext/larb/vec4.h +38 -0
- data/lib/larb/version.rb +5 -0
- data/lib/larb.rb +2 -14
- data/test/larb/color_test.rb +278 -0
- data/test/larb/mat2_test.rb +144 -0
- data/test/larb/mat2d_test.rb +172 -0
- data/test/larb/mat3_test.rb +147 -0
- data/test/larb/mat4_test.rb +270 -0
- data/test/larb/quat2_test.rb +161 -0
- data/test/larb/quat_test.rb +224 -0
- data/test/larb/vec2_test.rb +251 -0
- data/test/larb/vec3_test.rb +310 -0
- data/test/larb/vec4_test.rb +189 -0
- data/test/test_helper.rb +4 -0
- metadata +53 -14
- data/Rakefile +0 -11
- data/lib/larb/color.rb +0 -148
- data/lib/larb/mat2.rb +0 -119
- data/lib/larb/mat2d.rb +0 -180
- data/lib/larb/mat3.rb +0 -238
- data/lib/larb/mat4.rb +0 -329
- data/lib/larb/quat.rb +0 -238
- data/lib/larb/quat2.rb +0 -193
- data/lib/larb/vec2.rb +0 -150
- data/lib/larb/vec3.rb +0 -218
- data/lib/larb/vec4.rb +0 -125
data/ext/larb/mat2.c
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#include "mat2.h"
|
|
2
|
+
|
|
3
|
+
#include <math.h>
|
|
4
|
+
|
|
5
|
+
static void mat2_free(void *ptr) {
|
|
6
|
+
xfree(ptr);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static size_t mat2_memsize(const void *ptr) {
|
|
10
|
+
return sizeof(Mat2Data);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static const rb_data_type_t mat2_type = {
|
|
14
|
+
"Mat2",
|
|
15
|
+
{0, mat2_free, mat2_memsize},
|
|
16
|
+
0,
|
|
17
|
+
0,
|
|
18
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
static VALUE cMat2 = Qnil;
|
|
22
|
+
static VALUE cVec2 = Qnil;
|
|
23
|
+
|
|
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
|
+
static Mat2Data *mat2_get(VALUE obj) {
|
|
30
|
+
Mat2Data *data = NULL;
|
|
31
|
+
TypedData_Get_Struct(obj, Mat2Data, &mat2_type, data);
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static VALUE mat2_build(VALUE klass, double a0, double a1, double a2,
|
|
36
|
+
double a3) {
|
|
37
|
+
VALUE obj = mat2_alloc(klass);
|
|
38
|
+
Mat2Data *data = mat2_get(obj);
|
|
39
|
+
data->data[0] = a0;
|
|
40
|
+
data->data[1] = a1;
|
|
41
|
+
data->data[2] = a2;
|
|
42
|
+
data->data[3] = a3;
|
|
43
|
+
return obj;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
VALUE mat2_alloc(VALUE klass) {
|
|
47
|
+
Mat2Data *data = ALLOC(Mat2Data);
|
|
48
|
+
data->data[0] = 1.0;
|
|
49
|
+
data->data[1] = 0.0;
|
|
50
|
+
data->data[2] = 0.0;
|
|
51
|
+
data->data[3] = 1.0;
|
|
52
|
+
return TypedData_Wrap_Struct(klass, &mat2_type, data);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
VALUE mat2_initialize(int argc, VALUE *argv, VALUE self) {
|
|
56
|
+
VALUE data_arg = Qnil;
|
|
57
|
+
Mat2Data *data = mat2_get(self);
|
|
58
|
+
|
|
59
|
+
rb_scan_args(argc, argv, "01", &data_arg);
|
|
60
|
+
if (NIL_P(data_arg)) {
|
|
61
|
+
data->data[0] = 1.0;
|
|
62
|
+
data->data[1] = 0.0;
|
|
63
|
+
data->data[2] = 0.0;
|
|
64
|
+
data->data[3] = 1.0;
|
|
65
|
+
return self;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
VALUE ary = rb_check_array_type(data_arg);
|
|
69
|
+
if (NIL_P(ary)) {
|
|
70
|
+
rb_raise(rb_eTypeError, "expected Array");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
data->data[0] = value_to_double(rb_ary_entry(ary, 0));
|
|
74
|
+
data->data[1] = value_to_double(rb_ary_entry(ary, 1));
|
|
75
|
+
data->data[2] = value_to_double(rb_ary_entry(ary, 2));
|
|
76
|
+
data->data[3] = value_to_double(rb_ary_entry(ary, 3));
|
|
77
|
+
|
|
78
|
+
return self;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static VALUE mat2_class_identity(VALUE klass) {
|
|
82
|
+
return mat2_build(klass, 1.0, 0.0, 0.0, 1.0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static VALUE mat2_class_zero(VALUE klass) {
|
|
86
|
+
return mat2_build(klass, 0.0, 0.0, 0.0, 0.0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static VALUE mat2_class_rotation(VALUE klass, VALUE radians) {
|
|
90
|
+
double r = value_to_double(radians);
|
|
91
|
+
double c = cos(r);
|
|
92
|
+
double s = sin(r);
|
|
93
|
+
return mat2_build(klass, c, s, -s, c);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static VALUE mat2_class_scaling(VALUE klass, VALUE x, VALUE y) {
|
|
97
|
+
return mat2_build(klass, value_to_double(x), 0.0, 0.0, value_to_double(y));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static VALUE mat2_class_from_vec2(VALUE klass, VALUE v1, VALUE v2) {
|
|
101
|
+
double x1 = value_to_double(rb_funcall(v1, rb_intern("x"), 0));
|
|
102
|
+
double y1 = value_to_double(rb_funcall(v1, rb_intern("y"), 0));
|
|
103
|
+
double x2 = value_to_double(rb_funcall(v2, rb_intern("x"), 0));
|
|
104
|
+
double y2 = value_to_double(rb_funcall(v2, rb_intern("y"), 0));
|
|
105
|
+
return mat2_build(klass, x1, y1, x2, y2);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
VALUE mat2_aref(VALUE self, VALUE index) {
|
|
109
|
+
Mat2Data *data = mat2_get(self);
|
|
110
|
+
long idx = NUM2LONG(index);
|
|
111
|
+
if (idx < 0 || idx > 3) {
|
|
112
|
+
return Qnil;
|
|
113
|
+
}
|
|
114
|
+
return DBL2NUM(data->data[idx]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
VALUE mat2_aset(VALUE self, VALUE index, VALUE value) {
|
|
118
|
+
Mat2Data *data = mat2_get(self);
|
|
119
|
+
long idx = NUM2LONG(index);
|
|
120
|
+
if (idx < 0 || idx > 3) {
|
|
121
|
+
rb_raise(rb_eIndexError, "index %ld out of range", idx);
|
|
122
|
+
}
|
|
123
|
+
data->data[idx] = value_to_double(value);
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
VALUE mat2_mul(VALUE self, VALUE other) {
|
|
128
|
+
Mat2Data *a = mat2_get(self);
|
|
129
|
+
|
|
130
|
+
if (rb_obj_is_kind_of(other, cMat2)) {
|
|
131
|
+
Mat2Data *b = mat2_get(other);
|
|
132
|
+
return mat2_build(rb_obj_class(self),
|
|
133
|
+
a->data[0] * b->data[0] + a->data[2] * b->data[1],
|
|
134
|
+
a->data[1] * b->data[0] + a->data[3] * b->data[1],
|
|
135
|
+
a->data[0] * b->data[2] + a->data[2] * b->data[3],
|
|
136
|
+
a->data[1] * b->data[2] + a->data[3] * b->data[3]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (rb_obj_is_kind_of(other, cVec2)) {
|
|
140
|
+
double x = value_to_double(rb_funcall(other, rb_intern("x"), 0));
|
|
141
|
+
double y = value_to_double(rb_funcall(other, rb_intern("y"), 0));
|
|
142
|
+
VALUE vec2_class = rb_const_get(mLarb, rb_intern("Vec2"));
|
|
143
|
+
return rb_funcall(vec2_class, rb_intern("new"), 2,
|
|
144
|
+
DBL2NUM(a->data[0] * x + a->data[2] * y),
|
|
145
|
+
DBL2NUM(a->data[1] * x + a->data[3] * y));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (rb_obj_is_kind_of(other, rb_cNumeric)) {
|
|
149
|
+
double s = value_to_double(other);
|
|
150
|
+
return mat2_build(rb_obj_class(self), a->data[0] * s, a->data[1] * s,
|
|
151
|
+
a->data[2] * s, a->data[3] * s);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return Qnil;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
VALUE mat2_add(VALUE self, VALUE other) {
|
|
158
|
+
Mat2Data *a = mat2_get(self);
|
|
159
|
+
Mat2Data *b = mat2_get(other);
|
|
160
|
+
return mat2_build(rb_obj_class(self), a->data[0] + b->data[0],
|
|
161
|
+
a->data[1] + b->data[1], a->data[2] + b->data[2],
|
|
162
|
+
a->data[3] + b->data[3]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
VALUE mat2_sub(VALUE self, VALUE other) {
|
|
166
|
+
Mat2Data *a = mat2_get(self);
|
|
167
|
+
Mat2Data *b = mat2_get(other);
|
|
168
|
+
return mat2_build(rb_obj_class(self), a->data[0] - b->data[0],
|
|
169
|
+
a->data[1] - b->data[1], a->data[2] - b->data[2],
|
|
170
|
+
a->data[3] - b->data[3]);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
VALUE mat2_determinant(VALUE self) {
|
|
174
|
+
Mat2Data *a = mat2_get(self);
|
|
175
|
+
return DBL2NUM(a->data[0] * a->data[3] - a->data[2] * a->data[1]);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
VALUE mat2_inverse(VALUE self) {
|
|
179
|
+
Mat2Data *a = mat2_get(self);
|
|
180
|
+
double det = a->data[0] * a->data[3] - a->data[2] * a->data[1];
|
|
181
|
+
if (fabs(det) < 1e-10) {
|
|
182
|
+
rb_raise(rb_eRuntimeError, "Matrix is not invertible");
|
|
183
|
+
}
|
|
184
|
+
double inv_det = 1.0 / det;
|
|
185
|
+
return mat2_build(rb_obj_class(self), a->data[3] * inv_det,
|
|
186
|
+
-a->data[1] * inv_det, -a->data[2] * inv_det,
|
|
187
|
+
a->data[0] * inv_det);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
VALUE mat2_transpose(VALUE self) {
|
|
191
|
+
Mat2Data *a = mat2_get(self);
|
|
192
|
+
return mat2_build(rb_obj_class(self), a->data[0], a->data[2], a->data[1],
|
|
193
|
+
a->data[3]);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
VALUE mat2_adjoint(VALUE self) {
|
|
197
|
+
Mat2Data *a = mat2_get(self);
|
|
198
|
+
return mat2_build(rb_obj_class(self), a->data[3], -a->data[1], -a->data[2],
|
|
199
|
+
a->data[0]);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
VALUE mat2_frobenius_norm(VALUE self) {
|
|
203
|
+
Mat2Data *a = mat2_get(self);
|
|
204
|
+
double sum = a->data[0] * a->data[0] + a->data[1] * a->data[1] +
|
|
205
|
+
a->data[2] * a->data[2] + a->data[3] * a->data[3];
|
|
206
|
+
return DBL2NUM(sqrt(sum));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
VALUE mat2_to_a(VALUE self) {
|
|
210
|
+
Mat2Data *a = mat2_get(self);
|
|
211
|
+
VALUE ary = rb_ary_new_capa(4);
|
|
212
|
+
rb_ary_push(ary, DBL2NUM(a->data[0]));
|
|
213
|
+
rb_ary_push(ary, DBL2NUM(a->data[1]));
|
|
214
|
+
rb_ary_push(ary, DBL2NUM(a->data[2]));
|
|
215
|
+
rb_ary_push(ary, DBL2NUM(a->data[3]));
|
|
216
|
+
return ary;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
VALUE mat2_data(VALUE self) {
|
|
220
|
+
return mat2_to_a(self);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
VALUE mat2_equal(VALUE self, VALUE other) {
|
|
224
|
+
if (!rb_obj_is_kind_of(other, cMat2)) {
|
|
225
|
+
return Qfalse;
|
|
226
|
+
}
|
|
227
|
+
Mat2Data *a = mat2_get(self);
|
|
228
|
+
Mat2Data *b = mat2_get(other);
|
|
229
|
+
return (a->data[0] == b->data[0] && a->data[1] == b->data[1] &&
|
|
230
|
+
a->data[2] == b->data[2] && a->data[3] == b->data[3])
|
|
231
|
+
? Qtrue
|
|
232
|
+
: Qfalse;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
VALUE mat2_near(int argc, VALUE *argv, VALUE self) {
|
|
236
|
+
VALUE other = Qnil;
|
|
237
|
+
VALUE epsilon = Qnil;
|
|
238
|
+
|
|
239
|
+
rb_scan_args(argc, argv, "11", &other, &epsilon);
|
|
240
|
+
Mat2Data *a = mat2_get(self);
|
|
241
|
+
Mat2Data *b = mat2_get(other);
|
|
242
|
+
double eps = NIL_P(epsilon) ? 1e-6 : value_to_double(epsilon);
|
|
243
|
+
|
|
244
|
+
if (fabs(a->data[0] - b->data[0]) < eps &&
|
|
245
|
+
fabs(a->data[1] - b->data[1]) < eps &&
|
|
246
|
+
fabs(a->data[2] - b->data[2]) < eps &&
|
|
247
|
+
fabs(a->data[3] - b->data[3]) < eps) {
|
|
248
|
+
return Qtrue;
|
|
249
|
+
}
|
|
250
|
+
return Qfalse;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
VALUE mat2_inspect(VALUE self) {
|
|
254
|
+
Mat2Data *a = mat2_get(self);
|
|
255
|
+
VALUE s0 = rb_funcall(DBL2NUM(a->data[0]), rb_intern("to_s"), 0);
|
|
256
|
+
VALUE s1 = rb_funcall(DBL2NUM(a->data[1]), rb_intern("to_s"), 0);
|
|
257
|
+
VALUE s2 = rb_funcall(DBL2NUM(a->data[2]), rb_intern("to_s"), 0);
|
|
258
|
+
VALUE s3 = rb_funcall(DBL2NUM(a->data[3]), rb_intern("to_s"), 0);
|
|
259
|
+
VALUE str = rb_str_new_cstr("Mat2[");
|
|
260
|
+
rb_str_concat(str, s0);
|
|
261
|
+
rb_str_cat_cstr(str, ", ");
|
|
262
|
+
rb_str_concat(str, s1);
|
|
263
|
+
rb_str_cat_cstr(str, ", ");
|
|
264
|
+
rb_str_concat(str, s2);
|
|
265
|
+
rb_str_cat_cstr(str, ", ");
|
|
266
|
+
rb_str_concat(str, s3);
|
|
267
|
+
rb_str_cat_cstr(str, "]");
|
|
268
|
+
return str;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
void Init_mat2(VALUE module) {
|
|
272
|
+
cMat2 = rb_define_class_under(module, "Mat2", rb_cObject);
|
|
273
|
+
cVec2 = rb_const_get(mLarb, rb_intern("Vec2"));
|
|
274
|
+
|
|
275
|
+
rb_define_alloc_func(cMat2, mat2_alloc);
|
|
276
|
+
rb_define_method(cMat2, "initialize", mat2_initialize, -1);
|
|
277
|
+
|
|
278
|
+
rb_define_singleton_method(cMat2, "identity", mat2_class_identity, 0);
|
|
279
|
+
rb_define_singleton_method(cMat2, "zero", mat2_class_zero, 0);
|
|
280
|
+
rb_define_singleton_method(cMat2, "rotation", mat2_class_rotation, 1);
|
|
281
|
+
rb_define_singleton_method(cMat2, "scaling", mat2_class_scaling, 2);
|
|
282
|
+
rb_define_singleton_method(cMat2, "from_vec2", mat2_class_from_vec2, 2);
|
|
283
|
+
|
|
284
|
+
rb_define_method(cMat2, "data", mat2_data, 0);
|
|
285
|
+
rb_define_method(cMat2, "[]", mat2_aref, 1);
|
|
286
|
+
rb_define_method(cMat2, "[]=", mat2_aset, 2);
|
|
287
|
+
rb_define_method(cMat2, "*", mat2_mul, 1);
|
|
288
|
+
rb_define_method(cMat2, "+", mat2_add, 1);
|
|
289
|
+
rb_define_method(cMat2, "-", mat2_sub, 1);
|
|
290
|
+
rb_define_method(cMat2, "determinant", mat2_determinant, 0);
|
|
291
|
+
rb_define_method(cMat2, "inverse", mat2_inverse, 0);
|
|
292
|
+
rb_define_method(cMat2, "transpose", mat2_transpose, 0);
|
|
293
|
+
rb_define_method(cMat2, "adjoint", mat2_adjoint, 0);
|
|
294
|
+
rb_define_method(cMat2, "frobenius_norm", mat2_frobenius_norm, 0);
|
|
295
|
+
rb_define_method(cMat2, "to_a", mat2_to_a, 0);
|
|
296
|
+
rb_define_method(cMat2, "==", mat2_equal, 1);
|
|
297
|
+
rb_define_method(cMat2, "near?", mat2_near, -1);
|
|
298
|
+
rb_define_method(cMat2, "inspect", mat2_inspect, 0);
|
|
299
|
+
rb_define_alias(cMat2, "to_s", "inspect");
|
|
300
|
+
}
|
data/ext/larb/mat2.h
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#ifndef MAT2_H
|
|
2
|
+
#define MAT2_H
|
|
3
|
+
|
|
4
|
+
#include "larb.h"
|
|
5
|
+
|
|
6
|
+
typedef struct {
|
|
7
|
+
double data[4];
|
|
8
|
+
} Mat2Data;
|
|
9
|
+
|
|
10
|
+
void Init_mat2(VALUE module);
|
|
11
|
+
VALUE mat2_alloc(VALUE klass);
|
|
12
|
+
VALUE mat2_initialize(int argc, VALUE *argv, VALUE self);
|
|
13
|
+
|
|
14
|
+
VALUE mat2_aref(VALUE self, VALUE index);
|
|
15
|
+
VALUE mat2_aset(VALUE self, VALUE index, VALUE value);
|
|
16
|
+
VALUE mat2_mul(VALUE self, VALUE other);
|
|
17
|
+
VALUE mat2_add(VALUE self, VALUE other);
|
|
18
|
+
VALUE mat2_sub(VALUE self, VALUE other);
|
|
19
|
+
VALUE mat2_determinant(VALUE self);
|
|
20
|
+
VALUE mat2_inverse(VALUE self);
|
|
21
|
+
VALUE mat2_transpose(VALUE self);
|
|
22
|
+
VALUE mat2_adjoint(VALUE self);
|
|
23
|
+
VALUE mat2_frobenius_norm(VALUE self);
|
|
24
|
+
VALUE mat2_to_a(VALUE self);
|
|
25
|
+
VALUE mat2_equal(VALUE self, VALUE other);
|
|
26
|
+
VALUE mat2_near(int argc, VALUE *argv, VALUE self);
|
|
27
|
+
VALUE mat2_inspect(VALUE self);
|
|
28
|
+
VALUE mat2_data(VALUE self);
|
|
29
|
+
|
|
30
|
+
#endif
|
data/ext/larb/mat2d.c
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
#include "mat2d.h"
|
|
2
|
+
|
|
3
|
+
#include <math.h>
|
|
4
|
+
|
|
5
|
+
static void mat2d_free(void *ptr) {
|
|
6
|
+
xfree(ptr);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static size_t mat2d_memsize(const void *ptr) {
|
|
10
|
+
return sizeof(Mat2dData);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static const rb_data_type_t mat2d_type = {
|
|
14
|
+
"Mat2d",
|
|
15
|
+
{0, mat2d_free, mat2d_memsize},
|
|
16
|
+
0,
|
|
17
|
+
0,
|
|
18
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
static VALUE cMat2d = Qnil;
|
|
22
|
+
static VALUE cVec2 = Qnil;
|
|
23
|
+
static VALUE cMat3 = Qnil;
|
|
24
|
+
|
|
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
|
+
static Mat2dData *mat2d_get(VALUE obj) {
|
|
31
|
+
Mat2dData *data = NULL;
|
|
32
|
+
TypedData_Get_Struct(obj, Mat2dData, &mat2d_type, data);
|
|
33
|
+
return data;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static VALUE mat2d_build(VALUE klass, const double *values) {
|
|
37
|
+
VALUE obj = mat2d_alloc(klass);
|
|
38
|
+
Mat2dData *data = mat2d_get(obj);
|
|
39
|
+
for (int i = 0; i < 6; i++) {
|
|
40
|
+
data->data[i] = values[i];
|
|
41
|
+
}
|
|
42
|
+
return obj;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static VALUE mat2d_build6(VALUE klass, double v0, double v1, double v2,
|
|
46
|
+
double v3, double v4, double v5) {
|
|
47
|
+
double values[6] = {v0, v1, v2, v3, v4, v5};
|
|
48
|
+
return mat2d_build(klass, values);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
VALUE mat2d_alloc(VALUE klass) {
|
|
52
|
+
Mat2dData *data = ALLOC(Mat2dData);
|
|
53
|
+
data->data[0] = 1.0;
|
|
54
|
+
data->data[1] = 0.0;
|
|
55
|
+
data->data[2] = 0.0;
|
|
56
|
+
data->data[3] = 1.0;
|
|
57
|
+
data->data[4] = 0.0;
|
|
58
|
+
data->data[5] = 0.0;
|
|
59
|
+
return TypedData_Wrap_Struct(klass, &mat2d_type, data);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
VALUE mat2d_initialize(int argc, VALUE *argv, VALUE self) {
|
|
63
|
+
VALUE data_arg = Qnil;
|
|
64
|
+
Mat2dData *data = mat2d_get(self);
|
|
65
|
+
|
|
66
|
+
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));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return self;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static VALUE mat2d_class_identity(VALUE klass) {
|
|
90
|
+
return mat2d_alloc(klass);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static VALUE mat2d_class_zero(VALUE klass) {
|
|
94
|
+
return mat2d_build6(klass, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
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));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static VALUE mat2d_class_rotation(VALUE klass, VALUE radians) {
|
|
103
|
+
double r = value_to_double(radians);
|
|
104
|
+
double c = cos(r);
|
|
105
|
+
double s = sin(r);
|
|
106
|
+
return mat2d_build6(klass, c, s, -s, c, 0.0, 0.0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
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);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static VALUE mat2d_class_from_rotation_translation_scale(VALUE klass,
|
|
115
|
+
VALUE rotation,
|
|
116
|
+
VALUE translation,
|
|
117
|
+
VALUE scale) {
|
|
118
|
+
double r = value_to_double(rotation);
|
|
119
|
+
double c = cos(r);
|
|
120
|
+
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));
|
|
125
|
+
|
|
126
|
+
return mat2d_build6(klass, c * sx, s * sx, -s * sy, c * sy, tx, ty);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
VALUE mat2d_aref(VALUE self, VALUE index) {
|
|
130
|
+
Mat2dData *data = mat2d_get(self);
|
|
131
|
+
long idx = NUM2LONG(index);
|
|
132
|
+
if (idx < 0 || idx > 5) {
|
|
133
|
+
return Qnil;
|
|
134
|
+
}
|
|
135
|
+
return DBL2NUM(data->data[idx]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
VALUE mat2d_aset(VALUE self, VALUE index, VALUE value) {
|
|
139
|
+
Mat2dData *data = mat2d_get(self);
|
|
140
|
+
long idx = NUM2LONG(index);
|
|
141
|
+
if (idx < 0 || idx > 5) {
|
|
142
|
+
rb_raise(rb_eIndexError, "index %ld out of range", idx);
|
|
143
|
+
}
|
|
144
|
+
data->data[idx] = value_to_double(value);
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
VALUE mat2d_mul(VALUE self, VALUE other) {
|
|
149
|
+
Mat2dData *a = mat2d_get(self);
|
|
150
|
+
|
|
151
|
+
if (rb_obj_is_kind_of(other, cMat2d)) {
|
|
152
|
+
Mat2dData *b = mat2d_get(other);
|
|
153
|
+
return mat2d_build6(
|
|
154
|
+
rb_obj_class(self),
|
|
155
|
+
a->data[0] * b->data[0] + a->data[2] * b->data[1],
|
|
156
|
+
a->data[1] * b->data[0] + a->data[3] * b->data[1],
|
|
157
|
+
a->data[0] * b->data[2] + a->data[2] * b->data[3],
|
|
158
|
+
a->data[1] * b->data[2] + a->data[3] * b->data[3],
|
|
159
|
+
a->data[0] * b->data[4] + a->data[2] * b->data[5] + a->data[4],
|
|
160
|
+
a->data[1] * b->data[4] + a->data[3] * b->data[5] + a->data[5]);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
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));
|
|
166
|
+
VALUE vec2_class = rb_const_get(mLarb, rb_intern("Vec2"));
|
|
167
|
+
return rb_funcall(vec2_class, rb_intern("new"), 2,
|
|
168
|
+
DBL2NUM(a->data[0] * x + a->data[2] * y + a->data[4]),
|
|
169
|
+
DBL2NUM(a->data[1] * x + a->data[3] * y + a->data[5]));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (rb_obj_is_kind_of(other, rb_cNumeric)) {
|
|
173
|
+
double s = value_to_double(other);
|
|
174
|
+
return mat2d_build6(rb_obj_class(self), a->data[0] * s, a->data[1] * s,
|
|
175
|
+
a->data[2] * s, a->data[3] * s, a->data[4] * s,
|
|
176
|
+
a->data[5] * s);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return Qnil;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
VALUE mat2d_add(VALUE self, VALUE other) {
|
|
183
|
+
Mat2dData *a = mat2d_get(self);
|
|
184
|
+
Mat2dData *b = mat2d_get(other);
|
|
185
|
+
return mat2d_build6(rb_obj_class(self), a->data[0] + b->data[0],
|
|
186
|
+
a->data[1] + b->data[1], a->data[2] + b->data[2],
|
|
187
|
+
a->data[3] + b->data[3], a->data[4] + b->data[4],
|
|
188
|
+
a->data[5] + b->data[5]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
VALUE mat2d_sub(VALUE self, VALUE other) {
|
|
192
|
+
Mat2dData *a = mat2d_get(self);
|
|
193
|
+
Mat2dData *b = mat2d_get(other);
|
|
194
|
+
return mat2d_build6(rb_obj_class(self), a->data[0] - b->data[0],
|
|
195
|
+
a->data[1] - b->data[1], a->data[2] - b->data[2],
|
|
196
|
+
a->data[3] - b->data[3], a->data[4] - b->data[4],
|
|
197
|
+
a->data[5] - b->data[5]);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
VALUE mat2d_determinant(VALUE self) {
|
|
201
|
+
Mat2dData *a = mat2d_get(self);
|
|
202
|
+
return DBL2NUM(a->data[0] * a->data[3] - a->data[1] * a->data[2]);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
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);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
VALUE mat2d_translate(VALUE self, VALUE x, VALUE y) {
|
|
222
|
+
Mat2dData *a = mat2d_get(self);
|
|
223
|
+
double dx = value_to_double(x);
|
|
224
|
+
double dy = value_to_double(y);
|
|
225
|
+
return mat2d_build6(rb_obj_class(self), a->data[0], a->data[1], a->data[2],
|
|
226
|
+
a->data[3], a->data[0] * dx + a->data[2] * dy + a->data[4],
|
|
227
|
+
a->data[1] * dx + a->data[3] * dy + a->data[5]);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
VALUE mat2d_rotate(VALUE self, VALUE radians) {
|
|
231
|
+
VALUE rot = mat2d_class_rotation(cMat2d, radians);
|
|
232
|
+
return mat2d_mul(self, rot);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
VALUE mat2d_scale(VALUE self, VALUE x, VALUE y) {
|
|
236
|
+
Mat2dData *a = mat2d_get(self);
|
|
237
|
+
double sx = value_to_double(x);
|
|
238
|
+
double sy = value_to_double(y);
|
|
239
|
+
return mat2d_build6(rb_obj_class(self), a->data[0] * sx, a->data[1] * sx,
|
|
240
|
+
a->data[2] * sy, a->data[3] * sy, a->data[4], a->data[5]);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
VALUE mat2d_extract_translation(VALUE self) {
|
|
244
|
+
Mat2dData *a = mat2d_get(self);
|
|
245
|
+
VALUE vec2_class = rb_const_get(mLarb, rb_intern("Vec2"));
|
|
246
|
+
return rb_funcall(vec2_class, rb_intern("new"), 2, DBL2NUM(a->data[4]),
|
|
247
|
+
DBL2NUM(a->data[5]));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
VALUE mat2d_extract_rotation(VALUE self) {
|
|
251
|
+
Mat2dData *a = mat2d_get(self);
|
|
252
|
+
return DBL2NUM(atan2(a->data[1], a->data[0]));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
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));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
VALUE mat2d_frobenius_norm(VALUE self) {
|
|
264
|
+
Mat2dData *a = mat2d_get(self);
|
|
265
|
+
double sum = 0.0;
|
|
266
|
+
for (int i = 0; i < 6; i++) {
|
|
267
|
+
sum += a->data[i] * a->data[i];
|
|
268
|
+
}
|
|
269
|
+
return DBL2NUM(sqrt(sum));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
VALUE mat2d_to_mat3(VALUE self) {
|
|
273
|
+
Mat2dData *a = mat2d_get(self);
|
|
274
|
+
VALUE mat3_class = rb_const_get(mLarb, rb_intern("Mat3"));
|
|
275
|
+
return rb_funcall(mat3_class, rb_intern("new"), 1,
|
|
276
|
+
rb_ary_new3(9, DBL2NUM(a->data[0]), DBL2NUM(a->data[1]),
|
|
277
|
+
DBL2NUM(0.0), DBL2NUM(a->data[2]),
|
|
278
|
+
DBL2NUM(a->data[3]), DBL2NUM(0.0),
|
|
279
|
+
DBL2NUM(a->data[4]), DBL2NUM(a->data[5]),
|
|
280
|
+
DBL2NUM(1.0)));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
VALUE mat2d_to_a(VALUE self) {
|
|
284
|
+
Mat2dData *a = mat2d_get(self);
|
|
285
|
+
VALUE ary = rb_ary_new_capa(6);
|
|
286
|
+
for (int i = 0; i < 6; i++) {
|
|
287
|
+
rb_ary_push(ary, DBL2NUM(a->data[i]));
|
|
288
|
+
}
|
|
289
|
+
return ary;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
VALUE mat2d_data(VALUE self) {
|
|
293
|
+
return mat2d_to_a(self);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
VALUE mat2d_equal(VALUE self, VALUE other) {
|
|
297
|
+
if (!rb_obj_is_kind_of(other, cMat2d)) {
|
|
298
|
+
return Qfalse;
|
|
299
|
+
}
|
|
300
|
+
Mat2dData *a = mat2d_get(self);
|
|
301
|
+
Mat2dData *b = mat2d_get(other);
|
|
302
|
+
for (int i = 0; i < 6; i++) {
|
|
303
|
+
if (a->data[i] != b->data[i]) {
|
|
304
|
+
return Qfalse;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return Qtrue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
VALUE mat2d_near(int argc, VALUE *argv, VALUE self) {
|
|
311
|
+
VALUE other = Qnil;
|
|
312
|
+
VALUE epsilon = Qnil;
|
|
313
|
+
|
|
314
|
+
rb_scan_args(argc, argv, "11", &other, &epsilon);
|
|
315
|
+
Mat2dData *a = mat2d_get(self);
|
|
316
|
+
Mat2dData *b = mat2d_get(other);
|
|
317
|
+
double eps = NIL_P(epsilon) ? 1e-6 : value_to_double(epsilon);
|
|
318
|
+
|
|
319
|
+
for (int i = 0; i < 6; i++) {
|
|
320
|
+
if (fabs(a->data[i] - b->data[i]) >= eps) {
|
|
321
|
+
return Qfalse;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return Qtrue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
VALUE mat2d_inspect(VALUE self) {
|
|
328
|
+
Mat2dData *a = mat2d_get(self);
|
|
329
|
+
VALUE str = rb_str_new_cstr("Mat2d[");
|
|
330
|
+
for (int i = 0; i < 6; i++) {
|
|
331
|
+
VALUE formatted = rb_funcall(rb_mKernel, rb_intern("format"), 2,
|
|
332
|
+
rb_str_new_cstr("%.4f"), DBL2NUM(a->data[i]));
|
|
333
|
+
if (i > 0) {
|
|
334
|
+
rb_str_cat_cstr(str, ", ");
|
|
335
|
+
}
|
|
336
|
+
rb_str_concat(str, formatted);
|
|
337
|
+
}
|
|
338
|
+
rb_str_cat_cstr(str, "]");
|
|
339
|
+
return str;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
void Init_mat2d(VALUE module) {
|
|
343
|
+
cMat2d = rb_define_class_under(module, "Mat2d", rb_cObject);
|
|
344
|
+
cVec2 = rb_const_get(mLarb, rb_intern("Vec2"));
|
|
345
|
+
cMat3 = rb_const_get(mLarb, rb_intern("Mat3"));
|
|
346
|
+
|
|
347
|
+
rb_define_alloc_func(cMat2d, mat2d_alloc);
|
|
348
|
+
rb_define_method(cMat2d, "initialize", mat2d_initialize, -1);
|
|
349
|
+
|
|
350
|
+
rb_define_singleton_method(cMat2d, "identity", mat2d_class_identity, 0);
|
|
351
|
+
rb_define_singleton_method(cMat2d, "zero", mat2d_class_zero, 0);
|
|
352
|
+
rb_define_singleton_method(cMat2d, "translation", mat2d_class_translation,
|
|
353
|
+
2);
|
|
354
|
+
rb_define_singleton_method(cMat2d, "rotation", mat2d_class_rotation, 1);
|
|
355
|
+
rb_define_singleton_method(cMat2d, "scaling", mat2d_class_scaling, 2);
|
|
356
|
+
rb_define_singleton_method(cMat2d, "from_rotation_translation_scale",
|
|
357
|
+
mat2d_class_from_rotation_translation_scale, 3);
|
|
358
|
+
|
|
359
|
+
rb_define_method(cMat2d, "data", mat2d_data, 0);
|
|
360
|
+
rb_define_method(cMat2d, "[]", mat2d_aref, 1);
|
|
361
|
+
rb_define_method(cMat2d, "[]=", mat2d_aset, 2);
|
|
362
|
+
rb_define_method(cMat2d, "*", mat2d_mul, 1);
|
|
363
|
+
rb_define_method(cMat2d, "+", mat2d_add, 1);
|
|
364
|
+
rb_define_method(cMat2d, "-", mat2d_sub, 1);
|
|
365
|
+
rb_define_method(cMat2d, "determinant", mat2d_determinant, 0);
|
|
366
|
+
rb_define_method(cMat2d, "inverse", mat2d_inverse, 0);
|
|
367
|
+
rb_define_method(cMat2d, "translate", mat2d_translate, 2);
|
|
368
|
+
rb_define_method(cMat2d, "rotate", mat2d_rotate, 1);
|
|
369
|
+
rb_define_method(cMat2d, "scale", mat2d_scale, 2);
|
|
370
|
+
rb_define_method(cMat2d, "extract_translation", mat2d_extract_translation, 0);
|
|
371
|
+
rb_define_method(cMat2d, "extract_rotation", mat2d_extract_rotation, 0);
|
|
372
|
+
rb_define_method(cMat2d, "extract_scale", mat2d_extract_scale, 0);
|
|
373
|
+
rb_define_method(cMat2d, "frobenius_norm", mat2d_frobenius_norm, 0);
|
|
374
|
+
rb_define_method(cMat2d, "to_mat3", mat2d_to_mat3, 0);
|
|
375
|
+
rb_define_method(cMat2d, "to_a", mat2d_to_a, 0);
|
|
376
|
+
rb_define_method(cMat2d, "==", mat2d_equal, 1);
|
|
377
|
+
rb_define_method(cMat2d, "near?", mat2d_near, -1);
|
|
378
|
+
rb_define_method(cMat2d, "inspect", mat2d_inspect, 0);
|
|
379
|
+
rb_define_alias(cMat2d, "to_s", "inspect");
|
|
380
|
+
}
|