Add basic artboard snapping (#1734)

* Initial vector modify node

* Initial extraction of data from monitor nodes

* Migrate to point id

* Start converting to modify node

* Non destructive spline tool (tout le reste est cassé)

* Fix unconnected modify node

* Fix freehand tool

* Pen tool

* Migrate demo art

* Select points

* Fix the demo artwork

* Fix the X and Y inputs for path tool

* G1 continous toggle

* Delete points

* Fix test

* Insert point

* Improve robustness of handles

* Fix GRS shortcuts on path

* Dragging points

* Fix build

* Preserve opposing handle lengths

* Update demo art and snapping

* Fix polygon tool

* Double click end anchor

* Improve dragging

* Fix text shifting

* Select only connected verts

* Colinear alt

* Cleanup

* Fix imports

* first commit

* changes in SnapData::ignore()

* Bug fixes and cleanup

* Code review

---------

Co-authored-by: 0hypercube <0hypercube@gmail.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Sahil gill
2024-07-05 16:14:47 -07:00
committed by GitHub
co-authored by 0hypercube Keavon Chambers
parent 176ce314c7
commit e66620dc5c
4 changed files with 199 additions and 109 deletions
@@ -1,15 +1,15 @@
use super::*;
use crate::consts::HIDE_HANDLE_DISTANCE;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::{
BoardSnapSource, BoardSnapTarget, BoundingBoxSnapSource, BoundingBoxSnapTarget, GeometrySnapSource, GeometrySnapTarget, SnapSource, SnapTarget,
};
use crate::messages::portfolio::document::utility_types::misc::*;
use crate::messages::prelude::*;
use bezier_rs::{Bezier, Identifier, Subpath, TValue};
use glam::{DAffine2, DVec2};
use graphene_core::renderer::Quad;
use graphene_core::vector::PointId;
use glam::{DAffine2, DVec2};
#[derive(Clone, Debug, Default)]
pub struct LayerSnapper {
points_to_snap: Vec<SnapCandidatePoint>,
@@ -21,13 +21,21 @@ impl LayerSnapper {
if !document.snapping_state.target_enabled(target) {
return;
}
let Some(bounds) = document.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY) else {
return;
let bounds = if document.metadata.is_artboard(layer) {
document.metadata.bounding_box_with_transform(layer, document.metadata.transform_to_document(layer)).map(Quad::from_box)
} else {
document
.metadata
.bounding_box_with_transform(layer, DAffine2::IDENTITY)
.map(|bounds| document.metadata.transform_to_document(layer) * Quad::from_box(bounds))
};
let bounds = document.metadata.transform_to_document(layer) * Quad::from_box(bounds);
let Some(bounds) = bounds else { return };
if bounds.0.iter().any(|point| !point.is_finite()) {
return;
}
for document_curve in bounds.bezier_lines() {
self.paths_to_snap.push(SnapCandidatePath {
document_curve,
@@ -46,7 +54,7 @@ impl LayerSnapper {
self.paths_to_snap.clear();
for layer in document.metadata.all_layers() {
if !document.metadata.is_artboard(layer) {
if !document.metadata.is_artboard(layer) || snap_data.ignore.contains(&layer) {
continue;
}
self.add_layer_bounds(document, layer, SnapTarget::Board(BoardSnapTarget::Edge));
@@ -168,22 +176,16 @@ impl LayerSnapper {
self.points_to_snap.clear();
for layer in document.metadata.all_layers() {
if !document.metadata.is_artboard(layer) {
if !document.metadata.is_artboard(layer) || snap_data.ignore.contains(&layer) {
continue;
}
if document.snapping_state.target_enabled(SnapTarget::Board(BoardSnapTarget::Corner)) {
let Some(bounds) = document.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY) else {
let Some(bounds) = document.metadata.bounding_box_with_transform(layer, document.metadata.transform_to_document(layer)) else {
continue;
};
let quad = document.metadata.transform_to_document(layer) * Quad::from_box(bounds);
let values = BBoxSnapValues {
corner_source: SnapSource::Board(BoardSnapSource::Corner),
corner_target: SnapTarget::Board(BoardSnapTarget::Corner),
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);
get_bbox_points(Quad::from_box(bounds), &mut self.points_to_snap, BBoxSnapValues::ARTBOARD, document);
}
}
for &layer in snap_data.get_candidates() {
@@ -351,6 +353,15 @@ impl BBoxSnapValues {
center_source: SnapSource::BoundingBox(BoundingBoxSnapSource::Center),
center_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::Center),
};
pub const ARTBOARD: Self = Self {
corner_source: SnapSource::Board(BoardSnapSource::Corner),
corner_target: SnapTarget::Board(BoardSnapTarget::Corner),
edge_source: SnapSource::None,
edge_target: SnapTarget::None,
center_source: SnapSource::Board(BoardSnapSource::Center),
center_target: SnapTarget::Board(BoardSnapTarget::Center),
};
}
pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values: BBoxSnapValues, document: &DocumentMessageHandler) {
for index in 0..4 {
@@ -216,6 +216,43 @@ pub fn axis_align_drag(axis_align: bool, position: DVec2, start: DVec2) -> DVec2
}
}
/// Snaps a dragging event from the artboard or select tool
pub fn snap_drag(start: DVec2, current: DVec2, axis_align: bool, snap_data: SnapData, snap_manager: &mut SnapManager, candidates: &Vec<SnapCandidatePoint>) -> DVec2 {
let mouse_position = axis_align_drag(axis_align, snap_data.input.mouse.position, start);
let document = snap_data.document;
let total_mouse_delta_document = document.metadata.document_to_viewport.inverse().transform_vector2(mouse_position - start);
let mouse_delta_document = document.metadata.document_to_viewport.inverse().transform_vector2(mouse_position - current);
let mut offset = mouse_delta_document;
let mut best_snap = SnappedPoint::infinite_snap(document.metadata.document_to_viewport.inverse().transform_point2(mouse_position));
for point in candidates {
let origin = point.document_point + total_mouse_delta_document;
let snapped = if axis_align {
snap_manager.constrained_snap(
&snap_data,
point,
SnapConstraint::Line {
origin,
direction: total_mouse_delta_document.try_normalize().unwrap_or(DVec2::X),
},
None,
)
} else {
snap_manager.free_snap(&snap_data, point, None, false)
};
if best_snap.other_snap_better(&snapped) {
offset = snapped.snapped_point_document - origin + mouse_delta_document;
best_snap = snapped;
}
}
snap_manager.update_indicator(best_snap);
document.metadata.document_to_viewport.transform_vector2(offset)
}
/// Contains info on the overlays for the bounding box and transform handles
#[derive(Clone, Debug, Default)]
pub struct BoundingBoxManager {