From f07dc0bf515ba8dc80a444583ec58fed916c8ed5 Mon Sep 17 00:00:00 2001 From: kolaflash Date: Mon, 9 Feb 2026 23:26:50 +0100 Subject: [PATCH] WIP --- engine/AABB.ts | 43 ++++++++++++ engine/Circle.ts | 26 +++++++ engine/Polygon.ts | 7 ++ engine/Vector.ts | 7 ++ engine/lib/geometry.ts | 121 +++++++++++++++++++++++++++++++++ game/entities/hero/entity.ts | 2 + game/scenes/testScene/scene.ts | 18 ++--- 7 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 engine/AABB.ts create mode 100644 engine/Circle.ts create mode 100644 engine/Polygon.ts create mode 100644 engine/lib/geometry.ts diff --git a/engine/AABB.ts b/engine/AABB.ts new file mode 100644 index 0000000..1ea6ee8 --- /dev/null +++ b/engine/AABB.ts @@ -0,0 +1,43 @@ +import { copyVector, type Vector } from "#/engine/Vector.ts"; + +export type AABB = { + position: Vector; + width: number; + height: number; +}; + +export function createAABB( + position: Vector, + width: number, + height: number, +): AABB { + return { + position, + width, + height, + }; +} + +export function fromMinMax(min: Vector, max: Vector): AABB { + return { + position: copyVector(min), + width: max.x - min.x, + height: max.y - min.y, + }; +} + +export function drawAABB( + ctx: CanvasRenderingContext2D, + aabb: AABB, + color: string = "#fff", +): void { + ctx.save(); + ctx.strokeStyle = color; + ctx.strokeRect( + Math.round(aabb.position.x) - 0.5, + Math.round(aabb.position.y) - 0.5, + aabb.width, + aabb.height, + ); + ctx.restore(); +} diff --git a/engine/Circle.ts b/engine/Circle.ts new file mode 100644 index 0000000..57eea55 --- /dev/null +++ b/engine/Circle.ts @@ -0,0 +1,26 @@ +import type { Vector } from "#/engine/Vector.ts"; + +export type Circle = { + position: Vector; + radius: number; +}; + +export function createCircle(position: Vector, radius: number): Circle { + return { + position, + radius, + }; +} + +export function drawCircle( + ctx: CanvasRenderingContext2D, + circle: Circle, + color: string = "#fff", +): void { + ctx.save(); + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.arc(circle.position.x, circle.position.y, circle.radius, 0, 2 * Math.PI); + ctx.stroke(); + ctx.restore(); +} diff --git a/engine/Polygon.ts b/engine/Polygon.ts new file mode 100644 index 0000000..03b5f39 --- /dev/null +++ b/engine/Polygon.ts @@ -0,0 +1,7 @@ +import type { Vector } from "#/engine/Vector.ts"; + +export type Polygon = { + position: Vector; + points: Array; + axes: Array; +}; diff --git a/engine/Vector.ts b/engine/Vector.ts index 39fe6b8..3235880 100644 --- a/engine/Vector.ts +++ b/engine/Vector.ts @@ -7,6 +7,13 @@ export function createVector(x: number = 0, y: number = 0): Vector { return { x, y }; } +export function copyVector(vector: Vector): Vector { + return { + x: vector.x, + y: vector.y, + }; +} + export function fromPolar(direction: number, magnitude: number): Vector { return { x: Math.cos(direction) * magnitude, diff --git a/engine/lib/geometry.ts b/engine/lib/geometry.ts new file mode 100644 index 0000000..9de04fc --- /dev/null +++ b/engine/lib/geometry.ts @@ -0,0 +1,121 @@ +import type { AABB } from "#/engine/AABB.ts"; +import type { Circle } from "#/engine/Circle.ts"; +import type { Vector } from "#/engine/Vector.ts"; + +export function isPointInCircle(point: Vector, circle: Circle): boolean { + const dx = point.x - circle.position.x; + const dy = point.y - circle.position.y; + + return dx * dx + dy * dy <= circle.radius ** 2; +} + +export function isPointInAABB(point: Vector, aabb: AABB): boolean { + const rx = aabb.position.x; + const ry = aabb.position.y; + + return ( + point.x >= rx && + point.x <= rx + aabb.width && + point.y >= ry && + point.y <= ry + aabb.height + ); +} + +export function circleIntersectCircle(a: Circle, b: Circle): boolean { + const dx = a.position.x - b.position.x; + const dy = a.position.y - b.position.y; + + return dx * dx + dy * dy <= (a.radius + b.radius) ** 2; +} + +export function AABBIntersectAABB(a: AABB, b: AABB): boolean { + const ax = a.position.x; + const ay = a.position.y; + const bx = b.position.x; + const by = b.position.y; + + return ( + ax <= bx + b.width && + ax + a.width >= bx && + ay <= by + b.height && + ay + a.height >= by + ); +} + +export function circleIntersectAABB(circle: Circle, aabb: AABB): boolean { + const cx = circle.position.x; + const cy = circle.position.y; + const rx = aabb.position.x; + const ry = aabb.position.y; + + const dx = cx - Math.max(rx, Math.min(cx, rx + aabb.width)); + const dy = cy - Math.max(ry, Math.min(cy, ry + aabb.height)); + + return dx * dx + dy * dy <= circle.radius ** 2; +} + +export function circleContainsCircle(a: Circle, b: Circle): boolean { + const dx = a.position.x - b.position.x; + const dy = a.position.y - b.position.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + return a.radius >= b.radius + distance; +} + +export function AABBContainsAABB(a: AABB, b: AABB): boolean { + const ax = a.position.x; + const ay = a.position.y; + const bx = b.position.x; + const by = b.position.y; + + return ( + ax <= bx && + ax + a.width >= bx + b.width && + ay <= by && + ay + a.height >= by + b.height + ); +} + +export function AABBContainsCircle(aabb: AABB, circle: Circle): boolean { + const rx = aabb.position.x; + const ry = aabb.position.y; + const cx = circle.position.x; + const cy = circle.position.y; + + return ( + rx <= cx - circle.radius && + rx + aabb.width >= cx + circle.radius && + ry <= cy - circle.radius && + ry + aabb.height >= cy + circle.radius + ); +} + +export function circleContainsAABB(circle: Circle, aabb: AABB): boolean { + const radiusSquare = circle.radius ** 2; + + const dax = aabb.position.x - circle.position.x; + const day = aabb.position.y - circle.position.y; + if (dax * dax + day * day > radiusSquare) { + return false; + } + + const dbx = aabb.position.x + aabb.width - circle.position.x; + const dby = aabb.position.y - circle.position.y; + if (dbx * dbx + dby * dby > radiusSquare) { + return false; + } + + const dcx = aabb.position.x - circle.position.x; + const dcy = aabb.position.y + aabb.height - circle.position.y; + if (dcx * dcx + dcy * dcy > radiusSquare) { + return false; + } + + const ddx = aabb.position.x + aabb.width - circle.position.x; + const ddy = aabb.position.y + aabb.height - circle.position.y; + if (ddx * ddx + ddy * ddy > radiusSquare) { + return false; + } + + return true; +} diff --git a/game/entities/hero/entity.ts b/game/entities/hero/entity.ts index 88e9d7b..e6c3c74 100644 --- a/game/entities/hero/entity.ts +++ b/game/entities/hero/entity.ts @@ -5,6 +5,8 @@ import { pointer } from "#/engine/Pointer.ts"; import { fromSrc, type Sprite } from "#/engine/Sprite.ts"; import charIdle from "#/game/assets/char-idle.png"; +// import { moveAndCollide } from "#/some/where.ts"; + const idle: Sprite = await fromSrc(charIdle, 2, 4); const { animate, process } = defineEntity("hero", { diff --git a/game/scenes/testScene/scene.ts b/game/scenes/testScene/scene.ts index 11def94..e7ee122 100644 --- a/game/scenes/testScene/scene.ts +++ b/game/scenes/testScene/scene.ts @@ -1,6 +1,6 @@ +import { drawCircle } from "#/engine/Circle"; import { pointer } from "#/engine/Pointer.ts"; import { defineScene } from "#/engine/Scene.ts"; -import type { Vector } from "#/engine/Vector.ts"; import data from "./data.json"; const { process, preProcess, linkScenes } = defineScene(data); @@ -15,19 +15,11 @@ preProcess((entityInstanceMap) => { instance.position.y = data.height - 16; }); -function drawPoint( - ctx: CanvasRenderingContext2D, - position: Vector, - radius: number = 4, -): void { - ctx.beginPath(); - ctx.arc(position.x, position.y, radius, 0, 2 * Math.PI); - ctx.strokeStyle = "#ff9000"; - ctx.stroke(); -} - process((ctx, delta) => { if (!pointer.isDown) { - drawPoint(ctx, pointer.position); + drawCircle(ctx, { + position: pointer.position, + radius: 4, + }); } });