➡️
Nannou の一次元の範囲を表す Range の使い方
早見表
種類 | 意味 | Methods | 同類・備考 |
---|---|---|---|
生成 | pos ± (len / 2) | from_pos_and_len | 推奨 |
生成 | start..end | new(start, end) | |
参照 | 左, 右, 中央 | start end middle() | |
参照 | 強さ (end - start) | magnitude() | |
参照 | 長さ (強さの絶対値) | len() | |
向き | 向き | direction() | -1, 0, +1 を返す |
向き | 相手と同じ向きか? | has_same_direction(b) | |
向き | 反転 | invert() | |
向き | 「←」なら「→」にする | absolute() | |
対領域 | OR | a.max(b) | |
対領域 | OR (向きを維持する) | a.max_directed(b) | |
対領域 | AND | a.overlap(b) | |
移動 | 相手の軸に揃える | a.align_start_of(b) | align_end_of / align_middle_of |
移動 | 相手の軸の ? に揃える | a.align_to(?, b) | |
移動 | 相手の隣に並べる | a.align_after(b) | a.align_before(b) |
移動 | v だけ移動する | shift(v) | |
変形 | 縮小 | pad(v) | pad_start / pad_end / pad_ends |
変形 | v に近い方の端を広げる | stretch_to_value(v) | |
その他 | 整形補完 | a.lerp(v) | a.map_value(v, &b) |
その他 | 領域に含まれるか? | a.contains(v) | |
その他 | 相手を補正する | clamp_value(v) | v.clamp(range) に類似 |
その他 | v に近い方の Edge を返す | closest_edge(v) | |
その他 | 小数補正 | round() | floor() |
特徴
- 2方向の向きを持っている
- 1次元ベクトルとも言える
- Immutable
- 破壊的メソッドは一つもない
- とはいえ start end は外部から更新できてしまう
- 整数を使っていると急に怒り出す
- コンストラクタでは整数が使えるのに割り算などが絡むとめちゃくちゃ怒られる
- なので基本小数で指定した方がいい
- Ruby の Range クラスとは根本的に考え方が異なる
-
start <= end
の関係である必要はない - 範囲というより単純に start 地点と end 地点を持つ
- end は行き先の地点そのものを指すので終端を含む・含まない問題とは無縁
- 無縁だが「向き」を持つため複雑さはたいして変わらない
-
説明文章の補足
- イメージしやすいように start, end を「左右」と呼んだりしている
- 実際は
end < start
の関係になることもある
コンストラクタ
基本
Range::new(1, 2); // => Range { start: 1, end: 2 }
pos を中心に半径 len / 2 の幅とする (重要)
Range::from_pos_and_len(100.0, 10.0); // => Range { start: 95.0, end: 105.0 }
Rect 型の x y 要素はこれで作られる。
それぞれの値
Range::new(100.0, 200.0).start; // => 100.0
Range::new(100.0, 200.0).middle(); // => 150.0
Range::new(100.0, 200.0).end; // => 200.0
ベクトルの強さと長さ
強さ (end - start)
Range::new(100, -200).magnitude(); // => -300
長さ (強さの絶対値)
Range::new(100, -200).len(); // => 300
小数の補正
Range::new(0.4, 0.5).round(); // => Range { start: 0.0, end: 1.0 }
Range::new(0.4, 0.5).floor(); // => Range { start: 0.0, end: 0.0 }
範囲
OR (向きを破壊する)
let a = Range::new(5.0, 3.0);
let b = Range::new(4.0, 6.0);
a.max(b); // => Range { start: 3.0, end: 6.0 }
OR (向きを維持する)
let a = Range::new(5.0, 3.0);
let b = Range::new(4.0, 6.0);
a.max_directed(b); // => Range { start: 6.0, end: 3.0 }
a が右向きなら max と同じだが左向きなら max の invert になる。
AND (向きを破壊する)
let a = Range::new(5.0, 3.0);
let b = Range::new(4.0, 6.0);
a.overlap(b); // => Some(Range { start: 4.0, end: 5.0 })
max_directed があるなら overlap_directed もあってよさそうだが見つからなかった。
向き
現在の向きを返す
Range::new(0, 10).direction(); // => 1
Range::new(10, 0).direction(); // => -1
Range::new(10, 10).direction(); // => 0
(end - start) の符号に相当する。
向きが同じか?
let a = Range::new(1, 2);
let b = Range::new(3, 4);
a.has_same_direction(b); // => true
向きを反転する
Range::new(0, 100).invert(); // => Range { start: 100, end: 0 }
正の向きにする
Range::new(10, 0).absolute(); // => Range { start: 0, end: 10 }
-
start > end
なら invert する。
スケーリング
let a = Range::new(0.0, 1.0);
let b = Range::new(0.0, 100.0);
a.map_value(0.9, &b); // => 90.0
a の 0.9 は b では 90 になる。
元の範囲が 0..1 の場合 lerp 使うと簡潔に書ける。
b.lerp(0.9); // => 90.0
指定の軸で整列する
相手の左端に揃える
let a = Range::new(0, 100);
let b = Range::new(50, 100);
a.align_start_of(b); // => Range { start: 50, end: 150 }
相手の右端に揃える
let a = Range::new(0, 50);
let b = Range::new(0, 100);
a.align_end_of(b); // => Range { start: 50, end: 100 }
相手の中央に揃える
let a = Range::new(0.0, 50.0);
let b = Range::new(0.0, 100.0);
a.align_middle_of(b); // => Range { start: 25.0, end: 75.0 }
相手のどこかに揃える
let a = Range::new(0.0, 5.0);
let b = Range::new(10.0, 20.0);
a.align_to(Align::Start, b); // => Range { start: 10.0, end: 15.0 }
a.align_to(Align::End, b); // => Range { start: 15.0, end: 20.0 }
a.align_to(Align::Middle, b); // => Range { start: 12.5, end: 17.5 }
横に並べる
相手の左隣り並べる
let a = Range::new(0.0, 5.0);
let b = Range::new(0.0, 10.0);
a.align_after(b); // => Range { start: 10.0, end: 15.0 }
相手の右隣り並べる
let a = Range::new(0.0, 5.0);
let b = Range::new(0.0, 0.0);
a.align_before(b); // => Range { start: -5.0, end: 0.0 }
Edge を寄せる (サイズが変わる)
左端を内側に寄せる
Range::new(10, 0).pad_start(3); // => Range { start: 7, end: 0 }
右端を内側に寄せる
Range::new(10, 0).pad_end(3); // => Range { start: 10, end: 3 }
両端を内側に寄せる
Range::new(10, 0).pad(3); // => Range { start: 7, end: 3 }
両端を内側に寄せる (個別指定)
Range::new(10, 0).pad_ends(3, 4); // => Range { start: 7, end: 4 }
この範囲に含むか?
Range::new(1, 2).contains(2); // => true
対象を補正する
Range::new(10, 0).clamp_value(-1); // => 0
Range::new(10, 0).clamp_value(11); // => 10
Ruby で言うところの value.clamp(range)
の向きを逆に適用したもの
ずらす (サイズ不変)
Range::new(2, 3).shift(10); // => Range { start: 12, end: 13 }
近い方の端を引き伸ばす
Range::new(10, 20).stretch_to_value(5); // => Range { start: 5, end: 20 }
Range::new(10, 20).stretch_to_value(25); // => Range { start: 10, end: 25 }
範囲内を指定した場合は何も変化しない。
Range::new(10, 20).stretch_to_value(15); // => Range { start: 10, end: 20 }
値に近い方の Edge を返す
Range::new(0.0, 10.0).closest_edge(4.0); // => Start
Range::new(0.0, 10.0).closest_edge(6.0); // => End
戻値: Edge::Start, Edge::End
Discussion