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/quat.c
CHANGED
|
@@ -21,11 +21,6 @@ static const rb_data_type_t quat_type = {
|
|
|
21
21
|
static VALUE cQuat = Qnil;
|
|
22
22
|
static VALUE cVec3 = Qnil;
|
|
23
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
24
|
static QuatData *quat_get(VALUE obj) {
|
|
30
25
|
QuatData *data = NULL;
|
|
31
26
|
TypedData_Get_Struct(obj, QuatData, &quat_type, data);
|
|
@@ -53,11 +48,19 @@ static double clamp_double(double value, double min, double max) {
|
|
|
53
48
|
}
|
|
54
49
|
|
|
55
50
|
static void normalize_quat(double *x, double *y, double *z, double *w) {
|
|
56
|
-
double
|
|
57
|
-
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
51
|
+
double values[4] = {*x, *y, *z, *w};
|
|
52
|
+
larb_normalize(values, 4);
|
|
53
|
+
*x = values[0];
|
|
54
|
+
*y = values[1];
|
|
55
|
+
*z = values[2];
|
|
56
|
+
*w = values[3];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static VALUE quat_initialize_copy(VALUE self, VALUE other) {
|
|
60
|
+
if (self == other) return self;
|
|
61
|
+
rb_obj_init_copy(self, other);
|
|
62
|
+
*quat_get(self) = *quat_get(other);
|
|
63
|
+
return self;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
VALUE quat_alloc(VALUE klass) {
|
|
@@ -70,6 +73,7 @@ VALUE quat_alloc(VALUE klass) {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
VALUE quat_initialize(int argc, VALUE *argv, VALUE self) {
|
|
76
|
+
rb_check_frozen(self);
|
|
73
77
|
VALUE vx = Qnil;
|
|
74
78
|
VALUE vy = Qnil;
|
|
75
79
|
VALUE vz = Qnil;
|
|
@@ -77,18 +81,20 @@ VALUE quat_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
77
81
|
QuatData *data = quat_get(self);
|
|
78
82
|
|
|
79
83
|
rb_scan_args(argc, argv, "04", &vx, &vy, &vz, &vw);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
QuatData values = {argc > 0 ? NUM2DBL(vx) : 0.0,
|
|
85
|
+
argc > 1 ? NUM2DBL(vy) : 0.0,
|
|
86
|
+
argc > 2 ? NUM2DBL(vz) : 0.0,
|
|
87
|
+
argc > 3 ? NUM2DBL(vw) : 1.0};
|
|
88
|
+
rb_check_frozen(self);
|
|
89
|
+
*data = values;
|
|
84
90
|
|
|
85
91
|
return self;
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
static VALUE quat_class_bracket(VALUE klass, VALUE x, VALUE y, VALUE z,
|
|
89
95
|
VALUE w) {
|
|
90
|
-
return quat_build(klass,
|
|
91
|
-
|
|
96
|
+
return quat_build(klass, NUM2DBL(x), NUM2DBL(y),
|
|
97
|
+
NUM2DBL(z), NUM2DBL(w));
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
static VALUE quat_class_identity(VALUE klass) {
|
|
@@ -97,19 +103,19 @@ static VALUE quat_class_identity(VALUE klass) {
|
|
|
97
103
|
|
|
98
104
|
static VALUE quat_class_from_axis_angle(VALUE klass, VALUE axis,
|
|
99
105
|
VALUE radians) {
|
|
100
|
-
double half =
|
|
106
|
+
double half = NUM2DBL(radians) * 0.5;
|
|
101
107
|
double s = sin(half);
|
|
102
108
|
VALUE normalized = rb_funcall(axis, rb_intern("normalize"), 0);
|
|
103
|
-
double x =
|
|
104
|
-
double y =
|
|
105
|
-
double z =
|
|
109
|
+
double x = NUM2DBL(rb_funcall(normalized, rb_intern("x"), 0));
|
|
110
|
+
double y = NUM2DBL(rb_funcall(normalized, rb_intern("y"), 0));
|
|
111
|
+
double z = NUM2DBL(rb_funcall(normalized, rb_intern("z"), 0));
|
|
106
112
|
return quat_build(klass, x * s, y * s, z * s, cos(half));
|
|
107
113
|
}
|
|
108
114
|
|
|
109
115
|
static VALUE quat_class_from_euler(VALUE klass, VALUE x, VALUE y, VALUE z) {
|
|
110
|
-
double rx =
|
|
111
|
-
double ry =
|
|
112
|
-
double rz =
|
|
116
|
+
double rx = NUM2DBL(x) * 0.5;
|
|
117
|
+
double ry = NUM2DBL(y) * 0.5;
|
|
118
|
+
double rz = NUM2DBL(z) * 0.5;
|
|
113
119
|
double cx = cos(rx);
|
|
114
120
|
double sx = sin(rx);
|
|
115
121
|
double cy = cos(ry);
|
|
@@ -136,15 +142,15 @@ static VALUE quat_class_look_rotation(int argc, VALUE *argv, VALUE klass) {
|
|
|
136
142
|
VALUE up_final =
|
|
137
143
|
rb_funcall(forward_norm, rb_intern("cross"), 1, right);
|
|
138
144
|
|
|
139
|
-
double m00 =
|
|
140
|
-
double m01 =
|
|
141
|
-
double m02 =
|
|
142
|
-
double m10 =
|
|
143
|
-
double m11 =
|
|
144
|
-
double m12 =
|
|
145
|
-
double m20 =
|
|
146
|
-
double m21 =
|
|
147
|
-
double m22 =
|
|
145
|
+
double m00 = NUM2DBL(rb_funcall(right, rb_intern("x"), 0));
|
|
146
|
+
double m01 = NUM2DBL(rb_funcall(up_final, rb_intern("x"), 0));
|
|
147
|
+
double m02 = NUM2DBL(rb_funcall(forward_norm, rb_intern("x"), 0));
|
|
148
|
+
double m10 = NUM2DBL(rb_funcall(right, rb_intern("y"), 0));
|
|
149
|
+
double m11 = NUM2DBL(rb_funcall(up_final, rb_intern("y"), 0));
|
|
150
|
+
double m12 = NUM2DBL(rb_funcall(forward_norm, rb_intern("y"), 0));
|
|
151
|
+
double m20 = NUM2DBL(rb_funcall(right, rb_intern("z"), 0));
|
|
152
|
+
double m21 = NUM2DBL(rb_funcall(up_final, rb_intern("z"), 0));
|
|
153
|
+
double m22 = NUM2DBL(rb_funcall(forward_norm, rb_intern("z"), 0));
|
|
148
154
|
|
|
149
155
|
double trace = m00 + m11 + m22;
|
|
150
156
|
if (trace > 0.0) {
|
|
@@ -173,8 +179,11 @@ static VALUE quat_get_x(VALUE self) {
|
|
|
173
179
|
}
|
|
174
180
|
|
|
175
181
|
static VALUE quat_set_x(VALUE self, VALUE value) {
|
|
182
|
+
rb_check_frozen(self);
|
|
176
183
|
QuatData *data = quat_get(self);
|
|
177
|
-
|
|
184
|
+
double number = NUM2DBL(value);
|
|
185
|
+
rb_check_frozen(self);
|
|
186
|
+
data->x = number;
|
|
178
187
|
return value;
|
|
179
188
|
}
|
|
180
189
|
|
|
@@ -184,8 +193,11 @@ static VALUE quat_get_y(VALUE self) {
|
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
static VALUE quat_set_y(VALUE self, VALUE value) {
|
|
196
|
+
rb_check_frozen(self);
|
|
187
197
|
QuatData *data = quat_get(self);
|
|
188
|
-
|
|
198
|
+
double number = NUM2DBL(value);
|
|
199
|
+
rb_check_frozen(self);
|
|
200
|
+
data->y = number;
|
|
189
201
|
return value;
|
|
190
202
|
}
|
|
191
203
|
|
|
@@ -195,8 +207,11 @@ static VALUE quat_get_z(VALUE self) {
|
|
|
195
207
|
}
|
|
196
208
|
|
|
197
209
|
static VALUE quat_set_z(VALUE self, VALUE value) {
|
|
210
|
+
rb_check_frozen(self);
|
|
198
211
|
QuatData *data = quat_get(self);
|
|
199
|
-
|
|
212
|
+
double number = NUM2DBL(value);
|
|
213
|
+
rb_check_frozen(self);
|
|
214
|
+
data->z = number;
|
|
200
215
|
return value;
|
|
201
216
|
}
|
|
202
217
|
|
|
@@ -206,8 +221,11 @@ static VALUE quat_get_w(VALUE self) {
|
|
|
206
221
|
}
|
|
207
222
|
|
|
208
223
|
static VALUE quat_set_w(VALUE self, VALUE value) {
|
|
224
|
+
rb_check_frozen(self);
|
|
209
225
|
QuatData *data = quat_get(self);
|
|
210
|
-
|
|
226
|
+
double number = NUM2DBL(value);
|
|
227
|
+
rb_check_frozen(self);
|
|
228
|
+
data->w = number;
|
|
211
229
|
return value;
|
|
212
230
|
}
|
|
213
231
|
|
|
@@ -224,9 +242,9 @@ VALUE quat_mul(VALUE self, VALUE other) {
|
|
|
224
242
|
}
|
|
225
243
|
|
|
226
244
|
if (rb_obj_is_kind_of(other, cVec3)) {
|
|
227
|
-
double vx =
|
|
228
|
-
double vy =
|
|
229
|
-
double vz =
|
|
245
|
+
double vx = NUM2DBL(rb_funcall(other, rb_intern("x"), 0));
|
|
246
|
+
double vy = NUM2DBL(rb_funcall(other, rb_intern("y"), 0));
|
|
247
|
+
double vz = NUM2DBL(rb_funcall(other, rb_intern("z"), 0));
|
|
230
248
|
|
|
231
249
|
double uvx = a->y * vz - a->z * vy;
|
|
232
250
|
double uvy = a->z * vx - a->x * vz;
|
|
@@ -245,12 +263,12 @@ VALUE quat_mul(VALUE self, VALUE other) {
|
|
|
245
263
|
}
|
|
246
264
|
|
|
247
265
|
if (rb_obj_is_kind_of(other, rb_cNumeric)) {
|
|
248
|
-
double s =
|
|
266
|
+
double s = NUM2DBL(other);
|
|
249
267
|
return quat_build(rb_obj_class(self), a->x * s, a->y * s, a->z * s,
|
|
250
268
|
a->w * s);
|
|
251
269
|
}
|
|
252
270
|
|
|
253
|
-
|
|
271
|
+
rb_raise(rb_eTypeError, "expected Quat, Vec3, or Numeric");
|
|
254
272
|
}
|
|
255
273
|
|
|
256
274
|
VALUE quat_add(VALUE self, VALUE other) {
|
|
@@ -280,7 +298,7 @@ VALUE quat_dot(VALUE self, VALUE other) {
|
|
|
280
298
|
|
|
281
299
|
VALUE quat_length(VALUE self) {
|
|
282
300
|
QuatData *a = quat_get(self);
|
|
283
|
-
return DBL2NUM(
|
|
301
|
+
return DBL2NUM(hypot(hypot(a->x, a->y), hypot(a->z, a->w)));
|
|
284
302
|
}
|
|
285
303
|
|
|
286
304
|
VALUE quat_length_squared(VALUE self) {
|
|
@@ -299,6 +317,7 @@ VALUE quat_normalize(VALUE self) {
|
|
|
299
317
|
}
|
|
300
318
|
|
|
301
319
|
VALUE quat_normalize_bang(VALUE self) {
|
|
320
|
+
rb_check_frozen(self);
|
|
302
321
|
QuatData *a = quat_get(self);
|
|
303
322
|
normalize_quat(&a->x, &a->y, &a->z, &a->w);
|
|
304
323
|
return self;
|
|
@@ -311,27 +330,44 @@ VALUE quat_conjugate(VALUE self) {
|
|
|
311
330
|
|
|
312
331
|
VALUE quat_inverse(VALUE self) {
|
|
313
332
|
QuatData *a = quat_get(self);
|
|
314
|
-
double
|
|
315
|
-
|
|
316
|
-
|
|
333
|
+
double values[4] = {a->x, a->y, a->z, a->w};
|
|
334
|
+
larb_normalize(values, 4);
|
|
335
|
+
double scale = fmax(fmax(fabs(a->x), fabs(a->y)),
|
|
336
|
+
fmax(fabs(a->z), fabs(a->w)));
|
|
337
|
+
double len = hypot(hypot(a->x / scale, a->y / scale),
|
|
338
|
+
hypot(a->z / scale, a->w / scale));
|
|
339
|
+
return quat_build(rb_obj_class(self), -values[0] / len / scale,
|
|
340
|
+
-values[1] / len / scale, -values[2] / len / scale,
|
|
341
|
+
values[3] / len / scale);
|
|
317
342
|
}
|
|
318
343
|
|
|
319
344
|
VALUE quat_lerp(VALUE self, VALUE other, VALUE t) {
|
|
320
|
-
QuatData
|
|
321
|
-
QuatData
|
|
322
|
-
double s =
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
double
|
|
326
|
-
|
|
345
|
+
QuatData a = *quat_get(self);
|
|
346
|
+
QuatData b = *quat_get(other);
|
|
347
|
+
double s = NUM2DBL(t);
|
|
348
|
+
normalize_quat(&a.x, &a.y, &a.z, &a.w);
|
|
349
|
+
normalize_quat(&b.x, &b.y, &b.z, &b.w);
|
|
350
|
+
double sign = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w < 0.0
|
|
351
|
+
? -1.0 : 1.0;
|
|
352
|
+
double x = a.x + (sign * b.x - a.x) * s;
|
|
353
|
+
double y = a.y + (sign * b.y - a.y) * s;
|
|
354
|
+
double z = a.z + (sign * b.z - a.z) * s;
|
|
355
|
+
double w = a.w + (sign * b.w - a.w) * s;
|
|
327
356
|
normalize_quat(&x, &y, &z, &w);
|
|
328
357
|
return quat_build(rb_obj_class(self), x, y, z, w);
|
|
329
358
|
}
|
|
330
359
|
|
|
331
360
|
VALUE quat_slerp(VALUE self, VALUE other, VALUE t) {
|
|
332
|
-
QuatData
|
|
333
|
-
QuatData
|
|
334
|
-
|
|
361
|
+
QuatData left = *quat_get(self);
|
|
362
|
+
QuatData right = *quat_get(other);
|
|
363
|
+
normalize_quat(&left.x, &left.y, &left.z, &left.w);
|
|
364
|
+
normalize_quat(&right.x, &right.y, &right.z, &right.w);
|
|
365
|
+
QuatData *a = &left;
|
|
366
|
+
QuatData *b = &right;
|
|
367
|
+
double s = NUM2DBL(t);
|
|
368
|
+
if (!isfinite(s)) {
|
|
369
|
+
rb_raise(rb_eArgError, "interpolation amount must be finite");
|
|
370
|
+
}
|
|
335
371
|
double dot = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w;
|
|
336
372
|
|
|
337
373
|
double ox = b->x;
|
|
@@ -362,23 +398,26 @@ VALUE quat_slerp(VALUE self, VALUE other, VALUE t) {
|
|
|
362
398
|
double s0 = cos(theta) - dot * sin_theta / sin_theta0;
|
|
363
399
|
double s1 = sin_theta / sin_theta0;
|
|
364
400
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
401
|
+
double x = a->x * s0 + ox * s1;
|
|
402
|
+
double y = a->y * s0 + oy * s1;
|
|
403
|
+
double z = a->z * s0 + oz * s1;
|
|
404
|
+
double w = a->w * s0 + ow * s1;
|
|
405
|
+
normalize_quat(&x, &y, &z, &w);
|
|
406
|
+
return quat_build(rb_obj_class(self), x, y, z, w);
|
|
368
407
|
}
|
|
369
408
|
|
|
370
409
|
VALUE quat_to_axis_angle(VALUE self) {
|
|
371
|
-
QuatData
|
|
372
|
-
|
|
373
|
-
double
|
|
374
|
-
double
|
|
410
|
+
QuatData a = *quat_get(self);
|
|
411
|
+
normalize_quat(&a.x, &a.y, &a.z, &a.w);
|
|
412
|
+
double s = hypot(hypot(a.x, a.y), a.z);
|
|
413
|
+
double angle = 2.0 * atan2(s, a.w);
|
|
375
414
|
VALUE axis;
|
|
376
|
-
if (s
|
|
415
|
+
if (s == 0.0) {
|
|
377
416
|
axis = rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(1.0), DBL2NUM(0.0),
|
|
378
417
|
DBL2NUM(0.0));
|
|
379
418
|
} else {
|
|
380
|
-
axis = rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(a
|
|
381
|
-
DBL2NUM(a
|
|
419
|
+
axis = rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(a.x / s),
|
|
420
|
+
DBL2NUM(a.y / s), DBL2NUM(a.z / s));
|
|
382
421
|
}
|
|
383
422
|
VALUE ary = rb_ary_new_capa(2);
|
|
384
423
|
rb_ary_push(ary, axis);
|
|
@@ -447,7 +486,10 @@ VALUE quat_near(int argc, VALUE *argv, VALUE self) {
|
|
|
447
486
|
rb_scan_args(argc, argv, "11", &other, &epsilon);
|
|
448
487
|
QuatData *a = quat_get(self);
|
|
449
488
|
QuatData *b = quat_get(other);
|
|
450
|
-
double eps =
|
|
489
|
+
double eps = argc < 2 ? 1e-6 : NUM2DBL(epsilon);
|
|
490
|
+
if (!isfinite(eps) || eps <= 0.0) {
|
|
491
|
+
rb_raise(rb_eArgError, "epsilon must be finite and positive");
|
|
492
|
+
}
|
|
451
493
|
|
|
452
494
|
if (fabs(a->x - b->x) < eps && fabs(a->y - b->y) < eps &&
|
|
453
495
|
fabs(a->z - b->z) < eps && fabs(a->w - b->w) < eps) {
|
|
@@ -480,6 +522,8 @@ void Init_quat(VALUE module) {
|
|
|
480
522
|
|
|
481
523
|
rb_define_alloc_func(cQuat, quat_alloc);
|
|
482
524
|
rb_define_method(cQuat, "initialize", quat_initialize, -1);
|
|
525
|
+
rb_define_private_method(cQuat, "initialize_copy",
|
|
526
|
+
quat_initialize_copy, 1);
|
|
483
527
|
|
|
484
528
|
rb_define_singleton_method(cQuat, "[]", quat_class_bracket, 4);
|
|
485
529
|
rb_define_singleton_method(cQuat, "identity", quat_class_identity, 0);
|
data/ext/larb/quat2.c
CHANGED
|
@@ -23,17 +23,59 @@ static VALUE cQuat = Qnil;
|
|
|
23
23
|
static VALUE cVec3 = Qnil;
|
|
24
24
|
static VALUE cMat4 = Qnil;
|
|
25
25
|
|
|
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
26
|
static Quat2Data *quat2_get(VALUE obj) {
|
|
32
27
|
Quat2Data *data = NULL;
|
|
33
28
|
TypedData_Get_Struct(obj, Quat2Data, &quat2_type, data);
|
|
34
29
|
return data;
|
|
35
30
|
}
|
|
36
31
|
|
|
32
|
+
static VALUE quat2_initialize_copy(VALUE self, VALUE other) {
|
|
33
|
+
if (self == other) return self;
|
|
34
|
+
rb_obj_init_copy(self, other);
|
|
35
|
+
*quat2_get(self) = *quat2_get(other);
|
|
36
|
+
return self;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static void normalize_quat2(double *values) {
|
|
40
|
+
for (int i = 0; i < 8; i++) {
|
|
41
|
+
if (!isfinite(values[i])) {
|
|
42
|
+
rb_raise(rb_eArgError, "dual quaternion must be finite");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
double scale = 0.0;
|
|
46
|
+
for (int i = 0; i < 4; i++) {
|
|
47
|
+
scale = fmax(scale, fabs(values[i]));
|
|
48
|
+
}
|
|
49
|
+
if (scale == 0.0) {
|
|
50
|
+
rb_raise(rb_eArgError, "real quaternion must be nonzero");
|
|
51
|
+
}
|
|
52
|
+
for (int i = 0; i < 4; i++) {
|
|
53
|
+
values[i] /= scale;
|
|
54
|
+
}
|
|
55
|
+
double len = hypot(hypot(values[0], values[1]), hypot(values[2], values[3]));
|
|
56
|
+
double dual_scale = 0.0;
|
|
57
|
+
for (int i = 0; i < 4; i++) {
|
|
58
|
+
values[i] /= len;
|
|
59
|
+
dual_scale = fmax(dual_scale, fabs(values[i + 4]));
|
|
60
|
+
}
|
|
61
|
+
if (dual_scale > 0.0) {
|
|
62
|
+
/* Remove the parallel component before scaling by the real length. */
|
|
63
|
+
double dot = 0.0;
|
|
64
|
+
for (int i = 0; i < 4; i++) {
|
|
65
|
+
dot += values[i] * (values[i + 4] / dual_scale);
|
|
66
|
+
}
|
|
67
|
+
double ratio = dual_scale / scale;
|
|
68
|
+
for (int i = 0; i < 4; i++) {
|
|
69
|
+
double component = (values[i + 4] / dual_scale - values[i] * dot) / len;
|
|
70
|
+
values[i + 4] = isfinite(ratio) ? component * ratio
|
|
71
|
+
: component * dual_scale / scale;
|
|
72
|
+
if (!isfinite(values[i + 4])) {
|
|
73
|
+
rb_raise(rb_eArgError, "normalized dual quaternion is not finite");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
37
79
|
static VALUE quat2_build(VALUE klass, const double *values) {
|
|
38
80
|
VALUE obj = quat2_alloc(klass);
|
|
39
81
|
Quat2Data *data = quat2_get(obj);
|
|
@@ -56,13 +98,13 @@ static VALUE quat2_class_identity(VALUE klass) {
|
|
|
56
98
|
|
|
57
99
|
static VALUE quat2_class_from_rotation_translation(VALUE klass, VALUE rotation,
|
|
58
100
|
VALUE translation) {
|
|
59
|
-
double rx =
|
|
60
|
-
double ry =
|
|
61
|
-
double rz =
|
|
62
|
-
double rw =
|
|
63
|
-
double tx =
|
|
64
|
-
double ty =
|
|
65
|
-
double tz =
|
|
101
|
+
double rx = NUM2DBL(rb_funcall(rotation, rb_intern("x"), 0));
|
|
102
|
+
double ry = NUM2DBL(rb_funcall(rotation, rb_intern("y"), 0));
|
|
103
|
+
double rz = NUM2DBL(rb_funcall(rotation, rb_intern("z"), 0));
|
|
104
|
+
double rw = NUM2DBL(rb_funcall(rotation, rb_intern("w"), 0));
|
|
105
|
+
double tx = NUM2DBL(rb_funcall(translation, rb_intern("x"), 0));
|
|
106
|
+
double ty = NUM2DBL(rb_funcall(translation, rb_intern("y"), 0));
|
|
107
|
+
double tz = NUM2DBL(rb_funcall(translation, rb_intern("z"), 0));
|
|
66
108
|
|
|
67
109
|
return quat2_build8(
|
|
68
110
|
klass, rx, ry, rz, rw, (tx * rw + ty * rz - tz * ry) * 0.5,
|
|
@@ -77,10 +119,10 @@ static VALUE quat2_class_from_translation(VALUE klass, VALUE translation) {
|
|
|
77
119
|
}
|
|
78
120
|
|
|
79
121
|
static VALUE quat2_class_from_rotation(VALUE klass, VALUE rotation) {
|
|
80
|
-
double rx =
|
|
81
|
-
double ry =
|
|
82
|
-
double rz =
|
|
83
|
-
double rw =
|
|
122
|
+
double rx = NUM2DBL(rb_funcall(rotation, rb_intern("x"), 0));
|
|
123
|
+
double ry = NUM2DBL(rb_funcall(rotation, rb_intern("y"), 0));
|
|
124
|
+
double rz = NUM2DBL(rb_funcall(rotation, rb_intern("z"), 0));
|
|
125
|
+
double rw = NUM2DBL(rb_funcall(rotation, rb_intern("w"), 0));
|
|
84
126
|
return quat2_build8(klass, rx, ry, rz, rw, 0.0, 0.0, 0.0, 0.0);
|
|
85
127
|
}
|
|
86
128
|
|
|
@@ -104,31 +146,26 @@ VALUE quat2_alloc(VALUE klass) {
|
|
|
104
146
|
}
|
|
105
147
|
|
|
106
148
|
VALUE quat2_initialize(int argc, VALUE *argv, VALUE self) {
|
|
149
|
+
rb_check_frozen(self);
|
|
107
150
|
VALUE data_arg = Qnil;
|
|
108
151
|
Quat2Data *data = quat2_get(self);
|
|
152
|
+
Quat2Data values = {{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0}};
|
|
109
153
|
|
|
110
154
|
rb_scan_args(argc, argv, "01", &data_arg);
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
VALUE ary = rb_check_array_type(data_arg);
|
|
124
|
-
if (NIL_P(ary)) {
|
|
125
|
-
rb_raise(rb_eTypeError, "expected Array");
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
for (int i = 0; i < 8; i++) {
|
|
129
|
-
data->data[i] = value_to_double(rb_ary_entry(ary, i));
|
|
155
|
+
if (argc > 0) {
|
|
156
|
+
VALUE ary = rb_check_array_type(data_arg);
|
|
157
|
+
if (NIL_P(ary)) {
|
|
158
|
+
rb_raise(rb_eTypeError, "expected Array");
|
|
159
|
+
}
|
|
160
|
+
if (RARRAY_LEN(ary) != 8) {
|
|
161
|
+
rb_raise(rb_eArgError, "expected 8 components");
|
|
162
|
+
}
|
|
163
|
+
for (int i = 0; i < 8; i++) {
|
|
164
|
+
values.data[i] = NUM2DBL(rb_ary_entry(ary, i));
|
|
165
|
+
}
|
|
130
166
|
}
|
|
131
|
-
|
|
167
|
+
rb_check_frozen(self);
|
|
168
|
+
*data = values;
|
|
132
169
|
return self;
|
|
133
170
|
}
|
|
134
171
|
|
|
@@ -156,12 +193,15 @@ VALUE quat2_aref(VALUE self, VALUE index) {
|
|
|
156
193
|
}
|
|
157
194
|
|
|
158
195
|
VALUE quat2_aset(VALUE self, VALUE index, VALUE value) {
|
|
196
|
+
rb_check_frozen(self);
|
|
159
197
|
Quat2Data *data = quat2_get(self);
|
|
160
198
|
long idx = NUM2LONG(index);
|
|
161
199
|
if (idx < 0 || idx > 7) {
|
|
162
200
|
rb_raise(rb_eIndexError, "index %ld out of range", idx);
|
|
163
201
|
}
|
|
164
|
-
|
|
202
|
+
double number = NUM2DBL(value);
|
|
203
|
+
rb_check_frozen(self);
|
|
204
|
+
data->data[idx] = number;
|
|
165
205
|
return value;
|
|
166
206
|
}
|
|
167
207
|
|
|
@@ -208,7 +248,7 @@ VALUE quat2_mul(VALUE self, VALUE other) {
|
|
|
208
248
|
}
|
|
209
249
|
|
|
210
250
|
if (rb_obj_is_kind_of(other, rb_cNumeric)) {
|
|
211
|
-
double s =
|
|
251
|
+
double s = NUM2DBL(other);
|
|
212
252
|
double values[8];
|
|
213
253
|
for (int i = 0; i < 8; i++) {
|
|
214
254
|
values[i] = a->data[i] * s;
|
|
@@ -216,7 +256,7 @@ VALUE quat2_mul(VALUE self, VALUE other) {
|
|
|
216
256
|
return quat2_build(rb_obj_class(self), values);
|
|
217
257
|
}
|
|
218
258
|
|
|
219
|
-
|
|
259
|
+
rb_raise(rb_eTypeError, "expected Quat2, Vec3, or Numeric");
|
|
220
260
|
}
|
|
221
261
|
|
|
222
262
|
VALUE quat2_add(VALUE self, VALUE other) {
|
|
@@ -254,40 +294,22 @@ VALUE quat2_length_squared(VALUE self) {
|
|
|
254
294
|
}
|
|
255
295
|
|
|
256
296
|
VALUE quat2_length(VALUE self) {
|
|
257
|
-
|
|
258
|
-
return DBL2NUM(
|
|
297
|
+
Quat2Data *a = quat2_get(self);
|
|
298
|
+
return DBL2NUM(hypot(hypot(a->data[0], a->data[1]),
|
|
299
|
+
hypot(a->data[2], a->data[3])));
|
|
259
300
|
}
|
|
260
301
|
|
|
261
302
|
VALUE quat2_normalize(VALUE self) {
|
|
262
|
-
Quat2Data
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (len < 1e-10) {
|
|
266
|
-
double values[8];
|
|
267
|
-
for (int i = 0; i < 8; i++) {
|
|
268
|
-
values[i] = a->data[i];
|
|
269
|
-
}
|
|
270
|
-
return quat2_build(rb_obj_class(self), values);
|
|
271
|
-
}
|
|
272
|
-
double inv_len = 1.0 / len;
|
|
273
|
-
double values[8];
|
|
274
|
-
for (int i = 0; i < 8; i++) {
|
|
275
|
-
values[i] = a->data[i] * inv_len;
|
|
276
|
-
}
|
|
277
|
-
return quat2_build(rb_obj_class(self), values);
|
|
303
|
+
Quat2Data values = *quat2_get(self);
|
|
304
|
+
normalize_quat2(values.data);
|
|
305
|
+
return quat2_build(rb_obj_class(self), values.data);
|
|
278
306
|
}
|
|
279
307
|
|
|
280
308
|
VALUE quat2_normalize_bang(VALUE self) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return self;
|
|
286
|
-
}
|
|
287
|
-
double inv_len = 1.0 / len;
|
|
288
|
-
for (int i = 0; i < 8; i++) {
|
|
289
|
-
a->data[i] *= inv_len;
|
|
290
|
-
}
|
|
309
|
+
rb_check_frozen(self);
|
|
310
|
+
Quat2Data values = *quat2_get(self);
|
|
311
|
+
normalize_quat2(values.data);
|
|
312
|
+
*quat2_get(self) = values;
|
|
291
313
|
return self;
|
|
292
314
|
}
|
|
293
315
|
|
|
@@ -300,17 +322,21 @@ VALUE quat2_conjugate(VALUE self) {
|
|
|
300
322
|
|
|
301
323
|
VALUE quat2_inverse(VALUE self) {
|
|
302
324
|
Quat2Data *a = quat2_get(self);
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
325
|
+
for (int i = 0; i < 8; i++) {
|
|
326
|
+
if (!isfinite(a->data[i])) {
|
|
327
|
+
rb_raise(rb_eArgError, "dual quaternion must be finite");
|
|
328
|
+
}
|
|
307
329
|
}
|
|
308
|
-
VALUE
|
|
309
|
-
|
|
310
|
-
|
|
330
|
+
VALUE real_inverse = rb_funcall(quat2_real(self), rb_intern("inverse"), 0);
|
|
331
|
+
VALUE dual_inverse = rb_funcall(real_inverse, rb_intern("*"), 1, quat2_dual(self));
|
|
332
|
+
dual_inverse = rb_funcall(dual_inverse, rb_intern("*"), 1, real_inverse);
|
|
333
|
+
VALUE real = rb_funcall(real_inverse, rb_intern("to_a"), 0);
|
|
334
|
+
VALUE dual = rb_funcall(dual_inverse, rb_intern("to_a"), 0);
|
|
335
|
+
/* (r + epsilon d)^-1 = r^-1 - epsilon r^-1 d r^-1. */
|
|
311
336
|
double values[8];
|
|
312
|
-
for (int i = 0; i <
|
|
313
|
-
values[i] =
|
|
337
|
+
for (int i = 0; i < 4; i++) {
|
|
338
|
+
values[i] = NUM2DBL(rb_ary_entry(real, i));
|
|
339
|
+
values[i + 4] = -NUM2DBL(rb_ary_entry(dual, i));
|
|
314
340
|
}
|
|
315
341
|
return quat2_build(rb_obj_class(self), values);
|
|
316
342
|
}
|
|
@@ -347,15 +373,22 @@ VALUE quat2_transform_point(VALUE self, VALUE point) {
|
|
|
347
373
|
}
|
|
348
374
|
|
|
349
375
|
VALUE quat2_lerp(VALUE self, VALUE other, VALUE t) {
|
|
350
|
-
Quat2Data
|
|
351
|
-
Quat2Data
|
|
352
|
-
double s =
|
|
376
|
+
Quat2Data a = *quat2_get(self);
|
|
377
|
+
Quat2Data b = *quat2_get(other);
|
|
378
|
+
double s = NUM2DBL(t);
|
|
379
|
+
normalize_quat2(a.data);
|
|
380
|
+
normalize_quat2(b.data);
|
|
381
|
+
double dot = 0.0;
|
|
382
|
+
for (int i = 0; i < 4; i++) {
|
|
383
|
+
dot += a.data[i] * b.data[i];
|
|
384
|
+
}
|
|
385
|
+
double sign = dot < 0.0 ? -1.0 : 1.0;
|
|
353
386
|
double values[8];
|
|
354
387
|
for (int i = 0; i < 8; i++) {
|
|
355
|
-
values[i] = a
|
|
388
|
+
values[i] = a.data[i] + (sign * b.data[i] - a.data[i]) * s;
|
|
356
389
|
}
|
|
357
|
-
|
|
358
|
-
return
|
|
390
|
+
normalize_quat2(values);
|
|
391
|
+
return quat2_build(rb_obj_class(self), values);
|
|
359
392
|
}
|
|
360
393
|
|
|
361
394
|
VALUE quat2_to_mat4(VALUE self) {
|
|
@@ -367,7 +400,7 @@ VALUE quat2_to_mat4(VALUE self) {
|
|
|
367
400
|
rb_funcall(trans, rb_intern("x"), 0),
|
|
368
401
|
rb_funcall(trans, rb_intern("y"), 0),
|
|
369
402
|
rb_funcall(trans, rb_intern("z"), 0));
|
|
370
|
-
return rb_funcall(
|
|
403
|
+
return rb_funcall(trans_m, rb_intern("*"), 1, rot_m);
|
|
371
404
|
}
|
|
372
405
|
|
|
373
406
|
VALUE quat2_to_a(VALUE self) {
|
|
@@ -404,10 +437,13 @@ VALUE quat2_near(int argc, VALUE *argv, VALUE self) {
|
|
|
404
437
|
rb_scan_args(argc, argv, "11", &other, &epsilon);
|
|
405
438
|
Quat2Data *a = quat2_get(self);
|
|
406
439
|
Quat2Data *b = quat2_get(other);
|
|
407
|
-
double eps =
|
|
440
|
+
double eps = argc < 2 ? 1e-6 : NUM2DBL(epsilon);
|
|
441
|
+
if (!isfinite(eps) || eps <= 0.0) {
|
|
442
|
+
rb_raise(rb_eArgError, "epsilon must be finite and positive");
|
|
443
|
+
}
|
|
408
444
|
|
|
409
445
|
for (int i = 0; i < 8; i++) {
|
|
410
|
-
if (fabs(a->data[i] - b->data[i])
|
|
446
|
+
if (!(fabs(a->data[i] - b->data[i]) < eps)) {
|
|
411
447
|
return Qfalse;
|
|
412
448
|
}
|
|
413
449
|
}
|
|
@@ -435,6 +471,8 @@ void Init_quat2(VALUE module) {
|
|
|
435
471
|
|
|
436
472
|
rb_define_alloc_func(cQuat2, quat2_alloc);
|
|
437
473
|
rb_define_method(cQuat2, "initialize", quat2_initialize, -1);
|
|
474
|
+
rb_define_private_method(cQuat2, "initialize_copy",
|
|
475
|
+
quat2_initialize_copy, 1);
|
|
438
476
|
|
|
439
477
|
rb_define_singleton_method(cQuat2, "identity", quat2_class_identity, 0);
|
|
440
478
|
rb_define_singleton_method(cQuat2, "from_rotation_translation",
|