From b24315e16209c583a4ecc72a673cc61b3dec3c28 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Mon, 18 Aug 2025 11:58:03 +0530 Subject: [PATCH] impl spacing gizmos for isometric grid --- .../shape_gizmos/grid_row_columns_gizmo.rs | 5 - .../shape_gizmos/grid_spacing_gizmos.rs | 486 ++++++++++-------- 2 files changed, 259 insertions(+), 232 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_row_columns_gizmo.rs b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_row_columns_gizmo.rs index 3ec7f66e5e..0ed69f66ce 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_row_columns_gizmo.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_row_columns_gizmo.rs @@ -387,11 +387,6 @@ impl RowColumnGizmoType { convert_to_gizmo_line(viewport.transform_point2(p0) + gap, viewport.transform_point2(p1) + gap) } - fn opposite(&self, grid_type: GridType, columns: u32, rows: u32, spacing: DVec2, angles: DVec2, viewport: DAffine2) -> Line { - let opposite_gizmo_type = self.opposite_gizmo_type(); - opposite_gizmo_type.line(grid_type, columns, rows, spacing, angles, viewport) - } - fn opposite_gizmo_type(&self) -> Self { return match self { Self::Top => Self::Down, diff --git a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_spacing_gizmos.rs b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_spacing_gizmos.rs index 6c97333e0f..16002c5274 100644 --- a/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_spacing_gizmos.rs +++ b/editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/grid_spacing_gizmos.rs @@ -1,4 +1,4 @@ -use crate::consts::{GRID_COLUMNS_INDEX, GRID_ROW_COLUMN_GIZMO_OFFSET, GRID_ROW_INDEX, GRID_SPACING_INDEX}; +use crate::consts::{GRID_ANGLE_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; @@ -7,16 +7,14 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye 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::gizmos::shape_gizmos::grid_row_columns_gizmo::convert_to_gizmo_line; 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::uuid::NodeId; use graphene_std::vector::misc::{GridType, dvec2_to_point, get_line_endpoints}; use kurbo::{Line, ParamCurveNearest, Rect, Shape, Triangle}; use std::collections::VecDeque; @@ -36,6 +34,7 @@ pub struct GridSpacingGizmo { column_index: u32, row_index: u32, initial_spacing: DVec2, + angles: DVec2, gizmo_type: Option, } @@ -64,14 +63,13 @@ impl GridSpacingGizmo { 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, angles, mouse_position, viewport) { - log::info!("col {:?} , row {:?}", col, row); self.layer = Some(layer); self.column_index = col; self.row_index = row; self.initial_spacing = spacing; + self.angles = angles; self.update_state(GridSpacingGizmoState::Hover); let closest_gizmo = GridSpacingGizmoType::get_closest_line(grid_type, mouse_position, col, row, spacing, angles, viewport, stroke_width); - log::info!("gizmo type {:?}", closest_gizmo); responses.add(FrontendMessage::UpdateMouseCursor { cursor: closest_gizmo.mouse_icon() }); self.gizmo_type = Some(closest_gizmo); } @@ -79,7 +77,7 @@ impl GridSpacingGizmo { pub fn overlays(&self, document: &DocumentMessageHandler, layer: Option, _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 { + let Some((_grid_type, spacing, _columns, _rows, angles)) = extract_grid_parameters(layer, document) else { return; }; let viewport = document.metadata().transform_to_viewport(layer); @@ -87,34 +85,54 @@ impl GridSpacingGizmo { match self.gizmo_state { GridSpacingGizmoState::Inactive => {} - GridSpacingGizmoState::Hover | GridSpacingGizmoState::Dragging => match grid_type { - GridType::Rectangular => { - if let Some(gizmo_type) = &self.gizmo_type { - let line = gizmo_type.line(grid_type, self.column_index, self.row_index, angles, 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)); - } + GridSpacingGizmoState::Hover | GridSpacingGizmoState::Dragging => { + if let Some(gizmo_type) = &self.gizmo_type { + let line = gizmo_type.line(self.column_index, self.row_index, angles, 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)); } - GridType::Isometric => { - if let Some(gizmo_type) = &self.gizmo_type { - let line = gizmo_type.line(grid_type, self.column_index, self.row_index, angles, 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_rectangle_grid( + &self, + node_id: NodeId, + layer: LayerNodeIdentifier, + gizmo_type: &GridSpacingGizmoType, + current_spacing: DVec2, + angles: DVec2, + delta: f64, + viewport: DAffine2, + responses: &mut VecDeque, + ) { + let direction = gizmo_type.direction(self.column_index, self.row_index, angles, self.initial_spacing, viewport); + let new_spacing = gizmo_type.new_spacing(delta, self.initial_spacing); + let spacing_delta = new_spacing - current_spacing; + + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, GRID_SPACING_INDEX), + input: NodeInput::value(TaggedValue::DVec2(new_spacing), false), + }); + + let transform = gizmo_type.transform_grid(spacing_delta, direction, self.column_index, self.row_index); + + responses.add(GraphOperationMessage::TransformChange { + layer, + transform, + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + } + pub fn update(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque, 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 { + 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 Some(gizmo_type) = &self.gizmo_type else { return }; let direction = gizmo_type.direction(self.column_index, self.row_index, angles, self.initial_spacing, viewport); let delta_vector = input.mouse.position - drag_start; @@ -125,15 +143,169 @@ impl GridSpacingGizmo { return; }; - let new_spacing = gizmo_type.new_spacing(delta, self.initial_spacing); - let spacing_delta = new_spacing - spacing; + if grid_type == GridType::Rectangular { + self.update_rectangle_grid(node_id, layer, gizmo_type, spacing, angles, delta, viewport, responses); + } else { + match gizmo_type { + GridSpacingGizmoType::Rect(_) => unreachable!(), + GridSpacingGizmoType::Iso(h) => { + if *h == IsometricGizmoType::Right || *h == IsometricGizmoType::Left { + self.update_isometric_x_spacing(layer, delta_vector, node_id, spacing, angles, gizmo_type, h, viewport, responses); + } else { + self.update_isometric_y_spacing(layer, delta_vector, node_id, spacing, angles, gizmo_type, viewport, responses); + } + } + }; + } + responses.add(NodeGraphMessage::RunDocumentGraph); + } + + fn update_isometric_y_spacing( + &self, + layer: LayerNodeIdentifier, + delta: DVec2, + node_id: NodeId, + spacing: DVec2, + angles: DVec2, + gizmo_type: &GridSpacingGizmoType, + viewport: DAffine2, + responses: &mut VecDeque, + ) { + let (a, b) = self.angles.into(); + let (tan_a_old, tan_b_old) = (a.to_radians().tan(), b.to_radians().tan()); + let direction = gizmo_type.direction(self.column_index, self.row_index, self.angles, spacing, viewport); + + let ((old_prev_row, old_prev_col), _sign) = match gizmo_type { + GridSpacingGizmoType::Rect(_) => unreachable!(), + GridSpacingGizmoType::Iso(h) => (h.old_row_col_index(self.row_index, self.column_index), h.delta_sign()), + }; + let projection = viewport.inverse().transform_vector2(delta.project_onto(direction)); + let a = (self.column_index + 1).div_ceil(2) as f64; + let b = ((self.column_index + 1) / 2) as f64; + + let p = self.initial_spacing.y / (tan_a_old + tan_b_old); // spacing_x, must stay constant + + let y = self.row_index as f64; + let delta = projection.y; + + // 1) Put the whole vertical move into y-spacing (for y>0): + let new_y_spacing = if y > 0.0 { + self.initial_spacing.y + delta / y + } else { + self.initial_spacing.y // y==0 handled below in edge cases + }; + + // 2) S' = sum of new tans required to keep spacing_x (=p) constant: + let s_prime = new_y_spacing / p; + + // 3) R = b*tb - a*ta (OLD values) + let r = b * tan_b_old - a * tan_a_old; + + // 4) Solve for new tangents: + let denom = a + b; // safe when col > 0 + let tan_a_new = (b * s_prime - r) / denom; + let tan_b_new = (r + a * s_prime) / denom; + + // 5) Convert to degrees and set: + let angle_a_new_deg = tan_a_new.atan().to_degrees(); + let angle_b_new_deg = tan_b_new.atan().to_degrees(); + + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, GRID_ANGLE_INDEX), + input: NodeInput::value(TaggedValue::DVec2((angle_a_new_deg, angle_b_new_deg).into()), false), + }); + + let old_position = isometric_point_position(old_prev_row, old_prev_col, spacing, angles); + let new_position = isometric_point_position(old_prev_row, old_prev_col, (new_y_spacing, new_y_spacing).into(), (angle_a_new_deg, angle_b_new_deg).into()); + + responses.add(GraphOperationMessage::TransformChange { + layer, + transform: DAffine2::from_translation(-DVec2::new(0., viewport.transform_vector2(new_position - old_position).y)), + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); responses.add(NodeGraphMessage::SetInput { input_connector: InputConnector::node(node_id, GRID_SPACING_INDEX), - input: NodeInput::value(TaggedValue::DVec2(new_spacing), false), + input: NodeInput::value(TaggedValue::DVec2((new_y_spacing, new_y_spacing).into()), false), + }); + } + + fn update_isometric_x_spacing( + &self, + layer: LayerNodeIdentifier, + delta: DVec2, + node_id: NodeId, + spacing: DVec2, + angles: DVec2, + gizmo_type: &GridSpacingGizmoType, + iso_gizmo_type: &IsometricGizmoType, + viewport: DAffine2, + responses: &mut VecDeque, + ) { + let (row, column) = if *iso_gizmo_type == IsometricGizmoType::Right { + (self.row_index + 1, self.column_index + 1) + } else { + (self.row_index, self.column_index) + }; + + let (a, b) = self.angles.into(); + let (tan_a_old, tan_b_old) = (a.to_radians().tan(), b.to_radians().tan()); + let direction = gizmo_type.direction(column, row, self.angles, spacing, viewport); + + let ((old_prev_row, old_prev_col), sign) = (iso_gizmo_type.old_row_col_index(self.row_index, self.column_index), iso_gizmo_type.delta_sign()); + + let projection = viewport.inverse().transform_vector2(sign * delta.project_onto(direction)); + let old_spacing_x = spacing.y / (tan_a_old + tan_b_old); + + let a_steps = ((column) as f64 / 2.0).ceil(); + let b_steps = ((column) / 2) as f64; + + let old_offset_y_fraction = b_steps * tan_b_old - a_steps * tan_a_old; + + let old_x_pos = old_spacing_x * (column) as f64; + let old_y_pos = spacing.y * (row) as f64 + old_offset_y_fraction * old_spacing_x; + + // --- Step 1: Apply delta to get new position --- + let new_x_pos = old_x_pos + projection.x; + let new_y_pos = old_y_pos + projection.y; + + // --- Step 2: New spacing.x from horizontal position --- + let spacing_x_new = if (column) != 0 { + new_x_pos / (column) as f64 + } else { + old_spacing_x // Can't deduce from vertical column + }; + + // --- Step 3: Sum of tangents --- + let sum_tan = spacing.y / spacing_x_new; + + // --- Step 4: RHS from vertical position --- + let rhs = (new_y_pos - spacing.y * row as f64) / spacing_x_new; + + // --- Step 5: Difference of tangents --- + let denom = b_steps + a_steps; + let diff_tan = if denom.abs() > f64::EPSILON { (2.0 * rhs - (b_steps - a_steps) * sum_tan) / denom } else { 0.0 }; + + // --- Step 6: Compute tangents and angles --- + let tan_a_new = (sum_tan - diff_tan) / 2.0; + let tan_b_new = (sum_tan + diff_tan) / 2.0; + + let new_angles = DVec2::new(tan_a_new.atan().to_degrees(), tan_b_new.atan().to_degrees()); + + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, GRID_ANGLE_INDEX), + input: NodeInput::value(TaggedValue::DVec2(new_angles), false), }); - let transform = self.gizmo_type.as_ref().unwrap().transform_grid(spacing_delta, direction, self.column_index, self.row_index); + let new_point = isometric_point_position(old_prev_row, old_prev_col, spacing, new_angles); + let old_point = isometric_point_position(old_prev_row, old_prev_col, spacing, angles); + + let transform = self + .gizmo_type + .as_ref() + .unwrap() + .transform_grid(viewport.transform_vector2(new_point - old_point), direction, self.column_index, self.row_index); responses.add(GraphOperationMessage::TransformChange { layer, @@ -141,16 +313,6 @@ impl GridSpacingGizmo { transform_in: TransformIn::Viewport, skip_rerender: false, }); - - log::info!("{:?}", (self.row_index, self.column_index)); - 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; - // } } } @@ -196,116 +358,6 @@ fn check_if_over_gizmo(grid_type: GridType, columns: u32, rows: u32, spacing: DV None } -// #[derive(Clone, Debug, Default, PartialEq)] -// pub enum GridSpacingGizmoType { -// #[default] -// None, -// Top, -// Down, -// Left, -// Right, -// IsometricMiddleUp, -// IsometricMiddleDown, -// } - -// impl GridSpacingGizmoType { -// pub fn get_line_points(&self, grid_type: GridType, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, stroke: Option) -> (DVec2, DVec2) { -// match grid_type { -// GridType::Rectangular => 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"), -// }, -// GridType::Isometric => match self { -// Self::Top => get_isometric_top_points(column_index, row_index, angles, spacing, stroke), -// Self::Right => get_isometric_right_points(column_index, row_index, angles, spacing, stroke), -// Self::Down => get_isometric_down_points(column_index, row_index, angles, spacing, stroke), -// Self::Left => get_isometric_left_points(column_index, row_index, angles, spacing, stroke), -// Self::None => panic!("RowColumnGizmoType::None does not have line points"), -// }, -// } -// } - -// pub fn get_closest_line(mouse_position: DVec2, grid_type: GridType, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, viewport: DAffine2, stroke_width: Option) -> Self { -// let mut gizmo_type = GridSpacingGizmoType::Top; -// let mut closest_distance = gizmo_type -// .line(grid_type, column_index, row_index, angles, 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(grid_type, column_index, row_index, angles, 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, grid_type: GridType, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, viewport: DAffine2, stroke_width: Option) -> Line { -// let (p0, p1) = self.get_line_points(grid_type, column_index, row_index, angles, spacing, stroke_width); -// // let gap = 2. * stroke_width.unwrap_or(3.) * (p1 - p0).perp().normalize(); - -// convert_to_gizmo_line(viewport.transform_point2(p0), viewport.transform_point2(p1)) -// } - -// 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, grid_type: GridType, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, viewport: DAffine2) -> DVec2 { -// match grid_type { -// GridType::Rectangular => match self { -// GridSpacingGizmoType::Top => viewport.transform_vector2(DVec2::Y), -// GridSpacingGizmoType::Down => -viewport.transform_vector2(DVec2::Y), -// 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"), -// }, -// GridType::Isometric => match &self { -// GridSpacingGizmoType::Top | GridSpacingGizmoType::Down | GridSpacingGizmoType::Left | GridSpacingGizmoType::Right => { -// let (p1, p2) = self.get_line_points(grid_type, column_index, row_index, angles, spacing, None); -// (p1 - p2).perp().normalize() -// } -// 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) -> (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); @@ -354,19 +406,26 @@ fn isometric_point_position(row: u32, col: u32, spacing: DVec2, angles: DVec2) - DVec2::new(spacing.x * col as f64, spacing.y * row as f64 + offset_y_fraction * spacing.x) } +fn apply_gizmo_padding_and_offset(x0: DVec2, x1: DVec2, stroke_width: f64, inward: bool) -> (DVec2, DVec2) { + let Some(direction) = (x1 - x0).try_normalize() else { + // No valid direction, return original points unchanged + return (x0, x1); + }; + + // Apply normal padding and offset logic + let padding = (x1 - x0).length() * 0.1 * direction; + let push_out = calculate_gap_vector(direction, stroke_width); + let push_out_vector = if inward { -push_out } else { push_out }; + + (x0 + push_out_vector + padding, x1 + push_out_vector - padding) +} + fn get_isometric_top_points(column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, stroke_width: Option) -> (DVec2, DVec2) { let stroke_width = stroke_width.unwrap_or_default(); let x0 = isometric_point_position(row_index, column_index, spacing, angles); let x1 = isometric_point_position(row_index, column_index + 1, spacing, angles); - let direction = (x1 - x0).normalize(); - - // in the direction of edge - let padding = (x1 - x0).length() * 0.1 * direction; - // perpendicular to the edge - let push_out = calculate_gap_vector(direction, stroke_width); - - (x0 + push_out + padding, x1 + push_out - padding) + apply_gizmo_padding_and_offset(x0, x1, stroke_width, false) // push_out outward } fn get_isometric_right_points(column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, stroke_width: Option) -> (DVec2, DVec2) { @@ -374,14 +433,7 @@ fn get_isometric_right_points(column_index: u32, row_index: u32, angles: DVec2, let x0 = isometric_point_position(row_index, column_index + 1, spacing, angles); let x1 = isometric_point_position(row_index + 1, column_index + 1, spacing, angles); - let direction = (x1 - x0).normalize(); - - // in the direction of edge - let padding = (x1 - x0).length() * 0.1 * direction; - // perpendicular to the edge - let push_out = calculate_gap_vector(direction, stroke_width); - - (x0 + push_out + padding, x1 + push_out - padding) + apply_gizmo_padding_and_offset(x0, x1, stroke_width, false) // push_out outward } fn get_isometric_down_points(column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, stroke_width: Option) -> (DVec2, DVec2) { @@ -389,14 +441,7 @@ fn get_isometric_down_points(column_index: u32, row_index: u32, angles: DVec2, s let x0 = isometric_point_position(row_index + 1, column_index, spacing, angles); let x1 = isometric_point_position(row_index + 1, column_index + 1, spacing, angles); - let direction = (x1 - x0).normalize(); - - // in the direction of edge - let padding = (x1 - x0).length() * 0.1 * direction; - // perpendicular to the edge - let push_out = calculate_gap_vector(direction, stroke_width); - - (x0 - push_out + padding, x1 - push_out - padding) + apply_gizmo_padding_and_offset(x0, x1, stroke_width, true) // push_out inward } fn get_isometric_left_points(column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, stroke_width: Option) -> (DVec2, DVec2) { @@ -404,14 +449,7 @@ fn get_isometric_left_points(column_index: u32, row_index: u32, angles: DVec2, s let x0 = isometric_point_position(row_index, column_index, spacing, angles); let x1 = isometric_point_position(row_index + 1, column_index, spacing, angles); - let direction = (x1 - x0).normalize(); - - // in the direction of edge - let padding = (x1 - x0).length() * 0.1 * direction; - // perpendicular to the edge - let push_out = calculate_gap_vector(direction, stroke_width); - - (x0 - push_out + padding, x1 - push_out - padding) + apply_gizmo_padding_and_offset(x0, x1, stroke_width, true) // push_out inward } fn get_isometric_middle_up_points(column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, stroke_width: Option) -> (DVec2, DVec2) { @@ -429,14 +467,7 @@ fn get_isometric_middle_up_points(column_index: u32, row_index: u32, angles: DVe isometric_point_position(row_index, column_index + 1, spacing, angles), ) }; - let direction = (x1 - x0).normalize(); - - // in the direction of edge - let padding = (x1 - x0).length() * 0.1 * direction; - // perpendicular to the edge - let push_out = calculate_gap_vector(direction, stroke_width); - - (x0 - push_out + padding, x1 - push_out - padding) + apply_gizmo_padding_and_offset(x0, x1, stroke_width, true) // push_out inward } fn get_isometric_middle_down_points(column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, stroke_width: Option) -> (DVec2, DVec2) { @@ -454,18 +485,12 @@ fn get_isometric_middle_down_points(column_index: u32, row_index: u32, angles: D isometric_point_position(row_index, column_index + 1, spacing, angles), ) }; - let direction = (x1 - x0).normalize(); - // in the direction of edge - let padding = (x1 - x0).length() * 0.1 * direction; - // perpendicular to the edge - let push_out = calculate_gap_vector(direction, stroke_width); - - (x0 + push_out + padding, x1 + push_out - padding) + apply_gizmo_padding_and_offset(x0, x1, stroke_width, false) // push_out inward } fn calculate_gap_vector(direction: DVec2, stroke_width: f64) -> DVec2 { let perp = direction.perp().normalize(); - (stroke_width + 0.5) * perp + (stroke_width + 1.) * perp } #[derive(Clone, Copy, Debug, Default, PartialEq)] @@ -519,26 +544,6 @@ pub fn gizmo_line_from_points(p0: DVec2, p1: DVec2, viewport: DAffine2) -> Line convert_to_gizmo_line(viewport.transform_point2(p0), viewport.transform_point2(p1)) } -// pub fn gizmo_opposite_rect(g: RectangularGizmoType) -> RectangularGizmoType { -// match g { -// RectangularGizmoType::Top => RectangularGizmoType::Down, -// RectangularGizmoType::Down => RectangularGizmoType::Top, -// RectangularGizmoType::Right => RectangularGizmoType::Left, -// RectangularGizmoType::Left => RectangularGizmoType::Right, -// } -// } - -// pub fn gizmo_opposite_iso(g: IsometricGizmoType) -> IsometricGizmoType { -// match g { -// IsometricGizmoType::Top => IsometricGizmoType::Down, -// IsometricGizmoType::Down => IsometricGizmoType::Top, -// IsometricGizmoType::Right => IsometricGizmoType::Left, -// IsometricGizmoType::Left => IsometricGizmoType::Right, -// IsometricGizmoType::IsometricMiddleUp => IsometricGizmoType::IsometricMiddleDown, -// IsometricGizmoType::IsometricMiddleDown => IsometricGizmoType::IsometricMiddleUp, -// } -// } - pub fn gizmo_new_spacing_rect(g: RectangularGizmoType, delta: f64, spacing: DVec2) -> DVec2 { match g { RectangularGizmoType::Top | RectangularGizmoType::Down => DVec2::new(spacing.x, spacing.y + delta), @@ -550,10 +555,7 @@ pub fn gizmo_new_spacing_iso(g: IsometricGizmoType, delta: f64, spacing: DVec2) match g { IsometricGizmoType::Top | IsometricGizmoType::Down => DVec2::new(spacing.x, spacing.y + delta), IsometricGizmoType::Right | IsometricGizmoType::Left => DVec2::new(spacing.x + delta, spacing.y), - IsometricGizmoType::IsometricMiddleUp | IsometricGizmoType::IsometricMiddleDown => { - // Adjust both? depends on your desired behavior - DVec2::new(spacing.x + delta, spacing.y + delta) - } + IsometricGizmoType::IsometricMiddleUp | IsometricGizmoType::IsometricMiddleDown => DVec2::new(spacing.x + delta, spacing.y + delta), } } @@ -568,7 +570,7 @@ pub fn gizmo_direction_rect(g: RectangularGizmoType, spacing: DVec2, viewport: D pub fn gizmo_direction_iso(g: IsometricGizmoType, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2) -> DVec2 { let (p1, p2) = get_line_points_for_iso(g, column_index, row_index, angles, spacing, None); - (p1 - p2).perp().normalize() + (p1 - p2).perp().try_normalize().unwrap_or(DVec2::X) } pub fn gizmo_mouse_icon_rect(g: RectangularGizmoType) -> MouseCursorIcon { @@ -595,10 +597,40 @@ impl IsometricGizmoType { pub fn all() -> [Self; 6] { [Self::Top, Self::Right, Self::Down, Self::Left, Self::IsometricMiddleUp, Self::IsometricMiddleDown] } + + pub fn old_row_col_index(&self, row_index: u32, column_index: u32) -> (u32, u32) { + match self { + IsometricGizmoType::Right => (row_index, column_index), + IsometricGizmoType::Left => (row_index, column_index + 1), + IsometricGizmoType::Down => { + if column_index % 2 == 0 { + (row_index, column_index) + } else { + (row_index, column_index + 1) + } + } + IsometricGizmoType::Top => (row_index, column_index), + IsometricGizmoType::IsometricMiddleUp | IsometricGizmoType::IsometricMiddleDown => { + if column_index % 2 == 0 { + (row_index - 1, column_index) + } else { + (row_index, column_index) + } + } + } + } + + pub fn delta_sign(&self) -> f64 { + match self { + IsometricGizmoType::Right => 1., + IsometricGizmoType::Left => -1., + _ => 1., + } + } } impl GridSpacingGizmoType { - pub fn line(&self, grid_type: GridType, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, viewport: DAffine2, stroke_width: Option) -> Line { + pub fn line(&self, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, viewport: DAffine2, stroke_width: Option) -> Line { match self { GridSpacingGizmoType::Rect(g) => { let (p0, p1) = get_line_points_for_rect(*g, column_index, row_index, spacing, stroke_width); @@ -672,10 +704,10 @@ impl GridSpacingGizmoType { } }, - GridSpacingGizmoType::Iso(_) => { - // Placeholder: no transformation for now - DAffine2::IDENTITY - } + GridSpacingGizmoType::Iso(gizmo_type) => match gizmo_type { + IsometricGizmoType::Right | IsometricGizmoType::Left => DAffine2::from_translation(-spacing_delta), + _ => DAffine2::IDENTITY, + }, // Placeholder: no transformation for now } } }