init
This commit is contained in:
+119
@@ -0,0 +1,119 @@
|
||||
import type { Vector } from "./Vector.ts";
|
||||
|
||||
export type Node<T> = T;
|
||||
export type Edge<T> = [Node<T>, Node<T>];
|
||||
export type Graph<T> = Map<Node<T>, Array<Node<T>>>;
|
||||
|
||||
type PositionPartial = {
|
||||
position: Vector;
|
||||
};
|
||||
|
||||
export function drawNode(
|
||||
node: Node<PositionPartial>,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
color: string,
|
||||
): void {
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.position.x, node.position.y, 8, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
export function getNodeByPosition(
|
||||
nodes: Array<Node<PositionPartial>>,
|
||||
position: Vector,
|
||||
): Node<PositionPartial> | null {
|
||||
for (const node of nodes) {
|
||||
if (
|
||||
node.position.x <= position.x + 16 &&
|
||||
node.position.x >= position.x - 16 &&
|
||||
node.position.y <= position.y + 16 &&
|
||||
node.position.y >= position.y - 16
|
||||
) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function drawEdge(
|
||||
edge: Edge<PositionPartial>,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
color: string,
|
||||
): void {
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(edge[0].position.x, edge[0].position.y);
|
||||
ctx.lineTo(edge[1].position.x, edge[1].position.y);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
export function createGraph<T>(
|
||||
nodes: Array<Node<T>>,
|
||||
edges: Array<Edge<T>>,
|
||||
): Graph<T> {
|
||||
const graph: Graph<T> = new Map();
|
||||
|
||||
for (const node of nodes) {
|
||||
const neighbours = edges.reduce(
|
||||
(acc, edge) => {
|
||||
if (edge[0] === node) {
|
||||
acc.push(edge[1]);
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (edge[1] === node) {
|
||||
acc.push(edge[0]);
|
||||
return acc;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
[] as Array<Node<T>>,
|
||||
);
|
||||
|
||||
if (neighbours.length > 0) {
|
||||
graph.set(node, neighbours);
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
export function originDFS<T>(origin: Node<T>, graph: Graph<T>): Array<Node<T>> {
|
||||
const visited: Array<Node<T>> = [origin];
|
||||
|
||||
if (!graph.has(origin)) {
|
||||
return visited;
|
||||
}
|
||||
|
||||
innerDFS(origin, graph, visited);
|
||||
|
||||
return visited;
|
||||
}
|
||||
|
||||
function innerDFS<T>(
|
||||
node: Node<T>,
|
||||
graph: Graph<T>,
|
||||
visited: Array<Node<T>>,
|
||||
): void {
|
||||
const neighbours = graph.get(node)!;
|
||||
for (const node of neighbours) {
|
||||
if (visited.includes(node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.push(node);
|
||||
innerDFS(node, graph, visited);
|
||||
}
|
||||
}
|
||||
|
||||
export function getNeighbours<T>(
|
||||
graph: Graph<T>,
|
||||
node: Node<T>,
|
||||
): Array<Node<T>> {
|
||||
return graph.has(node) ? graph.get(node)! : [];
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { lerp } from "./lerp.ts";
|
||||
import type { Vector } from "./Vector.ts";
|
||||
|
||||
export type Segment = [Vector, Vector];
|
||||
|
||||
export type SegmentIntersection = {
|
||||
position: Vector;
|
||||
offset: number;
|
||||
};
|
||||
|
||||
export function createSegmentIntersection(): SegmentIntersection {
|
||||
return {
|
||||
position: {
|
||||
x: Number.NaN,
|
||||
y: Number.NaN,
|
||||
},
|
||||
offset: Number.NaN,
|
||||
};
|
||||
}
|
||||
|
||||
export function setSegmentIntersection(
|
||||
segmentIntersection: SegmentIntersection,
|
||||
a: Segment,
|
||||
b: Segment,
|
||||
): void {
|
||||
const bottom =
|
||||
(b[1].y - b[0].y) * (a[1].x - a[0].x) -
|
||||
(b[1].x - b[0].x) * (a[1].y - a[0].y);
|
||||
|
||||
if (bottom === 0) {
|
||||
segmentIntersection.position.x = Number.NaN;
|
||||
segmentIntersection.position.y = Number.NaN;
|
||||
segmentIntersection.offset = Number.NaN;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const tTop =
|
||||
(b[1].x - b[0].x) * (a[0].y - b[0].y) -
|
||||
(b[1].y - b[0].y) * (a[0].x - b[0].x);
|
||||
const uTop =
|
||||
(b[0].y - a[0].y) * (a[0].x - a[1].x) -
|
||||
(b[0].x - a[0].x) * (a[0].y - a[1].y);
|
||||
|
||||
const t = tTop / bottom;
|
||||
const u = uTop / bottom;
|
||||
|
||||
if (t < 0 || t > 1 || u < 0 || u > 1) {
|
||||
segmentIntersection.position.x = Number.NaN;
|
||||
segmentIntersection.position.y = Number.NaN;
|
||||
segmentIntersection.offset = Number.NaN;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
segmentIntersection.position.x = lerp(a[0].x, a[1].x, t);
|
||||
segmentIntersection.position.y = lerp(a[0].y, a[1].y, t);
|
||||
segmentIntersection.offset = t;
|
||||
}
|
||||
|
||||
export function drawSegment(
|
||||
segment: Segment,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
color: string,
|
||||
): void {
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(segment[0].x, segment[0].y);
|
||||
ctx.lineTo(segment[1].x, segment[1].y);
|
||||
ctx.stroke();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
export type Sprite = {
|
||||
image: HTMLImageElement;
|
||||
xFrames: number;
|
||||
yFrames: number;
|
||||
frameWidth: number;
|
||||
frameHeight: number;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export function createSprite(
|
||||
image: HTMLImageElement,
|
||||
xFrames: number,
|
||||
yFrames: number,
|
||||
): Sprite {
|
||||
return {
|
||||
image,
|
||||
xFrames,
|
||||
yFrames,
|
||||
frameWidth: image.width / xFrames,
|
||||
frameHeight: image.height / yFrames,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function drawSprite(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
sprite: Sprite,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
): void {
|
||||
ctx.drawImage(
|
||||
sprite.image,
|
||||
sprite.x,
|
||||
sprite.y,
|
||||
sprite.frameWidth,
|
||||
sprite.frameHeight,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
export type Vector = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export function createVector(x: number = 0, y: number = 0): Vector {
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
export function fromPolar(direction: number, magnitude: number): Vector {
|
||||
return {
|
||||
x: Math.cos(direction) * magnitude,
|
||||
y: Math.sin(direction) * magnitude,
|
||||
};
|
||||
}
|
||||
|
||||
export function getDirection(vector: Vector): number {
|
||||
return Math.atan2(vector.y, vector.x);
|
||||
}
|
||||
|
||||
export function getMagnitude(vector: Vector): number {
|
||||
return Math.sqrt(vector.x * vector.x + vector.y * vector.y);
|
||||
}
|
||||
|
||||
export function getDistance(a: Vector, b: Vector): number {
|
||||
const dx = a.x - b.x;
|
||||
const dy = a.y - b.y;
|
||||
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
export function getSquared(vector: Vector): number {
|
||||
return vector.x * vector.x + vector.y * vector.y;
|
||||
}
|
||||
|
||||
export function setDistanceNormal(normal: Vector, a: Vector, b: Vector): void {
|
||||
const dx = a.x - b.x;
|
||||
const dy = a.y - b.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance > 0) {
|
||||
normal.x = dx / distance;
|
||||
normal.y = dy / distance;
|
||||
}
|
||||
}
|
||||
|
||||
export function getDotProduct(a: Vector, b: Vector): number {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
export function getPerpDotProduct(a: Vector, b: Vector): number {
|
||||
return a.x * b.x - a.y * b.y;
|
||||
}
|
||||
|
||||
export function scale(vector: Vector, scalar: number): void {
|
||||
vector.x *= scalar;
|
||||
vector.y *= scalar;
|
||||
}
|
||||
|
||||
export function normalize(vector: Vector): void {
|
||||
const magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
|
||||
|
||||
if (magnitude > 0) {
|
||||
const scalar = 1 / magnitude;
|
||||
vector.x *= scalar;
|
||||
vector.y *= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
export function isZero(vector: Vector): boolean {
|
||||
return vector.x === 0 && vector.y === 0;
|
||||
}
|
||||
|
||||
export function isBelowThreshold(vector: Vector, threshold: number): boolean {
|
||||
return Math.abs(vector.x) < threshold && Math.abs(vector.y) < threshold;
|
||||
}
|
||||
|
||||
export function equals(a: Vector, b: Vector): boolean {
|
||||
return a.x === b.x && a.y === b.y;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export function lerp(a: number, b: number, t: number): number {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
export function lerpPrecice(a: number, b: number, t: number): number {
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export function loadImage(src: string): Promise<HTMLImageElement> {
|
||||
return new Promise((resolve) => {
|
||||
const image = new Image();
|
||||
image.onload = () => resolve(image);
|
||||
image.onerror = () => {
|
||||
console.error(image.src);
|
||||
return resolve(image);
|
||||
};
|
||||
|
||||
image.src = src;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user