Repository: GraphiteEditor/Graphite (language: rust)

Issue:
Tracking Issue: Quick measurement overlays while holding the Alt key
- [x] First basic implementation (#1894)
- [x] Improvements to match the spec described below (#2147)
- [x] Tidy up the number text alignment and decimal precision (#2155)
- [ ] Make it work when the layers are rotated and skewed

---

When holding the <kbd>Alt</kbd> key with a layer (or multiple) selected, while hovering over another layer (or group), an overlay should appear visualizing the AABB of both the selected and hovered layers, with lines and distance numbers (in document coordinates) between them as shown below.

When the document is viewed with a tilt in the viewport PTZ navigation, the lines become angled but the text labels stay upright for readability.

Here are, I believe, all the scenarios. The three diagrams with text do care about which is the selected layer and which is the hovered layer; all the others are drawn the same in either order, so it doesn't matter which is the selected or hovered one. The two colored boxes represent AABBs, not actual shapes in the artwork— those should be drawn with a blue outline around both their AABBs.

| Scenarios |
| - |
| <strong>Terminology definitions:</strong><br /><br />- "Boxes overlap along 0/1/2 axes" means extruding the box's edge infinitely left/right, or up/down, would hit the other box<br />- "0/1/2 of their centerlines cross the other boxes" means extending both box's center points infinitely left/right, or up/down, would hit the other box<br />- "Along axis A/B" means either the X and Y, or the Y and X, axes<br />- "box A/B" means either one particular box and its opposite, or the reverse<br /><br /> |
| Boxes overlap along 0 axes:<br /><br />![image](https://github.com/user-attachments/assets/4f1e6752-f4c4-4494-8b76-c34b3903f20b) |
| Boxes overlap along 1 axis and 0 of their centerlines cross the other boxes:<br /><br />![image](https://github.com/user-attachments/assets/3dd9bbc2-a55a-4465-b5bb-7c9bd1511f15) |
| Boxes overlap along 1 axis and 2 of their centerlines cross the other boxes:<br /><br />![image](https://github.com/user-attachments/assets/79bb251b-31bd-49d6-90a4-cdc4518715fb) |
| Boxes overlap along 1 axis and 1 of their centerlines cross the other boxes:<br /><br />![image](https://github.com/user-attachments/assets/2fdd2181-93f8-4780-b7d5-afdee3f9ab09) |
| Boxes overlap along 2 axes and:<br />- Along axis A, box A has 1 edge line cross box B while box B has 1 edge line cross box A<br />- Along axis B, box A has 1 edge line cross box B while box B has 1 edge line cross box A<br /><br />![capture](https://github.com/user-attachments/assets/e73f8059-4c04-4859-829a-eec18d80226d) |
| Boxes overlap along 2 axes and:<br />- Along axis A, box A has 1 edge line cross box B while box B has 1 edge line cross box A<br />- Along axis B, box A has 2 edge lines cross box B while box B has 0 edge lines cross box A<br /><br />![capture](https://github.com/user-attachments/assets/98f2921b-827b-41db-acf1-2aedbfb0cfb3) |
| Boxes overlap along 2 axes and:<br />- Along axis A, box A has 2 edge lines cross box B while box B has 0 edge lines cross box A<br />- Along axis B, box A has 0 edge lines cross box B while box B has 2 edge lines cross box A<br /><br />![capture](https://github.com/user-attachments/assets/b097c8aa-e205-4c2d-8182-1f5e6c96ffa1) |
| Boxes overlap along 2 axes and:<br />- Along axis A, box A has 2 edge lines cross box B while box B has 0 edge lines cross box A<br />- Along axis B, box A has 1 edge line cross box B while box B has 1 edge line cross box A<br /><br />![capture](https://github.com/user-attachments/assets/55f15296-86d9-42cc-814a-0c2f0beadfa2) |
|Boxes overlap along 2 axes and:<br />- Along axis A, box A has 2 edge lines cross box B while box B has 0 edge lines cross box A<br />- Along axis B, box A has 2 edge lines cross box B while box B has 0 edge lines cross box A<br /><br />![capture](https://github.com/user-attachments/assets/e31c5473-fc33-4772-a600-fe33e94270ab) |

Diagram source file: [Quick Measurement Overlays Design.graphite.txt](https://github.com/user-attachments/files/16493997/Quick.Measurement.Overlays.Design.graphite.txt)


Discussion / hints:

Fix patch:
diff --git a/editor/src/messages/portfolio/document/overlays/grid_overlays.rs b/editor/src/messages/portfolio/document/overlays/grid_overlays.rs
index dbed875dcd..3c3c05309c 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 2e3fd64930..cb0b15dff6 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<f64>) {
+	pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option<f64>, gap_width: Option<f64>) {
 		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 14b8a85e2a..fbbd47dfa6 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);
+
+	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],
+	);
+}
+
+/// 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
+}
+
+/// 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
+		};
 
-	// 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);
-
-	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);
-
-	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]);
+		// 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);
 	}
-	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]);
+}
+
+/// 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
 	}
 }


Test patch: