use std::ops::Mul; use glam::{DAffine2, DVec2}; use kurbo::{BezPath, Line, PathSeg, Point, Shape}; #[derive(Debug, Clone, Default, Copy)] pub struct Quad([DVec2; 4]); impl Quad { 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]) } pub fn lines(&self) -> [Line; 4] { [ Line::new(to_point(self.0[0]), to_point(self.0[1])), Line::new(to_point(self.0[1]), to_point(self.0[2])), Line::new(to_point(self.0[2]), to_point(self.0[3])), Line::new(to_point(self.0[3]), to_point(self.0[0])), ] } pub fn path(&self) -> BezPath { let mut path = kurbo::BezPath::new(); path.move_to(to_point(self.0[0])); path.line_to(to_point(self.0[1])); path.line_to(to_point(self.0[2])); path.line_to(to_point(self.0[3])); path.close_path(); path } } impl Mul for DAffine2 { type Output = Quad; fn mul(self, rhs: Quad) -> Self::Output { let mut output = Quad::default(); for (i, point) in rhs.0.iter().enumerate() { output.0[i] = self.transform_point2(*point); } output } } fn to_point(vec: DVec2) -> Point { Point::new(vec.x, vec.y) } pub fn intersect_quad_bez_path(quad: Quad, shape: &BezPath, closed: bool) -> bool { // check if outlines intersect if shape.segments().any(|path_segment| quad.lines().iter().any(|line| !path_segment.intersect_line(*line).is_empty())) { return true; } // check if selection is entirely within the shape if closed && shape.contains(to_point(quad.0[0])) { return true; } // check if shape is entirely within selection get_arbitrary_point_on_path(shape).map(|shape_point| quad.path().contains(shape_point)).unwrap_or_default() } pub fn get_arbitrary_point_on_path(path: &BezPath) -> Option { path.segments().next().map(|seg| match seg { PathSeg::Line(line) => line.p0, PathSeg::Quad(quad) => quad.p0, PathSeg::Cubic(cubic) => cubic.p0, }) }