fix(sockets): a board a hair off the seat is still plugged in

This commit is contained in:
David Montero Crespo 2026-08-01 06:14:43 +02:00
parent f2e6464c36
commit 13a8403d6d
3 changed files with 27 additions and 29 deletions

View File

@ -50,7 +50,7 @@ import {
seatOnDrop, seatOnDrop,
snapPositionToBreadboard, snapPositionToBreadboard,
} from '../../utils/breadboardSnap'; } from '../../utils/breadboardSnap';
import { snapBoardToSocket } from '../../utils/socketSnap'; import { snapBoardToSocket, isBoardSeated } from '../../utils/socketSnap';
import { import {
findWireNearPoint, findWireNearPoint,
findSegmentNearPoint, findSegmentNearPoint,
@ -138,8 +138,7 @@ function carrySeatedBoards(
for (const b of st.boards) { for (const b of st.boards) {
// Restricting the candidate list to the dragged component asks the // Restricting the candidate list to the dragged component asks the
// narrow question: is this board seated on THIS socket? // narrow question: is this board seated on THIS socket?
const seat = snapBoardToSocket(b.id, b.boardKind, b.x, b.y, [dragged]); if (isBoardSeated(b.id, b.boardKind, b.x, b.y, [dragged])) {
if (seat && Math.hypot(seat.x - b.x, seat.y - b.y) < 0.5) {
st.setBoardPosition({ x: b.x + dx, y: b.y + dy }, b.id); st.setBoardPosition({ x: b.x + dx, y: b.y + dy }, b.id);
} }
} }
@ -949,10 +948,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
const boardRunning = !!(b && (b.running || st.running)); const boardRunning = !!(b && (b.running || st.running));
const seatedOn = const seatedOn =
boardRunning && b boardRunning && b
? st.components.find((c) => { ? st.components.find((c) => isBoardSeated(b.id, b.boardKind, b.x, b.y, [c]))
const seat = snapBoardToSocket(b.id, b.boardKind, b.x, b.y, [c]);
return !!seat && Math.hypot(seat.x - b.x, seat.y - b.y) < 0.5;
})
: undefined; : undefined;
carriedSocketRef.current = { dragId: touchId, sockId: seatedOn?.id ?? '' }; carriedSocketRef.current = { dragId: touchId, sockId: seatedOn?.id ?? '' };
} }
@ -1628,10 +1624,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
const boardRunning = !!(b && (b.running || st.running)); const boardRunning = !!(b && (b.running || st.running));
const seatedOn = const seatedOn =
boardRunning && b boardRunning && b
? st.components.find((c) => { ? st.components.find((c) => isBoardSeated(b.id, b.boardKind, b.x, b.y, [c]))
const seat = snapBoardToSocket(b.id, b.boardKind, b.x, b.y, [c]);
return !!seat && Math.hypot(seat.x - b.x, seat.y - b.y) < 0.5;
})
: undefined; : undefined;
carriedSocketRef.current = { carriedSocketRef.current = {
dragId: draggedComponentId, dragId: draggedComponentId,

View File

@ -52,7 +52,7 @@ import {
collectWireSegments, collectWireSegments,
} from '../utils/wireAutoRoute'; } from '../utils/wireAutoRoute';
import { isBreadboard } from '../utils/breadboardNets'; import { isBreadboard } from '../utils/breadboardNets';
import { snapBoardToSocket } from '../utils/socketSnap'; import { isBoardSeated } from '../utils/socketSnap';
import { computeSeating } from '../utils/breadboardSnap'; import { computeSeating } from '../utils/breadboardSnap';
import { createSerialBatcher } from './serialBatcher'; import { createSerialBatcher } from './serialBatcher';
import { import {
@ -3169,16 +3169,9 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
// above — raising the socket alone buried its own seated board, and a // above — raising the socket alone buried its own seated board, and a
// buried board cannot be grabbed to unplug it. // buried board cannot be grabbed to unplug it.
const seatedOnIt = s.components.some((c) => c.id === id) const seatedOnIt = s.components.some((c) => c.id === id)
? s.boards.filter((b) => { ? s.boards.filter((b) =>
const seat = snapBoardToSocket( isBoardSeated(b.id, b.boardKind, b.x, b.y, s.components.filter((c) => c.id === id)),
b.id, )
b.boardKind,
b.x,
b.y,
s.components.filter((c) => c.id === id),
);
return !!seat && Math.hypot(seat.x - b.x, seat.y - b.y) < 0.5;
})
: []; : [];
let top = s.zTop; let top = s.zTop;
const zOrders = { ...s.zOrders, [id]: ++top }; const zOrders = { ...s.zOrders, [id]: ++top };

View File

@ -95,12 +95,24 @@ export function snapBoardToSocket(
} }
/** /**
* True when the board's CURRENT position IS a socket seat (within half a * How far off the exact seat a board may sit and still count as plugged in.
* pixel). This is the z-order question, not the drag question: a seated * Not zero, and deliberately far below SOCKET_SNAP_TOLERANCE: a stack the
* board must paint above its socket component, an unseated one must stay * magnet built lands exact, but one an EXAMPLE declares (or a project saved
* below components like every other board the blanket zIndex bump that * before a socket's art was nudged) can be a fraction of a pixel out. At the
* preceded this check hid a resistor behind an Arduino in every ordinary * old half-pixel bar such a board looked seated on screen while every seat
* example. * test said otherwise so it got no electrical connection and its socket
* did not travel with it. A couple of pixels is invisible to the eye and
* still nowhere near the next hole.
*/
const SEATED_EPSILON = 2;
/**
* True when the board's CURRENT position IS a socket seat. This is the
* "is it plugged in?" question, asked by z-order (a seated board must paint
* above its socket, an unseated one stays below components like every other
* board a blanket zIndex bump once hid a resistor behind an Arduino),
* by the electrical hop that makes seating mean connection, and by the drag
* rules that keep a plugged stack together.
*/ */
export function isBoardSeated( export function isBoardSeated(
boardId: string, boardId: string,
@ -110,5 +122,5 @@ export function isBoardSeated(
components: ComponentLike[], components: ComponentLike[],
): boolean { ): boolean {
const seat = snapBoardToSocket(boardId, boardKind, x, y, components); const seat = snapBoardToSocket(boardId, boardKind, x, y, components);
return !!seat && Math.hypot(seat.x - x, seat.y - y) < 0.5; return !!seat && Math.hypot(seat.x - x, seat.y - y) <= SEATED_EPSILON;
} }