mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 15:28:04 +08:00
Prep gcore splitup: move various symbols into their own modules (#2746)
* move `trait AsU32` from `gcore::vector::misc` to `gcore` * move blending and gradient to their own modules * fix unused warnings * move `Quad`, `Rect` and `BBox` to `gcore::math` * extract `ReferencePoint` and transform nodes from `transform` * move color-related code to `mod color` * fix unused warning in test code * move blending-related nodes and code to `mod blending_nodes` * move ClickTarget code to `mod vector::click_target`
This commit is contained in:
109
node-graph/gcore/src/math/bbox.rs
Normal file
109
node-graph/gcore/src/math/bbox.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Clone, Debug, DynAny)]
|
||||
pub struct AxisAlignedBbox {
|
||||
pub start: DVec2,
|
||||
pub end: DVec2,
|
||||
}
|
||||
|
||||
impl AxisAlignedBbox {
|
||||
pub const ZERO: Self = Self { start: DVec2::ZERO, end: DVec2::ZERO };
|
||||
pub const ONE: Self = Self { start: DVec2::ZERO, end: DVec2::ONE };
|
||||
|
||||
pub fn size(&self) -> DVec2 {
|
||||
self.end - self.start
|
||||
}
|
||||
|
||||
pub fn to_transform(&self) -> DAffine2 {
|
||||
DAffine2::from_translation(self.start) * DAffine2::from_scale(self.size())
|
||||
}
|
||||
|
||||
pub fn contains(&self, point: DVec2) -> bool {
|
||||
point.x >= self.start.x && point.x <= self.end.x && point.y >= self.start.y && point.y <= self.end.y
|
||||
}
|
||||
|
||||
pub fn intersects(&self, other: &AxisAlignedBbox) -> bool {
|
||||
other.start.x <= self.end.x && other.end.x >= self.start.x && other.start.y <= self.end.y && other.end.y >= self.start.y
|
||||
}
|
||||
|
||||
pub fn union(&self, other: &AxisAlignedBbox) -> AxisAlignedBbox {
|
||||
AxisAlignedBbox {
|
||||
start: DVec2::new(self.start.x.min(other.start.x), self.start.y.min(other.start.y)),
|
||||
end: DVec2::new(self.end.x.max(other.end.x), self.end.y.max(other.end.y)),
|
||||
}
|
||||
}
|
||||
pub fn union_non_empty(&self, other: &AxisAlignedBbox) -> Option<AxisAlignedBbox> {
|
||||
match (self.size() == DVec2::ZERO, other.size() == DVec2::ZERO) {
|
||||
(true, true) => None,
|
||||
(true, _) => Some(other.clone()),
|
||||
(_, true) => Some(self.clone()),
|
||||
_ => Some(AxisAlignedBbox {
|
||||
start: DVec2::new(self.start.x.min(other.start.x), self.start.y.min(other.start.y)),
|
||||
end: DVec2::new(self.end.x.max(other.end.x), self.end.y.max(other.end.y)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intersect(&self, other: &AxisAlignedBbox) -> AxisAlignedBbox {
|
||||
AxisAlignedBbox {
|
||||
start: DVec2::new(self.start.x.max(other.start.x), self.start.y.max(other.start.y)),
|
||||
end: DVec2::new(self.end.x.min(other.end.x), self.end.y.min(other.end.y)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(DVec2, DVec2)> for AxisAlignedBbox {
|
||||
fn from((start, end): (DVec2, DVec2)) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Bbox {
|
||||
pub top_left: DVec2,
|
||||
pub top_right: DVec2,
|
||||
pub bottom_left: DVec2,
|
||||
pub bottom_right: DVec2,
|
||||
}
|
||||
|
||||
impl Bbox {
|
||||
pub fn unit() -> Self {
|
||||
Self {
|
||||
top_left: DVec2::new(0., 1.),
|
||||
top_right: DVec2::new(1., 1.),
|
||||
bottom_left: DVec2::new(0., 0.),
|
||||
bottom_right: DVec2::new(1., 0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_transform(transform: DAffine2) -> Self {
|
||||
Self {
|
||||
top_left: transform.transform_point2(DVec2::new(0., 1.)),
|
||||
top_right: transform.transform_point2(DVec2::new(1., 1.)),
|
||||
bottom_left: transform.transform_point2(DVec2::new(0., 0.)),
|
||||
bottom_right: transform.transform_point2(DVec2::new(1., 0.)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn affine_transform(self, transform: DAffine2) -> Self {
|
||||
Self {
|
||||
top_left: transform.transform_point2(self.top_left),
|
||||
top_right: transform.transform_point2(self.top_right),
|
||||
bottom_left: transform.transform_point2(self.bottom_left),
|
||||
bottom_right: transform.transform_point2(self.bottom_right),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_axis_aligned_bbox(&self) -> AxisAlignedBbox {
|
||||
let start_x = self.top_left.x.min(self.top_right.x).min(self.bottom_left.x).min(self.bottom_right.x);
|
||||
let start_y = self.top_left.y.min(self.top_right.y).min(self.bottom_left.y).min(self.bottom_right.y);
|
||||
let end_x = self.top_left.x.max(self.top_right.x).max(self.bottom_left.x).max(self.bottom_right.x);
|
||||
let end_y = self.top_left.y.max(self.top_right.y).max(self.bottom_left.y).max(self.bottom_right.y);
|
||||
|
||||
AxisAlignedBbox {
|
||||
start: DVec2::new(start_x, start_y),
|
||||
end: DVec2::new(end_x, end_y),
|
||||
}
|
||||
}
|
||||
}
|
||||
25
node-graph/gcore/src/math/math_ext.rs
Normal file
25
node-graph/gcore/src/math/math_ext.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use crate::math::quad::Quad;
|
||||
use crate::math::rect::Rect;
|
||||
use bezier_rs::Bezier;
|
||||
|
||||
pub trait QuadExt {
|
||||
/// Get all the edges in the rect as linear bezier curves
|
||||
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_;
|
||||
}
|
||||
|
||||
impl QuadExt for Quad {
|
||||
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_ {
|
||||
self.all_edges().into_iter().map(|[start, end]| Bezier::from_linear_dvec2(start, end))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RectExt {
|
||||
/// Get all the edges in the quad as linear bezier curves
|
||||
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_;
|
||||
}
|
||||
|
||||
impl RectExt for Rect {
|
||||
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_ {
|
||||
self.edges().into_iter().map(|[start, end]| Bezier::from_linear_dvec2(start, end))
|
||||
}
|
||||
}
|
||||
4
node-graph/gcore/src/math/mod.rs
Normal file
4
node-graph/gcore/src/math/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod bbox;
|
||||
pub mod math_ext;
|
||||
pub mod quad;
|
||||
pub mod rect;
|
||||
192
node-graph/gcore/src/math/quad.rs
Normal file
192
node-graph/gcore/src/math/quad.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Debug, Clone, Default, Copy)]
|
||||
/// A quad defined by four vertices. Clockwise from the top left:
|
||||
///
|
||||
/// `top_left`, `top_right`, `bottom_right`, `bottom_left`.
|
||||
pub struct Quad(pub [DVec2; 4]);
|
||||
|
||||
impl Quad {
|
||||
/// Get the top left corner of the quad.
|
||||
pub fn top_left(&self) -> DVec2 {
|
||||
self.0[0]
|
||||
}
|
||||
|
||||
/// Get the top right corner of the quad.
|
||||
pub fn top_right(&self) -> DVec2 {
|
||||
self.0[1]
|
||||
}
|
||||
|
||||
/// Get the bottom right corner of the quad.
|
||||
pub fn bottom_right(&self) -> DVec2 {
|
||||
self.0[2]
|
||||
}
|
||||
|
||||
/// Get the bottom left corner of the quad.
|
||||
pub fn bottom_left(&self) -> DVec2 {
|
||||
self.0[3]
|
||||
}
|
||||
|
||||
/// Create a zero-sized quad at the point.
|
||||
pub fn from_point(point: DVec2) -> Self {
|
||||
Self([point; 4])
|
||||
}
|
||||
|
||||
/// Convert a box defined by two corner points to a quad. The points must be given as `minimum (top left)` then `maximum (bottom right)`.
|
||||
pub fn from_box(bbox: [DVec2; 2]) -> Self {
|
||||
let size = bbox[1] - bbox[0];
|
||||
Self([bbox[0], bbox[0] + size * DVec2::X, bbox[1], bbox[0] + size * DVec2::Y])
|
||||
}
|
||||
|
||||
/// Create a quad from the center and offset (distance from center to middle of an edge)
|
||||
pub fn from_square(center: DVec2, offset: f64) -> Self {
|
||||
Self::from_box([center - offset, center + offset])
|
||||
}
|
||||
|
||||
/// Get all the edges in the quad.
|
||||
pub fn all_edges(&self) -> [[DVec2; 2]; 4] {
|
||||
[[self.0[0], self.0[1]], [self.0[1], self.0[2]], [self.0[2], self.0[3]], [self.0[3], self.0[0]]]
|
||||
}
|
||||
|
||||
/// Get two edges as bases.
|
||||
pub fn edges(&self) -> [[DVec2; 2]; 2] {
|
||||
[[self.0[0], self.0[1]], [self.0[1], self.0[2]]]
|
||||
}
|
||||
|
||||
/// Returns true only if the width and height are both greater than or equal to the given width.
|
||||
pub fn all_sides_at_least_width(&self, width: f64) -> bool {
|
||||
self.edges().into_iter().all(|[a, b]| (a - b).length_squared() >= width.powi(2))
|
||||
}
|
||||
|
||||
/// Generates the axis aligned bounding box of the quad
|
||||
pub fn bounding_box(&self) -> [DVec2; 2] {
|
||||
[
|
||||
self.0.into_iter().reduce(|a, b| a.min(b)).unwrap_or_default(),
|
||||
self.0.into_iter().reduce(|a, b| a.max(b)).unwrap_or_default(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Gets the center of a quad
|
||||
pub fn center(&self) -> DVec2 {
|
||||
self.0.iter().sum::<DVec2>() / 4.
|
||||
}
|
||||
|
||||
/// Take the outside bounds of two axis aligned rectangles, which are defined by two corner points.
|
||||
pub fn combine_bounds(a: [DVec2; 2], b: [DVec2; 2]) -> [DVec2; 2] {
|
||||
[a[0].min(b[0]), a[1].max(b[1])]
|
||||
}
|
||||
|
||||
/// "Clip" bounds of `a` to the limits of `b`.
|
||||
pub fn clip(a: [DVec2; 2], b: [DVec2; 2]) -> [DVec2; 2] {
|
||||
[
|
||||
a[0].max(b[0]), // Constrain min corner
|
||||
a[1].min(b[1]), // Constrain max corner
|
||||
]
|
||||
}
|
||||
|
||||
/// Expand a quad by a certain amount on all sides.
|
||||
///
|
||||
/// Not currently very optimized
|
||||
pub fn inflate(&self, offset: f64) -> Quad {
|
||||
let offset = |index_before, index, index_after| {
|
||||
let [point_before, point, point_after]: [DVec2; 3] = [self.0[index_before], self.0[index], self.0[index_after]];
|
||||
let [line_in, line_out] = [point - point_before, point_after - point];
|
||||
let angle = line_in.angle_to(-line_out);
|
||||
let offset_length = offset / (std::f64::consts::FRAC_PI_2 - angle / 2.).cos();
|
||||
point + (line_in.perp().normalize_or_zero() + line_out.perp().normalize_or_zero()).normalize_or_zero() * offset_length
|
||||
};
|
||||
Self([offset(3, 0, 1), offset(0, 1, 2), offset(1, 2, 3), offset(2, 3, 0)])
|
||||
}
|
||||
|
||||
/// Does this quad contain a point
|
||||
///
|
||||
/// Code from https://wrfranklin.org/Research/Short_Notes/pnpoly.html
|
||||
pub fn contains(&self, p: DVec2) -> bool {
|
||||
let mut inside = false;
|
||||
for (i, j) in (0..4).zip([3, 0, 1, 2]) {
|
||||
if (self.0[i].y > p.y) != (self.0[j].y > p.y) && p.x < ((self.0[j].x - self.0[i].x) * (p.y - self.0[i].y) / (self.0[j].y - self.0[i].y) + self.0[i].x) {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
inside
|
||||
}
|
||||
|
||||
/// https://www.cs.rpi.edu/~cutler/classes/computationalgeometry/F23/lectures/02_line_segment_intersections.pdf
|
||||
fn line_intersection_t(a: DVec2, b: DVec2, c: DVec2, d: DVec2) -> (f64, f64) {
|
||||
let t = ((a.x - c.x) * (c.y - d.y) - (a.y - c.y) * (c.x - d.x)) / ((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x));
|
||||
let u = ((a.x - c.x) * (a.y - b.y) - (a.y - c.y) * (a.x - b.x)) / ((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x));
|
||||
|
||||
(t, u)
|
||||
}
|
||||
|
||||
fn intersect_lines(a: DVec2, b: DVec2, c: DVec2, d: DVec2) -> Option<DVec2> {
|
||||
let (t, u) = Self::line_intersection_t(a, b, c, d);
|
||||
((0. ..=1.).contains(&t) && (0. ..=1.).contains(&u)).then(|| a + t * (b - a))
|
||||
}
|
||||
|
||||
pub fn intersect_rays(a: DVec2, a_direction: DVec2, b: DVec2, b_direction: DVec2) -> Option<DVec2> {
|
||||
let (t, u) = Self::line_intersection_t(a, a + a_direction, b, b + b_direction);
|
||||
(t.is_finite() && u.is_finite()).then(|| a + t * a_direction)
|
||||
}
|
||||
|
||||
pub fn intersects(&self, other: Quad) -> bool {
|
||||
let intersects = self
|
||||
.all_edges()
|
||||
.into_iter()
|
||||
.any(|[a, b]| other.all_edges().into_iter().any(|[c, d]| Self::intersect_lines(a, b, c, d).is_some()));
|
||||
self.contains(other.center()) || other.contains(self.center()) || intersects
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<Quad> for DAffine2 {
|
||||
type Output = Quad;
|
||||
|
||||
fn mul(self, rhs: Quad) -> Self::Output {
|
||||
Quad(rhs.0.map(|point| self.transform_point2(point)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn offset_quad() {
|
||||
fn eq(a: Quad, b: Quad) -> bool {
|
||||
a.0.iter().zip(b.0).all(|(a, b)| a.abs_diff_eq(b, 0.0001))
|
||||
}
|
||||
|
||||
assert!(eq(Quad::from_box([DVec2::ZERO, DVec2::ONE]).inflate(0.5), Quad::from_box([DVec2::splat(-0.5), DVec2::splat(1.5)])));
|
||||
assert!(eq(Quad::from_box([DVec2::ONE, DVec2::ZERO]).inflate(0.5), Quad::from_box([DVec2::splat(1.5), DVec2::splat(-0.5)])));
|
||||
assert!(eq(
|
||||
(DAffine2::from_scale(DVec2::new(-1., 1.)) * Quad::from_box([DVec2::ZERO, DVec2::ONE])).inflate(0.5),
|
||||
DAffine2::from_scale(DVec2::new(-1., 1.)) * Quad::from_box([DVec2::splat(-0.5), DVec2::splat(1.5)])
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn quad_contains() {
|
||||
assert!(Quad::from_box([DVec2::ZERO, DVec2::ONE]).contains(DVec2::splat(0.5)));
|
||||
assert!(Quad::from_box([DVec2::ONE, DVec2::ZERO]).contains(DVec2::splat(0.5)));
|
||||
assert!(Quad::from_box([DVec2::splat(300.), DVec2::splat(500.)]).contains(DVec2::splat(350.)));
|
||||
assert!((DAffine2::from_scale(DVec2::new(-1., 1.)) * Quad::from_box([DVec2::ZERO, DVec2::ONE])).contains(DVec2::new(-0.5, 0.5)));
|
||||
|
||||
assert!(!Quad::from_box([DVec2::ZERO, DVec2::ONE]).contains(DVec2::new(1., 1.1)));
|
||||
assert!(!Quad::from_box([DVec2::ONE, DVec2::ZERO]).contains(DVec2::new(0.5, -0.01)));
|
||||
assert!(!(DAffine2::from_scale(DVec2::new(-1., 1.)) * Quad::from_box([DVec2::ZERO, DVec2::ONE])).contains(DVec2::splat(0.5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intersect_lines() {
|
||||
assert_eq!(
|
||||
Quad::intersect_lines(DVec2::new(-5., 5.), DVec2::new(5., 5.), DVec2::new(2., 7.), DVec2::new(2., 3.)),
|
||||
Some(DVec2::new(2., 5.))
|
||||
);
|
||||
assert_eq!(Quad::intersect_lines(DVec2::new(4., 6.), DVec2::new(4., 5.), DVec2::new(2., 7.), DVec2::new(2., 3.)), None);
|
||||
assert_eq!(Quad::intersect_lines(DVec2::new(-5., 5.), DVec2::new(5., 5.), DVec2::new(2., 7.), DVec2::new(2., 9.)), None);
|
||||
}
|
||||
#[test]
|
||||
fn intersect_quad() {
|
||||
assert!(Quad::from_box([DVec2::ZERO, DVec2::splat(5.)]).intersects(Quad::from_box([DVec2::splat(4.), DVec2::splat(7.)])));
|
||||
assert!(Quad::from_box([DVec2::ZERO, DVec2::splat(5.)]).intersects(Quad::from_box([DVec2::splat(4.), DVec2::splat(4.2)])));
|
||||
assert!(!Quad::from_box([DVec2::ZERO, DVec2::splat(3.)]).intersects(Quad::from_box([DVec2::splat(4.), DVec2::splat(4.2)])));
|
||||
}
|
||||
}
|
||||
119
node-graph/gcore/src/math/rect.rs
Normal file
119
node-graph/gcore/src/math/rect.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use crate::math::quad::Quad;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Debug, Clone, Default, Copy, PartialEq)]
|
||||
/// An axis aligned rect defined by two vertices.
|
||||
pub struct Rect(pub [DVec2; 2]);
|
||||
|
||||
impl Rect {
|
||||
/// Create a zero sized quad at the point
|
||||
#[must_use]
|
||||
pub fn from_point(point: DVec2) -> Self {
|
||||
Self([point; 2])
|
||||
}
|
||||
|
||||
/// Convert a box defined by two corner points to a quad.
|
||||
#[must_use]
|
||||
pub fn from_box(bbox: [DVec2; 2]) -> Self {
|
||||
Self([bbox[0].min(bbox[1]), bbox[0].max(bbox[1])])
|
||||
}
|
||||
|
||||
/// Create a quad from the center and offset (distance from center to middle of an edge)
|
||||
#[must_use]
|
||||
pub fn from_square(center: DVec2, offset: f64) -> Self {
|
||||
Self::from_box([center - offset, center + offset])
|
||||
}
|
||||
|
||||
/// Create an AABB from an iter of points, returning None if empty.
|
||||
#[must_use]
|
||||
pub fn point_iter(points: impl Iterator<Item = DVec2>) -> Option<Self> {
|
||||
let mut bounds = None;
|
||||
for point in points {
|
||||
let bounds = bounds.get_or_insert(Self::from_point(point));
|
||||
bounds[0] = bounds[0].min(point);
|
||||
bounds[1] = bounds[1].max(point);
|
||||
}
|
||||
bounds
|
||||
}
|
||||
|
||||
/// Get all the edges in the rect.
|
||||
#[must_use]
|
||||
pub fn edges(&self) -> [[DVec2; 2]; 4] {
|
||||
let corners = [self[0], DVec2::new(self[0].x, self[1].y), self[1], DVec2::new(self[1].y, self[0].x)];
|
||||
[[corners[0], corners[1]], [corners[1], corners[2]], [corners[2], corners[3]], [corners[3], corners[0]]]
|
||||
}
|
||||
|
||||
/// Gets the center of a rect
|
||||
#[must_use]
|
||||
pub fn center(&self) -> DVec2 {
|
||||
self.0.iter().sum::<DVec2>() / 2.
|
||||
}
|
||||
|
||||
/// Take the outside bounds of two axis aligned rectangles, which are defined by two corner points.
|
||||
#[must_use]
|
||||
pub fn combine_bounds(a: Self, b: Self) -> Self {
|
||||
Self::from_box([a[0].min(b[0]), a[1].max(b[1])])
|
||||
}
|
||||
|
||||
/// Expand a rect by a certain amount on top/bottom and on left/right
|
||||
#[must_use]
|
||||
pub fn expand_by(&self, x: f64, y: f64) -> Self {
|
||||
let delta = DVec2::new(x, y);
|
||||
Self::from_box([self[0] - delta, self[1] + delta])
|
||||
}
|
||||
|
||||
/// Checks if two rects intersect
|
||||
#[must_use]
|
||||
pub fn intersects(&self, other: Self) -> bool {
|
||||
let [mina, maxa] = [self[0].min(self[1]), self[0].max(self[1])];
|
||||
let [minb, maxb] = [other[0].min(other[1]), other[0].max(other[1])];
|
||||
mina.x <= maxb.x && minb.x <= maxa.x && mina.y <= maxb.y && minb.y <= maxa.y
|
||||
}
|
||||
|
||||
/// Does this rect contain a point
|
||||
#[must_use]
|
||||
pub fn contains(&self, p: DVec2) -> bool {
|
||||
(self[0].x < p.x && p.x < self[1].x) && (self[0].y < p.y && p.y < self[1].y)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn min(&self) -> DVec2 {
|
||||
self.0[0].min(self.0[1])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn max(&self) -> DVec2 {
|
||||
self.0[0].max(self.0[1])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn translate(&self, offset: DVec2) -> Self {
|
||||
Self([self.0[0] + offset, self.0[1] + offset])
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<Rect> for DAffine2 {
|
||||
type Output = Quad;
|
||||
|
||||
fn mul(self, rhs: Rect) -> Self::Output {
|
||||
self * Quad::from_box(rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<usize> for Rect {
|
||||
type Output = DVec2;
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
impl std::ops::IndexMut<usize> for Rect {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rect> for Quad {
|
||||
fn from(val: Rect) -> Self {
|
||||
Quad::from_box(val.0)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user