🔲

nannou の範囲を扱う Rect の使い方

2023/08/11に公開

早見表

種類 意味 Methods 同類・補足
生成 x, y, w, h から from_x_y_w_h from_xy_wh
生成 w, h から from_w_h from_wh
生成 対角から from_corner_points from_corners
生成 f64 で from_xy_wh_f64 from_wh_f64 from_corners_f64
参照 個別 x() y() w() h()
参照 配列 x_y() w_h()
参照 ベクトル型 xy() wh() xy_wh
参照 まとめて l_r_b_t() l_t_w_h l_b_w_h
参照 十字の先端 mid_left() mid_top mid_right mid_bottom
参照 left() right bottom top
個別 top_left() top_right bottom_left bottom_right
まとめて corners() corners_iter
指定の角座標 corner_at_index(i)
座標に近い角の名前 closest_corner(xy) stretch_to_point の影響を受ける角が分かる
領域 上下左右 subdivision_ranges subdivisions_iter
領域 対角切断時の三角形 triangles() triangles_iter
対領域 相手との AND a.overlap(b)
対領域 相手との OR a.max(b)
移動 相手の軸の ? に a.align_x_of(?, b) align_y_of align_middle_of
移動 相手の内側の辺に a.mid_top_of(b) mid_bottom_of mid_left_of mid_right_of
移動 相手の中心に a.middle_of(b)
移動 相手の辺の外に left_of(b) right_of below above
移動 相手の辺の内に align_left_of(b) align_right_of align_bottom_of align_top_of
移動 相手の角の内に top_left_of(b) top_right_of bottom_left_of bottom_right_of
変形 軸を動かす shift(vec2) shift_x shift_y
変形 指定座標を覆う stretch_to(pt2) stretch_to_point(xy)
変形 縮小 pad pad_left pad_right pad_bottom pad_top padding
向き 反転 invert_x() invert_y
向き 正にする absolute()
その他 座標が含まれるか? contains(pt2) contains_point(xy)
その他 相対的な領域を返す relative_to(xy) relative_to_x relative_to_y

https://docs.rs/nannou/0.18.1/nannou/geom/rect/struct.Rect.html

コンストラクタ

x, y, w, h から作る
let a = Rect::from_x_y_w_h(0.0, 0.0, 100.0, 100.0);
a.x;  // => Range { start: -50.0, end: 50.0 }
a.y;  // => Range { start: -50.0, end: 50.0 }

よくあるのが x, y, w, h のメンバーを持った構造体だったり (x, y) と (w, h) がベクトルになっていたりするタイプだが、Nannou の Rect は Range::from_pos_and_len(x, w)Range::from_pos_and_len(y, h) で生成した Range のインスタンスを x, y に持っているという驚きのそして非常に考えられた構造になっている。

w, h から作る
let a = Rect::from_w_h(100.0, 100.0);
a.x;  // => Range { start: -50.0, end: 50.0 }
a.y;  // => Range { start: -50.0, end: 50.0 }

角を指定して領域を作る

Rect::from_corner_points([-10, 10], [-10, 10]);  // => Rect { x: Range { start: -10, end: -10 }, y: Range { start: 10, end: 10 } }

f64 で作る

Rect::from_xy_wh_f64(dvec2(0.0, 0.0), dvec2(10.0, 10.0));    // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
Rect::from_wh_f64(dvec2(10.0, 10.0));                        // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
Rect::from_corners_f64(dvec2(-5.0, 5.0), dvec2(5.0, -5.0));  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }

基本的な値の取得

let a = Rect::from_x_y_w_h(10.0, 20.0, 100.0, 100.0);
a.x();    // => 10.0
a.y();    // => 20.0
a.w();    // => 100.0
a.h();    // => 100.0
a.x_y();  // => (10.0, 20.0)
a.w_h();  // => (100.0, 100.0)

整列

a を b の座標の align に揃える
let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
let b = Rect::from_x_y_w_h(0.0, 0.0, 100.0, 100.0);
a.align_x_of(Align::Start, b);   // => Rect { x: Range { start: -50.0, end: -40.0 }, y: Range { start: -5.0, end: 5.0 } }
a.align_x_of(Align::Middle, b);  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
a.align_x_of(Align::End, b);     // => Rect { x: Range { start: 40.0, end: 50.0 }, y: Range { start: -5.0, end: 5.0 } }
a.align_y_of(Align::Start, b);   // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -50.0, end: -40.0 } }
a.align_y_of(Align::Middle, b);  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
a.align_y_of(Align::End, b);     // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: 40.0, end: 50.0 } }

a.align_x_of(Align::Middle, b)a.align_y_of(Align::Middle, b) のショートカット:

a.align_middle_x_of(b);  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
a.align_middle_y_of(b);  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }

a を b の内側の上下左右の辺にくっつける

a.mid_top_of(b);     // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: 40.0, end: 50.0 } }
a.mid_bottom_of(b);  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -50.0, end: -40.0 } }
a.mid_left_of(b);    // => Rect { x: Range { start: -50.0, end: -40.0 }, y: Range { start: -5.0, end: 5.0 } }
a.mid_right_of(b);   // => Rect { x: Range { start: 40.0, end: 50.0 }, y: Range { start: -5.0, end: 5.0 } }

a を b の中心に配置する

a.middle_of(b);  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }

中央で区切って上下左右の範囲を返す

let a = Rect::from_x_y_w_h(200.0, 300.0, 10.0, 10.0);
let b = a.subdivision_ranges();
b.y_a;  // => Range { start: 295.0, end: 300.0 }
b.y_b;  // => Range { start: 300.0, end: 305.0 }
b.x_a;  // => Range { start: 195.0, end: 200.0 }
b.x_b;  // => Range { start: 200.0, end: 205.0 }

イテレータを返すメソッドもある

a.subdivisions_iter().for_each(|e| println!("{:?}", e));

内部の方向を正にする

let a = Rect { x: Range::new(1.0, -1.0), y: Range::new(1.0, -1.0) };
a;             // => Rect { x: Range { start: 1.0, end: -1.0 }, y: Range { start: 1.0, end: -1.0 } }
a.absolute();  // => Rect { x: Range { start: -1.0, end: 1.0 }, y: Range { start: -1.0, end: 1.0 } }

同じ領域でも右下から左上の向きになっている場合がある。それを左上から右下方向に直す。

AND 領域

let a = Rect::from_x_y_w_h(100.0, 100.0, 100.0, 100.0);
let b = Rect::from_x_y_w_h(150.0, 150.0, 100.0, 100.0);
a.overlap(b);  // => Some(Rect { x: Range { start: 100.0, end: 150.0 }, y: Range { start: 100.0, end: 150.0 } })

OR 領域

let a = Rect::from_x_y_w_h(100.0, 100.0, 100.0, 100.0);
let b = Rect::from_x_y_w_h(150.0, 150.0, 100.0, 100.0);
a.max(b);  // => Rect { x: Range { start: 50.0, end: 200.0 }, y: Range { start: 50.0, end: 200.0 } }

辺の座標

let a = Rect::from_x_y_w_h(100.0, 100.0, 100.0, 100.0);
a.left();    // => 50.0
a.right();   // => 150.0
a.bottom();  // => 50.0
a.top();     // => 150.0

まとめて

a.l_r_b_t();  // => (50.0, 150.0, 50.0, 150.0)

x y をそれぞれ移動

let a = Rect::from_x_y_w_h(0.0, 0.0, 100.0, 100.0);
a;                // => Rect { x: Range { start: -50.0, end: 50.0 }, y: Range { start: -50.0, end: 50.0 } }
a.shift_x(25.0);  // => Rect { x: Range { start: -25.0, end: 75.0 }, y: Range { start: -50.0, end: 50.0 } }
a.shift_y(25.0);  // => Rect { x: Range { start: -50.0, end: 50.0 }, y: Range { start: -25.0, end: 75.0 } }

相手の辺の外側に移動する

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
let b = Rect::from_x_y_w_h(0.0, 0.0, 100.0, 100.0);
a.left_of(b);   // => Rect { x: Range { start: -60.0, end: -50.0 }, y: Range { start: -5.0, end: 5.0 } }
a.right_of(b);  // => Rect { x: Range { start: 50.0, end: 60.0 }, y: Range { start: -5.0, end: 5.0 } }
a.below(b);     // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -60.0, end: -50.0 } }
a.above(b);     // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: 50.0, end: 60.0 } }

below: 下above: 上

相手の辺の内側に移動する

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
let b = Rect::from_x_y_w_h(0.0, 0.0, 100.0, 100.0);
a.align_left_of(b);    // => Rect { x: Range { start: -50.0, end: -40.0 }, y: Range { start: -5.0, end: 5.0 } }
a.align_right_of(b);   // => Rect { x: Range { start: 40.0, end: 50.0 }, y: Range { start: -5.0, end: 5.0 } }
a.align_bottom_of(b);  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -50.0, end: -40.0 } }
a.align_top_of(b);     // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: 40.0, end: 50.0 } }

相手の角の内側に移動する

a.top_left_of(b);      // => Rect { x: Range { start: -50.0, end: -40.0 }, y: Range { start: 40.0, end: 50.0 } }
a.top_right_of(b);     // => Rect { x: Range { start: 40.0, end: 50.0 }, y: Range { start: 40.0, end: 50.0 } }
a.bottom_left_of(b);   // => Rect { x: Range { start: -50.0, end: -40.0 }, y: Range { start: -50.0, end: -40.0 } }
a.bottom_right_of(b);  // => Rect { x: Range { start: 40.0, end: 50.0 }, y: Range { start: -50.0, end: -40.0 } }

指定の x, y が範囲に含まれるか?

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
a.contains_point([0.0, 0.0]);  // => true
a.contains_point([6.0, 6.0]);  // => false

指定の座標を含むように近い方の辺を広げる

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
a.stretch_to_point([6.0, 6.0]);  // => Rect { x: Range { start: -5.0, end: 6.0 }, y: Range { start: -5.0, end: 6.0 } }

指定の座標にいちばん近い角の名前を返す

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
a.closest_corner([1.0, 1.0]);    // => TopRight
a.closest_corner([-1.0, -1.0]);  // => BottomLeft
a.closest_corner([-1.0, 1.0]);   // => TopLeft
a.closest_corner([1.0, -1.0]);   // => BottomRight

角の座標を返す

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
a.corners();  // => Quad([[-5.0, 5.0], [5.0, 5.0], [5.0, -5.0], [-5.0, -5.0]])

イテレータを返すメソッドもある

a.corners_iter().for_each(|e| println!("{:?}", e));

四角形を斜めに切ってできる三角形を得る

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
a.triangles();  // => (Tri([[-5.0, 5.0], [5.0, 5.0], [5.0, -5.0]]), Tri([[-5.0, 5.0], [5.0, -5.0], [-5.0, -5.0]]))

左上から右下に切ったときの右上と左下の三角形が2つ返ってくる

イテレータを返すメソッドもある

a.triangles_iter().for_each(|e| println!("{:?}", e));

角の座標をインデックスで得る

let a = Rect::from_x_y_w_h(0.0, 0.0, 10.0, 10.0);
a.corner_at_index(0);  // => Some([-5.0, 5.0])
a.corner_at_index(1);  // => Some([5.0, 5.0])
a.corner_at_index(2);  // => Some([5.0, -5.0])
a.corner_at_index(3);  // => Some([-5.0, -5.0])
a.corner_at_index(4);  // => None

ベクトルや Point2 から作る

Rect::from_xy_wh(pt2(0.0, 0.0), vec2(10.0, 10.0));   // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
Rect::from_wh(vec2(10.0, 10.0));                     // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
Rect::from_corners(pt2(-5.0, 5.0), pt2(5.0, -5.0));  // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }

基本的な情報の参照

let a = Rect::from_wh(pt2(100.0, 100.0));
a.xy();     // => Vec2(0.0, 0.0)
a.wh();     // => Vec2(100.0, 100.0)
a.xy_wh();  // => (Vec2(0.0, 0.0), Vec2(100.0, 100.0))
a.top_left();      // => Vec2(-50.0, 50.0)
a.top_right();     // => Vec2(50.0, 50.0)
a.bottom_left();   // => Vec2(-50.0, -50.0)
a.bottom_right();  // => Vec2(50.0, -50.0)
a.mid_left();    // => Vec2(-50.0, 0.0)
a.mid_top();     // => Vec2(0.0, 50.0)
a.mid_right();   // => Vec2(50.0, 0.0)
a.mid_bottom();  // => Vec2(0.0, -50.0)

x, y をまとめて移動

let a = Rect::from_wh(vec2(100.0, 100.0));
a.shift(vec2(10.0, 10.0));  // => Rect { x: Range { start: -40.0, end: 60.0 }, y: Range { start: -40.0, end: 60.0 } }

指定の座標が含まれるか?

let a = Rect::from_wh(vec2(100.0, 100.0));
a.contains(pt2(0.0, 0.0));   // => true
a.contains(vec2(0.0, 0.0));  // => true

pt2 は vec2 と書いてもいいらしい

指定の座標が含まれるまで近い方を伸ばす

let a = Rect::from_wh(vec2(2.0, 2.0));
a.stretch_to(pt2(5.0, 5.0));  // => Rect { x: Range { start: -1.0, end: 5.0 }, y: Range { start: -1.0, end: 5.0 } }

左上wh や 左下wh をまとめて得る

let a = Rect::from_wh(vec2(100.0, 100.0));
a.l_t_w_h();  // => (-50.0, 50.0, 100.0, 100.0)
a.l_b_w_h();  // => (-50.0, -50.0, 100.0, 100.0)

内側に辺をずらす

let a = Rect::from_wh(vec2(100.0, 100.0));
a.pad_left(10.0);    // => Rect { x: Range { start: -40.0, end: 50.0 }, y: Range { start: -50.0, end: 50.0 } }
a.pad_right(10.0);   // => Rect { x: Range { start: -50.0, end: 40.0 }, y: Range { start: -50.0, end: 50.0 } }
a.pad_bottom(10.0);  // => Rect { x: Range { start: -50.0, end: 50.0 }, y: Range { start: -40.0, end: 50.0 } }
a.pad_top(10.0);     // => Rect { x: Range { start: -50.0, end: 50.0 }, y: Range { start: -50.0, end: 40.0 } }
a.pad(10.0);         // => Rect { x: Range { start: -40.0, end: 40.0 }, y: Range { start: -40.0, end: 40.0 } }
let p = Padding { x: Range::new(10.0, 10.0), y: Range::new(10.0, 10.0) };
a.padding(p);        // => Rect { x: Range { start: -40.0, end: 40.0 }, y: Range { start: -40.0, end: 40.0 } }

相対的な範囲を返す

let a = Rect::from_wh(vec2(10.0, 10.0));
a;                            // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -5.0, end: 5.0 } }
a.relative_to_x(10.0);        // => Rect { x: Range { start: -15.0, end: -5.0 }, y: Range { start: -5.0, end: 5.0 } }
a.relative_to_y(10.0);        // => Rect { x: Range { start: -5.0, end: 5.0 }, y: Range { start: -15.0, end: -5.0 } }
a.relative_to([10.0, 10.0]);  // => Rect { x: Range { start: -15.0, end: -5.0 }, y: Range { start: -15.0, end: -5.0 } }

反転

let a = Rect::from_wh(vec2(2.0, 2.0));
a;             // => Rect { x: Range { start: -1.0, end: 1.0 }, y: Range { start: -1.0, end: 1.0 } }
a.invert_x();  // => Rect { x: Range { start: 1.0, end: -1.0 }, y: Range { start: -1.0, end: 1.0 } }
a.invert_y();  // => Rect { x: Range { start: -1.0, end: 1.0 }, y: Range { start: 1.0, end: -1.0 } }

Discussion