Refactor shapes
This commit is contained in:
parent
eb39846b75
commit
006ab3c4e6
6 changed files with 203 additions and 256 deletions
61
frontend/src/scripts/shapes/bounding-box-base.ts
Normal file
61
frontend/src/scripts/shapes/bounding-box-base.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { IShape } from './i-shape';
|
||||
|
||||
export abstract class BoundingBoxBase {
|
||||
constructor(
|
||||
public readonly shape: IShape,
|
||||
protected _xMin: number = 0,
|
||||
protected _xMax: number = 0,
|
||||
protected _yMin: number = 0,
|
||||
protected _yMax: number = 0
|
||||
) {}
|
||||
|
||||
public get 0(): number {
|
||||
return this._xMin;
|
||||
}
|
||||
|
||||
public get 1(): number {
|
||||
return this._xMax;
|
||||
}
|
||||
|
||||
public get 2(): number {
|
||||
return this._yMin;
|
||||
}
|
||||
|
||||
public get 3(): number {
|
||||
return this._yMax;
|
||||
}
|
||||
|
||||
public get xMin(): number {
|
||||
return this._xMin;
|
||||
}
|
||||
|
||||
public get xMax(): number {
|
||||
return this._xMax;
|
||||
}
|
||||
|
||||
public get yMin(): number {
|
||||
return this._yMin;
|
||||
}
|
||||
|
||||
public get yMax(): number {
|
||||
return this._yMax;
|
||||
}
|
||||
|
||||
public get topLeft(): vec2 {
|
||||
return vec2.fromValues(this._xMin, this._yMax);
|
||||
}
|
||||
|
||||
public get size(): vec2 {
|
||||
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
|
||||
}
|
||||
|
||||
public intersects(other: BoundingBoxBase): boolean {
|
||||
return (
|
||||
this._xMin < other._xMax &&
|
||||
this._xMax > other._xMin &&
|
||||
this._yMin < other._yMax &&
|
||||
this._yMax > other._yMin
|
||||
);
|
||||
}
|
||||
}
|
||||
33
frontend/src/scripts/shapes/bounding-box.ts
Normal file
33
frontend/src/scripts/shapes/bounding-box.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { BoundingBoxBase } from './bounding-box-base';
|
||||
import { ImmutableBoundingBox } from './immutable-bounding-box';
|
||||
|
||||
export class BoundingBox extends BoundingBoxBase {
|
||||
public get topLeft(): vec2 {
|
||||
return vec2.fromValues(this._xMin, this._yMax);
|
||||
}
|
||||
|
||||
public set topLeft(value: vec2) {
|
||||
this._xMin = value.x;
|
||||
this._yMax = value.y;
|
||||
}
|
||||
|
||||
public get size(): vec2 {
|
||||
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
|
||||
}
|
||||
|
||||
public set size(value: vec2) {
|
||||
this._xMax = this.xMin + value.x;
|
||||
this._yMin = this.yMax - value.y;
|
||||
}
|
||||
|
||||
public cloneAsImmutable(): ImmutableBoundingBox {
|
||||
return new ImmutableBoundingBox(
|
||||
this.shape,
|
||||
this.xMin,
|
||||
this.xMax,
|
||||
this.yMin,
|
||||
this.yMax
|
||||
);
|
||||
}
|
||||
}
|
||||
12
frontend/src/scripts/shapes/i-shape.ts
Normal file
12
frontend/src/scripts/shapes/i-shape.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { BoundingBox } from './bounding-box';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export interface IShape {
|
||||
readonly isInverted: boolean;
|
||||
readonly boundingBox: BoundingBox;
|
||||
|
||||
distance(target: vec2): number;
|
||||
normal(from: vec2): vec2;
|
||||
|
||||
clone(): IShape;
|
||||
}
|
||||
3
frontend/src/scripts/shapes/immutable-bounding-box.ts
Normal file
3
frontend/src/scripts/shapes/immutable-bounding-box.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { BoundingBoxBase } from './bounding-box-base';
|
||||
|
||||
export class ImmutableBoundingBox extends BoundingBoxBase {}
|
||||
41
frontend/src/scripts/shapes/types/circle.ts
Normal file
41
frontend/src/scripts/shapes/types/circle.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { IShape } from '../i-shape';
|
||||
import { BoundingBox } from '../bounding-box';
|
||||
|
||||
export class Circle implements IShape {
|
||||
public readonly isInverted = false;
|
||||
|
||||
public constructor(public center = vec2.create(), public radius = 0) {}
|
||||
|
||||
public distance(target: vec2): number {
|
||||
return vec2.distance(this.center, target) - this.radius;
|
||||
}
|
||||
|
||||
public normal(from: vec2): vec2 {
|
||||
const diff = vec2.subtract(vec2.create(), from, this.center);
|
||||
return vec2.normalize(diff, diff);
|
||||
}
|
||||
|
||||
public get boundingBox(): BoundingBox {
|
||||
return new BoundingBox(
|
||||
this,
|
||||
this.center.x - this.radius,
|
||||
this.center.x + this.radius,
|
||||
this.center.y - this.radius,
|
||||
this.center.y + this.radius
|
||||
);
|
||||
}
|
||||
|
||||
public isInside(target: vec2): boolean {
|
||||
return this.distance(target) < 0;
|
||||
}
|
||||
|
||||
public areIntersecting(other: Circle): boolean {
|
||||
const distance = vec2.distance(this.center, other.center);
|
||||
return distance < this.radius + other.radius;
|
||||
}
|
||||
|
||||
public clone(): Circle {
|
||||
return new Circle(this.center, this.radius);
|
||||
}
|
||||
}
|
||||
65
frontend/src/scripts/shapes/types/tunnel-shape.ts
Normal file
65
frontend/src/scripts/shapes/types/tunnel-shape.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { BoundingBox } from '../bounding-box';
|
||||
import { clamp01 } from '../../helper/clamp';
|
||||
import { mix } from '../../helper/mix';
|
||||
import { IShape } from '../i-shape';
|
||||
|
||||
export class TunnelShape implements IShape {
|
||||
public readonly isInverted = true;
|
||||
|
||||
public readonly toFromDelta: vec2;
|
||||
|
||||
constructor(
|
||||
public readonly from: vec2,
|
||||
public readonly to: vec2,
|
||||
public readonly fromRadius: number,
|
||||
public readonly toRadius: number
|
||||
) {
|
||||
this.toFromDelta = vec2.subtract(vec2.create(), to, from);
|
||||
}
|
||||
|
||||
public get boundingBox(): BoundingBox {
|
||||
const xMin = Math.min(
|
||||
this.from.x - this.fromRadius,
|
||||
this.to.x - this.toRadius
|
||||
);
|
||||
const yMin = Math.min(
|
||||
this.from.y - this.fromRadius,
|
||||
this.to.y - this.toRadius
|
||||
);
|
||||
const xMax = Math.max(
|
||||
this.from.x + this.fromRadius,
|
||||
this.to.x + this.toRadius
|
||||
);
|
||||
const yMax = Math.max(
|
||||
this.from.y + this.fromRadius,
|
||||
this.to.y + this.toRadius
|
||||
);
|
||||
|
||||
return new BoundingBox(this, xMin, xMax, yMin, yMax);
|
||||
}
|
||||
|
||||
public normal(from: vec2): vec2 {
|
||||
throw new Error('Unimplemented');
|
||||
}
|
||||
|
||||
public distance(target: vec2): number {
|
||||
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
||||
|
||||
const h = clamp01(
|
||||
vec2.dot(targetFromDelta, this.toFromDelta) /
|
||||
vec2.dot(this.toFromDelta, this.toFromDelta)
|
||||
);
|
||||
|
||||
return (
|
||||
vec2.distance(
|
||||
targetFromDelta,
|
||||
vec2.scale(vec2.create(), this.toFromDelta, h)
|
||||
) - mix(this.fromRadius, this.toRadius, h)
|
||||
);
|
||||
}
|
||||
|
||||
public clone(): TunnelShape {
|
||||
return new TunnelShape(this.from, this.to, this.fromRadius, this.toRadius);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue