mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
Code review
This commit is contained in:
parent
bb8d8d8f85
commit
6c9fe9c071
2 changed files with 29 additions and 32 deletions
|
@ -15,6 +15,8 @@ use graphene_std::text::{FontCache, load_font};
|
||||||
use graphene_std::vector::{HandleExt, HandleId, ManipulatorPointId, PointId, SegmentId, VectorData, VectorModificationType};
|
use graphene_std::vector::{HandleExt, HandleId, ManipulatorPointId, PointId, SegmentId, VectorData, VectorModificationType};
|
||||||
use kurbo::{CubicBez, Line, ParamCurveExtrema, PathSeg, Point, QuadBez};
|
use kurbo::{CubicBez, Line, ParamCurveExtrema, PathSeg, Point, QuadBez};
|
||||||
|
|
||||||
|
const OPTIMIZATION_SAMPLES: usize = 40;
|
||||||
|
|
||||||
/// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable.
|
/// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable.
|
||||||
pub fn should_extend(
|
pub fn should_extend(
|
||||||
document: &DocumentMessageHandler,
|
document: &DocumentMessageHandler,
|
||||||
|
@ -495,7 +497,7 @@ pub fn transforming_transform_cage(
|
||||||
|
|
||||||
/// Calculates similarity metric between new bezier curve and two old beziers by using sampled points.
|
/// Calculates similarity metric between new bezier curve and two old beziers by using sampled points.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn log_optimization(a: f64, b: f64, p1: DVec2, p3: DVec2, d1: DVec2, d2: DVec2, points1: &[DVec2], n: usize) -> f64 {
|
pub fn log_optimization(a: f64, b: f64, p1: DVec2, p3: DVec2, d1: DVec2, d2: DVec2, points1: &[DVec2], optimization_samples: usize) -> f64 {
|
||||||
let start_handle_length = a.exp();
|
let start_handle_length = a.exp();
|
||||||
let end_handle_length = b.exp();
|
let end_handle_length = b.exp();
|
||||||
|
|
||||||
|
@ -506,40 +508,40 @@ pub fn log_optimization(a: f64, b: f64, p1: DVec2, p3: DVec2, d1: DVec2, d2: DVe
|
||||||
let new_curve = Bezier::from_cubic_coordinates(p1.x, p1.y, c1.x, c1.y, c2.x, c2.y, p3.x, p3.y);
|
let new_curve = Bezier::from_cubic_coordinates(p1.x, p1.y, c1.x, c1.y, c2.x, c2.y, p3.x, p3.y);
|
||||||
|
|
||||||
// Sample 2*n points from new curve and get the L2 metric between all of points
|
// Sample 2*n points from new curve and get the L2 metric between all of points
|
||||||
let points = new_curve.compute_lookup_table(Some(n), None).collect::<Vec<_>>();
|
let points = new_curve.compute_lookup_table(Some(optimization_samples), None).collect::<Vec<_>>();
|
||||||
let dist = points1.iter().zip(points.iter()).map(|(p1, p2)| (p1.x - p2.x).powi(2) + (p1.y - p2.y).powi(2)).sum::<f64>();
|
let dist = points1.iter().zip(points.iter()).map(|(p1, p2)| (p1.x - p2.x).powi(2) + (p1.y - p2.y).powi(2)).sum::<f64>();
|
||||||
|
|
||||||
dist / (n) as f64
|
dist / optimization_samples as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates optimal handle lengths with adam optimization.
|
/// Calculates optimal handle lengths with adam optimization.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn find_two_param_best_approximate(p1: DVec2, p3: DVec2, d1: DVec2, d2: DVec2, min_len1: f64, min_len2: f64, farther_segment: Bezier, other_segment: Bezier) -> (DVec2, DVec2) {
|
pub fn find_two_param_best_approximate(p1: DVec2, p3: DVec2, d1: DVec2, d2: DVec2, min_len1: f64, min_len2: f64, further_segment: Bezier, other_segment: Bezier) -> (DVec2, DVec2) {
|
||||||
let n = 40;
|
let further_segment = if further_segment.start.distance(p1) >= f64::EPSILON {
|
||||||
|
further_segment.reverse()
|
||||||
let farther_segment = if farther_segment.start.distance(p1) >= f64::EPSILON {
|
|
||||||
farther_segment.reverse()
|
|
||||||
} else {
|
} else {
|
||||||
farther_segment
|
further_segment
|
||||||
};
|
};
|
||||||
|
|
||||||
let other_segment = if other_segment.end.distance(p3) >= f64::EPSILON { other_segment.reverse() } else { other_segment };
|
let other_segment = if other_segment.end.distance(p3) >= f64::EPSILON { other_segment.reverse() } else { other_segment };
|
||||||
|
|
||||||
// Now we sample points proportional to the lengths of the beziers
|
// Now we sample points proportional to the lengths of the beziers
|
||||||
let l1 = farther_segment.length(None);
|
let l1 = further_segment.length(None);
|
||||||
let l2 = other_segment.length(None);
|
let l2 = other_segment.length(None);
|
||||||
|
|
||||||
let ratio = l1 / (l1 + l2);
|
let ratio = l1 / (l1 + l2);
|
||||||
let n_points1 = ((n) as f64 * ratio).floor() as usize;
|
|
||||||
let n_points2 = n - n_points1;
|
let n_points1 = (OPTIMIZATION_SAMPLES as f64 * ratio).floor() as usize;
|
||||||
let mut points1 = farther_segment.compute_lookup_table(Some(2), None).collect::<Vec<_>>();
|
let n_points2 = OPTIMIZATION_SAMPLES - n_points1;
|
||||||
|
|
||||||
|
let mut points1 = further_segment.compute_lookup_table(Some(2), None).collect::<Vec<_>>();
|
||||||
let points2 = other_segment.compute_lookup_table(Some(n_points2), None).collect::<Vec<_>>();
|
let points2 = other_segment.compute_lookup_table(Some(n_points2), None).collect::<Vec<_>>();
|
||||||
|
|
||||||
if points2.len() >= 2 {
|
if points2.len() >= 2 {
|
||||||
points1.extend_from_slice(&points2[1..]);
|
points1.extend_from_slice(&points2[1..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let f = |a: f64, b: f64| -> f64 { log_optimization(a, b, p1, p3, d1, d2, &points1, n) };
|
let (a, b) = adam_optimizer(|a: f64, b: f64| -> f64 { log_optimization(a, b, p1, p3, d1, d2, &points1, OPTIMIZATION_SAMPLES) });
|
||||||
|
|
||||||
let (a, b) = adam_optimizer(f);
|
|
||||||
|
|
||||||
let len1 = a.exp().max(min_len1);
|
let len1 = a.exp().max(min_len1);
|
||||||
let len2 = b.exp().max(min_len2);
|
let len2 = b.exp().max(min_len2);
|
||||||
|
@ -568,8 +570,8 @@ pub fn adam_optimizer(f: impl Fn(f64, f64) -> f64) -> (f64, f64) {
|
||||||
let epsilon = 1e-8;
|
let epsilon = 1e-8;
|
||||||
|
|
||||||
for t in 1..=max_iter {
|
for t in 1..=max_iter {
|
||||||
let dfa: f64 = (f(a + h, b) - f(a - h, b)) / (2. * h);
|
let dfa = (f(a + h, b) - f(a - h, b)) / (2. * h);
|
||||||
let dfb: f64 = (f(a, b + h) - f(a, b - h)) / (2. * h);
|
let dfb = (f(a, b + h) - f(a, b - h)) / (2. * h);
|
||||||
|
|
||||||
m_a = beta1 * m_a + (1. - beta1) * dfa;
|
m_a = beta1 * m_a + (1. - beta1) * dfa;
|
||||||
m_b = beta1 * m_b + (1. - beta1) * dfb;
|
m_b = beta1 * m_b + (1. - beta1) * dfb;
|
||||||
|
@ -597,16 +599,14 @@ pub fn adam_optimizer(f: impl Fn(f64, f64) -> f64) -> (f64, f64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_refit_handle_lengths(p1: DVec2, p3: DVec2, beziers: Vec<Bezier>, d1: DVec2, d2: DVec2) -> [DVec2; 2] {
|
pub fn find_refit_handle_lengths(p1: DVec2, p3: DVec2, beziers: Vec<Bezier>, d1: DVec2, d2: DVec2) -> [DVec2; 2] {
|
||||||
let n = 40;
|
let points_per_bezier = OPTIMIZATION_SAMPLES / beziers.len();
|
||||||
|
|
||||||
let points_per_bez = n / beziers.len();
|
let points = if points_per_bezier < 1 {
|
||||||
|
|
||||||
let points = if points_per_bez < 1 {
|
|
||||||
beziers.iter().map(|bezier| bezier.start()).collect::<Vec<_>>()
|
beziers.iter().map(|bezier| bezier.start()).collect::<Vec<_>>()
|
||||||
} else {
|
} else {
|
||||||
let mut points = Vec::new();
|
let mut points = Vec::new();
|
||||||
for bezier in &beziers {
|
for bezier in &beziers {
|
||||||
let lookup = bezier.compute_lookup_table(Some(points_per_bez), None).collect::<Vec<_>>();
|
let lookup = bezier.compute_lookup_table(Some(points_per_bezier), None).collect::<Vec<_>>();
|
||||||
points.extend_from_slice(&lookup[..lookup.len() - 1]);
|
points.extend_from_slice(&lookup[..lookup.len() - 1]);
|
||||||
}
|
}
|
||||||
points
|
points
|
||||||
|
@ -614,7 +614,7 @@ pub fn find_refit_handle_lengths(p1: DVec2, p3: DVec2, beziers: Vec<Bezier>, d1:
|
||||||
|
|
||||||
let limit = points.len();
|
let limit = points.len();
|
||||||
|
|
||||||
let f = |a: f64, b: f64| -> f64 {
|
let (a, b) = adam_optimizer(|a: f64, b: f64| -> f64 {
|
||||||
let start_handle_len = a.exp();
|
let start_handle_len = a.exp();
|
||||||
let end_handle_len = b.exp();
|
let end_handle_len = b.exp();
|
||||||
|
|
||||||
|
@ -626,10 +626,8 @@ pub fn find_refit_handle_lengths(p1: DVec2, p3: DVec2, beziers: Vec<Bezier>, d1:
|
||||||
let new_points = new_curve.compute_lookup_table(Some(limit), None);
|
let new_points = new_curve.compute_lookup_table(Some(limit), None);
|
||||||
let dist = points.iter().zip(new_points).map(|(p1, p2)| (p1.x - p2.x).powi(2) + (p1.y - p2.y).powi(2)).sum::<f64>();
|
let dist = points.iter().zip(new_points).map(|(p1, p2)| (p1.x - p2.x).powi(2) + (p1.y - p2.y).powi(2)).sum::<f64>();
|
||||||
|
|
||||||
dist / (limit) as f64
|
dist / limit as f64
|
||||||
};
|
});
|
||||||
|
|
||||||
let (a, b) = adam_optimizer(f);
|
|
||||||
|
|
||||||
let len1 = a.exp();
|
let len1 = a.exp();
|
||||||
let len2 = b.exp();
|
let len2 = b.exp();
|
||||||
|
|
|
@ -20,7 +20,6 @@ use bezier_rs::{Bezier, TValue};
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::vector::{HandleExt, HandleId, NoHashBuilder, SegmentId, VectorData};
|
use graphene_std::vector::{HandleExt, HandleId, NoHashBuilder, SegmentId, VectorData};
|
||||||
use graphene_std::vector::{ManipulatorPointId, PointId, VectorModificationType};
|
use graphene_std::vector::{ManipulatorPointId, PointId, VectorModificationType};
|
||||||
use std::vec;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PathTool {
|
pub struct PathTool {
|
||||||
|
@ -2063,7 +2062,7 @@ impl Fsm for PathToolFsmState {
|
||||||
// Delete the selected points and clean up overlays
|
// Delete the selected points and clean up overlays
|
||||||
responses.add(DocumentMessage::AddTransaction);
|
responses.add(DocumentMessage::AddTransaction);
|
||||||
shape_editor.delete_selected_points(document, responses, false);
|
shape_editor.delete_selected_points(document, responses, false);
|
||||||
shape_editor.delete_selected_segments(document, responses);
|
shape_editor.delete_selected_segments(document, responses);
|
||||||
responses.add(PathToolMessage::SelectionChanged);
|
responses.add(PathToolMessage::SelectionChanged);
|
||||||
|
|
||||||
PathToolFsmState::Ready
|
PathToolFsmState::Ready
|
||||||
|
@ -2440,9 +2439,9 @@ fn update_dynamic_hints(
|
||||||
let mut delete_selected_hints = vec![HintInfo::keys([Key::Delete], "Delete Selected")];
|
let mut delete_selected_hints = vec![HintInfo::keys([Key::Delete], "Delete Selected")];
|
||||||
|
|
||||||
if at_least_one_anchor_selected {
|
if at_least_one_anchor_selected {
|
||||||
delete_selected_hints.push(HintInfo::keys([Key::Accel], "No Dissolve").prepend_plus());
|
delete_selected_hints.push(HintInfo::keys([Key::Accel], "With Segments").prepend_plus());
|
||||||
delete_selected_hints.push(HintInfo::keys([Key::Alt], "Cut Anchor").prepend_plus());
|
|
||||||
delete_selected_hints.push(HintInfo::keys([Key::Shift], "Re-Fit").prepend_plus());
|
delete_selected_hints.push(HintInfo::keys([Key::Shift], "Re-Fit").prepend_plus());
|
||||||
|
delete_selected_hints.push(HintInfo::keys([Key::Alt], "Cut Anchor").prepend_plus());
|
||||||
}
|
}
|
||||||
|
|
||||||
if single_colinear_anchor_selected {
|
if single_colinear_anchor_selected {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue