mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
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:
parent
c8ea9e05a6
commit
218e9675fd
30 changed files with 991 additions and 436 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue