impl spacing gizmos for isometric grid

This commit is contained in:
0SlowPoke0
2025-08-18 11:58:03 +05:30
parent f7629e3443
commit b24315e162
2 changed files with 259 additions and 232 deletions

View File

@@ -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,

View File

@@ -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<GridSpacingGizmoType>,
}
@@ -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<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 {
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<Message>,
) {
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<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 {
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<Message>,
) {
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<Message>,
) {
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<f64>) -> (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<f64>) -> 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<f64>) -> 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<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);
@@ -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<f64>) -> (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<f64>) -> (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<f64>) -> (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<f64>) -> (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<f64>) -> (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<f64>) -> (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<f64>) -> Line {
pub fn line(&self, column_index: u32, row_index: u32, angles: DVec2, spacing: DVec2, viewport: DAffine2, stroke_width: Option<f64>) -> 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
}
}
}