Refactor shapes

This commit is contained in:
schmelczerandras 2020-08-16 10:24:12 +02:00
commit 006ab3c4e6
6 changed files with 203 additions and 256 deletions

View 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
);
}
}

View 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
);
}
}

View 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;
}

View file

@ -0,0 +1,3 @@
import { BoundingBoxBase } from './bounding-box-base';
export class ImmutableBoundingBox extends BoundingBoxBase {}

View 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);
}
}

View 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);
}
}