Skip to content

Instantly share code, notes, and snippets.

@fostyfost
Created April 3, 2023 08:14
Show Gist options
  • Save fostyfost/251f34c3846f09233a506b1d9f766aab to your computer and use it in GitHub Desktop.
Save fostyfost/251f34c3846f09233a506b1d9f766aab to your computer and use it in GitHub Desktop.
D3 force rectangle
import { quadtree } from 'd3'
type Rectangle = {
x: number
y: number
width: number
height: number
}
export const forceRectangle = <T extends Rectangle>(gapX: number, gapY: number) => {
let nodes: T[] = []
const getX = (rectangle: T): number => rectangle.x
const getY = (rectangle: T): number => rectangle.y
const force = () => {
const quad = quadtree(nodes, getX, getY)
for (const node of nodes) {
quad.visit(quadItem => {
let updated = false
if ('data' in quadItem && quadItem.data && quadItem.data !== node) {
let x = getX(node) - getX(quadItem.data)
let y = getY(node) - getY(quadItem.data)
const xSpacing = gapX + (quadItem.data.width + node.width) / 2
const ySpacing = gapY + (quadItem.data.height + node.height) / 2
const absX = Math.abs(x)
const absY = Math.abs(y)
if (absX < xSpacing && absY < ySpacing) {
const distance = Math.sqrt(x * x + y * y)
let distanceX = (absX - xSpacing) / distance
let distanceY = (absY - ySpacing) / distance
// The one that's barely within the bounds probably triggered the collision
if (Math.abs(distanceX) > Math.abs(distanceY)) {
distanceX = 0
} else {
distanceY = 0
}
node.x -= x *= distanceX
node.y -= y *= distanceY
quadItem.data.x += x
quadItem.data.y += y
updated = true
}
}
return updated
})
}
}
force.initialize = (_: T[]) => (nodes = _)
return force
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment