@loaders.gl/potree 4.3.0-alpha.5 → 4.3.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import {load} from '@loaders.gl/core';
6
- import {MeshGeometry} from '@loaders.gl/schema';
6
+ import {Mesh} from '@loaders.gl/schema';
7
7
  import {DataSource, DataSourceProps, LoaderOptions, resolvePath} from '@loaders.gl/loader-utils';
8
8
  import {LASLoader} from '@loaders.gl/las';
9
9
  import {PotreeMetadata} from '../types/potree-metadata';
@@ -59,6 +59,10 @@ export class PotreeNodesSource extends DataSource {
59
59
 
60
60
  /** Initial data source loading */
61
61
  async init() {
62
+ if (this.initPromise) {
63
+ await this.initPromise;
64
+ return;
65
+ }
62
66
  this.metadata = await load(`${this.baseUrl}/cloud.js`, PotreeLoader);
63
67
  await this.loadHierarchy();
64
68
  this.isReady = true;
@@ -70,7 +74,7 @@ export class PotreeNodesSource extends DataSource {
70
74
  return (
71
75
  this.isReady &&
72
76
  major === 1 &&
73
- minor < 2 &&
77
+ minor <= 8 &&
74
78
  typeof this.metadata?.pointAttributes === 'string' &&
75
79
  ['LAS', 'LAZ'].includes(this.metadata?.pointAttributes)
76
80
  );
@@ -96,7 +100,7 @@ export class PotreeNodesSource extends DataSource {
96
100
  * @param path array of numbers between 0-7 specifying successive octree divisions.
97
101
  * @return node content geometry or null if the node doesn't exist
98
102
  */
99
- async loadNodeContent(path: number[]): Promise<MeshGeometry | null> {
103
+ async loadNodeContent(path: number[]): Promise<Mesh | null> {
100
104
  await this.initPromise;
101
105
 
102
106
  if (!this.isSupported()) {
@@ -106,25 +110,15 @@ export class PotreeNodesSource extends DataSource {
106
110
  const isAvailable = await this.isNodeAvailable(path);
107
111
  if (isAvailable) {
108
112
  return load(
109
- `${this.baseUrl}/${this.metadata
110
- ?.octreeDir}/r/r${path.join()}.${this.getContentExtension()}`,
113
+ `${this.baseUrl}/${this.metadata?.octreeDir}/r/r${path.join(
114
+ ''
115
+ )}.${this.getContentExtension()}`,
111
116
  LASLoader
112
117
  );
113
118
  }
114
119
  return null;
115
120
  }
116
121
 
117
- /**
118
- * Load data source hierarchy into tree of available nodes
119
- */
120
- async loadHierarchy(): Promise<void> {
121
- await this.initPromise;
122
- this.root = await load(
123
- `${this.baseUrl}/${this.metadata?.octreeDir}/r/r.hrc`,
124
- PotreeHierarchyChunkLoader
125
- );
126
- }
127
-
128
122
  /**
129
123
  * Check if a node exists in the octree
130
124
  * @param path array of numbers between 0-7 specifying successive octree divisions
@@ -155,6 +149,16 @@ export class PotreeNodesSource extends DataSource {
155
149
  return result;
156
150
  }
157
151
 
152
+ /**
153
+ * Load data source hierarchy into tree of available nodes
154
+ */
155
+ private async loadHierarchy(): Promise<void> {
156
+ this.root = await load(
157
+ `${this.baseUrl}/${this.metadata?.octreeDir}/r/r.hrc`,
158
+ PotreeHierarchyChunkLoader
159
+ );
160
+ }
161
+
158
162
  /**
159
163
  * Deduce base url from the input url sring
160
164
  * @param data - data source input data
@@ -54,32 +54,53 @@ Would have an index looking like this:
54
54
  | r36 | `0b00000000` (=0) | `1` |
55
55
  */
56
56
 
57
- /** @todo these types are an incorrect mess */
57
+ /** Node metadata from index file */
58
58
  export type POTreeTileHeader = {
59
+ /** Number of child nodes */
59
60
  childCount: number;
61
+ /** Human readable name */
60
62
  name: string;
63
+ /** Child availability mask */
61
64
  childMask: number;
62
65
  };
63
66
 
64
- /** @todo these types are an incorrect mess */
67
+ /** Hierarchical potree node structure */
65
68
  export type POTreeNode = {
69
+ /** Index data */
66
70
  header: POTreeTileHeader;
71
+ /** Human readable name */
67
72
  name: string;
73
+ /** Number of points */
68
74
  pointCount: number;
75
+ /** Node's level in the tree */
76
+ level: number;
77
+ /** Has children */
78
+ hasChildren: boolean;
79
+ /** Space between points */
80
+ spacing: number;
81
+ /** Available children */
69
82
  children: POTreeNode[];
83
+ /** All children including unavailable */
70
84
  childrenByIndex: POTreeNode[];
71
85
  };
72
86
 
73
- // type POTreeTileNode = POTreeNode;
74
-
75
- // load hierarchy
76
- export function parsePotreeHierarchyChunk(arrayBuffer: ArrayBuffer) {
87
+ /**
88
+ * load hierarchy
89
+ * @param arrayBuffer - binary index data
90
+ * @returns root node
91
+ **/
92
+ export function parsePotreeHierarchyChunk(arrayBuffer: ArrayBuffer): POTreeNode {
77
93
  const tileHeaders = parseBinaryChunk(arrayBuffer);
78
94
  return buildHierarchy(tileHeaders);
79
95
  }
80
96
 
81
- // Parses the binary rows
82
- function parseBinaryChunk(arrayBuffer: ArrayBuffer, byteOffset = 0) {
97
+ /**
98
+ * Parses the binary rows
99
+ * @param arrayBuffer - binary index data to parse
100
+ * @param byteOffset - byte offset to start from
101
+ * @returns flat nodes array
102
+ * */
103
+ function parseBinaryChunk(arrayBuffer: ArrayBuffer, byteOffset = 0): POTreeNode[] {
83
104
  const dataView = new DataView(arrayBuffer);
84
105
 
85
106
  const stack: POTreeNode[] = [];
@@ -90,8 +111,7 @@ function parseBinaryChunk(arrayBuffer: ArrayBuffer, byteOffset = 0) {
90
111
  byteOffset = decodeRow(dataView, byteOffset, topTileHeader);
91
112
 
92
113
  stack.push(topTileHeader);
93
-
94
- const tileHeaders: POTreeTileHeader[] = [];
114
+ const tileHeaders: POTreeNode[] = [topTileHeader];
95
115
 
96
116
  while (stack.length > 0) {
97
117
  const snode = stack.shift();
@@ -100,11 +120,10 @@ function parseBinaryChunk(arrayBuffer: ArrayBuffer, byteOffset = 0) {
100
120
  for (let i = 0; i < 8; i++) {
101
121
  if (snode && (snode.header.childMask & mask) !== 0) {
102
122
  // @ts-expect-error
103
- const tileHeader: POTreeTileHeader = {};
123
+ const tileHeader: POTreeNode = {};
104
124
  byteOffset = decodeRow(dataView, byteOffset, tileHeader);
105
125
  tileHeader.name = snode.name + i;
106
126
 
107
- // @ts-expect-error
108
127
  stack.push(tileHeader);
109
128
  tileHeaders.push(tileHeader);
110
129
  snode.header.childCount++;
@@ -120,7 +139,14 @@ function parseBinaryChunk(arrayBuffer: ArrayBuffer, byteOffset = 0) {
120
139
  return tileHeaders;
121
140
  }
122
141
 
123
- function decodeRow(dataView, byteOffset, tileHeader) {
142
+ /**
143
+ * Reads next row from binary index file
144
+ * @param dataView - index data
145
+ * @param byteOffset - current offset in the index data
146
+ * @param tileHeader - container to read to
147
+ * @returns new offset
148
+ */
149
+ function decodeRow(dataView: DataView, byteOffset: number, tileHeader: POTreeNode): number {
124
150
  tileHeader.header = tileHeader.header || {};
125
151
  tileHeader.header.childMask = dataView.getUint8(byteOffset);
126
152
  tileHeader.header.childCount = 0;
@@ -130,16 +156,16 @@ function decodeRow(dataView, byteOffset, tileHeader) {
130
156
  return byteOffset;
131
157
  }
132
158
 
133
- // Resolves the binary rows into a hierarchy (tree structure)
134
- function buildHierarchy(tileHeaders, options: {spacing?: number} = {}): POTreeNode {
159
+ /** Resolves the binary rows into a hierarchy (tree structure) */
160
+ function buildHierarchy(flatNodes: POTreeNode[], options: {spacing?: number} = {}): POTreeNode {
135
161
  const DEFAULT_OPTIONS = {spacing: 100}; // TODO assert instead of default?
136
162
  options = {...DEFAULT_OPTIONS, ...options};
137
163
 
138
- const topNode = tileHeaders[0];
164
+ const topNode: POTreeNode = flatNodes[0];
139
165
  const nodes = {};
140
166
 
141
- for (const tileHeader of tileHeaders) {
142
- const {name} = tileHeader;
167
+ for (const node of flatNodes) {
168
+ const {name} = node;
143
169
 
144
170
  const index = parseInt(name.charAt(name.length - 1), 10);
145
171
  const parentName = name.substring(0, name.length - 1);
@@ -147,20 +173,20 @@ function buildHierarchy(tileHeaders, options: {spacing?: number} = {}): POTreeNo
147
173
  const level = name.length - 1;
148
174
  // assert(parentNode && level >= 0);
149
175
 
150
- tileHeader.level = level;
151
- tileHeader.hasChildren = tileHeader.header.childCount;
152
- tileHeader.children = [];
153
- tileHeader.childrenByIndex = new Array(8).fill(null);
154
- tileHeader.spacing = (options?.spacing || 0) / Math.pow(2, level);
176
+ node.level = level;
177
+ node.hasChildren = Boolean(node.header.childCount);
178
+ node.children = [];
179
+ node.childrenByIndex = new Array(8).fill(null);
180
+ node.spacing = (options?.spacing || 0) / Math.pow(2, level);
155
181
  // tileHeader.boundingVolume = Utils.createChildAABB(parentNode.boundingBox, index);
156
182
 
157
183
  if (parentNode) {
158
- parentNode.children.push(tileHeader);
159
- parentNode.childrenByIndex[index] = tileHeader;
184
+ parentNode.children.push(node);
185
+ parentNode.childrenByIndex[index] = node;
160
186
  }
161
187
 
162
188
  // Add the node to the map
163
- nodes[name] = tileHeader;
189
+ nodes[name] = node;
164
190
  }
165
191
 
166
192
  // First node is the root