Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ impl OverlayContext {
self.render_context.set_line_width(width);
self.render_context.set_stroke_style_str(color);
self.render_context.stroke();
self.render_context.set_line_width(1.);
} else {
self.render_context.set_fill_style_str(color);
self.render_context.fill();
Expand Down
15 changes: 15 additions & 0 deletions editor/src/messages/portfolio/document/utility_types/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,19 @@ pub enum PathSnapSource {
IntersectionPoint,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GradientSnapSource {
Endpoint,
}

impl fmt::Display for GradientSnapSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GradientSnapSource::Endpoint => write!(f, "Gradient: Endpoint"),
}
}
}

impl fmt::Display for PathSnapSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand Down Expand Up @@ -347,6 +360,7 @@ pub enum SnapSource {
Artboard(ArtboardSnapSource),
Path(PathSnapSource),
Alignment(AlignmentSnapSource),
Gradient(GradientSnapSource),
}

impl SnapSource {
Expand Down Expand Up @@ -377,6 +391,7 @@ impl fmt::Display for SnapSource {
SnapSource::Artboard(artboard_snap_source) => write!(f, "{artboard_snap_source}"),
SnapSource::Path(path_snap_source) => write!(f, "{path_snap_source}"),
SnapSource::Alignment(alignment_snap_source) => write!(f, "{alignment_snap_source}"),
SnapSource::Gradient(gradient_snap_source) => write!(f, "{gradient_snap_source}"),
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions editor/src/messages/tool/common_functionality/snapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,23 @@ impl SnapManager {
pub fn update_indicator(&mut self, snapped_point: SnappedPoint) {
self.indicator = snapped_point.is_snapped().then_some(snapped_point);
}

pub fn clear_indicator(&mut self) {
self.indicator = None;
}

pub fn preview_draw(&mut self, snap_data: &SnapData, mouse: DVec2) {
let point = SnapCandidatePoint::handle(snap_data.document.metadata().document_to_viewport.inverse().transform_point2(mouse));
let snapped = self.free_snap(snap_data, &point, SnapTypeConfiguration::default());
self.update_indicator(snapped);
}

pub fn preview_draw_gradient(&mut self, snap_data: &SnapData, mouse: DVec2) {
let point = SnapCandidatePoint::gradient_handle(snap_data.document.metadata().document_to_viewport.inverse().transform_point2(mouse));
let snapped = self.free_snap(snap_data, &point, SnapTypeConfiguration::default());
self.update_indicator(snapped);
}

pub fn indicator_pos(&self) -> Option<DVec2> {
self.indicator.as_ref().map(|point| point.snapped_point_document)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ impl SnapCandidatePoint {
Self::new_source(document_point, SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles))
}

pub fn gradient_handle(document_point: DVec2) -> Self {
Self::new_source(document_point, SnapSource::Gradient(GradientSnapSource::Endpoint))
}

pub fn handle_neighbors(document_point: DVec2, neighbors: impl Into<Vec<DVec2>>) -> Self {
let mut point = Self::new_source(document_point, SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles));
point.neighbors = neighbors.into();
Expand Down
114 changes: 91 additions & 23 deletions editor/src/messages/tool/tool_messages/gradient_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::graph_modification_utils::{NodeGraphLayer, get_gradient};
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType};

#[derive(Default, ExtractField)]
Expand Down Expand Up @@ -215,7 +215,17 @@ impl SelectedGradient {
}
}

pub fn update_gradient(&mut self, mut mouse: DVec2, responses: &mut VecDeque<Message>, snap_rotate: bool, gradient_type: GradientType, drag_start: DVec2) {
#[allow(clippy::too_many_arguments)]
pub fn update_gradient(
&mut self,
mut mouse: DVec2,
responses: &mut VecDeque<Message>,
snap_rotate: bool,
gradient_type: GradientType,
drag_start: DVec2,
snap_data: SnapData,
snap_manager: &mut SnapManager,
) {
if mouse.distance(drag_start) < DRAG_THRESHOLD {
self.gradient = self.initial_gradient.clone();
self.render_gradient(responses);
Expand Down Expand Up @@ -243,22 +253,60 @@ impl SelectedGradient {

let rotated = DVec2::new(length * angle.cos(), length * angle.sin());
mouse = point - rotated;
} else {
// Basic point snapping when not angle-constraining
let document_to_viewport = snap_data.document.metadata().document_to_viewport;
let document_mouse = document_to_viewport.inverse().transform_point2(mouse);
let point_candidate = SnapCandidatePoint::gradient_handle(document_mouse);
let snapped = snap_manager.free_snap(&snap_data, &point_candidate, SnapTypeConfiguration::default());
if snapped.is_snapped() {
mouse = document_to_viewport.transform_point2(snapped.snapped_point_document);
}
snap_manager.update_indicator(snapped);
}

let transformed_mouse = self.transform.inverse().transform_point2(mouse);

match self.dragging {
GradientDragTarget::Start => self.gradient.start = transformed_mouse,
GradientDragTarget::End => self.gradient.end = transformed_mouse,
GradientDragTarget::Start => {
self.gradient.start = transformed_mouse;
}
GradientDragTarget::End => {
self.gradient.end = transformed_mouse;
}
GradientDragTarget::New => {
self.gradient.start = self.transform.inverse().transform_point2(drag_start);
self.gradient.end = transformed_mouse;
}
GradientDragTarget::Step(s) => {
let (start, end) = (self.transform.transform_point2(self.gradient.start), self.transform.transform_point2(self.gradient.end));
let document_to_viewport = snap_data.document.metadata().document_to_viewport;

let (viewport_start, viewport_end) = (self.transform.transform_point2(self.gradient.start), self.transform.transform_point2(self.gradient.end));
let (document_start, document_end) = (
document_to_viewport.inverse().transform_point2(viewport_start),
document_to_viewport.inverse().transform_point2(viewport_end),
);

let constraint = SnapConstraint::Line {
origin: document_start,
direction: document_end - document_start,
};

let document_mouse = document_to_viewport.inverse().transform_point2(mouse);
let point_candidate = SnapCandidatePoint::gradient_handle(document_mouse);

let snapped = snap_manager.constrained_snap(&snap_data, &point_candidate, constraint, SnapTypeConfiguration::default());

let projected_mouse_document = if snapped.is_snapped() {
snapped.snapped_point_document
} else {
constraint.projection(document_mouse)
};
let projected_mouse = document_to_viewport.transform_point2(projected_mouse_document);
snap_manager.update_indicator(snapped);

// Calculate the new position by finding the closest point on the line
let new_pos = ((end - start).angle_to(mouse - start)).cos() * start.distance(mouse) / start.distance(end);
let new_pos = ((viewport_end - viewport_start).angle_to(projected_mouse - viewport_start)).cos() * viewport_start.distance(projected_mouse) / viewport_start.distance(viewport_end);

// Should not go off end but can swap
let clamped = new_pos.clamp(0., 1.);
Expand Down Expand Up @@ -379,6 +427,9 @@ impl Fsm for GradientToolFsmState {
}
}

let snap_data = SnapData::new(document, input, viewport);
tool_data.snap_manager.draw_overlays(snap_data, &mut overlay_context);

self
}
(GradientToolFsmState::Ready { .. }, GradientToolMessage::SelectionChanged) => {
Expand Down Expand Up @@ -483,7 +534,18 @@ impl Fsm for GradientToolFsmState {
self
}
(GradientToolFsmState::Ready { .. }, GradientToolMessage::PointerDown) => {
let mouse = input.mouse.position;
let document_to_viewport = document.metadata().document_to_viewport;

let mut mouse = input.mouse.position;

let snap_data = SnapData::new(document, input, viewport);
let point = SnapCandidatePoint::gradient_handle(document_to_viewport.inverse().transform_point2(mouse));
let snapped = tool_data.snap_manager.free_snap(&snap_data, &point, SnapTypeConfiguration::default());

if snapped.is_snapped() {
mouse = document_to_viewport.transform_point2(snapped.snapped_point_document);
}

tool_data.drag_start = mouse;
let tolerance = (MANIPULATOR_GROUP_MARKER_SIZE * 2.).powi(2);

Expand Down Expand Up @@ -528,29 +590,28 @@ impl Fsm for GradientToolFsmState {
let distance = (end - start).angle_to(mouse - start).sin() * (mouse - start).length();
let projection = ((end - start).angle_to(mouse - start)).cos() * start.distance(mouse) / start.distance(end);

if distance.abs() < SEGMENT_INSERTION_DISTANCE
&& (0. ..=1.).contains(&projection)
&& let Some(index) = gradient.clone().insert_stop(mouse, transform)
{
responses.add(DocumentMessage::StartTransaction);
transaction_started = true;
if distance.abs() < SEGMENT_INSERTION_DISTANCE && (0. ..=1.).contains(&projection) {
let mut new_gradient = gradient.clone();
new_gradient.insert_stop(mouse, transform);

let mut selected_gradient = SelectedGradient::new(new_gradient, layer, document);
selected_gradient.dragging = GradientDragTarget::Step(index);
// No offset when inserting a new stop, it should be exactly under the mouse
selected_gradient.render_gradient(responses);
tool_data.selected_gradient = Some(selected_gradient);
dragging = true;
if let Some(index) = new_gradient.insert_stop(mouse, transform) {
responses.add(DocumentMessage::StartTransaction);
transaction_started = true;

let mut selected_gradient = SelectedGradient::new(new_gradient, layer, document);
selected_gradient.dragging = GradientDragTarget::Step(index);
// No offset when inserting a new stop, it should be exactly under the mouse
selected_gradient.render_gradient(responses);
tool_data.selected_gradient = Some(selected_gradient);
dragging = true;
}
}
}
}

let gradient_state = if dragging {
GradientToolFsmState::Drawing
} else {
let selected_layer = document.click(input, viewport);
let document_mouse = document.metadata().document_to_viewport.inverse().transform_point2(mouse);
let selected_layer = document.click_based_on_position(document_mouse);

// Apply the gradient to the selected layer
if let Some(layer) = selected_layer {
Expand Down Expand Up @@ -592,13 +653,17 @@ impl Fsm for GradientToolFsmState {
}
(GradientToolFsmState::Drawing, GradientToolMessage::PointerMove { constrain_axis }) => {
if let Some(selected_gradient) = &mut tool_data.selected_gradient {
let mouse = input.mouse.position; // tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
let mouse = input.mouse.position;
let snap_data = SnapData::new(document, input, viewport);

selected_gradient.update_gradient(
mouse,
responses,
input.keyboard.get(constrain_axis as usize),
selected_gradient.gradient.gradient_type,
tool_data.drag_start,
snap_data,
&mut tool_data.snap_manager,
);
}

Expand Down Expand Up @@ -662,6 +727,9 @@ impl Fsm for GradientToolFsmState {
}
}

let snap_data = SnapData::new(document, input, viewport);
tool_data.snap_manager.preview_draw_gradient(&snap_data, mouse);

responses.add(OverlaysMessage::Draw);
GradientToolFsmState::Ready { hover_insertion }
}
Expand Down
Loading