Refactor shapes

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

View file

@ -0,0 +1,26 @@
import { IDrawable } from './i-drawable';
import { TunnelShape } from '../../shapes/types/tunnel-shape';
import { IDrawableDescriptor } from './i-drawable-descriptor';
import { settings } from '../settings';
export class DrawableTunnel extends TunnelShape implements IDrawable {
public static descriptor: IDrawableDescriptor = {
uniformName: 'lines',
countMacroName: 'lineCount',
shaderCombinationSteps: settings.shaderCombinations.lineSteps,
};
public serializeToUniforms(uniforms: any): void {
const uniformName = DrawableTunnel.descriptor.uniformName;
if (!uniforms.hasOwnProperty(uniformName)) {
uniforms[uniformName] = [];
}
uniforms[uniformName].push({
from: this.from,
toFromDelta: this.toFromDelta,
fromRadius: this.fromRadius,
toRadius: this.toRadius,
});
}
}

View file

@ -1,11 +1,6 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { ImmutableBoundingBox } from '../../physics/containers/immutable-bounding-box';
import { GameObject } from '../../objects/game-object';
export interface IDrawable { export interface IDrawable {
serializeToUniforms(uniforms: any): void;
distance(target: vec2): number; distance(target: vec2): number;
minimumDistance(target: vec2): number; serializeToUniforms(uniforms: any): void;
readonly owner: GameObject;
readonly boundingBox: ImmutableBoundingBox;
} }

View file

@ -1,6 +1,5 @@
import { vec2, vec3 } from 'gl-matrix'; import { vec2, vec3 } from 'gl-matrix';
import { GameObject } from '../../../objects/game-object'; import { GameObject } from '../../../objects/game-object';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { settings } from '../../settings'; import { settings } from '../../settings';
import { IDrawableDescriptor } from '../i-drawable-descriptor'; import { IDrawableDescriptor } from '../i-drawable-descriptor';
import { ILight } from './i-light'; import { ILight } from './i-light';
@ -20,24 +19,18 @@ export class CircleLight implements ILight {
public lightness: number public lightness: number
) {} ) {}
boundingBox: ImmutableBoundingBox;
public distance(target: vec2): number { public distance(target: vec2): number {
return 0; return 0;
} }
public minimumDistance(target: vec2): number {
return 0;
}
public serializeToUniforms(uniforms: any): void { public serializeToUniforms(uniforms: any): void {
const listName = CircleLight.descriptor.uniformName; const uniformName = CircleLight.descriptor.uniformName;
if (!uniforms.hasOwnProperty(listName)) { if (!uniforms.hasOwnProperty(uniformName)) {
uniforms[listName] = []; uniforms[uniformName] = [];
} }
uniforms[listName].push({ uniforms[uniformName].push({
center: this.center, center: this.center,
radius: this.radius, radius: this.radius,
value: this.value, value: this.value,

View file

@ -1,3 +1,4 @@
import { vec2 } from 'gl-matrix';
import { IDrawable } from '../i-drawable'; import { IDrawable } from '../i-drawable';
export interface ILight extends IDrawable {} export interface ILight extends IDrawable {}

View file

@ -2,7 +2,6 @@ import { ILight } from './i-light';
import { vec2, vec3 } from 'gl-matrix'; import { vec2, vec3 } from 'gl-matrix';
import { IDrawableDescriptor } from '../i-drawable-descriptor'; import { IDrawableDescriptor } from '../i-drawable-descriptor';
import { settings } from '../../settings'; import { settings } from '../../settings';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { GameObject } from '../../../objects/game-object'; import { GameObject } from '../../../objects/game-object';
export class PointLight implements ILight { export class PointLight implements ILight {
@ -20,16 +19,10 @@ export class PointLight implements ILight {
public lightness: number public lightness: number
) {} ) {}
boundingBox: ImmutableBoundingBox;
public distance(target: vec2): number { public distance(target: vec2): number {
return vec2.distance(this.center, target) - this.radius; return vec2.distance(this.center, target) - this.radius;
} }
public minimumDistance(target: vec2): number {
return vec2.distance(this.center, target) - this.radius;
}
public serializeToUniforms(uniforms: any): void { public serializeToUniforms(uniforms: any): void {
const listName = PointLight.descriptor.uniformName; const listName = PointLight.descriptor.uniformName;

View file

@ -1,47 +0,0 @@
import { vec2 } from 'gl-matrix';
import { GameObject } from '../../../objects/game-object';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { IPrimitive } from './i-primitive';
export class Circle implements IPrimitive {
public constructor(
public readonly owner: GameObject,
public center = vec2.create(),
public radius = 0
) { }
public serializeToUniforms(uniforms: any): void {
throw new Error('Method not implemented.');
}
public distance(target: vec2): number {
return vec2.distance(this.center, target) - this.radius;
}
public minimumDistance(target: vec2): number {
return vec2.distance(this.center, target) - this.radius;
}
public get boundingBox(): ImmutableBoundingBox {
return new ImmutableBoundingBox(
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.owner, this.center, this.radius);
}
}

View file

@ -1,5 +0,0 @@
import { IDrawable } from '../i-drawable';
export interface IPrimitive extends IDrawable {
clone(): IPrimitive
}

View file

@ -1,99 +0,0 @@
import { vec2 } from 'gl-matrix';
import { clamp01 } from '../../../helper/clamp';
import { mix } from '../../../helper/mix';
import { GameObject } from '../../../objects/game-object';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { settings } from '../../settings';
import { IDrawableDescriptor } from '../i-drawable-descriptor';
import { Circle } from './circle';
import { IPrimitive } from './i-primitive';
export class TunnelShape implements IPrimitive {
public static descriptor: IDrawableDescriptor = {
uniformName: 'lines',
countMacroName: 'lineCount',
shaderCombinationSteps: settings.shaderCombinations.lineSteps,
};
public readonly toFromDelta: vec2;
private boundingCircle: Circle;
constructor(
public readonly owner: GameObject,
public readonly from: vec2,
public readonly to: vec2,
public readonly fromRadius: number,
public readonly toRadius: number
) {
this.toFromDelta = vec2.subtract(vec2.create(), to, from);
this.boundingCircle = new Circle(
this.owner,
vec2.fromValues(from.x / 2 + to.x / 2, from.y / 2 + to.y / 2),
Math.max(fromRadius, toRadius) + vec2.distance(from, to)
);
}
public serializeToUniforms(uniforms: any): void {
if (!uniforms.hasOwnProperty(TunnelShape.descriptor.uniformName)) {
uniforms[TunnelShape.descriptor.uniformName] = [];
}
uniforms[TunnelShape.descriptor.uniformName].push({
from: this.from,
toFromDelta: this.toFromDelta,
fromRadius: this.fromRadius,
toRadius: this.toRadius,
});
}
public get boundingBox(): ImmutableBoundingBox {
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 ImmutableBoundingBox(this, xMin, xMax, yMin, yMax);
}
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 minimumDistance(target: vec2): number {
return this.boundingCircle.distance(target);
}
public clone(): TunnelShape {
return new TunnelShape(
this.owner,
this.from,
this.to,
this.fromRadius,
this.toRadius
);
}
}

View file

@ -1,12 +1,12 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { ILight } from './drawables/lights/i-light'; import { ILight } from './drawables/lights/i-light';
import { IPrimitive } from './drawables/primitives/i-primitive'; import { IDrawable } from './drawables/i-drawable';
export interface IRenderer { export interface IRenderer {
startFrame(deltaTime: DOMHighResTimeStamp): void; startFrame(deltaTime: DOMHighResTimeStamp): void;
finishFrame(): void; finishFrame(): void;
drawPrimitive(primitive: IPrimitive): void; drawShape(drawable: IDrawable): void;
drawLight(light: ILight): void; drawLight(light: ILight): void;
drawInfoText(text: string): void; drawInfoText(text: string): void;

View file

@ -38,7 +38,7 @@ export class RenderingPass {
const tileUvSize = vec2.fromValues(q, q); const tileUvSize = vec2.fromValues(q, q);
const possiblyOnScreenDrawables = this.drawables.filter( const possiblyOnScreenDrawables = this.drawables.filter(
(p) => p.minimumDistance(viewBoxCenter) < viewBoxRadius (p) => p.distance(viewBoxCenter) < viewBoxRadius
); );
const origin = vec2.transformMat2d( const origin = vec2.transformMat2d(

View file

@ -2,8 +2,6 @@ import { mat2d, vec2 } from 'gl-matrix';
import { CircleLight } from '../drawables/lights/circle-light'; import { CircleLight } from '../drawables/lights/circle-light';
import { ILight } from '../drawables/lights/i-light'; import { ILight } from '../drawables/lights/i-light';
import { PointLight } from '../drawables/lights/point-light'; import { PointLight } from '../drawables/lights/point-light';
import { IPrimitive } from '../drawables/primitives/i-primitive';
import { TunnelShape } from '../drawables/primitives/tunnel-shape';
// import lightsShader from '../shaders/rainbow-shading-fs.glsl'; // import lightsShader from '../shaders/rainbow-shading-fs.glsl';
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer'; import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer'; import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
@ -16,6 +14,8 @@ import caveVertexShader from '../shaders/passthrough-distance-vs.glsl';
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl'; import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
import { FpsAutoscaler } from './fps-autoscaler'; import { FpsAutoscaler } from './fps-autoscaler';
import { RenderingPass } from './rendering-pass'; import { RenderingPass } from './rendering-pass';
import { IDrawable } from '../drawables/i-drawable';
import { DrawableTunnel } from '../drawables/drawable-tunnel';
export class WebGl2Renderer implements IRenderer { export class WebGl2Renderer implements IRenderer {
private gl: WebGL2RenderingContext; private gl: WebGL2RenderingContext;
@ -50,7 +50,7 @@ export class WebGl2Renderer implements IRenderer {
this.distancePass = new RenderingPass( this.distancePass = new RenderingPass(
this.gl, this.gl,
[caveVertexShader, caveFragmentShader], [caveVertexShader, caveFragmentShader],
[TunnelShape.descriptor], [DrawableTunnel.descriptor],
this.distanceFieldFrameBuffer this.distanceFieldFrameBuffer
); );
@ -68,11 +68,11 @@ export class WebGl2Renderer implements IRenderer {
try { try {
this.stopwatch = new WebGlStopwatch(this.gl); this.stopwatch = new WebGlStopwatch(this.gl);
} catch { } } catch {}
} }
public drawPrimitive(primitive: IPrimitive) { public drawShape(shape: IDrawable) {
this.distancePass.addDrawable(primitive); this.distancePass.addDrawable(shape);
} }
public drawLight(light: ILight) { public drawLight(light: ILight) {
@ -94,14 +94,11 @@ export class WebGl2Renderer implements IRenderer {
vec2.scale(vec2.create(), this.viewBoxSize, 0.5) vec2.scale(vec2.create(), this.viewBoxSize, 0.5)
); );
this.distancePass.render( this.distancePass.render(this.uniforms, this.cameraPosition, viewBoxRadius);
this.uniforms,
this.cameraPosition,
viewBoxRadius
);
this.lightingPass.render( this.lightingPass.render(
this.uniforms, this.cameraPosition, this.uniforms,
this.cameraPosition,
viewBoxRadius, viewBoxRadius,
this.distanceFieldFrameBuffer.colorTexture this.distanceFieldFrameBuffer.colorTexture
); );
@ -111,7 +108,7 @@ export class WebGl2Renderer implements IRenderer {
private get uniforms(): any { private get uniforms(): any {
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition); const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
return { ...this.matrices, cursorPosition, viewBoxSize: this.viewBoxSize } return { ...this.matrices, cursorPosition, viewBoxSize: this.viewBoxSize };
} }
private calculateMatrices() { private calculateMatrices() {

View file

@ -3,7 +3,7 @@ import { BeforeRenderCommand } from '../../drawing/commands/before-render';
import { CursorMoveCommand } from '../../input/commands/cursor-move-command'; import { CursorMoveCommand } from '../../input/commands/cursor-move-command';
import { ZoomCommand } from '../../input/commands/zoom'; import { ZoomCommand } from '../../input/commands/zoom';
import { MoveToCommand } from '../../physics/commands/move-to'; import { MoveToCommand } from '../../physics/commands/move-to';
import { BoundingBox } from '../../physics/containers/bounding-box'; import { BoundingBox } from '../../shapes/bounding-box';
import { Physics } from '../../physics/physics'; import { Physics } from '../../physics/physics';
import { GameObject } from '../game-object'; import { GameObject } from '../game-object';
import { Lamp } from './lamp'; import { Lamp } from './lamp';

View file

@ -1,5 +1,5 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { Circle } from '../../drawing/drawables/primitives/circle'; import { Circle } from '../../shapes/types/circle';
import { KeyDownCommand } from '../../input/commands/key-down'; import { KeyDownCommand } from '../../input/commands/key-down';
import { KeyUpCommand } from '../../input/commands/key-up'; import { KeyUpCommand } from '../../input/commands/key-up';
import { SwipeCommand } from '../../input/commands/swipe'; import { SwipeCommand } from '../../input/commands/swipe';
@ -19,7 +19,7 @@ export class Character extends GameObject {
constructor(private physics: Physics, private camera: Camera) { constructor(private physics: Physics, private camera: Camera) {
super(); super();
this.primitive = new Circle(this); this.primitive = new Circle();
this.primitive.radius = 40; this.primitive.radius = 40;
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this)); this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
@ -45,9 +45,9 @@ export class Character extends GameObject {
const intersects = this.physics const intersects = this.physics
.findIntersecting(nextPrimitive.boundingBox) .findIntersecting(nextPrimitive.boundingBox)
.filter((b) => b.value) .filter((b) => b.shape)
.map( .map(
(b) => b.value.distance(nextPrimitive.center) + nextPrimitive.radius (b) => b.shape.distance(nextPrimitive.center) + nextPrimitive.radius
); );
console.log(intersects); console.log(intersects);

View file

@ -2,12 +2,11 @@ import { vec2 } from 'gl-matrix';
import { GameObject } from '../game-object'; import { GameObject } from '../game-object';
import { RenderCommand } from '../../drawing/commands/render'; import { RenderCommand } from '../../drawing/commands/render';
import { Physics } from '../../physics/physics'; import { Physics } from '../../physics/physics';
import { TunnelShape } from '../../drawing/drawables/primitives/tunnel-shape'; import { TunnelShape } from '../../shapes/types/tunnel-shape';
import { DrawableTunnel } from '../../drawing/drawables/drawable-tunnel';
export interface Line {}
export class Tunnel extends GameObject { export class Tunnel extends GameObject {
private primitive: TunnelShape; private shape: DrawableTunnel;
constructor( constructor(
physics: Physics, physics: Physics,
@ -18,12 +17,12 @@ export class Tunnel extends GameObject {
) { ) {
super(); super();
this.primitive = new TunnelShape(this, from, to, fromRadius, toRadius); this.shape = new DrawableTunnel(from, to, fromRadius, toRadius);
physics.addStaticBoundingBox(this.primitive.boundingBox); physics.addStaticBoundingBox(this.shape.boundingBox);
this.addCommandExecutor(RenderCommand, this.draw.bind(this)); this.addCommandExecutor(RenderCommand, this.draw.bind(this));
} }
private draw(c: RenderCommand) { private draw(c: RenderCommand) {
c.renderer.drawPrimitive(this.primitive); c.renderer.drawShape(this.shape);
} }
} }

View file

@ -1,4 +1,4 @@
import { BoundingBoxBase } from './bounding-box-base'; import { BoundingBoxBase } from '../../shapes/bounding-box-base';
export class BoundingBoxList { export class BoundingBoxList {
constructor(private boundingBoxes: Array<BoundingBoxBase> = []) {} constructor(private boundingBoxes: Array<BoundingBoxBase> = []) {}

View file

@ -1,7 +1,7 @@
import { ImmutableBoundingBox } from './immutable-bounding-box';
// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js // source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js
import { ImmutableBoundingBox } from '../../shapes/immutable-bounding-box';
class Node { class Node {
public left?: Node = null; public left?: Node = null;
public right?: Node = null; public right?: Node = null;
@ -63,20 +63,6 @@ export class BoundingBoxTree {
} }
} }
public print() {
this.printRecursive(this.root, 0);
}
private printRecursive(node: Node, tabCount: number) {
if (node === null) {
return;
}
console.log(' '.repeat(tabCount) + '- ' + node.rectangle.value);
this.printRecursive(node.left, tabCount + 2);
this.printRecursive(node.right, tabCount + 2);
}
public findIntersecting( public findIntersecting(
box: ImmutableBoundingBox box: ImmutableBoundingBox
): Array<ImmutableBoundingBox> { ): Array<ImmutableBoundingBox> {

View file

@ -1,14 +0,0 @@
import { BoundingBoxBase } from './bounding-box-base';
import { IPrimitive } from '../../drawing/drawables/primitives/i-primitive';
export class ImmutableBoundingBox extends BoundingBoxBase {
constructor(
value: IPrimitive,
xMin: number = 0,
xMax: number = 0,
yMin: number = 0,
yMax: number = 0
) {
super(value, xMin, xMax, yMin, yMax);
}
}

View file

@ -1,7 +1,7 @@
import { BoundingBoxTree } from './containers/bounding-box-tree'; import { BoundingBoxTree } from './containers/bounding-box-tree';
import { BoundingBoxList } from './containers/bounding-box-list'; import { BoundingBoxList } from './containers/bounding-box-list';
import { ImmutableBoundingBox } from './containers/immutable-bounding-box'; import { ImmutableBoundingBox } from '../shapes/immutable-bounding-box';
import { BoundingBoxBase } from './containers/bounding-box-base'; import { BoundingBoxBase } from '../shapes/bounding-box-base';
export class Physics { export class Physics {
private isTreeInitialized = false; private isTreeInitialized = false;

View file

@ -1,13 +1,13 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { IPrimitive } from '../../drawing/drawables/primitives/i-primitive'; import { IShape } from './i-shape';
export abstract class BoundingBoxBase { export abstract class BoundingBoxBase {
constructor( constructor(
public readonly value: IPrimitive, public readonly shape: IShape,
protected _xMin: number, protected _xMin: number = 0,
protected _xMax: number, protected _xMax: number = 0,
protected _yMin: number, protected _yMin: number = 0,
protected _yMax: number protected _yMax: number = 0
) {} ) {}
public get 0(): number { public get 0(): number {

View file

@ -1,32 +1,33 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { BoundingBoxBase } from './bounding-box-base'; import { BoundingBoxBase } from './bounding-box-base';
import { IPrimitive } from '../../drawing/drawables/primitives/i-primitive'; import { ImmutableBoundingBox } from './immutable-bounding-box';
export class BoundingBox extends BoundingBoxBase { export class BoundingBox extends BoundingBoxBase {
constructor(
value: IPrimitive,
xMin: number = 0,
xMax: number = 0,
yMin: number = 0,
yMax: number = 0
) {
super(value, xMin, xMax, yMin, yMax);
}
public get topLeft(): vec2 { public get topLeft(): vec2 {
return vec2.fromValues(this._xMin, this._yMax); return vec2.fromValues(this._xMin, this._yMax);
} }
public get size(): vec2 {
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
}
public set topLeft(value: vec2) { public set topLeft(value: vec2) {
this._xMin = value.x; this._xMin = value.x;
this._yMax = value.y; this._yMax = value.y;
} }
public get size(): vec2 {
return vec2.fromValues(this._xMax - this._xMin, this._yMax - this._yMin);
}
public set size(value: vec2) { public set size(value: vec2) {
this._xMax = this.xMin + value.x; this._xMax = this.xMin + value.x;
this._yMin = this.yMax - value.y; 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);
}
}