mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 02:08:11 +08:00
SVG import (#1579)
* SVG import * Fix error * Transforms * Code review nits --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
8eef96511e
commit
002151d9c0
@@ -34,6 +34,19 @@ pub fn new_image_layer(image_frame: ImageFrame<Color>, id: NodeId, parent: Layer
|
||||
LayerNodeIdentifier::new_unchecked(id)
|
||||
}
|
||||
|
||||
/// Create a new group layer from an svg
|
||||
pub fn new_svg_layer(svg: String, transform: glam::DAffine2, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
let insert_index = -1;
|
||||
responses.add(GraphOperationMessage::NewSvg {
|
||||
id,
|
||||
svg,
|
||||
transform,
|
||||
parent,
|
||||
insert_index,
|
||||
});
|
||||
LayerNodeIdentifier::new_unchecked(id)
|
||||
}
|
||||
|
||||
/// Batch set all of the manipulator groups to mirror on a specific layer
|
||||
pub fn set_manipulator_mirror_angle(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>], layer: LayerNodeIdentifier, mirror_angle: bool, responses: &mut VecDeque<Message>) {
|
||||
for manipulator_group in manipulator_groups {
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Resize {
|
||||
let mut points_viewport = [start, mouse];
|
||||
let ignore = if let Some(layer) = self.layer { vec![layer] } else { vec![] };
|
||||
let ratio = input.keyboard.get(lock_ratio as usize);
|
||||
let centre = input.keyboard.get(center as usize);
|
||||
let center = input.keyboard.get(center as usize);
|
||||
let snap_data = SnapData::ignore(document, input, &ignore);
|
||||
if ratio {
|
||||
let size = points_viewport[1] - points_viewport[0];
|
||||
@@ -56,7 +56,7 @@ impl Resize {
|
||||
origin: self.drag_start,
|
||||
direction: end_document - self.drag_start,
|
||||
};
|
||||
if centre {
|
||||
if center {
|
||||
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, None);
|
||||
let far = SnapCandidatePoint::handle(2. * self.drag_start - end_document);
|
||||
let snapped_far = self.snap_manager.constrained_snap(&snap_data, &far, constraint, None);
|
||||
@@ -69,7 +69,7 @@ impl Resize {
|
||||
points_viewport[1] = to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
self.snap_manager.update_indicator(snapped);
|
||||
}
|
||||
} else if centre {
|
||||
} else if center {
|
||||
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), None, false);
|
||||
let snapped_far = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(2. * self.drag_start - document_mouse), None, false);
|
||||
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
|
||||
|
||||
@@ -100,7 +100,7 @@ impl ShapeState {
|
||||
let Some(position) = handle.get_position(&group) else { continue };
|
||||
let mut point = SnapCandidatePoint::new_source(to_document.transform_point2(position) + mouse_delta, source);
|
||||
|
||||
let mut push_neighbour = |group: ManipulatorGroup<ManipulatorGroupId>| {
|
||||
let mut push_neighbor = |group: ManipulatorGroup<ManipulatorGroupId>| {
|
||||
if !state.is_selected(ManipulatorPointId::new(group.id, SelectedType::Anchor)) {
|
||||
point.neighbors.push(to_document.transform_point2(group.anchor));
|
||||
}
|
||||
@@ -108,15 +108,15 @@ impl ShapeState {
|
||||
if handle == SelectedType::Anchor {
|
||||
// Previous anchor (looping if closed)
|
||||
if index > 0 {
|
||||
push_neighbour(subpath.manipulator_groups()[index - 1]);
|
||||
push_neighbor(subpath.manipulator_groups()[index - 1]);
|
||||
} else if subpath.closed() {
|
||||
push_neighbour(subpath.manipulator_groups()[subpath.len() - 1]);
|
||||
push_neighbor(subpath.manipulator_groups()[subpath.len() - 1]);
|
||||
}
|
||||
// Next anchor (looping if closed)
|
||||
if index + 1 < subpath.len() {
|
||||
push_neighbour(subpath.manipulator_groups()[index + 1]);
|
||||
push_neighbor(subpath.manipulator_groups()[index + 1]);
|
||||
} else if subpath.closed() {
|
||||
push_neighbour(subpath.manipulator_groups()[0]);
|
||||
push_neighbor(subpath.manipulator_groups()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ pub enum SnapConstraint {
|
||||
},
|
||||
Direction(DVec2),
|
||||
Circle {
|
||||
centre: DVec2,
|
||||
center: DVec2,
|
||||
radius: f64,
|
||||
},
|
||||
}
|
||||
@@ -39,14 +39,14 @@ impl SnapConstraint {
|
||||
pub fn projection(&self, point: DVec2) -> DVec2 {
|
||||
match *self {
|
||||
Self::Line { origin, direction } if direction != DVec2::ZERO => (point - origin).project_onto(direction) + origin,
|
||||
Self::Circle { centre, radius } => {
|
||||
let from_centre = point - centre;
|
||||
let distance = from_centre.length();
|
||||
Self::Circle { center, radius } => {
|
||||
let from_center = point - center;
|
||||
let distance = from_center.length();
|
||||
if distance > 0. {
|
||||
centre + radius * from_centre / distance
|
||||
center + radius * from_center / distance
|
||||
} else {
|
||||
// Point is exactly at the centre, so project right
|
||||
centre + DVec2::new(radius, 0.)
|
||||
// Point is exactly at the center, so project right
|
||||
center + DVec2::new(radius, 0.)
|
||||
}
|
||||
}
|
||||
_ => point,
|
||||
|
||||
@@ -88,7 +88,12 @@ impl LayerSnapper {
|
||||
let normals = document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Normal));
|
||||
let tangents = document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Tangent));
|
||||
let tolerance = snap_tolerance(document);
|
||||
|
||||
for path in &self.paths_to_snap {
|
||||
// Skip very short paths
|
||||
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 snapped_point_document = path.document_curve.evaluate(bezier_rs::TValue::Parametric(time));
|
||||
|
||||
@@ -120,8 +125,8 @@ impl LayerSnapper {
|
||||
self.collect_paths(snap_data, point.source_index == 0);
|
||||
|
||||
let tolerance = snap_tolerance(document);
|
||||
let constraint_path = if let SnapConstraint::Circle { centre, radius } = constraint {
|
||||
Subpath::new_ellipse(centre - DVec2::splat(radius), centre + DVec2::splat(radius))
|
||||
let constraint_path = if let SnapConstraint::Circle { center, radius } = constraint {
|
||||
Subpath::new_ellipse(center - DVec2::splat(radius), center + DVec2::splat(radius))
|
||||
} else {
|
||||
let constrained_point = constraint.projection(point.document_point);
|
||||
let direction = constraint.direction().normalize_or_zero();
|
||||
@@ -174,8 +179,8 @@ impl LayerSnapper {
|
||||
let values = BBoxSnapValues {
|
||||
corner_source: SnapSource::Board(BoardSnapSource::Corner),
|
||||
corner_target: SnapTarget::Board(BoardSnapTarget::Corner),
|
||||
centre_source: SnapSource::Board(BoardSnapSource::Centre),
|
||||
centre_target: SnapTarget::Board(BoardSnapTarget::Centre),
|
||||
center_source: SnapSource::Board(BoardSnapSource::Center),
|
||||
center_target: SnapTarget::Board(BoardSnapTarget::Center),
|
||||
..Default::default()
|
||||
};
|
||||
get_bbox_points(quad, &mut self.points_to_snap, values, document);
|
||||
@@ -244,8 +249,8 @@ impl LayerSnapper {
|
||||
|
||||
fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool, point: &SnapCandidatePoint, tolerance: f64, snap_results: &mut SnapResults) {
|
||||
if normals && path.bounds.is_none() {
|
||||
for &neighbour in &point.neighbors {
|
||||
for t in path.document_curve.normals_to_point(neighbour) {
|
||||
for &neighbor in &point.neighbors {
|
||||
for t in path.document_curve.normals_to_point(neighbor) {
|
||||
let normal_point = path.document_curve.evaluate(TValue::Parametric(t));
|
||||
let distance = normal_point.distance(point.document_point);
|
||||
if distance > tolerance {
|
||||
@@ -265,8 +270,8 @@ fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool,
|
||||
}
|
||||
}
|
||||
if tangents && path.bounds.is_none() {
|
||||
for &neighbour in &point.neighbors {
|
||||
for t in path.document_curve.tangents_to_point(neighbour) {
|
||||
for &neighbor in &point.neighbors {
|
||||
for t in path.document_curve.tangents_to_point(neighbor) {
|
||||
let tangent_point = path.document_curve.evaluate(TValue::Parametric(t));
|
||||
let distance = tangent_point.distance(point.document_point);
|
||||
if distance > tolerance {
|
||||
@@ -323,9 +328,9 @@ impl SnapCandidatePoint {
|
||||
pub fn handle(document_point: DVec2) -> Self {
|
||||
Self::new_source(document_point, SnapSource::Geometry(GeometrySnapSource::Sharp))
|
||||
}
|
||||
pub fn handle_neighbors(document_point: DVec2, neighbours: impl Into<Vec<DVec2>>) -> Self {
|
||||
pub fn handle_neighbors(document_point: DVec2, neighbors: impl Into<Vec<DVec2>>) -> Self {
|
||||
let mut point = Self::new_source(document_point, SnapSource::Geometry(GeometrySnapSource::Sharp));
|
||||
point.neighbors = neighbours.into();
|
||||
point.neighbors = neighbors.into();
|
||||
point
|
||||
}
|
||||
}
|
||||
@@ -335,8 +340,8 @@ pub struct BBoxSnapValues {
|
||||
corner_target: SnapTarget,
|
||||
edge_source: SnapSource,
|
||||
edge_target: SnapTarget,
|
||||
centre_source: SnapSource,
|
||||
centre_target: SnapTarget,
|
||||
center_source: SnapSource,
|
||||
center_target: SnapTarget,
|
||||
}
|
||||
impl BBoxSnapValues {
|
||||
pub const BOUNDING_BOX: Self = Self {
|
||||
@@ -344,8 +349,8 @@ impl BBoxSnapValues {
|
||||
corner_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::Corner),
|
||||
edge_source: SnapSource::BoundingBox(BoundingBoxSnapSource::EdgeMidpoint),
|
||||
edge_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::EdgeMidpoint),
|
||||
centre_source: SnapSource::BoundingBox(BoundingBoxSnapSource::Centre),
|
||||
centre_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::Centre),
|
||||
center_source: SnapSource::BoundingBox(BoundingBoxSnapSource::Center),
|
||||
center_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::Center),
|
||||
};
|
||||
}
|
||||
pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values: BBoxSnapValues, document: &DocumentMessageHandler) {
|
||||
@@ -359,8 +364,8 @@ pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values:
|
||||
points.push(SnapCandidatePoint::new_quad((start + end) / 2., values.edge_source, values.edge_target, Some(quad)));
|
||||
}
|
||||
}
|
||||
if document.snapping_state.target_enabled(values.centre_target) {
|
||||
points.push(SnapCandidatePoint::new_quad(quad.center(), values.centre_source, values.centre_target, Some(quad)));
|
||||
if document.snapping_state.target_enabled(values.center_target) {
|
||||
points.push(SnapCandidatePoint::new_quad(quad.center(), values.center_source, values.center_target, Some(quad)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,8 +153,8 @@ impl ArtboardToolData {
|
||||
return;
|
||||
};
|
||||
|
||||
let centre = from_center.then_some(bounds.center_of_transformation);
|
||||
let (position, size) = movement.new_size(mouse_position, bounds.transform, centre, constrain_square, None);
|
||||
let center = from_center.then_some(bounds.center_of_transformation);
|
||||
let (position, size) = movement.new_size(mouse_position, bounds.transform, center, constrain_square, None);
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: self.selected_artboard.unwrap().to_node(),
|
||||
location: position.round().as_ivec2(),
|
||||
|
||||
@@ -258,7 +258,7 @@ impl Fsm for LineToolFsmState {
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_transform(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, centre: bool) -> Message {
|
||||
fn generate_transform(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool) -> Message {
|
||||
let document_to_viewport = snap_data.document.metadata.document_to_viewport;
|
||||
let mut document_points = [tool_data.drag_start, document_to_viewport.inverse().transform_point2(tool_data.drag_current)];
|
||||
|
||||
@@ -289,7 +289,7 @@ fn generate_transform(tool_data: &mut LineToolData, snap_data: SnapData, lock_an
|
||||
origin: document_points[0],
|
||||
direction: document_points[1] - document_points[0],
|
||||
};
|
||||
if centre {
|
||||
if center {
|
||||
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, None);
|
||||
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, None);
|
||||
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
|
||||
@@ -301,7 +301,7 @@ fn generate_transform(tool_data: &mut LineToolData, snap_data: SnapData, lock_an
|
||||
document_points[1] = snapped.snapped_point_document;
|
||||
snap.update_indicator(snapped);
|
||||
}
|
||||
} else if centre {
|
||||
} else if center {
|
||||
let snapped = snap.free_snap(&snap_data, &near_point, None, false);
|
||||
let snapped_far = snap.free_snap(&snap_data, &far_point, None, false);
|
||||
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
|
||||
|
||||
@@ -490,12 +490,12 @@ impl PenToolData {
|
||||
}
|
||||
|
||||
/// Snap the angle of the line from relative to position if the key is pressed.
|
||||
fn compute_snapped_angle(&mut self, snap_data: SnapData, transform: DAffine2, lock_angle: bool, snap_angle: bool, mirror: bool, mouse: DVec2, relative: Option<DVec2>, neighbour: bool) -> DVec2 {
|
||||
fn compute_snapped_angle(&mut self, snap_data: SnapData, transform: DAffine2, lock_angle: bool, snap_angle: bool, mirror: bool, mouse: DVec2, relative: Option<DVec2>, neighbor: bool) -> DVec2 {
|
||||
let document = snap_data.document;
|
||||
let mut document_pos = document.metadata.document_to_viewport.inverse().transform_point2(mouse);
|
||||
let snap = &mut self.snap_manager;
|
||||
|
||||
let neighbours = relative.filter(|_| neighbour).map_or(Vec::new(), |neighbour| vec![neighbour]);
|
||||
let neighbors = relative.filter(|_| neighbor).map_or(Vec::new(), |neighbor| vec![neighbor]);
|
||||
|
||||
if let Some(relative) = relative.map(|layer| transform.transform_point2(layer)).filter(|_| snap_angle || lock_angle) {
|
||||
let resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
|
||||
@@ -510,8 +510,8 @@ impl PenToolData {
|
||||
origin: relative,
|
||||
direction: document_pos - relative,
|
||||
};
|
||||
let near_point = SnapCandidatePoint::handle_neighbors(document_pos, neighbours.clone());
|
||||
let far_point = SnapCandidatePoint::handle_neighbors(2. * relative - document_pos, neighbours);
|
||||
let near_point = SnapCandidatePoint::handle_neighbors(document_pos, neighbors.clone());
|
||||
let far_point = SnapCandidatePoint::handle_neighbors(2. * relative - document_pos, neighbors);
|
||||
if mirror {
|
||||
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, None);
|
||||
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, None);
|
||||
@@ -527,8 +527,8 @@ impl PenToolData {
|
||||
snap.update_indicator(snapped);
|
||||
}
|
||||
} else if let Some(relative) = relative.map(|layer| transform.transform_point2(layer)).filter(|_| mirror) {
|
||||
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbours.clone()), None, false);
|
||||
let snapped_far = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(2. * relative - document_pos, neighbours), None, false);
|
||||
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbors.clone()), None, false);
|
||||
let snapped_far = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(2. * relative - document_pos, neighbors), None, false);
|
||||
document_pos = if snapped_far.other_snap_better(&snapped) {
|
||||
snapped.snapped_point_document
|
||||
} else {
|
||||
@@ -536,7 +536,7 @@ impl PenToolData {
|
||||
};
|
||||
snap.update_indicator(if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far });
|
||||
} else {
|
||||
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbours), None, false);
|
||||
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbors), None, false);
|
||||
document_pos = snapped.snapped_point_document;
|
||||
snap.update_indicator(snapped);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use super::tool_prelude::*;
|
||||
use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
|
||||
use crate::consts::{self, ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
@@ -274,7 +274,9 @@ impl SelectToolData {
|
||||
fn get_snap_candidates(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
|
||||
self.snap_candidates.clear();
|
||||
for &layer in &self.layers_dragging {
|
||||
snapping::get_layer_snap_points(layer, &SnapData::new(document, input), &mut self.snap_candidates);
|
||||
if (self.snap_candidates.len() as f64) < document.snapping_state.tolerance {
|
||||
snapping::get_layer_snap_points(layer, &SnapData::new(document, input), &mut self.snap_candidates);
|
||||
}
|
||||
if let Some(bounds) = document.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY) {
|
||||
let quad = document.metadata.transform_to_document(layer) * Quad::from_box(bounds);
|
||||
snapping::get_bbox_points(quad, &mut self.snap_candidates, snapping::BBoxSnapValues::BOUNDING_BOX, document);
|
||||
@@ -642,13 +644,13 @@ impl Fsm for SelectToolFsmState {
|
||||
let (_center, constrain) = (input.keyboard.key(center), input.keyboard.key(axis_align));
|
||||
let center = false; // TODO: Reenable this feature after fixing it
|
||||
|
||||
let centre = center.then_some(bounds.center_of_transformation);
|
||||
let center = center.then_some(bounds.center_of_transformation);
|
||||
let snap = Some(SizeSnapData {
|
||||
manager: &mut tool_data.snap_manager,
|
||||
points: &mut tool_data.snap_candidates,
|
||||
snap_data: SnapData::ignore(document, input, &tool_data.layers_dragging),
|
||||
});
|
||||
let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, centre, constrain, snap);
|
||||
let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center, constrain, snap);
|
||||
let (delta, mut pivot) = movement.bounds_to_scale_transform(position, size);
|
||||
|
||||
let pivot_transform = DAffine2::from_translation(pivot);
|
||||
|
||||
Reference in New Issue
Block a user