47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import type { Iwt2ArenaBounds, Iwt2Circle, Iwt2Vec2 } from './types'
|
|
import {
|
|
addVec2,
|
|
clamp,
|
|
clampVec2ToArena,
|
|
distanceVec2,
|
|
dotVec2,
|
|
lengthSqVec2,
|
|
normalizeVec2,
|
|
scaleVec2,
|
|
subtractVec2,
|
|
} from './vector'
|
|
|
|
export function circlesOverlap(a: Iwt2Circle, b: Iwt2Circle): boolean {
|
|
const radiusSum = a.radius + b.radius
|
|
return distanceVec2(a.position, b.position) <= radiusSum
|
|
}
|
|
|
|
export function distanceToSegment(point: Iwt2Vec2, start: Iwt2Vec2, end: Iwt2Vec2): number {
|
|
const segment = subtractVec2(end, start)
|
|
const segmentLengthSq = lengthSqVec2(segment)
|
|
if (segmentLengthSq <= 0.0001) return distanceVec2(point, start)
|
|
const pointOffset = subtractVec2(point, start)
|
|
const t = clamp(dotVec2(pointOffset, segment) / segmentLengthSq, 0, 1)
|
|
const projection = addVec2(start, scaleVec2(segment, t))
|
|
return distanceVec2(point, projection)
|
|
}
|
|
|
|
export function circleIntersectsSegment(
|
|
circle: Iwt2Circle,
|
|
start: Iwt2Vec2,
|
|
end: Iwt2Vec2,
|
|
width: number,
|
|
): boolean {
|
|
return distanceToSegment(circle.position, start, end) <= circle.radius + width
|
|
}
|
|
|
|
export function separateCircles(moving: Iwt2Circle, fixed: Iwt2Circle, bounds: Iwt2ArenaBounds): Iwt2Vec2 {
|
|
const offset = subtractVec2(moving.position, fixed.position)
|
|
const minDistance = moving.radius + fixed.radius
|
|
const distanceSq = lengthSqVec2(offset)
|
|
if (distanceSq >= minDistance * minDistance) return moving.position
|
|
const direction = distanceSq <= 0.0001 ? { x: 1, y: 0 } : normalizeVec2(offset)
|
|
const resolved = addVec2(fixed.position, scaleVec2(direction, minDistance))
|
|
return clampVec2ToArena(resolved, moving.radius, bounds)
|
|
}
|