Improve the Shape tool's arrow drawing controls (#3650)
* fixed the arrow's parameters Signed-off-by: krVatsal <kumarvatsal34@gmail.com> * shifted the arrow's origin to its tail Signed-off-by: krVatsal <kumarvatsal34@gmail.com> * modified arrow shapetype fucntion to be like other shapes * fixed rust formatting * Remove misleading part of comment referencing the origin --------- Signed-off-by: krVatsal <kumarvatsal34@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
87739ff877
commit
82f7dc7062
|
|
@ -15,16 +15,16 @@ use std::collections::VecDeque;
|
||||||
pub struct Arrow;
|
pub struct Arrow;
|
||||||
|
|
||||||
impl Arrow {
|
impl Arrow {
|
||||||
pub fn create_node(document: &DocumentMessageHandler, drag_start: DVec2) -> NodeTemplate {
|
pub fn create_node(document: &DocumentMessageHandler, drag_start: DVec2, shaft_width: f64, head_width: f64, head_length: f64) -> NodeTemplate {
|
||||||
let node_type = resolve_proto_node_type(graphene_std::vector_nodes::arrow::IDENTIFIER).expect("Arrow node does not exist");
|
let node_type = resolve_proto_node_type(graphene_std::vector_nodes::arrow::IDENTIFIER).expect("Arrow node does not exist");
|
||||||
let viewport_pos = document.metadata().document_to_viewport.transform_point2(drag_start);
|
let viewport_pos = document.metadata().document_to_viewport.transform_point2(drag_start);
|
||||||
node_type.node_template_input_override([
|
node_type.node_template_input_override([
|
||||||
None,
|
None,
|
||||||
Some(NodeInput::value(TaggedValue::DVec2(viewport_pos), false)), // start
|
Some(NodeInput::value(TaggedValue::DVec2(viewport_pos), false)), // start
|
||||||
Some(NodeInput::value(TaggedValue::DVec2(viewport_pos), false)), // end
|
Some(NodeInput::value(TaggedValue::DVec2(viewport_pos), false)), // end
|
||||||
Some(NodeInput::value(TaggedValue::F64(10.), false)), // shaft_width
|
Some(NodeInput::value(TaggedValue::F64(shaft_width), false)), // shaft_width
|
||||||
Some(NodeInput::value(TaggedValue::F64(30.), false)), // head_width
|
Some(NodeInput::value(TaggedValue::F64(head_width), false)), // head_width
|
||||||
Some(NodeInput::value(TaggedValue::F64(20.), false)), // head_length
|
Some(NodeInput::value(TaggedValue::F64(head_length), false)), // head_length
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,12 +56,7 @@ impl Arrow {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate proportional dimensions based on arrow length
|
// Update Arrow node start and end points with document space coordinates
|
||||||
let shaft_width = length_document * 0.1;
|
|
||||||
let head_width = length_document * 0.3;
|
|
||||||
let head_length = length_document * 0.2;
|
|
||||||
|
|
||||||
// Update Arrow node parameters with document space coordinates (like Line tool)
|
|
||||||
responses.add(NodeGraphMessage::SetInput {
|
responses.add(NodeGraphMessage::SetInput {
|
||||||
input_connector: InputConnector::node(node_id, 1),
|
input_connector: InputConnector::node(node_id, 1),
|
||||||
input: NodeInput::value(TaggedValue::DVec2(start_document), false),
|
input: NodeInput::value(TaggedValue::DVec2(start_document), false),
|
||||||
|
|
@ -70,18 +65,6 @@ impl Arrow {
|
||||||
input_connector: InputConnector::node(node_id, 2),
|
input_connector: InputConnector::node(node_id, 2),
|
||||||
input: NodeInput::value(TaggedValue::DVec2(end_document), false),
|
input: NodeInput::value(TaggedValue::DVec2(end_document), false),
|
||||||
});
|
});
|
||||||
responses.add(NodeGraphMessage::SetInput {
|
|
||||||
input_connector: InputConnector::node(node_id, 3),
|
|
||||||
input: NodeInput::value(TaggedValue::F64(shaft_width), false),
|
|
||||||
});
|
|
||||||
responses.add(NodeGraphMessage::SetInput {
|
|
||||||
input_connector: InputConnector::node(node_id, 4),
|
|
||||||
input: NodeInput::value(TaggedValue::F64(head_width), false),
|
|
||||||
});
|
|
||||||
responses.add(NodeGraphMessage::SetInput {
|
|
||||||
input_connector: InputConnector::node(node_id, 5),
|
|
||||||
input: NodeInput::value(TaggedValue::F64(head_length), false),
|
|
||||||
});
|
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ pub struct ShapeToolOptions {
|
||||||
grid_type: GridType,
|
grid_type: GridType,
|
||||||
spiral_type: SpiralType,
|
spiral_type: SpiralType,
|
||||||
turns: f64,
|
turns: f64,
|
||||||
|
arrow_shaft_width: f64,
|
||||||
|
arrow_head_width: f64,
|
||||||
|
arrow_head_length: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ShapeToolOptions {
|
impl Default for ShapeToolOptions {
|
||||||
|
|
@ -58,6 +61,9 @@ impl Default for ShapeToolOptions {
|
||||||
spiral_type: SpiralType::Archimedean,
|
spiral_type: SpiralType::Archimedean,
|
||||||
turns: 5.,
|
turns: 5.,
|
||||||
grid_type: GridType::Rectangular,
|
grid_type: GridType::Rectangular,
|
||||||
|
arrow_shaft_width: 14.,
|
||||||
|
arrow_head_width: 32.,
|
||||||
|
arrow_head_length: 28.,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +82,9 @@ pub enum ShapeOptionsUpdate {
|
||||||
SpiralType(SpiralType),
|
SpiralType(SpiralType),
|
||||||
Turns(f64),
|
Turns(f64),
|
||||||
GridType(GridType),
|
GridType(GridType),
|
||||||
|
ArrowShaftWidth(f64),
|
||||||
|
ArrowHeadWidth(f64),
|
||||||
|
ArrowHeadLength(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[impl_message(Message, ToolMessage, Shape)]
|
#[impl_message(Message, ToolMessage, Shape)]
|
||||||
|
|
@ -236,6 +245,51 @@ fn create_weight_widget(line_weight: f64) -> WidgetInstance {
|
||||||
.widget_instance()
|
.widget_instance()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_arrow_shaft_width_widget(shaft_width: f64) -> WidgetInstance {
|
||||||
|
NumberInput::new(Some(shaft_width))
|
||||||
|
.unit(" px")
|
||||||
|
.label("Shaft")
|
||||||
|
.min(0.1)
|
||||||
|
.max(1000.)
|
||||||
|
.on_update(|number_input: &NumberInput| {
|
||||||
|
ShapeToolMessage::UpdateOptions {
|
||||||
|
options: ShapeOptionsUpdate::ArrowShaftWidth(number_input.value.unwrap()),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.widget_instance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_arrow_head_width_widget(head_width: f64) -> WidgetInstance {
|
||||||
|
NumberInput::new(Some(head_width))
|
||||||
|
.unit(" px")
|
||||||
|
.label("Head W")
|
||||||
|
.min(0.1)
|
||||||
|
.max(1000.)
|
||||||
|
.on_update(|number_input: &NumberInput| {
|
||||||
|
ShapeToolMessage::UpdateOptions {
|
||||||
|
options: ShapeOptionsUpdate::ArrowHeadWidth(number_input.value.unwrap()),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.widget_instance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_arrow_head_length_widget(head_length: f64) -> WidgetInstance {
|
||||||
|
NumberInput::new(Some(head_length))
|
||||||
|
.unit(" px")
|
||||||
|
.label("Head L")
|
||||||
|
.min(0.1)
|
||||||
|
.max(1000.)
|
||||||
|
.on_update(|number_input: &NumberInput| {
|
||||||
|
ShapeToolMessage::UpdateOptions {
|
||||||
|
options: ShapeOptionsUpdate::ArrowHeadLength(number_input.value.unwrap()),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.widget_instance()
|
||||||
|
}
|
||||||
|
|
||||||
fn create_spiral_type_widget(spiral_type: SpiralType) -> WidgetInstance {
|
fn create_spiral_type_widget(spiral_type: SpiralType) -> WidgetInstance {
|
||||||
let entries = vec![vec![
|
let entries = vec![vec![
|
||||||
MenuListEntry::new("Archimedean").label("Archimedean").on_commit(move |_| {
|
MenuListEntry::new("Archimedean").label("Archimedean").on_commit(move |_| {
|
||||||
|
|
@ -304,6 +358,15 @@ impl LayoutHolder for ShapeTool {
|
||||||
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.options.shape_type == ShapeType::Arrow {
|
||||||
|
widgets.push(create_arrow_shaft_width_widget(self.options.arrow_shaft_width));
|
||||||
|
widgets.push(Separator::new(SeparatorStyle::Related).widget_instance());
|
||||||
|
widgets.push(create_arrow_head_width_widget(self.options.arrow_head_width));
|
||||||
|
widgets.push(Separator::new(SeparatorStyle::Related).widget_instance());
|
||||||
|
widgets.push(create_arrow_head_length_widget(self.options.arrow_head_length));
|
||||||
|
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||||
|
}
|
||||||
|
|
||||||
if self.options.shape_type != ShapeType::Line {
|
if self.options.shape_type != ShapeType::Line {
|
||||||
widgets.append(&mut self.options.fill.create_widgets(
|
widgets.append(&mut self.options.fill.create_widgets(
|
||||||
"Fill",
|
"Fill",
|
||||||
|
|
@ -414,6 +477,15 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Shap
|
||||||
ShapeOptionsUpdate::GridType(grid_type) => {
|
ShapeOptionsUpdate::GridType(grid_type) => {
|
||||||
self.options.grid_type = grid_type;
|
self.options.grid_type = grid_type;
|
||||||
}
|
}
|
||||||
|
ShapeOptionsUpdate::ArrowShaftWidth(shaft_width) => {
|
||||||
|
self.options.arrow_shaft_width = shaft_width;
|
||||||
|
}
|
||||||
|
ShapeOptionsUpdate::ArrowHeadWidth(head_width) => {
|
||||||
|
self.options.arrow_head_width = head_width;
|
||||||
|
}
|
||||||
|
ShapeOptionsUpdate::ArrowHeadLength(head_length) => {
|
||||||
|
self.options.arrow_head_length = head_length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update_dynamic_hints(&self.fsm_state, responses, &self.tool_data);
|
update_dynamic_hints(&self.fsm_state, responses, &self.tool_data);
|
||||||
|
|
@ -854,7 +926,13 @@ impl Fsm for ShapeToolFsmState {
|
||||||
ShapeType::Grid => Grid::create_node(tool_options.grid_type),
|
ShapeType::Grid => Grid::create_node(tool_options.grid_type),
|
||||||
ShapeType::Rectangle => Rectangle::create_node(),
|
ShapeType::Rectangle => Rectangle::create_node(),
|
||||||
ShapeType::Ellipse => Ellipse::create_node(),
|
ShapeType::Ellipse => Ellipse::create_node(),
|
||||||
ShapeType::Arrow => Arrow::create_node(document, tool_data.data.drag_start),
|
ShapeType::Arrow => Arrow::create_node(
|
||||||
|
document,
|
||||||
|
tool_data.data.drag_start,
|
||||||
|
tool_options.arrow_shaft_width,
|
||||||
|
tool_options.arrow_head_width,
|
||||||
|
tool_options.arrow_head_length,
|
||||||
|
),
|
||||||
ShapeType::Line => Line::create_node(document, tool_data.data.drag_start),
|
ShapeType::Line => Line::create_node(document, tool_data.data.drag_start),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -330,14 +330,16 @@ impl<PointId: Identifier> Subpath<PointId> {
|
||||||
let head_base_distance = (length - head_length).max(0.);
|
let head_base_distance = (length - head_length).max(0.);
|
||||||
let head_base = start + direction * head_base_distance;
|
let head_base = start + direction * head_base_distance;
|
||||||
|
|
||||||
|
// Arrow path starts at the tail, traces around the shape, and returns to the tail
|
||||||
let anchors = [
|
let anchors = [
|
||||||
start - perpendicular * half_shaft, // Tail bottom
|
start, // Tail center (origin)
|
||||||
head_base - perpendicular * half_shaft, // Head base bottom (shaft)
|
|
||||||
head_base - perpendicular * half_head, // Head base bottom (wide)
|
|
||||||
end, // Tip
|
|
||||||
head_base + perpendicular * half_head, // Head base top (wide)
|
|
||||||
head_base + perpendicular * half_shaft, // Head base top (shaft)
|
|
||||||
start + perpendicular * half_shaft, // Tail top
|
start + perpendicular * half_shaft, // Tail top
|
||||||
|
head_base + perpendicular * half_shaft, // Head base top (shaft)
|
||||||
|
head_base + perpendicular * half_head, // Head base top (wide)
|
||||||
|
end, // Tip
|
||||||
|
head_base - perpendicular * half_head, // Head base bottom (wide)
|
||||||
|
head_base - perpendicular * half_shaft, // Head base bottom (shaft)
|
||||||
|
start - perpendicular * half_shaft, // Tail bottom
|
||||||
];
|
];
|
||||||
|
|
||||||
Self::from_anchors(anchors, true)
|
Self::from_anchors(anchors, true)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue