Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Last active June 23, 2023 01:50
Show Gist options
  • Save steveruizok/5f47a58318afddf09379e07bce4e883b to your computer and use it in GitHub Desktop.
Save steveruizok/5f47a58318afddf09379e07bce4e883b to your computer and use it in GitHub Desktop.
Find the snap points between a bounding box and several other bounding boxes.
interface TLBoundsWithCenter {
minX: number
midX: number
maxX: number
minY: number
midY: number
maxY: number
width: number
height: number
}
function findSnapPoints(
bounds: TLBoundsWithCenter,
others: TLBoundsWithCenter[],
isCareful: boolean
) {
const A = { ...bounds }
const offset = [0, 0]
const snapLines: number[][][] = []
// 1.
// Find the snap points for the x and y axes
let xs = null as { B: TLBoundsWithCenter; i: number } | null
let ys = null as { B: TLBoundsWithCenter; i: number } | null
const fxs = [A.midX, A.minX, A.maxX]
const fys = [A.midY, A.minY, A.maxY]
for (const B of others) {
if (!xs) {
const txs = [B.midX, B.minX, B.maxX]
fxs.forEach((f, i) =>
txs.forEach((t, k) => {
// If we're not dragging carefully, only snap to center or opposite points
if (xs || !(isCareful || i === 0 || i + k === 3)) return
if (Math.abs(t - f) < distance) {
xs = { B, i }
offset[0] = [
// How far to offset the delta on the x axis in
// order to "snap" the selection to the right place
A.midX - t,
A.midX - (t + A.width / 2),
A.midX - (t - A.width / 2),
][i]
// Also apply the offset to the bounds
A.minX -= offset[0]
A.midX -= offset[0]
A.maxX -= offset[0]
}
})
)
}
if (!ys) {
const tys = [B.midY, B.minY, B.maxY]
fys.forEach((f, i) =>
tys.forEach((t, k) => {
if (ys || !(isCareful || i === k)) return
if (Math.abs(t - f) < distance) {
ys = { B, i }
offset[1] = [
//
A.midY - t,
A.midY - (t + A.height / 2),
A.midY - (t - A.height / 2),
][i]
A.minY -= offset[1]
A.midY -= offset[1]
A.maxY -= offset[1]
}
})
)
}
if (xs && ys) break
}
// 2.
// Calculate snap lines based on adjusted bounds A. This has
// to happen after we've adjusted both dimensions x and y of
// the bounds A!
if (xs) {
const { i, B } = xs
const x = [A.midX, A.minX, A.maxX][i % 3]
// If A is snapped at its center, show include only the midY;
// otherwise, include both its minY and maxY.
snapLines.push(
i === 0
? [
[x, A.midY],
[x, B.minY],
[x, B.maxY],
]
: [
[x, A.minY],
[x, A.maxY],
[x, B.minY],
[x, B.maxY],
]
)
}
if (ys) {
const { i, B } = ys
const y = [A.midY, A.minY, A.maxY][i % 3]
snapLines.push(
i === 0
? [
[A.midX, y],
[B.minX, y],
[B.maxX, y],
]
: [
[A.minX, y],
[A.maxX, y],
[B.minX, y],
[B.maxX, y],
]
)
}
return { offset, snapLines }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment