Make the transform cage show/hide resize grips as space allows (#2209)

* Changes rotation handles to be around overlay squares

Fixes https://discord.com/channels/731730685944922173/931942323644928040/1330785941786329209

* Fix zero width objects not being selected by slightly nudging the transform

* Follow the categorical limits to render overlay quads

As discussed here: https://discord.com/channels/731730685944922173/931942323644928040/1331166336923074600

* Replace area based calculations with edge based calculations

* Fix 3rd category vis

* Code review

* Add missing powi(2)

* Fixes to handle logic

* Remove single axis prioritisation

* Explicitly check for distance to find nearest handle

* Replace threshold check based on corner vis bounds

* Fix discrepancy at h=12px

* Allow grab when box is too small by disabling resizing within bounds

* Replace inside resize pixel limit

* Code review

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
mTvare
2025-01-30 06:40:40 +05:30
committed by GitHub
parent c5a3c32114
commit a0f8f89e71
10 changed files with 200 additions and 88 deletions

View File

@@ -15,7 +15,7 @@ use bezier_rs::Subpath;
use dyn_any::DynAny;
use base64::Engine;
use glam::{DAffine2, DVec2};
use glam::{DAffine2, DMat2, DVec2};
use num_traits::Zero;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
@@ -60,9 +60,11 @@ impl ClickTarget {
/// Does the click target intersect the path
pub fn intersect_path<It: Iterator<Item = bezier_rs::Bezier>>(&self, mut bezier_iter: impl FnMut() -> It, layer_transform: DAffine2) -> bool {
// Check if the matrix is not invertible
let mut layer_transform = layer_transform;
if layer_transform.matrix2.determinant().abs() <= f64::EPSILON {
return false;
layer_transform.matrix2 += DMat2::IDENTITY * 1e-4; // TODO: Is this the cleanest way to handle this?
}
let inverse = layer_transform.inverse();
let mut bezier_iter = || bezier_iter().map(|bezier| bezier.apply_transformation(|point| inverse.transform_point2(point)));

View File

@@ -1,16 +1,38 @@
use glam::{DAffine2, DVec2};
#[derive(Debug, Clone, Default, Copy)]
/// A quad defined by four vertices.
/// 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 {
/// Create a zero sized quad at the point
/// 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.
/// 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])
@@ -49,8 +71,8 @@ impl Quad {
[a[0].min(b[0]), a[1].max(b[1])]
}
/// "Clip" bounds of 'a' to the limits of 'b'
pub fn constraint_bounds(a: [DVec2; 2], b: [DVec2; 2]) -> [DVec2; 2] {
/// "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
@@ -59,7 +81,7 @@ impl Quad {
/// Expand a quad by a certain amount on all sides.
///
/// Not currently very optimised
/// 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]];