From c995d42935c287cc9024898419f2c2ebab4730c0 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Thu, 15 Feb 2024 16:30:11 -0600 Subject: [PATCH] feat: Debug Draw Static Helpers (#2929) This PR adds new `ex.Debug` static for more convenient debug drawing where you might not have a graphics context accessible to you. This works by batching up all the debug draw requests and flushing them during the debug draw step. * `ex.Debug.drawRay(ray: Ray, options?: { distance?: number, color?: Color })` * `ex.Debug.drawBounds(boundingBox: BoundingBox, options?: { color?: Color })` * `ex.Debug.drawCircle(center: Vector, radius: number, options?: ...)` * `ex.Debug.drawPolygon(points: Vector[], options?: { color?: Color })` * `ex.Debug.drawText(text: string, pos: Vector)` * `ex.Debug.drawLine(start: Vector, end: Vector, options?: LineGraphicsOptions)` * `ex.Debug.drawLines(points: Vector[], options?: LineGraphicsOptions)` * `drawPoint(point: Vector, options?: PointGraphicsOptions)` --- CHANGELOG.md | 9 + sandbox/src/game.ts | 33 +++- sandbox/tests/polygon/index.ts | 2 +- src/engine/Collision/BoundingBox.ts | 33 +++- .../Collision/Colliders/PolygonCollider.ts | 13 +- src/engine/Debug/{Debug.ts => DebugConfig.ts} | 5 +- src/engine/Debug/DebugFlags.ts | 4 - src/engine/Debug/DebugSystem.ts | 15 +- src/engine/Debug/index.ts | 2 +- src/engine/Engine.ts | 6 +- .../Context/ExcaliburGraphicsContext.ts | 4 +- .../Context/line-renderer/line-renderer.ts | 5 + src/engine/Graphics/Debug.ts | 107 ++++++++++++ src/engine/Graphics/DebugGraphicsComponent.ts | 4 +- src/engine/Graphics/index.ts | 3 + src/engine/TileMap/IsometricMap.ts | 4 +- src/engine/TileMap/TileMap.ts | 4 +- src/spec/DebugSpec.ts | 164 ++++++++++++++++++ src/spec/images/DebugSpec/bounds.png | Bin 0 -> 140 bytes src/spec/images/DebugSpec/circle.png | Bin 0 -> 255 bytes src/spec/images/DebugSpec/line.png | Bin 0 -> 149 bytes src/spec/images/DebugSpec/lines.png | Bin 0 -> 165 bytes src/spec/images/DebugSpec/point.png | Bin 0 -> 226 bytes src/spec/images/DebugSpec/polygon.png | Bin 0 -> 141 bytes src/spec/images/DebugSpec/ray.png | Bin 0 -> 130 bytes src/spec/images/DebugSpec/text.png | Bin 0 -> 2094 bytes 26 files changed, 378 insertions(+), 39 deletions(-) rename src/engine/Debug/{Debug.ts => DebugConfig.ts} (99%) create mode 100644 src/engine/Graphics/Debug.ts create mode 100644 src/spec/DebugSpec.ts create mode 100644 src/spec/images/DebugSpec/bounds.png create mode 100644 src/spec/images/DebugSpec/circle.png create mode 100644 src/spec/images/DebugSpec/line.png create mode 100644 src/spec/images/DebugSpec/lines.png create mode 100644 src/spec/images/DebugSpec/point.png create mode 100644 src/spec/images/DebugSpec/polygon.png create mode 100644 src/spec/images/DebugSpec/ray.png create mode 100644 src/spec/images/DebugSpec/text.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd125801..f9beb597f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Added new `ex.Debug` static for more convenient debug drawing where you might not have a graphics context accessible to you. This works by batching up all the debug draw requests and flushing them during the debug draw step. + * `ex.Debug.drawRay(ray: Ray, options?: { distance?: number, color?: Color })` + * `ex.Debug.drawBounds(boundingBox: BoundingBox, options?: { color?: Color })` + * `ex.Debug.drawCircle(center: Vector, radius: number, options?: ...)` + * `ex.Debug.drawPolygon(points: Vector[], options?: { color?: Color })` + * `ex.Debug.drawText(text: string, pos: Vector)` + * `ex.Debug.drawLine(start: Vector, end: Vector, options?: LineGraphicsOptions)` + * `ex.Debug.drawLines(points: Vector[], options?: LineGraphicsOptions)` + * `drawPoint(point: Vector, options?: PointGraphicsOptions)` - Experimental `ex.coroutine` for running code that changes over time, useful for modeling complex animation code. Coroutines return a promise when they are complete. You can think of each `yield` as a frame. * The result of a yield is the current elapsed time * You can yield a number in milliseconds and it will wait that long before resuming diff --git a/sandbox/src/game.ts b/sandbox/src/game.ts index e38165173..74e02a454 100644 --- a/sandbox/src/game.ts +++ b/sandbox/src/game.ts @@ -53,9 +53,17 @@ var game = new ex.Engine({ pointerScope: ex.PointerScope.Canvas, displayMode: ex.DisplayMode.FitScreenAndZoom, snapToPixel: false, + // fixedUpdateFps: 30, + pixelRatio: 2, fixedUpdateFps: 60, maxFps: 60, - antialiasing: false, + antialiasing: { + pixelArtSampler: true, + canvasImageRendering: 'auto', + nativeContextAntialiasing: false, + filtering: ex.ImageFiltering.Pixel, + multiSampleAntialiasing: true, + }, uvPadding: 0, physics: { colliders: { @@ -555,9 +563,9 @@ player.onPostUpdate = (engine) => { }); // console.log(hits); } -player.graphics.onPostDraw = (ctx) => { - ctx.drawLine(ex.Vector.Zero, ex.Vector.Down.scale(100), ex.Color.Red, 2); -} +// player.graphics.onPostDraw = (ctx) => { +// ctx.drawLine(ex.Vector.Zero, ex.Vector.Down.scale(100), ex.Color.Red, 2); +// } player.body.canSleep = false; player.graphics.copyGraphics = false; follower.actions @@ -594,6 +602,23 @@ var healthbar = new ex.Actor({ height: 5, color: new ex.Color(0, 255, 0)}); player.addChild(healthbar); +player.onPostUpdate = () => { + ex.Debug.drawLine( + player.pos, + player.pos.add(ex.Vector.Down.scale(100)), { + color: ex.Color.Red + }); + ex.Debug.drawPoint(player.pos, { + size: 1, + color: ex.Color.Violet + }); + ex.Debug.drawCircle(player.pos, 100, { + color: ex.Color.Transparent, + strokeColor: ex.Color.Black, + width: 1 + }); + ex.Debug.drawBounds(player.collider.bounds, { color: ex.Color.Yellow }); +} // player.onPostDraw = (ctx: CanvasRenderingContext2D) => { // ctx.fillStyle = 'red'; // ctx.fillRect(0, 0, 100, 100); diff --git a/sandbox/tests/polygon/index.ts b/sandbox/tests/polygon/index.ts index 854e3ccdf..496ed701b 100644 --- a/sandbox/tests/polygon/index.ts +++ b/sandbox/tests/polygon/index.ts @@ -91,5 +91,5 @@ var triangulated = shape.triangulate(); actor.collider.set(triangulated); game.add(actor); - +game.currentScene.camera.zoom = .75; game.start(); \ No newline at end of file diff --git a/src/engine/Collision/BoundingBox.ts b/src/engine/Collision/BoundingBox.ts index a673b1787..6e711442a 100644 --- a/src/engine/Collision/BoundingBox.ts +++ b/src/engine/Collision/BoundingBox.ts @@ -213,13 +213,34 @@ export class BoundingBox { return 2 * (wx + wy); } + + // Cache bounding box point returns + private _points: Vector[] = []; + private _left?: number; + private _right?: number; + private _top?: number; + private _bottom?: number; + + /** + * Returns the world space points that make up the corners of the bounding box as a polygon + */ public getPoints(): Vector[] { - const results = []; - results.push(new Vector(this.left, this.top)); - results.push(new Vector(this.right, this.top)); - results.push(new Vector(this.right, this.bottom)); - results.push(new Vector(this.left, this.bottom)); - return results; + if (this._left !== this.left || + this._right !== this.right || + this._top !== this.top || + this._bottom !== this.bottom + ) { + this._points.length = 0; + this._points.push(new Vector(this.left, this.top)); + this._points.push(new Vector(this.right, this.top)); + this._points.push(new Vector(this.right, this.bottom)); + this._points.push(new Vector(this.left, this.bottom)); + this._left = this.left; + this._right = this.right; + this._top = this.top; + this._bottom = this.bottom; + } + return this._points; } /** diff --git a/src/engine/Collision/Colliders/PolygonCollider.ts b/src/engine/Collision/Colliders/PolygonCollider.ts index 09bb4d838..d235f9302 100644 --- a/src/engine/Collision/Colliders/PolygonCollider.ts +++ b/src/engine/Collision/Colliders/PolygonCollider.ts @@ -11,7 +11,7 @@ import { AffineMatrix } from '../../Math/affine-matrix'; import { Ray } from '../../Math/ray'; import { ClosestLineJumpTable } from './ClosestLineJumpTable'; import { Collider } from './Collider'; -import { BodyComponent, ExcaliburGraphicsContext, Logger } from '../..'; +import { BodyComponent, Debug, ExcaliburGraphicsContext, Logger } from '../..'; import { CompositeCollider } from './CompositeCollider'; import { Shape } from './Shape'; import { Transform } from '../../Math/transform'; @@ -46,6 +46,7 @@ export class PolygonCollider extends Collider { public flagDirty() { this._localBoundsDirty = true; this._localSidesDirty = true; + this._transformedPointsDirty = true; this._sidesDirty = true; } @@ -701,13 +702,7 @@ export class PolygonCollider extends Collider { } public debug(ex: ExcaliburGraphicsContext, color: Color, options?: { lineWidth: number, pointSize: number }) { - const firstPoint = this.getTransformedPoints()[0]; - const points = [firstPoint, ...this.getTransformedPoints(), firstPoint]; - const { lineWidth, pointSize } = { ...{ lineWidth: 1, pointSize: 1 }, ...options }; - for (let i = 0; i < points.length - 1; i++) { - ex.drawLine(points[i], points[i + 1], color, lineWidth); - ex.drawCircle(points[i], pointSize, color); - ex.drawCircle(points[i + 1], pointSize, color); - } + const points = this.getTransformedPoints(); + Debug.drawPolygon(points, { color }); } } diff --git a/src/engine/Debug/Debug.ts b/src/engine/Debug/DebugConfig.ts similarity index 99% rename from src/engine/Debug/Debug.ts rename to src/engine/Debug/DebugConfig.ts index eddf3742d..b2bf7ec31 100644 --- a/src/engine/Debug/Debug.ts +++ b/src/engine/Debug/DebugConfig.ts @@ -1,4 +1,4 @@ -import { DebugFlags, ColorBlindFlags } from './DebugFlags'; +import { ColorBlindFlags } from './DebugFlags'; import { Engine } from '../Engine'; import { Color } from '../Color'; import { CollisionContact } from '../Collision/Detection/CollisionContact'; @@ -152,7 +152,7 @@ export interface GraphicsStatistics { * best to do so on the `postupdate` event for [[Engine]], after all values have been * updated during a frame. */ -export class Debug implements DebugFlags { +export class DebugConfig { private _engine: Engine; constructor(engine: Engine) { @@ -309,6 +309,7 @@ export class Debug implements DebugFlags { collisionNormalColor: Color.Cyan, showCollisionContacts: true, + contactSize: 2, collisionContactColor: Color.Red }; diff --git a/src/engine/Debug/DebugFlags.ts b/src/engine/Debug/DebugFlags.ts index c83031bf6..bc4d32952 100644 --- a/src/engine/Debug/DebugFlags.ts +++ b/src/engine/Debug/DebugFlags.ts @@ -3,10 +3,6 @@ import { ColorBlindnessPostProcessor } from '../Graphics/PostProcessor/ColorBlin import { Engine } from '../Engine'; import { ExcaliburGraphicsContextWebGL } from '../Graphics/Context/ExcaliburGraphicsContextWebGL'; -export interface DebugFlags { - colorBlindMode: ColorBlindFlags; -} - export class ColorBlindFlags { private _engine: Engine; private _colorBlindPostProcessor: ColorBlindnessPostProcessor; diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index c87a809f1..2462f595a 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -15,6 +15,7 @@ import { GraphicsComponent } from '../Graphics/GraphicsComponent'; import { Particle } from '../Particles'; import { DebugGraphicsComponent } from '../Graphics/DebugGraphicsComponent'; import { CoordPlane } from '../Math/coord-plane'; +import { Debug } from '../Graphics/Debug'; export class DebugSystem extends System { public readonly systemType = SystemType.Draw; @@ -260,7 +261,10 @@ export class DebugSystem extends System { for (const [_, contact] of this._engine.debug.stats.currFrame.physics.contacts) { if (physicsSettings.showAll || physicsSettings.showCollisionContacts) { for (const point of contact.points) { - this._graphicsContext.debug.drawPoint(point, { size: 5, color: physicsSettings.collisionContactColor }); + this._graphicsContext.debug.drawPoint(point, { + size: physicsSettings.contactSize, + color: physicsSettings.collisionContactColor + }); } } @@ -290,6 +294,15 @@ export class DebugSystem extends System { this._graphicsContext.flush(); } + postupdate(engine: Scene, elapsedMs: number): void { + this._graphicsContext.save(); + if (this._camera) { + this._camera.draw(this._graphicsContext); + } + Debug.flush(this._graphicsContext); + this._graphicsContext.restore(); + } + /** * This applies the current entity transform to the graphics context * @param entity diff --git a/src/engine/Debug/index.ts b/src/engine/Debug/index.ts index 51da2d076..fa4a1ec20 100644 --- a/src/engine/Debug/index.ts +++ b/src/engine/Debug/index.ts @@ -1,3 +1,3 @@ -export * from './Debug'; +export * from './DebugConfig'; export * from './DebugFlags'; export * from './DebugSystem'; diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index fe6ac79c5..c6b6776ae 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -32,7 +32,7 @@ import { Logger, LogLevel } from './Util/Log'; import { Color } from './Color'; import { Scene, SceneConstructor, isSceneConstructor } from './Scene'; import { Entity } from './EntityComponentSystem/Entity'; -import { Debug, DebugStats } from './Debug/Debug'; +import { DebugConfig, DebugStats } from './Debug/DebugConfig'; import { BrowserEvents } from './Util/Browser'; import { AntialiasOptions, @@ -505,7 +505,7 @@ export class Engine implements CanInitialize, * * Graphics * * Colliders */ - public debug: Debug; + public debug: DebugConfig; /** * Access [[stats]] that holds frame statistics. @@ -932,7 +932,7 @@ O|===|* >________________>\n\ }; } - this.debug = new Debug(this); + this.debug = new DebugConfig(this); this.director = new Director(this, options.scenes); diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts index a564e24f3..d49af0620 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts @@ -130,11 +130,11 @@ export interface ExcaliburGraphicsContextState { material: Material; } export interface LineGraphicsOptions { - color: Color; + color?: Color; } export interface RectGraphicsOptions { - color: Color; + color?: Color; } export interface PointGraphicsOptions { diff --git a/src/engine/Graphics/Context/line-renderer/line-renderer.ts b/src/engine/Graphics/Context/line-renderer/line-renderer.ts index 1b8df8bc2..5efd878c9 100644 --- a/src/engine/Graphics/Context/line-renderer/line-renderer.ts +++ b/src/engine/Graphics/Context/line-renderer/line-renderer.ts @@ -7,6 +7,11 @@ import { RendererPlugin } from '../renderer'; import { Shader, VertexBuffer, VertexLayout } from '../..'; import { GraphicsDiagnostics } from '../../GraphicsDiagnostics'; +export interface LineOptions { + color?: Color; + width?: number; +} + export class LineRenderer implements RendererPlugin { public readonly type = 'ex.line'; public priority: number = 0; diff --git a/src/engine/Graphics/Debug.ts b/src/engine/Graphics/Debug.ts new file mode 100644 index 000000000..a76400e35 --- /dev/null +++ b/src/engine/Graphics/Debug.ts @@ -0,0 +1,107 @@ +import { Vector } from '../Math/vector'; +import { ExcaliburGraphicsContext, LineGraphicsOptions, PointGraphicsOptions } from './Context/ExcaliburGraphicsContext'; +import { Color } from '../Color'; +import { Ray } from '../Math/ray'; +import { BoundingBox } from '../excalibur'; + +export class Debug { + static _drawCalls: ((ctx: ExcaliburGraphicsContext) => void)[] = []; + static _ctx: ExcaliburGraphicsContext; + static z: number = Infinity; + static registerGraphicsContext(ctx: ExcaliburGraphicsContext) { + Debug._ctx = ctx; + } + static draw(debugDrawCall: (ctx: ExcaliburGraphicsContext) => void) { + this._drawCalls.push(debugDrawCall); + } + + static drawPoint(point: Vector, options?: PointGraphicsOptions) { + Debug.draw(ctx => { + ctx.debug.drawPoint(point, options); + }); + } + + static drawLine(start: Vector, end: Vector, options?: LineGraphicsOptions) { + Debug.draw(ctx => { + ctx.debug.drawLine(start, end, options); + }); + } + + static drawLines(points: Vector[], options?: LineGraphicsOptions) { + if (points.length > 1) { + Debug.draw(ctx => { + for (let i = 0; i < points.length - 1; i++) { + ctx.debug.drawLine(points[i], points[i + 1], options); + } + }); + } + } + + static drawText(text: string, pos: Vector) { + Debug.draw(ctx => { + ctx.debug.drawText(text, pos); + }); + } + + static drawPolygon(points: Vector[], options?: { color?: Color }) { + if (points.length > 1) { + Debug.draw(ctx => { + const firstPoint = points[0]; + const polygon = [...points, firstPoint]; + for (let i = 0; i < polygon.length - 1; i++) { + ctx.debug.drawLine(polygon[i], polygon[i + 1], options); + } + }); + } + } + + static drawCircle(center: Vector, radius: number, options?: { + color?: Color, + strokeColor?: Color, + width?: number + }) { + const { color, strokeColor, width} = { + color: Color.Black, + strokeColor: undefined, + width: undefined, + ...options + }; + Debug.draw(ctx => { + ctx.drawCircle(center, radius, color, strokeColor, width); + }); + } + + static drawBounds(boundingBox: BoundingBox, options?: { color?: Color }): void { + Debug.draw(ctx => { + ctx.debug.drawRect(boundingBox.left, boundingBox.top, boundingBox.width, boundingBox.height, options); + }); + } + + static drawRay(ray: Ray, options?: { distance?: number, color?: Color }) { + const { distance, color } = { + color: Color.Blue, + distance: 10, + ...options + }; + Debug.draw((ctx) => { + const start = ray.pos; + const end = ray.pos.add(ray.dir.scale(distance)); + + ctx.debug.drawLine(start, end, { color }); + }); + } + + static flush(ctx: ExcaliburGraphicsContext) { + ctx.save(); + ctx.z = Debug.z; + for (const drawCall of Debug._drawCalls) { + drawCall(ctx); + } + ctx.restore(); + Debug.clear(); + } + + static clear() { + Debug._drawCalls.length = 0; + } +} \ No newline at end of file diff --git a/src/engine/Graphics/DebugGraphicsComponent.ts b/src/engine/Graphics/DebugGraphicsComponent.ts index 247081651..871d6b933 100644 --- a/src/engine/Graphics/DebugGraphicsComponent.ts +++ b/src/engine/Graphics/DebugGraphicsComponent.ts @@ -1,5 +1,5 @@ import { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext'; -import { Debug } from '../Debug'; +import { DebugConfig } from '../Debug'; import { Component } from '../EntityComponentSystem/Component'; @@ -11,7 +11,7 @@ import { Component } from '../EntityComponentSystem/Component'; */ export class DebugGraphicsComponent extends Component { constructor( - public draw: (ctx: ExcaliburGraphicsContext, debugFlags: Debug) => void, + public draw: (ctx: ExcaliburGraphicsContext, debugFlags: DebugConfig) => void, public useTransform = true) { super(); } diff --git a/src/engine/Graphics/index.ts b/src/engine/Graphics/index.ts index 3f36fb480..2a23e1fa0 100644 --- a/src/engine/Graphics/index.ts +++ b/src/engine/Graphics/index.ts @@ -49,6 +49,9 @@ export * from './Context/vertex-layout'; export * from './Context/quad-index-buffer'; export * from './Context/material'; +// Debug +export * from './Debug'; + // Util import * as webgl from './Context/webgl-util'; diff --git a/src/engine/TileMap/IsometricMap.ts b/src/engine/TileMap/IsometricMap.ts index 1a1982346..29ae28b66 100644 --- a/src/engine/TileMap/IsometricMap.ts +++ b/src/engine/TileMap/IsometricMap.ts @@ -9,7 +9,7 @@ import { TransformComponent } from '../EntityComponentSystem/Components/Transfor import { Entity } from '../EntityComponentSystem/Entity'; import { DebugGraphicsComponent, ExcaliburGraphicsContext, Graphic, GraphicsComponent } from '../Graphics'; import { IsometricEntityComponent } from './IsometricEntityComponent'; -import { Debug } from '../Debug'; +import { DebugConfig } from '../Debug'; export class IsometricTile extends Entity { /** * Indicates whether this tile is solid @@ -445,7 +445,7 @@ export class IsometricMap extends Entity { * Debug draw for IsometricMap, called internally by excalibur when debug mode is toggled on * @param gfx */ - public debug(gfx: ExcaliburGraphicsContext, debugFlags: Debug) { + public debug(gfx: ExcaliburGraphicsContext, debugFlags: DebugConfig) { const { showAll, showPosition, diff --git a/src/engine/TileMap/TileMap.ts b/src/engine/TileMap/TileMap.ts index fad270e6a..3852b5760 100644 --- a/src/engine/TileMap/TileMap.ts +++ b/src/engine/TileMap/TileMap.ts @@ -17,7 +17,7 @@ import { PostDrawEvent, PostUpdateEvent, PreDrawEvent, PreUpdateEvent } from '.. import { EventEmitter, EventKey, Handler, Subscription } from '../EventEmitter'; import { CoordPlane } from '../Math/coord-plane'; import { QuadTree } from '../Collision/Detection/QuadTree'; -import { Debug } from '../Debug'; +import { DebugConfig } from '../Debug'; export interface TileMapOptions { /** @@ -539,7 +539,7 @@ export class TileMap extends Entity { this.emit('postdraw', new PostDrawEvent(ctx as any, delta, this)); } - public debug(gfx: ExcaliburGraphicsContext, debugFlags: Debug) { + public debug(gfx: ExcaliburGraphicsContext, debugFlags: DebugConfig) { const { showAll, showGrid, diff --git a/src/spec/DebugSpec.ts b/src/spec/DebugSpec.ts new file mode 100644 index 000000000..1d2ca5fa1 --- /dev/null +++ b/src/spec/DebugSpec.ts @@ -0,0 +1,164 @@ +import * as ex from '@excalibur'; +import { ExcaliburAsyncMatchers } from 'excalibur-jasmine'; + +describe('Debug draw static', () => { + beforeAll(() => { + jasmine.addAsyncMatchers(ExcaliburAsyncMatchers); + }); + it('exists', () => { + expect(ex.Debug).toBeDefined(); + }); + + it('can draw a point', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawPoint(ex.vec(5, 5), {size: 5, color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/point.png'); + }); + + it('can draw a line', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawLine(ex.vec(0, 0), ex.vec(10, 10), {color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/line.png'); + }); + + it('can draw lines', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawLines([ex.vec(0, 0), ex.vec(10, 10), ex.vec(10, 0), ex.vec(0, 10)], {color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/lines.png'); + }); + + xit('can draw text', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 100; + canvas.height = 100; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawText('some text', ex.vec(0, 50)); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/text.png'); + }); + + it('can draw a polygon', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawPolygon([ex.vec(2, 2), ex.vec(8, 2), ex.vec(8, 8), ex.vec(2, 8)], {color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/polygon.png'); + }); + + it('can draw a circle', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawCircle(ex.vec(5, 5), 5, {color: ex.Color.Transparent, strokeColor: ex.Color.ExcaliburBlue, width: 1}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/circle.png'); + }); + + it('can draw bounds', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + const bounds = new ex.BoundingBox({ + left: 3, + right: 7, + top: 3, + bottom: 7 + }); + + ex.Debug.drawBounds(bounds, { color: ex.Color.ExcaliburBlue }); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/bounds.png'); + }); + + it('can draw a ray', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + const ray = new ex.Ray(ex.vec(0, 0), ex.vec(1, 1)); + + ex.Debug.drawRay(ray, { distance: 4, color: ex.Color.ExcaliburBlue }); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/ray.png'); + }); +}); \ No newline at end of file diff --git a/src/spec/images/DebugSpec/bounds.png b/src/spec/images/DebugSpec/bounds.png new file mode 100644 index 0000000000000000000000000000000000000000..fef36347245ae8f817e5875c2243ed8cd98a533d GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>ar1O>4ABTq zPDx1kao&L?LB(6KKT@OXX1w3pw$BgMnnX5wW*h&hsuI=VOg=2KSc65g!SjI6av#OO lu;w+Yr;J;gCcP+NVK~1|kC7$T_BhZ)22WQ%mvv4FO#oSrD?b1L literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSpec/circle.png b/src/spec/images/DebugSpec/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..898e9c96b07b8b6416645481d5c6b5d2dbd63d96 GIT binary patch literal 255 zcmVPx#xk*GpR47wpWME+U&&a~Yz|1Gcz$T-|z@cu-00KX59%lIUpH)(mfyX?YfmKwI;rE*dV1=wA3Jkwr-)H!8awWs>=hqn+S%hT%^EqWPFfcMQ zd^x(9;n%ZkV1;Z_S`0kau?!6V|1*5rH;sXjOV8^+uXQZL`;EtF#49A)YN9f4F%}28J29*~C-V}>@%MCb4ABTq z7C9gH_5a^Cx3+ly^+pevdvY$_`p?SzN|Hk}EhQo0$9c82XB|8YBNR_~Idwhc;?#5t v?C>{ibBgKOsC8tS#B!&SqZOh`+H4GA=601T8-51@jb`w4^>bP0l+XkKs=Y8@ literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSpec/lines.png b/src/spec/images/DebugSpec/lines.png new file mode 100644 index 0000000000000000000000000000000000000000..addc4cad027ab5ea62437b4b7ac6c9a2f163bf07 GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>iT8AI4ABTq z7C9gH_5a^Cx3+ly^+pNIIhStnG%anSJM!doC3e->?>=_m#DN1JB9MClR4k)4+L(Ruvt*1q|1?;#bP>Z+#E6iow&> K&t;ucLK6Up>o{Kk literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSpec/point.png b/src/spec/images/DebugSpec/point.png new file mode 100644 index 0000000000000000000000000000000000000000..758ca7cfb561e35d4b6e881ded4a00d916c6df29 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>nd9l=7@`rJ zoRX06hSyL=92&R)E+r-LP_WQeCMmP z8lA6pHO>s5(%3kWbIpW@|LxoM&yP3$v!k}>;qkYNd3xqpt^2=|_pii^pGOqAE*!Y; zuP?c8-hH6{_tXB*cRnhVD75R;|NjzocP!5x&=4~JYJbDahT#dzBT=DT(~I*IttvWr a3mBM9BfctJWIhFSCxfS}pUXO@geCyvkzy7A literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSpec/polygon.png b/src/spec/images/DebugSpec/polygon.png new file mode 100644 index 0000000000000000000000000000000000000000..8ab60da9641233b6ace38c6527b96a9d82fc3a14 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>arbm_4ABTq zPDx1kao&L?p+)nFA@|W$e@?HwnozjGX@ZhjW^P-&|N2E5ZJmuGheex04wM`YIh-S@ n!`1nT=fp=BR(`>UXBik2SgaV=W}J-$8p+`4>gTe~DWM4f8v`w( literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSpec/ray.png b/src/spec/images/DebugSpec/ray.png new file mode 100644 index 0000000000000000000000000000000000000000..4a523f3046349847f96d41f26a8b4c5e7245b139 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>vGH_q4ABTq z7C9gH_5a^Cx3+ly^+pevdvY$_`p?SzN|Hk}EhQo0$9c82XBmS26lF>HIUnTuc#CtY b%uFsu(-28UiI%zspg9blu6{1-oD!MdZ2q{Wwqe426T)aliAolFCM~os$tcnt9Css+d6g6Ags<)-NrEaNF zq(x~$TeWAEC_=8?7}xzP-t+$OdA{d-&v~Bn{P6wZn|ayZ0xTpW1ONcQR+gq$kJayA zfc(cZeC~k*03fhsWoqmYgIz5l;vF0%(i1lVmRD>na+|AwN}JjokXJk2S&bPH+9P>ItsIVtLy$POGWL9Jl!Y4EZWHM0FfoL>pa0ToEG^6 z*t-aaoqG#L-YMYAd&C)gDyT@mVV&Xg`BCJi8=+z>X(i#EkM`BpHp~iq zN>aO#k|aBoYU`+Q$?t$;^j52!k8#0SiZl`}>~plyoZH{z4^QcPrXW6=!`#$SZ4o?t zdCrlmYqaciwZB~FonMclh(W1hbtA$h+p z$ds_X>G9aC#B;NaXel}>H13uD=yhG&U5FFusP>%@VB|*SUZy)BSdxC$-Z*@ThaC5( z;>Mu*rp3r#V3XY%x1vmB8Srp;jl5hkczfmBx?NyNLq_6uTcWuBa@|GcCmM}}YbbZo z(<(%^5+R$YSEzEG2QTe%RD|Pkq047=a#$kAh#nvVsS4Xj-WAF=TmA#bQbg)?65t-f48ei*S%sO zzu_8Xx4k)}#Q}r=CSUT^8r%#AA)r{Q2)*L`H8UM@Awnx=oOYa_=^m6Wqgky*J@-xL znC!2pn|ms-4?n2#@oSS=Sz&bU*ILYui?Ng`Vb1nrDoU1aJRg}wVo4Q^4wzY z@EvmR#A@P^oW=8*MyqVLN>%V?xBk=)D4HDjVca?u%_(yG*bB`hduGJ)B z(0yC9n{i=%qv?%7KVzU7McJ)otp8AoW7l}(>tVkP8FF`GilwGHreAKTkoO3qMiL=A z-`Jz`69oR0S5w37)=WivBNt2dSa-tIz??DFAE8IBp-zy>i=6|5=f?kC7j~Cf5vXV7 z4J){h3RhKv3i3hU6Zt##HZqU@60Yr;y zwLyX>%+FoOCH2fAO35T()qQbG%zU^c9fKiV=@o>-Zfb*YFj(jFdEWaFol