mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 23:38:06 +08:00
integrated grid shape in shape-tool
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -52,5 +52,6 @@
|
||||
"files.insertFinalNewline": true,
|
||||
"files.associations": {
|
||||
"*.graphite": "json"
|
||||
}
|
||||
},
|
||||
"rust-analyzer.checkOnSave": false
|
||||
}
|
||||
|
||||
@@ -356,6 +356,10 @@ pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn
|
||||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text")
|
||||
}
|
||||
|
||||
pub fn get_grid_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
||||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Grid")
|
||||
}
|
||||
|
||||
/// Gets properties from the Text node
|
||||
pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> {
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?;
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
use super::shape_utility::ShapeToolModifierKey;
|
||||
use super::shape_utility::update_radius_sign;
|
||||
use super::*;
|
||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
|
||||
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::NumberOfPointsDial;
|
||||
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::NumberOfPointsDialState;
|
||||
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandle;
|
||||
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandleState;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
||||
use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler;
|
||||
use crate::messages::tool::common_functionality::shapes::shape_utility::polygon_outline;
|
||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||
use glam::DAffine2;
|
||||
use graph_craft::document::NodeInput;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::vector::misc::GridType;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
// #[derive(Clone, Debug, Default)]
|
||||
// pub struct PolygonGizmoHandler {
|
||||
// number_of_points_dial: NumberOfPointsDial,
|
||||
// point_radius_handle: PointRadiusHandle,
|
||||
// }
|
||||
|
||||
// impl ShapeGizmoHandler for PolygonGizmoHandler {
|
||||
// fn is_any_gizmo_hovered(&self) -> bool {
|
||||
// self.number_of_points_dial.is_hovering() || self.point_radius_handle.hovered()
|
||||
// }
|
||||
|
||||
// fn handle_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
// self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document, responses);
|
||||
// self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position, responses);
|
||||
// }
|
||||
|
||||
// fn handle_click(&mut self) {
|
||||
// if self.number_of_points_dial.is_hovering() {
|
||||
// self.number_of_points_dial.update_state(NumberOfPointsDialState::Dragging);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if self.point_radius_handle.hovered() {
|
||||
// self.point_radius_handle.update_state(PointRadiusHandleState::Dragging);
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
// if self.number_of_points_dial.is_dragging() {
|
||||
// self.number_of_points_dial.update_number_of_sides(document, input, responses, drag_start);
|
||||
// }
|
||||
|
||||
// if self.point_radius_handle.is_dragging_or_snapped() {
|
||||
// self.point_radius_handle.update_inner_radius(document, input, responses, drag_start);
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn overlays(
|
||||
// &self,
|
||||
// document: &DocumentMessageHandler,
|
||||
// selected_polygon_layer: Option<LayerNodeIdentifier>,
|
||||
// input: &InputPreprocessorMessageHandler,
|
||||
// shape_editor: &mut &mut ShapeState,
|
||||
// mouse_position: DVec2,
|
||||
// overlay_context: &mut OverlayContext,
|
||||
// ) {
|
||||
// self.number_of_points_dial.overlays(document, selected_polygon_layer, shape_editor, mouse_position, overlay_context);
|
||||
// self.point_radius_handle.overlays(selected_polygon_layer, document, input, mouse_position, overlay_context);
|
||||
|
||||
// polygon_outline(selected_polygon_layer, document, overlay_context);
|
||||
// }
|
||||
|
||||
// fn dragging_overlays(
|
||||
// &self,
|
||||
// document: &DocumentMessageHandler,
|
||||
// input: &InputPreprocessorMessageHandler,
|
||||
// shape_editor: &mut &mut ShapeState,
|
||||
// mouse_position: DVec2,
|
||||
// overlay_context: &mut OverlayContext,
|
||||
// ) {
|
||||
// if self.number_of_points_dial.is_dragging() {
|
||||
// self.number_of_points_dial.overlays(document, None, shape_editor, mouse_position, overlay_context);
|
||||
// }
|
||||
|
||||
// if self.point_radius_handle.is_dragging_or_snapped() {
|
||||
// self.point_radius_handle.overlays(None, document, input, mouse_position, overlay_context);
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn cleanup(&mut self) {
|
||||
// self.number_of_points_dial.cleanup();
|
||||
// self.point_radius_handle.cleanup();
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Grid;
|
||||
|
||||
impl Grid {
|
||||
pub fn create_node(grid_type: GridType) -> NodeTemplate {
|
||||
let node_type = resolve_document_node_type("Grid").expect("Grid can't be found");
|
||||
node_type.node_template_input_override([
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::GridType(grid_type), false)),
|
||||
Some(NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false)),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn update_shape(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
grid_type: GridType,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) {
|
||||
let [center, lock_ratio, _, _] = modifier;
|
||||
|
||||
let start = shape_tool_data.data.viewport_drag_start(document);
|
||||
let end = ipp.mouse.position;
|
||||
|
||||
let mut dimensions = (start - end).abs();
|
||||
|
||||
let mut translation = shape_tool_data.data.viewport_drag_start(document);
|
||||
let mut scale = (end - start).signum();
|
||||
|
||||
match grid_type {
|
||||
GridType::Rectangular => {
|
||||
if ipp.keyboard.key(center) && ipp.keyboard.key(lock_ratio) {
|
||||
let max = dimensions.x.max(dimensions.y);
|
||||
let distance_to_make_center = max;
|
||||
translation = shape_tool_data.data.viewport_drag_start(document) - distance_to_make_center;
|
||||
dimensions = 2. * DVec2::splat(max) / 9.;
|
||||
scale = DVec2::ONE;
|
||||
} else if ipp.keyboard.key(lock_ratio) {
|
||||
let max = dimensions.x.max(dimensions.y);
|
||||
dimensions = DVec2::splat(max) / 9.
|
||||
} else if ipp.keyboard.key(center) {
|
||||
let distance_to_make_center = dimensions;
|
||||
translation = shape_tool_data.data.viewport_drag_start(document) - distance_to_make_center;
|
||||
dimensions = 2. * dimensions / 9.;
|
||||
scale = DVec2::ONE;
|
||||
} else {
|
||||
dimensions = dimensions / 9.;
|
||||
};
|
||||
}
|
||||
GridType::Isometric => {
|
||||
if ipp.keyboard.key(center) {
|
||||
let distance_to_make_center = DVec2::splat(dimensions.y);
|
||||
translation = shape_tool_data.data.viewport_drag_start(document) - distance_to_make_center;
|
||||
dimensions = 2. * DVec2::splat(dimensions.y) / 9.;
|
||||
scale = DVec2::ONE;
|
||||
} else {
|
||||
dimensions = DVec2::splat(dimensions.y) / 9.;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let Some(node_id) = graph_modification_utils::get_grid_id(layer, &document.network_interface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, 2),
|
||||
input: NodeInput::value(TaggedValue::DVec2(dimensions), false),
|
||||
});
|
||||
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(scale, 0., translation),
|
||||
transform_in: TransformIn::Viewport,
|
||||
skip_rerender: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod ellipse_shape;
|
||||
pub mod grid_shape;
|
||||
pub mod line_shape;
|
||||
pub mod polygon_shape;
|
||||
pub mod rectangle_shape;
|
||||
|
||||
@@ -24,9 +24,10 @@ pub enum ShapeType {
|
||||
#[default]
|
||||
Polygon = 0,
|
||||
Star = 1,
|
||||
Rectangle = 2,
|
||||
Ellipse = 3,
|
||||
Line = 4,
|
||||
Grid = 2,
|
||||
Rectangle = 3,
|
||||
Ellipse = 4,
|
||||
Line = 5,
|
||||
}
|
||||
|
||||
impl ShapeType {
|
||||
@@ -37,6 +38,7 @@ impl ShapeType {
|
||||
Self::Rectangle => "Rectangle",
|
||||
Self::Ellipse => "Ellipse",
|
||||
Self::Line => "Line",
|
||||
Self::Grid => "Grid",
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::messages::tool::common_functionality::gizmos::gizmo_manager::GizmoMan
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
use crate::messages::tool::common_functionality::shapes::grid_shape::Grid;
|
||||
use crate::messages::tool::common_functionality::shapes::line_shape::{LineToolData, clicked_on_line_endpoints};
|
||||
use crate::messages::tool::common_functionality::shapes::polygon_shape::Polygon;
|
||||
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeToolModifierKey, ShapeType, anchor_overlays, transform_cage_overlays};
|
||||
@@ -22,7 +23,7 @@ use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graphene_std::Color;
|
||||
use graphene_std::renderer::Quad;
|
||||
use graphene_std::vector::misc::ArcType;
|
||||
use graphene_std::vector::misc::{ArcType, GridType};
|
||||
use std::vec;
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -39,6 +40,7 @@ pub struct ShapeToolOptions {
|
||||
vertices: u32,
|
||||
shape_type: ShapeType,
|
||||
arc_type: ArcType,
|
||||
grid_type: GridType,
|
||||
}
|
||||
|
||||
impl Default for ShapeToolOptions {
|
||||
@@ -50,6 +52,7 @@ impl Default for ShapeToolOptions {
|
||||
vertices: 5,
|
||||
shape_type: ShapeType::Polygon,
|
||||
arc_type: ArcType::Open,
|
||||
grid_type: GridType::Rectangular,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +68,7 @@ pub enum ShapeOptionsUpdate {
|
||||
Vertices(u32),
|
||||
ShapeType(ShapeType),
|
||||
ArcType(ArcType),
|
||||
GridType(GridType),
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Shape)]
|
||||
@@ -109,6 +113,9 @@ fn create_shape_option_widget(shape_type: ShapeType) -> WidgetHolder {
|
||||
MenuListEntry::new("Star")
|
||||
.label("Star")
|
||||
.on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(ShapeType::Star)).into()),
|
||||
MenuListEntry::new("Grid")
|
||||
.label("Grid")
|
||||
.on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(ShapeType::Grid)).into()),
|
||||
]];
|
||||
DropdownInput::new(entries).selected_index(Some(shape_type as u32)).widget_holder()
|
||||
}
|
||||
@@ -123,6 +130,18 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
fn create_grid_type_widget(grid_type: GridType) -> WidgetHolder {
|
||||
let entries = vec![
|
||||
RadioEntryData::new("Rectangular")
|
||||
.label("Rectangular")
|
||||
.on_update(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::GridType(GridType::Rectangular)).into()),
|
||||
RadioEntryData::new("Isometric")
|
||||
.label("Isometric")
|
||||
.on_update(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::GridType(GridType::Isometric)).into()),
|
||||
];
|
||||
RadioInput::new(entries).selected_index(Some(grid_type as u32)).widget_holder()
|
||||
}
|
||||
|
||||
impl LayoutHolder for ShapeTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let mut widgets = vec![];
|
||||
@@ -137,6 +156,11 @@ impl LayoutHolder for ShapeTool {
|
||||
}
|
||||
}
|
||||
|
||||
if self.options.shape_type == ShapeType::Grid {
|
||||
widgets.push(create_grid_type_widget(self.options.grid_type));
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
}
|
||||
|
||||
if self.options.shape_type != ShapeType::Line {
|
||||
widgets.append(&mut self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
@@ -203,6 +227,9 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Shap
|
||||
ShapeOptionsUpdate::ArcType(arc_type) => {
|
||||
self.options.arc_type = arc_type;
|
||||
}
|
||||
ShapeOptionsUpdate::GridType(grid_type) => {
|
||||
self.options.grid_type = grid_type;
|
||||
}
|
||||
}
|
||||
|
||||
self.fsm_state.update_hints(responses);
|
||||
@@ -578,7 +605,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
};
|
||||
|
||||
match tool_data.current_shape {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Ellipse | ShapeType::Rectangle => tool_data.data.start(document, input),
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Grid => tool_data.data.start(document, input),
|
||||
ShapeType::Line => {
|
||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let snapped = tool_data.data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
||||
@@ -594,6 +621,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
ShapeType::Rectangle => Rectangle::create_node(),
|
||||
ShapeType::Ellipse => Ellipse::create_node(),
|
||||
ShapeType::Line => Line::create_node(document, tool_data.data.drag_start),
|
||||
ShapeType::Grid => Grid::create_node(tool_options.grid_type),
|
||||
};
|
||||
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
@@ -602,7 +630,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
responses.add(Message::StartBuffer);
|
||||
|
||||
match tool_data.current_shape {
|
||||
ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Polygon | ShapeType::Star => {
|
||||
ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Polygon | ShapeType::Star | ShapeType::Grid => {
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
@@ -635,6 +663,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
ShapeType::Line => Line::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Polygon => Polygon::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Star => Star::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Grid => Grid::update_shape(document, input, layer, tool_options.grid_type, tool_data, modifier, responses),
|
||||
}
|
||||
|
||||
// Auto-panning
|
||||
@@ -853,6 +882,11 @@ impl Fsm for ShapeToolFsmState {
|
||||
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
ShapeType::Grid => vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Grid"),
|
||||
HintInfo::keys([Key::Shift], "Constrain Grid").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
};
|
||||
HintData(hint_groups)
|
||||
}
|
||||
@@ -862,6 +896,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
ShapeType::Polygon | ShapeType::Star => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Regular"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Rectangle => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Ellipse => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Circular"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Grid => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Grid"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Line => HintGroup(vec![
|
||||
HintInfo::keys([Key::Shift], "15° Increments"),
|
||||
HintInfo::keys([Key::Alt], "From Center"),
|
||||
|
||||
@@ -56,8 +56,8 @@ impl AsI64 for f64 {
|
||||
#[widget(Radio)]
|
||||
pub enum GridType {
|
||||
#[default]
|
||||
Rectangular,
|
||||
Isometric,
|
||||
Rectangular = 0,
|
||||
Isometric = 1,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
||||
@@ -127,7 +127,7 @@ impl PointDomain {
|
||||
}
|
||||
|
||||
pub fn push(&mut self, id: PointId, position: DVec2) {
|
||||
debug_assert!(!self.id.contains(&id));
|
||||
// debug_assert!(!self.id.contains(&id));
|
||||
self.id.push(id);
|
||||
self.position.push(position);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user