• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

excaliburjs / Excalibur / 15354777440

30 May 2025 08:03PM UTC coverage: 87.858% (-1.5%) from 89.344%
15354777440

Pull #3385

github

web-flow
Merge a00f57733 into e6ec66358
Pull Request #3385: updated Meet action to add tolerance

5002 of 6948 branches covered (71.99%)

3 of 5 new or added lines in 2 files covered. (60.0%)

872 existing lines in 83 files now uncovered.

13661 of 15549 relevant lines covered (87.86%)

25187.01 hits per line

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

86.54
/src/engine/Graphics/GraphicsGroup.ts
1
import { Vector } from '../Math/vector';
2
import type { GraphicOptions } from './Graphic';
3
import { Graphic } from './Graphic';
4
import type { HasTick } from './Animation';
5
import { Animation } from './Animation';
6
import type { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext';
7
import { BoundingBox } from '../Collision/Index';
8
import { Logger } from '../Util/Log';
9

10
export interface GraphicsGroupingOptions {
11
  members: (GraphicsGrouping | Graphic)[];
12
  /**
13
   * Default true, GraphicsGroup will use the anchor to position all the graphics based on their combined bounds
14
   *
15
   * Setting to false will ignore anchoring from parent components and position the top left of all graphics at the actor's position,
16
   * positioning graphics in the group is done with the `offset` property.
17
   */
18
  useAnchor?: boolean;
19
}
20

21
export interface GraphicsGrouping {
22
  offset: Vector;
23
  graphic: Graphic;
24
  /**
25
   * Optionally disable this graphics bounds as part of group calculation, default true
26
   * if unspecified
27
   *
28
   * You may want disable this if you're using text because their bounds will affect
29
   * the centering of the whole group.
30
   *
31
   * **WARNING** having inaccurate bounds can cause offscreen culling issues.
32
   */
33
  useBounds?: boolean;
34
}
35

36
export class GraphicsGroup extends Graphic implements HasTick {
37
  private _logger = Logger.getInstance();
6✔
38
  public useAnchor: boolean = true;
6✔
39
  public members: (GraphicsGrouping | Graphic)[] = [];
6✔
40

41
  constructor(options: GraphicsGroupingOptions & GraphicOptions) {
42
    super(options);
6✔
43
    this.members = options.members;
6✔
44
    this.useAnchor = options.useAnchor ?? this.useAnchor;
6✔
45
    this._updateDimensions();
6✔
46
  }
47

48
  public clone(): GraphicsGroup {
49
    return new GraphicsGroup({
1✔
50
      members: [...this.members],
51
      ...this.cloneGraphicOptions()
52
    });
53
  }
54

55
  private _updateDimensions(): BoundingBox {
56
    const bb = this.localBounds;
8✔
57
    this.width = bb.width;
8✔
58
    this.height = bb.height;
8✔
59
    return bb;
8✔
60
  }
61

62
  public get localBounds(): BoundingBox {
63
    const bb = new BoundingBox();
12✔
64
    for (const member of this.members) {
12✔
65
      if (member instanceof Graphic) {
20!
UNCOV
66
        member.localBounds.combine(bb, bb);
×
67
      } else {
68
        const { graphic, offset: pos, useBounds } = member;
20✔
69
        const shouldUseBounds = useBounds === undefined ? true : useBounds;
20!
70
        if (graphic) {
20!
71
          if (shouldUseBounds) {
20!
72
            graphic.localBounds.translate(pos).combine(bb, bb);
20✔
73
          }
74
        } else {
UNCOV
75
          this._logger.warnOnce(`Graphics group member has an null or undefined graphic, member definition: ${JSON.stringify(member)}.`);
×
76
        }
77
      }
78
    }
79
    return bb;
12✔
80
  }
81

82
  private _isAnimationOrGroup(graphic: Graphic): graphic is Animation | GraphicsGroup {
83
    return graphic instanceof Animation || graphic instanceof GraphicsGroup;
2!
84
  }
85

86
  public tick(elapsed: number, idempotencyToken?: number) {
87
    for (const member of this.members) {
1✔
88
      let graphic: Graphic;
89
      if (member instanceof Graphic) {
1!
UNCOV
90
        graphic = member;
×
91
      } else {
92
        graphic = member.graphic;
1✔
93
      }
94
      if (this._isAnimationOrGroup(graphic)) {
1!
95
        graphic.tick(elapsed, idempotencyToken);
1✔
96
      }
97
    }
98
  }
99

100
  public reset() {
101
    for (const member of this.members) {
1✔
102
      let graphic: Graphic;
103
      if (member instanceof Graphic) {
1!
UNCOV
104
        graphic = member;
×
105
      } else {
106
        graphic = member.graphic;
1✔
107
      }
108
      if (this._isAnimationOrGroup(graphic)) {
1!
109
        graphic.reset();
1✔
110
      }
111
    }
112
  }
113

114
  protected _preDraw(ex: ExcaliburGraphicsContext, x: number, y: number) {
115
    this._updateDimensions();
2✔
116
    super._preDraw(ex, this.useAnchor ? x : 0, this.useAnchor ? y : 0);
2✔
117
  }
118

119
  protected _drawImage(ex: ExcaliburGraphicsContext, x: number, y: number) {
120
    const pos = Vector.Zero;
2✔
121
    for (const member of this.members) {
2✔
122
      let graphic: Graphic;
123
      if (member instanceof Graphic) {
4!
UNCOV
124
        graphic = member;
×
125
      } else {
126
        graphic = member.graphic;
4✔
127
        member.offset.clone(pos);
4✔
128
      }
129
      if (!graphic) {
4!
UNCOV
130
        continue;
×
131
      }
132
      ex.save();
4✔
133
      ex.translate(x, y);
4✔
134
      graphic.draw(ex, pos.x, pos.y);
4✔
135
      if (this.showDebug) {
4!
136
        /* istanbul ignore next */
UNCOV
137
        ex.debug.drawRect(0, 0, this.width, this.height);
×
138
      }
139
      ex.restore();
4✔
140
    }
141
  }
142
}
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