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

visgl / luma.gl / 23098639930

14 Mar 2026 11:24PM UTC coverage: 75.881% (+0.2%) from 75.702%
23098639930

push

github

web-flow
Refactor conversion of glTF nodes to luma.gl nodes, add skin capability (#2391)

2462 of 3163 branches covered (77.84%)

Branch coverage included in aggregate %.

315 of 382 new or added lines in 14 files covered. (82.46%)

3 existing lines in 2 files now uncovered.

29411 of 38841 relevant lines covered (75.72%)

90.99 hits per line

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

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

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

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

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

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

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

53✔
36
  props: ScenegraphNodeProps = {};
53✔
37

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

53✔
41
    this.id = id || uid(this.constructor.name);
53✔
42

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

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

53✔
50
  destroy(): void {}
53✔
51

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

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

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

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

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

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

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

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

63✔
124
    return this;
63✔
125
  }
63✔
126

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

2✔
138
    this.updateMatrix();
2✔
139

2✔
140
    return this;
2✔
141
  }
2✔
142

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

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

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

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

53✔
191
    return this;
53✔
192
  }
53✔
193
  */
53✔
194

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

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

60✔
210
    this.updateMatrix();
60✔
211

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

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