impl spacing gizmo-not fully integrated

This commit is contained in:
0SlowPoke0
2025-07-29 00:44:45 +05:30
parent 0ae00085f1
commit 7d53b0cb22
4 changed files with 332 additions and 16 deletions

View File

@@ -175,7 +175,7 @@ fn check_if_over_gizmo(grid_type: GridType, columns: u32, rows: u32, spacing: DV
None
}
fn convert_to_gizmo_line(p0: DVec2, p1: DVec2) -> Line {
pub fn convert_to_gizmo_line(p0: DVec2, p1: DVec2) -> Line {
Line {
p0: dvec2_to_point(p0),
p1: dvec2_to_point(p1),
@@ -198,7 +198,7 @@ fn get_corners(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2, DVec2,
(point0, point1, point2, point3)
}
fn get_viewport_grid_spacing(grid_type: GridType, angles: DVec2, spacing: DVec2, viewport: DAffine2) -> DVec2 {
pub fn get_viewport_grid_spacing(grid_type: GridType, angles: DVec2, spacing: DVec2, viewport: DAffine2) -> DVec2 {
match grid_type {
GridType::Rectangular => {
let p0 = DVec2::ZERO;
@@ -223,28 +223,28 @@ fn get_viewport_grid_spacing(grid_type: GridType, angles: DVec2, spacing: DVec2,
}
}
fn get_rectangle_top_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
pub fn get_rectangle_top_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
let (top_left, top_right, _, _) = get_corners(columns, rows, spacing);
let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(spacing.x * 0.5, 0.) };
(top_left + offset, top_right - offset)
}
fn get_rectangle_bottom_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
pub fn get_rectangle_bottom_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
let (_, _, bottom_right, bottom_left) = get_corners(columns, rows, spacing);
let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(spacing.x * 0.5, 0.) };
(bottom_left + offset, bottom_right - offset)
}
fn get_rectangle_right_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
pub fn get_rectangle_right_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
let (_, top_right, bottom_right, _) = get_corners(columns, rows, spacing);
let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(0., -spacing.y * 0.5) };
(top_right - offset, bottom_right + offset)
}
fn get_rectangle_left_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
pub fn get_rectangle_left_line_points(columns: u32, rows: u32, spacing: DVec2) -> (DVec2, DVec2) {
let (top_left, _, _, bottom_left) = get_corners(columns, rows, spacing);
let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(0., -spacing.y * 0.5) };
@@ -292,14 +292,14 @@ fn calculate_isometric_bottom_line_points(columns: u32, rows: u32, spacing: DVec
(bottom_left + offset, bottom_right - offset + isometric_offset)
}
fn calculate_isometric_offset(spacing: DVec2, angles: DVec2) -> DVec2 {
pub fn calculate_isometric_offset(spacing: DVec2, angles: DVec2) -> DVec2 {
let first_point = calculate_isometric_point(0, 0, angles, spacing);
let second_point = calculate_isometric_point(1, 0, angles, spacing);
DVec2::new(first_point.x - second_point.x, first_point.y - second_point.y)
}
fn calculate_isometric_right_line_points(columns: u32, rows: u32, spacing: DVec2, angles: DVec2) -> (DVec2, DVec2) {
pub fn calculate_isometric_right_line_points(columns: u32, rows: u32, spacing: DVec2, angles: DVec2) -> (DVec2, DVec2) {
let top_right = calculate_isometric_point(columns - 1, 0, angles, spacing);
let bottom_right = calculate_isometric_point(columns - 1, rows - 1, angles, spacing);
@@ -308,7 +308,7 @@ fn calculate_isometric_right_line_points(columns: u32, rows: u32, spacing: DVec2
(top_right - offset, bottom_right + offset)
}
fn calculate_isometric_left_line_points(columns: u32, rows: u32, spacing: DVec2, angles: DVec2) -> (DVec2, DVec2) {
pub fn calculate_isometric_left_line_points(columns: u32, rows: u32, spacing: DVec2, angles: DVec2) -> (DVec2, DVec2) {
let top_left = calculate_isometric_point(0, 0, angles, spacing);
let bottom_left = calculate_isometric_point(0, rows - 1, angles, spacing);
@@ -317,13 +317,13 @@ fn calculate_isometric_left_line_points(columns: u32, rows: u32, spacing: DVec2,
(top_left - offset, bottom_left + offset)
}
fn calculate_rectangle_side_direction(spacing: DVec2, viewport: DAffine2) -> DVec2 {
pub fn calculate_rectangle_side_direction(spacing: DVec2, viewport: DAffine2) -> DVec2 {
let p0 = DVec2::ZERO;
let p1 = DVec2::new(spacing.x, 0.);
(viewport.transform_point2(p1) - viewport.transform_point2(p0)).normalize()
}
fn calculate_rectangle_top_direction(spacing: DVec2, viewport: DAffine2) -> DVec2 {
pub fn calculate_rectangle_top_direction(spacing: DVec2, viewport: DAffine2) -> DVec2 {
let p0 = DVec2::ZERO;
let p1 = DVec2::new(0., spacing.y);
(viewport.transform_point2(p0) - viewport.transform_point2(p1)).try_normalize().unwrap_or(DVec2::ZERO)

View File

@@ -0,0 +1,296 @@
use crate::consts::{GRID_COLUMNS_INDEX, GRID_ROW_COLUMN_GIZMO_OFFSET, GRID_ROW_INDEX, GRID_SPACING_INDEX};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
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;
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage};
use crate::messages::prelude::{GraphOperationMessage, Responses};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::grid_row_columns_gizmo::{
calculate_rectangle_side_direction, calculate_rectangle_top_direction, convert_to_gizmo_line, get_viewport_grid_spacing,
};
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::extract_grid_parameters;
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use graphene_std::renderer::Quad;
use graphene_std::vector::misc::{GridType, dvec2_to_point, get_line_endpoints};
use kurbo::{Line, ParamCurveNearest, Rect};
use std::collections::VecDeque;
#[derive(Clone, Debug, Default, PartialEq)]
pub enum GridSpacingGizmoState {
#[default]
Inactive,
Hover,
Dragging,
}
#[derive(Clone, Debug, Default)]
pub struct GridSpacingGizmo {
pub layer: Option<LayerNodeIdentifier>,
gizmo_state: GridSpacingGizmoState,
column_index: u32,
row_index: u32,
initial_spacing: DVec2,
gizmo_type: Option<GridSpacingGizmoType>,
}
impl GridSpacingGizmo {
pub fn cleanup(&mut self) {
self.layer = None;
self.gizmo_state = GridSpacingGizmoState::Inactive;
}
pub fn update_state(&mut self, state: GridSpacingGizmoState) {
self.gizmo_state = state;
}
pub fn is_hovered(&self) -> bool {
self.gizmo_state == GridSpacingGizmoState::Hover
}
pub fn is_dragging(&self) -> bool {
self.gizmo_state == GridSpacingGizmoState::Dragging
}
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
let Some((grid_type, spacing, columns, rows, angles)) = extract_grid_parameters(layer, document) else {
return;
};
let stroke_width = graph_modification_utils::get_stroke_width(layer, &document.network_interface);
let viewport = document.metadata().transform_to_viewport(layer);
if let Some((col, row)) = check_if_over_gizmo(grid_type, columns, rows, spacing, mouse_position, viewport) {
self.layer = Some(layer);
self.column_index = col;
self.row_index = row;
self.initial_spacing = spacing;
self.update_state(GridSpacingGizmoState::Hover);
let closest_gizmo = GridSpacingGizmoType::get_closest_line(mouse_position, col, row, spacing, viewport, stroke_width);
responses.add(FrontendMessage::UpdateMouseCursor { cursor: closest_gizmo.mouse_icon() });
self.gizmo_type = Some(closest_gizmo);
}
}
pub fn overlays(&self, document: &DocumentMessageHandler, layer: Option<LayerNodeIdentifier>, _shape_editor: &mut &mut ShapeState, _mouse_position: DVec2, overlay_context: &mut OverlayContext) {
let Some(layer) = layer.or(self.layer) else { return };
let Some((grid_type, spacing, columns, rows, angles)) = extract_grid_parameters(layer, document) else {
return;
};
let viewport = document.metadata().transform_to_viewport(layer);
let stroke_width = graph_modification_utils::get_stroke_width(layer, &document.network_interface);
match self.gizmo_state {
GridSpacingGizmoState::Inactive => {}
GridSpacingGizmoState::Hover | GridSpacingGizmoState::Dragging => {
if let Some(gizmo_type) = &self.gizmo_type {
let line = gizmo_type.line(self.column_index, self.row_index, spacing, viewport, stroke_width);
let (p0, p1) = get_line_endpoints(line);
overlay_context.dashed_line(p0, p1, None, None, Some(5.), Some(5.), Some(0.5));
if matches!(self.gizmo_state, GridSpacingGizmoState::Hover) {
let line = gizmo_type.opposite_gizmo_type().line(self.column_index, self.row_index, spacing, viewport, stroke_width);
let (p0, p1) = get_line_endpoints(line);
overlay_context.dashed_line(p0, p1, None, None, Some(5.), Some(5.), Some(0.5));
}
}
}
}
}
pub fn update(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, drag_start: DVec2) {
let Some(layer) = self.layer else { return };
let viewport = document.metadata().transform_to_viewport(layer);
let Some((grid_type, spacing, columns, rows, angles)) = extract_grid_parameters(layer, document) else {
return;
};
let Some(gizmo_type) = &self.gizmo_type else { return };
let direction = gizmo_type.direction(spacing, viewport);
let delta_vector = input.mouse.position - drag_start;
let delta = delta_vector.dot(direction);
let Some(node_id) = graph_modification_utils::get_grid_id(layer, &document.network_interface) else {
return;
};
let new_spacing = gizmo_type.new_spacing(delta, self.initial_spacing);
let delta_spacing = new_spacing - spacing;
// let transform = self.transform_grid(dimensions_delta, grid_type, viewport_spacing, angles, viewport);
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, GRID_SPACING_INDEX),
input: NodeInput::value(TaggedValue::DVec2(new_spacing), false),
});
// responses.add(GraphOperationMessage::TransformChange {
// layer,
// transform: DAffine2::from_translation(-delta_spacing * direction),
// transform_in: TransformIn::Viewport,
// skip_rerender: false,
// });
responses.add(NodeGraphMessage::RunDocumentGraph);
// if self.initial_dimension() as i32 + dimensions_to_add < 1 {
// self.initial_mouse_start = Some(input.mouse.position);
// self.gizmo_type = self.gizmo_type.opposite_gizmo_type();
// self.initial_rows = 1;
// self.initial_columns = 1;
// }
}
fn transform_grid(&self, dimensions_delta: i32, grid_type: GridType, spacing: DVec2, angles: DVec2, viewport: DAffine2) {}
}
fn check_if_over_gizmo(grid_type: GridType, columns: u32, rows: u32, spacing: DVec2, mouse_position: DVec2, viewport: DAffine2) -> Option<(u32, u32)> {
let layer_mouse = viewport.inverse().transform_point2(mouse_position);
for column in 0..columns - 1 {
for row in 0..rows - 1 {
let p0 = DVec2::new(spacing.x * column as f64, spacing.y * row as f64);
let p1 = DVec2::new((1 + column) as f64 * spacing.x, (1 + row) as f64 * spacing.y);
let rect = Rect::from_points(dvec2_to_point(p0), dvec2_to_point(p1));
if rect.contains(dvec2_to_point(layer_mouse)) {
return Some((column, row));
};
}
}
None
}
#[derive(Clone, Debug, Default, PartialEq)]
pub enum GridSpacingGizmoType {
#[default]
None,
Top,
Down,
Left,
Right,
}
impl GridSpacingGizmoType {
pub fn get_line_points(&self, column_index: u32, row_index: u32, spacing: DVec2, stroke: Option<f64>) -> (DVec2, DVec2) {
match self {
Self::Top => get_rectangular_top_points(column_index, row_index, spacing, stroke),
Self::Right => get_rectangular_right_points(column_index, row_index, spacing, stroke),
Self::Down => get_rectangular_down_points(column_index, row_index, spacing, stroke),
Self::Left => get_rectangular_left_points(column_index, row_index, spacing, stroke),
Self::None => panic!("RowColumnGizmoType::None does not have line points"),
}
}
pub fn get_closest_line(mouse_position: DVec2, column_index: u32, row_index: u32, spacing: DVec2, viewport: DAffine2, stroke_width: Option<f64>) -> Self {
let mut gizmo_type = GridSpacingGizmoType::Top;
let mut closest_distance = gizmo_type
.line(column_index, row_index, spacing, viewport, stroke_width)
.nearest(dvec2_to_point(mouse_position), 1e-6)
.distance_sq;
for t in Self::all() {
if matches!(t, GridSpacingGizmoType::Top) {
continue;
}
let line = t.line(column_index, row_index, spacing, viewport, stroke_width);
let nearest = line.nearest(dvec2_to_point(mouse_position), 1e-6);
if nearest.distance_sq < closest_distance {
gizmo_type = t;
closest_distance = nearest.distance_sq;
}
}
gizmo_type
}
pub fn line(&self, column_index: u32, row_index: u32, spacing: DVec2, viewport: DAffine2, stroke_width: Option<f64>) -> Line {
let (p0, p1) = self.get_line_points(column_index, row_index, spacing, stroke_width);
let gap = -5. * self.direction(spacing, viewport);
convert_to_gizmo_line(viewport.transform_point2(p0) + gap, viewport.transform_point2(p1) + gap)
}
fn opposite(&self, grid_type: GridType, column_index: u32, row_index: u32, spacing: DVec2, angles: DVec2, viewport: DAffine2, stroke: Option<f64>) -> Line {
let opposite_gizmo_type = self.opposite_gizmo_type();
opposite_gizmo_type.line(column_index, row_index, spacing, viewport, stroke)
}
fn opposite_gizmo_type(&self) -> Self {
return match self {
Self::Top => Self::Down,
Self::Right => Self::Left,
Self::Down => Self::Top,
Self::Left => Self::Right,
Self::None => panic!("RowColumnGizmoType::None does not have opposite"),
};
}
fn new_spacing(&self, delta: f64, spacing: DVec2) -> DVec2 {
match self {
GridSpacingGizmoType::Top | GridSpacingGizmoType::Down => DVec2::new(spacing.x, spacing.y + delta),
GridSpacingGizmoType::Right | GridSpacingGizmoType::Left => DVec2::new(spacing.x + delta, spacing.y),
GridSpacingGizmoType::None => panic!("RowColumnGizmoType::None does not have a mouse_icon"),
}
}
fn direction(&self, spacing: DVec2, viewport: DAffine2) -> DVec2 {
match self {
GridSpacingGizmoType::Top => calculate_rectangle_top_direction(spacing, viewport),
GridSpacingGizmoType::Down => -calculate_rectangle_top_direction(spacing, viewport),
GridSpacingGizmoType::Right => calculate_rectangle_side_direction(spacing, viewport),
GridSpacingGizmoType::Left => -calculate_rectangle_side_direction(spacing, viewport),
GridSpacingGizmoType::None => panic!("RowColumnGizmoType::None does not have a line"),
}
}
fn mouse_icon(&self) -> MouseCursorIcon {
match self {
GridSpacingGizmoType::Top | GridSpacingGizmoType::Down => MouseCursorIcon::NSResize,
GridSpacingGizmoType::Right | GridSpacingGizmoType::Left => MouseCursorIcon::EWResize,
GridSpacingGizmoType::None => panic!("RowColumnGizmoType::None does not have a mouse_icon"),
}
}
pub fn all() -> [Self; 4] {
[Self::Top, Self::Right, Self::Down, Self::Left]
}
}
fn get_rectangular_top_points(column_index: u32, row_index: u32, spacing: DVec2, stroke_width: Option<f64>) -> (DVec2, DVec2) {
let stroke_width = stroke_width.unwrap_or_default();
let p0 = DVec2::new(column_index as f64 * spacing.x, row_index as f64 * spacing.y) + DVec2::new(stroke_width, stroke_width);
let p1 = p0 + DVec2::new(spacing.x - 2. * stroke_width, 0.);
(p0, p1)
}
fn get_rectangular_right_points(column_index: u32, row_index: u32, spacing: DVec2, stroke_width: Option<f64>) -> (DVec2, DVec2) {
let stroke_width = stroke_width.unwrap_or_default();
let p0 = DVec2::new((1 + column_index) as f64 * spacing.x, row_index as f64 * spacing.y) + DVec2::new(-stroke_width, stroke_width);
let p1 = p0 + DVec2::new(0., spacing.y - 2. * stroke_width);
(p0, p1)
}
fn get_rectangular_down_points(column_index: u32, row_index: u32, spacing: DVec2, stroke_width: Option<f64>) -> (DVec2, DVec2) {
let stroke_width = stroke_width.unwrap_or_default();
let p0 = DVec2::new(column_index as f64 * spacing.x, (1 + row_index) as f64 * spacing.y) + DVec2::new(stroke_width, -stroke_width);
let p1 = p0 + DVec2::new(spacing.x - 2. * stroke_width, 0.);
(p0, p1)
}
fn get_rectangular_left_points(column_index: u32, row_index: u32, spacing: DVec2, stroke_width: Option<f64>) -> (DVec2, DVec2) {
let stroke_width = stroke_width.unwrap_or_default();
let p0 = DVec2::new(column_index as f64 * spacing.x, row_index as f64 * spacing.y) + DVec2::new(stroke_width, stroke_width);
let p1 = p0 + DVec2::new(0., spacing.y - 2. * stroke_width);
(p0, p1)
}

View File

@@ -1,3 +1,4 @@
pub mod grid_row_columns_gizmo;
pub mod grid_spacing_gizmos;
pub mod number_of_points_dial;
pub mod point_radius_handle;

View File

@@ -5,9 +5,9 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
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::grid_row_columns_gizmo::RowColumnGizmo;
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::grid_row_columns_gizmo::RowColumnGizmoState;
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::grid_row_columns_gizmo::{RowColumnGizmo, calculate_rectangle_side_direction, calculate_rectangle_top_direction};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::grid_spacing_gizmos::{GridSpacingGizmo, GridSpacingGizmoState};
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;
@@ -15,33 +15,44 @@ 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 graphene_std::vector::misc::{GridType, dvec2_to_point};
use kurbo::{Line, ParamCurveNearest};
use std::collections::VecDeque;
#[derive(Clone, Debug, Default)]
pub struct GridGizmoHandler {
row_column_gizmo: RowColumnGizmo,
grid_spacing_gizmo: GridSpacingGizmo,
}
impl ShapeGizmoHandler for GridGizmoHandler {
fn is_any_gizmo_hovered(&self) -> bool {
self.row_column_gizmo.is_hovered()
self.row_column_gizmo.is_hovered() || self.grid_spacing_gizmo.is_hovered()
}
fn handle_state(&mut self, selected_grid_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
self.row_column_gizmo.handle_actions(selected_grid_layer, mouse_position, document, responses);
self.grid_spacing_gizmo.handle_actions(selected_grid_layer, mouse_position, document, responses);
}
fn handle_click(&mut self) {
if self.row_column_gizmo.is_hovered() {
self.row_column_gizmo.update_state(RowColumnGizmoState::Dragging);
}
if self.grid_spacing_gizmo.is_hovered() {
self.grid_spacing_gizmo.update_state(GridSpacingGizmoState::Dragging);
}
}
fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
if self.row_column_gizmo.is_dragging() {
self.row_column_gizmo.update(document, input, responses, drag_start);
}
if self.grid_spacing_gizmo.is_dragging() {
self.grid_spacing_gizmo.update(document, input, responses, drag_start);
}
}
fn overlays(
@@ -54,6 +65,7 @@ impl ShapeGizmoHandler for GridGizmoHandler {
overlay_context: &mut OverlayContext,
) {
self.row_column_gizmo.overlays(document, selected_grid_layer, shape_editor, mouse_position, overlay_context);
self.grid_spacing_gizmo.overlays(document, selected_grid_layer, shape_editor, mouse_position, overlay_context);
}
fn dragging_overlays(
@@ -67,9 +79,16 @@ impl ShapeGizmoHandler for GridGizmoHandler {
if self.row_column_gizmo.is_dragging() {
self.row_column_gizmo.overlays(document, None, shape_editor, mouse_position, overlay_context);
}
if self.grid_spacing_gizmo.is_dragging() {
self.grid_spacing_gizmo.overlays(document, None, shape_editor, mouse_position, overlay_context);
}
}
fn cleanup(&mut self) {}
fn cleanup(&mut self) {
self.grid_spacing_gizmo.cleanup();
self.row_column_gizmo.cleanup();
}
}
#[derive(Default)]