geom_craft 0.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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +20 -0
- data/Rakefile +4 -0
- data/geom_craft.gemspec +23 -0
- data/lib/geom_craft/core_ext/math.rb +13 -0
- data/lib/geom_craft/core_ext/numeric.rb +44 -0
- data/lib/geom_craft/core_ext.rb +7 -0
- data/lib/geom_craft/rand_norm.rb +55 -0
- data/lib/geom_craft/range2.rb +462 -0
- data/lib/geom_craft/rect.rb +723 -0
- data/lib/geom_craft/top_level_bind.rb +17 -0
- data/lib/geom_craft/vec2.rb +610 -0
- data/lib/geom_craft/version.rb +3 -0
- data/lib/geom_craft.rb +8 -0
- data/spec/geom_craft_spec.rb +4 -0
- data/spec/spec_helper.rb +6 -0
- data/workbench/core_ext.rb +21 -0
- data/workbench/range2.rb +217 -0
- data/workbench/rect.rb +271 -0
- data/workbench/setup.rb +8 -0
- data/workbench/vec2.rb +538 -0
- metadata +110 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require "geom_craft"
|
2
|
+
|
3
|
+
RandNorm = GeomCraft::RandNorm
|
4
|
+
V = GeomCraft::Vec2
|
5
|
+
Rect = GeomCraft::Rect
|
6
|
+
Range2 = GeomCraft::Range2
|
7
|
+
|
8
|
+
Kernel.module_eval do
|
9
|
+
private
|
10
|
+
def rand_norm(...)
|
11
|
+
GeomCraft::RandNorm.call(...)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if $0 == __FILE__
|
16
|
+
rand_norm # => 0.2171834669473525
|
17
|
+
end
|
@@ -0,0 +1,610 @@
|
|
1
|
+
# https://zenn.dev/megeton/articles/b407350ad51562
|
2
|
+
# https://docs.rs/glam/latest/glam/f32/struct.Vec2.html
|
3
|
+
|
4
|
+
require "forwardable"
|
5
|
+
require "geom_craft/rand_norm"
|
6
|
+
|
7
|
+
module GeomCraft
|
8
|
+
class Vec2
|
9
|
+
# https://github.com/godotengine/godot/blob/44e399ed5fa895f760b2995e59788bdb49782666/core/math/math_defs.h#L53
|
10
|
+
UNIT_EPSILON = 0.00001
|
11
|
+
private_constant :UNIT_EPSILON
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def [](...)
|
15
|
+
new(...)
|
16
|
+
end
|
17
|
+
|
18
|
+
def splat(v); new(v, v); end
|
19
|
+
|
20
|
+
def zero_element; 0.0; end
|
21
|
+
def one_element; 1.0; end
|
22
|
+
|
23
|
+
def zero; splat(zero_element); end
|
24
|
+
def one; splat(one_element); end
|
25
|
+
def neg_one; splat(-one_element); end
|
26
|
+
|
27
|
+
def max; splat(Float::INFINITY); end
|
28
|
+
def min; splat(-Float::INFINITY); end
|
29
|
+
|
30
|
+
def infinity; max; end
|
31
|
+
def neg_infinity; min; end
|
32
|
+
|
33
|
+
def nan; splat(0.0 / 0.0); end
|
34
|
+
|
35
|
+
def x; new(one_element, zero_element); end
|
36
|
+
def y; new(zero_element, one_element); end
|
37
|
+
def neg_x; new(-one_element, zero_element); end
|
38
|
+
def neg_y; new(zero_element, -one_element); end
|
39
|
+
|
40
|
+
def left; new(-one_element, zero_element); end
|
41
|
+
def right; new(+one_element, zero_element); end
|
42
|
+
def up; new(zero_element, -one_element); end
|
43
|
+
def down; new(zero_element, +one_element); end
|
44
|
+
|
45
|
+
def axes; [x, y]; end
|
46
|
+
|
47
|
+
def rand(...); new(Kernel.rand(...), Kernel.rand(...)); end
|
48
|
+
def rand_norm(...); new(RandNorm.call(...), RandNorm.call(...)); end # mu: 0, sigma: 1.0, random: Random.new
|
49
|
+
end
|
50
|
+
|
51
|
+
extend Forwardable
|
52
|
+
def_delegators :to_a, :each
|
53
|
+
def_delegators :to_a, :==, :hash, :<=>;
|
54
|
+
def_delegators :to_a, :to_ary, :sum, :[]
|
55
|
+
|
56
|
+
include Enumerable
|
57
|
+
|
58
|
+
attr_accessor :x, :y
|
59
|
+
|
60
|
+
def initialize(x = self.class.zero_element, y = self.class.zero_element)
|
61
|
+
@x = x
|
62
|
+
@y = y
|
63
|
+
end
|
64
|
+
|
65
|
+
def eql?(other)
|
66
|
+
self == other
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_a
|
70
|
+
[x, y]
|
71
|
+
end
|
72
|
+
|
73
|
+
def as_json
|
74
|
+
{ x: x, y: y }
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_s
|
78
|
+
"(#{x}, #{y})"
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_h(prefix: "", suffix: "")
|
82
|
+
{
|
83
|
+
"#{prefix}x#{suffix}".to_sym => x,
|
84
|
+
"#{prefix}y#{suffix}".to_sym => y,
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspect
|
89
|
+
to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
# def scale(s)
|
93
|
+
# self * s
|
94
|
+
# end
|
95
|
+
|
96
|
+
def min(other); self.class.new([x, other.x].min, [y, other.y].min); end
|
97
|
+
def max(other); self.class.new([x, other.x].max, [y, other.y].max); end
|
98
|
+
|
99
|
+
def clamp(min, max)
|
100
|
+
min.cmple(max).all? or raise "clamp: expected min <= max"
|
101
|
+
max(min).min(max)
|
102
|
+
end
|
103
|
+
|
104
|
+
# https://github.com/godotengine/godot/blob/44e399ed5fa895f760b2995e59788bdb49782666/core/math/vector2.cpp#L138
|
105
|
+
def snapped(step)
|
106
|
+
self.class.new(Math.snapped(x, step.x), Math.snapped(y, step.y))
|
107
|
+
end
|
108
|
+
|
109
|
+
def min_element; to_a.min; end
|
110
|
+
def max_element; to_a.max; end
|
111
|
+
|
112
|
+
def cmpeq(other); self.class.new(x == other.x, y == other.y); end
|
113
|
+
def cmpne(other); self.class.new(x != other.x, y != other.y); end
|
114
|
+
def cmpge(other); self.class.new(x >= other.x, y >= other.y); end
|
115
|
+
def cmpgt(other); self.class.new(x > other.x, y > other.y); end
|
116
|
+
def cmple(other); self.class.new(x <= other.x, y <= other.y); end
|
117
|
+
def cmplt(other); self.class.new(x < other.x, y < other.y); end
|
118
|
+
|
119
|
+
def abs
|
120
|
+
self.class.new(x.abs, y.abs)
|
121
|
+
end
|
122
|
+
|
123
|
+
def sign
|
124
|
+
self.class.new(
|
125
|
+
x.negative? ? -1 : 1,
|
126
|
+
y.negative? ? -1 : 1,
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
def signum
|
131
|
+
sign
|
132
|
+
end
|
133
|
+
|
134
|
+
def copysign(other)
|
135
|
+
abs * other.signum
|
136
|
+
end
|
137
|
+
|
138
|
+
def finite?
|
139
|
+
all?(&:finite?)
|
140
|
+
end
|
141
|
+
|
142
|
+
def nan?
|
143
|
+
any?(&:nan?)
|
144
|
+
end
|
145
|
+
|
146
|
+
def nan_mask
|
147
|
+
self.class.new(x.nan?, y.nan?)
|
148
|
+
end
|
149
|
+
|
150
|
+
def zero?; all?(&:zero?); end
|
151
|
+
def nonzero?; all?(&:nonzero?); end
|
152
|
+
|
153
|
+
def length_squared; dot(self); end
|
154
|
+
def length; Math.sqrt(dot(self)); end
|
155
|
+
|
156
|
+
def norm; length; end
|
157
|
+
def mag; length; end
|
158
|
+
def magnitude; length; end
|
159
|
+
|
160
|
+
def length_recip; 1.0 / length; end
|
161
|
+
|
162
|
+
def distance_to(other); (other - self).length; end
|
163
|
+
def distance_squared_to(other); (other - self).length_squared; end
|
164
|
+
|
165
|
+
################################################################################
|
166
|
+
|
167
|
+
def normalize
|
168
|
+
normalized = self * length_recip
|
169
|
+
normalized.finite? or raise
|
170
|
+
normalized
|
171
|
+
end
|
172
|
+
|
173
|
+
def try_normalize
|
174
|
+
rcp = length_recip
|
175
|
+
if rcp.finite? && rcp.positive?
|
176
|
+
self * rcp
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def normalize_or_zero
|
181
|
+
try_normalize || self.class.zero
|
182
|
+
end
|
183
|
+
|
184
|
+
def normalized?
|
185
|
+
(length_squared - 1.0).abs < UNIT_EPSILON
|
186
|
+
end
|
187
|
+
|
188
|
+
################################################################################
|
189
|
+
|
190
|
+
def project_onto(other)
|
191
|
+
other_len_sq_rcp = 1.0 / other.length_squared
|
192
|
+
other_len_sq_rcp.finite? or raise
|
193
|
+
other * dot(other) * other_len_sq_rcp
|
194
|
+
end
|
195
|
+
|
196
|
+
def reject_from(other)
|
197
|
+
self - project_onto(other)
|
198
|
+
end
|
199
|
+
|
200
|
+
def project_onto_normalized(other)
|
201
|
+
other.normalized? or raise
|
202
|
+
other * dot(other)
|
203
|
+
end
|
204
|
+
|
205
|
+
def reject_from_normalized(other)
|
206
|
+
self - project_onto_normalized(other)
|
207
|
+
end
|
208
|
+
|
209
|
+
################################################################################
|
210
|
+
|
211
|
+
def slide(normal)
|
212
|
+
self - project_onto_normalized(normal)
|
213
|
+
end
|
214
|
+
|
215
|
+
def bounce(normal)
|
216
|
+
self - project_onto_normalized(normal) * 2
|
217
|
+
end
|
218
|
+
|
219
|
+
def reflect(normal)
|
220
|
+
project_onto_normalized(normal) * 2 - self
|
221
|
+
end
|
222
|
+
|
223
|
+
################################################################################
|
224
|
+
|
225
|
+
def round(...); self.class.new(x.round(...), y.round(...)); end
|
226
|
+
def floor(...); self.class.new(x.floor(...), y.floor(...)); end
|
227
|
+
def ceil(...); self.class.new(x.ceil(...), y.ceil(...)); end
|
228
|
+
def truncate(...); self.class.new(x.truncate(...), y.truncate(...)); end
|
229
|
+
def trunc(...); truncate(...); end
|
230
|
+
def fract; self - floor; end
|
231
|
+
|
232
|
+
################################################################################
|
233
|
+
|
234
|
+
def exp
|
235
|
+
self.class.new(Math.exp(x), Math.exp(y))
|
236
|
+
end
|
237
|
+
|
238
|
+
def pow(...)
|
239
|
+
self.class.new(x.pow(...), y.pow(...))
|
240
|
+
end
|
241
|
+
|
242
|
+
def recip
|
243
|
+
self.class.new(1.0 / x, 1.0 / y)
|
244
|
+
end
|
245
|
+
|
246
|
+
def lerp(other, s)
|
247
|
+
self + (other - self) * s
|
248
|
+
end
|
249
|
+
|
250
|
+
def mix(...)
|
251
|
+
lerp(...)
|
252
|
+
end
|
253
|
+
|
254
|
+
def abs_diff_eq(other, max_abs_diff)
|
255
|
+
sub(other).abs.cmple(self.class.splat(max_abs_diff)).all?
|
256
|
+
end
|
257
|
+
|
258
|
+
################################################################################
|
259
|
+
|
260
|
+
def clamp_length(min, max)
|
261
|
+
min <= max or raise
|
262
|
+
length_sq = length_squared
|
263
|
+
if length_sq < min * min
|
264
|
+
min * self / Math.sqrt(length_sq)
|
265
|
+
elsif length_sq > max * max
|
266
|
+
max * self / Math.sqrt(length_sq)
|
267
|
+
else
|
268
|
+
self
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def clamp_length_max(max)
|
273
|
+
length_sq = length_squared
|
274
|
+
if length_sq > max * max
|
275
|
+
max * self / Math.sqrt(length_sq)
|
276
|
+
else
|
277
|
+
self
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def clamp_length_min(min)
|
282
|
+
length_sq = length_squared
|
283
|
+
if length_sq < min * min
|
284
|
+
min * self / Math.sqrt(length_sq)
|
285
|
+
else
|
286
|
+
self
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
################################################################################
|
291
|
+
|
292
|
+
class << self
|
293
|
+
def from_angle(angle)
|
294
|
+
new(Math.cos(angle), Math.sin(angle))
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# https://github.com/godotengine/godot/blob/44e399ed5fa895f760b2995e59788bdb49782666/core/math/vector2.cpp#L81
|
299
|
+
def angle_to(other)
|
300
|
+
Math.atan2(cross(other), dot(other))
|
301
|
+
end
|
302
|
+
|
303
|
+
def angle_between(other)
|
304
|
+
angle_to(other)
|
305
|
+
end
|
306
|
+
|
307
|
+
# https://github.com/godotengine/godot/blob/44e399ed5fa895f760b2995e59788bdb49782666/core/math/vector2.cpp#L84
|
308
|
+
def angle_to_point(other)
|
309
|
+
(other - self).angle
|
310
|
+
end
|
311
|
+
|
312
|
+
def angle
|
313
|
+
Math.atan2(y, x)
|
314
|
+
end
|
315
|
+
|
316
|
+
def rotate(other)
|
317
|
+
# https://docs.rs/nannou_core/0.18.0/src/nannou_core/math.rs.html#92
|
318
|
+
unless other.kind_of?(self.class)
|
319
|
+
other = V.from_angle(other)
|
320
|
+
end
|
321
|
+
|
322
|
+
# https://docs.rs/glam/lait/src/glam/f32/vec2.rs.html#662
|
323
|
+
self.class.new(
|
324
|
+
x * other.x - y * other.y,
|
325
|
+
y * other.x + x * other.y,
|
326
|
+
)
|
327
|
+
end
|
328
|
+
|
329
|
+
# Experimental
|
330
|
+
def rotate_angle_add(rad)
|
331
|
+
self.class.from_angle(angle + rad) * length
|
332
|
+
end
|
333
|
+
|
334
|
+
################################################################################
|
335
|
+
|
336
|
+
def dot(other)
|
337
|
+
x * other.x + y * other.y
|
338
|
+
end
|
339
|
+
|
340
|
+
def inner_product(...); dot(...); end
|
341
|
+
|
342
|
+
################################################################################
|
343
|
+
|
344
|
+
def cross(other)
|
345
|
+
x * other.y - y * other.x
|
346
|
+
end
|
347
|
+
|
348
|
+
def perp_dot(...); cross(...); end
|
349
|
+
def outer_product(...); cross(...); end
|
350
|
+
|
351
|
+
################################################################################
|
352
|
+
|
353
|
+
def perp
|
354
|
+
right90
|
355
|
+
end
|
356
|
+
|
357
|
+
def right90
|
358
|
+
self.class.new(-y, x)
|
359
|
+
end
|
360
|
+
|
361
|
+
def left90
|
362
|
+
self.class.new(y, -x)
|
363
|
+
end
|
364
|
+
|
365
|
+
################################################################################
|
366
|
+
|
367
|
+
def add(other)
|
368
|
+
if other.kind_of?(self.class)
|
369
|
+
self.class.new(x + other.x, y + other.y)
|
370
|
+
else
|
371
|
+
self.class.new(x + other, y + other)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def sub(other)
|
376
|
+
if other.kind_of?(self.class)
|
377
|
+
self.class.new(x - other.x, y - other.y)
|
378
|
+
else
|
379
|
+
self.class.new(x - other, y - other)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def mul(other)
|
384
|
+
if other.kind_of?(self.class)
|
385
|
+
self.class.new(x * other.x, y * other.y)
|
386
|
+
else
|
387
|
+
self.class.new(x * other, y * other)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# pow and `**` are different
|
392
|
+
# float doesn't have pow, but it does have `**`.
|
393
|
+
def **(other)
|
394
|
+
if other.kind_of?(self.class)
|
395
|
+
self.class.new(x**other.x, y**other.y)
|
396
|
+
else
|
397
|
+
self.class.new(x**other, y**other)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def /(other)
|
402
|
+
if other.kind_of?(self.class)
|
403
|
+
self.class.new(x / other.x, y / other.y)
|
404
|
+
else
|
405
|
+
self.class.new(x / other, y / other)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def div(other)
|
410
|
+
if other.kind_of?(self.class)
|
411
|
+
self.class.new(x.div(other.x), y.div(other.y))
|
412
|
+
else
|
413
|
+
self.class.new(x.div(other), y.div(other))
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def fdiv(other)
|
418
|
+
if other.kind_of?(self.class)
|
419
|
+
self.class.new(x.fdiv(other.x), y.fdiv(other.y))
|
420
|
+
else
|
421
|
+
self.class.new(x.fdiv(other), y.fdiv(other))
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def ceildiv(other)
|
426
|
+
if other.kind_of?(self.class)
|
427
|
+
self.class.new(x.ceildiv(other.x), y.ceildiv(other.y))
|
428
|
+
else
|
429
|
+
self.class.new(x.ceildiv(other), y.ceildiv(other))
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def modulo(other)
|
434
|
+
if other.kind_of?(self.class)
|
435
|
+
self.class.new(x.modulo(other.x), y.modulo(other.y))
|
436
|
+
else
|
437
|
+
self.class.new(x.modulo(other), y.modulo(other))
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
def +(other)
|
442
|
+
add(other)
|
443
|
+
end
|
444
|
+
|
445
|
+
def -(other)
|
446
|
+
sub(other)
|
447
|
+
end
|
448
|
+
|
449
|
+
def *(other)
|
450
|
+
mul(other)
|
451
|
+
end
|
452
|
+
|
453
|
+
def %(other)
|
454
|
+
modulo(other)
|
455
|
+
end
|
456
|
+
|
457
|
+
def coerce(other)
|
458
|
+
[self, other]
|
459
|
+
end
|
460
|
+
|
461
|
+
def -@
|
462
|
+
self.class.new(-x, -y)
|
463
|
+
end
|
464
|
+
|
465
|
+
def +@
|
466
|
+
self
|
467
|
+
end
|
468
|
+
|
469
|
+
def neg
|
470
|
+
-self
|
471
|
+
end
|
472
|
+
|
473
|
+
# def reverse
|
474
|
+
# -self
|
475
|
+
# end
|
476
|
+
|
477
|
+
# def mul_add(a, b)
|
478
|
+
# self * a + b
|
479
|
+
# end
|
480
|
+
|
481
|
+
################################################################################
|
482
|
+
|
483
|
+
def xy; self; end
|
484
|
+
def yx; self.class.new(y, x); end
|
485
|
+
def xx; self.class.new(x, x); end
|
486
|
+
def yy; self.class.new(y, y); end
|
487
|
+
|
488
|
+
################################################################################
|
489
|
+
|
490
|
+
def center; self * 0.5; end
|
491
|
+
def middle; self * 0.5; end
|
492
|
+
|
493
|
+
def cover?(other, padding: 0)
|
494
|
+
true &&
|
495
|
+
((x - padding)..(x + padding)).cover?(other.x) &&
|
496
|
+
((y - padding)..(y + padding)).cover?(other.y)
|
497
|
+
end
|
498
|
+
|
499
|
+
################################################################################
|
500
|
+
|
501
|
+
def replace(other)
|
502
|
+
self.x, self.y = other.to_a
|
503
|
+
self
|
504
|
+
end
|
505
|
+
|
506
|
+
# def add!(other) = replace(add(other))
|
507
|
+
# def sub!(other) = replace(sub(other))
|
508
|
+
# def mul!(other) = replace(mul(other))
|
509
|
+
# def div!(other) = replace(div(other))
|
510
|
+
# def fdiv!(other) = replace(fdiv(other))
|
511
|
+
# def modulo!(other) = replace(modulo(other))
|
512
|
+
|
513
|
+
if false
|
514
|
+
prepend Module.new {
|
515
|
+
def initialize(...)
|
516
|
+
super
|
517
|
+
freeze
|
518
|
+
end
|
519
|
+
}
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
V = Vec2
|
524
|
+
end
|
525
|
+
|
526
|
+
if $0 == __FILE__ && ENV["ATCODER"] != "1"
|
527
|
+
V = GeomCraft::V
|
528
|
+
|
529
|
+
$LOAD_PATH.unshift("..")
|
530
|
+
require "geom_craft/core_ext"
|
531
|
+
|
532
|
+
require "rspec/autorun"
|
533
|
+
|
534
|
+
RSpec.configure do |config|
|
535
|
+
config.expect_with :test_unit
|
536
|
+
end
|
537
|
+
|
538
|
+
describe V do
|
539
|
+
it "sort" do
|
540
|
+
assert { [V[3, 4], V[1, 2]].sort == [V[1, 2], V[3, 4]] }
|
541
|
+
end
|
542
|
+
|
543
|
+
it "hash key" do
|
544
|
+
assert { V[3, 4] == V[3, 4] }
|
545
|
+
assert { V[3, 4].hash == V[3, 4].hash }
|
546
|
+
assert { V[3, 4].eql?(V[3, 4]) }
|
547
|
+
a = {V[3, 4] => true}
|
548
|
+
assert { a[V[3, 4]] }
|
549
|
+
end
|
550
|
+
|
551
|
+
it "rand_norm" do
|
552
|
+
assert { V.rand_norm.finite? }
|
553
|
+
end
|
554
|
+
|
555
|
+
describe "rotation" do
|
556
|
+
before do
|
557
|
+
@v0 = V.from_angle(45.deg_to_rad).mul(5)
|
558
|
+
end
|
559
|
+
|
560
|
+
it "rotate with radian" do
|
561
|
+
v1 = @v0.rotate(45.deg_to_rad)
|
562
|
+
assert { v1.angle.rad_to_deg.round(2) == 90.0 }
|
563
|
+
assert { v1.length == 5.0 }
|
564
|
+
end
|
565
|
+
|
566
|
+
it "rotate_angle_add" do
|
567
|
+
v1 = @v0.rotate_angle_add(45.deg_to_rad)
|
568
|
+
assert { v1.angle.rad_to_deg.round(2) == 90.0 }
|
569
|
+
assert { v1.length == 5.0 }
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
it "Enumerable" do
|
574
|
+
assert { V[true, true].all? }
|
575
|
+
assert { V[true, true].any? }
|
576
|
+
assert { V[false, false].none? }
|
577
|
+
assert { V.one.sum == 2 }
|
578
|
+
end
|
579
|
+
|
580
|
+
it "min, max" do
|
581
|
+
assert { V[3, 6].min(V[4, 5]) == V[3, 5] }
|
582
|
+
assert { V[3, 6].max(V[4, 5]) == V[4, 6] }
|
583
|
+
end
|
584
|
+
|
585
|
+
it "normalized?" do
|
586
|
+
assert { 1000.times.collect.all? { V.rand.normalize.normalized? } }
|
587
|
+
end
|
588
|
+
|
589
|
+
it "pow" do
|
590
|
+
assert { V[2, 3]**2 == V[4, 9] }
|
591
|
+
assert { V[2, 3].pow(2) == V[4, 9] }
|
592
|
+
end
|
593
|
+
|
594
|
+
it "**" do
|
595
|
+
assert { V[2.0, 3.0]**2.0 == V[4.0, 9.0] }
|
596
|
+
end
|
597
|
+
|
598
|
+
it "distance_to" do
|
599
|
+
a = V.zero
|
600
|
+
b = V.one
|
601
|
+
assert { a.distance_to(b) == b.distance_to(a) }
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
# >> ...........
|
607
|
+
# >>
|
608
|
+
# >> Finished in 0.02384 seconds (files took 0.06057 seconds to load)
|
609
|
+
# >> 11 examples, 0 failures
|
610
|
+
# >>
|
data/lib/geom_craft.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "./setup"
|
2
|
+
|
3
|
+
#+title2: ついでに面倒な角度の扱い簡単にする
|
4
|
+
|
5
|
+
# - 一周を 2π とする人間にはわかりにくいがコンピュータにはわかりやすい radian 単位
|
6
|
+
# - 一周を 360 度とする人間にはわかりやすいがコンピュータはわかりにくい degree 単位
|
7
|
+
# - 一周を 1.0 とする人間にはまぁまぁわかりやすいがコンピュータはわかりにくい単位
|
8
|
+
|
9
|
+
# これらをメソッドチェインで相互変換するため Numeric を拡張する。
|
10
|
+
|
11
|
+
Math::PI.rad_to_rad # => 3.141592653589793
|
12
|
+
Math::PI.rad_to_deg # => 180.0
|
13
|
+
Math::PI.rad_to_turn # => 0.5
|
14
|
+
|
15
|
+
180.deg_to_rad # => 3.141592653589793
|
16
|
+
180.deg_to_deg # => 180
|
17
|
+
180.deg_to_turn # => 0.5
|
18
|
+
|
19
|
+
0.5.turn_to_rad # => 3.141592653589793
|
20
|
+
0.5.turn_to_deg # => 180.0
|
21
|
+
0.5.turn_to_turn # => 0.5
|