Wiresheet: make the last wire turn 1.5 grid before the target arrow

The final vertical run into an input slot now stands at least
1.5 * GRID_SIZE in front of the box, so the arrowhead sits on a straight
run instead of on the bend. Column gaps in tidy layout widen to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 19:45:39 +03:00
co-authored by Claude Opus 5.5
parent 96a703c5d5
commit fd22eb314d
2 changed files with 29 additions and 14 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
import configparser
import os
VERSION = "0.0.0.148"
VERSION = "0.0.0.149"
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
DEFAULT_GRID_SIZE = 10
DEFAULT_ADD_TIMESTAMP = True
+28 -13
View File
@@ -33,6 +33,10 @@ WIRE_LANE_GAP = 6
# knobs, the same way a link leaving the sheet is.
WIRE_STANDOFF_MAX = 60
# The last turn before a target slot is kept this many grid squares back from the box,
# so the arrowhead always sits on a straight run of its own instead of on the bend.
ARRIVE_TURN_GRIDS = 1.5
# Two different wires running horizontally at the same height are indistinguishable, so
# a free horizontal lane is nudged until it clears any other wire's by this much. The
# short stubs joining a slot cannot move -- they have to meet their pin -- so a fan-out
@@ -315,6 +319,11 @@ def vertical_run_clearance(cx, y1, y2, boxes):
return nearest
def arrive_turn():
"""How far in front of a target box the wire makes its last turn, at 1:1."""
return max(WIRE_HUG, ARRIVE_TURN_GRIDS * app_state.GRID_SIZE)
def channel_candidates(src_box, dst_box, boxes, low, high):
"""Vertical channel positions to try, best first.
@@ -322,7 +331,7 @@ def channel_candidates(src_box, dst_box, boxes, low, high):
that a vertical line stays within WIRE_HUG of a component. The midpoint of the gap
is kept only as a last resort, for when every hug position is blocked.
"""
ordered = [src_box[2] + WIRE_HUG, dst_box[0] - WIRE_HUG]
ordered = [src_box[2] + WIRE_HUG, dst_box[0] - arrive_turn()]
for bx1, _, bx2, _ in boxes:
ordered.append(bx2 + WIRE_HUG)
ordered.append(bx1 - WIRE_HUG)
@@ -362,18 +371,22 @@ def free_lane(cx, run_lo, run_hi, boxes, taken, low, high, max_steps=8):
return None
def hug_lane(edge_x, side, run_lo, run_hi, boxes, claimed, max_offset=WIRE_HUG):
def hug_lane(edge_x, side, run_lo, run_hi, boxes, claimed, max_offset=WIRE_HUG,
min_offset=WIRE_MIN_CLEAR):
"""Lane for a run pinned to one box edge, starting WIRE_MIN_CLEAR out from it.
`side` is +1 for a run leaving a box's right edge, -1 for one entering its left
edge. Offsets inside the hug band are offered first, furthest out of the band
first so the run keeps clear of its own box; only when every one of those is taken
does it reach past WIRE_HUG, out to `max_offset`. Returns None when nothing up to
that limit clears both the boxes and the runs already placed.
that limit clears both the boxes and the runs already placed. `min_offset` raises
the floor, and the hug position with it, for a run that must stand further off.
"""
offsets = list(range(WIRE_HUG, WIRE_MIN_CLEAR - 1, -1))
if max_offset > WIRE_HUG:
offsets += list(range(WIRE_HUG + 1, int(max_offset) + 1))
floor = max(WIRE_MIN_CLEAR, int(math.ceil(min_offset)))
hug = max(WIRE_HUG, floor)
offsets = list(range(hug, floor - 1, -1))
if max_offset > hug:
offsets += list(range(hug + 1, int(max_offset) + 1))
for offset in offsets:
lane = edge_x + side * offset
@@ -437,7 +450,8 @@ def route_link_path(src_box, src_y, dst_box, dst_y, boxes, taken=None,
start = (src_box[2], src_y)
end = (dst_box[0], dst_y)
leave = (start[0] + LINK_STUB, src_y)
arrive = (end[0] - LINK_STUB, dst_y)
# The last turn stands arrive_turn() in front of the target, never closer.
arrive = (end[0] - max(LINK_STUB, arrive_turn()), dst_y)
low, high = min(leave[0], arrive[0]), max(leave[0], arrive[0])
run_lo, run_hi = min(src_y, dst_y), max(src_y, dst_y)
@@ -509,7 +523,8 @@ def route_link_path(src_box, src_y, dst_box, dst_y, boxes, taken=None,
continue
in_x = hug_lane(dst_box[0], -1, min(base_cy, dst_y), max(base_cy, dst_y),
boxes, claimed + [(out_x, min(src_y, base_cy),
max(src_y, base_cy))], max_offset)
max(src_y, base_cy))], max_offset,
min_offset=arrive_turn())
if in_x is None:
continue
@@ -1205,12 +1220,12 @@ def wire_demand_by_boundary(column_of_comp):
def column_gap_for(run_count):
"""Clear space a column gap needs to carry `run_count` vertical wire runs.
Lanes start WIRE_MIN_CLEAR in from either box edge and stand WIRE_LANE_GAP apart, so
n of them span 2 * WIRE_MIN_CLEAR + (n - 1) * WIRE_LANE_GAP. One or two runs already
fit inside the plain TIDY_GAP -- at 10 and 20 across a 30px gap, both still hugging
a box -- which is why the great majority of gaps stay at exactly TIDY_GAP.
Lanes start WIRE_MIN_CLEAR out from the source box and arrive_turn() in front of
the target box, and stand WIRE_LANE_GAP apart, so n of them span
WIRE_MIN_CLEAR + arrive_turn() + (n - 1) * WIRE_LANE_GAP.
"""
return max(TIDY_GAP, 2 * WIRE_MIN_CLEAR + max(0, run_count - 1) * WIRE_LANE_GAP)
return max(TIDY_GAP, WIRE_MIN_CLEAR + arrive_turn()
+ max(0, run_count - 1) * WIRE_LANE_GAP)
def plan_columns(columns, column_keys, demand, scale):