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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/README.md +63 -1
- data/ext/larb/color.c +449 -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 +31 -0
- data/ext/larb/mat2.c +303 -0
- data/ext/larb/mat2.h +30 -0
- data/ext/larb/mat2d.c +397 -0
- data/ext/larb/mat2d.h +35 -0
- data/ext/larb/mat3.c +455 -0
- data/ext/larb/mat3.h +33 -0
- data/ext/larb/mat4.c +651 -0
- data/ext/larb/mat4.h +31 -0
- data/ext/larb/matrix_utils.h +97 -0
- data/ext/larb/quat.c +567 -0
- data/ext/larb/quat.h +39 -0
- data/ext/larb/quat2.c +511 -0
- data/ext/larb/quat2.h +39 -0
- data/ext/larb/vec2.c +365 -0
- data/ext/larb/vec2.h +43 -0
- data/ext/larb/vec3.c +538 -0
- data/ext/larb/vec3.h +52 -0
- data/ext/larb/vec4.c +361 -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 +299 -0
- data/test/larb/mat2_test.rb +144 -0
- data/test/larb/mat2d_test.rb +197 -0
- data/test/larb/mat3_test.rb +147 -0
- data/test/larb/mat4_test.rb +347 -0
- data/test/larb/matrix_test.rb +135 -0
- data/test/larb/native_value_test.rb +157 -0
- data/test/larb/quat2_test.rb +254 -0
- data/test/larb/quat_test.rb +284 -0
- data/test/larb/vec2_test.rb +274 -0
- data/test/larb/vec3_test.rb +373 -0
- data/test/larb/vec4_test.rb +196 -0
- data/test/test_helper.rb +4 -0
- metadata +60 -18
- 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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#ifndef LARB_MATRIX_UTILS_H
|
|
2
|
+
#define LARB_MATRIX_UTILS_H
|
|
3
|
+
|
|
4
|
+
#include "larb.h"
|
|
5
|
+
#include <float.h>
|
|
6
|
+
#include <limits.h>
|
|
7
|
+
#include <math.h>
|
|
8
|
+
|
|
9
|
+
/* Column-major matrices of size 2 through 4. Combine row and column exponents
|
|
10
|
+
* before scaling, so neither intermediate division loses small components. */
|
|
11
|
+
static inline void larb_matrix_inverse(const double *values, double *inverse,
|
|
12
|
+
int size) {
|
|
13
|
+
double rows[4][8] = {{0.0}};
|
|
14
|
+
int row_exponents[4], column_exponents[4];
|
|
15
|
+
double row_scales[4] = {0.0};
|
|
16
|
+
for (int col = 0; col < size; col++) {
|
|
17
|
+
for (int row = 0; row < size; row++) {
|
|
18
|
+
double value = values[col * size + row];
|
|
19
|
+
if (!isfinite(value)) {
|
|
20
|
+
rb_raise(rb_eRuntimeError, "Matrix is not invertible");
|
|
21
|
+
}
|
|
22
|
+
row_scales[row] = fmax(row_scales[row], fabs(value));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (int row = 0; row < size; row++) {
|
|
26
|
+
if (row_scales[row] == 0.0) {
|
|
27
|
+
rb_raise(rb_eRuntimeError, "Matrix is not invertible");
|
|
28
|
+
}
|
|
29
|
+
row_exponents[row] = ilogb(row_scales[row]);
|
|
30
|
+
row_scales[row] = 0.0;
|
|
31
|
+
}
|
|
32
|
+
for (int col = 0; col < size; col++) {
|
|
33
|
+
int exponent = INT_MIN;
|
|
34
|
+
for (int row = 0; row < size; row++) {
|
|
35
|
+
double value = values[col * size + row];
|
|
36
|
+
if (value != 0.0) {
|
|
37
|
+
int adjusted = ilogb(fabs(value)) - row_exponents[row];
|
|
38
|
+
if (adjusted > exponent) exponent = adjusted;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (exponent == INT_MIN) {
|
|
42
|
+
rb_raise(rb_eRuntimeError, "Matrix is not invertible");
|
|
43
|
+
}
|
|
44
|
+
column_exponents[col] = exponent;
|
|
45
|
+
for (int row = 0; row < size; row++) {
|
|
46
|
+
rows[row][col] = scalbn(values[col * size + row],
|
|
47
|
+
-(row_exponents[row] + exponent));
|
|
48
|
+
row_scales[row] = fmax(row_scales[row], fabs(rows[row][col]));
|
|
49
|
+
}
|
|
50
|
+
rows[col][size + col] = 1.0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (int col = 0; col < size; col++) {
|
|
54
|
+
int pivot = col;
|
|
55
|
+
for (int row = col + 1; row < size; row++) {
|
|
56
|
+
if (fabs(rows[row][col]) / row_scales[row] >
|
|
57
|
+
fabs(rows[pivot][col]) / row_scales[pivot]) {
|
|
58
|
+
pivot = row;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (fabs(rows[pivot][col]) / row_scales[pivot] <= size * DBL_EPSILON) {
|
|
62
|
+
rb_raise(rb_eRuntimeError, "Matrix is not invertible");
|
|
63
|
+
}
|
|
64
|
+
double row_scale = row_scales[col];
|
|
65
|
+
row_scales[col] = row_scales[pivot];
|
|
66
|
+
row_scales[pivot] = row_scale;
|
|
67
|
+
for (int i = 0; i < size * 2; i++) {
|
|
68
|
+
double tmp = rows[col][i];
|
|
69
|
+
rows[col][i] = rows[pivot][i];
|
|
70
|
+
rows[pivot][i] = tmp;
|
|
71
|
+
}
|
|
72
|
+
double divisor = rows[col][col];
|
|
73
|
+
for (int i = 0; i < size * 2; i++) {
|
|
74
|
+
rows[col][i] /= divisor;
|
|
75
|
+
}
|
|
76
|
+
for (int row = 0; row < size; row++) {
|
|
77
|
+
if (row == col) continue;
|
|
78
|
+
double factor = rows[row][col];
|
|
79
|
+
for (int i = 0; i < size * 2; i++) {
|
|
80
|
+
rows[row][i] -= factor * rows[col][i];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (int col = 0; col < size; col++) {
|
|
86
|
+
for (int row = 0; row < size; row++) {
|
|
87
|
+
double value = scalbn(rows[row][size + col],
|
|
88
|
+
-(column_exponents[row] + row_exponents[col]));
|
|
89
|
+
if (!isfinite(value)) {
|
|
90
|
+
rb_raise(rb_eRuntimeError, "Matrix is not invertible");
|
|
91
|
+
}
|
|
92
|
+
inverse[col * size + row] = value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#endif
|
data/ext/larb/quat.c
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
#include "quat.h"
|
|
2
|
+
|
|
3
|
+
#include <math.h>
|
|
4
|
+
|
|
5
|
+
static void quat_free(void *ptr) {
|
|
6
|
+
xfree(ptr);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static size_t quat_memsize(const void *ptr) {
|
|
10
|
+
return sizeof(QuatData);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static const rb_data_type_t quat_type = {
|
|
14
|
+
"Quat",
|
|
15
|
+
{0, quat_free, quat_memsize},
|
|
16
|
+
0,
|
|
17
|
+
0,
|
|
18
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
static VALUE cQuat = Qnil;
|
|
22
|
+
static VALUE cVec3 = Qnil;
|
|
23
|
+
|
|
24
|
+
static QuatData *quat_get(VALUE obj) {
|
|
25
|
+
QuatData *data = NULL;
|
|
26
|
+
TypedData_Get_Struct(obj, QuatData, &quat_type, data);
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static VALUE quat_build(VALUE klass, double x, double y, double z, double w) {
|
|
31
|
+
VALUE obj = quat_alloc(klass);
|
|
32
|
+
QuatData *data = quat_get(obj);
|
|
33
|
+
data->x = x;
|
|
34
|
+
data->y = y;
|
|
35
|
+
data->z = z;
|
|
36
|
+
data->w = w;
|
|
37
|
+
return obj;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static double clamp_double(double value, double min, double max) {
|
|
41
|
+
if (value < min) {
|
|
42
|
+
return min;
|
|
43
|
+
}
|
|
44
|
+
if (value > max) {
|
|
45
|
+
return max;
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static void normalize_quat(double *x, double *y, double *z, double *w) {
|
|
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;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
VALUE quat_alloc(VALUE klass) {
|
|
67
|
+
QuatData *data = ALLOC(QuatData);
|
|
68
|
+
data->x = 0.0;
|
|
69
|
+
data->y = 0.0;
|
|
70
|
+
data->z = 0.0;
|
|
71
|
+
data->w = 1.0;
|
|
72
|
+
return TypedData_Wrap_Struct(klass, &quat_type, data);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
VALUE quat_initialize(int argc, VALUE *argv, VALUE self) {
|
|
76
|
+
rb_check_frozen(self);
|
|
77
|
+
VALUE vx = Qnil;
|
|
78
|
+
VALUE vy = Qnil;
|
|
79
|
+
VALUE vz = Qnil;
|
|
80
|
+
VALUE vw = Qnil;
|
|
81
|
+
QuatData *data = quat_get(self);
|
|
82
|
+
|
|
83
|
+
rb_scan_args(argc, argv, "04", &vx, &vy, &vz, &vw);
|
|
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;
|
|
90
|
+
|
|
91
|
+
return self;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static VALUE quat_class_bracket(VALUE klass, VALUE x, VALUE y, VALUE z,
|
|
95
|
+
VALUE w) {
|
|
96
|
+
return quat_build(klass, NUM2DBL(x), NUM2DBL(y),
|
|
97
|
+
NUM2DBL(z), NUM2DBL(w));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static VALUE quat_class_identity(VALUE klass) {
|
|
101
|
+
return quat_build(klass, 0.0, 0.0, 0.0, 1.0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static VALUE quat_class_from_axis_angle(VALUE klass, VALUE axis,
|
|
105
|
+
VALUE radians) {
|
|
106
|
+
double half = NUM2DBL(radians) * 0.5;
|
|
107
|
+
double s = sin(half);
|
|
108
|
+
VALUE normalized = rb_funcall(axis, rb_intern("normalize"), 0);
|
|
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));
|
|
112
|
+
return quat_build(klass, x * s, y * s, z * s, cos(half));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static VALUE quat_class_from_euler(VALUE klass, VALUE x, VALUE y, VALUE z) {
|
|
116
|
+
double rx = NUM2DBL(x) * 0.5;
|
|
117
|
+
double ry = NUM2DBL(y) * 0.5;
|
|
118
|
+
double rz = NUM2DBL(z) * 0.5;
|
|
119
|
+
double cx = cos(rx);
|
|
120
|
+
double sx = sin(rx);
|
|
121
|
+
double cy = cos(ry);
|
|
122
|
+
double sy = sin(ry);
|
|
123
|
+
double cz = cos(rz);
|
|
124
|
+
double sz = sin(rz);
|
|
125
|
+
|
|
126
|
+
return quat_build(klass, sx * cy * cz - cx * sy * sz,
|
|
127
|
+
cx * sy * cz + sx * cy * sz,
|
|
128
|
+
cx * cy * sz - sx * sy * cz,
|
|
129
|
+
cx * cy * cz + sx * sy * sz);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static VALUE quat_class_look_rotation(int argc, VALUE *argv, VALUE klass) {
|
|
133
|
+
VALUE forward = Qnil;
|
|
134
|
+
VALUE up = Qnil;
|
|
135
|
+
rb_scan_args(argc, argv, "11", &forward, &up);
|
|
136
|
+
|
|
137
|
+
VALUE forward_norm = rb_funcall(forward, rb_intern("normalize"), 0);
|
|
138
|
+
VALUE up_vec = NIL_P(up) ? rb_funcall(cVec3, rb_intern("up"), 0) : up;
|
|
139
|
+
VALUE right =
|
|
140
|
+
rb_funcall(rb_funcall(up_vec, rb_intern("cross"), 1, forward_norm),
|
|
141
|
+
rb_intern("normalize"), 0);
|
|
142
|
+
VALUE up_final =
|
|
143
|
+
rb_funcall(forward_norm, rb_intern("cross"), 1, right);
|
|
144
|
+
|
|
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));
|
|
154
|
+
|
|
155
|
+
double trace = m00 + m11 + m22;
|
|
156
|
+
if (trace > 0.0) {
|
|
157
|
+
double s = 0.5 / sqrt(trace + 1.0);
|
|
158
|
+
return quat_build(klass, (m21 - m12) * s, (m02 - m20) * s,
|
|
159
|
+
(m10 - m01) * s, 0.25 / s);
|
|
160
|
+
}
|
|
161
|
+
if (m00 > m11 && m00 > m22) {
|
|
162
|
+
double s = 2.0 * sqrt(1.0 + m00 - m11 - m22);
|
|
163
|
+
return quat_build(klass, 0.25 * s, (m01 + m10) / s, (m02 + m20) / s,
|
|
164
|
+
(m21 - m12) / s);
|
|
165
|
+
}
|
|
166
|
+
if (m11 > m22) {
|
|
167
|
+
double s = 2.0 * sqrt(1.0 + m11 - m00 - m22);
|
|
168
|
+
return quat_build(klass, (m01 + m10) / s, 0.25 * s, (m12 + m21) / s,
|
|
169
|
+
(m02 - m20) / s);
|
|
170
|
+
}
|
|
171
|
+
double s = 2.0 * sqrt(1.0 + m22 - m00 - m11);
|
|
172
|
+
return quat_build(klass, (m02 + m20) / s, (m12 + m21) / s, 0.25 * s,
|
|
173
|
+
(m10 - m01) / s);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static VALUE quat_get_x(VALUE self) {
|
|
177
|
+
QuatData *data = quat_get(self);
|
|
178
|
+
return DBL2NUM(data->x);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static VALUE quat_set_x(VALUE self, VALUE value) {
|
|
182
|
+
rb_check_frozen(self);
|
|
183
|
+
QuatData *data = quat_get(self);
|
|
184
|
+
double number = NUM2DBL(value);
|
|
185
|
+
rb_check_frozen(self);
|
|
186
|
+
data->x = number;
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
static VALUE quat_get_y(VALUE self) {
|
|
191
|
+
QuatData *data = quat_get(self);
|
|
192
|
+
return DBL2NUM(data->y);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
static VALUE quat_set_y(VALUE self, VALUE value) {
|
|
196
|
+
rb_check_frozen(self);
|
|
197
|
+
QuatData *data = quat_get(self);
|
|
198
|
+
double number = NUM2DBL(value);
|
|
199
|
+
rb_check_frozen(self);
|
|
200
|
+
data->y = number;
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static VALUE quat_get_z(VALUE self) {
|
|
205
|
+
QuatData *data = quat_get(self);
|
|
206
|
+
return DBL2NUM(data->z);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
static VALUE quat_set_z(VALUE self, VALUE value) {
|
|
210
|
+
rb_check_frozen(self);
|
|
211
|
+
QuatData *data = quat_get(self);
|
|
212
|
+
double number = NUM2DBL(value);
|
|
213
|
+
rb_check_frozen(self);
|
|
214
|
+
data->z = number;
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static VALUE quat_get_w(VALUE self) {
|
|
219
|
+
QuatData *data = quat_get(self);
|
|
220
|
+
return DBL2NUM(data->w);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
static VALUE quat_set_w(VALUE self, VALUE value) {
|
|
224
|
+
rb_check_frozen(self);
|
|
225
|
+
QuatData *data = quat_get(self);
|
|
226
|
+
double number = NUM2DBL(value);
|
|
227
|
+
rb_check_frozen(self);
|
|
228
|
+
data->w = number;
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
VALUE quat_mul(VALUE self, VALUE other) {
|
|
233
|
+
QuatData *a = quat_get(self);
|
|
234
|
+
|
|
235
|
+
if (rb_obj_is_kind_of(other, cQuat)) {
|
|
236
|
+
QuatData *b = quat_get(other);
|
|
237
|
+
return quat_build(
|
|
238
|
+
rb_obj_class(self), a->w * b->x + a->x * b->w + a->y * b->z - a->z * b->y,
|
|
239
|
+
a->w * b->y - a->x * b->z + a->y * b->w + a->z * b->x,
|
|
240
|
+
a->w * b->z + a->x * b->y - a->y * b->x + a->z * b->w,
|
|
241
|
+
a->w * b->w - a->x * b->x - a->y * b->y - a->z * b->z);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (rb_obj_is_kind_of(other, cVec3)) {
|
|
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));
|
|
248
|
+
|
|
249
|
+
double uvx = a->y * vz - a->z * vy;
|
|
250
|
+
double uvy = a->z * vx - a->x * vz;
|
|
251
|
+
double uvz = a->x * vy - a->y * vx;
|
|
252
|
+
|
|
253
|
+
double uuvx = a->y * uvz - a->z * uvy;
|
|
254
|
+
double uuvy = a->z * uvx - a->x * uvz;
|
|
255
|
+
double uuvz = a->x * uvy - a->y * uvx;
|
|
256
|
+
|
|
257
|
+
double rx = vx + (uvx * a->w + uuvx) * 2.0;
|
|
258
|
+
double ry = vy + (uvy * a->w + uuvy) * 2.0;
|
|
259
|
+
double rz = vz + (uvz * a->w + uuvz) * 2.0;
|
|
260
|
+
|
|
261
|
+
return rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(rx), DBL2NUM(ry),
|
|
262
|
+
DBL2NUM(rz));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (rb_obj_is_kind_of(other, rb_cNumeric)) {
|
|
266
|
+
double s = NUM2DBL(other);
|
|
267
|
+
return quat_build(rb_obj_class(self), a->x * s, a->y * s, a->z * s,
|
|
268
|
+
a->w * s);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
rb_raise(rb_eTypeError, "expected Quat, Vec3, or Numeric");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
VALUE quat_add(VALUE self, VALUE other) {
|
|
275
|
+
QuatData *a = quat_get(self);
|
|
276
|
+
QuatData *b = quat_get(other);
|
|
277
|
+
return quat_build(rb_obj_class(self), a->x + b->x, a->y + b->y,
|
|
278
|
+
a->z + b->z, a->w + b->w);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
VALUE quat_sub(VALUE self, VALUE other) {
|
|
282
|
+
QuatData *a = quat_get(self);
|
|
283
|
+
QuatData *b = quat_get(other);
|
|
284
|
+
return quat_build(rb_obj_class(self), a->x - b->x, a->y - b->y,
|
|
285
|
+
a->z - b->z, a->w - b->w);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
VALUE quat_negate(VALUE self) {
|
|
289
|
+
QuatData *a = quat_get(self);
|
|
290
|
+
return quat_build(rb_obj_class(self), -a->x, -a->y, -a->z, -a->w);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
VALUE quat_dot(VALUE self, VALUE other) {
|
|
294
|
+
QuatData *a = quat_get(self);
|
|
295
|
+
QuatData *b = quat_get(other);
|
|
296
|
+
return DBL2NUM(a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
VALUE quat_length(VALUE self) {
|
|
300
|
+
QuatData *a = quat_get(self);
|
|
301
|
+
return DBL2NUM(hypot(hypot(a->x, a->y), hypot(a->z, a->w)));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
VALUE quat_length_squared(VALUE self) {
|
|
305
|
+
QuatData *a = quat_get(self);
|
|
306
|
+
return DBL2NUM(a->x * a->x + a->y * a->y + a->z * a->z + a->w * a->w);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
VALUE quat_normalize(VALUE self) {
|
|
310
|
+
QuatData *a = quat_get(self);
|
|
311
|
+
double x = a->x;
|
|
312
|
+
double y = a->y;
|
|
313
|
+
double z = a->z;
|
|
314
|
+
double w = a->w;
|
|
315
|
+
normalize_quat(&x, &y, &z, &w);
|
|
316
|
+
return quat_build(rb_obj_class(self), x, y, z, w);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
VALUE quat_normalize_bang(VALUE self) {
|
|
320
|
+
rb_check_frozen(self);
|
|
321
|
+
QuatData *a = quat_get(self);
|
|
322
|
+
normalize_quat(&a->x, &a->y, &a->z, &a->w);
|
|
323
|
+
return self;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
VALUE quat_conjugate(VALUE self) {
|
|
327
|
+
QuatData *a = quat_get(self);
|
|
328
|
+
return quat_build(rb_obj_class(self), -a->x, -a->y, -a->z, a->w);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
VALUE quat_inverse(VALUE self) {
|
|
332
|
+
QuatData *a = quat_get(self);
|
|
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);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
VALUE quat_lerp(VALUE self, VALUE other, VALUE t) {
|
|
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;
|
|
356
|
+
normalize_quat(&x, &y, &z, &w);
|
|
357
|
+
return quat_build(rb_obj_class(self), x, y, z, w);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
VALUE quat_slerp(VALUE self, VALUE other, VALUE t) {
|
|
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
|
+
}
|
|
371
|
+
double dot = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w;
|
|
372
|
+
|
|
373
|
+
double ox = b->x;
|
|
374
|
+
double oy = b->y;
|
|
375
|
+
double oz = b->z;
|
|
376
|
+
double ow = b->w;
|
|
377
|
+
if (dot < 0.0) {
|
|
378
|
+
dot = -dot;
|
|
379
|
+
ox = -ox;
|
|
380
|
+
oy = -oy;
|
|
381
|
+
oz = -oz;
|
|
382
|
+
ow = -ow;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (dot > 0.9995) {
|
|
386
|
+
double x = a->x + (ox - a->x) * s;
|
|
387
|
+
double y = a->y + (oy - a->y) * s;
|
|
388
|
+
double z = a->z + (oz - a->z) * s;
|
|
389
|
+
double w = a->w + (ow - a->w) * s;
|
|
390
|
+
normalize_quat(&x, &y, &z, &w);
|
|
391
|
+
return quat_build(rb_obj_class(self), x, y, z, w);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
double theta0 = acos(clamp_double(dot, -1.0, 1.0));
|
|
395
|
+
double theta = theta0 * s;
|
|
396
|
+
double sin_theta = sin(theta);
|
|
397
|
+
double sin_theta0 = sin(theta0);
|
|
398
|
+
double s0 = cos(theta) - dot * sin_theta / sin_theta0;
|
|
399
|
+
double s1 = sin_theta / sin_theta0;
|
|
400
|
+
|
|
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);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
VALUE quat_to_axis_angle(VALUE self) {
|
|
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);
|
|
414
|
+
VALUE axis;
|
|
415
|
+
if (s == 0.0) {
|
|
416
|
+
axis = rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(1.0), DBL2NUM(0.0),
|
|
417
|
+
DBL2NUM(0.0));
|
|
418
|
+
} else {
|
|
419
|
+
axis = rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(a.x / s),
|
|
420
|
+
DBL2NUM(a.y / s), DBL2NUM(a.z / s));
|
|
421
|
+
}
|
|
422
|
+
VALUE ary = rb_ary_new_capa(2);
|
|
423
|
+
rb_ary_push(ary, axis);
|
|
424
|
+
rb_ary_push(ary, DBL2NUM(angle));
|
|
425
|
+
return ary;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
VALUE quat_to_euler(VALUE self) {
|
|
429
|
+
QuatData *a = quat_get(self);
|
|
430
|
+
double sinr_cosp = 2.0 * (a->w * a->x + a->y * a->z);
|
|
431
|
+
double cosr_cosp = 1.0 - 2.0 * (a->x * a->x + a->y * a->y);
|
|
432
|
+
double roll = atan2(sinr_cosp, cosr_cosp);
|
|
433
|
+
|
|
434
|
+
double sinp = 2.0 * (a->w * a->y - a->z * a->x);
|
|
435
|
+
double pitch;
|
|
436
|
+
double pi = acos(-1.0);
|
|
437
|
+
if (fabs(sinp) >= 1.0) {
|
|
438
|
+
pitch = copysign(pi / 2.0, sinp);
|
|
439
|
+
} else {
|
|
440
|
+
pitch = asin(sinp);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
double siny_cosp = 2.0 * (a->w * a->z + a->x * a->y);
|
|
444
|
+
double cosy_cosp = 1.0 - 2.0 * (a->y * a->y + a->z * a->z);
|
|
445
|
+
double yaw = atan2(siny_cosp, cosy_cosp);
|
|
446
|
+
|
|
447
|
+
return rb_funcall(cVec3, rb_intern("new"), 3, DBL2NUM(roll), DBL2NUM(pitch),
|
|
448
|
+
DBL2NUM(yaw));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
VALUE quat_to_mat4(VALUE self) {
|
|
452
|
+
VALUE mat4_class = rb_const_get(mLarb, rb_intern("Mat4"));
|
|
453
|
+
return rb_funcall(mat4_class, rb_intern("from_quaternion"), 1, self);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
VALUE quat_to_a(VALUE self) {
|
|
457
|
+
QuatData *a = quat_get(self);
|
|
458
|
+
VALUE ary = rb_ary_new_capa(4);
|
|
459
|
+
rb_ary_push(ary, DBL2NUM(a->x));
|
|
460
|
+
rb_ary_push(ary, DBL2NUM(a->y));
|
|
461
|
+
rb_ary_push(ary, DBL2NUM(a->z));
|
|
462
|
+
rb_ary_push(ary, DBL2NUM(a->w));
|
|
463
|
+
return ary;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
VALUE quat_aref(VALUE self, VALUE index) {
|
|
467
|
+
VALUE ary = quat_to_a(self);
|
|
468
|
+
return rb_ary_entry(ary, NUM2LONG(index));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
VALUE quat_equal(VALUE self, VALUE other) {
|
|
472
|
+
if (!rb_obj_is_kind_of(other, cQuat)) {
|
|
473
|
+
return Qfalse;
|
|
474
|
+
}
|
|
475
|
+
QuatData *a = quat_get(self);
|
|
476
|
+
QuatData *b = quat_get(other);
|
|
477
|
+
return (a->x == b->x && a->y == b->y && a->z == b->z && a->w == b->w)
|
|
478
|
+
? Qtrue
|
|
479
|
+
: Qfalse;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
VALUE quat_near(int argc, VALUE *argv, VALUE self) {
|
|
483
|
+
VALUE other = Qnil;
|
|
484
|
+
VALUE epsilon = Qnil;
|
|
485
|
+
|
|
486
|
+
rb_scan_args(argc, argv, "11", &other, &epsilon);
|
|
487
|
+
QuatData *a = quat_get(self);
|
|
488
|
+
QuatData *b = quat_get(other);
|
|
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
|
+
}
|
|
493
|
+
|
|
494
|
+
if (fabs(a->x - b->x) < eps && fabs(a->y - b->y) < eps &&
|
|
495
|
+
fabs(a->z - b->z) < eps && fabs(a->w - b->w) < eps) {
|
|
496
|
+
return Qtrue;
|
|
497
|
+
}
|
|
498
|
+
return Qfalse;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
VALUE quat_inspect(VALUE self) {
|
|
502
|
+
QuatData *a = quat_get(self);
|
|
503
|
+
VALUE sx = rb_funcall(DBL2NUM(a->x), rb_intern("to_s"), 0);
|
|
504
|
+
VALUE sy = rb_funcall(DBL2NUM(a->y), rb_intern("to_s"), 0);
|
|
505
|
+
VALUE sz = rb_funcall(DBL2NUM(a->z), rb_intern("to_s"), 0);
|
|
506
|
+
VALUE sw = rb_funcall(DBL2NUM(a->w), rb_intern("to_s"), 0);
|
|
507
|
+
VALUE str = rb_str_new_cstr("Quat[");
|
|
508
|
+
rb_str_concat(str, sx);
|
|
509
|
+
rb_str_cat_cstr(str, ", ");
|
|
510
|
+
rb_str_concat(str, sy);
|
|
511
|
+
rb_str_cat_cstr(str, ", ");
|
|
512
|
+
rb_str_concat(str, sz);
|
|
513
|
+
rb_str_cat_cstr(str, ", ");
|
|
514
|
+
rb_str_concat(str, sw);
|
|
515
|
+
rb_str_cat_cstr(str, "]");
|
|
516
|
+
return str;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
void Init_quat(VALUE module) {
|
|
520
|
+
cQuat = rb_define_class_under(module, "Quat", rb_cObject);
|
|
521
|
+
cVec3 = rb_const_get(mLarb, rb_intern("Vec3"));
|
|
522
|
+
|
|
523
|
+
rb_define_alloc_func(cQuat, quat_alloc);
|
|
524
|
+
rb_define_method(cQuat, "initialize", quat_initialize, -1);
|
|
525
|
+
rb_define_private_method(cQuat, "initialize_copy",
|
|
526
|
+
quat_initialize_copy, 1);
|
|
527
|
+
|
|
528
|
+
rb_define_singleton_method(cQuat, "[]", quat_class_bracket, 4);
|
|
529
|
+
rb_define_singleton_method(cQuat, "identity", quat_class_identity, 0);
|
|
530
|
+
rb_define_singleton_method(cQuat, "from_axis_angle",
|
|
531
|
+
quat_class_from_axis_angle, 2);
|
|
532
|
+
rb_define_singleton_method(cQuat, "from_euler", quat_class_from_euler, 3);
|
|
533
|
+
rb_define_singleton_method(cQuat, "look_rotation", quat_class_look_rotation,
|
|
534
|
+
-1);
|
|
535
|
+
|
|
536
|
+
rb_define_method(cQuat, "x", quat_get_x, 0);
|
|
537
|
+
rb_define_method(cQuat, "x=", quat_set_x, 1);
|
|
538
|
+
rb_define_method(cQuat, "y", quat_get_y, 0);
|
|
539
|
+
rb_define_method(cQuat, "y=", quat_set_y, 1);
|
|
540
|
+
rb_define_method(cQuat, "z", quat_get_z, 0);
|
|
541
|
+
rb_define_method(cQuat, "z=", quat_set_z, 1);
|
|
542
|
+
rb_define_method(cQuat, "w", quat_get_w, 0);
|
|
543
|
+
rb_define_method(cQuat, "w=", quat_set_w, 1);
|
|
544
|
+
|
|
545
|
+
rb_define_method(cQuat, "*", quat_mul, 1);
|
|
546
|
+
rb_define_method(cQuat, "+", quat_add, 1);
|
|
547
|
+
rb_define_method(cQuat, "-", quat_sub, 1);
|
|
548
|
+
rb_define_method(cQuat, "-@", quat_negate, 0);
|
|
549
|
+
rb_define_method(cQuat, "dot", quat_dot, 1);
|
|
550
|
+
rb_define_method(cQuat, "length", quat_length, 0);
|
|
551
|
+
rb_define_method(cQuat, "length_squared", quat_length_squared, 0);
|
|
552
|
+
rb_define_method(cQuat, "normalize", quat_normalize, 0);
|
|
553
|
+
rb_define_method(cQuat, "normalize!", quat_normalize_bang, 0);
|
|
554
|
+
rb_define_method(cQuat, "conjugate", quat_conjugate, 0);
|
|
555
|
+
rb_define_method(cQuat, "inverse", quat_inverse, 0);
|
|
556
|
+
rb_define_method(cQuat, "lerp", quat_lerp, 2);
|
|
557
|
+
rb_define_method(cQuat, "slerp", quat_slerp, 2);
|
|
558
|
+
rb_define_method(cQuat, "to_axis_angle", quat_to_axis_angle, 0);
|
|
559
|
+
rb_define_method(cQuat, "to_euler", quat_to_euler, 0);
|
|
560
|
+
rb_define_method(cQuat, "to_mat4", quat_to_mat4, 0);
|
|
561
|
+
rb_define_method(cQuat, "to_a", quat_to_a, 0);
|
|
562
|
+
rb_define_method(cQuat, "[]", quat_aref, 1);
|
|
563
|
+
rb_define_method(cQuat, "==", quat_equal, 1);
|
|
564
|
+
rb_define_method(cQuat, "near?", quat_near, -1);
|
|
565
|
+
rb_define_method(cQuat, "inspect", quat_inspect, 0);
|
|
566
|
+
rb_define_alias(cQuat, "to_s", "inspect");
|
|
567
|
+
}
|
data/ext/larb/quat.h
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#ifndef QUAT_H
|
|
2
|
+
#define QUAT_H
|
|
3
|
+
|
|
4
|
+
#include "larb.h"
|
|
5
|
+
|
|
6
|
+
typedef struct {
|
|
7
|
+
double x;
|
|
8
|
+
double y;
|
|
9
|
+
double z;
|
|
10
|
+
double w;
|
|
11
|
+
} QuatData;
|
|
12
|
+
|
|
13
|
+
void Init_quat(VALUE module);
|
|
14
|
+
VALUE quat_alloc(VALUE klass);
|
|
15
|
+
VALUE quat_initialize(int argc, VALUE *argv, VALUE self);
|
|
16
|
+
|
|
17
|
+
VALUE quat_mul(VALUE self, VALUE other);
|
|
18
|
+
VALUE quat_add(VALUE self, VALUE other);
|
|
19
|
+
VALUE quat_sub(VALUE self, VALUE other);
|
|
20
|
+
VALUE quat_negate(VALUE self);
|
|
21
|
+
VALUE quat_dot(VALUE self, VALUE other);
|
|
22
|
+
VALUE quat_length(VALUE self);
|
|
23
|
+
VALUE quat_length_squared(VALUE self);
|
|
24
|
+
VALUE quat_normalize(VALUE self);
|
|
25
|
+
VALUE quat_normalize_bang(VALUE self);
|
|
26
|
+
VALUE quat_conjugate(VALUE self);
|
|
27
|
+
VALUE quat_inverse(VALUE self);
|
|
28
|
+
VALUE quat_lerp(VALUE self, VALUE other, VALUE t);
|
|
29
|
+
VALUE quat_slerp(VALUE self, VALUE other, VALUE t);
|
|
30
|
+
VALUE quat_to_axis_angle(VALUE self);
|
|
31
|
+
VALUE quat_to_euler(VALUE self);
|
|
32
|
+
VALUE quat_to_mat4(VALUE self);
|
|
33
|
+
VALUE quat_to_a(VALUE self);
|
|
34
|
+
VALUE quat_aref(VALUE self, VALUE index);
|
|
35
|
+
VALUE quat_equal(VALUE self, VALUE other);
|
|
36
|
+
VALUE quat_near(int argc, VALUE *argv, VALUE self);
|
|
37
|
+
VALUE quat_inspect(VALUE self);
|
|
38
|
+
|
|
39
|
+
#endif
|