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

visgl / luma.gl / 23355601514

20 Mar 2026 05:50PM UTC coverage: 52.166% (-25.8%) from 77.934%
23355601514

push

github

web-flow
chore: Migrate to vitest (#2554)

4188 of 11561 branches covered (36.23%)

Branch coverage included in aggregate %.

613 of 632 new or added lines in 22 files covered. (96.99%)

343 existing lines in 28 files now uncovered.

7757 of 11337 relevant lines covered (68.42%)

394.31 hits per line

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

37.24
/modules/engine/src/scenegraph/scenegraph-node.ts
1
// luma.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import {Vector3, Matrix4, NumericArray} from '@math.gl/core';
6
import {uid} from '../utils/uid';
7

8
function assert(condition: boolean, message?: string): asserts condition {
9
  if (!condition) {
2!
10
    throw new Error(message);
2✔
11
  }
12
}
13

14
/** Properties for creating a new Scenegraph */
15
export type ScenegraphNodeProps = {
16
  id?: string;
17
  /** whether to display the object at all */
18
  display?: boolean;
19
  matrix?: NumericArray;
20
  position?: NumericArray;
21
  rotation?: NumericArray;
22
  scale?: NumericArray;
23
  update?: boolean;
24
};
25

26
export class ScenegraphNode {
27
  readonly id: string;
28
  matrix: Matrix4 = new Matrix4();
44✔
29

30
  display = true;
44✔
31
  position = new Vector3();
44✔
32
  rotation = new Vector3();
44✔
33
  scale = new Vector3(1, 1, 1);
44✔
34
  userData: Record<string, unknown> = {};
44✔
35

36
  props: ScenegraphNodeProps = {};
44✔
37

38
  constructor(props: ScenegraphNodeProps = {}) {
44!
39
    const {id} = props;
44✔
40

41
    this.id = id || uid(this.constructor.name);
44!
42

43
    this._setScenegraphNodeProps(props);
44✔
44
  }
45

46
  getBounds(): [number[], number[]] | null {
47
    return null;
×
48
  }
49

50
  destroy(): void {}
51

52
  /** @deprecated use .destroy() */
53
  delete(): void {
54
    this.destroy();
×
55
  }
56
  setProps(props: ScenegraphNodeProps): this {
57
    this._setScenegraphNodeProps(props);
7✔
58
    return this;
7✔
59
  }
60

61
  toString(): string {
UNCOV
62
    return `{type: ScenegraphNode, id: ${this.id})}`;
×
63
  }
64

65
  setPosition(position: any): this {
UNCOV
66
    assert(position.length === 3, 'setPosition requires vector argument');
×
UNCOV
67
    this.position = position;
×
UNCOV
68
    return this;
×
69
  }
70

71
  setRotation(rotation: any): this {
72
    assert(rotation.length === 3 || rotation.length === 4, 'setRotation requires vector argument');
2!
73
    this.rotation = rotation;
2✔
74
    return this;
2✔
75
  }
76

77
  setScale(scale: any): this {
UNCOV
78
    assert(scale.length === 3, 'setScale requires vector argument');
×
UNCOV
79
    this.scale = scale;
×
UNCOV
80
    return this;
×
81
  }
82

83
  setMatrix(matrix: any, copyMatrix: boolean = true): void {
5!
84
    if (copyMatrix) {
5!
85
      this.matrix.copy(matrix);
5✔
86
    } else {
87
      this.matrix = matrix;
5✔
88
    }
89
  }
90

91
  setMatrixComponents(components: {
92
    position?: any;
93
    rotation?: any;
94
    scale?: any;
95
    update?: boolean;
96
  }): this {
UNCOV
97
    const {position, rotation, scale, update = true} = components;
×
UNCOV
98
    if (position) {
×
UNCOV
99
      this.setPosition(position);
×
100
    }
UNCOV
101
    if (rotation) {
×
UNCOV
102
      this.setRotation(rotation);
×
103
    }
UNCOV
104
    if (scale) {
×
UNCOV
105
      this.setScale(scale);
×
106
    }
UNCOV
107
    if (update) {
×
UNCOV
108
      this.updateMatrix();
×
109
    }
UNCOV
110
    return this;
×
111
  }
112

113
  updateMatrix(): this {
114
    this.matrix.identity();
51✔
115
    this.matrix.translate(this.position);
51✔
116
    if (this.rotation.length === 4) {
51!
117
      const rotationMatrix = new Matrix4().fromQuaternion(this.rotation);
51✔
118
      this.matrix.multiplyRight(rotationMatrix);
51✔
119
    } else {
120
      this.matrix.rotateXYZ(this.rotation);
51✔
121
    }
122
    this.matrix.scale(this.scale);
51✔
123

124
    return this;
51✔
125
  }
126

127
  update({position, rotation, scale}: {position?: any; rotation?: any; scale?: any} = {}): this {
×
UNCOV
128
    if (position) {
×
UNCOV
129
      this.setPosition(position);
×
130
    }
UNCOV
131
    if (rotation) {
×
UNCOV
132
      this.setRotation(rotation);
×
133
    }
UNCOV
134
    if (scale) {
×
UNCOV
135
      this.setScale(scale);
×
136
    }
137

UNCOV
138
    this.updateMatrix();
×
139

UNCOV
140
    return this;
×
141
  }
142

143
  getCoordinateUniforms(
144
    viewMatrix: any,
145
    modelMatrix?: any
146
  ): {
147
    viewMatrix: any;
148
    modelMatrix: any;
149
    objectMatrix: any;
150
    worldMatrix: any;
151
    worldInverseMatrix: any;
152
    worldInverseTransposeMatrix: any;
153
  } {
154
    // TODO - solve multiple class problem
155
    // assert(viewMatrix instanceof Matrix4);
156
    // assert(viewMatrix);
UNCOV
157
    modelMatrix = modelMatrix || this.matrix;
×
UNCOV
158
    const worldMatrix = new Matrix4(viewMatrix).multiplyRight(modelMatrix);
×
UNCOV
159
    const worldInverse = worldMatrix.invert();
×
UNCOV
160
    const worldInverseTranspose = worldInverse.transpose();
×
161

UNCOV
162
    return {
×
163
      viewMatrix,
164
      modelMatrix,
165
      objectMatrix: modelMatrix,
166
      worldMatrix,
167
      worldInverseMatrix: worldInverse,
168
      worldInverseTransposeMatrix: worldInverseTranspose
169
    };
170
  }
171

172
  // TODO - copied code, not yet vetted
173
  /*
174
  transform() {
175
    if (!this.parent) {
176
      this.endPosition.set(this.position);
177
      this.endRotation.set(this.rotation);
178
      this.endScale.set(this.scale);
179
    } else {
180
      const parent = this.parent;
181
      this.endPosition.set(this.position.add(parent.endPosition));
182
      this.endRotation.set(this.rotation.add(parent.endRotation));
183
      this.endScale.set(this.scale.add(parent.endScale));
184
    }
185

186
    const ch = this.children;
187
    for (let i = 0; i < ch.length; ++i) {
188
      ch[i].transform();
189
    }
190

191
    return this;
192
  }
193
  */
194

195
  _setScenegraphNodeProps(props: ScenegraphNodeProps): void {
196
    // if ('display' in props) {
197
    //   this.display = props.display;
198
    // }
199

200
    if (props?.position) {
51!
201
      this.setPosition(props.position);
51✔
202
    }
203
    if (props?.rotation) {
51!
204
      this.setRotation(props.rotation);
51✔
205
    }
206
    if (props?.scale) {
51!
207
      this.setScale(props.scale);
51✔
208
    }
209

210
    this.updateMatrix();
51✔
211

212
    // Matrix overwrites other props
213
    if (props?.matrix) {
51!
214
      this.setMatrix(props.matrix);
51✔
215
    }
216

217
    Object.assign(this.props, props);
51✔
218
  }
219
}
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