@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.
Files changed (107) hide show
  1. package/dist/components/axis.d.ts +1 -2
  2. package/dist/components/axis.d.ts.map +1 -1
  3. package/dist/control/ExtendedCurveInterpolator.d.ts.map +1 -1
  4. package/dist/control/IntersectionReferenceSystem.d.ts +2 -6
  5. package/dist/control/IntersectionReferenceSystem.d.ts.map +1 -1
  6. package/dist/control/LayerManager.d.ts +2 -2
  7. package/dist/control/LayerManager.d.ts.map +1 -1
  8. package/dist/control/MainController.d.ts +3 -3
  9. package/dist/control/MainController.d.ts.map +1 -1
  10. package/dist/control/ZoomPanHandler.d.ts +5 -4
  11. package/dist/control/ZoomPanHandler.d.ts.map +1 -1
  12. package/dist/control/interfaces.d.ts +3 -4
  13. package/dist/control/interfaces.d.ts.map +1 -1
  14. package/dist/control/overlay.d.ts +3 -3
  15. package/dist/control/overlay.d.ts.map +1 -1
  16. package/dist/datautils/colortable.d.ts +1 -1
  17. package/dist/datautils/colortable.d.ts.map +1 -1
  18. package/dist/datautils/findsample.d.ts.map +1 -1
  19. package/dist/datautils/schematicShapeGenerator.d.ts +3 -3
  20. package/dist/datautils/schematicShapeGenerator.d.ts.map +1 -1
  21. package/dist/datautils/seismicimage.d.ts.map +1 -1
  22. package/dist/datautils/trajectory.d.ts +1 -1
  23. package/dist/datautils/trajectory.d.ts.map +1 -1
  24. package/dist/index.cjs +2 -2
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.mjs +1373 -1272
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/index.umd.js +2 -2
  29. package/dist/index.umd.js.map +1 -1
  30. package/dist/interfaces.d.ts +1 -2
  31. package/dist/interfaces.d.ts.map +1 -1
  32. package/dist/layers/CalloutCanvasLayer.d.ts +2 -2
  33. package/dist/layers/CalloutCanvasLayer.d.ts.map +1 -1
  34. package/dist/layers/CustomDisplayObjects/ComplexRopeGeometry.d.ts.map +1 -1
  35. package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.d.ts.map +1 -1
  36. package/dist/layers/GeomodelCanvasLayer.d.ts +1 -1
  37. package/dist/layers/GeomodelCanvasLayer.d.ts.map +1 -1
  38. package/dist/layers/GeomodelLabelsLayer.d.ts +4 -4
  39. package/dist/layers/GeomodelLabelsLayer.d.ts.map +1 -1
  40. package/dist/layers/GeomodelLayerV2.d.ts.map +1 -1
  41. package/dist/layers/GridLayer.d.ts.map +1 -1
  42. package/dist/layers/ImageCanvasLayer.d.ts +1 -1
  43. package/dist/layers/ImageCanvasLayer.d.ts.map +1 -1
  44. package/dist/layers/ReferenceLineLayer.d.ts +1 -2
  45. package/dist/layers/ReferenceLineLayer.d.ts.map +1 -1
  46. package/dist/layers/SchematicLayer.d.ts.map +1 -1
  47. package/dist/layers/WellborePathLayer.d.ts +1 -1
  48. package/dist/layers/WellborePathLayer.d.ts.map +1 -1
  49. package/dist/layers/base/CanvasLayer.d.ts +3 -3
  50. package/dist/layers/base/CanvasLayer.d.ts.map +1 -1
  51. package/dist/layers/base/HTMLLayer.d.ts +1 -1
  52. package/dist/layers/base/HTMLLayer.d.ts.map +1 -1
  53. package/dist/layers/base/Layer.d.ts +10 -10
  54. package/dist/layers/base/Layer.d.ts.map +1 -1
  55. package/dist/layers/base/PixiLayer.d.ts +4 -4
  56. package/dist/layers/base/PixiLayer.d.ts.map +1 -1
  57. package/dist/layers/base/SVGLayer.d.ts +1 -1
  58. package/dist/layers/base/SVGLayer.d.ts.map +1 -1
  59. package/dist/utils/arc-length.d.ts.map +1 -1
  60. package/dist/utils/color.d.ts.map +1 -1
  61. package/dist/utils/root-finder.d.ts +1 -1
  62. package/dist/utils/root-finder.d.ts.map +1 -1
  63. package/dist/utils/text.d.ts +2 -2
  64. package/dist/utils/text.d.ts.map +1 -1
  65. package/dist/utils/vectorUtils.d.ts.map +1 -1
  66. package/dist/vendor/pixi-dashed-line/index.d.ts.map +1 -1
  67. package/package.json +11 -11
  68. package/src/components/axis.ts +23 -30
  69. package/src/control/ExtendedCurveInterpolator.ts +7 -7
  70. package/src/control/IntersectionReferenceSystem.ts +33 -41
  71. package/src/control/LayerManager.ts +33 -31
  72. package/src/control/MainController.ts +5 -7
  73. package/src/control/ZoomPanHandler.ts +50 -45
  74. package/src/control/interfaces.ts +3 -3
  75. package/src/control/overlay.ts +20 -16
  76. package/src/datautils/colortable.ts +4 -4
  77. package/src/datautils/findsample.ts +8 -7
  78. package/src/datautils/picks.ts +13 -13
  79. package/src/datautils/schematicShapeGenerator.ts +47 -17
  80. package/src/datautils/seismicimage.ts +12 -13
  81. package/src/datautils/surfacedata.ts +27 -26
  82. package/src/datautils/trajectory.ts +34 -32
  83. package/src/interfaces.ts +1 -1
  84. package/src/layers/CalloutCanvasLayer.ts +55 -58
  85. package/src/layers/CustomDisplayObjects/ComplexRopeGeometry.ts +23 -20
  86. package/src/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.ts +9 -10
  87. package/src/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.ts +13 -13
  88. package/src/layers/GeomodelCanvasLayer.ts +43 -36
  89. package/src/layers/GeomodelLabelsLayer.ts +99 -96
  90. package/src/layers/GeomodelLayerV2.ts +11 -11
  91. package/src/layers/GridLayer.ts +25 -21
  92. package/src/layers/ImageCanvasLayer.ts +17 -11
  93. package/src/layers/ReferenceLineLayer.ts +53 -45
  94. package/src/layers/SchematicLayer.ts +78 -75
  95. package/src/layers/WellborePathLayer.ts +48 -46
  96. package/src/layers/base/CanvasLayer.ts +18 -18
  97. package/src/layers/base/HTMLLayer.ts +5 -5
  98. package/src/layers/base/Layer.ts +18 -18
  99. package/src/layers/base/PixiLayer.ts +24 -20
  100. package/src/layers/base/SVGLayer.ts +5 -5
  101. package/src/utils/arc-length.ts +3 -10
  102. package/src/utils/binary-search.ts +2 -2
  103. package/src/utils/color.ts +9 -5
  104. package/src/utils/root-finder.ts +4 -4
  105. package/src/utils/text.ts +5 -5
  106. package/src/utils/vectorUtils.ts +13 -6
  107. 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 == null ? trajectory[0][0] : trajectory[trajectory.length - 1][0];
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], col[1], col[2], opacity], offset);
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], l.values[j]]),
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], g.top[j], next ? next.top[j] : null]),
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: SurfaceArea[] = surfaceAreas[g.name];
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: SurfaceArea[] = surfaceAreas[g.name];
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: StratUnit = findStratcolumnUnit(stratColumn, s.name, path);
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: StratUnit = path[0] || stratUnit;
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: StratUnit = units.find((u: StratUnit) => u.identifier.toLowerCase() === unitname.toLowerCase());
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.stratUnitParent);
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 null;
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.isBase) {
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 null;
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: number = findBestMatchingBaseIndex(surface, i, surfaces, stratColumn);
288
- acc[surface.group].push({
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: number = surface.values[j] !== null ? getBaseValue(baseIndex, surfaces, j) : null;
295
- return [p[0], surface.values[j], baseValue];
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 null;
309
+ return undefined;
309
310
  }
310
311
 
311
312
  for (let i: number = index; i < surfaces.length; i++) {
312
- if (surfaces[i].values[datapoint] !== null) {
313
- return surfaces[i].values[datapoint];
313
+ if (surfaces[i]?.values[datapoint] != null) {
314
+ return surfaces[i]?.values[datapoint];
314
315
  }
315
316
  }
316
- return null;
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[][] = poslog ? poslog.map((p: SurveySample) => [p.easting, p.northing, p.tvd, p.md]) : [];
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: number[][] = simplify(projectCurtain(points));
26
- const offset: number = projection[projection.length - 1][0];
25
+ const projection = simplify(projectCurtain(points));
26
+ const offset = projection[projection.length - 1]?.[0];
27
27
 
28
- projection.forEach((p, i) => {
29
- projection[i][0] = offset - p[0];
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[][] = poslog ? poslog.map((p) => [p.easting, p.northing, p.tvd, p.md]) : [];
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[][] = null;
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: number[] = path[0];
62
- const last: number[] = path[path.length - 1];
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 = null;
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: number[][] = seqI(Math.ceil(extensionLength * stepSize))
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, null, offset);
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[][], maxOffset = 0.001, maxDistance = 10): 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: number[][] = [inputArr[0]];
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[] = [a0 - t0, a1 - t1];
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: number[] = arr[arr.length - 1];
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: number[] = null, offset = 0): number[][] {
196
- let p0: number[] = origin || points[0];
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: number[]) => {
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] || 0];
205
+ return [offset > 0 ? offset - l : l, p1[2] ?? 0];
204
206
  });
205
207
  return projected;
206
208
  }
package/src/interfaces.ts CHANGED
@@ -15,7 +15,7 @@ export interface OnMountEvent extends LayerEvent {
15
15
  height?: number;
16
16
  }
17
17
 
18
- export interface OnUnmountEvent extends LayerEvent {}
18
+ export type OnUnmountEvent = LayerEvent;
19
19
 
20
20
  export interface OnResizeEvent extends LayerEvent {
21
21
  width: number;
@@ -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[] = null;
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.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;
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 = undefined;
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 || !this.callouts) {
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) => !groupFilter || groupFilter.includes(d.group));
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.font = `${fontStyle} ${fontSize}px ${font}`;
152
- ctx.fillStyle = color;
153
- ctx.fillText(title, x, y);
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: number = 3): void {
148
+ private renderPoint(x: number, y: number, radius = 3): void {
157
149
  const { ctx } = this;
158
- ctx.beginPath();
159
- ctx.moveTo(x, y);
160
- ctx.arc(x, y, radius, 0, Math.PI * 2);
161
- ctx.fill();
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: boolean = true): void => {
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.strokeStyle = color;
182
- ctx.lineWidth = 1;
176
+ if (ctx != null) {
177
+ ctx.strokeStyle = color;
178
+ ctx.lineWidth = 1;
183
179
 
184
- ctx.beginPath();
185
- ctx.moveTo(dotX, dotY);
186
- ctx.lineTo(textX, textY);
187
- ctx.lineTo(inverseTextX, textY);
180
+ ctx.beginPath();
181
+ ctx.moveTo(dotX, dotY);
182
+ ctx.lineTo(textX, textY);
183
+ ctx.lineTo(inverseTextX, textY);
188
184
 
189
- ctx.stroke();
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: number = 20,
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.project(a.md);
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], y: pos[1] },
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.measureText(label).width;
281
- const titleWidth = ctx.measureText(title).width;
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;