• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 14804036802

02 May 2025 09:58PM UTC coverage: 5.927% (-83.4%) from 89.28%
14804036802

Pull #3404

github

web-flow
Merge 5c103d7f8 into 0f2ccaeb2
Pull Request #3404: feat: added Graph module to Math

234 of 8383 branches covered (2.79%)

229 of 246 new or added lines in 1 file covered. (93.09%)

13145 existing lines in 208 files now uncovered.

934 of 15759 relevant lines covered (5.93%)

4.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.57
/src/engine/Debug/DebugSystem.ts
1
import { Engine } from '../Engine';
2
import { Scene } from '../Scene';
3
import { Camera } from '../Camera';
4
import { MotionComponent } from '../EntityComponentSystem/Components/MotionComponent';
5
import { ColliderComponent } from '../Collision/ColliderComponent';
6
import { Entity, Query, SystemPriority, TransformComponent, World } from '../EntityComponentSystem';
7
import { System, SystemType } from '../EntityComponentSystem/System';
8
import { ExcaliburGraphicsContext } from '../Graphics/Context/ExcaliburGraphicsContext';
9
import { vec, Vector } from '../Math/vector';
10
import { toDegrees } from '../Math/util';
11
import { BodyComponent } from '../Collision/BodyComponent';
12
import { CollisionSystem } from '../Collision/CollisionSystem';
13
import { CompositeCollider } from '../Collision/Colliders/CompositeCollider';
14
import { GraphicsComponent } from '../Graphics/GraphicsComponent';
15
import { Particle } from '../Particles/Particles';
16
import { DebugGraphicsComponent } from '../Graphics/DebugGraphicsComponent';
17
import { CoordPlane } from '../Math/coord-plane';
18
import { Debug } from '../Graphics/Debug';
19

20
export class DebugSystem extends System {
21
  static priority = SystemPriority.Lowest;
1✔
22

UNCOV
23
  public readonly systemType = SystemType.Draw;
×
24
  private _graphicsContext: ExcaliburGraphicsContext;
25
  private _collisionSystem: CollisionSystem;
26
  private _camera: Camera;
27
  private _engine: Engine;
28
  query: Query<typeof TransformComponent>;
29

UNCOV
30
  constructor(public world: World) {
×
UNCOV
31
    super();
×
UNCOV
32
    this.query = this.world.query([TransformComponent]);
×
33
  }
34

35
  public initialize(world: World, scene: Scene): void {
UNCOV
36
    this._graphicsContext = scene.engine.graphicsContext;
×
UNCOV
37
    this._camera = scene.camera;
×
UNCOV
38
    this._engine = scene.engine;
×
UNCOV
39
    this._collisionSystem = world.systemManager.get(CollisionSystem);
×
40
  }
41

42
  update(): void {
UNCOV
43
    if (!this._engine.isDebug) {
×
UNCOV
44
      return;
×
45
    }
46

UNCOV
47
    const filterSettings = this._engine.debug.filter;
×
48

49
    let id: number;
50
    let name: string;
UNCOV
51
    const entitySettings = this._engine.debug.entity;
×
52

53
    let tx: TransformComponent;
UNCOV
54
    const txSettings = this._engine.debug.transform;
×
55

56
    let motion: MotionComponent;
UNCOV
57
    const motionSettings = this._engine.debug.motion;
×
58

59
    let colliderComp: ColliderComponent;
UNCOV
60
    const colliderSettings = this._engine.debug.collider;
×
61

UNCOV
62
    const physicsSettings = this._engine.debug.physics;
×
63

64
    let graphics: GraphicsComponent;
UNCOV
65
    const graphicsSettings = this._engine.debug.graphics;
×
66

67
    let debugDraw: DebugGraphicsComponent;
68

69
    let body: BodyComponent;
UNCOV
70
    const bodySettings = this._engine.debug.body;
×
71

UNCOV
72
    const cameraSettings = this._engine.debug.camera;
×
UNCOV
73
    for (let i = 0; i < this.query.entities.length; i++) {
×
UNCOV
74
      const entity = this.query.entities[i];
×
UNCOV
75
      if (entity.hasTag('offscreen')) {
×
76
        // skip offscreen entities
77
        continue;
×
78
      }
UNCOV
79
      if (entity instanceof Particle) {
×
80
        // Particles crush the renderer :(
81
        continue;
×
82
      }
UNCOV
83
      if (filterSettings.useFilter) {
×
84
        const allIds = filterSettings.ids.length === 0;
×
85
        const idMatch = allIds || filterSettings.ids.includes(entity.id);
×
86
        if (!idMatch) {
×
87
          continue;
×
88
        }
89
        const allNames = filterSettings.nameQuery === '';
×
90
        const nameMatch = allNames || entity.name.includes(filterSettings.nameQuery);
×
91
        if (!nameMatch) {
×
92
          continue;
×
93
        }
94
      }
95

UNCOV
96
      let cursor = Vector.Zero;
×
UNCOV
97
      const lineHeight = vec(0, 16);
×
UNCOV
98
      id = entity.id;
×
UNCOV
99
      name = entity.name;
×
UNCOV
100
      tx = entity.get(TransformComponent);
×
101

102
      // This optionally sets our camera based on the entity coord plan (world vs. screen)
UNCOV
103
      this._pushCameraTransform(tx);
×
104

UNCOV
105
      this._graphicsContext.save();
×
UNCOV
106
      if (tx.coordPlane === CoordPlane.Screen) {
×
107
        this._graphicsContext.translate(this._engine.screen.contentArea.left, this._engine.screen.contentArea.top);
×
108
      }
UNCOV
109
      this._graphicsContext.z = txSettings.debugZIndex;
×
110

UNCOV
111
      this._applyTransform(entity);
×
UNCOV
112
      if (tx) {
×
UNCOV
113
        if (txSettings.showAll || txSettings.showPosition) {
×
UNCOV
114
          this._graphicsContext.debug.drawPoint(Vector.Zero, { size: 4, color: txSettings.positionColor });
×
115
        }
UNCOV
116
        if (txSettings.showAll || txSettings.showPositionLabel) {
×
UNCOV
117
          this._graphicsContext.debug.drawText(`pos${tx.pos.toString(2)}`, cursor);
×
UNCOV
118
          cursor = cursor.add(lineHeight);
×
119
        }
UNCOV
120
        if (txSettings.showAll || txSettings.showZIndex) {
×
UNCOV
121
          this._graphicsContext.debug.drawText(`z(${tx.z.toFixed(1)})`, cursor);
×
UNCOV
122
          cursor = cursor.add(lineHeight);
×
123
        }
124

UNCOV
125
        if (entitySettings.showAll || entitySettings.showId) {
×
UNCOV
126
          this._graphicsContext.debug.drawText(`id(${id}) ${entity.parent ? 'child of id(' + entity.parent?.id + ')' : ''}`, cursor);
×
UNCOV
127
          cursor = cursor.add(lineHeight);
×
128
        }
129

UNCOV
130
        if (entitySettings.showAll || entitySettings.showName) {
×
UNCOV
131
          this._graphicsContext.debug.drawText(`name(${name})`, cursor);
×
UNCOV
132
          cursor = cursor.add(lineHeight);
×
133
        }
134

UNCOV
135
        if (txSettings.showAll || txSettings.showRotation) {
×
UNCOV
136
          this._graphicsContext.drawLine(
×
137
            Vector.Zero,
138
            Vector.fromAngle(tx.rotation).scale(50).add(Vector.Zero),
139
            txSettings.rotationColor,
140
            2
141
          );
UNCOV
142
          this._graphicsContext.debug.drawText(`rot deg(${toDegrees(tx.rotation).toFixed(2)})`, cursor);
×
UNCOV
143
          cursor = cursor.add(lineHeight);
×
144
        }
145

UNCOV
146
        if (txSettings.showAll || txSettings.showScale) {
×
UNCOV
147
          this._graphicsContext.drawLine(Vector.Zero, tx.scale.add(Vector.Zero), txSettings.scaleColor, 2);
×
148
        }
149
      }
150

UNCOV
151
      graphics = entity.get(GraphicsComponent);
×
UNCOV
152
      if (graphics) {
×
UNCOV
153
        if (graphicsSettings.showAll || graphicsSettings.showBounds) {
×
UNCOV
154
          const bounds = graphics.localBounds;
×
UNCOV
155
          bounds.draw(this._graphicsContext, graphicsSettings.boundsColor);
×
156
        }
157
      }
158

UNCOV
159
      debugDraw = entity.get(DebugGraphicsComponent);
×
UNCOV
160
      if (debugDraw) {
×
UNCOV
161
        if (!debugDraw.useTransform) {
×
UNCOV
162
          this._graphicsContext.restore();
×
163
        }
UNCOV
164
        debugDraw.draw(this._graphicsContext, this._engine.debug);
×
UNCOV
165
        if (!debugDraw.useTransform) {
×
UNCOV
166
          this._graphicsContext.save();
×
UNCOV
167
          this._applyTransform(entity);
×
168
        }
169
      }
170

UNCOV
171
      body = entity.get(BodyComponent);
×
UNCOV
172
      if (body) {
×
UNCOV
173
        if (bodySettings.showAll || bodySettings.showCollisionGroup) {
×
UNCOV
174
          this._graphicsContext.debug.drawText(`collision group(${body.group.name})`, cursor);
×
UNCOV
175
          cursor = cursor.add(lineHeight);
×
176
        }
177

UNCOV
178
        if (bodySettings.showAll || bodySettings.showCollisionType) {
×
UNCOV
179
          this._graphicsContext.debug.drawText(`collision type(${body.collisionType})`, cursor);
×
UNCOV
180
          cursor = cursor.add(lineHeight);
×
181
        }
182

UNCOV
183
        if (bodySettings.showAll || bodySettings.showMass) {
×
UNCOV
184
          this._graphicsContext.debug.drawText(`mass(${body.mass})`, cursor);
×
UNCOV
185
          cursor = cursor.add(lineHeight);
×
186
        }
187

UNCOV
188
        if (bodySettings.showAll || bodySettings.showMotion) {
×
UNCOV
189
          this._graphicsContext.debug.drawText(`motion(${body.sleepMotion})`, cursor);
×
UNCOV
190
          cursor = cursor.add(lineHeight);
×
191
        }
192

UNCOV
193
        if (bodySettings.showAll || bodySettings.showSleeping) {
×
UNCOV
194
          this._graphicsContext.debug.drawText(`sleeping(${body.canSleep ? body.isSleeping : 'cant sleep'})`, cursor);
×
UNCOV
195
          cursor = cursor.add(lineHeight);
×
196
        }
197
      }
198

UNCOV
199
      this._graphicsContext.restore();
×
200

201
      // World space
UNCOV
202
      this._graphicsContext.save();
×
UNCOV
203
      if (tx.coordPlane === CoordPlane.Screen) {
×
204
        this._graphicsContext.translate(this._engine.screen.contentArea.left, this._engine.screen.contentArea.top);
×
205
      }
UNCOV
206
      this._graphicsContext.z = txSettings.debugZIndex;
×
UNCOV
207
      motion = entity.get(MotionComponent);
×
UNCOV
208
      if (motion) {
×
UNCOV
209
        if (motionSettings.showAll || motionSettings.showVelocity) {
×
UNCOV
210
          this._graphicsContext.debug.drawText(`vel${motion.vel.toString(2)}`, cursor.add(tx.globalPos));
×
UNCOV
211
          this._graphicsContext.drawLine(tx.globalPos, tx.globalPos.add(motion.vel), motionSettings.velocityColor, 2);
×
UNCOV
212
          cursor = cursor.add(lineHeight);
×
213
        }
214

UNCOV
215
        if (motionSettings.showAll || motionSettings.showAcceleration) {
×
UNCOV
216
          this._graphicsContext.drawLine(tx.globalPos, tx.globalPos.add(motion.acc), motionSettings.accelerationColor, 2);
×
217
        }
218
      }
219

220
      // Colliders live in world space already so after the restore()
UNCOV
221
      colliderComp = entity.get(ColliderComponent);
×
UNCOV
222
      if (colliderComp) {
×
UNCOV
223
        const collider = colliderComp.get();
×
UNCOV
224
        if ((colliderSettings.showAll || colliderSettings.showGeometry) && collider) {
×
UNCOV
225
          collider.debug(this._graphicsContext, colliderSettings.geometryColor, {
×
226
            lineWidth: colliderSettings.geometryLineWidth,
227
            pointSize: colliderSettings.geometryPointSize
228
          });
229
        }
UNCOV
230
        if (colliderSettings.showAll || colliderSettings.showBounds) {
×
UNCOV
231
          if (collider instanceof CompositeCollider) {
×
UNCOV
232
            const colliders = collider.getColliders();
×
UNCOV
233
            for (const collider of colliders) {
×
UNCOV
234
              const bounds = collider.bounds;
×
UNCOV
235
              const pos = vec(bounds.left, bounds.top);
×
UNCOV
236
              this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor });
×
UNCOV
237
              if (colliderSettings.showAll || colliderSettings.showOwner) {
×
UNCOV
238
                this._graphicsContext.debug.drawText(`owner id(${collider.owner.id})`, pos);
×
239
              }
240
            }
UNCOV
241
            colliderComp.bounds.draw(this._graphicsContext, colliderSettings.boundsColor);
×
UNCOV
242
          } else if (collider) {
×
UNCOV
243
            const bounds = colliderComp.bounds;
×
UNCOV
244
            const pos = vec(bounds.left, bounds.top);
×
UNCOV
245
            this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor });
×
UNCOV
246
            if (colliderSettings.showAll || colliderSettings.showOwner) {
×
UNCOV
247
              this._graphicsContext.debug.drawText(`owner id(${colliderComp.owner.id})`, pos);
×
248
            }
249
          }
250
        }
251
      }
252

UNCOV
253
      this._graphicsContext.restore();
×
UNCOV
254
      this._popCameraTransform(tx);
×
255
    }
256

UNCOV
257
    this._graphicsContext.save();
×
UNCOV
258
    this._camera.draw(this._graphicsContext);
×
UNCOV
259
    if (physicsSettings.showAll || physicsSettings.showBroadphaseSpacePartitionDebug) {
×
260
      this._collisionSystem.debug(this._graphicsContext);
×
261
    }
UNCOV
262
    if (physicsSettings.showAll || physicsSettings.showCollisionContacts || physicsSettings.showCollisionNormals) {
×
UNCOV
263
      for (const [_, contact] of this._engine.debug.stats.currFrame.physics.contacts) {
×
264
        if (physicsSettings.showAll || physicsSettings.showCollisionContacts) {
×
265
          for (const point of contact.points) {
×
266
            this._graphicsContext.debug.drawPoint(point, {
×
267
              size: physicsSettings.contactSize,
268
              color: physicsSettings.collisionContactColor
269
            });
270
          }
271
        }
272

273
        if (physicsSettings.showAll || physicsSettings.showCollisionNormals) {
×
274
          for (const point of contact.points) {
×
275
            this._graphicsContext.debug.drawLine(point, contact.normal.scale(30).add(point), {
×
276
              color: physicsSettings.collisionNormalColor
277
            });
278
          }
279
        }
280
      }
281
    }
UNCOV
282
    this._graphicsContext.restore();
×
283

UNCOV
284
    if (cameraSettings) {
×
UNCOV
285
      this._graphicsContext.save();
×
UNCOV
286
      this._camera.draw(this._graphicsContext);
×
UNCOV
287
      if (cameraSettings.showAll || cameraSettings.showFocus) {
×
288
        this._graphicsContext.drawCircle(this._camera.pos, 4, cameraSettings.focusColor);
×
289
      }
UNCOV
290
      if (cameraSettings.showAll || cameraSettings.showZoom) {
×
291
        this._graphicsContext.debug.drawText(`zoom(${this._camera.zoom})`, this._camera.pos);
×
292
      }
UNCOV
293
      this._graphicsContext.restore();
×
294
    }
295

UNCOV
296
    this._graphicsContext.flush();
×
297
  }
298

299
  postupdate(engine: Scene<unknown>, elapsed: number): void {
UNCOV
300
    if (this._engine.isDebug) {
×
UNCOV
301
      this._graphicsContext.save();
×
UNCOV
302
      if (this._camera) {
×
UNCOV
303
        this._camera.draw(this._graphicsContext);
×
304
      }
UNCOV
305
      Debug.flush(this._graphicsContext);
×
UNCOV
306
      this._graphicsContext.restore();
×
307
    }
308
  }
309

310
  /**
311
   * This applies the current entity transform to the graphics context
312
   * @param entity
313
   */
314
  private _applyTransform(entity: Entity): void {
UNCOV
315
    const ancestors = entity.getAncestors();
×
UNCOV
316
    for (const ancestor of ancestors) {
×
UNCOV
317
      const transform = ancestor?.get(TransformComponent);
×
UNCOV
318
      if (transform) {
×
UNCOV
319
        this._graphicsContext.translate(transform.pos.x, transform.pos.y);
×
UNCOV
320
        this._graphicsContext.scale(transform.scale.x, transform.scale.y);
×
UNCOV
321
        this._graphicsContext.rotate(transform.rotation);
×
322
      }
323
    }
324
  }
325

326
  /**
327
   * Applies the current camera transform if in world coordinates
328
   * @param transform
329
   */
330
  private _pushCameraTransform(transform: TransformComponent) {
331
    // Establish camera offset per entity
UNCOV
332
    if (transform.coordPlane === CoordPlane.World) {
×
UNCOV
333
      this._graphicsContext.save();
×
UNCOV
334
      if (this._camera) {
×
UNCOV
335
        this._camera.draw(this._graphicsContext);
×
336
      }
337
    }
338
  }
339

340
  /**
341
   * Resets the current camera transform if in world coordinates
342
   * @param transform
343
   */
344
  private _popCameraTransform(transform: TransformComponent) {
UNCOV
345
    if (transform.coordPlane === CoordPlane.World) {
×
346
      // Apply camera world offset
UNCOV
347
      this._graphicsContext.restore();
×
348
    }
349
  }
350
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc