Skip to content

Commit 3b57561

Browse files
committed
add point-handle-gizmo
1 parent 655bc61 commit 3b57561

File tree

7 files changed

+307
-71
lines changed

7 files changed

+307
-71
lines changed

editor/src/messages/tool/common_functionality/snapping.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ impl SnapManager {
250250
self.update_indicator(snapped);
251251
}
252252

253+
pub fn indicator_pos(&self) -> Option<DVec2> {
254+
self.indicator.as_ref().map(|point| point.snapped_point_document)
255+
}
256+
253257
fn find_best_snap(snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: SnapResults, constrained: bool, off_screen: bool, to_path: bool) -> SnappedPoint {
254258
let mut snapped_points = Vec::new();
255259
let document = snap_data.document;

editor/src/messages/tool/common_functionality/snapping/alignment_snapper.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ impl AlignmentSnapper {
7070
if let Some(quad) = target_point.quad.map(|q| q.0) {
7171
if quad[0] == quad[3] && quad[1] == quad[2] && quad[0] == target_point.document_point {
7272
let [p1, p2, ..] = quad;
73-
let direction = (p2 - p1).normalize();
73+
let Some(direction) = (p2 - p1).try_normalize() else {
74+
return;
75+
};
7476
let normal = DVec2::new(-direction.y, direction.x);
7577

7678
for endpoint in [p1, p2] {

editor/src/messages/tool/shapes/line_shape.rs

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -103,34 +103,11 @@ impl Line {
103103
})
104104
.collect::<HashMap<LayerNodeIdentifier, [DVec2; 2]>>();
105105
}
106-
pub fn dragging_endpoints(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, line_data: &mut LineToolData) -> bool {
107-
for (layer, [document_start, document_end]) in line_data.selected_layers_with_position.iter() {
108-
let transform = document.metadata().transform_to_viewport(*layer);
109-
let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
110-
let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
111-
let threshold_x = transform.inverse().transform_vector2(viewport_x).length();
112-
let threshold_y = transform.inverse().transform_vector2(viewport_y).length();
113-
114-
let drag_start = input.mouse.position;
115-
let [start, end] = [document_start, document_end].map(|point| transform.transform_point2(*point));
116-
117-
let start_click = (drag_start.y - start.y).abs() < threshold_y && (drag_start.x - start.x).abs() < threshold_x;
118-
let end_click = (drag_start.y - end.y).abs() < threshold_y && (drag_start.x - end.x).abs() < threshold_x;
119-
120-
if start_click || end_click {
121-
line_data.dragging_endpoint = Some(if end_click { LineEnd::End } else { LineEnd::Start });
122-
line_data.drag_start = if end_click { *document_start } else { *document_end };
123-
line_data.editing_layer = Some(*layer);
124-
return true;
125-
}
126-
}
127-
false
128-
}
129106
}
130107

131108
fn generate_line(tool_data: &mut ShapeToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool) -> [DVec2; 2] {
132109
let document_to_viewport = snap_data.document.metadata().document_to_viewport;
133-
let mut document_points = [tool_data.line_data.drag_start, document_to_viewport.inverse().transform_point2(tool_data.line_data.drag_current)];
110+
let mut document_points = [tool_data.data.drag_start, document_to_viewport.inverse().transform_point2(tool_data.line_data.drag_current)];
134111

135112
let mut angle = -(document_points[1] - document_points[0]).angle_to(DVec2::X);
136113
let mut line_length = (document_points[1] - document_points[0]).length();
@@ -154,8 +131,8 @@ fn generate_line(tool_data: &mut ShapeToolData, snap_data: SnapData, lock_angle:
154131
let constrained = snap_angle || lock_angle;
155132
let snap = &mut tool_data.data.snap_manager;
156133

157-
let near_point = SnapCandidatePoint::handle_neighbors(document_points[1], [tool_data.line_data.drag_start]);
158-
let far_point = SnapCandidatePoint::handle_neighbors(2. * document_points[0] - document_points[1], [tool_data.line_data.drag_start]);
134+
let near_point = SnapCandidatePoint::handle_neighbors(document_points[1], [tool_data.data.drag_start]);
135+
let far_point = SnapCandidatePoint::handle_neighbors(2. * document_points[0] - document_points[1], [tool_data.data.drag_start]);
159136
let config = SnapTypeConfiguration::default();
160137

161138
if constrained {
@@ -191,7 +168,7 @@ fn generate_line(tool_data: &mut ShapeToolData, snap_data: SnapData, lock_angle:
191168
document_points
192169
}
193170

194-
pub fn clicked_on_line_endpoints(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, line_data: &mut LineToolData) -> bool {
171+
pub fn clicked_on_line_endpoints(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, shape_tool_data: &mut ShapeToolData) -> bool {
195172
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line") else {
196173
return false;
197174
};
@@ -213,9 +190,9 @@ pub fn clicked_on_line_endpoints(layer: LayerNodeIdentifier, document: &Document
213190
let end_click = (drag_start.y - end.y).abs() < threshold_y && (drag_start.x - end.x).abs() < threshold_x;
214191

215192
if start_click || end_click {
216-
line_data.dragging_endpoint = Some(if end_click { LineEnd::End } else { LineEnd::Start });
217-
line_data.drag_start = if end_click { document_start } else { document_end };
218-
line_data.editing_layer = Some(layer);
193+
shape_tool_data.line_data.dragging_endpoint = Some(if end_click { LineEnd::End } else { LineEnd::Start });
194+
shape_tool_data.data.drag_start = if end_click { document_start } else { document_end };
195+
shape_tool_data.line_data.editing_layer = Some(layer);
219196
return true;
220197
}
221198
false

editor/src/messages/tool/shapes/star_shape.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1+
use super::line_shape::NodeGraphLayer;
12
use super::shape_utility::{ShapeToolModifierKey, update_radius_sign};
23
use super::*;
34
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
45
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
6+
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
57
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
68
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
79
use crate::messages::tool::common_functionality::graph_modification_utils;
810
use crate::messages::tool::tool_messages::tool_prelude::*;
911
use glam::DAffine2;
1012
use graph_craft::document::NodeInput;
1113
use graph_craft::document::value::TaggedValue;
14+
use graphene_std::vector::PointId;
1215
use std::collections::VecDeque;
1316

1417
#[derive(Default)]
1518
pub struct Star;
1619

20+
#[derive(Clone, Debug, Default)]
21+
pub struct PointRadiusHandle {
22+
pub center: DVec2,
23+
pub vertex: Option<PointId>,
24+
pub index: usize,
25+
}
26+
27+
#[derive(Clone, Debug, Default)]
28+
pub struct StarShapeData {
29+
pub point_radius_handle: PointRadiusHandle,
30+
}
31+
1732
impl Star {
1833
pub fn create_node(vertices: u32) -> NodeTemplate {
1934
let node_type = resolve_document_node_type("Star").expect(" Star node does not exist");
@@ -25,6 +40,122 @@ impl Star {
2540
])
2641
}
2742

43+
pub fn set_point_radius_handle(document: &DocumentMessageHandler, mouse_pos: DVec2, shape_tool_data: &mut ShapeToolData) -> bool {
44+
if let Some((layer, (center, _, vertex, index))) = Self::points_on_inner_circle(document, mouse_pos).iter().next() {
45+
shape_tool_data.data.layer = Some(*layer);
46+
shape_tool_data.star_data.point_radius_handle = PointRadiusHandle {
47+
center: *center,
48+
vertex: Some(*vertex),
49+
index: *index,
50+
};
51+
return true;
52+
}
53+
false
54+
}
55+
56+
pub fn inner_gizmo_overlays(document: &DocumentMessageHandler, shape_tool_data: &mut ShapeToolData, overlay_context: &mut OverlayContext) {
57+
let PointRadiusHandle { center, vertex, .. } = shape_tool_data.star_data.point_radius_handle;
58+
let layer = shape_tool_data.data.layer.unwrap();
59+
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
60+
let viewport = document.metadata().transform_to_viewport(layer);
61+
let vertex_pos = vector_data.point_domain.position_from_id(vertex.unwrap()).unwrap();
62+
Self::draw_point_radius_overlay(center, vertex_pos, viewport, overlay_context);
63+
}
64+
65+
fn draw_point_radius_overlay(center: DVec2, vertex_pos: DVec2, transform: DAffine2, overlay_context: &mut OverlayContext) {
66+
let viewport_center = transform.transform_point2(center);
67+
let viewport_vertex = transform.transform_point2(vertex_pos);
68+
let extension_length = (viewport_vertex - viewport_center).length() * 0.5;
69+
let extension = (viewport_vertex - viewport_center).normalize() * extension_length;
70+
71+
overlay_context.line(viewport_center, viewport_vertex + extension, None, None);
72+
overlay_context.manipulator_handle(viewport_vertex, true, None);
73+
}
74+
// when hovered
75+
pub fn hover_point_radius_handle(document: &DocumentMessageHandler, mouse_pos: DVec2, overlay_context: &mut OverlayContext) -> bool {
76+
for (layer, (center, vertex_pos, _, _)) in Self::points_on_inner_circle(document, mouse_pos) {
77+
let transform = document.metadata().transform_to_viewport(layer);
78+
Self::draw_point_radius_overlay(center, vertex_pos, transform, overlay_context);
79+
return true;
80+
}
81+
82+
for layer in document
83+
.network_interface
84+
.selected_nodes()
85+
.selected_visible_and_unlocked_layers(&document.network_interface)
86+
.filter(|layer| graph_modification_utils::get_star_id(*layer, &document.network_interface).is_some())
87+
{
88+
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
89+
return false;
90+
};
91+
92+
for (_, anchor_positions) in vector_data.point_domain.position_ids() {
93+
let transform = document.metadata().transform_to_viewport(layer);
94+
overlay_context.manipulator_handle(transform.transform_point2(*anchor_positions), false, None);
95+
}
96+
97+
return false;
98+
}
99+
false
100+
}
101+
102+
pub fn points_on_inner_circle(document: &DocumentMessageHandler, mouse_pos: DVec2) -> HashMap<LayerNodeIdentifier, (DVec2, DVec2, PointId, usize)> {
103+
let mut result = HashMap::new();
104+
105+
for layer in document
106+
.network_interface
107+
.selected_nodes()
108+
.selected_visible_and_unlocked_layers(&document.network_interface)
109+
.filter(|layer| graph_modification_utils::get_star_id(*layer, &document.network_interface).is_some())
110+
{
111+
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star") else {
112+
return result;
113+
};
114+
115+
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
116+
return result;
117+
};
118+
119+
let transform = document.network_interface.document_metadata().transform_to_viewport(layer);
120+
let center = DVec2::ZERO;
121+
122+
let (Some(&TaggedValue::F64(outer)), Some(&TaggedValue::F64(inner))) = (node_inputs[2].as_value(), node_inputs[3].as_value()) else {
123+
return result;
124+
};
125+
126+
let mut index = 0;
127+
128+
let inner_point = vector_data.point_domain.position_ids().find(|(_, pos)| {
129+
let transformed = transform.transform_point2(**pos);
130+
if transformed.distance(mouse_pos) >= 5.0 {
131+
return false;
132+
}
133+
134+
let dist = pos.distance(center);
135+
if (dist - inner).abs() < 1e-6 {
136+
index = 3;
137+
138+
true
139+
} else if (dist - outer).abs() < 1e-6 {
140+
index = 2;
141+
log::info!("dist to outer {:?}", (dist - outer).abs());
142+
143+
true
144+
} else {
145+
false
146+
}
147+
});
148+
149+
// Only insert if we found an inner point
150+
if let Some((point_id, vertex_pos)) = inner_point {
151+
result.insert(layer, (center, *vertex_pos, point_id, index));
152+
break;
153+
}
154+
}
155+
156+
result
157+
}
158+
28159
pub fn update_shape(
29160
document: &DocumentMessageHandler,
30161
ipp: &InputPreprocessorMessageHandler,
@@ -74,4 +205,54 @@ impl Star {
74205
}
75206
false
76207
}
208+
209+
pub fn update_inner_radius(
210+
document: &DocumentMessageHandler,
211+
input: &InputPreprocessorMessageHandler,
212+
layer: LayerNodeIdentifier,
213+
responses: &mut VecDeque<Message>,
214+
shape_tool_data: &mut ShapeToolData,
215+
) {
216+
let Some(node_id) = graph_modification_utils::get_star_id(layer, &document.network_interface) else {
217+
return;
218+
};
219+
220+
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
221+
return;
222+
};
223+
224+
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star") else {
225+
return;
226+
};
227+
228+
let path = vector_data.stroke_bezier_paths().next().unwrap();
229+
let center = path.length_centroid(None, true).unwrap();
230+
let transform = document.network_interface.document_metadata().transform_to_viewport(layer);
231+
let index = shape_tool_data.star_data.point_radius_handle.index;
232+
233+
// inner radiust
234+
let Some(&TaggedValue::F64(required_radius)) = node_inputs[index].as_value() else {
235+
return;
236+
};
237+
238+
// update_radius_sign(start, end, layer, document, responses);
239+
240+
let delta = input.mouse.position - shape_tool_data.last_mouse_position;
241+
let radius = document.metadata().document_to_viewport.transform_point2(shape_tool_data.data.drag_start) - transform.transform_point2(center);
242+
let projection = delta.project_onto(radius);
243+
let sign = radius.dot(delta).signum();
244+
245+
let net_delta = projection.length() * sign;
246+
shape_tool_data.last_mouse_position = input.mouse.position;
247+
248+
// overlay_context.line(transform.transform_point2(center), transform.transform_point2(inner + net_delta), None, None);
249+
250+
// We keep the smaller dimension's scale at 1 and scale the other dimension accordingly
251+
252+
responses.add(NodeGraphMessage::SetInput {
253+
input_connector: InputConnector::node(node_id, index),
254+
input: NodeInput::value(TaggedValue::F64(required_radius + net_delta), false),
255+
});
256+
responses.add(NodeGraphMessage::RunDocumentGraph);
257+
}
77258
}

editor/src/messages/tool/tool_message_handler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::common_functionality::shape_editor::ShapeState;
2-
use super::shapes::shape_utility::ShapeType::{Ellipse, Line, Rectangle};
2+
use super::shapes::shape_utility::ShapeType::{self, Ellipse, Line, Rectangle};
33
use super::utility_types::{ToolActionHandlerData, ToolFsmState, tool_message_to_tool_type};
44
use crate::application::generate_uuid;
55
use crate::messages::layout::utility_types::widget_prelude::*;
@@ -65,6 +65,7 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
6565
self.tool_state.tool_data.active_tool_type = ToolType::Shape;
6666
}
6767
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Shape });
68+
responses.add(ShapeToolMessage::SetShape(ShapeType::Convex));
6869
responses.add(ShapeToolMessage::HideShapeTypeWidget(false))
6970
}
7071
ToolMessage::ActivateToolPolygon => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Polygon }),

0 commit comments

Comments
 (0)