From a1dc9556afffb8dd8ea6a01619f3c7982c4a2bf9 Mon Sep 17 00:00:00 2001 From: Utsav Singh Date: Mon, 23 Dec 2024 08:50:03 +0530 Subject: [PATCH] Improve quick measurement overlays across all possible arrangement scenarios (#2147) * setup single edge crossing handling for quick measure tool * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. * setup single edge crossing handling for quick measure tool * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. Add a dashed_line_with_pattern function that gives more granular control over dash_width and gap_width. * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. Add a dashed_line_with_pattern function that gives more granular control over dash_width and gap_width. * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. Add a dashed_line_with_pattern function that gives more granular control over dash_width and gap_width. * Setup quick measure functionality for intersecting objects and handle centerline cases for non intersecting objects. Add a dashed_line_with_pattern function that gives more granular control over dash_width and gap_width. * Nit picks * Define wrapper for Overlay_context line method * Comments and consolidating lines of code --------- Co-authored-by: Keavon Chambers --- .../document/overlays/grid_overlays.rs | 1 + .../document/overlays/utility_types.rs | 13 +- .../tool/common_functionality/measure.rs | 428 ++++++++++++++++-- 3 files changed, 412 insertions(+), 30 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/grid_overlays.rs b/editor/src/messages/portfolio/document/overlays/grid_overlays.rs index dbed875d..3c3c0530 100644 --- a/editor/src/messages/portfolio/document/overlays/grid_overlays.rs +++ b/editor/src/messages/portfolio/document/overlays/grid_overlays.rs @@ -184,6 +184,7 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context document_to_viewport.transform_point2(end), Some(&("#".to_string() + &grid_color.rgba_hex())), Some((spacing_x / cos_a) * document_to_viewport.matrix2.x_axis.length()), + None, ); } } diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index 2e3fd649..cb0b15df 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -46,16 +46,17 @@ impl OverlayContext { } pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>) { - self.dashed_line(start, end, color, None) + self.dashed_line(start, end, color, None, None) } - pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option) { + pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option, gap_width: Option) { let start = start.round() - DVec2::splat(0.5); let end = end.round() - DVec2::splat(0.5); if let Some(dash_width) = dash_width { + let gap_width = gap_width.unwrap_or(1.); let array = js_sys::Array::new(); - array.push(&JsValue::from(1)); array.push(&JsValue::from(dash_width - 1.)); + array.push(&JsValue::from(gap_width)); self.render_context .set_line_dash(&JsValue::from(array)) .map_err(|error| log::warn!("Error drawing dashed line: {:?}", error)) @@ -72,6 +73,12 @@ impl OverlayContext { self.render_context.line_to(end.x, end.y); self.render_context.set_stroke_style_str(color.unwrap_or(COLOR_OVERLAY_BLUE)); self.render_context.stroke(); + + // Reset the dash pattern to solid after drawing + self.render_context + .set_line_dash(&JsValue::from(js_sys::Array::new())) + .map_err(|error| log::warn!("Error drawing dashed line: {:?}", error)) + .ok(); } pub fn manipulator_handle(&mut self, position: DVec2, selected: bool) { diff --git a/editor/src/messages/tool/common_functionality/measure.rs b/editor/src/messages/tool/common_functionality/measure.rs index 14b8a85e..fbbd47df 100644 --- a/editor/src/messages/tool/common_functionality/measure.rs +++ b/editor/src/messages/tool/common_functionality/measure.rs @@ -4,36 +4,410 @@ use crate::messages::tool::tool_messages::tool_prelude::*; use graphene_std::renderer::Rect; -pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { +/// Draws a dashed line between two points transformed by the given affine transformation. +fn draw_dashed_line(line_start: DVec2, line_end: DVec2, transform: DAffine2, overlay_context: &mut OverlayContext) { + let min_viewport = transform.transform_point2(line_start); + let max_viewport = transform.transform_point2(line_end); + + overlay_context.dashed_line(min_viewport, max_viewport, None, Some(2.), Some(3.)); +} + +/// Draws a solid line with a length annotation between two points transformed by the given affine transformations. +fn draw_line_with_length(line_start: DVec2, line_end: DVec2, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { let transform_to_document = document_to_viewport.inverse() * transform; - if selected_bounds.intersects(hovered_bounds) { - // TODO: Figure out what to do here - return; - } + let min_viewport = transform.transform_point2(line_start); + let max_viewport = transform.transform_point2(line_end); - // Always do horizontal then vertical from the selected - let turn_x = selected_bounds.center().x.clamp(hovered_bounds.min().x, hovered_bounds.max().x); - let turn_y = hovered_bounds.center().y.clamp(selected_bounds.min().y, selected_bounds.max().y); + overlay_context.line(min_viewport, max_viewport, None); + let length = format!("{:.2}", transform_to_document.transform_vector2(line_end - line_start).length()); + overlay_context.text( + &length, + COLOR_OVERLAY_BLUE, + None, + DAffine2::from_translation((min_viewport + max_viewport) / 2.), + 5., + [Pivot::Middle, Pivot::Start], + ); +} - let selected_x = turn_x.clamp(selected_bounds.min().x, selected_bounds.max().x); - let hovered_y = turn_y.clamp(hovered_bounds.min().y, hovered_bounds.max().y); +/// Checks if the selected bounds overlap with the hovered bounds on the Y-axis. +fn does_overlap_y(selected_bounds: Rect, hovered_bounds: Rect) -> bool { + selected_bounds.min().x < hovered_bounds.max().x && selected_bounds.max().x > hovered_bounds.min().x +} - if turn_x != selected_x { - let min_viewport = transform.transform_point2(DVec2::new(turn_x.min(selected_x), turn_y)); - let max_viewport = transform.transform_point2(DVec2::new(turn_x.max(selected_x), turn_y)); - overlay_context.line(min_viewport, max_viewport, None); - let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::X * (turn_x - selected_x)).length()); - let direction = -(min_viewport - max_viewport).normalize_or_zero(); - let transform = DAffine2::from_translation((min_viewport + max_viewport) / 2.) * DAffine2::from_angle(-direction.angle_to(DVec2::X)); - overlay_context.text(&length, COLOR_OVERLAY_BLUE, None, transform, 5., [Pivot::Middle, Pivot::Start]); - } - if turn_y != hovered_y { - let min_viewport = transform.transform_point2(DVec2::new(turn_x, turn_y.min(hovered_y))); - let max_viewport = transform.transform_point2(DVec2::new(turn_x, turn_y.max(hovered_y))); - overlay_context.line(min_viewport, max_viewport, None); - let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::Y * (turn_y - hovered_y)).length()); - let direction = (min_viewport - max_viewport).normalize_or_zero().perp(); - let transform = DAffine2::from_translation((min_viewport + max_viewport) / 2.) * DAffine2::from_angle(-direction.angle_to(DVec2::X)); - overlay_context.text(&length, COLOR_OVERLAY_BLUE, None, transform, 5., [Pivot::Start, Pivot::Middle]); +/// Checks if the selected bounds overlap with the hovered bounds on the X-axis. +fn does_overlap_x(selected_bounds: Rect, hovered_bounds: Rect) -> bool { + selected_bounds.min().y < hovered_bounds.max().y && selected_bounds.max().y > hovered_bounds.min().y +} + +/// Draws measurements when both X and Y axes are involved in the overlap between selected and hovered bounds. +fn draw_zero_axis_crossings(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + let selected_on_right = selected_min.x > hovered_max.x; + let selected_on_bottom = selected_min.y > hovered_max.y; + + let selected_y = if selected_on_bottom { selected_min.y } else { selected_max.y }; + let hovered_y = if selected_on_bottom { hovered_max.y } else { hovered_min.y }; + let selected_x = if selected_on_right { selected_min.x } else { selected_max.x }; + let hovered_x = if selected_on_right { hovered_max.x } else { hovered_min.x }; + + // Draw horizontal solid line with length + let line_start = DVec2::new(selected_x, selected_y); + let line_end = DVec2::new(hovered_x, selected_y); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + + // Draw horizontal dashed line + let line_start = DVec2::new(selected_x, hovered_y); + let line_end = DVec2::new(hovered_x, hovered_y); + draw_dashed_line(line_start, line_end, transform, overlay_context); + + // Draw vertical solid line with length + let line_start = DVec2::new(selected_x, selected_y); + let line_end = DVec2::new(selected_x, hovered_y); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + + // Draw vertical dashed line + let line_start = DVec2::new(hovered_x, selected_y); + let line_end = DVec2::new(hovered_x, hovered_y); + draw_dashed_line(line_start, line_end, transform, overlay_context); +} + +/// Draws measurements when only one axis is involved in the overlap between selected and hovered bounds. +fn draw_single_axis_zero_crossings(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + let overlap_y = does_overlap_y(selected_bounds, hovered_bounds); + let overlap_x = does_overlap_x(selected_bounds, hovered_bounds); + + if overlap_y { + let selected_facing_edge = if hovered_max.y < selected_min.y { selected_min.y } else { selected_max.y }; + let hovered_facing_edge = if hovered_max.y < selected_min.y { hovered_max.y } else { hovered_min.y }; + let vertical_line_start_x = if hovered_max.x > selected_max.x { selected_max.x } else { selected_min.x }; + let dashed_vertical_line_start_x = if hovered_max.x > selected_max.x { hovered_min.x } else { hovered_max.x }; + + // Draw horizontal solid line with length + let line_start = DVec2::new(f64::min(hovered_max.x, selected_max.x), selected_facing_edge); + let line_end = DVec2::new(f64::max(hovered_min.x, selected_min.x), selected_facing_edge); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + + // Draw vertical solid line with length + let line_start = DVec2::new(vertical_line_start_x, selected_facing_edge); + let line_end = DVec2::new(vertical_line_start_x, hovered_facing_edge); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + + // Draw vertical dashed line + let dashed_line_start = DVec2::new(dashed_vertical_line_start_x, selected_facing_edge); + let dashed_line_end = DVec2::new(dashed_vertical_line_start_x, hovered_facing_edge); + draw_dashed_line(dashed_line_start, dashed_line_end, transform, overlay_context); + } else if overlap_x { + let selected_facing_edge = if hovered_max.x < selected_min.x { selected_min.x } else { selected_max.x }; + let hovered_facing_edge = if hovered_max.x < selected_min.x { hovered_max.x } else { hovered_min.x }; + let horizontal_line_start_y = if hovered_max.y > selected_max.y { selected_max.y } else { selected_min.y }; + let dashed_horizontal_line_start_y = if hovered_max.y > selected_max.y { hovered_min.y } else { hovered_max.y }; + + // Draw vertical solid line with length + let line_start = DVec2::new(selected_facing_edge, f64::min(hovered_max.y, selected_max.y)); + let line_end = DVec2::new(selected_facing_edge, f64::max(hovered_min.y, selected_min.y)); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + + // Draw horizontal solid line with length + let line_start = DVec2::new(selected_facing_edge, horizontal_line_start_y); + let line_end = DVec2::new(hovered_facing_edge, horizontal_line_start_y); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + + // Draw horizontal dashed line + let dashed_line_start = DVec2::new(selected_facing_edge, dashed_horizontal_line_start_y); + let dashed_line_end = DVec2::new(hovered_facing_edge, dashed_horizontal_line_start_y); + draw_dashed_line(dashed_line_start, dashed_line_end, transform, overlay_context); + } +} + +/// Draws measurements when only one axis is involved and there is one crossing between selected and hovered bounds. +fn draw_single_axis_one_crossings(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + let selected_center = selected_bounds.center(); + let hovered_center = hovered_bounds.center(); + + let overlap_y = does_overlap_y(selected_bounds, hovered_bounds); + let overlap_x = does_overlap_x(selected_bounds, hovered_bounds); + + if overlap_y { + let selected_facing_edge = if hovered_max.y < selected_min.y { selected_min.y } else { selected_max.y }; + let hovered_facing_edge = if hovered_max.y < selected_min.y { hovered_max.y } else { hovered_min.y }; + let vertical_line_start = if selected_center.x < hovered_max.x && selected_center.x > hovered_min.x { + selected_center.x + } else { + hovered_center.x + }; + + // Draw vertical solid line with length + let line_start = DVec2::new(vertical_line_start, selected_facing_edge); + let line_end = DVec2::new(vertical_line_start, hovered_facing_edge); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + } else if overlap_x { + let selected_facing_edge = if hovered_max.x < selected_min.x { selected_min.x } else { selected_max.x }; + let hovered_facing_edge = if hovered_max.x < selected_min.x { hovered_max.x } else { hovered_min.x }; + let horizontal_line_start_y = if selected_center.y < hovered_max.y && selected_center.y > hovered_min.y { + selected_center.y + } else { + hovered_center.y + }; + + // Draw horizontal solid line with length + let line_start = DVec2::new(selected_facing_edge, horizontal_line_start_y); + let line_end = DVec2::new(hovered_facing_edge, horizontal_line_start_y); + draw_line_with_length(line_start, line_end, transform, document_to_viewport, overlay_context); + } +} + +/// Draws measurements for cases where lines cross on both X and Y axes, handling diagonal intersections. +fn draw_two_axis_one_one_crossing(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + let mut top_y_bound = f64::min(selected_min.y, hovered_min.y); + let mut bottom_y_bound = f64::max(selected_max.y, hovered_max.y); + let mut top_x_bound = f64::max(selected_max.x, hovered_max.x); + let mut bottom_x_bound = f64::min(selected_min.x, hovered_min.x); + + // Handle diagonal intersection cases by swapping bounds if necessary + if (hovered_bounds.center().x > selected_bounds.center().x && hovered_bounds.center().y < selected_bounds.center().y) + || (hovered_bounds.center().x < selected_bounds.center().x && hovered_bounds.center().y > selected_bounds.center().y) + { + std::mem::swap(&mut top_y_bound, &mut bottom_y_bound); + std::mem::swap(&mut top_x_bound, &mut bottom_x_bound); + } + + // Draw horizontal solid lines with length + let top_x_start = DVec2::new(f64::min(selected_max.x, hovered_max.x), top_y_bound); + let top_x_end = DVec2::new(f64::max(selected_max.x, hovered_max.x), top_y_bound); + draw_line_with_length(top_x_start, top_x_end, transform, document_to_viewport, overlay_context); + + let bottom_x_start = DVec2::new(f64::min(selected_min.x, hovered_min.x), bottom_y_bound); + let bottom_x_end = DVec2::new(f64::max(selected_min.x, hovered_min.x), bottom_y_bound); + draw_line_with_length(bottom_x_start, bottom_x_end, transform, document_to_viewport, overlay_context); + + // Draw vertical solid lines with length + let top_y_start = DVec2::new(top_x_bound, f64::min(selected_min.y, hovered_min.y)); + let top_y_end = DVec2::new(top_x_bound, f64::max(selected_min.y, hovered_min.y)); + draw_line_with_length(top_y_start, top_y_end, transform, document_to_viewport, overlay_context); + + let bottom_y_start = DVec2::new(bottom_x_bound, f64::min(selected_max.y, hovered_max.y)); + let bottom_y_end = DVec2::new(bottom_x_bound, f64::max(selected_max.y, hovered_max.y)); + draw_line_with_length(bottom_y_start, bottom_y_end, transform, document_to_viewport, overlay_context); +} + +/// Draws measurements for partial overlaps with two vertical or horizontal edge intersections. +fn draw_two_axis_one_one_two_zero_crossing( + selected_bounds: Rect, + hovered_bounds: Rect, + transform: DAffine2, + document_to_viewport: DAffine2, + overlay_context: &mut OverlayContext, + two_vertical_edge_intersect: bool, +) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + if two_vertical_edge_intersect { + let selected_bound_edge = if selected_bounds.center().y >= hovered_bounds.center().y { + selected_max.y + } else { + selected_min.y + }; + let hovered_bound_edge = if selected_bounds.center().y >= hovered_bounds.center().y { hovered_max.y } else { hovered_min.y }; + + // Draw vertical solid lines with length + let y_start_left = DVec2::new(hovered_min.x, f64::min(selected_bound_edge, hovered_bound_edge)); + let y_end_left = DVec2::new(hovered_min.x, f64::max(selected_bound_edge, hovered_bound_edge)); + draw_line_with_length(y_start_left, y_end_left, transform, document_to_viewport, overlay_context); + + let y_start_right = DVec2::new(hovered_max.x, f64::min(selected_bound_edge, hovered_bound_edge)); + let y_end_right = DVec2::new(hovered_max.x, f64::max(selected_bound_edge, hovered_bound_edge)); + draw_line_with_length(y_start_right, y_end_right, transform, document_to_viewport, overlay_context); + + // Draw horizontal solid lines with length + let horizontal_line_y_bound = if selected_bounds.center().y >= hovered_bounds.center().y { + f64::max(selected_bound_edge, hovered_bound_edge) + } else { + f64::min(selected_bound_edge, hovered_bound_edge) + }; + + let x_start_left = DVec2::new(hovered_min.x, horizontal_line_y_bound); + let x_end_left = DVec2::new(selected_min.x, horizontal_line_y_bound); + draw_line_with_length(x_start_left, x_end_left, transform, document_to_viewport, overlay_context); + + let x_start_right = DVec2::new(hovered_max.x, horizontal_line_y_bound); + let x_end_right = DVec2::new(selected_max.x, horizontal_line_y_bound); + draw_line_with_length(x_start_right, x_end_right, transform, document_to_viewport, overlay_context); + } else { + let selected_bound_edge = if selected_bounds.center().x >= hovered_bounds.center().x { + selected_max.x + } else { + selected_min.x + }; + let hovered_bound_edge = if selected_bounds.center().x >= hovered_bounds.center().x { hovered_max.x } else { hovered_min.x }; + + // Determine the outermost X position for vertical lines + let vertical_line_x = if selected_bounds.center().x >= hovered_bounds.center().x { + f64::max(selected_bound_edge, hovered_bound_edge) + } else { + f64::min(selected_bound_edge, hovered_bound_edge) + }; + + // Draw vertical solid lines with length + let y_start_up = DVec2::new(vertical_line_x, selected_min.y); + let y_end_up = DVec2::new(vertical_line_x, hovered_min.y); + draw_line_with_length(y_start_up, y_end_up, transform, document_to_viewport, overlay_context); + + let y_start_down = DVec2::new(vertical_line_x, selected_max.y); + let y_end_down = DVec2::new(vertical_line_x, hovered_max.y); + draw_line_with_length(y_start_down, y_end_down, transform, document_to_viewport, overlay_context); + + // Draw horizontal solid lines with length + let horizontal_line_inner_x = if selected_bounds.center().x >= hovered_bounds.center().x { + f64::min(selected_bound_edge, hovered_bound_edge) + } else { + f64::max(selected_bound_edge, hovered_bound_edge) + }; + let x_start_up = DVec2::new(vertical_line_x, f64::min(selected_min.y, hovered_min.y)); + let x_end_up = DVec2::new(horizontal_line_inner_x, f64::min(selected_min.y, hovered_min.y)); + draw_line_with_length(x_start_up, x_end_up, transform, document_to_viewport, overlay_context); + + let x_start_down = DVec2::new(vertical_line_x, f64::max(selected_max.y, hovered_max.y)); + let x_end_down = DVec2::new(horizontal_line_inner_x, f64::max(selected_max.y, hovered_max.y)); + draw_line_with_length(x_start_down, x_end_down, transform, document_to_viewport, overlay_context); + } +} + +/// Draws measurements for cases with two vertical and two horizontal zero crossings. +fn draw_two_axis_two_zero_zero_two(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + // Draw vertical solid lines with length + let y_start_left_top = DVec2::new(f64::min(hovered_min.x, selected_min.x), f64::min(hovered_min.y, selected_min.y)); + let y_end_left_top = DVec2::new(f64::min(hovered_min.x, selected_min.x), f64::max(hovered_min.y, selected_min.y)); + draw_line_with_length(y_start_left_top, y_end_left_top, transform, document_to_viewport, overlay_context); + + let y_start_left_bottom = DVec2::new(f64::min(hovered_min.x, selected_min.x), f64::min(hovered_max.y, selected_max.y)); + let y_end_left_bottom = DVec2::new(f64::min(hovered_min.x, selected_min.x), f64::max(hovered_max.y, selected_max.y)); + draw_line_with_length(y_start_left_bottom, y_end_left_bottom, transform, document_to_viewport, overlay_context); + + let y_start_right_top = DVec2::new(f64::max(hovered_max.x, selected_max.x), f64::min(hovered_min.y, selected_min.y)); + let y_end_right_top = DVec2::new(f64::max(hovered_max.x, selected_max.x), f64::max(hovered_min.y, selected_min.y)); + draw_line_with_length(y_start_right_top, y_end_right_top, transform, document_to_viewport, overlay_context); + + let y_start_right_bottom = DVec2::new(f64::max(hovered_max.x, selected_max.x), f64::min(hovered_max.y, selected_max.y)); + let y_end_right_bottom = DVec2::new(f64::max(hovered_max.x, selected_max.x), f64::max(hovered_max.y, selected_max.y)); + draw_line_with_length(y_start_right_bottom, y_end_right_bottom, transform, document_to_viewport, overlay_context); + + // Draw horizontal solid lines with length + let x_start_left_top = DVec2::new(f64::min(hovered_min.x, selected_min.x), f64::min(hovered_min.y, selected_min.y)); + let x_end_left_top = DVec2::new(f64::max(hovered_min.x, selected_min.x), f64::min(hovered_min.y, selected_min.y)); + draw_line_with_length(x_start_left_top, x_end_left_top, transform, document_to_viewport, overlay_context); + + let x_start_right_top = DVec2::new(f64::min(hovered_max.x, selected_max.x), f64::min(hovered_min.y, selected_min.y)); + let x_end_right_top = DVec2::new(f64::max(hovered_max.x, selected_max.x), f64::min(hovered_min.y, selected_min.y)); + draw_line_with_length(x_start_right_top, x_end_right_top, transform, document_to_viewport, overlay_context); + + let x_start_left_bottom = DVec2::new(f64::min(hovered_min.x, selected_min.x), f64::max(hovered_max.y, selected_max.y)); + let x_end_left_bottom = DVec2::new(f64::max(hovered_min.x, selected_min.x), f64::max(hovered_max.y, selected_max.y)); + draw_line_with_length(x_start_left_bottom, x_end_left_bottom, transform, document_to_viewport, overlay_context); + + let x_start_right_bottom = DVec2::new(f64::min(hovered_max.x, selected_max.x), f64::max(hovered_max.y, selected_max.y)); + let x_end_right_bottom = DVec2::new(f64::max(hovered_max.x, selected_max.x), f64::max(hovered_max.y, selected_max.y)); + draw_line_with_length(x_start_right_bottom, x_end_right_bottom, transform, document_to_viewport, overlay_context); +} + +/// Draws measurements where selected and hovered bounds have two vertical edges crossing each other. +fn draw_two_axis_two_zero_two_zero(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + // Draw horizontal solid lines with length + let x_start_left = DVec2::new(f64::max(hovered_min.x, selected_min.x), selected_bounds.center().y); + let x_end_left = DVec2::new(f64::min(hovered_min.x, selected_min.x), selected_bounds.center().y); + draw_line_with_length(x_start_left, x_end_left, transform, document_to_viewport, overlay_context); + + let x_start_right = DVec2::new(f64::min(hovered_max.x, selected_max.x), selected_bounds.center().y); + let x_end_right = DVec2::new(f64::max(hovered_max.x, selected_max.x), selected_bounds.center().y); + draw_line_with_length(x_start_right, x_end_right, transform, document_to_viewport, overlay_context); + + // Draw vertical solid lines with length + let y_start_top = DVec2::new(selected_bounds.center().x, f64::max(hovered_min.y, selected_min.y)); + let y_end_top = DVec2::new(selected_bounds.center().x, f64::min(hovered_min.y, selected_min.y)); + draw_line_with_length(y_start_top, y_end_top, transform, document_to_viewport, overlay_context); + + let y_start_bottom = DVec2::new(selected_bounds.center().x, f64::min(hovered_max.y, selected_max.y)); + let y_end_bottom = DVec2::new(selected_bounds.center().x, f64::max(hovered_max.y, selected_max.y)); + draw_line_with_length(y_start_bottom, y_end_bottom, transform, document_to_viewport, overlay_context); +} + +/// Handles overlapping scenarios involving two axes between selected and hovered bounds. +fn handle_two_axis_overlap(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + // Calculate edge crossings on the X-axis + let selected_x_crosses = (selected_min.y >= hovered_min.y && selected_min.y <= hovered_max.y) as u8 + (selected_max.y >= hovered_min.y && selected_max.y <= hovered_max.y) as u8; + let hovered_x_crosses = (hovered_min.y >= selected_min.y && hovered_min.y <= selected_max.y) as u8 + (hovered_max.y >= selected_min.y && hovered_max.y <= selected_max.y) as u8; + + // Calculate edge crossings on the Y-axis + let selected_y_crosses = (selected_min.x >= hovered_min.x && selected_min.x <= hovered_max.x) as u8 + (selected_max.x >= hovered_min.x && selected_max.x <= hovered_max.x) as u8; + let hovered_y_crosses = (hovered_min.x >= selected_min.x && hovered_min.x <= selected_max.x) as u8 + (hovered_max.x >= selected_min.x && hovered_max.x <= selected_max.x) as u8; + + // Determine the overlap case based on edge crossings + match ((selected_x_crosses, hovered_x_crosses), (selected_y_crosses, hovered_y_crosses)) { + ((1, 1), (1, 1)) => draw_two_axis_one_one_crossing(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context), + ((1, 1), (2, 0)) => draw_two_axis_one_one_two_zero_crossing(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context, true), + ((1, 1), (0, 2)) => draw_two_axis_one_one_two_zero_crossing(hovered_bounds, selected_bounds, transform, document_to_viewport, overlay_context, true), + ((2, 0), (1, 1)) => draw_two_axis_one_one_two_zero_crossing(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context, false), + ((0, 2), (1, 1)) => draw_two_axis_one_one_two_zero_crossing(hovered_bounds, selected_bounds, transform, document_to_viewport, overlay_context, false), + ((2, 0), (0, 2)) | ((0, 2), (2, 0)) => draw_two_axis_two_zero_zero_two(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context), + ((2, 0), (2, 0)) | ((0, 2), (0, 2)) => draw_two_axis_two_zero_two_zero(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context), + _ => (), + } +} + +/// Overlays measurement lines between selected and hovered bounds based on their spatial relationships. +pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { + // TODO: Apply object rotation to bounds before drawing lines for all cases. + + let (selected_min, selected_max) = (selected_bounds.min(), selected_bounds.max()); + let (hovered_min, hovered_max) = (hovered_bounds.min(), hovered_bounds.max()); + + // Determine axis overlaps + let overlap_x = selected_min.x <= hovered_max.x && selected_max.x >= hovered_min.x; + let overlap_y = selected_min.y <= hovered_max.y && selected_max.y >= hovered_min.y; + let overlap_axes = match (overlap_x, overlap_y) { + (true, true) => 2, + (true, false) | (false, true) => 1, + _ => 0, + }; + + // Determine centerline crossings + let center_x_intersects = + (selected_bounds.center().y >= hovered_min.y && selected_bounds.center().y <= hovered_max.y) || (hovered_bounds.center().y >= selected_min.y && hovered_bounds.center().y <= selected_max.y); + let center_y_intersects = + (selected_bounds.center().x >= hovered_min.x && selected_bounds.center().x <= hovered_max.x) || (hovered_bounds.center().x >= selected_min.x && hovered_bounds.center().x <= selected_max.x); + let centerline_crosses = match (center_x_intersects, center_y_intersects) { + (true, true) => 2, + (true, false) | (false, true) => 1, + _ => 0, + }; + + // Handle each overlap case + match (overlap_axes, centerline_crosses) { + (0, _) => draw_zero_axis_crossings(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context), + (1, 0) => draw_single_axis_zero_crossings(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context), + (1, 1) | (1, 2) => draw_single_axis_one_crossings(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context), + (2, _) => handle_two_axis_overlap(selected_bounds, hovered_bounds, transform, document_to_viewport, overlay_context), + _ => (), // Fallback case, should not typically happen } }