@equinor/esv-intersection 3.0.0-beta.4 → 3.0.0-beta.6

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 (76) hide show
  1. package/README.md +0 -1
  2. package/dist/components/axis.d.ts +1 -1
  3. package/dist/control/ZoomPanHandler.d.ts +1 -1
  4. package/dist/datautils/picks.d.ts +6 -6
  5. package/dist/datautils/schematicShapeGenerator.d.ts +7 -8
  6. package/dist/datautils/seismicimage.d.ts +1 -1
  7. package/dist/index.esm.js +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.umd.js +1 -272
  10. package/dist/interfaces.d.ts +1 -1
  11. package/dist/layers/CalloutCanvasLayer.d.ts +2 -2
  12. package/dist/layers/CustomDisplayObjects/ComplexRope.d.ts +1 -1
  13. package/dist/layers/GeomodelCanvasLayer.d.ts +1 -1
  14. package/dist/layers/ImageCanvasLayer.d.ts +1 -1
  15. package/dist/layers/ReferenceLineLayer.d.ts +29 -0
  16. package/dist/layers/SchematicLayer.d.ts +10 -1
  17. package/dist/layers/SeismicCanvasLayer.d.ts +2 -3
  18. package/dist/layers/index.d.ts +1 -0
  19. package/dist/layers/schematicInterfaces.d.ts +13 -12
  20. package/dist/utils/arc-length.d.ts +1 -1
  21. package/dist/utils/root-finder.d.ts +1 -1
  22. package/package.json +38 -33
  23. package/src/components/axis.ts +247 -0
  24. package/src/components/index.ts +1 -0
  25. package/src/constants.ts +17 -0
  26. package/src/control/ExtendedCurveInterpolator.ts +155 -0
  27. package/src/control/IntersectionReferenceSystem.ts +391 -0
  28. package/src/control/LayerManager.ts +294 -0
  29. package/src/control/MainController.ts +296 -0
  30. package/src/control/ZoomPanHandler.ts +436 -0
  31. package/src/control/index.ts +5 -0
  32. package/src/control/interfaces.ts +42 -0
  33. package/src/control/overlay.ts +118 -0
  34. package/src/datautils/camelcase.ts +28 -0
  35. package/src/datautils/colortable.ts +14 -0
  36. package/src/datautils/findsample.ts +64 -0
  37. package/src/datautils/index.ts +6 -0
  38. package/src/datautils/interfaces.ts +68 -0
  39. package/src/datautils/picks.ts +328 -0
  40. package/src/datautils/schematicShapeGenerator.ts +1013 -0
  41. package/src/datautils/seismicimage.ts +180 -0
  42. package/src/datautils/surfacedata.ts +318 -0
  43. package/src/datautils/trajectory.ts +206 -0
  44. package/src/index.ts +6 -0
  45. package/src/interfaces.ts +99 -0
  46. package/src/layers/CalloutCanvasLayer.ts +338 -0
  47. package/src/layers/CustomDisplayObjects/ComplexRope.ts +45 -0
  48. package/src/layers/CustomDisplayObjects/ComplexRopeGeometry.ts +190 -0
  49. package/src/layers/CustomDisplayObjects/FixedWidthSimpleRope.ts +41 -0
  50. package/src/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.ts +149 -0
  51. package/src/layers/CustomDisplayObjects/UniformTextureStretchRope.ts +39 -0
  52. package/src/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.ts +174 -0
  53. package/src/layers/GeomodelCanvasLayer.ts +176 -0
  54. package/src/layers/GeomodelLabelsLayer.ts +619 -0
  55. package/src/layers/GeomodelLayerV2.ts +110 -0
  56. package/src/layers/GridLayer.ts +145 -0
  57. package/src/layers/ImageCanvasLayer.ts +55 -0
  58. package/src/layers/ReferenceLineLayer.ts +185 -0
  59. package/src/layers/SchematicLayer.ts +896 -0
  60. package/src/layers/SeismicCanvasLayer.ts +46 -0
  61. package/src/layers/WellborePathLayer.ts +129 -0
  62. package/src/layers/base/CanvasLayer.ts +102 -0
  63. package/src/layers/base/HTMLLayer.ts +70 -0
  64. package/src/layers/base/Layer.ts +217 -0
  65. package/src/layers/base/PixiLayer.ts +190 -0
  66. package/src/layers/base/SVGLayer.ts +63 -0
  67. package/src/layers/base/index.ts +5 -0
  68. package/src/layers/index.ts +16 -0
  69. package/src/layers/schematicInterfaces.ts +470 -0
  70. package/src/utils/arc-length.ts +66 -0
  71. package/src/utils/binary-search.ts +26 -0
  72. package/src/utils/color.ts +22 -0
  73. package/src/utils/index.ts +1 -0
  74. package/src/utils/root-finder.ts +78 -0
  75. package/src/utils/text.ts +88 -0
  76. package/src/utils/vectorUtils.ts +67 -0
@@ -0,0 +1,896 @@
1
+ import { max } from 'd3-array';
2
+ import { scaleLinear, ScaleLinear } from 'd3-scale';
3
+ import { Graphics, groupD8, IPoint, Point, Rectangle, RENDERER_TYPE, SimpleRope, Texture } from 'pixi.js';
4
+ import { DashLine } from 'pixi-dashed-line';
5
+ import { LayerOptions, PixiLayer, PixiRenderApplication } from '.';
6
+ import { DEFAULT_TEXTURE_SIZE, EXAGGERATED_DIAMETER, HOLE_OUTLINE, SCREEN_OUTLINE } from '../constants';
7
+ import {
8
+ assertNever,
9
+ Casing,
10
+ CasingOptions,
11
+ Cement,
12
+ CementOptions,
13
+ CementPlugOptions,
14
+ CementSqueeze,
15
+ CementSqueezeOptions,
16
+ foldCompletion,
17
+ HoleOptions,
18
+ HoleSize,
19
+ isCementSqueeze,
20
+ PAndA,
21
+ SchematicData,
22
+ ScreenOptions,
23
+ TubingOptions,
24
+ Screen,
25
+ Tubing,
26
+ CompletionSymbol,
27
+ isPAndASymbol,
28
+ isCementPlug,
29
+ CementPlug,
30
+ PAndASymbol,
31
+ InternalLayerOptions,
32
+ defaultHoleOptions,
33
+ defaultCasingOptions,
34
+ defaultCementOptions,
35
+ defaultCementSqueezeOptions,
36
+ defaultCementPlugOptions,
37
+ defaultScreenOptions,
38
+ defaultTubingOptions,
39
+ defaultInternalLayerOptions,
40
+ Perforation,
41
+ PerforationOptions,
42
+ defaultPerforationOptions,
43
+ Completion,
44
+ OutlineClosure,
45
+ hasPacking,
46
+ hasFracLines,
47
+ hasSpikes,
48
+ } from './schematicInterfaces';
49
+ import {
50
+ CasingRenderObject,
51
+ createCementTexture,
52
+ createComplexRopeSegmentsForCement,
53
+ createComplexRopeSegmentsForCementSqueeze,
54
+ createComplexRopeSegmentsForCementPlug,
55
+ createHoleBaseTexture,
56
+ createScreenTexture,
57
+ createTubingTexture,
58
+ createTubularRenderingObject,
59
+ makeTubularPolygon,
60
+ prepareCasingRenderObject,
61
+ createCementPlugTexture,
62
+ createComplexRopeSegmentsForPerforation,
63
+ createPerforationPackingTexture,
64
+ PerforationShape,
65
+ createCementSqueezeTexture,
66
+ createPerforationFracLineTexture,
67
+ createPerforationSpikeTexture,
68
+ } from '../datautils/schematicShapeGenerator';
69
+ import { OnUpdateEvent, OnRescaleEvent, OnUnmountEvent } from '../interfaces';
70
+ import { convertColor } from '../utils/color';
71
+ import { createNormals, offsetPoint, offsetPoints } from '../utils/vectorUtils';
72
+ import { ComplexRope, ComplexRopeSegment } from './CustomDisplayObjects/ComplexRope';
73
+ import { FixedWidthSimpleRope } from './CustomDisplayObjects/FixedWidthSimpleRope';
74
+ import { UniformTextureStretchRope } from './CustomDisplayObjects/UniformTextureStretchRope';
75
+
76
+ interface ScalingFactors {
77
+ height: number;
78
+ zFactor: number;
79
+ yScale: ScaleLinear<number, number, never>;
80
+ }
81
+
82
+ interface SymbolRenderObject {
83
+ pathPoints: Point[];
84
+ referenceDiameter: number;
85
+ symbolKey: string;
86
+ }
87
+
88
+ interface CementRenderObject {
89
+ kind: 'cement';
90
+ segments: ComplexRopeSegment[];
91
+ casingIds: string[];
92
+ zIndex?: number;
93
+ }
94
+
95
+ interface CementSqueezeRenderObject {
96
+ kind: 'cementSqueeze';
97
+ segments: ComplexRopeSegment[];
98
+ casingIds: string[];
99
+ zIndex?: number;
100
+ }
101
+
102
+ type InterlacedRenderObjects = CasingRenderObject | CementRenderObject | CementSqueezeRenderObject;
103
+
104
+ const foldInterlacedRenderObjects =
105
+ <T>(fCasing: (obj: CasingRenderObject) => T, fCement: (obj: CementRenderObject) => T, fCementSqueeze: (obj: CementSqueezeRenderObject) => T) =>
106
+ (renderObject: InterlacedRenderObjects): T => {
107
+ switch (renderObject.kind) {
108
+ case 'casing':
109
+ return fCasing(renderObject);
110
+ case 'cement':
111
+ return fCement(renderObject);
112
+ case 'cementSqueeze':
113
+ return fCementSqueeze(renderObject);
114
+ default:
115
+ return assertNever(renderObject);
116
+ }
117
+ };
118
+
119
+ export interface SchematicLayerOptions<T extends SchematicData> extends LayerOptions<T> {
120
+ exaggerationFactor?: number;
121
+ internalLayerOptions?: InternalLayerOptions;
122
+ holeOptions?: HoleOptions;
123
+ casingOptions?: CasingOptions;
124
+ cementOptions?: CementOptions;
125
+ cementSqueezeOptions?: CementSqueezeOptions;
126
+ screenOptions?: ScreenOptions;
127
+ tubingOptions?: TubingOptions;
128
+ cementPlugOptions?: CementPlugOptions;
129
+ perforationOptions?: PerforationOptions;
130
+ }
131
+
132
+ const defaultSchematicLayerOptions = (layerId: string): SchematicLayerOptions<SchematicData> => ({
133
+ exaggerationFactor: 2,
134
+ internalLayerOptions: defaultInternalLayerOptions(layerId),
135
+ holeOptions: defaultHoleOptions,
136
+ casingOptions: defaultCasingOptions,
137
+ cementOptions: defaultCementOptions,
138
+ cementSqueezeOptions: defaultCementSqueezeOptions,
139
+ screenOptions: defaultScreenOptions,
140
+ tubingOptions: defaultTubingOptions,
141
+ cementPlugOptions: defaultCementPlugOptions,
142
+ perforationOptions: defaultPerforationOptions,
143
+ });
144
+
145
+ type InternalLayerVisibility = { [K in keyof InternalLayerOptions]: boolean };
146
+
147
+ export class SchematicLayer<T extends SchematicData> extends PixiLayer<T> {
148
+ private internalLayerVisibility: InternalLayerVisibility = {
149
+ holeLayerId: true,
150
+ casingLayerId: true,
151
+ completionLayerId: true,
152
+ cementLayerId: true,
153
+ pAndALayerId: true,
154
+ perforationLayerId: true,
155
+ };
156
+
157
+ private cementTextureCache: Texture;
158
+ private cementSqueezeTextureCache: Texture;
159
+ private cementPlugTextureCache: Texture;
160
+ private holeTextureCache: Texture;
161
+ private screenTextureCache: Texture;
162
+ private tubingTextureCache: Texture;
163
+ private textureSymbolCacheArray: { [key: string]: Texture };
164
+
165
+ protected scalingFactors: ScalingFactors = {
166
+ height: 600,
167
+ zFactor: 1,
168
+ yScale: scaleLinear(),
169
+ };
170
+
171
+ constructor(ctx: PixiRenderApplication, id?: string, options?: SchematicLayerOptions<T>) {
172
+ super(ctx, id, options);
173
+ this.options = <SchematicLayerOptions<T>>{
174
+ ...this.options,
175
+ ...defaultSchematicLayerOptions(this.id),
176
+ ...options,
177
+ };
178
+ }
179
+
180
+ public onUnmount(event?: OnUnmountEvent): void {
181
+ super.onUnmount(event);
182
+ this.scalingFactors = null;
183
+ this.cementTextureCache = null;
184
+ this.cementSqueezeTextureCache = null;
185
+ this.holeTextureCache = null;
186
+ this.screenTextureCache = null;
187
+ this.tubingTextureCache = null;
188
+ this.textureSymbolCacheArray = null;
189
+ this.internalLayerVisibility = null;
190
+ }
191
+
192
+ public onUpdate(event: OnUpdateEvent<T>): void {
193
+ super.onUpdate(event);
194
+ this.clearLayer();
195
+ this.preRender();
196
+ this.render();
197
+ }
198
+
199
+ public override onRescale(event: OnRescaleEvent): void {
200
+ const shouldRecalculate = this.scalingFactors.zFactor !== event.zFactor;
201
+
202
+ this.scalingFactors = { height: event.height, zFactor: event.zFactor, yScale: event.yScale };
203
+ super.optionsRescale(event);
204
+ const yRatio = this.yRatio();
205
+ const flippedX = event.xBounds[0] > event.xBounds[1];
206
+ const flippedY = event.yBounds[0] > event.yBounds[1];
207
+ this.setContainerPosition(event.xScale(0), event.yScale(0));
208
+ this.setContainerScale(event.xRatio * (flippedX ? -1 : 1), yRatio * (flippedY ? -1 : 1));
209
+ if (shouldRecalculate) {
210
+ this.clearLayer();
211
+ this.preRender();
212
+ }
213
+
214
+ this.render();
215
+ }
216
+
217
+ public override setVisibility(isVisible: boolean, layerId: string) {
218
+ if (layerId === this.id) {
219
+ super.setVisibility(isVisible, layerId);
220
+ return;
221
+ }
222
+
223
+ const { internalLayerOptions } = this.options as SchematicLayerOptions<T>;
224
+
225
+ const [keyFound] = Object.entries(internalLayerOptions).find(([_key, id]: [string, string]) => id === layerId);
226
+ if (keyFound) {
227
+ this.internalLayerVisibility[keyFound as keyof InternalLayerVisibility] = isVisible;
228
+ this.clearLayer();
229
+ this.preRender();
230
+ this.render();
231
+ }
232
+ }
233
+
234
+ public override getInternalLayerIds(): string[] {
235
+ const { internalLayerOptions } = this.options as SchematicLayerOptions<T>;
236
+ return Object.values(internalLayerOptions);
237
+ }
238
+
239
+ /**
240
+ * Calculate yRatio without zFactor
241
+ * TODO consider to move this into ZoomPanHandler
242
+ */
243
+ protected yRatio(): number {
244
+ const domain = this.scalingFactors.yScale.domain();
245
+ const ySpan = domain[1] - domain[0];
246
+ const baseYSpan = ySpan * this.scalingFactors.zFactor;
247
+ const baseDomain = [domain[0], domain[0] + baseYSpan];
248
+ return Math.abs(this.scalingFactors.height / (baseDomain[1] - baseDomain[0]));
249
+ }
250
+
251
+ protected getZFactorScaledPathForPoints = (start: number, end: number): Point[] => {
252
+ const y = (y: number): number => y * this.scalingFactors.zFactor;
253
+
254
+ const path = this.referenceSystem.getCurtainPath(start, end, true);
255
+ return path.map((p) => new Point(p.point[0], y(p.point[1])));
256
+ };
257
+
258
+ protected drawBigPolygon = (coords: IPoint[], color = 0x000000) => {
259
+ const polygon = new Graphics();
260
+ polygon.beginFill(color);
261
+ polygon.drawPolygon(coords);
262
+ polygon.endFill();
263
+
264
+ this.addChild(polygon);
265
+ };
266
+
267
+ protected drawBigTexturedPolygon = (coords: Point[], t: Texture): Graphics => {
268
+ const polygon = new Graphics().beginTextureFill({ texture: t }).drawPolygon(coords).endFill();
269
+ this.addChild(polygon);
270
+ return polygon;
271
+ };
272
+
273
+ protected drawRope(path: Point[], texture: Texture, tint?: number): void {
274
+ if (path.length === 0) {
275
+ return null;
276
+ }
277
+
278
+ const rope: SimpleRope = new SimpleRope(texture, path, 1);
279
+
280
+ rope.tint = tint || rope.tint;
281
+
282
+ this.addChild(rope);
283
+ }
284
+
285
+ /**
286
+ *
287
+ * @param leftPath Points for line on left side
288
+ * @param rightPath Points for line on right side
289
+ * @param lineColor Color of line
290
+ * @param lineWidth Width of line
291
+ * @param outlineClosure If line should be drawn at top and/or bottom of the paths
292
+ * @param lineAlignment alignment of the line to draw, (0 = inner, 0.5 = middle, 1 = outer).
293
+ */
294
+ protected drawOutline(
295
+ leftPath: Point[],
296
+ rightPath: Point[],
297
+ lineColor: number,
298
+ lineWidth = 1,
299
+ outlineClosure: OutlineClosure = 'None',
300
+ lineAlignment = 1,
301
+ ): void {
302
+ const leftPathReverse = leftPath.map<Point>((d) => d.clone()).reverse();
303
+
304
+ const startPointRight = rightPath[0];
305
+ const startPointLeft = leftPathReverse[0];
306
+
307
+ const line = new Graphics();
308
+ line.lineStyle(lineWidth, lineColor, undefined, lineAlignment);
309
+ line.moveTo(startPointRight.x, startPointRight.y);
310
+ rightPath.forEach((p: Point) => line.lineTo(p.x, p.y));
311
+
312
+ if (outlineClosure === 'None' || outlineClosure === 'Top') {
313
+ line.moveTo(startPointLeft.x, startPointLeft.y);
314
+ }
315
+
316
+ leftPathReverse.forEach((p: Point) => line.lineTo(p.x, p.y));
317
+
318
+ if (outlineClosure === 'TopAndBottom' || outlineClosure === 'Top') {
319
+ line.lineTo(startPointRight.x, startPointRight.y);
320
+ }
321
+
322
+ this.addChild(line);
323
+ }
324
+
325
+ /**
326
+ * Uses a dashed outline on one side to represent casing window
327
+ * The casing window should be visualized at the upper side of the wellbore path
328
+ * @param leftPath Points for line on left side
329
+ * @param pointPath Points for line on right side
330
+ * @param lineColor Color of line
331
+ * @param lineWidth Width of line
332
+ * @param lineAlignment alignment of the line to draw, (0 = inner, 0.5 = middle, 1 = outer).
333
+ */
334
+ protected drawCasingWindowOutline(leftPath: Point[], rightPath: Point[], { lineColor, windowOptions }: CasingOptions, lineWidth = 1): void {
335
+ // Correct the dashed path. Should always be displayed on the upper side of the wellbore path.
336
+ const flippedPaths = !!this.referenceSystem?.options?.calculateDisplacementFromBottom;
337
+ const [linePath, dashedPath] = flippedPaths ? [leftPath, rightPath] : [rightPath, leftPath];
338
+ const [dashedAlignment, solidAlignment] = flippedPaths ? [1, 0] : [0, 1];
339
+
340
+ const graphics = new Graphics();
341
+ graphics.lineStyle(lineWidth, convertColor(lineColor), undefined, solidAlignment);
342
+
343
+ const startPointLinePath = linePath[0];
344
+ graphics.moveTo(startPointLinePath.x, startPointLinePath.y);
345
+ linePath.forEach((p: Point) => graphics.lineTo(p.x, p.y));
346
+
347
+ const dashedLine = new DashLine(graphics, {
348
+ dash: [windowOptions.dashLength, windowOptions.spaceLength],
349
+ color: convertColor(windowOptions.dashColor),
350
+ width: lineWidth,
351
+ alignment: dashedAlignment,
352
+ });
353
+
354
+ const startPointDashedPath = dashedPath[0];
355
+ dashedLine.moveTo(startPointDashedPath.x, startPointDashedPath.y);
356
+ dashedPath.forEach((currentPoint: Point) => {
357
+ dashedLine.lineTo(currentPoint.x, currentPoint.y);
358
+ });
359
+
360
+ this.addChild(graphics);
361
+ }
362
+
363
+ private perforationRopeAndTextureReferences: { rope: ComplexRope; texture: Texture }[] = [];
364
+
365
+ public preRender(): void {
366
+ if (!this.data || !this.referenceSystem) {
367
+ return;
368
+ }
369
+
370
+ const { exaggerationFactor } = this.options as SchematicLayerOptions<T>;
371
+ const { holeSizes, casings, cements, completion, symbols, pAndA, perforations } = this.data;
372
+
373
+ this.updateSymbolCache(symbols);
374
+
375
+ holeSizes.sort((a: HoleSize, b: HoleSize) => b.diameter - a.diameter);
376
+ const maxHoleDiameter = holeSizes.length > 0 ? max(holeSizes, (d) => d.diameter) * exaggerationFactor : EXAGGERATED_DIAMETER * exaggerationFactor;
377
+ if (this.internalLayerVisibility.holeLayerId) {
378
+ holeSizes.forEach((hole: HoleSize) => this.drawHoleSize(maxHoleDiameter, hole));
379
+ }
380
+
381
+ casings.sort((a: Casing, b: Casing) => b.diameter - a.diameter);
382
+ const casingRenderObjects: CasingRenderObject[] = casings.map((casing: Casing) => this.createCasingRenderObject(casing));
383
+
384
+ const cementShapes: CementRenderObject[] = cements.map(
385
+ (cement: Cement): CementRenderObject => ({
386
+ kind: 'cement',
387
+ segments: createComplexRopeSegmentsForCement(cement, casings, completion, holeSizes, exaggerationFactor, this.getZFactorScaledPathForPoints),
388
+ casingIds: (cement.referenceIds || []).filter((id) => id),
389
+ }),
390
+ );
391
+
392
+ const [cementSqueezes, remainingPAndA] = pAndA.reduce<[CementSqueeze[], Exclude<PAndA, CementSqueeze>[]]>(
393
+ ([squeezes, remaining], current: PAndA) =>
394
+ isCementSqueeze(current) ? [[current, ...squeezes], remaining] : [squeezes, [current, ...remaining]],
395
+ [[], []],
396
+ );
397
+
398
+ const cementSqueezesShape: CementSqueezeRenderObject[] = cementSqueezes.map((squeeze) => ({
399
+ kind: 'cementSqueeze',
400
+ segments: this.createCementSqueezeShape(squeeze, casings, completion, holeSizes),
401
+ casingIds: squeeze.referenceIds,
402
+ }));
403
+
404
+ this.sortCementAndCasingRenderObjects(casingRenderObjects, cementShapes, cementSqueezesShape).forEach(
405
+ foldInterlacedRenderObjects(
406
+ (casingRO: CasingRenderObject) => {
407
+ if (this.internalLayerVisibility.casingLayerId) {
408
+ this.drawCasing(casingRO);
409
+
410
+ if (casingRO.hasShoe) {
411
+ this.drawShoe(casingRO.bottom, casingRO.referenceRadius);
412
+ }
413
+ }
414
+ },
415
+ (cementRO: CementRenderObject) => {
416
+ if (this.internalLayerVisibility.cementLayerId) {
417
+ this.drawComplexRope(cementRO.segments, this.getCementTexture());
418
+ }
419
+ },
420
+ (cementSqueezesRO: CementSqueezeRenderObject) => {
421
+ if (this.internalLayerVisibility.pAndALayerId) {
422
+ this.drawComplexRope(cementSqueezesRO.segments, this.getCementSqueezeTexture());
423
+ }
424
+ },
425
+ ),
426
+ );
427
+
428
+ this.perforationRopeAndTextureReferences.forEach(({ rope, texture }) => {
429
+ rope.destroy({
430
+ children: true,
431
+ texture: true,
432
+ baseTexture: true,
433
+ });
434
+ texture.destroy(true);
435
+ });
436
+ this.perforationRopeAndTextureReferences = [];
437
+
438
+ if (this.internalLayerVisibility.perforationLayerId) {
439
+ const { perforationOptions } = this.options as SchematicLayerOptions<T>;
440
+ const packings = perforations.filter(hasPacking);
441
+ const fracLines = perforations.filter(hasFracLines);
442
+ const spikes = perforations.filter(hasSpikes);
443
+ packings.forEach((perforation) => {
444
+ const perfShapes = this.createPerforationShape(perforation, casings, holeSizes);
445
+ const perfShapesByDiameter: { [key: number]: ComplexRopeSegment[] } = perfShapes.reduce(
446
+ (dict: { [key: number]: ComplexRopeSegment[] }, ps) => {
447
+ if (!dict[ps.diameter]) {
448
+ dict[ps.diameter] = [];
449
+ }
450
+ dict[ps.diameter] = [...dict[ps.diameter], ps];
451
+ return dict;
452
+ },
453
+ {},
454
+ );
455
+ Object.values(perfShapesByDiameter).forEach((perfShapesWithSameDiameter) => {
456
+ const texture = createPerforationPackingTexture(perforation, perfShapesWithSameDiameter[0], perforationOptions);
457
+ const rope = this.drawComplexRope(perfShapesWithSameDiameter, texture);
458
+ this.perforationRopeAndTextureReferences.push({ rope, texture });
459
+ });
460
+ });
461
+
462
+ fracLines.forEach((perforation) => {
463
+ const perfShapes = this.createPerforationShape(perforation, casings, holeSizes);
464
+ const thiccPerfShapes = perfShapes.map((ps) => ({ ...ps, diameter: ps.diameter * 3 }));
465
+ const perfShapesByDiameter: { [key: number]: ComplexRopeSegment[] } = thiccPerfShapes.reduce(
466
+ (dict: { [key: number]: ComplexRopeSegment[] }, ps) => {
467
+ if (!dict[ps.diameter]) {
468
+ dict[ps.diameter] = [];
469
+ }
470
+ dict[ps.diameter] = [...dict[ps.diameter], ps];
471
+ return dict;
472
+ },
473
+ {},
474
+ );
475
+ Object.values(perfShapesByDiameter).forEach((perfShapesWithSameDiameter) => {
476
+ perfShapesWithSameDiameter.forEach((perfShape) => {
477
+ const texture = createPerforationFracLineTexture(perforation, perfShape, perforationOptions);
478
+ const rope = this.drawComplexRope([perfShape], texture);
479
+ this.perforationRopeAndTextureReferences.push({ rope, texture });
480
+ });
481
+ });
482
+ });
483
+ spikes.forEach((perforation) => {
484
+ const perfShapes = this.createPerforationShape(perforation, casings, holeSizes);
485
+ const thiccPerfShapes = perfShapes.map((ps) => ({ ...ps, diameter: ps.diameter * 3 }));
486
+ const perfShapesByDiameter: { [key: number]: ComplexRopeSegment[] } = thiccPerfShapes.reduce(
487
+ (dict: { [key: number]: ComplexRopeSegment[] }, ps) => {
488
+ if (!dict[ps.diameter]) {
489
+ dict[ps.diameter] = [];
490
+ }
491
+ dict[ps.diameter] = [...dict[ps.diameter], ps];
492
+ return dict;
493
+ },
494
+ {},
495
+ );
496
+ Object.values(perfShapesByDiameter).forEach((perfShapesWithSameDiameter) => {
497
+ perfShapesWithSameDiameter.forEach((perfShape) => {
498
+ const texture = createPerforationSpikeTexture(perforation, perforations, perfShape, perforationOptions);
499
+ const rope = this.drawComplexRope([perfShape], texture);
500
+ this.perforationRopeAndTextureReferences.push({ rope, texture });
501
+ });
502
+ });
503
+ });
504
+ }
505
+
506
+ if (this.internalLayerVisibility.completionLayerId) {
507
+ completion.forEach(
508
+ foldCompletion(
509
+ (obj: Screen) => this.drawScreen(obj),
510
+ (obj: Tubing) => this.drawTubing(obj),
511
+ (obj: CompletionSymbol) => {
512
+ const symbolRenderObject = this.prepareSymbolRenderObject(obj);
513
+ this.drawSymbolComponent(symbolRenderObject);
514
+ },
515
+ ),
516
+ );
517
+ }
518
+
519
+ if (this.internalLayerVisibility.pAndALayerId) {
520
+ remainingPAndA.forEach((obj) => {
521
+ if (isPAndASymbol(obj)) {
522
+ const symbolRenderObject = this.prepareSymbolRenderObject(obj);
523
+ this.drawSymbolComponent(symbolRenderObject);
524
+ }
525
+ if (isCementPlug(obj)) {
526
+ this.drawCementPlug(obj, casings, completion, holeSizes);
527
+ }
528
+ });
529
+ }
530
+ }
531
+
532
+ private updateSymbolCache(symbols: { [key: string]: string }) {
533
+ if (!this.textureSymbolCacheArray) {
534
+ this.textureSymbolCacheArray = {};
535
+ }
536
+ if (!symbols) {
537
+ return;
538
+ }
539
+
540
+ const existingKeys = Object.keys(this.textureSymbolCacheArray);
541
+ Object.entries(symbols).forEach(([key, symbol]: [string, string]) => {
542
+ if (!existingKeys.includes(key)) {
543
+ this.textureSymbolCacheArray[key] = Texture.from(symbol);
544
+ }
545
+ });
546
+ }
547
+
548
+ private drawCementPlug(cementPlug: CementPlug, casings: Casing[], completion: Completion[], holes: HoleSize[]) {
549
+ const { exaggerationFactor, cementPlugOptions } = this.options as SchematicLayerOptions<T>;
550
+
551
+ const cementPlugSegments = createComplexRopeSegmentsForCementPlug(
552
+ cementPlug,
553
+ casings,
554
+ completion,
555
+ holes,
556
+ exaggerationFactor,
557
+ this.getZFactorScaledPathForPoints,
558
+ );
559
+ this.drawComplexRope(cementPlugSegments, this.getCementPlugTexture(cementPlugOptions));
560
+
561
+ const { rightPath, leftPath } = cementPlugSegments.reduce<{ rightPath: Point[]; leftPath: Point[] }>(
562
+ (acc, current) => {
563
+ const { leftPath, rightPath } = createTubularRenderingObject(current.diameter / 2, current.points);
564
+
565
+ return {
566
+ rightPath: [...acc.rightPath, ...rightPath],
567
+ leftPath: [...acc.leftPath, ...leftPath],
568
+ };
569
+ },
570
+ { rightPath: [], leftPath: [] },
571
+ );
572
+ // eslint-disable-next-line no-magic-numbers
573
+ this.drawOutline(leftPath, rightPath, convertColor('black'), 0.25, 'TopAndBottom');
574
+ }
575
+
576
+ private createCasingRenderObject(casing: Casing): CasingRenderObject {
577
+ const { exaggerationFactor } = this.options as SchematicLayerOptions<T>;
578
+ return prepareCasingRenderObject(exaggerationFactor, casing, this.getZFactorScaledPathForPoints);
579
+ }
580
+
581
+ private getCementPlugTexture(cementPlugOptions: CementPlugOptions): Texture {
582
+ if (!this.cementPlugTextureCache) {
583
+ this.cementPlugTextureCache = createCementPlugTexture(cementPlugOptions);
584
+ }
585
+ return this.cementPlugTextureCache;
586
+ }
587
+
588
+ private prepareSymbolRenderObject = (component: CompletionSymbol | PAndASymbol): SymbolRenderObject => {
589
+ const { exaggerationFactor } = this.options as SchematicLayerOptions<T>;
590
+
591
+ const exaggeratedDiameter = component.diameter * exaggerationFactor;
592
+
593
+ const pathPoints = this.getZFactorScaledPathForPoints(component.start, component.end);
594
+
595
+ return {
596
+ pathPoints,
597
+ referenceDiameter: exaggeratedDiameter,
598
+ symbolKey: component.symbolKey,
599
+ };
600
+ };
601
+
602
+ private drawSymbolComponent = ({ pathPoints, referenceDiameter, symbolKey }: SymbolRenderObject): void => {
603
+ const texture = this.getSymbolTexture(symbolKey, referenceDiameter);
604
+ // The rope renders fine in CANVAS/fallback mode
605
+ this.drawSVGRope(pathPoints, texture);
606
+ };
607
+
608
+ private drawSVGRope(path: Point[], texture: Texture): void {
609
+ if (path.length === 0) {
610
+ return null;
611
+ }
612
+
613
+ const rope: UniformTextureStretchRope = new UniformTextureStretchRope(texture, path);
614
+
615
+ this.addChild(rope);
616
+ }
617
+
618
+ private getSymbolTexture(symbolKey: string, diameter: number): Texture {
619
+ return new Texture(this.textureSymbolCacheArray[symbolKey].baseTexture, null, new Rectangle(0, 0, 0, diameter), null, groupD8.MAIN_DIAGONAL);
620
+ }
621
+
622
+ private drawHoleSize = (maxHoleDiameter: number, holeObject: HoleSize): void => {
623
+ if (holeObject == null) {
624
+ return;
625
+ }
626
+
627
+ const pathPoints = this.getZFactorScaledPathForPoints(holeObject.start, holeObject.end);
628
+ if (pathPoints.length === 0) {
629
+ return;
630
+ }
631
+
632
+ const { exaggerationFactor, holeOptions } = this.options as SchematicLayerOptions<T>;
633
+ const exaggeratedDiameter = holeObject.diameter * exaggerationFactor;
634
+ const { rightPath, leftPath } = createTubularRenderingObject(exaggeratedDiameter / 2, pathPoints);
635
+
636
+ if (this.renderType() === RENDERER_TYPE.CANVAS) {
637
+ const polygonCoords = makeTubularPolygon(leftPath, rightPath);
638
+ this.drawBigPolygon(polygonCoords, convertColor(holeOptions.firstColor));
639
+ } else {
640
+ const texture = this.getHoleTexture(holeOptions, exaggeratedDiameter, maxHoleDiameter);
641
+ this.drawHoleRope(pathPoints, texture, maxHoleDiameter);
642
+ }
643
+
644
+ this.drawOutline(leftPath, rightPath, convertColor(holeOptions.lineColor), HOLE_OUTLINE * exaggerationFactor, 'TopAndBottom', 0);
645
+ };
646
+
647
+ private drawHoleRope(path: Point[], texture: Texture, maxHoleDiameter: number): void {
648
+ if (path.length === 0) {
649
+ return null;
650
+ }
651
+
652
+ const rope: SimpleRope = new SimpleRope(texture, path, maxHoleDiameter / DEFAULT_TEXTURE_SIZE);
653
+
654
+ this.addChild(rope);
655
+ }
656
+
657
+ private getHoleTexture(holeOptions: HoleOptions, diameter: number, maxHoleDiameter: number): Texture {
658
+ const size = DEFAULT_TEXTURE_SIZE;
659
+ const height = size;
660
+ const width = size;
661
+
662
+ const textureDiameter = (diameter / maxHoleDiameter) * size;
663
+
664
+ if (!this.holeTextureCache) {
665
+ this.holeTextureCache = createHoleBaseTexture(holeOptions, width, height);
666
+ }
667
+
668
+ const baseTexture = this.holeTextureCache.baseTexture;
669
+ const sidePadding = (height - textureDiameter) / 2;
670
+ const frame = new Rectangle(0, sidePadding, width, textureDiameter);
671
+ const texture = new Texture(baseTexture, frame);
672
+
673
+ return texture;
674
+ }
675
+
676
+ /**
677
+ * The rendering order of these components needs to be aligned
678
+ * @param casingRenderObjects
679
+ * @param cementRenderObject
680
+ * @param cementSqueezes
681
+ * @returns ordered rendering list
682
+ */
683
+ private sortCementAndCasingRenderObjects(
684
+ casingRenderObjects: CasingRenderObject[],
685
+ cementRenderObject: CementRenderObject[],
686
+ cementSqueezes: CementSqueezeRenderObject[],
687
+ ): InterlacedRenderObjects[] {
688
+ type InterlaceReducerAcc = {
689
+ result: InterlacedRenderObjects[];
690
+ remainingCement: CementRenderObject[];
691
+ remainingCementSqueezes: CementSqueezeRenderObject[];
692
+ };
693
+
694
+ let zIndex = 0;
695
+
696
+ const { result } = casingRenderObjects.reduce(
697
+ (acc: InterlaceReducerAcc, casingRenderObject: CasingRenderObject): InterlaceReducerAcc => {
698
+ const foundCementShape = acc.remainingCement.find((cement) => cement.casingIds.includes(casingRenderObject.id));
699
+ const foundCementSqueezes = acc.remainingCementSqueezes.filter((squeeze) => squeeze.casingIds.includes(casingRenderObject.id));
700
+
701
+ if (foundCementShape) {
702
+ foundCementShape.zIndex = zIndex++;
703
+ }
704
+ foundCementSqueezes.forEach((item) => (item.zIndex = zIndex++));
705
+ casingRenderObject.zIndex = zIndex++;
706
+
707
+ return {
708
+ result: [...acc.result, foundCementShape, casingRenderObject, ...foundCementSqueezes],
709
+ remainingCement: acc.remainingCement.filter((c) => c !== foundCementShape),
710
+ remainingCementSqueezes: acc.remainingCementSqueezes.filter((squeeze) => !foundCementSqueezes.includes(squeeze)),
711
+ };
712
+ },
713
+ { result: [], remainingCement: cementRenderObject, remainingCementSqueezes: cementSqueezes },
714
+ );
715
+
716
+ return result.filter((item) => item !== undefined).sort((a, b) => a.zIndex - b.zIndex);
717
+ }
718
+
719
+ /**
720
+ *
721
+ * @param intervals
722
+ * @param texture
723
+ * optionally fetch the exaggerationFactor from a different options prop
724
+ * options.perforationOptions for example
725
+ * @param getExaggerationFactor
726
+ * @returns
727
+ */
728
+ private drawComplexRope(intervals: ComplexRopeSegment[], texture: Texture): ComplexRope {
729
+ if (intervals.length === 0) {
730
+ return null;
731
+ }
732
+ const { exaggerationFactor } = this.options as SchematicLayerOptions<T>;
733
+
734
+ const rope = new ComplexRope(texture, intervals, exaggerationFactor);
735
+
736
+ this.addChild(rope);
737
+
738
+ return rope;
739
+ }
740
+
741
+ private static getOutlineClosureType = (index: number, maxIndex: number): OutlineClosure => {
742
+ if (index === 0) {
743
+ if (index === maxIndex) {
744
+ return 'TopAndBottom';
745
+ }
746
+ return 'Top';
747
+ }
748
+ if (index === maxIndex) {
749
+ return 'Bottom';
750
+ }
751
+
752
+ return 'None';
753
+ };
754
+
755
+ private drawCasing = (casingRenderObject: CasingRenderObject): void => {
756
+ const { casingOptions } = this.options as SchematicLayerOptions<T>;
757
+ const casingSolidColorNumber = convertColor(casingOptions.solidColor);
758
+ const casingLineColorNumber = convertColor(casingOptions.lineColor);
759
+
760
+ casingRenderObject.sections.forEach((section, index, list) => {
761
+ const outlineClosureType = SchematicLayer.getOutlineClosureType(index, list.length - 1);
762
+ // Pixi.js-legacy handles SimpleRope and advanced render methods poorly
763
+ if (this.renderType() === RENDERER_TYPE.CANVAS) {
764
+ this.drawBigPolygon(section.polygon, casingSolidColorNumber);
765
+ } else {
766
+ const texture = this.createCasingTexture(casingRenderObject.referenceDiameter);
767
+ this.drawRope(section.pathPoints, texture, casingSolidColorNumber);
768
+ }
769
+ if (section.kind === 'casing-window') {
770
+ this.drawCasingWindowOutline(section.leftPath, section.rightPath, casingOptions, casingRenderObject.casingWallWidth);
771
+ } else {
772
+ this.drawOutline(section.leftPath, section.rightPath, casingLineColorNumber, casingRenderObject.casingWallWidth, outlineClosureType);
773
+ }
774
+ });
775
+ };
776
+
777
+ private createCasingTexture(diameter: number): Texture {
778
+ const textureWidthPO2 = 16;
779
+ return new Texture(Texture.WHITE.baseTexture, null, new Rectangle(0, 0, textureWidthPO2, diameter));
780
+ }
781
+
782
+ private drawShoe(casingEnd: number, casingRadius: number): void {
783
+ const { exaggerationFactor, casingOptions } = this.options as SchematicLayerOptions<T>;
784
+ const shoeWidth = casingOptions.shoeSize.width * exaggerationFactor;
785
+ const shoeLength = casingOptions.shoeSize.length * exaggerationFactor;
786
+
787
+ const shoeCoords = this.generateShoe(casingEnd, casingRadius, shoeLength, shoeWidth);
788
+ const shoeCoords2 = this.generateShoe(casingEnd, casingRadius, shoeLength, -shoeWidth);
789
+ this.drawBigPolygon(shoeCoords2);
790
+ this.drawBigPolygon(shoeCoords);
791
+ }
792
+
793
+ private generateShoe = (casingEnd: number, casingRadius: number, length: number, width: number): Point[] => {
794
+ const start = casingEnd - length;
795
+ const end = casingEnd;
796
+
797
+ const points = this.getZFactorScaledPathForPoints(start, end);
798
+
799
+ const normal = createNormals(points);
800
+ const shoeEdge: Point[] = offsetPoints(points, normal, casingRadius * (width < 0 ? -1 : 1));
801
+
802
+ const shoeTipPoint = points[points.length - 1];
803
+ const shoeTipNormal = normal[normal.length - 1];
804
+ const shoeTip: Point = offsetPoint(shoeTipPoint, shoeTipNormal, width + casingRadius * (width < 0 ? -1 : 1));
805
+
806
+ return [...shoeEdge, shoeTip];
807
+ };
808
+
809
+ private createCementSqueezeShape = (
810
+ squeeze: CementSqueeze,
811
+ casings: Casing[],
812
+ completion: Completion[],
813
+ holes: HoleSize[],
814
+ ): ComplexRopeSegment[] => {
815
+ const { exaggerationFactor } = this.options as SchematicLayerOptions<T>;
816
+ return createComplexRopeSegmentsForCementSqueeze(squeeze, casings, completion, holes, exaggerationFactor, this.getZFactorScaledPathForPoints);
817
+ };
818
+
819
+ private getCementTexture(): Texture {
820
+ if (!this.cementTextureCache) {
821
+ const { cementOptions } = this.options as SchematicLayerOptions<T>;
822
+ this.cementTextureCache = createCementTexture(cementOptions);
823
+ }
824
+ return this.cementTextureCache;
825
+ }
826
+
827
+ private createPerforationShape = (perforation: Perforation, casings: Casing[], holes: HoleSize[]): PerforationShape[] => {
828
+ const { exaggerationFactor } = this.options as SchematicLayerOptions<T>;
829
+ return createComplexRopeSegmentsForPerforation(perforation, casings, holes, exaggerationFactor, this.getZFactorScaledPathForPoints);
830
+ };
831
+
832
+ private getCementSqueezeTexture(): Texture {
833
+ if (!this.cementSqueezeTextureCache) {
834
+ const { cementSqueezeOptions } = this.options as SchematicLayerOptions<T>;
835
+ this.cementSqueezeTextureCache = createCementSqueezeTexture(cementSqueezeOptions);
836
+ }
837
+ return this.cementSqueezeTextureCache;
838
+ }
839
+
840
+ private drawScreen({ start, end, diameter }: Screen): void {
841
+ const { exaggerationFactor, screenOptions } = this.options as SchematicLayerOptions<T>;
842
+ const exaggeratedDiameter = exaggerationFactor * diameter;
843
+
844
+ const pathPoints = this.getZFactorScaledPathForPoints(start, end);
845
+ const { leftPath, rightPath } = createTubularRenderingObject(exaggeratedDiameter / 2, pathPoints);
846
+ const polygon = makeTubularPolygon(leftPath, rightPath);
847
+
848
+ const texture = this.getScreenTexture();
849
+ if (this.renderType() === RENDERER_TYPE.CANVAS) {
850
+ this.drawBigTexturedPolygon(polygon, texture);
851
+ } else {
852
+ this.drawCompletionRope(pathPoints, texture, exaggeratedDiameter);
853
+ }
854
+ this.drawOutline(leftPath, rightPath, convertColor(screenOptions.lineColor), SCREEN_OUTLINE * exaggerationFactor, 'TopAndBottom');
855
+ }
856
+
857
+ private drawTubing({ diameter, start, end }: Tubing): void {
858
+ const { exaggerationFactor, tubingOptions } = this.options as SchematicLayerOptions<T>;
859
+ const exaggeratedDiameter = exaggerationFactor * diameter;
860
+
861
+ const pathPoints = this.getZFactorScaledPathForPoints(start, end);
862
+ const { leftPath, rightPath } = createTubularRenderingObject(exaggeratedDiameter / 2, pathPoints);
863
+ const polygon = makeTubularPolygon(leftPath, rightPath);
864
+
865
+ const texture = this.getTubingTexture(tubingOptions);
866
+ if (this.renderType() === RENDERER_TYPE.CANVAS) {
867
+ this.drawBigTexturedPolygon(polygon, texture);
868
+ } else {
869
+ this.drawCompletionRope(pathPoints, texture, exaggeratedDiameter);
870
+ }
871
+ }
872
+
873
+ private getTubingTexture(tubingOptions: TubingOptions): Texture {
874
+ if (!this.tubingTextureCache) {
875
+ this.tubingTextureCache = createTubingTexture(tubingOptions);
876
+ }
877
+ return this.tubingTextureCache;
878
+ }
879
+
880
+ private getScreenTexture(): Texture {
881
+ if (!this.screenTextureCache) {
882
+ const { screenOptions } = this.options as SchematicLayerOptions<T>;
883
+ this.screenTextureCache = createScreenTexture(screenOptions);
884
+ }
885
+ return this.screenTextureCache;
886
+ }
887
+
888
+ private drawCompletionRope(path: Point[], texture: Texture, diameter: number): void {
889
+ if (path.length === 0) {
890
+ return;
891
+ }
892
+
893
+ const rope: FixedWidthSimpleRope = new FixedWidthSimpleRope(texture, path, diameter);
894
+ this.addChild(rope);
895
+ }
896
+ }