Skip to content

Commit

Permalink
Merge branch 'master' into context
Browse files Browse the repository at this point in the history
  • Loading branch information
Keavon committed Jan 31, 2025
2 parents a5e3279 + 6802365 commit 6c02b86
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 81 deletions.
1 change: 1 addition & 0 deletions editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub const DEFAULT_STROKE_WIDTH: f64 = 2.;

// SELECT TOOL
pub const SELECTION_TOLERANCE: f64 = 5.;
pub const DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD: f64 = 15.;
pub const SELECTION_DRAG_ANGLE: f64 = 90.;
pub const PIVOT_CROSSHAIR_THICKNESS: f64 = 1.;
pub const PIVOT_CROSSHAIR_LENGTH: f64 = 9.;
Expand Down
10 changes: 5 additions & 5 deletions editor/src/messages/input_mapper/input_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ pub fn input_mappings() -> Mapping {
//
// SelectToolMessage
entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=SelectToolMessage::PointerMove(SelectToolPointerKeys { axis_align: Shift, snap_angle: Control, center: Alt, duplicate: Alt })),
entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { extend_selection: Shift, select_deepest: Accel }),
entry!(KeyUp(MouseLeft); action_dispatch=SelectToolMessage::DragStop { remove_from_selection: Shift, negative_box_selection: Control }),
entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { extend_selection: Shift, remove_from_selection: Alt, select_deepest: Accel, lasso_select: Control }),
entry!(KeyUp(MouseLeft); action_dispatch=SelectToolMessage::DragStop { remove_from_selection: Alt }),
entry!(KeyDown(Enter); action_dispatch=SelectToolMessage::Enter),
entry!(DoubleClick(MouseButton::Left); action_dispatch=SelectToolMessage::EditLayer),
entry!(KeyDown(MouseRight); action_dispatch=SelectToolMessage::Abort),
Expand Down Expand Up @@ -213,7 +213,7 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(Delete); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath),
entry!(KeyDown(Backspace); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath),
entry!(KeyDown(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles),
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift }),
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift, lasso_select: Control }),
entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick),
entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape),
entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }),
Expand All @@ -224,8 +224,8 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors),
entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints),
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
entry!(KeyUp(MouseLeft); action_dispatch=PathToolMessage::DragStop { extend_selection: Shift }),
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { extend_selection: Shift }),
entry!(KeyUp(MouseLeft); action_dispatch=PathToolMessage::DragStop { extend_selection: Shift, shrink_selection: Alt }),
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { extend_selection: Shift, shrink_selection: Alt }),
entry!(DoubleClick(MouseButton::Left); action_dispatch=PathToolMessage::FlipSmoothSharp),
entry!(KeyDown(ArrowRight); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: 0. }),
entry!(KeyDown(ArrowRight); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: 0. }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::ToolType;
use crate::node_graph_executor::NodeGraphExecutor;

use bezier_rs::Subpath;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeNetwork, OldNodeNetwork};
use graphene_core::raster::image::ImageFrame;
use graphene_core::raster::BlendMode;
use graphene_core::vector::style::ViewMode;
use graphene_std::renderer::{ClickTarget, Quad};
use graphene_std::vector::path_bool_lib;
use graphene_std::vector::{path_bool_lib, PointId};

use glam::{DAffine2, DVec2, IVec2};

Expand Down Expand Up @@ -1405,6 +1406,19 @@ impl DocumentMessageHandler {
self.intersect_quad(viewport_quad, ipp).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
}

/// Runs an intersection test with all layers and a viewport space subpath
pub fn intersect_polygon<'a>(&'a self, mut viewport_polygon: Subpath<PointId>, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
viewport_polygon.apply_transform(document_to_viewport.inverse());

ClickXRayIter::new(&self.network_interface, XRayTarget::Polygon(viewport_polygon))
}

/// Runs an intersection test with all layers and a viewport space subpath; ignoring artboards
pub fn intersect_polygon_no_artboards<'a>(&'a self, viewport_polygon: Subpath<PointId>, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
self.intersect_polygon(viewport_polygon, ipp).filter(|layer| !self.network_interface.is_artboard(&layer.to_node(), &[]))
}

pub fn is_layer_fully_inside(&self, layer: &LayerNodeIdentifier, quad: graphene_core::renderer::Quad) -> bool {
// Get the bounding box of the layer in document space
let Some(bounding_box) = self.metadata().bounding_box_viewport(*layer) else { return false };
Expand All @@ -1428,6 +1442,22 @@ impl DocumentMessageHandler {
layer_left >= quad_left && layer_right <= quad_right && layer_top <= quad_top && layer_bottom >= quad_bottom
}

pub fn is_layer_fully_inside_polygon(&self, layer: &LayerNodeIdentifier, ipp: &InputPreprocessorMessageHandler, mut viewport_polygon: Subpath<PointId>) -> bool {
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
viewport_polygon.apply_transform(document_to_viewport.inverse());

let layer_click_targets = self.network_interface.document_metadata().click_targets(*layer);
let layer_transform = self.network_interface.document_metadata().transform_to_document(*layer);

layer_click_targets.is_some_and(|targets| {
targets.iter().all(|target| {
let mut subpath = target.subpath().clone();
subpath.apply_transform(layer_transform);
subpath.is_inside_subpath(&viewport_polygon, None, None)
})
})
}

/// Find all of the layers that were clicked on from a viewport space location
pub fn click_xray(&self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
Expand Down Expand Up @@ -2172,6 +2202,7 @@ enum XRayTarget {
Point(DVec2),
Quad(Quad),
Path(Vec<path_bool_lib::PathSegment>),
Polygon(Subpath<PointId>),
}

/// The result for the [`ClickXRayIter`] on the layer
Expand Down Expand Up @@ -2275,6 +2306,10 @@ impl<'a> ClickXRayIter<'a> {
}
XRayTarget::Quad(quad) => self.check_layer_area_target(click_targets, clip, layer, quad_to_path_lib_segments(*quad), transform),
XRayTarget::Path(path) => self.check_layer_area_target(click_targets, clip, layer, path.clone(), transform),
XRayTarget::Polygon(polygon) => {
let polygon = polygon.iter_closed().map(|line| path_bool_lib::PathSegment::Line(line.start, line.end)).collect();
self.check_layer_area_target(click_targets, clip, layer, polygon, transform)
}
}
}
}
Expand Down
20 changes: 16 additions & 4 deletions editor/src/messages/portfolio/document/overlays/utility_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,22 @@ impl core::hash::Hash for OverlayContext {

impl OverlayContext {
pub fn quad(&mut self, quad: Quad, color_fill: Option<&str>) {
self.dashed_quad(quad, color_fill, None, None, None);
self.dashed_polygon(&quad.0, color_fill, None, None, None);
}

pub fn dashed_quad(&mut self, quad: Quad, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
self.dashed_polygon(&quad.0, color_fill, dash_width, dash_gap_width, dash_offset);
}

pub fn polygon(&mut self, polygon: &[DVec2], color_fill: Option<&str>) {
self.dashed_polygon(&polygon, color_fill, None, None, None);
}

pub fn dashed_polygon(&mut self, polygon: &[DVec2], color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
if polygon.len() < 2 {
return;
}

self.start_dpi_aware_transform();

// Set the dash pattern
Expand All @@ -63,10 +75,10 @@ impl OverlayContext {
}

self.render_context.begin_path();
self.render_context.move_to(quad.0[3].x.round() - 0.5, quad.0[3].y.round() - 0.5);
self.render_context.move_to(polygon.last().unwrap().x.round() - 0.5, polygon.last().unwrap().y.round() - 0.5);

for i in 0..4 {
self.render_context.line_to(quad.0[i].x.round() - 0.5, quad.0[i].y.round() - 0.5);
for point in polygon {
self.render_context.line_to(point.x.round() - 0.5, point.y.round() - 0.5);
}

if let Some(color_fill) = color_fill {
Expand Down
65 changes: 58 additions & 7 deletions editor/src/messages/tool/common_functionality/shape_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,32 @@ use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
use crate::messages::tool::tool_messages::path_tool::PointSelectState;

use bezier_rs::{Bezier, BezierHandles, TValue};
use bezier_rs::{Bezier, BezierHandles, Subpath, TValue};
use graphene_core::transform::Transform;
use graphene_core::vector::{ManipulatorPointId, PointId, VectorData, VectorModificationType};

use glam::{DAffine2, DVec2};
use graphene_std::vector::{HandleId, SegmentId};

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum SelectionChange {
Clear,
Extend,
Shrink,
}

#[derive(Clone, Copy, Debug)]
pub enum SelectionShape<'a> {
Box([DVec2; 2]),
Lasso(&'a Vec<DVec2>),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SelectionShapeType {
Box,
Lasso,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
pub enum ManipulatorAngle {
#[default]
Expand Down Expand Up @@ -1394,9 +1413,9 @@ impl ShapeState {
false
}

pub fn select_all_in_quad(&mut self, network_interface: &NodeNetworkInterface, quad: [DVec2; 2], clear_selection: bool) {
pub fn select_all_in_shape(&mut self, network_interface: &NodeNetworkInterface, selection_shape: SelectionShape, selection_change: SelectionChange) {
for (&layer, state) in &mut self.selected_shape_state {
if clear_selection {
if selection_change == SelectionChange::Clear {
state.clear_points()
}

Expand All @@ -1413,22 +1432,54 @@ impl ShapeState {
assert!(vector_data.point_domain.ids().contains(&end));
}

let polygon_subpath = if let SelectionShape::Lasso(polygon) = selection_shape {
if polygon.len() < 2 {
return;
}
let polygon: Subpath<PointId> = Subpath::from_anchors_linear(polygon.to_vec(), true);
Some(polygon)
} else {
None
};

for (id, bezier, _, _) in vector_data.segment_bezier_iter() {
for (position, id) in [(bezier.handle_start(), ManipulatorPointId::PrimaryHandle(id)), (bezier.handle_end(), ManipulatorPointId::EndHandle(id))] {
let Some(position) = position else { continue };
let transformed_position = transform.transform_point2(position);

if quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all() {
state.select_point(id);
let select = match selection_shape {
SelectionShape::Box(quad) => quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all(),
SelectionShape::Lasso(_) => polygon_subpath
.as_ref()
.expect("If `selection_shape` is a polygon then subpath is constructed beforehand.")
.contains_point(transformed_position),
};

if select {
match selection_change {
SelectionChange::Shrink => state.deselect_point(id),
_ => state.select_point(id),
}
}
}
}

for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
let transformed_position = transform.transform_point2(position);

if quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all() {
state.select_point(ManipulatorPointId::Anchor(id));
let select = match selection_shape {
SelectionShape::Box(quad) => quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all(),
SelectionShape::Lasso(_) => polygon_subpath
.as_ref()
.expect("If `selection_shape` is a polygon then subpath is constructed beforehand.")
.contains_point(transformed_position),
};

if select {
match selection_change {
SelectionChange::Shrink => state.deselect_point(ManipulatorPointId::Anchor(id)),
_ => state.select_point(ManipulatorPointId::Anchor(id)),
}
}
}
}
Expand Down
Loading

0 comments on commit 6c02b86

Please sign in to comment.