@equinor/esv-intersection 3.0.8 → 3.0.10
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/axis.d.ts +1 -2
- package/dist/components/axis.d.ts.map +1 -1
- package/dist/control/ExtendedCurveInterpolator.d.ts.map +1 -1
- package/dist/control/IntersectionReferenceSystem.d.ts +2 -6
- package/dist/control/IntersectionReferenceSystem.d.ts.map +1 -1
- package/dist/control/LayerManager.d.ts +2 -2
- package/dist/control/LayerManager.d.ts.map +1 -1
- package/dist/control/MainController.d.ts +3 -3
- package/dist/control/MainController.d.ts.map +1 -1
- package/dist/control/ZoomPanHandler.d.ts +5 -4
- package/dist/control/ZoomPanHandler.d.ts.map +1 -1
- package/dist/control/interfaces.d.ts +3 -4
- package/dist/control/interfaces.d.ts.map +1 -1
- package/dist/control/overlay.d.ts +3 -3
- package/dist/control/overlay.d.ts.map +1 -1
- package/dist/datautils/colortable.d.ts +1 -1
- package/dist/datautils/colortable.d.ts.map +1 -1
- package/dist/datautils/findsample.d.ts.map +1 -1
- package/dist/datautils/schematicShapeGenerator.d.ts +3 -3
- package/dist/datautils/schematicShapeGenerator.d.ts.map +1 -1
- package/dist/datautils/seismicimage.d.ts.map +1 -1
- package/dist/datautils/trajectory.d.ts +1 -1
- package/dist/datautils/trajectory.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1373 -1272
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/interfaces.d.ts +1 -2
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/layers/CalloutCanvasLayer.d.ts +2 -2
- package/dist/layers/CalloutCanvasLayer.d.ts.map +1 -1
- package/dist/layers/CustomDisplayObjects/ComplexRopeGeometry.d.ts.map +1 -1
- package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.d.ts.map +1 -1
- package/dist/layers/GeomodelCanvasLayer.d.ts +1 -1
- package/dist/layers/GeomodelCanvasLayer.d.ts.map +1 -1
- package/dist/layers/GeomodelLabelsLayer.d.ts +4 -4
- package/dist/layers/GeomodelLabelsLayer.d.ts.map +1 -1
- package/dist/layers/GeomodelLayerV2.d.ts.map +1 -1
- package/dist/layers/GridLayer.d.ts.map +1 -1
- package/dist/layers/ImageCanvasLayer.d.ts +1 -1
- package/dist/layers/ImageCanvasLayer.d.ts.map +1 -1
- package/dist/layers/ReferenceLineLayer.d.ts +1 -2
- package/dist/layers/ReferenceLineLayer.d.ts.map +1 -1
- package/dist/layers/SchematicLayer.d.ts.map +1 -1
- package/dist/layers/WellborePathLayer.d.ts +1 -1
- package/dist/layers/WellborePathLayer.d.ts.map +1 -1
- package/dist/layers/base/CanvasLayer.d.ts +3 -3
- package/dist/layers/base/CanvasLayer.d.ts.map +1 -1
- package/dist/layers/base/HTMLLayer.d.ts +1 -1
- package/dist/layers/base/HTMLLayer.d.ts.map +1 -1
- package/dist/layers/base/Layer.d.ts +10 -10
- package/dist/layers/base/Layer.d.ts.map +1 -1
- package/dist/layers/base/PixiLayer.d.ts +4 -4
- package/dist/layers/base/PixiLayer.d.ts.map +1 -1
- package/dist/layers/base/SVGLayer.d.ts +1 -1
- package/dist/layers/base/SVGLayer.d.ts.map +1 -1
- package/dist/utils/arc-length.d.ts.map +1 -1
- package/dist/utils/color.d.ts.map +1 -1
- package/dist/utils/root-finder.d.ts +1 -1
- package/dist/utils/root-finder.d.ts.map +1 -1
- package/dist/utils/text.d.ts +2 -2
- package/dist/utils/text.d.ts.map +1 -1
- package/dist/utils/vectorUtils.d.ts.map +1 -1
- package/dist/vendor/pixi-dashed-line/index.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/components/axis.ts +23 -30
- package/src/control/ExtendedCurveInterpolator.ts +7 -7
- package/src/control/IntersectionReferenceSystem.ts +33 -41
- package/src/control/LayerManager.ts +33 -31
- package/src/control/MainController.ts +5 -7
- package/src/control/ZoomPanHandler.ts +50 -45
- package/src/control/interfaces.ts +3 -3
- package/src/control/overlay.ts +20 -16
- package/src/datautils/colortable.ts +4 -4
- package/src/datautils/findsample.ts +8 -7
- package/src/datautils/picks.ts +13 -13
- package/src/datautils/schematicShapeGenerator.ts +47 -17
- package/src/datautils/seismicimage.ts +12 -13
- package/src/datautils/surfacedata.ts +27 -26
- package/src/datautils/trajectory.ts +34 -32
- package/src/interfaces.ts +1 -1
- package/src/layers/CalloutCanvasLayer.ts +55 -58
- package/src/layers/CustomDisplayObjects/ComplexRopeGeometry.ts +23 -20
- package/src/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.ts +9 -10
- package/src/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.ts +13 -13
- package/src/layers/GeomodelCanvasLayer.ts +43 -36
- package/src/layers/GeomodelLabelsLayer.ts +99 -96
- package/src/layers/GeomodelLayerV2.ts +11 -11
- package/src/layers/GridLayer.ts +25 -21
- package/src/layers/ImageCanvasLayer.ts +17 -11
- package/src/layers/ReferenceLineLayer.ts +53 -45
- package/src/layers/SchematicLayer.ts +78 -75
- package/src/layers/WellborePathLayer.ts +48 -46
- package/src/layers/base/CanvasLayer.ts +18 -18
- package/src/layers/base/HTMLLayer.ts +5 -5
- package/src/layers/base/Layer.ts +18 -18
- package/src/layers/base/PixiLayer.ts +24 -20
- package/src/layers/base/SVGLayer.ts +5 -5
- package/src/utils/arc-length.ts +3 -10
- package/src/utils/binary-search.ts +2 -2
- package/src/utils/color.ts +9 -5
- package/src/utils/root-finder.ts +4 -4
- package/src/utils/text.ts +5 -5
- package/src/utils/vectorUtils.ts +13 -6
- package/src/vendor/pixi-dashed-line/index.ts +3 -2
|
@@ -44,11 +44,11 @@ export function getSeismicInfo(data: { datapoints: number[][]; yAxisValues: numb
|
|
|
44
44
|
if (!(data && data.datapoints)) {
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
|
-
const minX = trajectory.reduce((acc: number, val: number[]) => Math.min(acc, val[0]), 0);
|
|
48
|
-
const maxX = trajectory.reduce((acc: number, val: number[]) => Math.max(acc, val[0]), 0);
|
|
47
|
+
const minX = trajectory.reduce((acc: number, val: number[]) => Math.min(acc, val[0]!), 0);
|
|
48
|
+
const maxX = trajectory.reduce((acc: number, val: number[]) => Math.max(acc, val[0]!), 0);
|
|
49
49
|
|
|
50
|
-
const minTvdMsl = data.yAxisValues && data.yAxisValues[0]
|
|
51
|
-
const maxTvdMsl = data.yAxisValues && data.yAxisValues[data.yAxisValues.length - 1]
|
|
50
|
+
const minTvdMsl = data.yAxisValues && data.yAxisValues[0]!;
|
|
51
|
+
const maxTvdMsl = data.yAxisValues && data.yAxisValues[data.yAxisValues.length - 1]!;
|
|
52
52
|
|
|
53
53
|
// Find value domain
|
|
54
54
|
const dp = data.datapoints || [];
|
|
@@ -117,8 +117,7 @@ export async function generateSeismicSliceImage(
|
|
|
117
117
|
difference: dmax - dmin,
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
const length = trajectory[0][0] - trajectory[trajectory.length - 1][0]
|
|
121
|
-
// eslint-disable-next-line no-magic-numbers
|
|
120
|
+
const length = trajectory[0]?.[0]! - trajectory[trajectory.length - 1]?.[0]!;
|
|
122
121
|
const width = Math.abs(Math.floor(length / 5));
|
|
123
122
|
const height = data.yAxisValues.length;
|
|
124
123
|
|
|
@@ -132,7 +131,7 @@ export async function generateSeismicSliceImage(
|
|
|
132
131
|
let offset = 0;
|
|
133
132
|
const colorFactor = (colorTableSize - 1) / domain.difference;
|
|
134
133
|
|
|
135
|
-
let pos = options?.isLeftToRight
|
|
134
|
+
let pos = options?.isLeftToRight ? trajectory[0]?.[0]! : trajectory[trajectory.length - 1]?.[0]!;
|
|
136
135
|
|
|
137
136
|
const step = (length / width) * (options?.isLeftToRight ? -1 : 1);
|
|
138
137
|
|
|
@@ -147,15 +146,15 @@ export async function generateSeismicSliceImage(
|
|
|
147
146
|
for (let x = 0; x < width; x++) {
|
|
148
147
|
offset = x * 4;
|
|
149
148
|
const index = findIndexOfSample(trajectory, pos);
|
|
150
|
-
const x1 = trajectory[index][0]
|
|
151
|
-
const x2 = trajectory[index + 1][0]
|
|
149
|
+
const x1 = trajectory[index]?.[0]!;
|
|
150
|
+
const x2 = trajectory[index + 1]?.[0]!;
|
|
152
151
|
const span = x2 - x1;
|
|
153
152
|
const dx = pos - x1;
|
|
154
153
|
const ratio = dx / span;
|
|
155
154
|
|
|
156
155
|
for (let y = 0; y < height; y++) {
|
|
157
|
-
val1 = dp[y][index]
|
|
158
|
-
val2 = dp[y][index + 1]
|
|
156
|
+
val1 = dp[y]?.[index]!;
|
|
157
|
+
val2 = dp[y]?.[index + 1]!;
|
|
159
158
|
if (val1 == null || val2 == null) {
|
|
160
159
|
col = black;
|
|
161
160
|
opacity = 0;
|
|
@@ -163,11 +162,11 @@ export async function generateSeismicSliceImage(
|
|
|
163
162
|
val = val1 * (1 - ratio) + val2 * ratio;
|
|
164
163
|
i = (val - domain.min) * colorFactor;
|
|
165
164
|
i = clamp(~~i, 0, colorTableSize - 1);
|
|
166
|
-
col = colorTable[i]
|
|
165
|
+
col = colorTable[i]!;
|
|
167
166
|
opacity = 255;
|
|
168
167
|
}
|
|
169
168
|
|
|
170
|
-
d.set([col[0]
|
|
169
|
+
d.set([col[0]!, col[1]!, col[2]!, opacity], offset);
|
|
171
170
|
|
|
172
171
|
offset += width * 4;
|
|
173
172
|
}
|
|
@@ -91,7 +91,7 @@ function getSurfaceLines(mappedSurfaces: MappedSurfaces[], trajectory: number[][
|
|
|
91
91
|
label: l.name,
|
|
92
92
|
width: 2,
|
|
93
93
|
color: convertColor(l.color || 'black'),
|
|
94
|
-
data: trajectory.map((p, j) => [p[0]
|
|
94
|
+
data: trajectory.map((p, j) => [p[0]!, l.values[j]!]),
|
|
95
95
|
}));
|
|
96
96
|
|
|
97
97
|
return lines;
|
|
@@ -99,13 +99,14 @@ function getSurfaceLines(mappedSurfaces: MappedSurfaces[], trajectory: number[][
|
|
|
99
99
|
|
|
100
100
|
function generateGroupAreas(groups: MappedGroup[], trajectory: number[][]): SurfaceArea[] {
|
|
101
101
|
const groupAreas = groups.map((g: MappedGroup, i: number) => {
|
|
102
|
-
const next: MappedGroup | null = i + 1 < groups.length ? groups[i + 1] : null;
|
|
102
|
+
const next: MappedGroup | null = i + 1 < groups.length ? groups[i + 1]! : null;
|
|
103
103
|
return {
|
|
104
104
|
id: g.id,
|
|
105
105
|
color: convertColor(g.color),
|
|
106
|
-
data: trajectory.map((p: number[], j: number) => [p[0]
|
|
106
|
+
data: trajectory.map((p: number[], j: number) => [p[0]!, g.top[j]!, ...(next ? [next.top[j]!] : [])]),
|
|
107
107
|
};
|
|
108
108
|
});
|
|
109
|
+
|
|
109
110
|
return groupAreas;
|
|
110
111
|
}
|
|
111
112
|
|
|
@@ -113,7 +114,7 @@ function mapGroups(stratGroups: Map<string, StratGroup>, surfaceAreas: SurfaceAr
|
|
|
113
114
|
const groups = Array.from(stratGroups.values())
|
|
114
115
|
.sort((a: StratGroup, b: StratGroup) => a.age - b.age)
|
|
115
116
|
.filter((g: StratGroup) => {
|
|
116
|
-
const surfaces
|
|
117
|
+
const surfaces = surfaceAreas[g.name];
|
|
117
118
|
const isValid = surfaces && surfaces.length > 0;
|
|
118
119
|
if (!isValid) {
|
|
119
120
|
console.warn(`Intersection surface group '${g.name}' has no valid entries and will be discarded.`);
|
|
@@ -121,13 +122,13 @@ function mapGroups(stratGroups: Map<string, StratGroup>, surfaceAreas: SurfaceAr
|
|
|
121
122
|
return isValid;
|
|
122
123
|
})
|
|
123
124
|
.map((g: StratGroup, i: number) => {
|
|
124
|
-
const surface
|
|
125
|
-
const top = surface[0]
|
|
125
|
+
const surface = surfaceAreas[g.name]!;
|
|
126
|
+
const top = surface[0]!;
|
|
126
127
|
return {
|
|
127
128
|
id: g.name,
|
|
128
129
|
label: g.name,
|
|
129
130
|
color: unassignedColorScale(i),
|
|
130
|
-
top: top.data.map((d: number[]) => d[1]),
|
|
131
|
+
top: top.data.map((d: number[]) => d[1]!),
|
|
131
132
|
};
|
|
132
133
|
});
|
|
133
134
|
return groups;
|
|
@@ -144,11 +145,11 @@ function combineSurfacesAndStratColumn(
|
|
|
144
145
|
.filter((d: MappedSurfaces) => d.visualization === 'interval' || d.visualization === 'none')
|
|
145
146
|
.map((s: MappedSurfaces) => {
|
|
146
147
|
const path: StratUnit[] = [];
|
|
147
|
-
const stratUnit
|
|
148
|
+
const stratUnit = findStratcolumnUnit(stratColumn, s.name, path);
|
|
148
149
|
if (!stratUnit) {
|
|
149
150
|
console.warn(`No match for ${s.name} in strat column`);
|
|
150
151
|
}
|
|
151
|
-
const group
|
|
152
|
+
const group = path[0]! || stratUnit;
|
|
152
153
|
const groupName: string = (group && group.identifier) || defaultGroupName;
|
|
153
154
|
if (group && !stratGroups.has(groupName)) {
|
|
154
155
|
stratGroups.set(groupName, {
|
|
@@ -158,7 +159,7 @@ function combineSurfacesAndStratColumn(
|
|
|
158
159
|
}
|
|
159
160
|
return {
|
|
160
161
|
...s,
|
|
161
|
-
unit: stratUnit
|
|
162
|
+
unit: stratUnit!,
|
|
162
163
|
group: groupName,
|
|
163
164
|
};
|
|
164
165
|
});
|
|
@@ -201,13 +202,13 @@ function sortStratigraphies(stratigrafies: Stratigraphy[]): void {
|
|
|
201
202
|
* @param {[]} path
|
|
202
203
|
*/
|
|
203
204
|
function findStratcolumnUnit(units: StratUnit[], unitname: string, path: StratUnit[] = []): StratUnit | null {
|
|
204
|
-
const unit
|
|
205
|
+
const unit = units.find((u: StratUnit) => u.identifier.toLowerCase() === unitname.toLowerCase());
|
|
205
206
|
if (unit) {
|
|
206
207
|
// Build path
|
|
207
|
-
let temp: StratUnit = unit;
|
|
208
|
+
let temp: StratUnit | undefined = unit;
|
|
208
209
|
do {
|
|
209
210
|
path.unshift(temp);
|
|
210
|
-
temp = units.find((u: StratUnit) => u.identifier === temp
|
|
211
|
+
temp = units.find((u: StratUnit) => u.identifier === temp!.stratUnitParent);
|
|
211
212
|
} while (temp);
|
|
212
213
|
|
|
213
214
|
return unit;
|
|
@@ -247,11 +248,11 @@ const unassignedColorScale = scaleOrdinal<number, string>()
|
|
|
247
248
|
/**
|
|
248
249
|
* Find the best matching base index based on name or by values
|
|
249
250
|
*/
|
|
250
|
-
function findBestMatchingBaseIndex(top: Stratigraphy, index: number, surfaces: Stratigraphy[], stratColumn: StratUnit[]): number {
|
|
251
|
+
function findBestMatchingBaseIndex(top: Stratigraphy, index: number, surfaces: Stratigraphy[], stratColumn: StratUnit[]): number | undefined {
|
|
251
252
|
const nextIndex: number = index + 1;
|
|
252
253
|
|
|
253
254
|
if (!surfaces || nextIndex >= surfaces.length) {
|
|
254
|
-
return
|
|
255
|
+
return undefined;
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
// If there is a matching base by name, use that. More robust, does not rely on sorting
|
|
@@ -262,14 +263,14 @@ function findBestMatchingBaseIndex(top: Stratigraphy, index: number, surfaces: S
|
|
|
262
263
|
|
|
263
264
|
for (let i = nextIndex; i < surfaces.length; i++) {
|
|
264
265
|
const candidate = surfaces[i];
|
|
265
|
-
if (!candidate
|
|
266
|
+
if (!candidate?.isBase) {
|
|
266
267
|
return i;
|
|
267
268
|
}
|
|
268
269
|
if (isAnchestor(top, candidate, stratColumn)) {
|
|
269
270
|
return i;
|
|
270
271
|
}
|
|
271
272
|
}
|
|
272
|
-
return
|
|
273
|
+
return undefined;
|
|
273
274
|
}
|
|
274
275
|
|
|
275
276
|
function isAnchestor(descendant: Stratigraphy, candidate: Stratigraphy, stratColumn: StratUnit[]): boolean {
|
|
@@ -284,15 +285,15 @@ function generateSurfaceAreas(projection: number[][], surfaces: Stratigraphy[],
|
|
|
284
285
|
if (!acc[surface.group]) {
|
|
285
286
|
acc[surface.group] = [];
|
|
286
287
|
}
|
|
287
|
-
const baseIndex
|
|
288
|
-
acc[surface.group]
|
|
288
|
+
const baseIndex = findBestMatchingBaseIndex(surface, i, surfaces, stratColumn);
|
|
289
|
+
acc[surface.group]?.push({
|
|
289
290
|
id: surface.name,
|
|
290
291
|
label: surface.name,
|
|
291
292
|
color: (surface.unit && getColorFromUnit(surface.unit)) || WHITE,
|
|
292
293
|
exclude: surface.visualization === 'none' || !surface.unit,
|
|
293
294
|
data: projection.map((p, j) => {
|
|
294
|
-
const baseValue
|
|
295
|
-
return [p[0]
|
|
295
|
+
const baseValue = surface.values[j] != null ? getBaseValue(baseIndex, surfaces, j) : undefined;
|
|
296
|
+
return [p[0]!, surface.values[j]!, baseValue!];
|
|
296
297
|
}),
|
|
297
298
|
});
|
|
298
299
|
}
|
|
@@ -303,15 +304,15 @@ function generateSurfaceAreas(projection: number[][], surfaces: Stratigraphy[],
|
|
|
303
304
|
|
|
304
305
|
// get the value from the surface with the supplied index,
|
|
305
306
|
// iterate to next surface if value is null
|
|
306
|
-
function getBaseValue(index: number, surfaces: Stratigraphy[], datapoint: number): number {
|
|
307
|
+
function getBaseValue(index: number | undefined, surfaces: Stratigraphy[], datapoint: number): number | undefined {
|
|
307
308
|
if (!surfaces || !index || index >= surfaces.length) {
|
|
308
|
-
return
|
|
309
|
+
return undefined;
|
|
309
310
|
}
|
|
310
311
|
|
|
311
312
|
for (let i: number = index; i < surfaces.length; i++) {
|
|
312
|
-
if (surfaces[i]
|
|
313
|
-
return surfaces[i]
|
|
313
|
+
if (surfaces[i]?.values[datapoint] != null) {
|
|
314
|
+
return surfaces[i]?.values[datapoint];
|
|
314
315
|
}
|
|
315
316
|
}
|
|
316
|
-
return
|
|
317
|
+
return undefined;
|
|
317
318
|
}
|
|
@@ -15,19 +15,21 @@ const pathSteps = 10;
|
|
|
15
15
|
* Code originally developed for REP
|
|
16
16
|
* @param {[]} poslog Position log from SMDA
|
|
17
17
|
*/
|
|
18
|
-
export function generateProjectedWellborePath(poslog: SurveySample[]): number
|
|
18
|
+
export function generateProjectedWellborePath(poslog: SurveySample[]): [number, number][] {
|
|
19
19
|
if (!poslog || poslog.length === 0) {
|
|
20
20
|
return [];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const points: number
|
|
23
|
+
const points: [number, number, number, number][] = poslog ? poslog.map((p: SurveySample) => [p.easting, p.northing, p.tvd, p.md]) : [];
|
|
24
24
|
|
|
25
|
-
const projection
|
|
26
|
-
const offset
|
|
25
|
+
const projection = simplify(projectCurtain(points));
|
|
26
|
+
const offset = projection[projection.length - 1]?.[0];
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
projection
|
|
30
|
-
|
|
28
|
+
if (offset != null) {
|
|
29
|
+
projection.forEach((p, i) => {
|
|
30
|
+
projection[i]![0] = offset - p[0];
|
|
31
|
+
});
|
|
32
|
+
}
|
|
31
33
|
|
|
32
34
|
return projection;
|
|
33
35
|
}
|
|
@@ -43,25 +45,25 @@ export function generateProjectedTrajectory(poslog: SurveySample[], defaultInter
|
|
|
43
45
|
return [];
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
const points: number
|
|
48
|
+
const points: [number, number, number, number][] = poslog ? poslog.map((p) => [p.easting, p.northing, p.tvd, p.md]) : [];
|
|
47
49
|
|
|
48
50
|
const interpolator: CurveInterpolator = new CurveInterpolator(points, { tension: 0.75, arcDivisions: 5000 });
|
|
49
51
|
const displacement: number = interpolator.length;
|
|
50
52
|
|
|
51
53
|
const nPoints: number = Math.round(displacement * pathSteps);
|
|
52
|
-
let path: number
|
|
54
|
+
let path: [number, number][];
|
|
53
55
|
if (nPoints > 0) {
|
|
54
56
|
const maxOffset = 0.0005;
|
|
55
57
|
const maxDistance = 10;
|
|
56
58
|
path = simplify(interpolator.getPoints(nPoints), maxOffset, maxDistance);
|
|
57
59
|
} else {
|
|
58
|
-
path = [[points[0][0], points[0][1]]];
|
|
60
|
+
path = [[points[0]![0], points[0]![1]]];
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
const first
|
|
62
|
-
const last
|
|
63
|
+
const first = path[0]!;
|
|
64
|
+
const last = path[path.length - 1]!;
|
|
63
65
|
const relativeDist: number = Vector2.distance(first, last);
|
|
64
|
-
let v: Vector2
|
|
66
|
+
let v: Vector2;
|
|
65
67
|
|
|
66
68
|
if (relativeDist < thresholdRelativeDist) {
|
|
67
69
|
const oneEighty = 180;
|
|
@@ -72,9 +74,9 @@ export function generateProjectedTrajectory(poslog: SurveySample[], defaultInter
|
|
|
72
74
|
}
|
|
73
75
|
const extensionLengthStart: number = Math.max(0, extensionLength - displacement);
|
|
74
76
|
const offset: number = extensionLengthStart + displacement;
|
|
75
|
-
const trajectory: number
|
|
77
|
+
const trajectory: [number, number][] = [];
|
|
76
78
|
|
|
77
|
-
let firstPoints: number
|
|
79
|
+
let firstPoints: [number, number][] = [];
|
|
78
80
|
|
|
79
81
|
// Reference to initial vector
|
|
80
82
|
const initial: number[] = v.toArray();
|
|
@@ -93,7 +95,7 @@ export function generateProjectedTrajectory(poslog: SurveySample[], defaultInter
|
|
|
93
95
|
}
|
|
94
96
|
trajectory.push(...path);
|
|
95
97
|
|
|
96
|
-
const endPoints
|
|
98
|
+
const endPoints = seqI(Math.ceil(extensionLength * stepSize))
|
|
97
99
|
.map((t) =>
|
|
98
100
|
v
|
|
99
101
|
.set(initial)
|
|
@@ -105,7 +107,7 @@ export function generateProjectedTrajectory(poslog: SurveySample[], defaultInter
|
|
|
105
107
|
|
|
106
108
|
trajectory.push(...endPoints);
|
|
107
109
|
|
|
108
|
-
const projectedTrajectory: number[][] = projectCurtain(trajectory,
|
|
110
|
+
const projectedTrajectory: number[][] = projectCurtain(trajectory, undefined, offset);
|
|
109
111
|
|
|
110
112
|
return projectedTrajectory;
|
|
111
113
|
}
|
|
@@ -124,7 +126,7 @@ function getDirectionVector(path: number[][], threshold: number): Vector2 {
|
|
|
124
126
|
|
|
125
127
|
for (let i = 0; i < path.length - 1; i++) {
|
|
126
128
|
const index = path.length - 1 - i;
|
|
127
|
-
temp.set(path[index]).sub(path[index - 1]);
|
|
129
|
+
temp.set(path[index]!).sub(path[index - 1]!);
|
|
128
130
|
res.add(temp);
|
|
129
131
|
|
|
130
132
|
len = res.magnitude;
|
|
@@ -152,25 +154,25 @@ function getDirectionVector(path: number[][], threshold: number): Vector2 {
|
|
|
152
154
|
*
|
|
153
155
|
* @return {Number[]} Simplified array
|
|
154
156
|
*/
|
|
155
|
-
function simplify(inputArr: number
|
|
157
|
+
function simplify(inputArr: [number, number][], maxOffset = 0.001, maxDistance = 10): [number, number][] {
|
|
156
158
|
if (inputArr.length <= 4) {
|
|
157
159
|
return inputArr;
|
|
158
160
|
}
|
|
159
|
-
const [o0, o1] = inputArr[0]
|
|
160
|
-
const arr = inputArr.map((d) => [d[0] - o0, d[1] - o1]);
|
|
161
|
-
let [a0, a1] = arr[0]
|
|
162
|
-
const sim
|
|
161
|
+
const [o0, o1] = inputArr[0]!;
|
|
162
|
+
const arr = inputArr.map<[number, number]>((d) => [d[0]! - o0, d[1]! - o1]);
|
|
163
|
+
let [a0, a1] = arr[0]!;
|
|
164
|
+
const sim = [inputArr[0]!];
|
|
163
165
|
|
|
164
166
|
for (let i = 1; i + 1 < arr.length; i++) {
|
|
165
|
-
const [t0, t1] = arr[i];
|
|
166
|
-
const [b0, b1] = arr[i + 1];
|
|
167
|
+
const [t0, t1] = arr[i] ?? [];
|
|
168
|
+
const [b0, b1] = arr[i + 1] ?? [];
|
|
167
169
|
|
|
168
170
|
// If t->b vector is NOT [0, 0]
|
|
169
|
-
if (b0 - t0 !== 0 || b1 - t1 !== 0) {
|
|
171
|
+
if (t0 != null && t1 != null && b0 != null && b1 != null && (b0 - t0 !== 0 || b1 - t1 !== 0)) {
|
|
170
172
|
// Proximity check
|
|
171
173
|
const proximity: number = Math.abs(a0 * b1 - a1 * b0 + b0 * t1 - b1 * t0 + a1 * t0 - a0 * t1) / Math.sqrt((b0 - a0) ** 2 + (b1 - a1) ** 2);
|
|
172
174
|
|
|
173
|
-
const dir: number
|
|
175
|
+
const dir: [number, number] = [a0 - t0, a1 - t1];
|
|
174
176
|
const len: number = Math.sqrt(dir[0] ** 2 + dir[1] ** 2);
|
|
175
177
|
|
|
176
178
|
if (proximity > maxOffset || len >= maxDistance) {
|
|
@@ -179,7 +181,7 @@ function simplify(inputArr: number[][], maxOffset = 0.001, maxDistance = 10): nu
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
|
-
const last
|
|
184
|
+
const last = arr[arr.length - 1]!;
|
|
183
185
|
sim.push([last[0] + o0, last[1] + o1]);
|
|
184
186
|
|
|
185
187
|
return sim;
|
|
@@ -192,15 +194,15 @@ function simplify(inputArr: number[][], maxOffset = 0.001, maxDistance = 10): nu
|
|
|
192
194
|
* @param offset
|
|
193
195
|
* @returns {array}
|
|
194
196
|
*/
|
|
195
|
-
function projectCurtain(points: number[][], origin
|
|
196
|
-
let p0
|
|
197
|
+
function projectCurtain(points: [number, number, ...number[]][], origin?: [number, number, number, number], offset = 0): [number, number][] {
|
|
198
|
+
let p0 = origin || points[0]!;
|
|
197
199
|
let l = 0;
|
|
198
|
-
const projected = points.map((p1
|
|
200
|
+
const projected = points.map<[number, number]>((p1) => {
|
|
199
201
|
const dx = p1[0] - p0[0];
|
|
200
202
|
const dy = p1[1] - p0[1];
|
|
201
203
|
l += Math.sqrt(dx ** 2 + dy ** 2);
|
|
202
204
|
p0 = p1;
|
|
203
|
-
return [offset > 0 ? offset - l : l, p1[2]
|
|
205
|
+
return [offset > 0 ? offset - l : l, p1[2] ?? 0];
|
|
204
206
|
});
|
|
205
207
|
return projected;
|
|
206
208
|
}
|
package/src/interfaces.ts
CHANGED
|
@@ -47,10 +47,10 @@ export interface CalloutOptions<T extends Annotation[]> extends LayerOptions<T>
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
50
|
-
rescaleEvent: OnRescaleEvent;
|
|
51
|
-
xRatio: number;
|
|
52
|
-
callouts: Callout[];
|
|
53
|
-
groupFilter: string[] =
|
|
50
|
+
rescaleEvent: OnRescaleEvent | undefined;
|
|
51
|
+
xRatio: number | undefined;
|
|
52
|
+
callouts: Callout[] = [];
|
|
53
|
+
groupFilter: string[] = [];
|
|
54
54
|
minFontSize: number;
|
|
55
55
|
maxFontSize: number;
|
|
56
56
|
fontSizeFactor: number;
|
|
@@ -60,25 +60,23 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
60
60
|
|
|
61
61
|
constructor(id?: string, options?: CalloutOptions<T>) {
|
|
62
62
|
super(id, options);
|
|
63
|
-
this.minFontSize = options
|
|
64
|
-
this.maxFontSize = options
|
|
65
|
-
this.fontSizeFactor = options
|
|
66
|
-
this.offsetMin = options
|
|
67
|
-
this.offsetMax = options
|
|
68
|
-
this.offsetFactor = options
|
|
63
|
+
this.minFontSize = options?.minFontSize || DEFAULT_MIN_FONT_SIZE;
|
|
64
|
+
this.maxFontSize = options?.maxFontSize || DEFAULT_MAX_FONT_SIZE;
|
|
65
|
+
this.fontSizeFactor = options?.fontSizeFactor || DEFAULT_FONT_SIZE_FACTOR;
|
|
66
|
+
this.offsetMin = options?.offsetMin || DEFAULT_OFFSET_MIN;
|
|
67
|
+
this.offsetMax = options?.offsetMax || DEFAULT_OFFSET_MAX;
|
|
68
|
+
this.offsetFactor = options?.offsetFactor || DEFAULT_OFFSET_FACTOR;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
setGroupFilter(filter: string[]): void {
|
|
72
72
|
this.groupFilter = filter;
|
|
73
|
-
this.callouts =
|
|
73
|
+
this.callouts = [];
|
|
74
74
|
this.render();
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
override onUpdate(event: OnUpdateEvent<T>): void {
|
|
78
78
|
super.onUpdate(event);
|
|
79
|
-
|
|
80
|
-
this.callouts = undefined;
|
|
81
|
-
|
|
79
|
+
this.callouts = [];
|
|
82
80
|
this.render();
|
|
83
81
|
}
|
|
84
82
|
|
|
@@ -102,14 +100,14 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
102
100
|
|
|
103
101
|
const fontSize = calcSize(this.fontSizeFactor, this.minFontSize, this.maxFontSize, xScale);
|
|
104
102
|
|
|
105
|
-
if (!isPanning ||
|
|
103
|
+
if (!isPanning || this.callouts.length <= 0) {
|
|
106
104
|
const { data, ctx, groupFilter } = this;
|
|
107
105
|
const { calculateDisplacementFromBottom } = this.referenceSystem.options;
|
|
108
106
|
const isLeftToRight = calculateDisplacementFromBottom ? xBounds[0] < xBounds[1] : xBounds[0] > xBounds[1];
|
|
109
107
|
const scale = 0;
|
|
110
108
|
|
|
111
|
-
ctx.font = `bold ${fontSize}px arial
|
|
112
|
-
const filtered = data.filter((d: Annotation) =>
|
|
109
|
+
ctx != null && (ctx.font = `bold ${fontSize}px arial`);
|
|
110
|
+
const filtered = data.filter((d: Annotation) => groupFilter.length <= 0 || groupFilter.includes(d.group));
|
|
113
111
|
const offset = calcSize(this.offsetFactor, this.offsetMin, this.offsetMax, xScale);
|
|
114
112
|
this.callouts = this.positionCallouts(filtered, isLeftToRight, xScale, yScale, scale, fontSize, offset);
|
|
115
113
|
}
|
|
@@ -138,27 +136,24 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
138
136
|
this.renderText(label, x, y, fontSize, color);
|
|
139
137
|
};
|
|
140
138
|
|
|
141
|
-
private renderText(
|
|
142
|
-
title: string,
|
|
143
|
-
x: number,
|
|
144
|
-
y: number,
|
|
145
|
-
fontSize: number,
|
|
146
|
-
color: string,
|
|
147
|
-
font: string = 'arial',
|
|
148
|
-
fontStyle: string = 'normal',
|
|
149
|
-
): void {
|
|
139
|
+
private renderText(title: string, x: number, y: number, fontSize: number, color: string, font = 'arial', fontStyle = 'normal'): void {
|
|
150
140
|
const { ctx } = this;
|
|
151
|
-
ctx
|
|
152
|
-
|
|
153
|
-
|
|
141
|
+
if (ctx != null) {
|
|
142
|
+
ctx.font = `${fontStyle} ${fontSize}px ${font}`;
|
|
143
|
+
ctx.fillStyle = color;
|
|
144
|
+
ctx.fillText(title, x, y);
|
|
145
|
+
}
|
|
154
146
|
}
|
|
155
147
|
|
|
156
|
-
private renderPoint(x: number, y: number, radius
|
|
148
|
+
private renderPoint(x: number, y: number, radius = 3): void {
|
|
157
149
|
const { ctx } = this;
|
|
158
|
-
|
|
159
|
-
ctx
|
|
160
|
-
|
|
161
|
-
|
|
150
|
+
|
|
151
|
+
if (ctx != null) {
|
|
152
|
+
ctx.beginPath();
|
|
153
|
+
ctx.moveTo(x, y);
|
|
154
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
155
|
+
ctx.fill();
|
|
156
|
+
}
|
|
162
157
|
}
|
|
163
158
|
|
|
164
159
|
private renderCallout(title: string, label: string, boundingBox: BoundingBox, color: string, location: string): void {
|
|
@@ -172,25 +167,27 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
172
167
|
this.renderLine(x, y, width, dotX, dotY, color, placeLeft);
|
|
173
168
|
}
|
|
174
169
|
|
|
175
|
-
private renderLine = (x: number, y: number, width: number, dotX: number, dotY: number, color: string, placeLeft
|
|
170
|
+
private renderLine = (x: number, y: number, width: number, dotX: number, dotY: number, color: string, placeLeft = true): void => {
|
|
176
171
|
const { ctx } = this;
|
|
177
172
|
const textX = placeLeft ? x : x + width;
|
|
178
173
|
const inverseTextX = placeLeft ? x + width : x;
|
|
179
174
|
const textY = y + 2;
|
|
180
175
|
|
|
181
|
-
ctx
|
|
182
|
-
|
|
176
|
+
if (ctx != null) {
|
|
177
|
+
ctx.strokeStyle = color;
|
|
178
|
+
ctx.lineWidth = 1;
|
|
183
179
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
180
|
+
ctx.beginPath();
|
|
181
|
+
ctx.moveTo(dotX, dotY);
|
|
182
|
+
ctx.lineTo(textX, textY);
|
|
183
|
+
ctx.lineTo(inverseTextX, textY);
|
|
188
184
|
|
|
189
|
-
|
|
185
|
+
ctx.stroke();
|
|
186
|
+
}
|
|
190
187
|
};
|
|
191
188
|
|
|
192
189
|
private getPosition(boundingBox: BoundingBox, location: string): Point {
|
|
193
|
-
const { x, y, offsetX, offsetY, width } = boundingBox;
|
|
190
|
+
const { x, y, offsetX = 0, offsetY = 0, width } = boundingBox;
|
|
194
191
|
switch (location) {
|
|
195
192
|
case Location.topleft:
|
|
196
193
|
return {
|
|
@@ -228,7 +225,7 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
228
225
|
yScale: ScaleLinear<number, number>,
|
|
229
226
|
_scale: number,
|
|
230
227
|
fontSize: number,
|
|
231
|
-
offset
|
|
228
|
+
offset = 20,
|
|
232
229
|
): Callout[] {
|
|
233
230
|
if (annotations.length === 0) {
|
|
234
231
|
return [];
|
|
@@ -236,12 +233,12 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
236
233
|
const alignment = isLeftToRight ? Location.topleft : Location.topright;
|
|
237
234
|
|
|
238
235
|
const nodes = annotations.map((a) => {
|
|
239
|
-
const pos = a.pos ? a.pos : this.referenceSystem
|
|
236
|
+
const pos = a.pos ? a.pos : this.referenceSystem?.project(a.md!)!;
|
|
240
237
|
return {
|
|
241
238
|
title: a.title,
|
|
242
239
|
label: a.label,
|
|
243
240
|
color: a.color,
|
|
244
|
-
pos: { x: pos[0]
|
|
241
|
+
pos: { x: pos?.[0]!, y: pos?.[1]! },
|
|
245
242
|
group: a.group,
|
|
246
243
|
alignment,
|
|
247
244
|
boundingBox: this.getAnnotationBoundingBox(a.title, a.label, pos, xScale, yScale, fontSize),
|
|
@@ -250,7 +247,7 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
250
247
|
};
|
|
251
248
|
});
|
|
252
249
|
|
|
253
|
-
const top = [nodes[nodes.length - 1]];
|
|
250
|
+
const top = [nodes[nodes.length - 1]!];
|
|
254
251
|
const bottom: Callout[] = [];
|
|
255
252
|
|
|
256
253
|
// Initial best effort
|
|
@@ -274,11 +271,11 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
274
271
|
height: number,
|
|
275
272
|
): { x: number; y: number; width: number; height: number } {
|
|
276
273
|
const { ctx } = this;
|
|
277
|
-
const ax1 = xScale(pos[0]);
|
|
278
|
-
const ay1 = yScale(pos[1]);
|
|
274
|
+
const ax1 = xScale(pos[0]!);
|
|
275
|
+
const ay1 = yScale(pos[1]!);
|
|
279
276
|
|
|
280
|
-
const labelWidth = ctx
|
|
281
|
-
const titleWidth = ctx
|
|
277
|
+
const labelWidth = ctx?.measureText(label).width ?? 0;
|
|
278
|
+
const titleWidth = ctx?.measureText(title).width ?? 0;
|
|
282
279
|
const width = Math.max(labelWidth, titleWidth);
|
|
283
280
|
|
|
284
281
|
const bbox = {
|
|
@@ -292,15 +289,15 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
292
289
|
|
|
293
290
|
chooseTopOrBottomPosition(nodes: Callout[], bottom: Callout[], top: Callout[]): void {
|
|
294
291
|
for (let i = nodes.length - 2; i >= 0; --i) {
|
|
295
|
-
const node = nodes[i]
|
|
296
|
-
const prevNode = top[0]
|
|
292
|
+
const node = nodes[i]!;
|
|
293
|
+
const prevNode = top[0]!;
|
|
297
294
|
|
|
298
295
|
const overlap = isOverlapping(node.boundingBox, prevNode.boundingBox);
|
|
299
296
|
if (overlap) {
|
|
300
297
|
node.alignment = node.alignment === Location.topleft ? Location.bottomright : Location.bottomleft;
|
|
301
298
|
bottom.push(node);
|
|
302
299
|
if (i > 0) {
|
|
303
|
-
top.unshift(nodes[--i]);
|
|
300
|
+
top.unshift(nodes[--i]!);
|
|
304
301
|
}
|
|
305
302
|
} else {
|
|
306
303
|
top.unshift(node);
|
|
@@ -310,9 +307,9 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
310
307
|
|
|
311
308
|
adjustTopPositions(top: Callout[]): void {
|
|
312
309
|
for (let i = top.length - 2; i >= 0; --i) {
|
|
313
|
-
const currentNode = top[i]
|
|
310
|
+
const currentNode = top[i]!;
|
|
314
311
|
for (let j = top.length - 1; j > i; --j) {
|
|
315
|
-
const prevNode = top[j]
|
|
312
|
+
const prevNode = top[j]!;
|
|
316
313
|
const overlap = getOverlapOffset(currentNode.boundingBox, prevNode.boundingBox);
|
|
317
314
|
if (overlap) {
|
|
318
315
|
currentNode.dy += overlap.dy;
|
|
@@ -324,9 +321,9 @@ export class CalloutCanvasLayer<T extends Annotation[]> extends CanvasLayer<T> {
|
|
|
324
321
|
|
|
325
322
|
adjustBottomPositions(bottom: Callout[]): void {
|
|
326
323
|
for (let i = bottom.length - 2; i >= 0; --i) {
|
|
327
|
-
const currentNode = bottom[i]
|
|
324
|
+
const currentNode = bottom[i]!;
|
|
328
325
|
for (let j = bottom.length - 1; j > i; --j) {
|
|
329
|
-
const prevNode = bottom[j]
|
|
326
|
+
const prevNode = bottom[j]!;
|
|
330
327
|
const overlap = getOverlapOffset(prevNode.boundingBox, currentNode.boundingBox);
|
|
331
328
|
if (overlap) {
|
|
332
329
|
currentNode.dy += overlap.dy;
|