@webviz/subsurface-viewer 1.17.7 → 1.18.1
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.
- package/dist/components/Map.d.ts +15 -1
- package/dist/components/Map.js.map +1 -1
- package/dist/layers/index.d.ts +2 -0
- package/dist/layers/index.js +1 -0
- package/dist/layers/index.js.map +1 -1
- package/dist/layers/polyline_group/polylineGroupLayer.d.ts +239 -0
- package/dist/layers/polyline_group/polylineGroupLayer.js +648 -0
- package/dist/layers/polyline_group/polylineGroupLayer.js.map +1 -0
- package/dist/layers/polylines/polylinesLayer.d.ts +4 -0
- package/dist/layers/polylines/polylinesLayer.js +4 -0
- package/dist/layers/polylines/polylinesLayer.js.map +1 -1
- package/package.json +14 -11
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import { CompositeLayer } from "@deck.gl/core";
|
|
2
|
+
import { DataFilterExtension, PathStyleExtension } from "@deck.gl/extensions";
|
|
3
|
+
import { PathLayer } from "@deck.gl/layers";
|
|
4
|
+
import { SectionViewport } from "../../viewports";
|
|
5
|
+
import { createPropertyData } from "../utils/layerTools";
|
|
6
|
+
import _ from "lodash";
|
|
7
|
+
const DEFAULT_GROUP_STYLE = {
|
|
8
|
+
color: [0, 128, 255, 255],
|
|
9
|
+
width: 2,
|
|
10
|
+
dashArray: [0, 0],
|
|
11
|
+
};
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Default props
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const defaultProps = {
|
|
16
|
+
"@@type": "PolylineGroupLayer",
|
|
17
|
+
name: "PolylineGroupLayer",
|
|
18
|
+
id: "polyline-group-layer",
|
|
19
|
+
pickable: false,
|
|
20
|
+
visible: true,
|
|
21
|
+
depthTest: true,
|
|
22
|
+
ZIncreasingDownwards: true,
|
|
23
|
+
defaultGroupStyle: DEFAULT_GROUP_STYLE,
|
|
24
|
+
widthUnits: "meters",
|
|
25
|
+
widthScale: 1,
|
|
26
|
+
widthMinPixels: 0,
|
|
27
|
+
widthMaxPixels: Number.MAX_SAFE_INTEGER,
|
|
28
|
+
jointRounded: false,
|
|
29
|
+
capRounded: false,
|
|
30
|
+
miterLimit: 4,
|
|
31
|
+
billboard: true,
|
|
32
|
+
getGroupPolylines: (g) => g.polylines,
|
|
33
|
+
getPolylinePath: (p) => p.path,
|
|
34
|
+
};
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Resolution helpers
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
function resolveColor(polyline, group, props) {
|
|
39
|
+
var _a, _b, _c, _d;
|
|
40
|
+
const fromPolylineAccessor = (_a = props.getPolylineColor) === null || _a === void 0 ? void 0 : _a.call(props, polyline, group);
|
|
41
|
+
if (fromPolylineAccessor != null)
|
|
42
|
+
return fromPolylineAccessor;
|
|
43
|
+
if (polyline.color != null)
|
|
44
|
+
return polyline.color;
|
|
45
|
+
const fromGroupAccessor = (_b = props.getGroupColor) === null || _b === void 0 ? void 0 : _b.call(props, group);
|
|
46
|
+
if (fromGroupAccessor != null)
|
|
47
|
+
return fromGroupAccessor;
|
|
48
|
+
if (group.color != null)
|
|
49
|
+
return group.color;
|
|
50
|
+
return (_d = (_c = props.defaultGroupStyle) === null || _c === void 0 ? void 0 : _c.color) !== null && _d !== void 0 ? _d : DEFAULT_GROUP_STYLE.color;
|
|
51
|
+
}
|
|
52
|
+
function resolveWidth(polyline, group, props) {
|
|
53
|
+
var _a, _b, _c, _d;
|
|
54
|
+
const fromPolylineAccessor = (_a = props.getPolylineWidth) === null || _a === void 0 ? void 0 : _a.call(props, polyline, group);
|
|
55
|
+
if (fromPolylineAccessor != null)
|
|
56
|
+
return fromPolylineAccessor;
|
|
57
|
+
if (polyline.width != null)
|
|
58
|
+
return polyline.width;
|
|
59
|
+
const fromGroupAccessor = (_b = props.getGroupWidth) === null || _b === void 0 ? void 0 : _b.call(props, group);
|
|
60
|
+
if (fromGroupAccessor != null)
|
|
61
|
+
return fromGroupAccessor;
|
|
62
|
+
if (group.width != null)
|
|
63
|
+
return group.width;
|
|
64
|
+
return (_d = (_c = props.defaultGroupStyle) === null || _c === void 0 ? void 0 : _c.width) !== null && _d !== void 0 ? _d : DEFAULT_GROUP_STYLE.width;
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Flatten
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
function isBinaryPolylines(p) {
|
|
70
|
+
return !Array.isArray(p) && "positions" in p;
|
|
71
|
+
}
|
|
72
|
+
function resolveDashArray(polyline, group, props) {
|
|
73
|
+
var _a, _b, _c, _d;
|
|
74
|
+
const fromPolylineAccessor = (_a = props.getPolylineDashArray) === null || _a === void 0 ? void 0 : _a.call(props, polyline, group);
|
|
75
|
+
if (fromPolylineAccessor != null)
|
|
76
|
+
return fromPolylineAccessor;
|
|
77
|
+
if (polyline.dashArray != null)
|
|
78
|
+
return polyline.dashArray;
|
|
79
|
+
const fromGroupAccessor = (_b = props.getGroupDashArray) === null || _b === void 0 ? void 0 : _b.call(props, group);
|
|
80
|
+
if (fromGroupAccessor != null)
|
|
81
|
+
return fromGroupAccessor;
|
|
82
|
+
if (group.dashArray != null)
|
|
83
|
+
return group.dashArray;
|
|
84
|
+
return (_d = (_c = props.defaultGroupStyle) === null || _c === void 0 ? void 0 : _c.dashArray) !== null && _d !== void 0 ? _d : DEFAULT_GROUP_STYLE.dashArray;
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Segment resolution helpers (used when polyline.path is a PolylineGroup)
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Cascade: accessor(segment) -> segment field -> subGroup field -> parent polyline
|
|
90
|
+
// resolution (which itself cascades through accessor -> field -> outerGroup -> default).
|
|
91
|
+
function resolveSegmentColor(segment, subGroup, parentPolyline, outerGroup, props) {
|
|
92
|
+
var _a;
|
|
93
|
+
const fromAccessor = (_a = props.getPolylineColor) === null || _a === void 0 ? void 0 : _a.call(props, segment, outerGroup);
|
|
94
|
+
if (fromAccessor != null)
|
|
95
|
+
return fromAccessor;
|
|
96
|
+
if (segment.color != null)
|
|
97
|
+
return segment.color;
|
|
98
|
+
if (subGroup.color != null)
|
|
99
|
+
return subGroup.color;
|
|
100
|
+
return resolveColor(parentPolyline, outerGroup, props);
|
|
101
|
+
}
|
|
102
|
+
function resolveSegmentWidth(segment, subGroup, parentPolyline, outerGroup, props) {
|
|
103
|
+
var _a;
|
|
104
|
+
const fromAccessor = (_a = props.getPolylineWidth) === null || _a === void 0 ? void 0 : _a.call(props, segment, outerGroup);
|
|
105
|
+
if (fromAccessor != null)
|
|
106
|
+
return fromAccessor;
|
|
107
|
+
if (segment.width != null)
|
|
108
|
+
return segment.width;
|
|
109
|
+
if (subGroup.width != null)
|
|
110
|
+
return subGroup.width;
|
|
111
|
+
return resolveWidth(parentPolyline, outerGroup, props);
|
|
112
|
+
}
|
|
113
|
+
function resolveSegmentDashArray(segment, subGroup, parentPolyline, outerGroup, props) {
|
|
114
|
+
var _a;
|
|
115
|
+
const fromAccessor = (_a = props.getPolylineDashArray) === null || _a === void 0 ? void 0 : _a.call(props, segment, outerGroup);
|
|
116
|
+
if (fromAccessor != null)
|
|
117
|
+
return fromAccessor;
|
|
118
|
+
if (segment.dashArray != null)
|
|
119
|
+
return segment.dashArray;
|
|
120
|
+
if (subGroup.dashArray != null)
|
|
121
|
+
return subGroup.dashArray;
|
|
122
|
+
return resolveDashArray(parentPolyline, outerGroup, props);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Expands a polyline whose `path` is a {@link PolylineGroup} (i.e. a
|
|
126
|
+
* discontinuous polyline made of disjoint segments) into one {@link FlatEntry}
|
|
127
|
+
* per segment. Returns an empty array and emits a warning if the sub-group
|
|
128
|
+
* contains {@link BinaryPolylines}, which are not supported at the segment level.
|
|
129
|
+
*/
|
|
130
|
+
function flattenSubGroupPolyline(polyline, subGroup, group, getPath, props) {
|
|
131
|
+
const segPolylines = subGroup.polylines;
|
|
132
|
+
if (isBinaryPolylines(segPolylines)) {
|
|
133
|
+
console.warn("PolylineGroupLayer: BinaryPolylines inside a sub-group path is not supported. Skipping.");
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
return segPolylines.flatMap((segment) => {
|
|
137
|
+
if (!Array.isArray(segment.path)) {
|
|
138
|
+
console.warn("PolylineGroupLayer: nested PolylineGroup paths inside segment polylines are not supported. Skipping segment.");
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
path: getPath(segment, group),
|
|
144
|
+
color: resolveSegmentColor(segment, subGroup, polyline, group, props),
|
|
145
|
+
width: resolveSegmentWidth(segment, subGroup, polyline, group, props),
|
|
146
|
+
dashArray: resolveSegmentDashArray(segment, subGroup, polyline, group, props),
|
|
147
|
+
_polyline: polyline, // root polyline — for picking & hiddenPolylines
|
|
148
|
+
_group: group,
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Allocates combined typed-array buffers and fills them from all raw binary
|
|
155
|
+
* groups. Positions and indices are concatenated; color/width are replicated
|
|
156
|
+
* per vertex so the GPU can read them without per-vertex JS work.
|
|
157
|
+
*
|
|
158
|
+
* @remarks * `polylines` is a sanitized copy of group.polylines with invalid
|
|
159
|
+
* colors/widths buffers stripped out; do not read from group.polylines directly.
|
|
160
|
+
*/
|
|
161
|
+
function buildBinaryData(binaryRaw, totalVerts, totalPaths, ZIncreasingDownwards, props) {
|
|
162
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
163
|
+
const positions = new Float32Array(totalVerts * 3);
|
|
164
|
+
const colors = new Uint8Array(totalVerts * 4);
|
|
165
|
+
const widths = new Float32Array(totalVerts);
|
|
166
|
+
const vertexGroupIndex = new Uint32Array(totalVerts);
|
|
167
|
+
const startIndices = new Uint32Array(totalPaths + 1);
|
|
168
|
+
const pathGroup = new Array(totalPaths);
|
|
169
|
+
const groups = binaryRaw.map((b) => b.group);
|
|
170
|
+
let vOffset = 0;
|
|
171
|
+
let pOffset = 0;
|
|
172
|
+
for (let gIdx = 0; gIdx < binaryRaw.length; gIdx++) {
|
|
173
|
+
const { group, polylines } = binaryRaw[gIdx];
|
|
174
|
+
const src = polylines.positions;
|
|
175
|
+
// Ensure positions buffer length is a multiple of 3; if not, ignore
|
|
176
|
+
// trailing vertices.
|
|
177
|
+
if (src.length % 3 !== 0) {
|
|
178
|
+
console.warn(`PolylineGroupLayer: BinaryPolylines group (id=` +
|
|
179
|
+
`${(_a = group.id) !== null && _a !== void 0 ? _a : "unknown"}) has a positions buffer whose` +
|
|
180
|
+
`length (${src.length}) is not a multiple of 3. Trailing ` +
|
|
181
|
+
`incomplete vertex will be ignored.`);
|
|
182
|
+
}
|
|
183
|
+
const srcVerts = Math.floor(src.length / 3);
|
|
184
|
+
// Copy positions (optionally flipping Z)
|
|
185
|
+
const base = vOffset * 3;
|
|
186
|
+
const srcLen = srcVerts * 3;
|
|
187
|
+
if (ZIncreasingDownwards) {
|
|
188
|
+
for (let i = 0; i < srcLen; i += 3) {
|
|
189
|
+
positions[base + i] = src[i];
|
|
190
|
+
positions[base + i + 1] = src[i + 1];
|
|
191
|
+
positions[base + i + 2] = -src[i + 2];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// Slice to srcLen to exclude any trailing incomplete vertex.
|
|
196
|
+
positions.set(srcLen < src.length ? src.subarray(0, srcLen) : src, base);
|
|
197
|
+
}
|
|
198
|
+
if (polylines.colors) {
|
|
199
|
+
colors.set(polylines.colors, vOffset * 4);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const color = (_f = (_d = (_c = (_b = props.getGroupColor) === null || _b === void 0 ? void 0 : _b.call(props, group)) !== null && _c !== void 0 ? _c : group.color) !== null && _d !== void 0 ? _d : (_e = props.defaultGroupStyle) === null || _e === void 0 ? void 0 : _e.color) !== null && _f !== void 0 ? _f : [0, 128, 255, 255];
|
|
203
|
+
// Flood-fill the group color across all vertices efficiently:
|
|
204
|
+
// 1. Write the first RGBA (4 × Uint8) into the combined Uint8Array
|
|
205
|
+
// at byte offset vOffset*4.
|
|
206
|
+
// 2. Reinterpret the same ArrayBuffer as Uint32 (1 element = 4 bytes
|
|
207
|
+
// = one RGBA pixel). Index vOffset in the Uint32 view therefore
|
|
208
|
+
// aliases the four bytes we just wrote.
|
|
209
|
+
// 3. Use TypedArray.fill to copy that single 32-bit value into every
|
|
210
|
+
// remaining vertex slot in one call — no per-channel bit-shifting.
|
|
211
|
+
colors.set([color[0], color[1], color[2], (_g = color[3]) !== null && _g !== void 0 ? _g : 255], vOffset * 4);
|
|
212
|
+
new Uint32Array(colors.buffer).fill(new Uint32Array(colors.buffer)[vOffset], vOffset + 1, vOffset + srcVerts);
|
|
213
|
+
}
|
|
214
|
+
if (polylines.widths) {
|
|
215
|
+
widths.set(polylines.widths, vOffset);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
widths.fill((_m = (_k = (_j = (_h = props.getGroupWidth) === null || _h === void 0 ? void 0 : _h.call(props, group)) !== null && _j !== void 0 ? _j : group.width) !== null && _k !== void 0 ? _k : (_l = props.defaultGroupStyle) === null || _l === void 0 ? void 0 : _l.width) !== null && _m !== void 0 ? _m : 2, vOffset, vOffset + srcVerts);
|
|
219
|
+
}
|
|
220
|
+
for (let v = 0; v < srcVerts; v++) {
|
|
221
|
+
vertexGroupIndex[vOffset + v] = gIdx;
|
|
222
|
+
}
|
|
223
|
+
// Offset and copy startIndices into the combined buffer.
|
|
224
|
+
const srcIdx = polylines.startIndices;
|
|
225
|
+
for (let p = 0; p < srcIdx.length; p++) {
|
|
226
|
+
startIndices[pOffset + p] = srcIdx[p] + vOffset;
|
|
227
|
+
pathGroup[pOffset + p] = group;
|
|
228
|
+
}
|
|
229
|
+
vOffset += srcVerts;
|
|
230
|
+
pOffset += srcIdx.length;
|
|
231
|
+
}
|
|
232
|
+
startIndices[totalPaths] = totalVerts; // terminal entry
|
|
233
|
+
return {
|
|
234
|
+
positions,
|
|
235
|
+
startIndices,
|
|
236
|
+
colors,
|
|
237
|
+
widths,
|
|
238
|
+
pathGroup,
|
|
239
|
+
vertexGroupIndex,
|
|
240
|
+
groups,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Builds the per-vertex GPU filter buffer for the binary sub-layer.
|
|
245
|
+
* Each vertex gets 1.0 if its group is visible, 0.0 if it is hidden.
|
|
246
|
+
* This is a plain typed-array allocation (O(vertices)); separated so it can
|
|
247
|
+
* be re-run cheaply when only `hiddenGroups` changes, without rebuilding the
|
|
248
|
+
* full `BinaryData`.
|
|
249
|
+
*/
|
|
250
|
+
function buildFilterValues(binaryData, hiddenGroups) {
|
|
251
|
+
const filterValues = new Float32Array(binaryData.vertexGroupIndex.length);
|
|
252
|
+
for (let i = 0; i < filterValues.length; i++) {
|
|
253
|
+
const g = binaryData.groups[binaryData.vertexGroupIndex[i]];
|
|
254
|
+
filterValues[i] = g.id != null && (hiddenGroups === null || hiddenGroups === void 0 ? void 0 : hiddenGroups.has(g.id)) ? 0 : 1;
|
|
255
|
+
}
|
|
256
|
+
return filterValues;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Assembles the `data` object passed to the binary PathLayer sub-layer.
|
|
260
|
+
* Thin wrapper around the cached typed arrays; cheap to call, but creating a
|
|
261
|
+
* new object reference signals deck.gl to re-examine attribute buffers.
|
|
262
|
+
*/
|
|
263
|
+
function buildBinaryLayerData(binaryData, filterValues) {
|
|
264
|
+
return {
|
|
265
|
+
length: binaryData.startIndices.length - 1,
|
|
266
|
+
startIndices: binaryData.startIndices,
|
|
267
|
+
attributes: {
|
|
268
|
+
getPath: { value: binaryData.positions, size: 3 },
|
|
269
|
+
getFilterValue: { value: filterValues, size: 1 },
|
|
270
|
+
getColor: { value: binaryData.colors, size: 4, normalized: true },
|
|
271
|
+
getWidth: { value: binaryData.widths, size: 1 },
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function flattenGroupData(data, props) {
|
|
276
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
277
|
+
const getPolylines = (_a = props.getGroupPolylines) !== null && _a !== void 0 ? _a : ((g) => g.polylines);
|
|
278
|
+
const getPath = (_b = props.getPolylinePath) !== null && _b !== void 0 ? _b : ((p) => p.path);
|
|
279
|
+
const flatData = [];
|
|
280
|
+
const ZIncreasingDownwards = (_c = props.ZIncreasingDownwards) !== null && _c !== void 0 ? _c : true;
|
|
281
|
+
// First pass over binary groups: collect groups + sum sizes so we can
|
|
282
|
+
// allocate combined typed arrays once.
|
|
283
|
+
const binaryRaw = [];
|
|
284
|
+
let totalVerts = 0;
|
|
285
|
+
let totalPaths = 0;
|
|
286
|
+
for (const group of data) {
|
|
287
|
+
const polylines = getPolylines(group);
|
|
288
|
+
if (isBinaryPolylines(polylines)) {
|
|
289
|
+
if (group.dashArray != null ||
|
|
290
|
+
((_d = props.getGroupDashArray) === null || _d === void 0 ? void 0 : _d.call(props, group)) != null) {
|
|
291
|
+
console.warn("PolylineGroupLayer: dashArray is not supported on BinaryPolylines groups; ignoring.");
|
|
292
|
+
}
|
|
293
|
+
if (polylines.positions.length % 3 !== 0) {
|
|
294
|
+
console.warn(`PolylineGroupLayer: BinaryPolylines group (id=${(_e = group.id) !== null && _e !== void 0 ? _e : "unknown"}) has a positions buffer whose length (${polylines.positions.length}) is not a multiple of 3. Trailing incomplete vertex will be ignored.`);
|
|
295
|
+
}
|
|
296
|
+
const srcVerts = Math.floor(polylines.positions.length / 3);
|
|
297
|
+
let colors = polylines.colors;
|
|
298
|
+
if (colors && colors.length !== srcVerts * 4) {
|
|
299
|
+
console.warn(`PolylineGroupLayer: BinaryPolylines group (id=${(_f = group.id) !== null && _f !== void 0 ? _f : "unknown"}) has a colors buffer with wrong length (expected ${srcVerts * 4}, got ${colors.length}). Ignoring colors.`);
|
|
300
|
+
colors = undefined;
|
|
301
|
+
}
|
|
302
|
+
let widths = polylines.widths;
|
|
303
|
+
if (widths && widths.length !== srcVerts) {
|
|
304
|
+
console.warn(`PolylineGroupLayer: BinaryPolylines group (id=${(_g = group.id) !== null && _g !== void 0 ? _g : "unknown"}) has a widths buffer with wrong length (expected ${srcVerts}, got ${widths.length}). Ignoring widths.`);
|
|
305
|
+
widths = undefined;
|
|
306
|
+
}
|
|
307
|
+
binaryRaw.push({
|
|
308
|
+
group,
|
|
309
|
+
polylines: Object.assign(Object.assign({}, polylines), { colors, widths }),
|
|
310
|
+
});
|
|
311
|
+
totalVerts += srcVerts;
|
|
312
|
+
totalPaths += polylines.startIndices.length;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
for (const polyline of polylines) {
|
|
316
|
+
if (!Array.isArray(polyline.path)) {
|
|
317
|
+
flatData.push(...flattenSubGroupPolyline(polyline, polyline.path, group, getPath, props));
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
flatData.push({
|
|
321
|
+
path: getPath(polyline, group),
|
|
322
|
+
color: resolveColor(polyline, group, props),
|
|
323
|
+
width: resolveWidth(polyline, group, props),
|
|
324
|
+
dashArray: resolveDashArray(polyline, group, props),
|
|
325
|
+
_polyline: polyline,
|
|
326
|
+
_group: group,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (binaryRaw.length === 0) {
|
|
333
|
+
return { flatData, binaryData: null };
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
flatData,
|
|
337
|
+
binaryData: buildBinaryData(binaryRaw, totalVerts, totalPaths, ZIncreasingDownwards, props),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function buildSectionIndex(path) {
|
|
341
|
+
const cumDist = [0];
|
|
342
|
+
for (let i = 1; i < path.length; i++) {
|
|
343
|
+
const dx = path[i][0] - path[i - 1][0];
|
|
344
|
+
const dy = path[i][1] - path[i - 1][1];
|
|
345
|
+
cumDist.push(cumDist[i - 1] + Math.sqrt(dx * dx + dy * dy));
|
|
346
|
+
}
|
|
347
|
+
return { cumDist, path };
|
|
348
|
+
}
|
|
349
|
+
/** Return the (x, y) world position on the fence at the given cumulative distance. */
|
|
350
|
+
function projectAbscissa(abscissa, idx) {
|
|
351
|
+
const { cumDist, path } = idx;
|
|
352
|
+
if (path.length === 0)
|
|
353
|
+
return [0, 0];
|
|
354
|
+
if (abscissa <= cumDist[0])
|
|
355
|
+
return [path[0][0], path[0][1]];
|
|
356
|
+
const last = cumDist.length - 1;
|
|
357
|
+
if (abscissa >= cumDist[last])
|
|
358
|
+
return [path[last][0], path[last][1]];
|
|
359
|
+
let lo = 0, hi = last;
|
|
360
|
+
while (hi - lo > 1) {
|
|
361
|
+
const mid = (lo + hi) >> 1;
|
|
362
|
+
if (cumDist[mid] <= abscissa)
|
|
363
|
+
lo = mid;
|
|
364
|
+
else
|
|
365
|
+
hi = mid;
|
|
366
|
+
}
|
|
367
|
+
const t = (abscissa - cumDist[lo]) / (cumDist[hi] - cumDist[lo]);
|
|
368
|
+
return [
|
|
369
|
+
path[lo][0] + t * (path[hi][0] - path[lo][0]),
|
|
370
|
+
path[lo][1] + t * (path[hi][1] - path[lo][1]),
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
// Layer class
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
/**
|
|
377
|
+
* A deck.gl {@link CompositeLayer} that renders collections of polylines
|
|
378
|
+
* organised into named groups.
|
|
379
|
+
*
|
|
380
|
+
* **Data formats**
|
|
381
|
+
*
|
|
382
|
+
* Each {@link PolylineGroup} holds one or more polylines. Two formats are
|
|
383
|
+
* supported for `PolylineGroup.polylines`:
|
|
384
|
+
* - `Polyline[]` — per-object format; supports `id`, and per-polyline `color`,
|
|
385
|
+
* `width`, and `dashArray` overrides.
|
|
386
|
+
* - {@link BinaryPolylines} — flat typed-array format; more efficient for
|
|
387
|
+
* large datasets loaded from binary sources. Group-level styling only.
|
|
388
|
+
*
|
|
389
|
+
* **Discontinuous polylines**
|
|
390
|
+
*
|
|
391
|
+
* A `Polyline.path` may be a nested {@link PolylineGroup}, making one logical
|
|
392
|
+
* polyline consist of disjoint segments (e.g. a seismic horizon cut by faults).
|
|
393
|
+
* All segments share the root `Polyline.id` for picking and visibility filtering.
|
|
394
|
+
*
|
|
395
|
+
* **Styling cascade**
|
|
396
|
+
*
|
|
397
|
+
* Color, width, and dash pattern are resolved per-path in this order:
|
|
398
|
+
* polyline accessor → `Polyline` field → group accessor → `PolylineGroup` field
|
|
399
|
+
* → layer default prop.
|
|
400
|
+
*
|
|
401
|
+
* **Section-view projection**
|
|
402
|
+
*
|
|
403
|
+
* When {@link PolylineGroupLayerProps.sectionPath} is provided, path coordinates
|
|
404
|
+
* are interpreted as `[abscissa, depth]`. The layer renders two sub-layers:
|
|
405
|
+
* one in abscissa/depth space for {@link SectionViewport}s, and one with
|
|
406
|
+
* abscissa values projected back to world XY for 3-D viewports.
|
|
407
|
+
*
|
|
408
|
+
* **GPU-side visibility**
|
|
409
|
+
*
|
|
410
|
+
* {@link PolylineGroupLayerProps.hiddenGroups} and
|
|
411
|
+
* {@link PolylineGroupLayerProps.hiddenPolylines} use `DataFilterExtension`.
|
|
412
|
+
* Changing these sets triggers only a GPU attribute update; the flattened
|
|
413
|
+
* data buffer is never rebuilt.
|
|
414
|
+
*/
|
|
415
|
+
export class PolylineGroupLayer extends CompositeLayer {
|
|
416
|
+
/** @override Builds the initial flat data buffer and section index from `props.data`. */
|
|
417
|
+
initializeState() {
|
|
418
|
+
const { data, sectionPath, hiddenGroups } = this.props;
|
|
419
|
+
const { flatData, binaryData } = flattenGroupData(data, this.props);
|
|
420
|
+
const binaryLayerData = binaryData
|
|
421
|
+
? buildBinaryLayerData(binaryData, buildFilterValues(binaryData, hiddenGroups))
|
|
422
|
+
: null;
|
|
423
|
+
this.setState({
|
|
424
|
+
flatData,
|
|
425
|
+
binaryData,
|
|
426
|
+
binaryLayerData,
|
|
427
|
+
sectionIndex: sectionPath ? buildSectionIndex(sectionPath) : null,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* @override
|
|
432
|
+
* Rebuilds the flat data buffer when `data` or any accessor/default prop changes.
|
|
433
|
+
* Rebuilds the section index when `sectionPath` changes.
|
|
434
|
+
*/
|
|
435
|
+
updateState({ props, oldProps, }) {
|
|
436
|
+
const needsRebuild = props.data !== oldProps.data ||
|
|
437
|
+
props.getGroupColor !== oldProps.getGroupColor ||
|
|
438
|
+
props.getGroupWidth !== oldProps.getGroupWidth ||
|
|
439
|
+
props.getGroupDashArray !== oldProps.getGroupDashArray ||
|
|
440
|
+
props.getPolylineColor !== oldProps.getPolylineColor ||
|
|
441
|
+
props.getPolylineWidth !== oldProps.getPolylineWidth ||
|
|
442
|
+
props.getPolylineDashArray !== oldProps.getPolylineDashArray ||
|
|
443
|
+
props.getGroupPolylines !== oldProps.getGroupPolylines ||
|
|
444
|
+
props.getPolylinePath !== oldProps.getPolylinePath ||
|
|
445
|
+
!_.isEqual(props.defaultGroupStyle, oldProps.defaultGroupStyle) ||
|
|
446
|
+
props.ZIncreasingDownwards !== oldProps.ZIncreasingDownwards;
|
|
447
|
+
if (needsRebuild) {
|
|
448
|
+
const { flatData, binaryData } = flattenGroupData(props.data, props);
|
|
449
|
+
const binaryLayerData = binaryData
|
|
450
|
+
? buildBinaryLayerData(binaryData, buildFilterValues(binaryData, props.hiddenGroups))
|
|
451
|
+
: null;
|
|
452
|
+
this.setState({ flatData, binaryData, binaryLayerData });
|
|
453
|
+
}
|
|
454
|
+
else if (props.hiddenGroups !== oldProps.hiddenGroups) {
|
|
455
|
+
// Only the per-vertex filter buffer needs rebuilding; the large
|
|
456
|
+
// position/color/width buffers are untouched.
|
|
457
|
+
const binaryData = this.state["binaryData"];
|
|
458
|
+
if (binaryData) {
|
|
459
|
+
this.setState({
|
|
460
|
+
binaryLayerData: buildBinaryLayerData(binaryData, buildFilterValues(binaryData, props.hiddenGroups)),
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (props.sectionPath !== oldProps.sectionPath) {
|
|
465
|
+
this.setState({
|
|
466
|
+
sectionIndex: props.sectionPath
|
|
467
|
+
? buildSectionIndex(props.sectionPath)
|
|
468
|
+
: null,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* @override
|
|
474
|
+
* Routes the `paths-section` sub-layer to {@link SectionViewport}s and
|
|
475
|
+
* the `paths-3d` sub-layer to all other viewports.
|
|
476
|
+
* Only active when `sectionPath` is set; otherwise the single `paths` sub-layer
|
|
477
|
+
* is always visible.
|
|
478
|
+
*/
|
|
479
|
+
filterSubLayer({ layer, viewport }) {
|
|
480
|
+
const isSV = viewport.constructor === SectionViewport;
|
|
481
|
+
if (layer.id.endsWith("-paths-section"))
|
|
482
|
+
return isSV;
|
|
483
|
+
if (layer.id.endsWith("-paths-3d"))
|
|
484
|
+
return !isSV;
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* @override
|
|
489
|
+
* Returns one or more `PathLayer` sub-layers.
|
|
490
|
+
* - Without `sectionPath`: a single `paths` sub-layer for non-binary groups,
|
|
491
|
+
* plus a single `paths-binary` sub-layer that merges all binary groups into
|
|
492
|
+
* combined typed-array buffers and passes them straight to the GPU.
|
|
493
|
+
* - With `sectionPath`: a `paths-section` sub-layer (abscissa/depth space)
|
|
494
|
+
* and a `paths-3d` sub-layer (world XY space), routed by {@link filterSubLayer}.
|
|
495
|
+
* Binary groups are not supported in section mode and are skipped with a warning.
|
|
496
|
+
*/
|
|
497
|
+
renderLayers() {
|
|
498
|
+
const flatData = this.state["flatData"];
|
|
499
|
+
const binaryData = this.state["binaryData"];
|
|
500
|
+
const sectionIndex = this.state["sectionIndex"];
|
|
501
|
+
const { widthUnits, widthScale, widthMinPixels, widthMaxPixels, jointRounded, capRounded, miterLimit, billboard, pickable, depthTest, ZIncreasingDownwards, hiddenGroups, hiddenPolylines, highPrecisionDash, } = this.props;
|
|
502
|
+
// Shared props for all sub-layers
|
|
503
|
+
const sharedProps = {
|
|
504
|
+
data: flatData,
|
|
505
|
+
pickable,
|
|
506
|
+
billboard,
|
|
507
|
+
widthUnits,
|
|
508
|
+
widthScale,
|
|
509
|
+
widthMinPixels,
|
|
510
|
+
widthMaxPixels,
|
|
511
|
+
jointRounded,
|
|
512
|
+
capRounded,
|
|
513
|
+
miterLimit,
|
|
514
|
+
parameters: { depthTest },
|
|
515
|
+
extensions: [
|
|
516
|
+
new DataFilterExtension({ filterSize: 1 }),
|
|
517
|
+
new PathStyleExtension({
|
|
518
|
+
dash: true,
|
|
519
|
+
highPrecisionDash: highPrecisionDash !== null && highPrecisionDash !== void 0 ? highPrecisionDash : false,
|
|
520
|
+
}),
|
|
521
|
+
],
|
|
522
|
+
getFilterValue: (d) => {
|
|
523
|
+
var _a;
|
|
524
|
+
if (d._group.id != null && (hiddenGroups === null || hiddenGroups === void 0 ? void 0 : hiddenGroups.has(d._group.id)))
|
|
525
|
+
return 0;
|
|
526
|
+
if (((_a = d._polyline) === null || _a === void 0 ? void 0 : _a.id) != null &&
|
|
527
|
+
(hiddenPolylines === null || hiddenPolylines === void 0 ? void 0 : hiddenPolylines.has(d._polyline.id)))
|
|
528
|
+
return 0;
|
|
529
|
+
return 1;
|
|
530
|
+
},
|
|
531
|
+
filterRange: [1, 1],
|
|
532
|
+
getColor: (d) => d.color,
|
|
533
|
+
getWidth: (d) => d.width,
|
|
534
|
+
getDashArray: (d) => d.dashArray,
|
|
535
|
+
};
|
|
536
|
+
const updateTriggers = {
|
|
537
|
+
getFilterValue: [hiddenGroups, hiddenPolylines],
|
|
538
|
+
getColor: [flatData],
|
|
539
|
+
getWidth: [flatData],
|
|
540
|
+
getDashArray: [flatData],
|
|
541
|
+
getPath: [ZIncreasingDownwards],
|
|
542
|
+
};
|
|
543
|
+
const layers = [];
|
|
544
|
+
if (sectionIndex) {
|
|
545
|
+
if (binaryData) {
|
|
546
|
+
console.warn("PolylineGroupLayer: BinaryPolylines are not supported when sectionPath is set; skipping binary groups.");
|
|
547
|
+
}
|
|
548
|
+
// Two separate sub-layers — one per coordinate system — so each has
|
|
549
|
+
// its own GPU attribute buffer. filterSubLayer() routes each to the
|
|
550
|
+
// appropriate viewport type (SectionViewport vs. 3D).
|
|
551
|
+
const sectionUpdateTriggers = Object.assign(Object.assign({}, updateTriggers), { getPath: [...updateTriggers.getPath, sectionIndex] });
|
|
552
|
+
layers.push(new PathLayer(this.getSubLayerProps(Object.assign(Object.assign({}, sharedProps), { id: "paths-section", getPath: (d) => d.path.map((pt) => {
|
|
553
|
+
const z = ZIncreasingDownwards ? -pt[1] : pt[1];
|
|
554
|
+
return [pt[0], z, 0];
|
|
555
|
+
}), updateTriggers: sectionUpdateTriggers }))), new PathLayer(this.getSubLayerProps(Object.assign(Object.assign({}, sharedProps), { id: "paths-3d", getPath: (d) => d.path.map((pt) => {
|
|
556
|
+
const z = ZIncreasingDownwards ? -pt[1] : pt[1];
|
|
557
|
+
const [wx, wy] = projectAbscissa(pt[0], sectionIndex);
|
|
558
|
+
return [wx, wy, z];
|
|
559
|
+
}), updateTriggers: sectionUpdateTriggers }))));
|
|
560
|
+
return layers;
|
|
561
|
+
}
|
|
562
|
+
if (flatData.length > 0) {
|
|
563
|
+
layers.push(new PathLayer(this.getSubLayerProps(Object.assign(Object.assign({}, sharedProps), { id: "paths", getPath: (d) => {
|
|
564
|
+
if (!ZIncreasingDownwards)
|
|
565
|
+
return d.path;
|
|
566
|
+
return d.path.map(([x, y, z]) => [x, y, -(z !== null && z !== void 0 ? z : 0)]);
|
|
567
|
+
}, updateTriggers }))));
|
|
568
|
+
}
|
|
569
|
+
const binaryLayerData = this.state["binaryLayerData"];
|
|
570
|
+
if (binaryLayerData) {
|
|
571
|
+
layers.push(new PathLayer(this.getSubLayerProps(Object.assign(Object.assign({ id: "paths-binary" }, sharedProps), { extensions: [
|
|
572
|
+
new DataFilterExtension({ filterSize: 1 }),
|
|
573
|
+
], data: binaryLayerData,
|
|
574
|
+
// Experimental deck.gl prop: tells PathLayer the paths
|
|
575
|
+
// are open (not closed rings), skipping the closure
|
|
576
|
+
// check. Safe here since BinaryPolylines are never
|
|
577
|
+
// closed. Re-evaluate if deck.gl removes/renames this.
|
|
578
|
+
_pathType: "open", getColor: undefined, getWidth: undefined, getFilterValue: undefined, getDashArray: undefined }))));
|
|
579
|
+
}
|
|
580
|
+
return layers;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* @override
|
|
584
|
+
* Enriches the pick result with the originating {@link PolylineGroup} (`info.group`),
|
|
585
|
+
* the root {@link Polyline} (`info.object`), and layer properties for display
|
|
586
|
+
* (group name, polyline id, depth at the picked coordinate).
|
|
587
|
+
*/
|
|
588
|
+
getPickingInfo({ info }) {
|
|
589
|
+
var _a, _b;
|
|
590
|
+
if (info.index < 0)
|
|
591
|
+
return info;
|
|
592
|
+
// Resolve the source group/polyline. For Polyline[] groups the picked
|
|
593
|
+
// FlatEntry carries them directly. For BinaryPolylines the sub-layer id
|
|
594
|
+
// (`paths-binary`) is used to look up the source group; the polyline
|
|
595
|
+
// is unavailable.
|
|
596
|
+
let pickedGroup;
|
|
597
|
+
let pickedPolyline;
|
|
598
|
+
let pickedSource;
|
|
599
|
+
const entry = info.object;
|
|
600
|
+
if (entry && entry._group) {
|
|
601
|
+
pickedGroup = entry._group;
|
|
602
|
+
pickedPolyline = entry._polyline;
|
|
603
|
+
pickedSource = entry;
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const sourceId = (_a = info.sourceLayer) === null || _a === void 0 ? void 0 : _a.id;
|
|
607
|
+
// Robust check: deck.gl may prefix/suffix the sub-layer id.
|
|
608
|
+
if (sourceId && sourceId.indexOf("paths-binary") !== -1) {
|
|
609
|
+
const binaryData = this.state["binaryData"];
|
|
610
|
+
const group = binaryData === null || binaryData === void 0 ? void 0 : binaryData.pathGroup[info.index];
|
|
611
|
+
if (group) {
|
|
612
|
+
pickedGroup = group;
|
|
613
|
+
pickedSource = group;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (!pickedSource)
|
|
618
|
+
return info;
|
|
619
|
+
const layerProperties = [];
|
|
620
|
+
if (pickedGroup === null || pickedGroup === void 0 ? void 0 : pickedGroup.name) {
|
|
621
|
+
layerProperties.push(createPropertyData("Group", pickedGroup.name));
|
|
622
|
+
}
|
|
623
|
+
if ((pickedPolyline === null || pickedPolyline === void 0 ? void 0 : pickedPolyline.id) != null) {
|
|
624
|
+
layerProperties.push(createPropertyData("Polyline", String(pickedPolyline.id)));
|
|
625
|
+
}
|
|
626
|
+
const zScale = this.props.modelMatrix ? this.props.modelMatrix[10] : 1;
|
|
627
|
+
// Omit Depth property when the pick originated from a SectionView.
|
|
628
|
+
const viewport = info
|
|
629
|
+
.viewport;
|
|
630
|
+
const isSectionView = (viewport === null || viewport === void 0 ? void 0 : viewport.constructor) === SectionViewport;
|
|
631
|
+
if (!isSectionView && typeof ((_b = info.coordinate) === null || _b === void 0 ? void 0 : _b[2]) !== "undefined") {
|
|
632
|
+
const depth = (this.props.ZIncreasingDownwards
|
|
633
|
+
? -info.coordinate[2]
|
|
634
|
+
: info.coordinate[2]) / Math.max(0.001, zScale);
|
|
635
|
+
layerProperties.push(createPropertyData("Depth", depth));
|
|
636
|
+
}
|
|
637
|
+
return Object.assign(Object.assign({}, info), {
|
|
638
|
+
// Expose the original polyline (when available) and group for
|
|
639
|
+
// onHover/onClick handlers. For binary groups `object` falls back
|
|
640
|
+
// to the BinaryGroupEntry so consumers always receive something stable.
|
|
641
|
+
object: pickedPolyline !== null && pickedPolyline !== void 0 ? pickedPolyline : pickedSource,
|
|
642
|
+
// @ts-expect-error -- deck.gl PickingInfo doesn't type extra fields; consumers can cast
|
|
643
|
+
group: pickedGroup, properties: layerProperties });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
PolylineGroupLayer.layerName = "PolylineGroupLayer";
|
|
647
|
+
PolylineGroupLayer.defaultProps = defaultProps;
|
|
648
|
+
//# sourceMappingURL=polylineGroupLayer.js.map
|