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.
@@ -0,0 +1,723 @@
1
+ # https://zenn.dev/megeton/articles/fc94eb1fece2c5
2
+ # https://docs.rs/nannou/0.18.1/nannou/geom/rect/struct.Rect.html
3
+
4
+ if $0 == __FILE__
5
+ $LOAD_PATH.unshift("..")
6
+ end
7
+
8
+ require "geom_craft/vec2"
9
+ require "geom_craft/range2"
10
+
11
+ module GeomCraft
12
+ class Rect
13
+ class << self
14
+ # 指定された x y 座標と w h 次元から Rect を構築する
15
+ def from_x_y_w_h(x, y, w, h)
16
+ new(Range2.from_pos_and_len(x, w), Range2.from_pos_and_len(y, h))
17
+ end
18
+
19
+ # 指定された Point と Dimensions から Rect を構築する
20
+ def from_xy_wh(p, s)
21
+ from_x_y_w_h(p.x, p.y, s.x, s.y)
22
+ end
23
+
24
+ # 指定された寸法で原点に Rect を構築する
25
+ def from_wh(s)
26
+ from_w_h(s.x, s.y)
27
+ end
28
+
29
+ # 指定された幅と高さで原点に Rect を構築する
30
+ def from_w_h(w, h)
31
+ from_x_y_w_h(0.0, 0.0, w, h)
32
+ end
33
+
34
+ # 2点の座標から Rect を構築する
35
+ def from_corners(a, b)
36
+ if a.x < b.x
37
+ left, right = a.x, b.x
38
+ else
39
+ left, right = b.x, a.x
40
+ end
41
+ if a.y < b.y
42
+ bottom, top = a.y, b.y
43
+ else
44
+ bottom, top = b.y, a.y
45
+ end
46
+ new(Range2.new(left, right), Range2.new(bottom, top))
47
+ end
48
+ end
49
+
50
+ attr_accessor :x
51
+ attr_accessor :y
52
+
53
+ def initialize(x, y)
54
+ @x = x
55
+ @y = y
56
+ end
57
+
58
+ # x の中央
59
+ def x_middle
60
+ @x.middle
61
+ end
62
+
63
+ # y の中央
64
+ def y_middle
65
+ @y.middle
66
+ end
67
+
68
+ # 中心
69
+ def x_y
70
+ [x_middle, y_middle]
71
+ end
72
+
73
+ # 幅
74
+ def w
75
+ @x.length
76
+ end
77
+
78
+ # 高さ
79
+ def h
80
+ @y.length
81
+ end
82
+
83
+ # 寸法
84
+ def w_h
85
+ [w, h]
86
+ end
87
+
88
+ # 中心と寸法
89
+ def x_y_w_h
90
+ [x_middle, y_middle, w, h]
91
+ end
92
+
93
+ # 左上と寸法
94
+ def l_t_w_h
95
+ [left, top, w, h]
96
+ end
97
+
98
+ # 左下と寸法
99
+ def l_b_w_h
100
+ [left, bottom, w, h]
101
+ end
102
+
103
+ # 長方形の最長辺の長さ
104
+ def length
105
+ [w, h].max
106
+ end
107
+
108
+ # Rect の最小の y 値
109
+ def bottom
110
+ @y.absolute.start
111
+ end
112
+
113
+ # Rect の最大の y 値
114
+ def top
115
+ @y.absolute.end
116
+ end
117
+
118
+ # Rect の最小の x 値
119
+ def left
120
+ @x.absolute.start
121
+ end
122
+
123
+ # Rect の最大の x 値
124
+ def right
125
+ @x.absolute.end
126
+ end
127
+
128
+ # 辺 (左右下上)
129
+ def l_r_b_t
130
+ [left, right, bottom, top]
131
+ end
132
+
133
+ # 境界の中央の xy 位置
134
+ def xy
135
+ V[x_middle, y_middle]
136
+ end
137
+
138
+ # Rect の合計寸法
139
+ def wh
140
+ V[w, h]
141
+ end
142
+
143
+ # Rect を Point と Dimensions に変換する
144
+ def xy_wh
145
+ [xy, wh]
146
+ end
147
+
148
+ # 左上隅位置
149
+ def top_left
150
+ V[left, top]
151
+ end
152
+
153
+ # 左下隅位置
154
+ def bottom_left
155
+ V[left, bottom]
156
+ end
157
+
158
+ # 右上隅位置
159
+ def top_right
160
+ V[right, top]
161
+ end
162
+
163
+ # 右下隅位置
164
+ def bottom_right
165
+ V[right, bottom]
166
+ end
167
+
168
+ # 左端の真ん中
169
+ def mid_left
170
+ V[left, y_middle]
171
+ end
172
+
173
+ # 上端の真ん中
174
+ def mid_top
175
+ V[x_middle, top]
176
+ end
177
+
178
+ # 右端の真ん中
179
+ def mid_right
180
+ V[right, y_middle]
181
+ end
182
+
183
+ # 下端の真ん中
184
+ def mid_bottom
185
+ V[x_middle, bottom]
186
+ end
187
+
188
+ ################################################################################
189
+
190
+ # 指定された Align バリアントに従って、x 軸に沿って self を other に位置を合わせる
191
+ def align_x_of(align, other)
192
+ self.class.new(@x.align_to(align, other.x), @y)
193
+ end
194
+
195
+ # 指定された Align バリアントに従って、y 軸に沿って self を other に位置を合わせる
196
+ def align_y_of(align, other)
197
+ self.class.new(@x, @y.align_to(align, other.y))
198
+ end
199
+
200
+ # x 軸に沿って、自分の中心を相手の Rect の中心に揃える
201
+ def align_middle_x_of(other)
202
+ self.class.new(@x.align_middle_of(other.x), @y)
203
+ end
204
+
205
+ # y 軸に沿って、self の中央を other Rect の中央に揃える
206
+ def align_middle_y_of(other)
207
+ self.class.new(@x, @y.align_middle_of(other.y))
208
+ end
209
+
210
+ # 自分を相手の Rect の上端の中央に配置する
211
+ def mid_top_of(other)
212
+ align_middle_x_of(other).align_top_of(other)
213
+ end
214
+
215
+ # 自分を相手の Rect の下端の中央に配置する
216
+ def mid_bottom_of(other)
217
+ align_middle_x_of(other).align_bottom_of(other)
218
+ end
219
+
220
+ # 自分を相手の Rect の左端の中央に配置する
221
+ def mid_left_of(other)
222
+ align_left_of(other).align_middle_y_of(other)
223
+ end
224
+
225
+ # 自分を相手の Rect の右端の中央に配置する
226
+ def mid_right_of(other)
227
+ align_right_of(other).align_middle_y_of(other)
228
+ end
229
+
230
+ # 自分を相手の Rect の中央に直接配置する
231
+ def middle_of(other)
232
+ align_middle_x_of(other).align_middle_y_of(other)
233
+ end
234
+
235
+ def subdivision_ranges
236
+ x_a = Range2.new(@x.start, x_middle)
237
+ x_b = Range2.new(x_middle, @x.end)
238
+ y_a = Range2.new(@y.start, y_middle)
239
+ y_b = Range2.new(y_middle, @y.end)
240
+ SubdivisionRanges.new(x_a, x_b, y_a, y_b)
241
+ end
242
+
243
+ # Rect を x 軸と y 軸に沿って半分に分割し、4 つの細分割を返す
244
+ #
245
+ # サブディビジョンは次の順序で生成されます
246
+ #
247
+ # 1. Bottom left
248
+ # 2. Bottom right
249
+ # 3. Top left
250
+ # 4. Top right
251
+ def subdivisions
252
+ subdivision_ranges.rects
253
+ end
254
+
255
+ # 各範囲の大きさが常に正になるように、self を絶対的な Rect に変換する
256
+ def absolute
257
+ self.class.new(@x.absolute, @y.absolute)
258
+ end
259
+
260
+ ################################################################################
261
+
262
+ # 2 つの Rect が重なる領域を表す Rect
263
+ def overlap(other)
264
+ if x = @x.overlap(other.x) && y = @y.overlap(other.y)
265
+ self.class.new(x, y)
266
+ end
267
+ end
268
+
269
+ # 指定された 2 つの Rect セットを包含する Rect
270
+ def max(other)
271
+ self.class.new(@x.max(other.x), @y.max(other.y))
272
+ end
273
+
274
+ ################################################################################
275
+
276
+ # Rect を x 軸に沿って移動します
277
+ def shift_x(x)
278
+ self.class.new(@x.shift(x), @y)
279
+ end
280
+
281
+ # Rect を y 軸に沿って移動します
282
+ def shift_y(y)
283
+ self.class.new(@x, @y.shift(y))
284
+ end
285
+
286
+ # 自分の右端を相手の Rect の左端に揃える
287
+ def left_of(other)
288
+ self.class.new(@x.align_before(other.x), @y)
289
+ end
290
+
291
+ # 自分の左端を相手の Rect の右端に揃える
292
+ def right_of(other)
293
+ self.class.new(@x.align_after(other.x), @y)
294
+ end
295
+
296
+ # 自分の上端を相手の Rectの下端に揃える
297
+ def below(other)
298
+ self.class.new(@x, @y.align_before(other.y))
299
+ end
300
+
301
+ # Align self's bottom edge with the top edge of the other Rect.
302
+ def above(other)
303
+ self.class.new(@x, @y.align_after(other.y))
304
+ end
305
+
306
+ # 自分の左端を相手の Rect の左端に揃える
307
+ def align_left_of(other)
308
+ self.class.new(@x.align_start_of(other.x), @y)
309
+ end
310
+
311
+ # 自分の右端を相手のRectの右端に揃える
312
+ def align_right_of(other)
313
+ self.class.new(@x.align_end_of(other.x), @y)
314
+ end
315
+
316
+ # self の下端を other Rect の下端に揃える
317
+ def align_bottom_of(other)
318
+ self.class.new(@x, @y.align_start_of(other.y))
319
+ end
320
+
321
+ # 自分の上端を相手の Rect の上端に揃える
322
+ def align_top_of(other)
323
+ self.class.new(@x, @y.align_end_of(other.y))
324
+ end
325
+
326
+ # 自分を相手の Rect の左上端に沿って配置する
327
+ def top_left_of(other)
328
+ align_left_of(other).align_top_of(other)
329
+ end
330
+
331
+ # 相手の Rect の右上端に沿って自分を配置する
332
+ def top_right_of(other)
333
+ align_right_of(other).align_top_of(other)
334
+ end
335
+
336
+ # 自分を相手の Rect の左下の端に沿って配置する
337
+ def bottom_left_of(other)
338
+ align_left_of(other).align_bottom_of(other)
339
+ end
340
+
341
+ # 相手の Rect の右下の端に沿って自分を配置する
342
+ def bottom_right_of(other)
343
+ align_right_of(other).align_bottom_of(other)
344
+ end
345
+
346
+ # 指定された位置に最も近い角を返す
347
+ CLOSEST_CORNER_TABLE = {
348
+ [:start, :start] => :bottom_left,
349
+ [:start, :end] => :top_left,
350
+ [:end, :start] => :bottom_right,
351
+ [:end, :end] => :top_right,
352
+ }
353
+ def closest_corner(v)
354
+ CLOSEST_CORNER_TABLE.fetch([@x.closest_edge(v.x), @y.closest_edge(v.y)])
355
+ end
356
+
357
+ # Rect の四隅
358
+ def corners
359
+ [
360
+ V[@x.start, @y.end],
361
+ V[@x.end, @y.end],
362
+ V[@x.end, @y.start],
363
+ V[@x.start, @y.start],
364
+ ]
365
+ end
366
+
367
+ # Rect を表す2つの三角形の頂点を返す
368
+ def triangles
369
+ a, b, c, d = corners
370
+ [[a, b, c], [a, c, d]]
371
+ end
372
+
373
+ # Rect を指定されたベクトルだけシフトします
374
+ def shift(v)
375
+ shift_x(v.x).shift_y(v.y)
376
+ end
377
+
378
+ # 指定された位置が Rectangle に触れているかどうか
379
+ def contains?(v)
380
+ @x.contains?(v.x) && @y.contains?(v.y)
381
+ end
382
+
383
+ # 指定された位置が Rect 領域の外側にある場合、その位置に最も近い辺を引き伸ばす
384
+ def stretch_to(v)
385
+ self.class.new(@x.stretch_to_value(v.x), @y.stretch_to_value(v.y))
386
+ end
387
+
388
+ # 指定された位置が Rect 領域の外側にある場合、その位置に最も近い辺を引き伸ばす
389
+ def stretch_to_point(v)
390
+ stretch_to(v)
391
+ end
392
+
393
+ # 左端にパディングが適用された Rect
394
+ def pad_left(pad)
395
+ self.class.new(@x.pad_start(pad), @y)
396
+ end
397
+
398
+ # 右端にパディングが適用された Rect
399
+ def pad_right(pad)
400
+ self.class.new(@x.pad_end(pad), @y)
401
+ end
402
+
403
+ # 下端にパディングが適用された四角形
404
+ def pad_bottom(pad)
405
+ self.class.new(@x, @y.pad_start(pad))
406
+ end
407
+
408
+ # 上端にパディングが適用された Rect
409
+ def pad_top(pad)
410
+ self.class.new(@x, @y.pad_end(pad))
411
+ end
412
+
413
+ # 各辺にある程度のパディング量が適用された Rect
414
+ def pad(pad)
415
+ self.class.new(@x.pad(pad), @y.pad(pad))
416
+ end
417
+
418
+ # パディングが適用された Rect
419
+ def padding(padding)
420
+ self.class.new(@x.pad_ends(padding.x.start, padding.x.end), @y.pad_ends(padding.y.start, padding.y.end))
421
+ end
422
+
423
+ # x 軸上の指定された位置を基準とした相対位置を持つ Rect を返す
424
+ def relative_to_x(x)
425
+ self.class.new(@x.shift(-x), @y)
426
+ end
427
+
428
+ # y 軸上の指定された位置を基準とした相対位置を持つ Rect を返す
429
+ def relative_to_y(y)
430
+ self.class.new(@x, @y.shift(-y))
431
+ end
432
+
433
+ # 指定された位置を基準とした相対位置を持つ Rect を返す
434
+ def relative_to(v)
435
+ relative_to_x(v.x).relative_to_y(v.y)
436
+ end
437
+
438
+ # X 軸を反転する (別名: Y 軸を中心に反転する)
439
+ def invert_x
440
+ self.class.new(@x.invert, @y)
441
+ end
442
+
443
+ # Y 軸を反転する (別名: X 軸を中心に反転する)
444
+ def invert_y
445
+ self.class.new(@x, @y.invert)
446
+ end
447
+
448
+ ################################################################################
449
+
450
+ def ==(other)
451
+ self.class == other.class && @x == other.x && @y == other.y
452
+ end
453
+
454
+ def eql?(other)
455
+ self.class == other.class && @x == other.x && @y == other.y
456
+ end
457
+
458
+ def hash
459
+ self.class.hash ^ @x.hash ^ @y.hash
460
+ end
461
+
462
+ def <=>(other)
463
+ [self.class, @x, @y] <=> [other.class, other.x, other.y]
464
+ end
465
+
466
+ def inspect
467
+ "(#{@x}, #{@y})"
468
+ end
469
+
470
+ def to_s
471
+ "(#{@x}, #{@y})"
472
+ end
473
+
474
+ SubdivisionRanges = Data.define(:x_a, :x_b, :y_a, :y_b) do
475
+ def rects
476
+ [
477
+ Rect.new(x_a, y_a),
478
+ Rect.new(x_b, y_a),
479
+ Rect.new(x_a, y_b),
480
+ Rect.new(x_b, y_b),
481
+ ]
482
+ end
483
+ end
484
+ end
485
+ end
486
+
487
+ if $0 == __FILE__
488
+ V = GeomCraft::Vec2
489
+ Range2 = GeomCraft::Range2
490
+ Rect = GeomCraft::Rect
491
+
492
+ require "rspec/autorun"
493
+
494
+ RSpec.configure do |config|
495
+ config.expect_with :test_unit
496
+ end
497
+
498
+ describe Rect do
499
+ it "x, y, w, h から作る" do
500
+ a = Rect.from_x_y_w_h(0.0, 0.0, 100.0, 100.0)
501
+ assert { a.x == Range2.new(-50.0, 50.0) }
502
+ assert { a.y == Range2.new(-50.0, 50.0) }
503
+ end
504
+
505
+ it "w, h から作る" do
506
+ a = Rect.from_w_h(100.0, 100.0)
507
+ assert { a.x == Range2.new(-50.0, 50.0) }
508
+ assert { a.y == Range2.new(-50.0, 50.0) }
509
+ end
510
+
511
+ it "対角から作る" do
512
+ assert { Rect.from_corners(V[-10, 10], V[-10, 10]) == Rect.new(Range2.new(-10, -10), Range2.new(10, 10)) }
513
+ end
514
+
515
+ it "基本的な値の取得" do
516
+ a = Rect.from_x_y_w_h(10.0, 20.0, 100.0, 100.0)
517
+ assert { a.x_middle == 10.0 }
518
+ assert { a.y_middle == 20.0 }
519
+ assert { a.w == 100.0 }
520
+ assert { a.h == 100.0 }
521
+ assert { a.x_y == [10.0, 20.0] }
522
+ assert { a.w_h == [100.0, 100.0] }
523
+ end
524
+
525
+ it "a を b の座標の align に揃える" do
526
+ a = Rect.from_x_y_w_h(0.0, 0.0, 10.0, 10.0)
527
+ b = Rect.from_x_y_w_h(0.0, 0.0, 100.0, 100.0)
528
+ assert { a.align_x_of(:start, b) == Rect.new(Range2.new(-50.0, -40.0), Range2.new(-5.0, 5.0)) }
529
+ assert { a.align_x_of(:middle, b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
530
+ assert { a.align_x_of(:end, b) == Rect.new(Range2.new(40.0, 50.0), Range2.new(-5.0, 5.0)) }
531
+
532
+ assert { a.align_y_of(:start, b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-50.0, -40.0)) }
533
+ assert { a.align_y_of(:middle, b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
534
+ assert { a.align_y_of(:end, b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(40.0, 50.0)) }
535
+
536
+ # a.align_x_of(:middle, b) と a.align_y_of(:middle, b) のショートカット:
537
+ assert { a.align_middle_x_of(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
538
+ assert { a.align_middle_y_of(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
539
+
540
+ # a を b の内側の上下左右の辺にくっつける
541
+ assert { a.mid_top_of(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(40.0, 50.0)) }
542
+ assert { a.mid_bottom_of(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-50.0, -40.0)) }
543
+ assert { a.mid_left_of(b) == Rect.new(Range2.new(-50.0, -40.0), Range2.new(-5.0, 5.0)) }
544
+ assert { a.mid_right_of(b) == Rect.new(Range2.new(40.0, 50.0), Range2.new(-5.0, 5.0)) }
545
+
546
+ # a を b の中心に配置する
547
+ assert { a.middle_of(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
548
+ end
549
+
550
+ it "中央で区切って上下左右の範囲を返す" do
551
+ a = Rect.from_x_y_w_h(200.0, 300.0, 10.0, 10.0)
552
+ b = a.subdivision_ranges
553
+ assert { b.y_a == Range2.new(295.0, 300.0) }
554
+ assert { b.y_b == Range2.new(300.0, 305.0) }
555
+ assert { b.x_a == Range2.new(195.0, 200.0) }
556
+ assert { b.x_b == Range2.new(200.0, 205.0) }
557
+ end
558
+
559
+ it "内部の方向を正にする" do
560
+ a = Rect.new(Range2.new(1.0, -1.0), Range2.new(1.0, -1.0))
561
+ assert { a == Rect.new(Range2.new(1.0, -1.0), Range2.new(1.0, -1.0)) }
562
+ assert { a.absolute == Rect.new(Range2.new(-1.0, 1.0), Range2.new(-1.0, 1.0)) }
563
+ end
564
+
565
+ it "AND 領域" do
566
+ a = Rect.from_x_y_w_h(100.0, 100.0, 100.0, 100.0)
567
+ b = Rect.from_x_y_w_h(150.0, 150.0, 100.0, 100.0)
568
+ assert { a.overlap(b) == Rect.new(Range2.new(100.0, 150.0), Range2.new(100.0, 150.0)) }
569
+ end
570
+
571
+ it "OR 領域" do
572
+ a = Rect.from_x_y_w_h(100.0, 100.0, 100.0, 100.0)
573
+ b = Rect.from_x_y_w_h(150.0, 150.0, 100.0, 100.0)
574
+ assert { a.max(b) == Rect.new(Range2.new(50.0, 200.0), Range2.new(50.0, 200.0)) }
575
+ end
576
+
577
+ it "辺の座標" do
578
+ a = Rect.from_x_y_w_h(100.0, 100.0, 100.0, 100.0)
579
+ assert { a.left == 50.0 }
580
+ assert { a.right == 150.0 }
581
+ assert { a.bottom == 50.0 }
582
+ assert { a.top == 150.0 }
583
+
584
+ # まとめて
585
+ assert { a.l_r_b_t == [50.0, 150.0, 50.0, 150.0] }
586
+ end
587
+
588
+ it "x y をそれぞれ移動" do
589
+ a = Rect.from_x_y_w_h(0.0, 0.0, 100.0, 100.0)
590
+ assert { a == Rect.new(Range2.new(-50.0, 50.0), Range2.new(-50.0, 50.0)) }
591
+ assert { a.shift_x(25.0) == Rect.new(Range2.new(-25.0, 75.0), Range2.new(-50.0, 50.0)) }
592
+ assert { a.shift_y(25.0) == Rect.new(Range2.new(-50.0, 50.0), Range2.new(-25.0, 75.0)) }
593
+ end
594
+
595
+ describe "相手のどこかに移動する" do
596
+ let(:a) { Rect.from_x_y_w_h(0.0, 0.0, 10.0, 10.0) }
597
+ let(:b) { Rect.from_x_y_w_h(0.0, 0.0, 100.0, 100.0) }
598
+
599
+ it "相手の辺の外側に移動する" do
600
+ assert { a.left_of(b) == Rect.new(Range2.new(-60.0, -50.0), Range2.new(-5.0, 5.0)) }
601
+ assert { a.right_of(b) == Rect.new(Range2.new(50.0, 60.0), Range2.new(-5.0, 5.0)) }
602
+ assert { a.below(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-60.0, -50.0)) }
603
+ assert { a.above(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(50.0, 60.0)) }
604
+ end
605
+
606
+ it "相手の辺の内側に移動する" do
607
+ assert { a.align_left_of(b) == Rect.new(Range2.new(-50.0, -40.0), Range2.new(-5.0, 5.0)) }
608
+ assert { a.align_right_of(b) == Rect.new(Range2.new(40.0, 50.0), Range2.new(-5.0, 5.0)) }
609
+ assert { a.align_bottom_of(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-50.0, -40.0)) }
610
+ assert { a.align_top_of(b) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(40.0, 50.0)) }
611
+ end
612
+
613
+ it "相手の角の内側に移動する" do
614
+ assert { a.top_left_of(b) == Rect.new(Range2.new(-50.0, -40.0), Range2.new(40.0, 50.0)) }
615
+ assert { a.top_right_of(b) == Rect.new(Range2.new(40.0, 50.0), Range2.new(40.0, 50.0)) }
616
+ assert { a.bottom_left_of(b) == Rect.new(Range2.new(-50.0, -40.0), Range2.new(-50.0, -40.0)) }
617
+ assert { a.bottom_right_of(b) == Rect.new(Range2.new(40.0, 50.0), Range2.new(-50.0, -40.0)) }
618
+ end
619
+ end
620
+
621
+ it "指定の座標を含むように近い方の辺を広げる(比率が壊れる)" do
622
+ a = Rect.from_x_y_w_h(0.0, 0.0, 10.0, 10.0)
623
+ assert { a.stretch_to_point(V[6.0, 6.0]) == Rect.new(Range2.new(-5.0, 6.0), Range2.new(-5.0, 6.0)) }
624
+ end
625
+
626
+ it "指定の座標にいちばん近い角の名前を返す" do
627
+ a = Rect.from_x_y_w_h(0.0, 0.0, 10.0, 10.0)
628
+ assert { a.closest_corner(V[1.0, 1.0]) == :top_right }
629
+ assert { a.closest_corner(V[-1.0, -1.0]) == :bottom_left }
630
+ assert { a.closest_corner(V[-1.0, 1.0]) == :top_left }
631
+ assert { a.closest_corner(V[1.0, -1.0]) == :bottom_right }
632
+ end
633
+
634
+ it "角の座標を返す" do
635
+ a = Rect.from_x_y_w_h(0.0, 0.0, 10.0, 10.0)
636
+ assert { a.corners == [[-5.0, 5.0], [5.0, 5.0], [5.0, -5.0], [-5.0, -5.0]] }
637
+ end
638
+
639
+ it "四角形を斜めに切ってできる三角形を得る" do
640
+ a = Rect.from_x_y_w_h(0.0, 0.0, 10.0, 10.0)
641
+ assert { a.triangles == [[[-5.0, 5.0], [5.0, 5.0], [5.0, -5.0]], [[-5.0, 5.0], [5.0, -5.0], [-5.0, -5.0]]] }
642
+ end
643
+
644
+ it "ベクトルや Point2 から作る" do
645
+ assert { Rect.from_xy_wh(V[0.0, 0.0], V[10.0, 10.0]) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
646
+ assert { Rect.from_wh(V[10.0, 10.0]) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
647
+ end
648
+
649
+ it "基本的な情報の参照" do
650
+ a = Rect.from_wh(V[100.0, 100.0])
651
+ assert { a.xy == V[0.0, 0.0] }
652
+ assert { a.wh == V[100.0, 100.0] }
653
+ assert { a.xy_wh == [V[0.0, 0.0], V[100.0, 100.0]] }
654
+
655
+ assert { a.top_left == V[-50.0, 50.0] }
656
+ assert { a.top_right == V[50.0, 50.0] }
657
+ assert { a.bottom_left == V[-50.0, -50.0] }
658
+ assert { a.bottom_right == V[50.0, -50.0] }
659
+
660
+ assert { a.mid_left == V[-50.0, 0.0] }
661
+ assert { a.mid_top == V[0.0, 50.0] }
662
+ assert { a.mid_right == V[50.0, 0.0] }
663
+ assert { a.mid_bottom == V[0.0, -50.0] }
664
+ end
665
+
666
+ it "x, y をまとめて移動" do
667
+ a = Rect.from_wh(V[100.0, 100.0])
668
+ assert { a.shift(V[10.0, 10.0]) == Rect.new(Range2.new(-40.0, 60.0), Range2.new(-40.0, 60.0)) }
669
+ end
670
+
671
+ it "指定の座標が含まれるか?" do
672
+ a = Rect.from_wh(V[100.0, 100.0])
673
+ assert { a.contains?(V[0.0, 0.0]) == true }
674
+ assert { a.contains?(V[0.0, 51.0]) == false }
675
+ end
676
+
677
+ it "指定の座標が含まれるまで近い方を伸ばす" do
678
+ a = Rect.from_wh(V[2.0, 2.0])
679
+ assert { a.stretch_to(V[5.0, 5.0]) == Rect.new(Range2.new(-1.0, 5.0), Range2.new(-1.0, 5.0)) }
680
+ end
681
+
682
+ it "左上wh や 左下wh をまとめて得る" do
683
+ a = Rect.from_wh(V[100.0, 100.0])
684
+ assert { a.l_t_w_h == [-50.0, 50.0, 100.0, 100.0] }
685
+ assert { a.l_b_w_h == [-50.0, -50.0, 100.0, 100.0] }
686
+ end
687
+
688
+ it "領域を内側に縮小する" do
689
+ a = Rect.from_wh(V[100.0, 100.0])
690
+ assert { a.pad_left(10.0) == Rect.new(Range2.new(-40.0, 50.0), Range2.new(-50.0, 50.0)) }
691
+ assert { a.pad_right(10.0) == Rect.new(Range2.new(-50.0, 40.0), Range2.new(-50.0, 50.0)) }
692
+ assert { a.pad_bottom(10.0) == Rect.new(Range2.new(-50.0, 50.0), Range2.new(-40.0, 50.0)) }
693
+ assert { a.pad_top(10.0) == Rect.new(Range2.new(-50.0, 50.0), Range2.new(-50.0, 40.0)) }
694
+ assert { a.pad(10.0) == Rect.new(Range2.new(-40.0, 40.0), Range2.new(-40.0, 40.0)) }
695
+ p = Rect.new(Range2.new(10.0, 10.0), Range2.new(10.0, 10.0))
696
+ assert { a.padding(p) == Rect.new(Range2.new(-40.0, 40.0), Range2.new(-40.0, 40.0)) }
697
+ end
698
+
699
+ it "相対的な範囲を返す" do
700
+ a = Rect.from_wh(V[10.0, 10.0])
701
+ assert { a == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-5.0, 5.0)) }
702
+ assert { a.relative_to_x(10.0) == Rect.new(Range2.new(-15.0, -5.0), Range2.new(-5.0, 5.0)) }
703
+ assert { a.relative_to_y(10.0) == Rect.new(Range2.new(-5.0, 5.0), Range2.new(-15.0, -5.0)) }
704
+ assert { a.relative_to(V[10.0, 10.0]) == Rect.new(Range2.new(-15.0, -5.0), Range2.new(-15.0, -5.0)) }
705
+ end
706
+
707
+ it "反転" do
708
+ a = Rect.from_wh(V[2.0, 2.0])
709
+ assert { a == Rect.new(Range2.new(-1.0, 1.0), Range2.new(-1.0, 1.0)) }
710
+ assert { a.invert_x == Rect.new(Range2.new(1.0, -1.0), Range2.new(-1.0, 1.0)) }
711
+ assert { a.invert_y == Rect.new(Range2.new(-1.0, 1.0), Range2.new(1.0, -1.0)) }
712
+ end
713
+
714
+ it "==" do
715
+ assert { Rect.from_wh(V[2.0, 2.0]) == Rect.from_wh(V[2.0, 2.0]) }
716
+ end
717
+ end
718
+ end
719
+ # >> ............................
720
+ # >>
721
+ # >> Finished in 0.04071 seconds (files took 0.06077 seconds to load)
722
+ # >> 28 examples, 0 failures
723
+ # >>