stylet_support 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/README.org +106 -0
- data/Rakefile +9 -0
- data/examples/0100_vector.rb +85 -0
- data/examples/setup.rb +2 -0
- data/lib/stylet_support.rb +5 -0
- data/lib/stylet_support/chore.rb +40 -0
- data/lib/stylet_support/magic.rb +246 -0
- data/lib/stylet_support/vector.rb +439 -0
- data/lib/stylet_support/version.rb +3 -0
- data/stylet_support.gemspec +20 -0
- data/test/test_etc.rb +16 -0
- data/test/test_helper.rb +6 -0
- data/test/test_magic.rb +66 -0
- data/test/test_vector.rb +69 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 12e01d3fd7833198068d5740bff66a76001630134c6987f496617ba499a5beb3
|
4
|
+
data.tar.gz: 40630818ff1a9324a7536f0372d8efcb6927e0bc8cd70e761090b9f2c6a8622d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 123937855380833c85ee6f40318ddcf31f9372a5f5cd1d74e305acbcf4d42fae1407ede0ef370ee5ce400132f8fedf9d134aa5214d020e5a38fc8de299498df1
|
7
|
+
data.tar.gz: b9e199aa2dc1ec62c47451050f636632f58bfb9f057d2db55108ce55f9ad2c35566190ce173f96e84fd4527b053eb1152b1b1bfcc886021919f9165a36fa6822
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.org
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
* GUIに依存しない数学関連ライブラリ
|
2
|
+
|
3
|
+
#+BEGIN_SRC ruby
|
4
|
+
Point.new # => [0.0, 0.0]
|
5
|
+
Point[1, 2] # => [1, 2]
|
6
|
+
Point[1, 2].members # => [:x, :y]
|
7
|
+
Point[1, 2].values # => [1, 2]
|
8
|
+
Point[1, 2] # => [1, 2]
|
9
|
+
|
10
|
+
Vector.superclass # => Stylet::Point2
|
11
|
+
|
12
|
+
Point.new(Point[1,2]) # => [1, 2]
|
13
|
+
Point[Point[1,2]] # => [1, 2]
|
14
|
+
|
15
|
+
Vector.new # => [0.0, 0.0]
|
16
|
+
Vector.new # => [0.0, 0.0]
|
17
|
+
Vector.new(1, 2) # => [1, 2]
|
18
|
+
|
19
|
+
Vector.zero # => [0.0, 0.0]
|
20
|
+
Vector.one # => [1.0, 1.0]
|
21
|
+
|
22
|
+
a = Vector[1, 2]
|
23
|
+
b = Vector[3, 4]
|
24
|
+
|
25
|
+
a + b # => [4.0, 6.0]
|
26
|
+
a - b # => [-2.0, -2.0]
|
27
|
+
a * 2 # => [2.0, 4.0]
|
28
|
+
a / 2 # => [0.5, 1.0]
|
29
|
+
|
30
|
+
a.add(b) # => [4.0, 6.0]
|
31
|
+
a.sub(b) # => [-2.0, -2.0]
|
32
|
+
a.scale(2) # => [2.0, 4.0]
|
33
|
+
a.mul(2) # => [2.0, 4.0]
|
34
|
+
a.div(2) # => [0.5, 1.0]
|
35
|
+
|
36
|
+
a + [3, 4] # => [4.0, 6.0]
|
37
|
+
a - [3, 4] # => [-2.0, -2.0]
|
38
|
+
|
39
|
+
Vector.one.reverse # => [-1.0, -1.0]
|
40
|
+
-Vector.one # => [-1.0, -1.0]
|
41
|
+
|
42
|
+
Vector[3, 4].normalize # => [0.6, 0.8]
|
43
|
+
|
44
|
+
Vector.one.normalize # => [0.7071067811865475, 0.7071067811865475]
|
45
|
+
Vector.one.magnitude # => 1.4142135623730951
|
46
|
+
Vector.one.magnitude_sq # => 2.0
|
47
|
+
|
48
|
+
v = Vector.rand
|
49
|
+
v.round(2) # => [-1.0, -0.88]
|
50
|
+
v.round # => [-1, -1]
|
51
|
+
v.floor # => [-1, -1]
|
52
|
+
v.ceil # => [0, 0]
|
53
|
+
v.truncate # => [0, 0]
|
54
|
+
|
55
|
+
Vector.rand # => [-0.5758140223421724, -0.23276547457672714]
|
56
|
+
Vector.rand(3) # => [1, 1]
|
57
|
+
Vector.rand(3..4) # => [4, 4]
|
58
|
+
Vector.rand(3.0..4) # => [3.9402342251571714, 3.550203689124972]
|
59
|
+
Vector.rand(-2.0..2.0) # => [-1.181268220930995, 1.4942116900421252]
|
60
|
+
|
61
|
+
Vector[1, 0].dot_product(Vector[1, 0]) # => 1
|
62
|
+
Vector[1, 0].dot_product(Vector[-1, 0]) # => -1
|
63
|
+
|
64
|
+
Vector.cross_product(Vector.rand, Vector.rand) # => -0.7348374986070235
|
65
|
+
|
66
|
+
Vector.rand.distance_to(Vector.rand) # => 1.7226903872525836
|
67
|
+
|
68
|
+
v = Vector.new
|
69
|
+
v.object_id # => 70298637530620
|
70
|
+
v.replace(Vector.rand) # => [-0.2619785178209082, -0.4396795163426983]
|
71
|
+
v.object_id # => 70298637530620
|
72
|
+
|
73
|
+
Vector.zero.distance_to(Vector.one) # => 1.4142135623730951
|
74
|
+
|
75
|
+
Vector.zero.zero? # => true
|
76
|
+
Vector.one.nonzero? # => true
|
77
|
+
|
78
|
+
Vector.zero.inspect # => "[0.0, 0.0]"
|
79
|
+
Vector.zero.to_s # => "[0.0, 0.0]"
|
80
|
+
|
81
|
+
Vector[1,2].prep # => [-2, 1]
|
82
|
+
#+END_SRC
|
83
|
+
|
84
|
+
** TIPS
|
85
|
+
|
86
|
+
*** 当たり判定を高速化するには?
|
87
|
+
|
88
|
+
当たり判定を次のようにしているとき
|
89
|
+
|
90
|
+
#+BEGIN_SRC ruby
|
91
|
+
if v.magnitude < r
|
92
|
+
end
|
93
|
+
#+END_SRC
|
94
|
+
|
95
|
+
次のようにすると sqrt を省略できる
|
96
|
+
|
97
|
+
#+BEGIN_SRC ruby
|
98
|
+
if v.magnitude_sq < r ** 2
|
99
|
+
end
|
100
|
+
#+END_SRC
|
101
|
+
|
102
|
+
*** a地点からb地点へのベクトルを求めるには?
|
103
|
+
|
104
|
+
#+BEGIN_SRC ruby
|
105
|
+
b - a
|
106
|
+
#+END_SRC
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require "./setup"
|
2
|
+
include Stylet
|
3
|
+
|
4
|
+
Point.new # => [0.0, 0.0]
|
5
|
+
Point.new(1, 2) # => [1, 2]
|
6
|
+
Point[1, 2] # => [1, 2]
|
7
|
+
Point[1, 2].members # => [:x, :y]
|
8
|
+
Point[1, 2].values # => [1, 2]
|
9
|
+
Point[1, 2] # => [1, 2]
|
10
|
+
|
11
|
+
Point[1, 2] == Point[1, 2] # => true
|
12
|
+
Point[1, 2] == Point[3, 4] # => false
|
13
|
+
|
14
|
+
Vector.superclass # => Stylet::Point2
|
15
|
+
|
16
|
+
Point.new(Point[1, 2]) # => [1, 2]
|
17
|
+
Point[Point[1, 2]] # => [1, 2]
|
18
|
+
|
19
|
+
Vector.new # => [0.0, 0.0]
|
20
|
+
Vector.new # => [0.0, 0.0]
|
21
|
+
Vector.new(1, 2) # => [1, 2]
|
22
|
+
|
23
|
+
Vector.zero # => [0.0, 0.0]
|
24
|
+
Vector.one # => [1.0, 1.0]
|
25
|
+
|
26
|
+
a = Vector[1, 2]
|
27
|
+
b = Vector[3, 4]
|
28
|
+
|
29
|
+
a + b # => [4.0, 6.0]
|
30
|
+
a - b # => [-2.0, -2.0]
|
31
|
+
a * 2 # => [2.0, 4.0]
|
32
|
+
a / 2 # => [0.5, 1.0]
|
33
|
+
|
34
|
+
a.add(b) # => [4.0, 6.0]
|
35
|
+
a.sub(b) # => [-2.0, -2.0]
|
36
|
+
a.scale(2) # => [2.0, 4.0]
|
37
|
+
a.mul(2) # => [2.0, 4.0]
|
38
|
+
a.div(2) # => [0.5, 1.0]
|
39
|
+
|
40
|
+
a + [3, 4] # => [4.0, 6.0]
|
41
|
+
a - [3, 4] # => [-2.0, -2.0]
|
42
|
+
|
43
|
+
Vector.one.reverse # => [-1.0, -1.0]
|
44
|
+
-Vector.one # => [-1.0, -1.0]
|
45
|
+
|
46
|
+
Vector[3, 4].normalize # => [0.6, 0.8]
|
47
|
+
|
48
|
+
Vector.one.normalize # => [0.7071067811865475, 0.7071067811865475]
|
49
|
+
Vector.one.magnitude # => 1.4142135623730951
|
50
|
+
Vector.one.magnitude_sq # => 2.0
|
51
|
+
|
52
|
+
v = Vector.rand
|
53
|
+
v.round(2) # => [0.48, 0.6]
|
54
|
+
v.round # => [0, 1]
|
55
|
+
v.floor # => [0, 0]
|
56
|
+
v.ceil # => [1, 1]
|
57
|
+
v.truncate # => [0, 0]
|
58
|
+
|
59
|
+
Vector.rand # => [0.37182089855094613, -0.99052832152511]
|
60
|
+
Vector.rand(3) # => [1, 1]
|
61
|
+
Vector.rand(3..4) # => [4, 4]
|
62
|
+
Vector.rand(3.0..4) # => [3.655724436092496, 3.3356907533135023]
|
63
|
+
Vector.rand(-2.0..2.0) # => [-0.6592404423144278, 1.5211933133588427]
|
64
|
+
|
65
|
+
Vector[1, 0].dot_product(Vector[1, 0]) # => 1
|
66
|
+
Vector[1, 0].dot_product(Vector[-1, 0]) # => -1
|
67
|
+
|
68
|
+
Vector.cross_product(Vector.rand, Vector.rand) # => 0.7501561461552074
|
69
|
+
|
70
|
+
Vector.rand.distance_to(Vector.rand) # => 0.7463133439748652
|
71
|
+
|
72
|
+
v = Vector.new
|
73
|
+
v.object_id # => 70119757980160
|
74
|
+
v.replace(Vector.rand) # => [0.29626268422451485, 0.8924141847532647]
|
75
|
+
v.object_id # => 70119757980160
|
76
|
+
|
77
|
+
Vector.zero.distance_to(Vector.one) # => 1.4142135623730951
|
78
|
+
|
79
|
+
Vector.zero.zero? # => true
|
80
|
+
Vector.one.nonzero? # => true
|
81
|
+
|
82
|
+
Vector.zero.inspect # => "[0.0, 0.0]"
|
83
|
+
Vector.zero.to_s # => "[0.0, 0.0]"
|
84
|
+
|
85
|
+
Vector[1, 2].prep # => [-2, 1]
|
data/examples/setup.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Stylet
|
2
|
+
module Chore
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def clamp(v, range = (0.0..1.0))
|
6
|
+
v.clamp(range.min, range.max)
|
7
|
+
end
|
8
|
+
|
9
|
+
def max_clamp(v, r = 1.0)
|
10
|
+
[v, r].min
|
11
|
+
end
|
12
|
+
|
13
|
+
def min_clamp(v, r = 0.0)
|
14
|
+
[v, r].max
|
15
|
+
end
|
16
|
+
|
17
|
+
def abs_clamp(v, r = 1.0)
|
18
|
+
clamp(v, (-r..r))
|
19
|
+
end
|
20
|
+
|
21
|
+
# b から a へ行きたいときの最短の角度差
|
22
|
+
def shortest_angular_difference(a, b)
|
23
|
+
d = a.modulo(1.0) - b.modulo(1.0)
|
24
|
+
if d < -1.0 / 2
|
25
|
+
d = 1.0 + d
|
26
|
+
elsif d > 1.0 / 2
|
27
|
+
d = -1.0 + d
|
28
|
+
end
|
29
|
+
d
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if $0 == __FILE__
|
35
|
+
Stylet::Chore.clamp(0.5) # => 0.5
|
36
|
+
Stylet::Chore.max_clamp(1.5) # => 1.0
|
37
|
+
Stylet::Chore.min_clamp(-0.5) # => 0.0
|
38
|
+
Stylet::Chore.shortest_angular_difference(0.2, 0.5) # => -0.3
|
39
|
+
Stylet::Chore.shortest_angular_difference(0.8, 1.2) # => -0.3999999999999999
|
40
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Stylet
|
4
|
+
ONE = 4096 # sin cos の精度
|
5
|
+
AROUND = 4096 * 2 # 4096の場合、64分割以上のときにズレが生じる。8092なら256分割でズレるようだ
|
6
|
+
|
7
|
+
class Magic
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@dir_offset = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
prepend Module.new {
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
@sin_table = AROUND.times.collect { |i| (Math.sin(2 * Math::PI * i / AROUND) * ONE).round }
|
18
|
+
end
|
19
|
+
|
20
|
+
def _rsin(a)
|
21
|
+
@sin_table[a.modulo(@sin_table.size)]
|
22
|
+
end
|
23
|
+
|
24
|
+
def _rcos(a)
|
25
|
+
_rsin(a + @sin_table.size / 4)
|
26
|
+
end
|
27
|
+
|
28
|
+
def rsin(a)
|
29
|
+
a = a.modulo(1.0) # ruby の場合、一周せずに a * @sin_table.size で無限に桁がでかくなる、のを防ぐため
|
30
|
+
@sin_table[(a * @sin_table.size).modulo(@sin_table.size)] * 1.0 / ONE
|
31
|
+
end
|
32
|
+
|
33
|
+
def rcos(a)
|
34
|
+
rsin(a + 1.0 / 4)
|
35
|
+
end
|
36
|
+
}
|
37
|
+
|
38
|
+
prepend Module.new {
|
39
|
+
AtanAreaInfo = Struct.new(:base_dir, :sign)
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
super
|
43
|
+
part = AROUND / 8
|
44
|
+
@atan_table = (0..part).collect {|i|
|
45
|
+
(Math.atan2(i, part) * part / (2 * Math::PI / 8)).round
|
46
|
+
}
|
47
|
+
@atan_area_info_table = [
|
48
|
+
AtanAreaInfo.new(AROUND / 4 * 3 + @dir_offset, +1),
|
49
|
+
AtanAreaInfo.new(AROUND / 4 * 4 + @dir_offset, -1),
|
50
|
+
AtanAreaInfo.new(AROUND / 4 * 1 + @dir_offset, -1),
|
51
|
+
AtanAreaInfo.new(AROUND / 4 * 0 + @dir_offset, +1),
|
52
|
+
AtanAreaInfo.new(AROUND / 4 * 3 + @dir_offset, -1),
|
53
|
+
AtanAreaInfo.new(AROUND / 4 * 2 + @dir_offset, +1),
|
54
|
+
AtanAreaInfo.new(AROUND / 4 * 1 + @dir_offset, +1),
|
55
|
+
AtanAreaInfo.new(AROUND / 4 * 2 + @dir_offset, -1),
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
def angle(ox, oy, tx, ty)
|
60
|
+
iangle(ox, oy, tx, ty).to_f / AROUND
|
61
|
+
# v % 1.0 # 一周したとき 1.0 にならないようにするため
|
62
|
+
end
|
63
|
+
|
64
|
+
# 4 0
|
65
|
+
# 5 1
|
66
|
+
# 7 3
|
67
|
+
# 6 2
|
68
|
+
def iangle(ox, oy, tx, ty)
|
69
|
+
dir = nil
|
70
|
+
|
71
|
+
sx = tx - ox
|
72
|
+
sy = ty - oy
|
73
|
+
|
74
|
+
dx = sx
|
75
|
+
dy = sy
|
76
|
+
|
77
|
+
dx = dx.abs
|
78
|
+
dy = dy.abs
|
79
|
+
if sx.zero? && sy.zero?
|
80
|
+
dir = 0
|
81
|
+
else
|
82
|
+
if sx >= 0
|
83
|
+
if sy <= 0
|
84
|
+
# p [sx, sy]
|
85
|
+
# p [dx, dy]
|
86
|
+
if dx < dy
|
87
|
+
# p :koko
|
88
|
+
dir = local_dir(dx, dy, 0)
|
89
|
+
else
|
90
|
+
dir = local_dir(dy, dx, 1)
|
91
|
+
# p dir
|
92
|
+
end
|
93
|
+
else
|
94
|
+
if dx < dy
|
95
|
+
dir = local_dir(dx, dy, 2)
|
96
|
+
else
|
97
|
+
dir = local_dir(dy, dx, 3)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
else
|
101
|
+
if sy <= 0
|
102
|
+
if dx < dy
|
103
|
+
dir = local_dir(dx, dy, 4)
|
104
|
+
else
|
105
|
+
dir = local_dir(dy, dx, 5)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
if dx < dy
|
109
|
+
dir = local_dir(dx, dy, 6)
|
110
|
+
else
|
111
|
+
dir = local_dir(dy, dx, 7)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# dir.modulo(AROUND)
|
117
|
+
dir
|
118
|
+
end
|
119
|
+
|
120
|
+
def local_dir(value, div_value, area_no)
|
121
|
+
# p [value, div_value, area_no]
|
122
|
+
unless value <= div_value
|
123
|
+
raise ArgumentError, "#{value} <= #{div_value}"
|
124
|
+
end
|
125
|
+
ip = @atan_area_info_table[area_no]
|
126
|
+
dir = ip.base_dir
|
127
|
+
if div_value.nonzero?
|
128
|
+
if value != div_value
|
129
|
+
index = (value.to_f * AROUND / 8 / div_value).round
|
130
|
+
raise unless index < @atan_table.size
|
131
|
+
dirsub = ip.sign * @atan_table[index]
|
132
|
+
dir += dirsub
|
133
|
+
else
|
134
|
+
dir += ip.sign * AROUND / 8
|
135
|
+
end
|
136
|
+
end
|
137
|
+
# dir.modulo(AROUND) の場合は、振り子の左右の移動量が均等にならない
|
138
|
+
# dir.round.modulo(AROUND)
|
139
|
+
dir.modulo(AROUND)
|
140
|
+
end
|
141
|
+
}
|
142
|
+
|
143
|
+
class << self
|
144
|
+
[:_rsin, :_rcos, :iangle, :rsin, :rcos, :angle].each do |method|
|
145
|
+
define_method(method) do |*args, &block|
|
146
|
+
instance.send(method, *args, &block)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def one
|
151
|
+
ONE
|
152
|
+
end
|
153
|
+
|
154
|
+
def one_round
|
155
|
+
AROUND
|
156
|
+
end
|
157
|
+
|
158
|
+
# 一周をアナログ時計の単位と考えたときの角度(抽象化のため)
|
159
|
+
def clock(hour = 6, minute = 0)
|
160
|
+
t = -(1.0 / 4) + (1.0 * (hour % 12) / 12)
|
161
|
+
t += minute.fdiv(60 * 12)
|
162
|
+
t.modulo(1.0) / 1.0
|
163
|
+
end
|
164
|
+
|
165
|
+
# 一周を360度と考えたときの角度(抽象化のため)
|
166
|
+
def r0; 0; end
|
167
|
+
def r45; 1.0 / 8.0; end
|
168
|
+
def r90; 1.0 / 4.0; end
|
169
|
+
def r180; 1.0 / 2.0; end
|
170
|
+
def r270; r90 * 3; end
|
171
|
+
|
172
|
+
def degree(r)
|
173
|
+
r / 360.0
|
174
|
+
end
|
175
|
+
|
176
|
+
# 円の右側か?
|
177
|
+
def cright?(v)
|
178
|
+
!cleft?(v)
|
179
|
+
end
|
180
|
+
|
181
|
+
# 円の左側か?
|
182
|
+
def cleft?(v)
|
183
|
+
(r90...r270).include?(v % 1.0)
|
184
|
+
end
|
185
|
+
|
186
|
+
# from から to への差分
|
187
|
+
def angle_diff(from: nil, to: nil)
|
188
|
+
v = to.modulo(1.0) - from.modulo(1.0)
|
189
|
+
if v < -1.0 / 2
|
190
|
+
1.0 + v
|
191
|
+
elsif v > 1.0 / 2
|
192
|
+
-1.0 + v
|
193
|
+
else
|
194
|
+
v
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
if $0 == __FILE__
|
202
|
+
require "pp"
|
203
|
+
# p Stylet::Magic.rcos(0)
|
204
|
+
# exit
|
205
|
+
|
206
|
+
# p Stylet::Magic._rsin(0)
|
207
|
+
# p Stylet::Magic._rcos(0)
|
208
|
+
# p Stylet::Magic.iangle(0, 0, 0, 1)
|
209
|
+
# p Stylet::Magic.rsin(0)
|
210
|
+
# p Stylet::Magic.rcos(0)
|
211
|
+
# p Stylet::Magic.iangle(320.0, 240.0, 447.990361835411, 240.429243)
|
212
|
+
|
213
|
+
# n = Stylet::AROUND
|
214
|
+
# pp (0..(n*2)).collect{|i|
|
215
|
+
# if i == 4 || true
|
216
|
+
# # r = (Stylet::Magic.one_round / n * i) % Stylet::Magic.one_round
|
217
|
+
# r = (Stylet::Magic.one_round / n * i)
|
218
|
+
# x = Stylet::Magic._rcos(r)
|
219
|
+
# y = Stylet::Magic._rsin(r)
|
220
|
+
# dir = Stylet::Magic.iangle(0, 0, x, y)
|
221
|
+
# [i, [x, y], r, dir, (r == dir)]
|
222
|
+
# end
|
223
|
+
# }.compact
|
224
|
+
|
225
|
+
# n = 32
|
226
|
+
# (0..n).collect{|i|
|
227
|
+
# if true
|
228
|
+
# r = ((Stylet::Magic.one_round.to_f * i / n) % Stylet::Magic.one_round)
|
229
|
+
# x = Stylet::Magic._rcos(r)
|
230
|
+
# y = Stylet::Magic._rsin(r)
|
231
|
+
# dir = Stylet::Magic.iangle(0, 0, x, y)
|
232
|
+
# p [x, y, r, dir, (r == dir)]
|
233
|
+
# r == dir
|
234
|
+
# end
|
235
|
+
# }
|
236
|
+
|
237
|
+
# pp (0..8).collect{|i|
|
238
|
+
# r = 1.0 / 8 * i % 1.0
|
239
|
+
# x = Stylet::Magic.rcos(r)
|
240
|
+
# y = Stylet::Magic.rsin(r)
|
241
|
+
# dir = Stylet::Magic.angle(0, 0, x, y)
|
242
|
+
# [i, r, dir, (r == dir)]
|
243
|
+
# }
|
244
|
+
# pp 8.times.collect{|i|[i, Stylet::Magic.rsin(1.0 / 8 * i)]}
|
245
|
+
# pp (0..12).collect{|i|[i, Stylet::Magic.clock(i)]}
|
246
|
+
end
|
@@ -0,0 +1,439 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module Stylet
|
4
|
+
module Shared
|
5
|
+
# Point.new # => [nil, nil]
|
6
|
+
# Point[1, 2] # => [1, 2]
|
7
|
+
# Point[1, 2].members # => [:x, :y]
|
8
|
+
# Point[1, 2].values # => [1, 2]
|
9
|
+
# Point[1, 2] # => [1, 2]
|
10
|
+
# Point.new(Point[1,2]) # => [1, 2]
|
11
|
+
# Point[Point[1,2]] # => [1, 2]
|
12
|
+
# Vector.new.to_h # => {:x=>0.0, :y=>0.0}
|
13
|
+
# Vector.new(1, 2).to_h # => {:x=>1, :y=>2}
|
14
|
+
def initialize(*args)
|
15
|
+
if args.empty?
|
16
|
+
args = [0.0] * members.size
|
17
|
+
elsif args.size == 1 && args.first.respond_to?(:values)
|
18
|
+
args = args.first.values
|
19
|
+
end
|
20
|
+
super(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Vector.zero.inspect # => "[0.0, 0.0]"
|
24
|
+
def inspect
|
25
|
+
values.inspect
|
26
|
+
end
|
27
|
+
|
28
|
+
# Vector.zero.to_s # => "[0.0, 0.0]"
|
29
|
+
def to_s
|
30
|
+
values.inspect
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Point2 < Struct.new(:x, :y)
|
35
|
+
include Shared
|
36
|
+
end
|
37
|
+
|
38
|
+
class Point3 < Struct.new(:x, :y, :z)
|
39
|
+
include Shared
|
40
|
+
end
|
41
|
+
|
42
|
+
Point = Point2
|
43
|
+
|
44
|
+
class ZeroVectorError < StandardError; end
|
45
|
+
|
46
|
+
module BasicVector
|
47
|
+
extend ActiveSupport::Concern
|
48
|
+
|
49
|
+
included do
|
50
|
+
end
|
51
|
+
|
52
|
+
class_methods do
|
53
|
+
# ゼロベクトルを返す
|
54
|
+
# Vector.zero.to_a # => [0.0, 0.0]
|
55
|
+
def zero
|
56
|
+
new(*Array.new(members.size) { 0.0 })
|
57
|
+
end
|
58
|
+
|
59
|
+
# メンバーが 1.0 のベクトルを返す
|
60
|
+
# Vector.one.to_a # => [1.0, 1.0]
|
61
|
+
def one
|
62
|
+
new(*Array.new(members.size) { 1.0 })
|
63
|
+
end
|
64
|
+
|
65
|
+
# ランダムを返す
|
66
|
+
# Vector.rand # => [0.017480344343668852, 0.8764385584061907]
|
67
|
+
# Vector.rand(3) # => [2, 2]
|
68
|
+
# Vector.rand(3..4) # => [4, 3]
|
69
|
+
# Vector.rand(3.0..4) # => [3.969678485069191, 3.4220406563055144]
|
70
|
+
# Vector.rand(-2.0..2.0) # => [-1.6587999052626938, 0.5996185615991392]
|
71
|
+
def rand(*args)
|
72
|
+
if args.empty?
|
73
|
+
args = [-1.0..1.0]
|
74
|
+
end
|
75
|
+
new(*members.collect { Kernel.rand(*args) })
|
76
|
+
end
|
77
|
+
|
78
|
+
# ゼロベクトルを作ろうとすると例外を出す new
|
79
|
+
def safe_new(*args)
|
80
|
+
new(*args).tap do |obj|
|
81
|
+
raise ZeroVectorError if obj.zero?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# # 1.0 と -1.0 で構成したランダム値
|
86
|
+
# def nonzero_random_new
|
87
|
+
# new(*members.collect{Chore.nonzero_random})
|
88
|
+
# end
|
89
|
+
|
90
|
+
# 内積
|
91
|
+
def dot_product(a, b)
|
92
|
+
members.collect { |m| a.send(m) * b.send(m) }.reduce(0, :+)
|
93
|
+
end
|
94
|
+
|
95
|
+
# # 外積
|
96
|
+
# def cross_product(a, b)
|
97
|
+
# members.collect {|m|
|
98
|
+
# (members - [m]).collect {|n| a.send(m) * b.send(n) }
|
99
|
+
# }.flatten.reduce(0, :+)
|
100
|
+
# end
|
101
|
+
end
|
102
|
+
|
103
|
+
[
|
104
|
+
{ :name => :add, :sym => :+ },
|
105
|
+
{ :name => :sub, :sym => :- },
|
106
|
+
].each do |attr|
|
107
|
+
define_method(attr[:name]) do |o|
|
108
|
+
unless o.is_a? self.class
|
109
|
+
o = self.class.new(*o.to_ary)
|
110
|
+
end
|
111
|
+
self.class.new(*members.collect { |m| Float(send(m)).send(attr[:sym], o.send(m)) })
|
112
|
+
end
|
113
|
+
define_method("#{attr[:name]}!") do |*args|
|
114
|
+
replace(send(attr[:name], *args))
|
115
|
+
end
|
116
|
+
alias_method attr[:sym], attr[:name]
|
117
|
+
end
|
118
|
+
|
119
|
+
[
|
120
|
+
{ :name => :scale, :sym => :* },
|
121
|
+
{ :name => :div, :sym => :/ },
|
122
|
+
].each do |attr|
|
123
|
+
define_method(attr[:name]) { |*args| apply(attr[:sym], *args) }
|
124
|
+
define_method("#{attr[:name]}!") { |*args| apply!(attr[:sym], *args) }
|
125
|
+
alias_method attr[:sym], attr[:name]
|
126
|
+
end
|
127
|
+
|
128
|
+
alias mul scale
|
129
|
+
alias mul! scale!
|
130
|
+
|
131
|
+
# Vector.rand.round.to_a # => [1, 0]
|
132
|
+
# Vector.rand.round(2).to_a # => [0.37, 0.92]
|
133
|
+
# Vector.rand.floor.to_a # => [0, 0]
|
134
|
+
# Vector.rand.ceil.to_a # => [1, 1]
|
135
|
+
# Vector.rand.truncate.to_a # => [0, 0]
|
136
|
+
[:ceil, :floor, :round, :truncate].each do |name|
|
137
|
+
define_method(name) { |*args| apply(name, *args) }
|
138
|
+
define_method("#{name}!") { |*args| apply!(name, *args) }
|
139
|
+
end
|
140
|
+
|
141
|
+
# メンバーだけ更新(主に内部用)
|
142
|
+
#
|
143
|
+
# v = Vector.new
|
144
|
+
# v.object_id # => 70228805905160
|
145
|
+
# v.replace(Vector.rand) # => [-0.5190386805455354, -0.5679474000175717]
|
146
|
+
# v.object_id # => 70228805905160
|
147
|
+
#
|
148
|
+
def replace(other)
|
149
|
+
tap { members.each { |m| send("#{m}=", other.send(m)) } }
|
150
|
+
end
|
151
|
+
|
152
|
+
# 単位ベクトル化
|
153
|
+
# Vector.one.normalize # => [0.7071067811865475, 0.7071067811865475]
|
154
|
+
def normalize
|
155
|
+
raise ZeroVectorError if zero?
|
156
|
+
c = magnitude
|
157
|
+
self.class.safe_new(*values.collect { |v| Float(v) / c })
|
158
|
+
end
|
159
|
+
|
160
|
+
def normalize!
|
161
|
+
replace(normalize)
|
162
|
+
end
|
163
|
+
|
164
|
+
# ベクトルの大きさを返す
|
165
|
+
# Vector.one.magnitude # => 1.4142135623730951
|
166
|
+
def magnitude
|
167
|
+
Math.sqrt(magnitude_sq)
|
168
|
+
rescue Errno::EDOM => error
|
169
|
+
warn "(p - p).magnitude と書いているコードがあるはず。それをやると sqrt(0) になるので if p != p を入れよう: #{values.inspect}"
|
170
|
+
raise error
|
171
|
+
end
|
172
|
+
|
173
|
+
def magnitude_sq
|
174
|
+
values.collect { |v| v**2 }.inject(0, &:+)
|
175
|
+
end
|
176
|
+
|
177
|
+
alias length magnitude
|
178
|
+
alias length_sq magnitude_sq
|
179
|
+
|
180
|
+
# 反対方向のベクトルを返す
|
181
|
+
# Vector.one.reverse # => [-1.0, -1.0]
|
182
|
+
def reverse
|
183
|
+
self.class.new(*values.collect(&:-@))
|
184
|
+
end
|
185
|
+
|
186
|
+
# -Vector.one # => [-1.0, -1.0]
|
187
|
+
alias -@ reverse
|
188
|
+
|
189
|
+
# 内積
|
190
|
+
# Vector[1, 0].dot_product(Vector[1, 0]) # => 1.0
|
191
|
+
# Vector[1, 0].dot_product(Vector[-1, 0]) # => -1.0
|
192
|
+
def dot_product(other)
|
193
|
+
self.class.send(__method__, self, other)
|
194
|
+
end
|
195
|
+
|
196
|
+
# 相手との距離を取得
|
197
|
+
# Vector.zero.distance_to(Vector.one) # => 1.4142135623730951
|
198
|
+
def distance_to(target)
|
199
|
+
(target - self).magnitude
|
200
|
+
end
|
201
|
+
|
202
|
+
# 0ベクトルか?
|
203
|
+
# Vector.zero.zero? # => true
|
204
|
+
def zero?
|
205
|
+
values.all?(&:zero?)
|
206
|
+
end
|
207
|
+
|
208
|
+
# 0ベクトルではない?
|
209
|
+
# Vector.one.nonzero? # => true
|
210
|
+
def nonzero?
|
211
|
+
!zero?
|
212
|
+
end
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
def apply(method, *args)
|
217
|
+
self.class.new(*apply_values(method, *args))
|
218
|
+
end
|
219
|
+
|
220
|
+
def apply!(method, *args)
|
221
|
+
replace(apply(method, *args))
|
222
|
+
end
|
223
|
+
|
224
|
+
def apply_values(method, *args)
|
225
|
+
members.collect { |m| Float(send(m)).send(method, *args) }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# ベクトル
|
231
|
+
#
|
232
|
+
# 移動制限のつけ方
|
233
|
+
#
|
234
|
+
# if vec.magnitude > n
|
235
|
+
# vec.normalize.scale(n)
|
236
|
+
# end
|
237
|
+
#
|
238
|
+
# 摩擦
|
239
|
+
#
|
240
|
+
# vec.scale(0.9)
|
241
|
+
#
|
242
|
+
class Vector < Point
|
243
|
+
include BasicVector
|
244
|
+
|
245
|
+
# 外積
|
246
|
+
# x1*y2-x2*y1 = |v1||v2|sin(θ)
|
247
|
+
def self.cross_product(a, b)
|
248
|
+
a.x * b.y - b.x * a.y
|
249
|
+
end
|
250
|
+
|
251
|
+
def cross_product(b)
|
252
|
+
self.class.cross_product(self, b)
|
253
|
+
end
|
254
|
+
|
255
|
+
# 方向ベクトル
|
256
|
+
#
|
257
|
+
# これを使うと次のように簡単に書ける
|
258
|
+
# cursor.x += Stylet::Magic.rcos(dir) * speed
|
259
|
+
# cursor.y += Stylet::Magic.rsin(dir) * speed
|
260
|
+
# ↓
|
261
|
+
# cursor += Stylet::Magic.angle_at(dir) * speed
|
262
|
+
#
|
263
|
+
def self.angle_at(x, y = x)
|
264
|
+
new(Magic.rcos(x), Magic.rsin(y))
|
265
|
+
end
|
266
|
+
|
267
|
+
# 反射ベクトルの取得
|
268
|
+
#
|
269
|
+
# s: スピードベクトル
|
270
|
+
# n: 法線ベクトル
|
271
|
+
#
|
272
|
+
# 定石
|
273
|
+
# 速度ベクトル += 速度ベクトル.reflect(法線ベクトル).scale(反射係数)
|
274
|
+
# @speed += @speed.reflect(@normal).scale(0.5)
|
275
|
+
#
|
276
|
+
# 反射係数
|
277
|
+
# ×1.5: 謎の力によってありえない反射をする
|
278
|
+
# ◎1.0: 摩擦なし(標準)
|
279
|
+
# ◎0.8: 少し滑る
|
280
|
+
# ○0.6: かなり滑ってほんの少しだけ反射する
|
281
|
+
# ○0.5: 線に沿って滑る
|
282
|
+
# ×0.4: 線にわずかにめり込んだまま滑る
|
283
|
+
# ○0.0: 線に沿って滑る(突き抜けるかと思ったけど滑る)
|
284
|
+
#
|
285
|
+
# hakuhin.jp/as/collide.html#COLLIDE_02 の方法だと x + t * n.x * 2.0 * 0.8 と一気に書いていたけど
|
286
|
+
# メソッド化するときには分解した方がわかりやすそうなのでこうした。
|
287
|
+
#
|
288
|
+
# 参考: 反射ベクトルと壁ずりベクトル
|
289
|
+
# http://marupeke296.com/COL_Basic_No5_WallVector.html
|
290
|
+
#
|
291
|
+
def reflect(n)
|
292
|
+
slide(n) * 2.0
|
293
|
+
end
|
294
|
+
|
295
|
+
# スライドベクトル
|
296
|
+
#
|
297
|
+
# self: スピードベクトル
|
298
|
+
# n: 法線ベクトル
|
299
|
+
#
|
300
|
+
def slide(n)
|
301
|
+
t = -(n.x * x + n.y * y) / (n.x**2 + n.y**2)
|
302
|
+
n * t
|
303
|
+
end
|
304
|
+
|
305
|
+
# p: 現在地点
|
306
|
+
# s: スピードベクトル
|
307
|
+
# a: 線の片方
|
308
|
+
# n: 法線ベクトル
|
309
|
+
#
|
310
|
+
# としたとき何倍したら線にぶつかるか
|
311
|
+
#
|
312
|
+
# 式の意味がまだ理解できてない
|
313
|
+
#
|
314
|
+
def self.collision_power_scale(p, s, a, n)
|
315
|
+
d = -(a.x * n.x + a.y * n.y)
|
316
|
+
-(n.x * p.x + n.y * p.y + d) / (n.x * s.x + n.y * s.y)
|
317
|
+
end
|
318
|
+
|
319
|
+
# 法線ベクトルの取得(方法1)
|
320
|
+
# どう見ても遅い
|
321
|
+
def slowly_normal(t)
|
322
|
+
a = angle(t) + Magic.r90
|
323
|
+
self.class.new(Magic.rcos(a), Magic.rsin(a))
|
324
|
+
end
|
325
|
+
|
326
|
+
# 法線ベクトルの取得(方法2)
|
327
|
+
def normal(t)
|
328
|
+
dx = t.x - x
|
329
|
+
dy = t.y - y
|
330
|
+
self.class.new(-dy, dx)
|
331
|
+
end
|
332
|
+
|
333
|
+
# 法線ベクトルの取得
|
334
|
+
def prep
|
335
|
+
self.class.new(-y, x)
|
336
|
+
end
|
337
|
+
|
338
|
+
#
|
339
|
+
# 相手の方向を取得
|
340
|
+
#
|
341
|
+
def angle_to(target)
|
342
|
+
(target - self).angle
|
343
|
+
end
|
344
|
+
|
345
|
+
# ベクトルから角度に変換
|
346
|
+
#
|
347
|
+
# Math.atan2(y, x) * 180 / Math.PI に相当する
|
348
|
+
#
|
349
|
+
def angle
|
350
|
+
Magic.angle(0, 0, x, y)
|
351
|
+
end
|
352
|
+
|
353
|
+
# 線分 A B の距離 1.0 をしたとき途中の位置ベクトルを取得
|
354
|
+
#
|
355
|
+
# a と b の位置が同じ場合いろいろおかしくなる
|
356
|
+
#
|
357
|
+
def self.pos_vector_ratio(a, b, rate)
|
358
|
+
a + angle_at(a.angle_to(b)) * (a.distance_to(b) * rate) # FIXME: ダメなコード
|
359
|
+
end
|
360
|
+
|
361
|
+
# 指定の角度だけ回転する
|
362
|
+
#
|
363
|
+
# 自分で最初に考えた方法
|
364
|
+
#
|
365
|
+
def rotate(a)
|
366
|
+
self.class.angle_at(angle + a) * magnitude
|
367
|
+
end
|
368
|
+
|
369
|
+
def rotate!(*args)
|
370
|
+
replace(rotate(*args))
|
371
|
+
end
|
372
|
+
|
373
|
+
# 指定の角度だけ回転する(方法2)
|
374
|
+
# 他のいくつかのライブラリで使われている方法。
|
375
|
+
def rotate2(a)
|
376
|
+
tx = (x * Magic.rcos(a)) - (y * Magic.rsin(a))
|
377
|
+
ty = (x * Magic.rsin(a)) + (y * Magic.rcos(a))
|
378
|
+
self.class.new(tx, ty)
|
379
|
+
end
|
380
|
+
|
381
|
+
def rotate2!(*args)
|
382
|
+
replace(rotate2(*args))
|
383
|
+
end
|
384
|
+
|
385
|
+
def to_2dv
|
386
|
+
self
|
387
|
+
end
|
388
|
+
|
389
|
+
def to_3dv
|
390
|
+
Vector3.new(*values, 0)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
class Vector3 < Point3
|
395
|
+
include BasicVector
|
396
|
+
|
397
|
+
def to_2dv
|
398
|
+
Vector.new(*values.take(2))
|
399
|
+
end
|
400
|
+
|
401
|
+
def to_3dv
|
402
|
+
self
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
if $0 == __FILE__
|
408
|
+
# Stylet::Vector.rand * 2
|
409
|
+
|
410
|
+
p0 = Stylet::Vector.new(1, 1)
|
411
|
+
p1 = Stylet::Vector.new(1, 1)
|
412
|
+
p(p0 + p1)
|
413
|
+
p(p0.add(p1))
|
414
|
+
p(p0.add!(p1))
|
415
|
+
p(p0)
|
416
|
+
p(Stylet::Vector.new(3, 4).magnitude)
|
417
|
+
p(Stylet::Vector.new(3, 4).normalize.scale(5))
|
418
|
+
p(-Stylet::Vector.new(3, 4))
|
419
|
+
|
420
|
+
# p0 = Stylet::Vector.new(1, 1)
|
421
|
+
# p1 = Stylet::Vector.new(1, 1)
|
422
|
+
# p(p0 == p1)
|
423
|
+
# p p0
|
424
|
+
# p p0 + p1
|
425
|
+
# p p0.add(p1)
|
426
|
+
# p p0
|
427
|
+
# p p0.add!(p1)
|
428
|
+
# p p0
|
429
|
+
#
|
430
|
+
# p0 = Stylet::Vector.zero
|
431
|
+
# p p0.normalize
|
432
|
+
end
|
433
|
+
# >> [2.0, 2.0]
|
434
|
+
# >> [2.0, 2.0]
|
435
|
+
# >> [2.0, 2.0]
|
436
|
+
# >> [2.0, 2.0]
|
437
|
+
# >> 5.0
|
438
|
+
# >> [3.0, 4.0]
|
439
|
+
# >> [-3, -4]
|
@@ -0,0 +1,20 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "stylet_support/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "stylet_support"
|
6
|
+
s.version = StyletSupport::VERSION
|
7
|
+
s.summary = "Vector library"
|
8
|
+
s.description = "Vector library"
|
9
|
+
s.author = "akicho8"
|
10
|
+
s.homepage = "http://github.com/akicho8/stylet_support"
|
11
|
+
s.email = "akicho8@gmail.com"
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {s,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_dependency "activesupport"
|
19
|
+
s.add_development_dependency "test-unit"
|
20
|
+
end
|
data/test/test_etc.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestEtc < Test::Unit::TestCase
|
4
|
+
test "all" do
|
5
|
+
assert { Stylet::Chore.clamp(0.5) == 0.5 }
|
6
|
+
|
7
|
+
assert { Stylet::Chore.clamp(0.5) == 0.5 }
|
8
|
+
assert { Stylet::Chore.max_clamp(1.5) == 1.0 }
|
9
|
+
assert { Stylet::Chore.min_clamp(-0.5) == 0.0 }
|
10
|
+
assert { Stylet::Chore.abs_clamp(-1.5) == -1.0 }
|
11
|
+
|
12
|
+
assert { Stylet::Chore.shortest_angular_difference(0.2, 0.8).round(3) == 0.4 }
|
13
|
+
assert { Stylet::Chore.shortest_angular_difference(0.3, 0.8).round(3) == -0.5 }
|
14
|
+
assert { Stylet::Chore.shortest_angular_difference(0.4, 0.8).round(3) == -0.4 }
|
15
|
+
end
|
16
|
+
end
|
data/test/test_helper.rb
ADDED
data/test/test_magic.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestMagic < Test::Unit::TestCase
|
4
|
+
sub_test_case "整数系" do
|
5
|
+
test "sin/cos" do
|
6
|
+
assert_equal 0, Magic._rsin(0)
|
7
|
+
assert_equal Magic.one, Magic._rcos(0)
|
8
|
+
end
|
9
|
+
|
10
|
+
test "sinとcosとatan2の整合性確認" do
|
11
|
+
n = 64
|
12
|
+
r = (0..n).collect {|i|
|
13
|
+
r = (Magic.one_round * i / n) % Magic.one_round
|
14
|
+
x = Magic._rcos(r)
|
15
|
+
y = Magic._rsin(r)
|
16
|
+
dir = Magic.iangle(0, 0, x, y)
|
17
|
+
r == dir
|
18
|
+
}.all?
|
19
|
+
|
20
|
+
assert { r }
|
21
|
+
end
|
22
|
+
|
23
|
+
test "二点間の角度を求める" do
|
24
|
+
assert_equal Magic.one_round / 4, Magic.iangle(0, 0, 0, 1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
sub_test_case "一周も角度も小数で表す系" do
|
29
|
+
test "sin/cos" do
|
30
|
+
assert { Magic.rsin(0) == 0.0 }
|
31
|
+
assert { Magic.rcos(0) == 1.0 }
|
32
|
+
end
|
33
|
+
|
34
|
+
test "sinとcosとatan2の整合性確認" do
|
35
|
+
n = 32
|
36
|
+
r = (0..n).collect {|i|
|
37
|
+
r = 1.0 * i / n % 1.0
|
38
|
+
x = Magic.rcos(r)
|
39
|
+
y = Magic.rsin(r)
|
40
|
+
dir = Magic.angle(0, 0, x, y)
|
41
|
+
r == dir
|
42
|
+
}.all?
|
43
|
+
assert { r }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
test "時計の時で方向指定" do
|
48
|
+
assert { Magic.clock(0) == 0.75 }
|
49
|
+
assert { Magic.clock(3) == 0.0 }
|
50
|
+
assert { Magic.clock(6) == 0.25 }
|
51
|
+
assert { Magic.clock(9) == 0.5 }
|
52
|
+
assert { Magic.clock(12) == 0.75 }
|
53
|
+
end
|
54
|
+
|
55
|
+
test "方向を抽象化" do
|
56
|
+
assert { Magic.r0 == Magic.clock(3) }
|
57
|
+
assert { Magic.r45 * 10000 == (Magic.clock(4, 30) * 10000).round }
|
58
|
+
assert { Magic.r90 == Magic.clock(6) }
|
59
|
+
assert { Magic.r180 == Magic.clock(9) }
|
60
|
+
end
|
61
|
+
|
62
|
+
test "左右どちらにいるか?" do
|
63
|
+
assert { Magic.cright?(Magic.clock(3)) }
|
64
|
+
assert { Magic.cleft?(Magic.clock(9)) }
|
65
|
+
end
|
66
|
+
end
|
data/test/test_vector.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestVector < Test::Unit::TestCase
|
4
|
+
setup do
|
5
|
+
@p0 = Vector.new(10, 20)
|
6
|
+
@p1 = Vector.new(100, 200)
|
7
|
+
@obj = Vector.new(3, 4)
|
8
|
+
end
|
9
|
+
|
10
|
+
test "ランダム" do
|
11
|
+
Vector.rand
|
12
|
+
end
|
13
|
+
|
14
|
+
sub_test_case "加減演算" do
|
15
|
+
test "加減算" do
|
16
|
+
assert { (@obj + @obj) == Vector.new(6, 8) }
|
17
|
+
assert { (@obj - @obj) == Vector.zero }
|
18
|
+
end
|
19
|
+
|
20
|
+
test "右辺は配列でも可" do
|
21
|
+
assert { (@obj + [3, 4]) == Vector.new(6, 8) }
|
22
|
+
assert { (@obj - [3, 4]) == Vector.zero }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
test "スケーリング" do
|
27
|
+
assert { @obj.scale(2) == Vector.new(6, 8) }
|
28
|
+
assert { (@obj * 2) == Vector.new(6, 8) }
|
29
|
+
assert { (@obj * 0.5) == Vector.new(1.5, 2.0) }
|
30
|
+
end
|
31
|
+
|
32
|
+
test "正規化" do
|
33
|
+
assert { Vector.new(3, 4).normalize == Vector.new(0.6, 0.8) }
|
34
|
+
end
|
35
|
+
|
36
|
+
sub_test_case "破壊的メソッド" do
|
37
|
+
test "add!" do
|
38
|
+
@p0.add!(@p1)
|
39
|
+
assert { @p0 == Vector.new(110, 220) }
|
40
|
+
end
|
41
|
+
|
42
|
+
test "sub!" do
|
43
|
+
@p0.sub!(@p1)
|
44
|
+
assert { @p0 == Vector.new(-90, -180) }
|
45
|
+
end
|
46
|
+
|
47
|
+
test "scale!" do
|
48
|
+
@p0.scale!(2)
|
49
|
+
assert { @p0 == Vector.new(20, 40) }
|
50
|
+
end
|
51
|
+
|
52
|
+
test "normalize!" do
|
53
|
+
@p0.normalize!
|
54
|
+
@p0
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
test "長さ" do
|
59
|
+
assert { Vector.new(3, 4).magnitude == 5 }
|
60
|
+
end
|
61
|
+
|
62
|
+
test "距離" do
|
63
|
+
assert { @p0.distance_to(@p1).to_i == 201 }
|
64
|
+
end
|
65
|
+
|
66
|
+
test "方向" do
|
67
|
+
assert { @p0.angle_to(@p1) < 1.0 }
|
68
|
+
end
|
69
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stylet_support
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- akicho8
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: test-unit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Vector library
|
42
|
+
email: akicho8@gmail.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- ".gitignore"
|
48
|
+
- ".travis.yml"
|
49
|
+
- Gemfile
|
50
|
+
- README.org
|
51
|
+
- Rakefile
|
52
|
+
- examples/0100_vector.rb
|
53
|
+
- examples/setup.rb
|
54
|
+
- lib/stylet_support.rb
|
55
|
+
- lib/stylet_support/chore.rb
|
56
|
+
- lib/stylet_support/magic.rb
|
57
|
+
- lib/stylet_support/vector.rb
|
58
|
+
- lib/stylet_support/version.rb
|
59
|
+
- stylet_support.gemspec
|
60
|
+
- test/test_etc.rb
|
61
|
+
- test/test_helper.rb
|
62
|
+
- test/test_magic.rb
|
63
|
+
- test/test_vector.rb
|
64
|
+
homepage: http://github.com/akicho8/stylet_support
|
65
|
+
licenses: []
|
66
|
+
metadata: {}
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 2.7.6
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Vector library
|
87
|
+
test_files: []
|