Repository: GraphiteEditor/Graphite (language: rust)

Issue:
Snake-like wires in the node graph
Wires between nodes currently follow bendy, angled curves:

![capture](https://github.com/user-attachments/assets/c76da177-298f-411b-ac66-855ad7a969f5)

We want to redesign this style of curvature so it follows only right-angle horizontal and vertical paths:

![capture](https://github.com/user-attachments/assets/f960be35-2f38-455d-a9a5-9f16427e909f)

That means it should follow "snake-like" curves with these numbers of bend points + end points:

![capture](https://github.com/user-attachments/assets/f8397875-5b8d-4a21-8369-3f223d0ea6e8)

Where the red arrows indicate, those horizontal/vertical segments should be able to shift in space to avoid overlapping obstacles like nodes and running on top of other wires in the same direction of travel.

The first part of this issue involves simply devising an algorithm to produce the coordinates of these snake ends/bends, with the end points specified and the offsets of the shiftable segments given as an array. Then constructing an SVG path that rounds/fillets the corners. Subsequent work can then integrate it as a replacement for the existing curve code ([see this code](https://github.com/GraphiteEditor/Graphite/blob/set-text-width/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs#L2351)) and determine the appropriate array of segment shifts to produce a result that nicely avoids overlapping nodes and other wires, as shown in the high-fidelity node graph UI mockup above.

A video on a related topic: https://www.youtube.com/watch?v=ysaIAwxl7fc


Discussion / hints:

Fix patch:
diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs
index a4b13e70fb..8c0df6d42c 100644
--- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs
+++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs
@@ -1,4 +1,5 @@
 use crate::messages::layout::utility_types::widget_prelude::*;
+use crate::messages::portfolio::document::node_graph::utility_types::GraphWireStyle;
 use crate::messages::preferences::SelectionMode;
 use crate::messages::prelude::*;
 
@@ -32,6 +33,29 @@ impl PreferencesDialogMessageHandler {
 	const TITLE: &'static str = "Editor Preferences";
 
 	fn layout(&self, preferences: &PreferencesMessageHandler) -> Layout {
+		// =====
+		// INPUT
+		// =====
+
+		let zoom_with_scroll_tooltip = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)";
+		let input_section = vec![TextLabel::new("Input").italic(true).widget_holder()];
+		let zoom_with_scroll = vec![
+			CheckboxInput::new(preferences.zoom_with_scroll)
+				.tooltip(zoom_with_scroll_tooltip)
+				.on_update(|checkbox_input: &CheckboxInput| {
+					PreferencesMessage::ModifyLayout {
+						zoom_with_scroll: checkbox_input.checked,
+					}
+					.into()
+				})
+				.widget_holder(),
+			TextLabel::new("Zoom with Scroll").table_align(true).tooltip(zoom_with_scroll_tooltip).widget_holder(),
+		];
+
+		// =========
+		// SELECTION
+		// =========
+
 		let selection_section = vec![TextLabel::new("Selection").italic(true).widget_holder()];
 		let selection_mode = RadioInput::new(vec![
 			RadioEntryData::new(SelectionMode::Touched.to_string())
@@ -65,20 +89,28 @@ impl PreferencesDialogMessageHandler {
 		.selected_index(Some(preferences.selection_mode as u32))
 		.widget_holder();
 
-		let zoom_with_scroll_tooltip = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)";
-		let input_section = vec![TextLabel::new("Input").italic(true).widget_holder()];
-		let zoom_with_scroll = vec![
-			CheckboxInput::new(preferences.zoom_with_scroll)
-				.tooltip(zoom_with_scroll_tooltip)
-				.on_update(|checkbox_input: &CheckboxInput| {
-					PreferencesMessage::ModifyLayout {
-						zoom_with_scroll: checkbox_input.checked,
-					}
-					.into()
-				})
-				.widget_holder(),
-			TextLabel::new("Zoom with Scroll").table_align(true).tooltip(zoom_with_scroll_tooltip).widget_holder(),
-		];
+		// ================
+		// NODE GRAPH WIRES
+		// ================
+
+		let node_graph_section_tooltip = "Appearance of the wires running between node connections in the graph";
+		let node_graph_section = vec![TextLabel::new("Node Graph Wires").tooltip(node_graph_section_tooltip).italic(true).widget_holder()];
+		let graph_wire_style = RadioInput::new(vec![
+			RadioEntryData::new(GraphWireStyle::GridAligned.to_string())
+				.label(GraphWireStyle::GridAligned.to_string())
+				.tooltip(GraphWireStyle::GridAligned.tooltip_description())
+				.on_update(move |_| PreferencesMessage::GraphWireStyle { style: GraphWireStyle::GridAligned }.into()),
+			RadioEntryData::new(GraphWireStyle::Direct.to_string())
+				.label(GraphWireStyle::Direct.to_string())
+				.tooltip(GraphWireStyle::Direct.tooltip_description())
+				.on_update(move |_| PreferencesMessage::GraphWireStyle { style: GraphWireStyle::Direct }.into()),
+		])
+		.selected_index(Some(preferences.graph_wire_style as u32))
+		.widget_holder();
+
+		// ============
+		// EXPERIMENTAL
+		// ============
 
 		let vello_tooltip = "Use the experimental Vello renderer (your browser must support WebGPU)";
 		let renderer_section = vec![TextLabel::new("Experimental").italic(true).widget_holder()];
@@ -126,10 +158,12 @@ impl PreferencesDialogMessageHandler {
 		// ];
 
 		Layout::WidgetLayout(WidgetLayout::new(vec![
-			LayoutGroup::Row { widgets: selection_section },
-			LayoutGroup::Row { widgets: vec![selection_mode] },
 			LayoutGroup::Row { widgets: input_section },
 			LayoutGroup::Row { widgets: zoom_with_scroll },
+			LayoutGroup::Row { widgets: selection_section },
+			LayoutGroup::Row { widgets: vec![selection_mode] },
+			LayoutGroup::Row { widgets: node_graph_section },
+			LayoutGroup::Row { widgets: vec![graph_wire_style] },
 			LayoutGroup::Row { widgets: renderer_section },
 			LayoutGroup::Row { widgets: use_vello },
 			LayoutGroup::Row { widgets: vector_meshes },
diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs
index 3bbf2791b5..89306b2111 100644
--- a/editor/src/messages/frontend/frontend_message.rs
+++ b/editor/src/messages/frontend/frontend_message.rs
@@ -250,6 +250,8 @@ pub enum FrontendMessage {
 	UpdateNodeGraph {
 		nodes: Vec<FrontendNode>,
 		wires: Vec<FrontendNodeWire>,
+		#[serde(rename = "wiresDirectNotGridAligned")]
+		wires_direct_not_grid_aligned: bool,
 	},
 	UpdateNodeGraphControlBarLayout {
 		#[serde(rename = "layoutTarget")]
diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs
index f60d19be31..8cef1fb930 100644
--- a/editor/src/messages/portfolio/document/document_message_handler.rs
+++ b/editor/src/messages/portfolio/document/document_message_handler.rs
@@ -42,6 +42,7 @@ pub struct DocumentMessageData<'a> {
 	pub persistent_data: &'a PersistentData,
 	pub executor: &'a mut NodeGraphExecutor,
 	pub current_tool: &'a ToolType,
+	pub preferences: &'a PreferencesMessageHandler,
 }
 
 #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -172,6 +173,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
 			persistent_data,
 			executor,
 			current_tool,
+			preferences,
 		} = data;
 
 		let selected_nodes_bounding_box_viewport = self.network_interface.selected_nodes_bounding_box_viewport(&self.breadcrumb_network_path);
@@ -222,6 +224,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
 						graph_view_overlay_open: self.graph_view_overlay_open,
 						graph_fade_artwork_percentage: self.graph_fade_artwork_percentage,
 						navigation_handler: &self.navigation_handler,
+						preferences,
 					},
 				);
 			}
diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs
index 4a452864ca..b38429f767 100644
--- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs
+++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs
@@ -35,6 +35,7 @@ pub struct NodeGraphHandlerData<'a> {
 	pub graph_view_overlay_open: bool,
 	pub graph_fade_artwork_percentage: f64,
 	pub navigation_handler: &'a NavigationMessageHandler,
+	pub preferences: &'a PreferencesMessageHandler,
 }
 
 #[derive(Debug, Clone)]
@@ -93,6 +94,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
 			graph_view_overlay_open,
 			graph_fade_artwork_percentage,
 			navigation_handler,
+			preferences,
 		} = data;
 
 		match message {
@@ -1293,8 +1295,14 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
 					let wires = Self::collect_wires(network_interface, breadcrumb_network_path);
 					let nodes = self.collect_nodes(network_interface, breadcrumb_network_path);
 					let (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths(breadcrumb_network_path);
+					let wires_direct_not_grid_aligned = preferences.graph_wire_style.is_direct();
+
 					responses.add(NodeGraphMessage::UpdateImportsExports);
-					responses.add(FrontendMessage::UpdateNodeGraph { nodes, wires });
+					responses.add(FrontendMessage::UpdateNodeGraph {
+						nodes,
+						wires,
+						wires_direct_not_grid_aligned,
+					});
 					responses.add(FrontendMessage::UpdateLayerWidths {
 						layer_widths,
 						chain_widths,
diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs
index 24e1ee4129..c3cca33883 100644
--- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs
+++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs
@@ -200,3 +200,32 @@ pub enum Direction {
 	Left,
 	Right,
 }
+
+#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
+pub enum GraphWireStyle {
+	#[default]
+	GridAligned = 0,
+	Direct = 1,
+}
+
+impl std::fmt::Display for GraphWireStyle {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		match self {
+			GraphWireStyle::GridAligned => write!(f, "Grid-Aligned"),
+			GraphWireStyle::Direct => write!(f, "Direct"),
+		}
+	}
+}
+
+impl GraphWireStyle {
+	pub fn tooltip_description(&self) -> &'static str {
+		match self {
+			GraphWireStyle::GridAligned => "Wires follow the grid, running in straight lines between nodes",
+			GraphWireStyle::Direct => "Wires bend to run at an angle directly between nodes",
+		}
+	}
+
+	pub fn is_direct(&self) -> bool {
+		*self == GraphWireStyle::Direct
+	}
+}
diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs
index 89497669df..6afd3683f7 100644
--- a/editor/src/messages/portfolio/portfolio_message_handler.rs
+++ b/editor/src/messages/portfolio/portfolio_message_handler.rs
@@ -106,6 +106,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
 							persistent_data: &self.persistent_data,
 							executor: &mut self.executor,
 							current_tool,
+							preferences,
 						};
 						document.process_message(message, responses, document_inputs)
 					}
@@ -121,6 +122,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
 						persistent_data: &self.persistent_data,
 						executor: &mut self.executor,
 						current_tool,
+						preferences,
 					};
 					document.process_message(message, responses, document_inputs)
 				}
diff --git a/editor/src/messages/preferences/preferences_message.rs b/editor/src/messages/preferences/preferences_message.rs
index d060267a70..63f7d59aae 100644
--- a/editor/src/messages/preferences/preferences_message.rs
+++ b/editor/src/messages/preferences/preferences_message.rs
@@ -1,3 +1,4 @@
+use crate::messages::portfolio::document::node_graph::utility_types::GraphWireStyle;
 use crate::messages::preferences::SelectionMode;
 use crate::messages::prelude::*;
 
@@ -13,6 +14,7 @@ pub enum PreferencesMessage {
 	SelectionMode { selection_mode: SelectionMode },
 	VectorMeshes { enabled: bool },
 	ModifyLayout { zoom_with_scroll: bool },
+	GraphWireStyle { style: GraphWireStyle },
 	// ImaginateRefreshFrequency { seconds: f64 },
 	// ImaginateServerHostname { hostname: String },
 }
diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs
index f52b109b9a..2b51aa2814 100644
--- a/editor/src/messages/preferences/preferences_message_handler.rs
+++ b/editor/src/messages/preferences/preferences_message_handler.rs
@@ -1,4 +1,5 @@
 use crate::messages::input_mapper::key_mapping::MappingVariant;
+use crate::messages::portfolio::document::node_graph::utility_types::GraphWireStyle;
 use crate::messages::preferences::SelectionMode;
 use crate::messages::prelude::*;
 
@@ -12,6 +13,7 @@ pub struct PreferencesMessageHandler {
 	pub zoom_with_scroll: bool,
 	pub use_vello: bool,
 	pub vector_meshes: bool,
+	pub graph_wire_style: GraphWireStyle,
 }
 
 impl PreferencesMessageHandler {
@@ -37,6 +39,7 @@ impl Default for PreferencesMessageHandler {
 			imaginate_hostname: host_name,
 			use_vello,
 		} = Default::default();
+
 		Self {
 			imaginate_server_hostname: host_name,
 			imaginate_refresh_frequency: 1.,
@@ -44,6 +47,7 @@ impl Default for PreferencesMessageHandler {
 			zoom_with_scroll: matches!(MappingVariant::default(), MappingVariant::ZoomWithScroll),
 			use_vello,
 			vector_meshes: false,
+			graph_wire_style: GraphWireStyle::default(),
 		}
 	}
 }
@@ -95,6 +99,10 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
 			PreferencesMessage::SelectionMode { selection_mode } => {
 				self.selection_mode = selection_mode;
 			}
+			PreferencesMessage::GraphWireStyle { style } => {
+				self.graph_wire_style = style;
+				responses.add(NodeGraphMessage::SendGraph);
+			}
 		}
 		// TODO: Reenable when Imaginate is restored (and move back up one line since the auto-formatter doesn't like it in that block)
 		// PreferencesMessage::ImaginateRefreshFrequency { seconds } => {
diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte
index 0186f60587..589a8411ac 100644
--- a/frontend/src/components/views/Graph.svelte
+++ b/frontend/src/components/views/Graph.svelte
@@ -159,11 +159,13 @@
 		return { nodeOutput, nodeInput };
 	}
 
-	function createWirePath(outputPort: SVGSVGElement, inputPort: SVGSVGElement, verticalOut: boolean, verticalIn: boolean, dashed: boolean): WirePath {
+	function createWirePath(outputPort: SVGSVGElement, inputPort: SVGSVGElement, verticalOut: boolean, verticalIn: boolean, dashed: boolean, directNotGridAligned: boolean): WirePath {
 		const inputPortRect = inputPort.getBoundingClientRect();
 		const outputPortRect = outputPort.getBoundingClientRect();
 
-		const pathString = buildWirePathString(outputPortRect, inputPortRect, verticalOut, verticalIn);
+		const pathString = directNotGridAligned
+			? buildCurvedWirePathString(outputPortRect, inputPortRect, verticalOut, verticalIn)
+			: buildStraightWirePathString(outputPortRect, inputPortRect, verticalOut, verticalIn);
 		const dataType = (outputPort.getAttribute("data-datatype") as FrontendGraphDataType) || "General";
 		const thick = verticalIn && verticalOut;
 
@@ -184,7 +186,7 @@
 			const wireEndNode = wire.wireEnd.nodeId !== undefined ? $nodeGraph.nodes.get(wire.wireEnd.nodeId) : undefined;
 			const wireEnd = (wireEndNode?.isLayer && Number(wire.wireEnd.index) === 0) || false;
 
-			return [createWirePath(nodeOutput, nodeInput, wireStart, wireEnd, wire.dashed)];
+			return [createWirePath(nodeOutput, nodeInput, wireStart, wireEnd, wire.dashed, $nodeGraph.wiresDirectNotGridAligned)];
 		});
 	}
 
@@ -198,7 +200,270 @@
 		return iconMap[icon] || "NodeNodes";
 	}
 
-	function buildWirePathLocations(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): { x: number; y: number }[] {
+	function buildStraightWirePathLocations(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): { x: number; y: number }[] {
+		if (!nodesContainer) return [];
+
+		const VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP = 1;
+		const LINE_WIDTH = 2;
+
+		// Calculate coordinates for input and output connectors
+		const inX = verticalIn ? inputBounds.x + inputBounds.width / 2 : inputBounds.x + 1;
+		const inY = verticalIn ? inputBounds.y + inputBounds.height - VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP : inputBounds.y + inputBounds.height / 2;
+		const outX = verticalOut ? outputBounds.x + outputBounds.width / 2 : outputBounds.x + outputBounds.width - 1;
+		const outY = verticalOut ? outputBounds.y + VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP : outputBounds.y + outputBounds.height / 2;
+
+		// Adjust for scale
+		const containerBounds = nodesContainer.getBoundingClientRect();
+		const scale = $nodeGraph.transform.scale;
+		const inConnectorX = Math.round((inX - containerBounds.x) / scale);
+		const inConnectorY = Math.round((inY - containerBounds.y) / scale);
+		const outConnectorX = Math.round((outX - containerBounds.x) / scale);
+		const outConnectorY = Math.round((outY - containerBounds.y) / scale);
+
+		// Helper functions for calculating coordinates
+		const calculateMidX = () => (inConnectorX + outConnectorX) / 2 + (((inConnectorX + outConnectorX) / 2) % gridSpacing);
+		const calculateMidY = () => (inConnectorY + outConnectorY) / 2 + (((inConnectorY + outConnectorY) / 2) % gridSpacing);
+		const calculateMidYAlternate = () => (inConnectorY + outConnectorY) / 2 - (((inConnectorY + outConnectorY) / 2) % gridSpacing);
+
+		// Define X coordinate calculations
+		const x1 = () => outConnectorX;
+		const x2 = () => outConnectorX + gridSpacing;
+		const x3 = () => inConnectorX - 2 * gridSpacing;
+		const x4 = () => inConnectorX;
+		const x5 = () => inConnectorX - 2 * gridSpacing + LINE_WIDTH;
+		const x6 = () => outConnectorX + gridSpacing + LINE_WIDTH;
+		const x7 = () => outConnectorX + 2 * gridSpacing + LINE_WIDTH;
+		const x8 = () => inConnectorX + LINE_WIDTH;
+		const x9 = () => outConnectorX + 2 * gridSpacing;
+		const x10 = () => calculateMidX() + LINE_WIDTH;
+		const x11 = () => outConnectorX - gridSpacing;
+		const x12 = () => outConnectorX - 4 * gridSpacing;
+		const x13 = () => calculateMidX();
+		const x14 = () => inConnectorX + gridSpacing;
+		const x15 = () => inConnectorX - 4 * gridSpacing;
+		const x16 = () => inConnectorX + 8 * gridSpacing;
+		const x17 = () => calculateMidX() - 2 * LINE_WIDTH;
+		const x18 = () => outConnectorX + gridSpacing - 2 * LINE_WIDTH;
+		const x19 = () => outConnectorX - 2 * LINE_WIDTH;
+		const x20 = () => calculateMidX() - LINE_WIDTH;
+
+		// Define Y coordinate calculations
+		const y1 = () => outConnectorY;
+		const y2 = () => outConnectorY - gridSpacing;
+		const y3 = () => inConnectorY;
+		const y4 = () => outConnectorY - gridSpacing + 5.5 * LINE_WIDTH;
+		const y5 = () => inConnectorY - 2 * gridSpacing;
+		const y6 = () => outConnectorY + 4 * LINE_WIDTH;
+		const y7 = () => outConnectorY + 5 * LINE_WIDTH;
+		const y8 = () => outConnectorY - 2 * gridSpacing + 5.5 * LINE_WIDTH;
+		const y9 = () => outConnectorY + 6 * LINE_WIDTH;
+		const y10 = () => inConnectorY + 2 * gridSpacing;
+		const y111 = () => inConnectorY + gridSpacing + 6.5 * LINE_WIDTH;
+		const y12 = () => inConnectorY + gridSpacing - 5.5 * LINE_WIDTH;
+		const y13 = () => inConnectorY - gridSpacing;
+		const y14 = () => inConnectorY + gridSpacing;
+		const y15 = () => calculateMidY();
+		const y16 = () => calculateMidYAlternate();
+
+		// Helper function for constructing coordinate pairs
+		const construct = (...coords: [() => number, () => number][]) => coords.map(([x, y]) => ({ x: x(), y: y() }));
+
+		// Define wire path shapes that get used more than once
+		const wire1 = () => construct([x1, y1], [x1, y4], [x5, y4], [x5, y3], [x4, y3]);
+		const wire2 = () => construct([x1, y1], [x1, y16], [x3, y16], [x3, y3], [x4, y3]);
+		const wire3 = () => construct([x1, y1], [x1, y4], [x12, y4], [x12, y10], [x3, y10], [x3, y3], [x4, y3]);
+		const wire4 = () => construct([x1, y1], [x1, y4], [x13, y4], [x13, y10], [x3, y10], [x3, y3], [x4, y3]);
+
+		// `outConnector` point and `inConnector` point lying on the same horizontal grid line and `outConnector` point lies to the right of `inConnector` point
+		if (outConnectorY === inConnectorY && outConnectorX > inConnectorX && (verticalOut || !verticalIn)) return construct([x1, y1], [x2, y1], [x2, y2], [x3, y2], [x3, y3], [x4, y3]);
+
+		// Handle straight lines
+		if (outConnectorY === inConnectorY || (outConnectorX === inConnectorX && verticalOut)) return construct([x1, y1], [x4, y3]);
+
+		// Handle standard right-angle paths
+		// Start vertical, then horizontal
+
+		// `outConnector` point lies to the left of `inConnector` point
+		if (verticalOut && inConnectorX > outConnectorX) {
+			// `outConnector` point lies above `inConnector` point
+			if (outConnectorY < inConnectorY) {
+				// `outConnector` point lies on the vertical grid line 4 units to the left of `inConnector` point point
+				if (-4 * gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX < -3 * gridSpacing) return wire1();
+
+				// `outConnector` point lying on vertical grid lines 3 and 2 units to the left of `inConnector` point
+				if (-3 * gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= -1 * gridSpacing) {
+					if (-2 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= -1 * gridSpacing) return construct([x1, y1], [x1, y2], [x2, y2], [x2, y3], [x4, y3]);
+
+					if (-1 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 0 * gridSpacing) return construct([x1, y1], [x1, y4], [x6, y4], [x6, y3], [x4, y3]);
+
+					return construct([x1, y1], [x1, y4], [x7, y4], [x7, y5], [x3, y5], [x3, y3], [x4, y3]);
+				}
+
+				// `outConnector` point lying on vertical grid line 1 units to the left of `inConnector` point
+				if (-1 * gridSpacing < outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 0 * gridSpacing) {
+					// `outConnector` point lying on horizontal grid line 1 unit above `inConnector` point
+					if (-2 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= -1 * gridSpacing) return construct([x1, y6], [x2, y6], [x8, y3]);
+
+					// `outConnector` point lying on the same horizontal grid line as `inConnector` point
+					if (-1 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 0 * gridSpacing) return construct([x1, y7], [x4, y3]);
+
+					return construct([x1, y1], [x1, y2], [x9, y2], [x9, y5], [x3, y5], [x3, y3], [x4, y3]);
+				}
+
+				return construct([x1, y1], [x1, y4], [x10, y4], [x10, y3], [x4, y3]);
+			}
+
+			// `outConnector` point lies below `inConnector` point
+			// `outConnector` point lying on vertical grid line 1 unit to the left of `inConnector` point
+			if (-1 * gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 0 * gridSpacing) {
+				// `outConnector` point lying on the horizontal grid lines 1 and 2 units below the `inConnector` point
+				if (0 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 2 * gridSpacing) construct([x1, y6], [x11, y6], [x11, y3], [x4, y3]);
+
+				return wire2();
+			}
+
+			return construct([x1, y1], [x1, y3], [x4, y3]);
+		}
+
+		// `outConnector` point lies to the right of `inConnector` point
+		if (verticalOut && inConnectorX <= outConnectorX) {
+			// `outConnector` point lying on any horizontal grid line above `inConnector` point
+			if (outConnectorY < inConnectorY) {
+				// `outConnector` point lying on horizontal grid line 1 unit above `inConnector` point
+				if (-2 * gridSpacing < outConnectorY - inConnectorY && outConnectorY - inConnectorY <= -1 * gridSpacing) return wire1();
+
+				// `outConnector` point lying on the same horizontal grid line as `inConnector` point
+				if (-1 * gridSpacing < outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 0 * gridSpacing) return construct([x1, y1], [x1, y8], [x5, y8], [x5, y3], [x4, y3]);
+
+				// `outConnector` point lying on vertical grid lines 1 and 2 units to the right of `inConnector` point
+				if (gridSpacing <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 3 * gridSpacing) {
+					return construct([x1, y1], [x1, y4], [x9, y4], [x9, y5], [x3, y5], [x3, y3], [x4, y3]);
+				}
+
+				return construct([x1, y1], [x1, y4], [x10, y4], [x10, y5], [x5, y5], [x5, y3], [x4, y3]);
+			}
+
+			// `outConnector` point lies below `inConnector` point
+			if (outConnectorY - inConnectorY <= gridSpacing) {
+				// `outConnector` point lies on the horizontal grid line 1 unit below the `inConnector` Point
+				if (0 <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 13 * gridSpacing) return construct([x1, y9], [x3, y9], [x3, y3], [x4, y3]);
+
+				if (13 < outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 18 * gridSpacing) return wire3();
+
+				return wire4();
+			}
+
+			// `outConnector` point lies on the horizontal grid line 2 units below `outConnector` point
+			if (gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= 2 * gridSpacing) {
+				if (0 <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 13 * gridSpacing) return construct([x1, y7], [x5, y7], [x5, y3], [x4, y3]);
+
+				if (13 < outConnectorX - inConnectorX && outConnectorX - inConnectorX <= 18 * gridSpacing) return wire3();
+
+				return wire4();
+			}
+
+			// 0 to 4 units below the `outConnector` Point
+			if (outConnectorY - inConnectorY <= 4 * gridSpacing) return wire1();
+
+			return wire2();
+		}
+
+		// Start horizontal, then vertical
+		if (verticalIn) {
+			// when `outConnector` lies below `inConnector`
+			if (outConnectorY > inConnectorY) {
+				// `outConnectorX` lies to the left of `inConnectorX`
+				if (outConnectorX < inConnectorX) return construct([x1, y1], [x4, y1], [x4, y3]);
+
+				// `outConnectorX` lies to the right of `inConnectorX`
+				if (outConnectorY - inConnectorY <= gridSpacing) {
+					// `outConnector` point directly below `inConnector` point
+					if (0 <= outConnectorX - inConnectorX && outConnectorX - inConnectorX <= gridSpacing) return construct([x1, y1], [x14, y1], [x14, y2], [x4, y2], [x4, y3]);
+
+					// `outConnector` point lies below `inConnector` point and strictly to the right of `inConnector` point
+					return construct([x1, y1], [x2, y1], [x2, y111], [x4, y111], [x4, y3]);
+				}
+
+				return construct([x1, y1], [x2, y1], [x2, y2], [x4, y2], [x4, y3]);
+			}
+
+			// `outConnectorY` lies on or above the `inConnectorY` point
+			if (-6 * gridSpacing < inConnectorX - outConnectorX && inConnectorX - outConnectorX < 4 * gridSpacing) {
+				// edge case: `outConnector` point lying on vertical grid lines ranging from 4 units to left to 5 units to right of `inConnector` point
+				if (-1 * gridSpacing < inConnectorX - outConnectorX && inConnectorX - outConnectorX < 4 * gridSpacing) {
+					return construct([x1, y1], [x2, y1], [x2, y2], [x15, y2], [x15, y12], [x4, y12], [x4, y3]);
+				}
+
+				return construct([x1, y1], [x16, y1], [x16, y12], [x4, y12], [x4, y3]);
+			}
+
+			// left of edge case: `outConnector` point lying on vertical grid lines more than 4 units to left of `inConnector` point
+			if (4 * gridSpacing < inConnectorX - outConnectorX) return construct([x1, y1], [x17, y1], [x17, y12], [x4, y12], [x4, y3]);
+
+			// right of edge case: `outConnector` point lying on the vertical grid lines more than 5 units to right of `inConnector` point
+			if (6 * gridSpacing > inConnectorX - outConnectorX) return construct([x1, y1], [x18, y1], [x18, y12], [x4, y12], [x4, y3]);
+		}
+
+		// Both horizontal - use horizontal middle point
+		// When `inConnector` point is one of the two closest diagonally opposite points
+		if (0 <= inConnectorX - outConnectorX && inConnectorX - outConnectorX <= gridSpacing && inConnectorY - outConnectorY >= -1 * gridSpacing && inConnectorY - outConnectorY <= gridSpacing) {
+			return construct([x19, y1], [x19, y3], [x4, y3]);
+		}
+
+		// When `inConnector` point lies on the horizontal line 1 unit above and below the `outConnector` point
+		if (-1 * gridSpacing <= outConnectorY - inConnectorY && outConnectorY - inConnectorY <= gridSpacing && outConnectorX > inConnectorX) {
+			// Horizontal line above `outConnectorY`
+			if (inConnectorY < outConnectorY) return construct([x1, y1], [x2, y1], [x2, y13], [x3, y13], [x3, y3], [x4, y3]);
+
+			// Horizontal line below `outConnectorY`
+			return construct([x1, y1], [x2, y1], [x2, y14], [x3, y14], [x3, y3], [x4, y3]);
+		}
+
+		// `outConnector` point to the right of `inConnector` point
+		if (outConnectorX > inConnectorX - gridSpacing) return construct([x1, y1], [x18, y1], [x18, y15], [x5, y15], [x5, y3], [x4, y3]);
+
+		// When `inConnector` point lies on the vertical grid line two units to the right of `outConnector` point
+		if (gridSpacing <= inConnectorX - outConnectorX && inConnectorX - outConnectorX <= 2 * gridSpacing) return construct([x1, y1], [x18, y1], [x18, y3], [x4, y3]);
+
+		return construct([x1, y1], [x20, y1], [x20, y3], [x4, y3]);
+	}
+
+	function buildStraightWirePathString(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): string {
+		const locations = buildStraightWirePathLocations(outputBounds, inputBounds, verticalOut, verticalIn);
+		if (locations.length === 0) return "[error]";
+		if (locations.length === 2) return `M${locations[0].x},${locations[0].y} L${locations[1].x},${locations[1].y}`;
+
+		const CORNER_RADIUS = 10;
+
+		// Create path with rounded corners
+		let path = `M${locations[0].x},${locations[0].y}`;
+
+		for (let i = 1; i < locations.length - 1; i++) {
+			const prev = locations[i - 1];
+			const curr = locations[i];
+			const next = locations[i + 1];
+
+			// Calculate corner points
+			const isVertical = curr.x === prev.x;
+			const cornerStart = {
+				x: curr.x + (isVertical ? 0 : prev.x < curr.x ? -CORNER_RADIUS : CORNER_RADIUS),
+				y: curr.y + (isVertical ? (prev.y < curr.y ? -CORNER_RADIUS : CORNER_RADIUS) : 0),
+			};
+			const cornerEnd = {
+				x: curr.x + (isVertical ? (next.x < curr.x ? -CORNER_RADIUS : CORNER_RADIUS) : 0),
+				y: curr.y + (isVertical ? 0 : next.y < curr.y ? -CORNER_RADIUS : CORNER_RADIUS),
+			};
+
+			// Add line to corner start, quadratic curve for corner, then continue to next point
+			path += ` L${cornerStart.x},${cornerStart.y}`;
+			path += ` Q${curr.x},${curr.y} ${cornerEnd.x},${cornerEnd.y}`;
+		}
+
+		path += ` L${locations[locations.length - 1].x},${locations[locations.length - 1].y}`;
+		return path;
+	}
+
+	function buildCurvedWirePathLocations(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): { x: number; y: number }[] {
 		if (!nodesContainer) return [];
 
 		const VERTICAL_WIRE_OVERLAP_ON_SHAPED_CAP = 1;
@@ -217,19 +482,6 @@
 		const horizontalGap = Math.abs(outConnectorX - inConnectorX);
 		const verticalGap = Math.abs(outConnectorY - inConnectorY);
 
-		// TODO: Finish this commented out code replacement for the code below it based on this diagram: <https://files.keavon.com/-/InsubstantialElegantQueenant/capture.png>
-		// // Straight: stacking lines which are always straight, or a straight horizontal wire between two aligned nodes
-		// if ((verticalOut && verticalIn) || (!verticalOut && !verticalIn && verticalGap === 0)) {
-		// 	return [
-		// 		{ x: outConnectorX, y: outConnectorY },
-		// 		{ x: inConnectorX, y: inConnectorY },
-		// 	];
-		// }
-
-		// // L-shape bend
-		// if (verticalOut !== verticalIn) {
-		// }
-
 		const curveLength = 24;
 		const curveFalloffRate = curveLength * Math.PI * 2;
 
@@ -246,12 +498,14 @@
 		];
 	}
 
-	function buildWirePathString(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): string {
-		const locations = buildWirePathLocations(outputBounds, inputBounds, verticalOut, verticalIn);
+	function buildCurvedWirePathString(outputBounds: DOMRect, inputBounds: DOMRect, verticalOut: boolean, verticalIn: boolean): string {
+		const locations = buildCurvedWirePathLocations(outputBounds, inputBounds, verticalOut, verticalIn);
 		if (locations.length === 0) return "[error]";
+
 		const SMOOTHING = 0.5;
 		const delta01 = { x: (locations[1].x - locations[0].x) * SMOOTHING, y: (locations[1].y - locations[0].y) * SMOOTHING };
 		const delta23 = { x: (locations[3].x - locations[2].x) * SMOOTHING, y: (locations[3].y - locations[2].y) * SMOOTHING };
+
 		return `
 			M${locations[0].x},${locations[0].y}
 			L${locations[1].x},${locations[1].y}
@@ -335,31 +589,20 @@
 	}
 
 	function outputConnectedToText(output: FrontendGraphOutput): string {
-		if (output.connectedTo.length === 0) {
-			return "Connected to nothing";
-		} else {
-			return output.connectedTo
-				.map((inputConnector) => {
-					if ((inputConnector as Node).nodeId === undefined) {
-						return `Connected to export index ${inputConnector.index}`;
-					} else {
-						return `Connected to ${(inputConnector as Node).nodeId}, port index ${inputConnector.index}`;
-					}
-				})
-				.join("\n");
-		}
+		if (output.connectedTo.length === 0) return "Connected to nothing";
+
+		return output.connectedTo
+			.map((inputConnector) => {
+				if ((inputConnector as Node).nodeId === undefined) return `Connected to export index ${inputConnector.index}`;
+				return `Connected to ${(inputConnector as Node).nodeId}, port index ${inputConnector.index}`;
+			})
+			.join("\n");
 	}
 
 	function inputConnectedToText(input: FrontendGraphInput): string {
-		if (input.connectedTo === undefined) {
-			return "Connected to nothing";
-		} else {
-			if ((input.connectedTo as Node).nodeId === undefined) {
-				return `Connected to import index ${input.connectedTo.index}`;
-			} else {
-				return `Connected to ${(input.connectedTo as Node).nodeId}, port index ${input.connectedTo.index}`;
-			}
-		}
+		if (input.connectedTo === undefined) return "Connected to nothing";
+		if ((input.connectedTo as Node).nodeId === undefined) return `Connected to import index ${input.connectedTo.index}`;
+		return `Connected to ${(input.connectedTo as Node).nodeId}, port index ${input.connectedTo.index}`;
 	}
 
 	function primaryOutputConnectedToLayer(node: FrontendNode): boolean {
diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts
index ca0da8f94f..f6e7188dec 100644
--- a/frontend/src/messages.ts
+++ b/frontend/src/messages.ts
@@ -100,6 +100,8 @@ export class UpdateNodeGraph extends JsMessage {
 
 	@Type(() => FrontendNodeWire)
 	readonly wires!: FrontendNodeWire[];
+
+	readonly wiresDirectNotGridAligned!: boolean;
 }
 
 export class UpdateNodeGraphTransform extends JsMessage {
diff --git a/frontend/src/state-providers/node-graph.ts b/frontend/src/state-providers/node-graph.ts
index 25e9c51980..266dcf18b8 100644
--- a/frontend/src/state-providers/node-graph.ts
+++ b/frontend/src/state-providers/node-graph.ts
@@ -41,6 +41,7 @@ export function createNodeGraphState(editor: Editor) {
 		addExport: undefined as { x: number; y: number } | undefined,
 		nodes: new Map<bigint, FrontendNode>(),
 		wires: [] as FrontendNodeWire[],
+		wiresDirectNotGridAligned: false,
 		wirePathInProgress: undefined as WirePath | undefined,
 		inputTypeDescriptions: new Map<string, string>(),
 		nodeDescriptions: new Map<string, string>(),
@@ -123,6 +124,7 @@ export function createNodeGraphState(editor: Editor) {
 				state.nodes.set(node.id, node);
 			});
 			state.wires = updateNodeGraph.wires;
+			state.wiresDirectNotGridAligned = updateNodeGraph.wiresDirectNotGridAligned;
 			return state;
 		});
 	});


Test patch: