SVG import (#1579)

* SVG import

* Fix error

* Transforms

* Code review nits

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2024-01-16 17:42:48 -08:00
committed by GitHub
co-authored by Keavon Chambers
parent 8eef96511e
commit 002151d9c0
22 changed files with 528 additions and 93 deletions
@@ -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)));
}
}