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/vec2.c CHANGED
@@ -20,11 +20,6 @@ static const rb_data_type_t vec2_type = {
20
20
 
21
21
  static VALUE cVec2 = Qnil;
22
22
 
23
- static double value_to_double(VALUE value) {
24
- VALUE coerced = rb_funcall(value, rb_intern("to_f"), 0);
25
- return NUM2DBL(coerced);
26
- }
27
-
28
23
  static Vec2Data *vec2_get(VALUE obj) {
29
24
  Vec2Data *data = NULL;
30
25
  TypedData_Get_Struct(obj, Vec2Data, &vec2_type, data);
@@ -47,19 +42,21 @@ VALUE vec2_alloc(VALUE klass) {
47
42
  }
48
43
 
49
44
  VALUE vec2_initialize(int argc, VALUE *argv, VALUE self) {
45
+ rb_check_frozen(self);
50
46
  VALUE vx = Qnil;
51
47
  VALUE vy = Qnil;
52
- Vec2Data *data = vec2_get(self);
53
-
54
48
  rb_scan_args(argc, argv, "02", &vx, &vy);
55
- data->x = NIL_P(vx) ? 0.0 : value_to_double(vx);
56
- data->y = NIL_P(vy) ? 0.0 : value_to_double(vy);
57
-
49
+ Vec2Data value = {
50
+ argc > 0 ? NUM2DBL(vx) : 0.0,
51
+ argc > 1 ? NUM2DBL(vy) : 0.0
52
+ };
53
+ rb_check_frozen(self);
54
+ *vec2_get(self) = value;
58
55
  return self;
59
56
  }
60
57
 
61
58
  static VALUE vec2_class_bracket(VALUE klass, VALUE x, VALUE y) {
62
- return vec2_build(klass, value_to_double(x), value_to_double(y));
59
+ return vec2_build(klass, NUM2DBL(x), NUM2DBL(y));
63
60
  }
64
61
 
65
62
  static VALUE vec2_class_zero(VALUE klass) {
@@ -76,8 +73,11 @@ static VALUE vec2_get_x(VALUE self) {
76
73
  }
77
74
 
78
75
  static VALUE vec2_set_x(VALUE self, VALUE value) {
76
+ rb_check_frozen(self);
79
77
  Vec2Data *data = vec2_get(self);
80
- data->x = NUM2DBL(value);
78
+ double component = NUM2DBL(value);
79
+ rb_check_frozen(self);
80
+ data->x = component;
81
81
  return value;
82
82
  }
83
83
 
@@ -87,8 +87,11 @@ static VALUE vec2_get_y(VALUE self) {
87
87
  }
88
88
 
89
89
  static VALUE vec2_set_y(VALUE self, VALUE value) {
90
+ rb_check_frozen(self);
90
91
  Vec2Data *data = vec2_get(self);
91
- data->y = NUM2DBL(value);
92
+ double component = NUM2DBL(value);
93
+ rb_check_frozen(self);
94
+ data->y = component;
92
95
  return value;
93
96
  }
94
97
 
@@ -129,7 +132,7 @@ VALUE vec2_dot(VALUE self, VALUE other) {
129
132
 
130
133
  VALUE vec2_length(VALUE self) {
131
134
  Vec2Data *a = vec2_get(self);
132
- return DBL2NUM(sqrt(a->x * a->x + a->y * a->y));
135
+ return DBL2NUM(hypot(a->x, a->y));
133
136
  }
134
137
 
135
138
  VALUE vec2_length_squared(VALUE self) {
@@ -139,15 +142,18 @@ VALUE vec2_length_squared(VALUE self) {
139
142
 
140
143
  VALUE vec2_normalize(VALUE self) {
141
144
  Vec2Data *a = vec2_get(self);
142
- double len = sqrt(a->x * a->x + a->y * a->y);
143
- return vec2_build(rb_obj_class(self), a->x / len, a->y / len);
145
+ double values[] = {a->x, a->y};
146
+ larb_normalize(values, 2);
147
+ return vec2_build(rb_obj_class(self), values[0], values[1]);
144
148
  }
145
149
 
146
150
  VALUE vec2_normalize_bang(VALUE self) {
151
+ rb_check_frozen(self);
147
152
  Vec2Data *a = vec2_get(self);
148
- double len = sqrt(a->x * a->x + a->y * a->y);
149
- a->x /= len;
150
- a->y /= len;
153
+ double values[] = {a->x, a->y};
154
+ larb_normalize(values, 2);
155
+ a->x = values[0];
156
+ a->y = values[1];
151
157
  return self;
152
158
  }
153
159
 
@@ -173,12 +179,17 @@ VALUE vec2_aref(VALUE self, VALUE index) {
173
179
  }
174
180
 
175
181
  VALUE vec2_aset(VALUE self, VALUE index, VALUE value) {
182
+ rb_check_frozen(self);
176
183
  Vec2Data *a = vec2_get(self);
177
184
  long idx = NUM2LONG(index);
185
+ if (idx < 0) idx += 2;
186
+ if (idx < 0 || idx >= 2) rb_raise(rb_eIndexError, "Index out of range");
187
+ double component = NUM2DBL(value);
188
+ rb_check_frozen(self);
178
189
  if (idx == 0) {
179
- a->x = NUM2DBL(value);
190
+ a->x = component;
180
191
  } else {
181
- a->y = NUM2DBL(value);
192
+ a->y = component;
182
193
  }
183
194
  return value;
184
195
  }
@@ -199,7 +210,10 @@ VALUE vec2_near(int argc, VALUE *argv, VALUE self) {
199
210
  rb_scan_args(argc, argv, "11", &other, &epsilon);
200
211
  Vec2Data *a = vec2_get(self);
201
212
  Vec2Data *b = vec2_get(other);
202
- double eps = NIL_P(epsilon) ? 1e-6 : NUM2DBL(epsilon);
213
+ double eps = argc < 2 ? 1e-6 : NUM2DBL(epsilon);
214
+ if (!isfinite(eps) || eps <= 0.0) {
215
+ rb_raise(rb_eArgError, "epsilon must be finite and positive");
216
+ }
203
217
 
204
218
  if (fabs(a->x - b->x) < eps && fabs(a->y - b->y) < eps) {
205
219
  return Qtrue;
@@ -232,7 +246,7 @@ VALUE vec2_distance(VALUE self, VALUE other) {
232
246
  Vec2Data *b = vec2_get(other);
233
247
  double dx = a->x - b->x;
234
248
  double dy = a->y - b->y;
235
- return DBL2NUM(sqrt(dx * dx + dy * dy));
249
+ return DBL2NUM(hypot(dx, dy));
236
250
  }
237
251
 
238
252
  VALUE vec2_distance_squared(VALUE self, VALUE other) {
@@ -265,12 +279,13 @@ VALUE vec2_reflect(VALUE self, VALUE normal) {
265
279
  VALUE vec2_clamp_length(VALUE self, VALUE max_length) {
266
280
  Vec2Data *a = vec2_get(self);
267
281
  double max_len = NUM2DBL(max_length);
268
- double len_sq = a->x * a->x + a->y * a->y;
269
- if (len_sq <= max_len * max_len) {
270
- return self;
282
+ if (!isfinite(max_len) || max_len < 0.0) {
283
+ rb_raise(rb_eArgError, "max_length must be finite and nonnegative");
271
284
  }
272
- double scale = max_len / sqrt(len_sq);
273
- return vec2_build(rb_obj_class(self), a->x * scale, a->y * scale);
285
+ if (hypot(a->x, a->y) <= max_len) return self;
286
+ double values[] = {a->x, a->y};
287
+ larb_normalize(values, 2);
288
+ return vec2_build(rb_obj_class(self), values[0] * max_len, values[1] * max_len);
274
289
  }
275
290
 
276
291
  VALUE vec2_to_vec3(int argc, VALUE *argv, VALUE self) {
@@ -280,7 +295,7 @@ VALUE vec2_to_vec3(int argc, VALUE *argv, VALUE self) {
280
295
  rb_scan_args(argc, argv, "01", &vz);
281
296
  VALUE vec3_class = rb_const_get(mLarb, rb_intern("Vec3"));
282
297
  return rb_funcall(vec3_class, rb_intern("new"), 3, DBL2NUM(a->x),
283
- DBL2NUM(a->y), NIL_P(vz) ? DBL2NUM(0.0) : vz);
298
+ DBL2NUM(a->y), argc == 0 ? DBL2NUM(0.0) : vz);
284
299
  }
285
300
 
286
301
  VALUE vec2_inspect(VALUE self) {
@@ -295,11 +310,19 @@ VALUE vec2_inspect(VALUE self) {
295
310
  return str;
296
311
  }
297
312
 
313
+ static VALUE vec2_initialize_copy(VALUE self, VALUE other) {
314
+ if (self == other) return self;
315
+ rb_obj_init_copy(self, other);
316
+ *vec2_get(self) = *vec2_get(other);
317
+ return self;
318
+ }
319
+
298
320
  void Init_vec2(VALUE module) {
299
321
  cVec2 = rb_define_class_under(module, "Vec2", rb_cObject);
300
322
 
301
323
  rb_define_alloc_func(cVec2, vec2_alloc);
302
324
  rb_define_method(cVec2, "initialize", vec2_initialize, -1);
325
+ rb_define_method(cVec2, "initialize_copy", vec2_initialize_copy, 1);
303
326
 
304
327
  rb_define_singleton_method(cVec2, "[]", vec2_class_bracket, 2);
305
328
  rb_define_singleton_method(cVec2, "zero", vec2_class_zero, 0);
data/ext/larb/vec3.c CHANGED
@@ -20,11 +20,6 @@ static const rb_data_type_t vec3_type = {
20
20
 
21
21
  static VALUE cVec3 = Qnil;
22
22
 
23
- static double value_to_double(VALUE value) {
24
- VALUE coerced = rb_funcall(value, rb_intern("to_f"), 0);
25
- return NUM2DBL(coerced);
26
- }
27
-
28
23
  static Vec3Data *vec3_get(VALUE obj) {
29
24
  Vec3Data *data = NULL;
30
25
  TypedData_Get_Struct(obj, Vec3Data, &vec3_type, data);
@@ -59,22 +54,24 @@ VALUE vec3_alloc(VALUE klass) {
59
54
  }
60
55
 
61
56
  VALUE vec3_initialize(int argc, VALUE *argv, VALUE self) {
57
+ rb_check_frozen(self);
62
58
  VALUE vx = Qnil;
63
59
  VALUE vy = Qnil;
64
60
  VALUE vz = Qnil;
65
- Vec3Data *data = vec3_get(self);
66
-
67
61
  rb_scan_args(argc, argv, "03", &vx, &vy, &vz);
68
- data->x = NIL_P(vx) ? 0.0 : value_to_double(vx);
69
- data->y = NIL_P(vy) ? 0.0 : value_to_double(vy);
70
- data->z = NIL_P(vz) ? 0.0 : value_to_double(vz);
71
-
62
+ Vec3Data value = {
63
+ argc > 0 ? NUM2DBL(vx) : 0.0,
64
+ argc > 1 ? NUM2DBL(vy) : 0.0,
65
+ argc > 2 ? NUM2DBL(vz) : 0.0
66
+ };
67
+ rb_check_frozen(self);
68
+ *vec3_get(self) = value;
72
69
  return self;
73
70
  }
74
71
 
75
72
  static VALUE vec3_class_bracket(VALUE klass, VALUE x, VALUE y, VALUE z) {
76
- return vec3_build(klass, value_to_double(x), value_to_double(y),
77
- value_to_double(z));
73
+ return vec3_build(klass, NUM2DBL(x), NUM2DBL(y),
74
+ NUM2DBL(z));
78
75
  }
79
76
 
80
77
  static VALUE vec3_class_zero(VALUE klass) {
@@ -115,8 +112,11 @@ static VALUE vec3_get_x(VALUE self) {
115
112
  }
116
113
 
117
114
  static VALUE vec3_set_x(VALUE self, VALUE value) {
115
+ rb_check_frozen(self);
118
116
  Vec3Data *data = vec3_get(self);
119
- data->x = value_to_double(value);
117
+ double component = NUM2DBL(value);
118
+ rb_check_frozen(self);
119
+ data->x = component;
120
120
  return value;
121
121
  }
122
122
 
@@ -126,8 +126,11 @@ static VALUE vec3_get_y(VALUE self) {
126
126
  }
127
127
 
128
128
  static VALUE vec3_set_y(VALUE self, VALUE value) {
129
+ rb_check_frozen(self);
129
130
  Vec3Data *data = vec3_get(self);
130
- data->y = value_to_double(value);
131
+ double component = NUM2DBL(value);
132
+ rb_check_frozen(self);
133
+ data->y = component;
131
134
  return value;
132
135
  }
133
136
 
@@ -137,8 +140,11 @@ static VALUE vec3_get_z(VALUE self) {
137
140
  }
138
141
 
139
142
  static VALUE vec3_set_z(VALUE self, VALUE value) {
143
+ rb_check_frozen(self);
140
144
  Vec3Data *data = vec3_get(self);
141
- data->z = value_to_double(value);
145
+ double component = NUM2DBL(value);
146
+ rb_check_frozen(self);
147
+ data->z = component;
142
148
  return value;
143
149
  }
144
150
 
@@ -165,13 +171,13 @@ VALUE vec3_mul(VALUE self, VALUE scalar) {
165
171
  a->z * b->z);
166
172
  }
167
173
 
168
- double s = value_to_double(scalar);
174
+ double s = NUM2DBL(scalar);
169
175
  return vec3_build(rb_obj_class(self), a->x * s, a->y * s, a->z * s);
170
176
  }
171
177
 
172
178
  VALUE vec3_div(VALUE self, VALUE scalar) {
173
179
  Vec3Data *a = vec3_get(self);
174
- double s = value_to_double(scalar);
180
+ double s = NUM2DBL(scalar);
175
181
  return vec3_build(rb_obj_class(self), a->x / s, a->y / s, a->z / s);
176
182
  }
177
183
 
@@ -196,7 +202,7 @@ VALUE vec3_cross(VALUE self, VALUE other) {
196
202
 
197
203
  VALUE vec3_length(VALUE self) {
198
204
  Vec3Data *a = vec3_get(self);
199
- return DBL2NUM(sqrt(a->x * a->x + a->y * a->y + a->z * a->z));
205
+ return DBL2NUM(hypot(hypot(a->x, a->y), a->z));
200
206
  }
201
207
 
202
208
  VALUE vec3_length_squared(VALUE self) {
@@ -206,8 +212,9 @@ VALUE vec3_length_squared(VALUE self) {
206
212
 
207
213
  VALUE vec3_normalize(VALUE self) {
208
214
  Vec3Data *a = vec3_get(self);
209
- double len = sqrt(a->x * a->x + a->y * a->y + a->z * a->z);
210
- return vec3_build(rb_obj_class(self), a->x / len, a->y / len, a->z / len);
215
+ double values[] = {a->x, a->y, a->z};
216
+ larb_normalize(values, 3);
217
+ return vec3_build(rb_obj_class(self), values[0], values[1], values[2]);
211
218
  }
212
219
 
213
220
  VALUE vec3_reflect(VALUE self, VALUE normal) {
@@ -242,7 +249,7 @@ VALUE vec3_yz(VALUE self) {
242
249
  VALUE vec3_lerp(VALUE self, VALUE other, VALUE t) {
243
250
  Vec3Data *a = vec3_get(self);
244
251
  Vec3Data *b = vec3_get(other);
245
- double s = value_to_double(t);
252
+ double s = NUM2DBL(t);
246
253
  return vec3_build(rb_obj_class(self), a->x + (b->x - a->x) * s,
247
254
  a->y + (b->y - a->y) * s,
248
255
  a->z + (b->z - a->z) * s);
@@ -265,7 +272,7 @@ VALUE vec3_to_vec4(int argc, VALUE *argv, VALUE self) {
265
272
  VALUE vec4_class = rb_const_get(mLarb, rb_intern("Vec4"));
266
273
  return rb_funcall(vec4_class, rb_intern("new"), 4, DBL2NUM(a->x),
267
274
  DBL2NUM(a->y), DBL2NUM(a->z),
268
- NIL_P(vw) ? DBL2NUM(1.0) : vw);
275
+ argc == 0 ? DBL2NUM(1.0) : vw);
269
276
  }
270
277
 
271
278
  VALUE vec3_aref(VALUE self, VALUE index) {
@@ -289,7 +296,10 @@ VALUE vec3_near(int argc, VALUE *argv, VALUE self) {
289
296
  rb_scan_args(argc, argv, "11", &other, &epsilon);
290
297
  Vec3Data *a = vec3_get(self);
291
298
  Vec3Data *b = vec3_get(other);
292
- double eps = NIL_P(epsilon) ? 1e-6 : value_to_double(epsilon);
299
+ double eps = argc < 2 ? 1e-6 : NUM2DBL(epsilon);
300
+ if (!isfinite(eps) || eps <= 0.0) {
301
+ rb_raise(rb_eArgError, "epsilon must be finite and positive");
302
+ }
293
303
 
294
304
  if (fabs(a->x - b->x) < eps && fabs(a->y - b->y) < eps &&
295
305
  fabs(a->z - b->z) < eps) {
@@ -304,7 +314,7 @@ VALUE vec3_distance(VALUE self, VALUE other) {
304
314
  double dx = a->x - b->x;
305
315
  double dy = a->y - b->y;
306
316
  double dz = a->z - b->z;
307
- return DBL2NUM(sqrt(dx * dx + dy * dy + dz * dz));
317
+ return DBL2NUM(hypot(hypot(dx, dy), dz));
308
318
  }
309
319
 
310
320
  VALUE vec3_distance_squared(VALUE self, VALUE other) {
@@ -319,75 +329,92 @@ VALUE vec3_distance_squared(VALUE self, VALUE other) {
319
329
  VALUE vec3_angle_between(VALUE self, VALUE other) {
320
330
  Vec3Data *a = vec3_get(self);
321
331
  Vec3Data *b = vec3_get(other);
322
- double len = sqrt(a->x * a->x + a->y * a->y + a->z * a->z);
323
- double other_len = sqrt(b->x * b->x + b->y * b->y + b->z * b->z);
324
- double dot = a->x * b->x + a->y * b->y + a->z * b->z;
325
- double d = dot / (len * other_len);
332
+ double av[] = {a->x, a->y, a->z};
333
+ double bv[] = {b->x, b->y, b->z};
334
+ larb_normalize(av, 3);
335
+ larb_normalize(bv, 3);
336
+ double d = av[0] * bv[0] + av[1] * bv[1] + av[2] * bv[2];
326
337
  return DBL2NUM(acos(clamp_double(d, -1.0, 1.0)));
327
338
  }
328
339
 
329
340
  VALUE vec3_project(VALUE self, VALUE onto) {
330
341
  Vec3Data *a = vec3_get(self);
331
342
  Vec3Data *b = vec3_get(onto);
332
- double denom = b->x * b->x + b->y * b->y + b->z * b->z;
333
- double scale = (a->x * b->x + a->y * b->y + a->z * b->z) / denom;
334
- return vec3_build(rb_obj_class(self), b->x * scale, b->y * scale,
335
- b->z * scale);
343
+ double direction[] = {b->x, b->y, b->z};
344
+ larb_normalize(direction, 3);
345
+ double scale = a->x * direction[0] + a->y * direction[1] + a->z * direction[2];
346
+ return vec3_build(rb_obj_class(self), direction[0] * scale,
347
+ direction[1] * scale, direction[2] * scale);
336
348
  }
337
349
 
338
350
  VALUE vec3_reject(VALUE self, VALUE from) {
339
- Vec3Data *a = vec3_get(self);
340
- Vec3Data *b = vec3_get(from);
341
- double denom = b->x * b->x + b->y * b->y + b->z * b->z;
342
- double scale = (a->x * b->x + a->y * b->y + a->z * b->z) / denom;
343
- double px = b->x * scale;
344
- double py = b->y * scale;
345
- double pz = b->z * scale;
346
- return vec3_build(rb_obj_class(self), a->x - px, a->y - py, a->z - pz);
351
+ return vec3_sub(self, vec3_project(self, from));
347
352
  }
348
353
 
349
354
  VALUE vec3_slerp(VALUE self, VALUE other, VALUE t) {
350
355
  Vec3Data *a = vec3_get(self);
351
356
  Vec3Data *b = vec3_get(other);
352
- double s = value_to_double(t);
353
-
354
- double len = sqrt(a->x * a->x + a->y * a->y + a->z * a->z);
355
- double other_len = sqrt(b->x * b->x + b->y * b->y + b->z * b->z);
356
- double dot = (a->x * b->x + a->y * b->y + a->z * b->z) / (len * other_len);
357
- dot = clamp_double(dot, -1.0, 1.0);
358
-
359
- double theta = acos(dot) * s;
360
- double rx = b->x - a->x * dot;
361
- double ry = b->y - a->y * dot;
362
- double rz = b->z - a->z * dot;
363
- double rlen = sqrt(rx * rx + ry * ry + rz * rz);
364
- rx /= rlen;
365
- ry /= rlen;
366
- rz /= rlen;
367
-
368
- return vec3_build(rb_obj_class(self), a->x * cos(theta) + rx * sin(theta),
369
- a->y * cos(theta) + ry * sin(theta),
370
- a->z * cos(theta) + rz * sin(theta));
357
+ double s = NUM2DBL(t);
358
+ double len = hypot(hypot(a->x, a->y), a->z);
359
+ double other_len = hypot(hypot(b->x, b->y), b->z);
360
+ if (!isfinite(s) || !isfinite(len) || !isfinite(other_len)) {
361
+ rb_raise(rb_eArgError, "slerp requires finite lengths and interpolation parameter");
362
+ }
363
+ double av[] = {a->x, a->y, a->z};
364
+ double bv[] = {b->x, b->y, b->z};
365
+ larb_normalize(av, 3);
366
+ larb_normalize(bv, 3);
367
+ if (s == 0.0) return vec3_build(rb_obj_class(self), a->x, a->y, a->z);
368
+ if (s == 1.0) return vec3_build(rb_obj_class(self), b->x, b->y, b->z);
369
+
370
+ double dot = clamp_double(av[0] * bv[0] + av[1] * bv[1] + av[2] * bv[2], -1.0, 1.0);
371
+ double direction[3];
372
+ if (dot > 1.0 - 1e-12) {
373
+ for (int i = 0; i < 3; i++) direction[i] = av[i] + (bv[i] - av[i]) * s;
374
+ } else {
375
+ double normal[] = {av[1] * bv[2] - av[2] * bv[1],
376
+ av[2] * bv[0] - av[0] * bv[2],
377
+ av[0] * bv[1] - av[1] * bv[0]};
378
+ double tangent[] = {normal[1] * av[2] - normal[2] * av[1],
379
+ normal[2] * av[0] - normal[0] * av[2],
380
+ normal[0] * av[1] - normal[1] * av[0]};
381
+ double sine = hypot(hypot(normal[0], normal[1]), normal[2]);
382
+ if (sine < 1e-12) {
383
+ // Antipodes have no unique arc; choose the least-aligned coordinate axis.
384
+ int axis = 0;
385
+ for (int i = 1; i < 3; i++) if (fabs(av[i]) < fabs(av[axis])) axis = i;
386
+ for (int i = 0; i < 3; i++) tangent[i] = (i == axis ? 1.0 : 0.0) - av[i] * av[axis];
387
+ }
388
+ larb_normalize(tangent, 3);
389
+ double theta = atan2(sine, dot) * s;
390
+ for (int i = 0; i < 3; i++) direction[i] = av[i] * cos(theta) + tangent[i] * sin(theta);
391
+ }
392
+ larb_normalize(direction, 3);
393
+ double length = (1.0 - s) * len + s * other_len;
394
+ return vec3_build(rb_obj_class(self), direction[0] * length,
395
+ direction[1] * length, direction[2] * length);
371
396
  }
372
397
 
373
398
  VALUE vec3_clamp_length(VALUE self, VALUE max_length) {
374
399
  Vec3Data *a = vec3_get(self);
375
- double max_len = value_to_double(max_length);
376
- double len_sq = a->x * a->x + a->y * a->y + a->z * a->z;
377
- if (len_sq <= max_len * max_len) {
378
- return self;
400
+ double max_len = NUM2DBL(max_length);
401
+ if (!isfinite(max_len) || max_len < 0.0) {
402
+ rb_raise(rb_eArgError, "max_length must be finite and nonnegative");
379
403
  }
380
- double scale = max_len / sqrt(len_sq);
381
- return vec3_build(rb_obj_class(self), a->x * scale, a->y * scale,
382
- a->z * scale);
404
+ if (hypot(hypot(a->x, a->y), a->z) <= max_len) return self;
405
+ double values[] = {a->x, a->y, a->z};
406
+ larb_normalize(values, 3);
407
+ return vec3_build(rb_obj_class(self), values[0] * max_len, values[1] * max_len, values[2] * max_len);
383
408
  }
384
409
 
385
410
  VALUE vec3_normalize_bang(VALUE self) {
411
+ rb_check_frozen(self);
386
412
  Vec3Data *a = vec3_get(self);
387
- double len = sqrt(a->x * a->x + a->y * a->y + a->z * a->z);
388
- a->x /= len;
389
- a->y /= len;
390
- a->z /= len;
413
+ double values[] = {a->x, a->y, a->z};
414
+ larb_normalize(values, 3);
415
+ a->x = values[0];
416
+ a->y = values[1];
417
+ a->z = values[2];
391
418
  return self;
392
419
  }
393
420
 
@@ -440,11 +467,19 @@ VALUE vec3_inspect(VALUE self) {
440
467
  return str;
441
468
  }
442
469
 
470
+ static VALUE vec3_initialize_copy(VALUE self, VALUE other) {
471
+ if (self == other) return self;
472
+ rb_obj_init_copy(self, other);
473
+ *vec3_get(self) = *vec3_get(other);
474
+ return self;
475
+ }
476
+
443
477
  void Init_vec3(VALUE module) {
444
478
  cVec3 = rb_define_class_under(module, "Vec3", rb_cObject);
445
479
 
446
480
  rb_define_alloc_func(cVec3, vec3_alloc);
447
481
  rb_define_method(cVec3, "initialize", vec3_initialize, -1);
482
+ rb_define_method(cVec3, "initialize_copy", vec3_initialize_copy, 1);
448
483
 
449
484
  rb_define_singleton_method(cVec3, "[]", vec3_class_bracket, 3);
450
485
  rb_define_singleton_method(cVec3, "zero", vec3_class_zero, 0);