Attribute-based vector format refactor (#1624)

* Initial vector format structure

* Click targets

* Code review pass

* Remove subpaths from vector data

* Morph node & vector node tests

* Insignificant change

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube 2024-03-09 18:27:30 +00:00 committed by GitHub
parent c8ea9e05a6
commit 218e9675fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 991 additions and 436 deletions

View file

@ -4,7 +4,6 @@ use crate::messages::prelude::Message;
use bezier_rs::Subpath;
use graphene_core::renderer::Quad;
use graphene_core::uuid::ManipulatorGroupId;
use core::f64::consts::PI;
use glam::{DAffine2, DVec2};
@ -114,7 +113,7 @@ impl OverlayContext {
self.render_context.stroke();
}
pub fn outline<'a>(&mut self, subpaths: impl Iterator<Item = &'a Subpath<ManipulatorGroupId>>, transform: DAffine2) {
pub fn outline<'a, Id: bezier_rs::Identifier>(&mut self, subpaths: impl Iterator<Item = &'a Subpath<Id>>, transform: DAffine2) {
self.render_context.begin_path();
for subpath in subpaths {
let mut curves = subpath.iter().peekable();

View file

@ -4,9 +4,9 @@ use graph_craft::document::{DocumentNode, NodeId, NodeNetwork};
use graphene_core::renderer::ClickTarget;
use graphene_core::renderer::Quad;
use graphene_core::transform::Footprint;
use graphene_core::uuid::ManipulatorGroupId;
use glam::{DAffine2, DVec2};
use graphene_std::vector::PointId;
use std::collections::{HashMap, HashSet};
use std::num::NonZeroU64;
@ -287,7 +287,7 @@ impl DocumentMetadata {
.reduce(Quad::combine_bounds)
}
pub fn layer_outline(&self, layer: LayerNodeIdentifier) -> impl Iterator<Item = &bezier_rs::Subpath<ManipulatorGroupId>> {
pub fn layer_outline(&self, layer: LayerNodeIdentifier) -> impl Iterator<Item = &bezier_rs::Subpath<PointId>> {
static EMPTY: Vec<ClickTarget> = Vec::new();
let click_targets = self.click_targets.get(&layer).unwrap_or(&EMPTY);
click_targets.iter().map(|click_target| &click_target.subpath)

View file

@ -125,7 +125,7 @@ impl ClosestSegment {
}
fn t_min_max(bezier: &Bezier, layer_scale: DVec2) -> (f64, f64) {
let length = bezier.apply_transformation(|point| point * layer_scale).length(Some(100));
let length = bezier.apply_transformation(|point| point * layer_scale).length(None);
let too_close_t = (INSERT_POINT_ON_SEGMENT_TOO_CLOSE_DISTANCE / length).min(0.5);
let t_min_euclidean = too_close_t;
@ -148,7 +148,7 @@ impl ClosestSegment {
// Linear approximation of parametric t-value ranges:
let t_min = self.t_min / self.scale;
let t_max = 1. - ((1. - self.t_max) / self.scale);
let t = self.bezier.project(layer_m_pos, None).max(t_min).min(t_max);
let t = self.bezier.project(layer_m_pos).max(t_min).min(t_max);
self.t = t;
let bezier_point = self.bezier.evaluate(TValue::Parametric(t));
@ -1099,8 +1099,6 @@ impl ShapeState {
let scale = document_metadata.document_to_viewport.decompose_scale().x;
let tolerance = tolerance + 0.5 * scale; // make more talerance at large scale
let lut_size = ((5. + scale) as usize).min(20); // need more precision at large scale
let projection_options = bezier_rs::ProjectionOptions { lut_size, ..Default::default() };
let mut closest = None;
let mut closest_distance_squared: f64 = tolerance * tolerance;
@ -1109,7 +1107,7 @@ impl ShapeState {
for (subpath_index, subpath) in subpaths.iter().enumerate() {
for (manipulator_index, bezier) in subpath.iter().enumerate() {
let t = bezier.project(layer_pos, Some(projection_options));
let t = bezier.project(layer_pos);
let layerspace = bezier.evaluate(TValue::Parametric(t));
let screenspace = transform.transform_point2(layerspace);

View file

@ -172,8 +172,8 @@ impl<'a> SnapData<'a> {
fn ignore_bounds(&self, layer: LayerNodeIdentifier) -> bool {
self.manipulators.iter().any(|&(ignore, _)| ignore == layer)
}
fn ignore_manipulator(&self, layer: LayerNodeIdentifier, manipulator: ManipulatorGroupId) -> bool {
self.manipulators.contains(&(layer, manipulator))
fn ignore_manipulator(&self, layer: LayerNodeIdentifier, manipulator: impl Into<ManipulatorGroupId>) -> bool {
self.manipulators.contains(&(layer, manipulator.into()))
}
}
impl SnapManager {
@ -327,7 +327,7 @@ impl SnapManager {
if let Some(ind) = &self.indicator {
for curve in &ind.curves {
let Some(curve) = curve else { continue };
overlay_context.outline([Subpath::from_bezier(curve)].iter(), to_viewport);
overlay_context.outline::<ManipulatorGroupId>([Subpath::from_bezier(curve)].iter(), to_viewport);
}
if let Some(quad) = ind.target_bounds {
overlay_context.quad(to_viewport * quad);

View file

@ -9,6 +9,7 @@ use bezier_rs::{Bezier, Identifier, Subpath, TValue};
use glam::{DAffine2, DVec2};
use graphene_core::renderer::Quad;
use graphene_core::uuid::ManipulatorGroupId;
use graphene_std::vector::PointId;
#[derive(Clone, Debug, Default)]
pub struct LayerSnapper {
@ -62,7 +63,7 @@ impl LayerSnapper {
for subpath in document.metadata.layer_outline(layer) {
for (start_index, curve) in subpath.iter().enumerate() {
let document_curve = curve.apply_transformation(|p| transform.transform_point2(p));
let start = subpath.manipulator_groups()[start_index].id;
let start = subpath.manipulator_groups()[start_index].id.into();
if snap_data.ignore_manipulator(layer, start) || snap_data.ignore_manipulator(layer, subpath.manipulator_groups()[(start_index + 1) % subpath.len()].id) {
continue;
}
@ -94,7 +95,7 @@ impl LayerSnapper {
if path.document_curve.start.distance_squared(path.document_curve.end) < tolerance * tolerance * 2. {
continue;
}
let time = path.document_curve.project(point.document_point, None);
let time = path.document_curve.project(point.document_point);
let snapped_point_document = path.document_curve.evaluate(bezier_rs::TValue::Parametric(time));
let distance = snapped_point_document.distance(point.document_point);
@ -372,7 +373,7 @@ pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values:
fn handle_not_under(to_document: DAffine2) -> impl Fn(&DVec2) -> bool {
move |&offset: &DVec2| to_document.transform_vector2(offset).length_squared() >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE
}
fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<ManipulatorGroupId>, snap_data: &SnapData, points: &mut Vec<SnapCandidatePoint>, to_document: DAffine2) {
fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<PointId>, snap_data: &SnapData, points: &mut Vec<SnapCandidatePoint>, to_document: DAffine2) {
let document = snap_data.document;
// Midpoints of linear segments
if document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::LineMidpoint)) {
@ -418,7 +419,7 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Mani
}
}
pub fn group_smooth(group: &bezier_rs::ManipulatorGroup<ManipulatorGroupId>, to_document: DAffine2, subpath: &Subpath<ManipulatorGroupId>, index: usize) -> bool {
pub fn group_smooth<Id: bezier_rs::Identifier>(group: &bezier_rs::ManipulatorGroup<Id>, to_document: DAffine2, subpath: &Subpath<Id>, index: usize) -> bool {
let anchor = group.anchor;
let handle_in = group.in_handle.map(|handle| anchor - handle).filter(handle_not_under(to_document));
let handle_out = group.out_handle.map(|handle| handle - anchor).filter(handle_not_under(to_document));