@equinor/esv-intersection 3.0.4 → 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +18 -3
  2. package/dist/components/axis.d.ts +48 -0
  3. package/dist/components/axis.d.ts.map +1 -0
  4. package/dist/components/index.d.ts +2 -0
  5. package/dist/components/index.d.ts.map +1 -0
  6. package/dist/constants.d.ts +1 -0
  7. package/dist/constants.d.ts.map +1 -0
  8. package/dist/control/ExtendedCurveInterpolator.d.ts +59 -0
  9. package/dist/control/ExtendedCurveInterpolator.d.ts.map +1 -0
  10. package/dist/control/IntersectionReferenceSystem.d.ts +97 -0
  11. package/dist/control/IntersectionReferenceSystem.d.ts.map +1 -0
  12. package/dist/control/LayerManager.d.ts +77 -0
  13. package/dist/control/LayerManager.d.ts.map +1 -0
  14. package/dist/control/MainController.d.ts +155 -0
  15. package/dist/control/MainController.d.ts.map +1 -0
  16. package/dist/control/ZoomPanHandler.d.ts +159 -0
  17. package/dist/control/ZoomPanHandler.d.ts.map +1 -0
  18. package/dist/control/index.d.ts +6 -0
  19. package/dist/control/index.d.ts.map +1 -0
  20. package/dist/control/interfaces.d.ts +38 -0
  21. package/dist/control/interfaces.d.ts.map +1 -0
  22. package/dist/control/overlay.d.ts +21 -0
  23. package/dist/control/overlay.d.ts.map +1 -0
  24. package/dist/datautils/colortable.d.ts +2 -0
  25. package/dist/datautils/colortable.d.ts.map +1 -0
  26. package/dist/datautils/findsample.d.ts +3 -0
  27. package/dist/datautils/findsample.d.ts.map +1 -0
  28. package/dist/datautils/index.d.ts +7 -0
  29. package/dist/datautils/index.d.ts.map +1 -0
  30. package/dist/datautils/interfaces.d.ts +64 -0
  31. package/dist/datautils/interfaces.d.ts.map +1 -0
  32. package/dist/datautils/picks.d.ts +75 -0
  33. package/dist/datautils/picks.d.ts.map +1 -0
  34. package/dist/datautils/schematicShapeGenerator.d.ts +60 -0
  35. package/dist/datautils/schematicShapeGenerator.d.ts.map +1 -0
  36. package/dist/datautils/seismicimage.d.ts +46 -0
  37. package/dist/datautils/seismicimage.d.ts.map +1 -0
  38. package/dist/datautils/surfacedata.d.ts +11 -0
  39. package/dist/datautils/surfacedata.d.ts.map +1 -0
  40. package/dist/datautils/trajectory.d.ts +15 -0
  41. package/dist/datautils/trajectory.d.ts.map +1 -0
  42. package/dist/index.cjs +1 -1
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.mjs +162 -160
  47. package/dist/index.mjs.map +1 -1
  48. package/dist/index.umd.js +1 -1
  49. package/dist/index.umd.js.map +1 -1
  50. package/dist/interfaces.d.ts +1 -0
  51. package/dist/interfaces.d.ts.map +1 -0
  52. package/dist/layers/CalloutCanvasLayer.d.ts +61 -0
  53. package/dist/layers/CalloutCanvasLayer.d.ts.map +1 -0
  54. package/dist/layers/CustomDisplayObjects/ComplexRope.d.ts +22 -0
  55. package/dist/layers/CustomDisplayObjects/ComplexRope.d.ts.map +1 -0
  56. package/dist/layers/CustomDisplayObjects/ComplexRopeGeometry.d.ts +24 -0
  57. package/dist/layers/CustomDisplayObjects/ComplexRopeGeometry.d.ts.map +1 -0
  58. package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRope.d.ts +21 -0
  59. package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRope.d.ts.map +1 -0
  60. package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.d.ts +27 -0
  61. package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.d.ts.map +1 -0
  62. package/dist/layers/CustomDisplayObjects/UniformTextureStretchRope.d.ts +18 -0
  63. package/dist/layers/CustomDisplayObjects/UniformTextureStretchRope.d.ts.map +1 -0
  64. package/dist/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.d.ts +25 -0
  65. package/dist/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.d.ts.map +1 -0
  66. package/dist/layers/GeomodelCanvasLayer.d.ts +29 -0
  67. package/dist/layers/GeomodelCanvasLayer.d.ts.map +1 -0
  68. package/dist/layers/GeomodelLabelsLayer.d.ts +50 -0
  69. package/dist/layers/GeomodelLabelsLayer.d.ts.map +1 -0
  70. package/dist/layers/GeomodelLayerV2.d.ts +13 -0
  71. package/dist/layers/GeomodelLayerV2.d.ts.map +1 -0
  72. package/dist/layers/GridLayer.d.ts +30 -0
  73. package/dist/layers/GridLayer.d.ts.map +1 -0
  74. package/dist/layers/ImageCanvasLayer.d.ts +21 -0
  75. package/dist/layers/ImageCanvasLayer.d.ts.map +1 -0
  76. package/dist/layers/ReferenceLineLayer.d.ts +30 -0
  77. package/dist/layers/ReferenceLineLayer.d.ts.map +1 -0
  78. package/dist/layers/SchematicLayer.d.ts +114 -0
  79. package/dist/layers/SchematicLayer.d.ts.map +1 -0
  80. package/dist/layers/SeismicCanvasLayer.d.ts +19 -0
  81. package/dist/layers/SeismicCanvasLayer.d.ts.map +1 -0
  82. package/dist/layers/WellborePathLayer.d.ts +18 -0
  83. package/dist/layers/WellborePathLayer.d.ts.map +1 -0
  84. package/dist/layers/base/CanvasLayer.d.ts +20 -0
  85. package/dist/layers/base/CanvasLayer.d.ts.map +1 -0
  86. package/dist/layers/base/HTMLLayer.d.ts +14 -0
  87. package/dist/layers/base/HTMLLayer.d.ts.map +1 -0
  88. package/dist/layers/base/Layer.d.ts +70 -0
  89. package/dist/layers/base/Layer.d.ts.map +1 -0
  90. package/dist/layers/base/PixiLayer.d.ts +33 -0
  91. package/dist/layers/base/PixiLayer.d.ts.map +1 -0
  92. package/dist/layers/base/SVGLayer.d.ts +14 -0
  93. package/dist/layers/base/SVGLayer.d.ts.map +1 -0
  94. package/dist/layers/base/index.d.ts +6 -0
  95. package/dist/layers/base/index.d.ts.map +1 -0
  96. package/dist/layers/index.d.ts +17 -0
  97. package/dist/layers/index.d.ts.map +1 -0
  98. package/dist/layers/schematicInterfaces.d.ts +210 -0
  99. package/dist/layers/schematicInterfaces.d.ts.map +1 -0
  100. package/dist/utils/arc-length.d.ts +24 -0
  101. package/dist/utils/arc-length.d.ts.map +1 -0
  102. package/dist/utils/binary-search.d.ts +9 -0
  103. package/dist/utils/binary-search.d.ts.map +1 -0
  104. package/dist/utils/color.d.ts +6 -0
  105. package/dist/utils/color.d.ts.map +1 -0
  106. package/dist/utils/index.d.ts +2 -0
  107. package/dist/utils/index.d.ts.map +1 -0
  108. package/dist/utils/root-finder.d.ts +35 -0
  109. package/dist/utils/root-finder.d.ts.map +1 -0
  110. package/dist/utils/text.d.ts +15 -0
  111. package/dist/utils/text.d.ts.map +1 -0
  112. package/dist/utils/vectorUtils.d.ts +16 -0
  113. package/dist/utils/vectorUtils.d.ts.map +1 -0
  114. package/dist/vendor/pixi-dashed-line/index.d.ts +57 -0
  115. package/dist/vendor/pixi-dashed-line/index.d.ts.map +1 -0
  116. package/package.json +29 -21
  117. package/src/.eslintrc.json +5 -0
  118. package/src/components/axis.ts +247 -0
  119. package/src/components/index.ts +1 -0
  120. package/src/control/ExtendedCurveInterpolator.ts +155 -0
  121. package/src/control/IntersectionReferenceSystem.ts +391 -0
  122. package/src/control/LayerManager.ts +294 -0
  123. package/src/control/MainController.ts +296 -0
  124. package/src/control/ZoomPanHandler.ts +436 -0
  125. package/src/control/index.ts +5 -0
  126. package/src/control/interfaces.ts +42 -0
  127. package/src/control/overlay.ts +118 -0
  128. package/src/datautils/colortable.ts +14 -0
  129. package/src/datautils/findsample.ts +72 -0
  130. package/src/datautils/index.ts +6 -0
  131. package/src/datautils/interfaces.ts +68 -0
  132. package/src/datautils/picks.ts +328 -0
  133. package/src/datautils/schematicShapeGenerator.ts +1008 -0
  134. package/src/datautils/seismicimage.ts +180 -0
  135. package/src/datautils/surfacedata.ts +317 -0
  136. package/src/datautils/trajectory.ts +206 -0
  137. package/src/layers/CalloutCanvasLayer.ts +338 -0
  138. package/src/layers/CustomDisplayObjects/ComplexRope.ts +44 -0
  139. package/src/layers/CustomDisplayObjects/ComplexRopeGeometry.ts +184 -0
  140. package/src/layers/CustomDisplayObjects/FixedWidthSimpleRope.ts +41 -0
  141. package/src/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.ts +149 -0
  142. package/src/layers/CustomDisplayObjects/UniformTextureStretchRope.ts +39 -0
  143. package/src/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.ts +174 -0
  144. package/src/layers/GeomodelCanvasLayer.ts +176 -0
  145. package/src/layers/GeomodelLabelsLayer.ts +615 -0
  146. package/src/layers/GeomodelLayerV2.ts +111 -0
  147. package/src/layers/GridLayer.ts +145 -0
  148. package/src/layers/ImageCanvasLayer.ts +55 -0
  149. package/src/layers/ReferenceLineLayer.ts +185 -0
  150. package/src/layers/SchematicLayer.ts +870 -0
  151. package/src/layers/SeismicCanvasLayer.ts +46 -0
  152. package/src/layers/WellborePathLayer.ts +129 -0
  153. package/src/layers/base/CanvasLayer.ts +102 -0
  154. package/src/layers/base/HTMLLayer.ts +70 -0
  155. package/src/layers/base/Layer.ts +217 -0
  156. package/src/layers/base/PixiLayer.ts +190 -0
  157. package/src/layers/base/SVGLayer.ts +63 -0
  158. package/src/layers/base/index.ts +5 -0
  159. package/src/layers/index.ts +16 -0
  160. package/src/layers/schematicInterfaces.ts +472 -0
  161. package/src/tsconfig.json +9 -0
  162. package/src/utils/arc-length.ts +66 -0
  163. package/src/utils/binary-search.ts +26 -0
  164. package/src/utils/color.ts +22 -0
  165. package/src/utils/index.ts +1 -0
  166. package/src/utils/root-finder.ts +78 -0
  167. package/src/utils/text.ts +88 -0
  168. package/src/utils/vectorUtils.ts +67 -0
  169. package/src/vendor/pixi-dashed-line/index.ts +390 -0
@@ -0,0 +1,1008 @@
1
+ import { groupD8, IPoint, Point, Rectangle, Texture, WRAP_MODES } from 'pixi.js';
2
+ import { DEFAULT_TEXTURE_SIZE } from '../constants';
3
+ import {
4
+ Casing,
5
+ CasingWindow,
6
+ Cement,
7
+ CementOptions,
8
+ CementPlug,
9
+ CementPlugOptions,
10
+ CementSqueeze,
11
+ CementSqueezeOptions,
12
+ Completion,
13
+ HoleOptions,
14
+ HoleSize,
15
+ ScreenOptions,
16
+ TubingOptions,
17
+ Perforation,
18
+ PerforationOptions,
19
+ foldPerforationSubKind,
20
+ intersect,
21
+ isSubKindCasedHoleFracPack,
22
+ isSubkindCasedHoleGravelPack,
23
+ PerforationSubKind,
24
+ isSubKindCasedHoleFracturation,
25
+ } from '../layers/schematicInterfaces';
26
+ import { ComplexRopeSegment } from '../layers/CustomDisplayObjects/ComplexRope';
27
+ import { createNormals, offsetPoints } from '../utils/vectorUtils';
28
+
29
+ export type PerforationShape = ComplexRopeSegment;
30
+
31
+ export interface TubularRenderingObject {
32
+ leftPath: Point[];
33
+ rightPath: Point[];
34
+ }
35
+
36
+ export interface CasingRenderObject {
37
+ id: string;
38
+ kind: 'casing';
39
+ referenceDiameter: number;
40
+ referenceRadius: number;
41
+ casingWallWidth: number;
42
+ hasShoe: boolean;
43
+ bottom: number;
44
+ zIndex?: number;
45
+ sections: {
46
+ kind: 'casing' | 'casing-window';
47
+ leftPath: Point[];
48
+ rightPath: Point[];
49
+ pathPoints: Point[];
50
+ }[];
51
+ }
52
+
53
+ export const getEndLines = (
54
+ rightPath: IPoint[],
55
+ leftPath: IPoint[],
56
+ ): {
57
+ top: IPoint[];
58
+ bottom: IPoint[];
59
+ } => {
60
+ return {
61
+ top: [rightPath[0], leftPath[0]],
62
+ bottom: [rightPath[rightPath.length - 1], leftPath[leftPath.length - 1]],
63
+ };
64
+ };
65
+
66
+ export const overlaps = (top1: number, bottom1: number, top2: number, bottom2: number): boolean => top1 <= bottom2 && top2 <= bottom1;
67
+
68
+ export const strictlyOverlaps = (top1: number, bottom1: number, top2: number, bottom2: number): boolean => top1 < bottom2 && top2 < bottom1;
69
+
70
+ export const uniq = <T>(arr: T[]): T[] => Array.from<T>(new Set(arr));
71
+
72
+ const findIntersectingItems = (
73
+ start: number,
74
+ end: number,
75
+ otherStrings: (Casing | Completion)[],
76
+ holes: HoleSize[],
77
+ ): { overlappingHoles: HoleSize[]; overlappingOuterStrings: (Casing | Completion)[] } => {
78
+ const overlappingHoles = holes.filter((hole: HoleSize) => overlaps(start, end, hole.start, hole.end));
79
+
80
+ const overlappingOuterStrings = otherStrings.filter((casing: Casing | Completion) => overlaps(start, end, casing.start, casing.end));
81
+
82
+ return {
83
+ overlappingHoles,
84
+ overlappingOuterStrings,
85
+ };
86
+ };
87
+
88
+ export const getUniqueDiameterChangeDepths = (
89
+ [intervalStart, intervalEnd]: [number, number],
90
+ diameterIntervals: { start: number; end: number }[],
91
+ ): number[] => {
92
+ const epsilon = 0.0001;
93
+ const diameterChangeDepths = diameterIntervals.flatMap(
94
+ (
95
+ d, // to find diameter right before/after object
96
+ ) => [d.start - epsilon, d.start, d.end, d.end + epsilon],
97
+ );
98
+ const trimmedChangedDepths = diameterChangeDepths.filter((d) => d >= intervalStart && d <= intervalEnd); // trim
99
+
100
+ trimmedChangedDepths.push(intervalStart);
101
+ trimmedChangedDepths.push(intervalEnd);
102
+
103
+ const uniqDepths = uniq(trimmedChangedDepths);
104
+ return uniqDepths.sort((a: number, b: number) => a - b);
105
+ };
106
+
107
+ const getInnerStringDiameter = (stringType: Casing | Completion): number =>
108
+ stringType.kind === 'casing' ? stringType.innerDiameter : stringType.diameter;
109
+
110
+ export const findCementOuterDiameterAtDepth = (
111
+ attachedStrings: (Casing | Completion)[],
112
+ nonAttachedStrings: (Casing | Completion)[],
113
+ holes: HoleSize[],
114
+ depth: number,
115
+ ): number => {
116
+ const defaultCementWidth = 100; // Default to flow cement outside to show error in data
117
+
118
+ const attachedStringAtDepth = attachedStrings.find(
119
+ (casingOrCompletion: Casing | Completion) => casingOrCompletion.start <= depth && casingOrCompletion.end >= depth,
120
+ );
121
+ const attachedOuterDiameter = attachedStringAtDepth ? attachedStringAtDepth.diameter : 0;
122
+
123
+ const outerCasingAtDepth = nonAttachedStrings
124
+ .filter((casingOrCompletion: Casing | Completion) => getInnerStringDiameter(casingOrCompletion) > attachedOuterDiameter)
125
+ .sort((a: Casing | Completion, b: Casing | Completion) => getInnerStringDiameter(a) - getInnerStringDiameter(b)) // ascending
126
+ .find((casing) => casing.start <= depth && casing.end >= depth);
127
+
128
+ const holeAtDepth = holes.find((hole: HoleSize) => hole.start <= depth && hole.end >= depth && hole.diameter > attachedOuterDiameter);
129
+
130
+ if (outerCasingAtDepth) {
131
+ return getInnerStringDiameter(outerCasingAtDepth);
132
+ }
133
+
134
+ if (holeAtDepth) {
135
+ return holeAtDepth.diameter;
136
+ }
137
+
138
+ return defaultCementWidth;
139
+ };
140
+
141
+ export const findPerforationOuterDiameterAtDepth = (
142
+ nonAttachedStrings: (Casing | Completion)[],
143
+ holes: HoleSize[],
144
+ depth: number,
145
+ perforationSubKind: PerforationSubKind,
146
+ ): number => {
147
+ const defaultPerforationWidth = 100; // Default to flow perforation outside to show error in data
148
+
149
+ const outerCasingAtDepth = nonAttachedStrings
150
+ .sort((a: Casing | Completion, b: Casing | Completion) => b.diameter - a.diameter) // descending
151
+ .find((casing) => casing.start <= depth && casing.end >= depth);
152
+
153
+ const holeAtDepth = holes.find((hole: HoleSize) => hole.start <= depth && hole.end >= depth);
154
+
155
+ if (outerCasingAtDepth && perforationSubKind !== 'Open hole frac pack' && perforationSubKind !== 'Open hole gravel pack') {
156
+ return getInnerStringDiameter(outerCasingAtDepth);
157
+ }
158
+
159
+ if (holeAtDepth) {
160
+ return holeAtDepth.diameter;
161
+ }
162
+
163
+ return defaultPerforationWidth;
164
+ };
165
+
166
+ export const findCementPlugInnerDiameterAtDepth = (
167
+ attachedStrings: (Casing | Completion)[],
168
+ nonAttachedStrings: (Casing | Completion)[],
169
+ holes: HoleSize[],
170
+ depth: number,
171
+ ): number => {
172
+ // Default to flow cement outside to show error in data
173
+ const defaultCementWidth = 100;
174
+ const attachedStringAtDepth = attachedStrings
175
+ .sort((a: Casing | Completion, b: Casing | Completion) => getInnerStringDiameter(a) - getInnerStringDiameter(b)) // ascending
176
+ .find((casingOrCompletion) => casingOrCompletion.start <= depth && casingOrCompletion.end >= depth);
177
+
178
+ if (attachedStringAtDepth) {
179
+ return getInnerStringDiameter(attachedStringAtDepth);
180
+ }
181
+
182
+ // Start from an attached diameter
183
+ const minimumDiameter = attachedStrings.length ? Math.min(...attachedStrings.map((c) => getInnerStringDiameter(c))) : 0;
184
+ const nonAttachedStringAtDepth = nonAttachedStrings
185
+ .sort((a: Casing | Completion, b: Casing | Completion) => getInnerStringDiameter(a) - getInnerStringDiameter(b)) // ascending
186
+ .find(
187
+ (casingOrCompletion: Casing | Completion) =>
188
+ casingOrCompletion.start <= depth && casingOrCompletion.end >= depth && minimumDiameter <= getInnerStringDiameter(casingOrCompletion),
189
+ );
190
+
191
+ if (nonAttachedStringAtDepth) {
192
+ return getInnerStringDiameter(nonAttachedStringAtDepth);
193
+ }
194
+
195
+ const holeAtDepth = holes.find((hole) => hole.start <= depth && hole.end >= depth && hole.diameter);
196
+
197
+ if (holeAtDepth) {
198
+ return holeAtDepth.diameter;
199
+ }
200
+
201
+ return defaultCementWidth;
202
+ };
203
+
204
+ export const createComplexRopeSegmentsForCement = (
205
+ cement: Cement,
206
+ casings: Casing[],
207
+ completion: Completion[],
208
+ holes: HoleSize[],
209
+ exaggerationFactor: number,
210
+ getPoints: (start: number, end: number) => Point[],
211
+ ): ComplexRopeSegment[] => {
212
+ const { attachedStrings, nonAttachedStrings } = splitByReferencedStrings(cement.referenceIds, casings, completion);
213
+
214
+ if (attachedStrings.length === 0) {
215
+ throw new Error(`Invalid cement data, can't find referenced casing/completion string for cement with id '${cement.id}'`);
216
+ }
217
+
218
+ attachedStrings.sort((a: Casing, b: Casing) => a.end - b.end); // ascending
219
+ const bottomOfCement = attachedStrings[attachedStrings.length - 1].end;
220
+
221
+ const { overlappingOuterStrings, overlappingHoles } = findIntersectingItems(cement.toc, bottomOfCement, nonAttachedStrings, holes);
222
+
223
+ const outerDiameterIntervals = [...overlappingOuterStrings, ...overlappingHoles].map((d) => ({
224
+ start: d.start,
225
+ end: d.end,
226
+ }));
227
+
228
+ const changeDepths = getUniqueDiameterChangeDepths([cement.toc, bottomOfCement], outerDiameterIntervals);
229
+
230
+ const diameterIntervals = changeDepths.flatMap((depth: number, index: number, list: number[]) => {
231
+ if (index === list.length - 1) {
232
+ return [];
233
+ }
234
+
235
+ const nextDepth = list[index + 1];
236
+ const diameterAtChangeDepth = findCementOuterDiameterAtDepth(attachedStrings, overlappingOuterStrings, overlappingHoles, depth);
237
+
238
+ return [{ top: depth, bottom: nextDepth, diameter: diameterAtChangeDepth * exaggerationFactor }];
239
+ });
240
+
241
+ const ropeSegments = diameterIntervals.map((interval) => ({
242
+ diameter: interval.diameter,
243
+ points: getPoints(interval.top, interval.bottom),
244
+ }));
245
+
246
+ return ropeSegments;
247
+ };
248
+
249
+ const splitByReferencedStrings = (
250
+ referenceIds: string[],
251
+ casings: Casing[],
252
+ completion: Completion[],
253
+ ): { attachedStrings: (Casing | Completion)[]; nonAttachedStrings: (Casing | Completion)[] } =>
254
+ [...casings, ...completion].reduce(
255
+ (acc, current) => {
256
+ if (referenceIds.includes(current.id)) {
257
+ return { ...acc, attachedStrings: [...acc.attachedStrings, current] };
258
+ }
259
+ return { ...acc, nonAttachedStrings: [...acc.nonAttachedStrings, current] };
260
+ },
261
+ { attachedStrings: [], nonAttachedStrings: [] },
262
+ );
263
+
264
+ export const createComplexRopeSegmentsForCementSqueeze = (
265
+ squeeze: CementSqueeze,
266
+ casings: Casing[],
267
+ completion: Completion[],
268
+ holes: HoleSize[],
269
+ exaggerationFactor: number,
270
+ getPoints: (start: number, end: number) => Point[],
271
+ ): ComplexRopeSegment[] => {
272
+ const { attachedStrings, nonAttachedStrings } = splitByReferencedStrings(squeeze.referenceIds, casings, completion);
273
+
274
+ if (attachedStrings.length === 0) {
275
+ throw new Error(`Invalid cement squeeze data, can't find referenced casing/completion for squeeze with id '${squeeze.id}'`);
276
+ }
277
+
278
+ const { overlappingOuterStrings, overlappingHoles } = findIntersectingItems(squeeze.start, squeeze.end, nonAttachedStrings, holes);
279
+
280
+ const outerDiameterIntervals = [...overlappingOuterStrings, ...overlappingHoles].map((d) => ({
281
+ start: d.start,
282
+ end: d.end,
283
+ }));
284
+
285
+ const changeDepths = getUniqueDiameterChangeDepths([squeeze.start, squeeze.end], outerDiameterIntervals);
286
+
287
+ const diameterIntervals = changeDepths.flatMap((depth, index, list) => {
288
+ if (index === list.length - 1) {
289
+ return [];
290
+ }
291
+
292
+ const nextDepth = list[index + 1];
293
+
294
+ const diameterAtDepth = findCementOuterDiameterAtDepth(attachedStrings, overlappingOuterStrings, overlappingHoles, depth);
295
+
296
+ return [{ top: depth, bottom: nextDepth, diameter: diameterAtDepth * exaggerationFactor }];
297
+ });
298
+
299
+ const ropeSegments = diameterIntervals.map((interval) => ({
300
+ diameter: interval.diameter,
301
+ points: getPoints(interval.top, interval.bottom),
302
+ }));
303
+
304
+ return ropeSegments;
305
+ };
306
+
307
+ export const createComplexRopeSegmentsForCementPlug = (
308
+ plug: CementPlug,
309
+ casings: Casing[],
310
+ completion: Completion[],
311
+ holes: HoleSize[],
312
+ exaggerationFactor: number,
313
+ getPoints: (start: number, end: number) => Point[],
314
+ ): ComplexRopeSegment[] => {
315
+ const { attachedStrings, nonAttachedStrings } = splitByReferencedStrings(plug.referenceIds, casings, completion);
316
+
317
+ const { overlappingHoles, overlappingOuterStrings } = findIntersectingItems(plug.start, plug.end, nonAttachedStrings, holes);
318
+ const innerDiameterIntervals = [...attachedStrings, ...overlappingHoles, ...overlappingOuterStrings].map((d) => ({
319
+ start: d.start,
320
+ end: d.end,
321
+ }));
322
+
323
+ const changeDepths = getUniqueDiameterChangeDepths([plug.start, plug.end], innerDiameterIntervals);
324
+
325
+ const diameterIntervals = changeDepths.flatMap((depth, index, list) => {
326
+ if (index === list.length - 1) {
327
+ return [];
328
+ }
329
+
330
+ const nextDepth = list[index + 1];
331
+ const diameterAtDepth = findCementPlugInnerDiameterAtDepth(attachedStrings, overlappingOuterStrings, overlappingHoles, depth);
332
+
333
+ return [{ top: depth, bottom: nextDepth, diameter: diameterAtDepth * exaggerationFactor }];
334
+ });
335
+
336
+ const ropeSegments = diameterIntervals.map((interval) => ({
337
+ diameter: interval.diameter,
338
+ points: getPoints(interval.top, interval.bottom),
339
+ }));
340
+
341
+ return ropeSegments;
342
+ };
343
+
344
+ const createGradientFill = (
345
+ canvas: HTMLCanvasElement,
346
+ canvasCtx: CanvasRenderingContext2D,
347
+ firstColor: string,
348
+ secondColor: string,
349
+ startPctOffset: number,
350
+ ): CanvasGradient => {
351
+ const halfWayPct = 0.5;
352
+ const gradient = canvasCtx.createLinearGradient(0, 0, 0, canvas.height);
353
+ gradient.addColorStop(0, firstColor);
354
+ gradient.addColorStop(halfWayPct - startPctOffset, secondColor);
355
+ gradient.addColorStop(halfWayPct + startPctOffset, secondColor);
356
+ gradient.addColorStop(1, firstColor);
357
+
358
+ return gradient;
359
+ };
360
+
361
+ export const createHoleBaseTexture = ({ firstColor, secondColor }: HoleOptions, width: number, height: number): Texture => {
362
+ const canvas = document.createElement('canvas');
363
+ canvas.width = width;
364
+ canvas.height = height;
365
+ const canvasCtx = canvas.getContext('2d');
366
+
367
+ canvasCtx.fillStyle = createGradientFill(canvas, canvasCtx, firstColor, secondColor, 0);
368
+ canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
369
+
370
+ return Texture.from(canvas);
371
+ };
372
+
373
+ export const createScreenTexture = ({ scalingFactor }: ScreenOptions): Texture => {
374
+ const canvas = document.createElement('canvas');
375
+ const size = DEFAULT_TEXTURE_SIZE * scalingFactor;
376
+ canvas.width = size;
377
+ canvas.height = size;
378
+ const canvasCtx = canvas.getContext('2d');
379
+
380
+ canvasCtx.fillStyle = 'white';
381
+ canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
382
+
383
+ const baseLineWidth = size / 10; // eslint-disable-line no-magic-numbers
384
+ canvasCtx.strokeStyle = '#AAAAAA';
385
+ canvasCtx.lineWidth = baseLineWidth;
386
+ canvasCtx.beginPath();
387
+
388
+ const distanceBetweenLines = size / 3;
389
+ for (let i = -canvas.width; i < canvas.width; i++) {
390
+ canvasCtx.moveTo(-canvas.width + distanceBetweenLines * i, -canvas.height);
391
+ canvasCtx.lineTo(canvas.width + distanceBetweenLines * i, canvas.height * 2);
392
+ }
393
+ canvasCtx.stroke();
394
+ return Texture.from(canvas);
395
+ };
396
+
397
+ export const createTubingTexture = ({ innerColor, outerColor, scalingFactor }: TubingOptions): Texture => {
398
+ const size = DEFAULT_TEXTURE_SIZE * scalingFactor;
399
+
400
+ const canvas = document.createElement('canvas');
401
+ canvas.width = size;
402
+ canvas.height = size;
403
+ const canvasCtx = canvas.getContext('2d');
404
+ const gradient = canvasCtx.createLinearGradient(0, 0, 0, size);
405
+
406
+ const innerColorStart = 0.3;
407
+ const innerColorEnd = 0.7;
408
+ gradient.addColorStop(0, outerColor);
409
+ gradient.addColorStop(innerColorStart, innerColor);
410
+ gradient.addColorStop(innerColorEnd, innerColor);
411
+ gradient.addColorStop(1, outerColor);
412
+
413
+ canvasCtx.fillStyle = gradient;
414
+ canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
415
+
416
+ return Texture.from(canvas);
417
+ };
418
+
419
+ export const createCementTexture = ({ firstColor, secondColor, scalingFactor }: CementOptions): Texture => {
420
+ const canvas = document.createElement('canvas');
421
+
422
+ const size = DEFAULT_TEXTURE_SIZE * scalingFactor;
423
+ const lineWidth = scalingFactor;
424
+ canvas.width = size;
425
+ canvas.height = size;
426
+ const canvasCtx = canvas.getContext('2d');
427
+
428
+ canvasCtx.fillStyle = firstColor;
429
+ canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
430
+ canvasCtx.lineWidth = lineWidth;
431
+ canvasCtx.fillStyle = secondColor;
432
+ canvasCtx.beginPath();
433
+
434
+ const distanceBetweenLines = size / 12; // eslint-disable-line no-magic-numbers
435
+ for (let i = -canvas.width; i < canvas.width; i++) {
436
+ canvasCtx.moveTo(-canvas.width + distanceBetweenLines * i, -canvas.height);
437
+ canvasCtx.lineTo(canvas.width + distanceBetweenLines * i, canvas.height);
438
+ }
439
+ canvasCtx.stroke();
440
+
441
+ return Texture.from(canvas);
442
+ };
443
+
444
+ export const createCementPlugTexture = ({ firstColor, secondColor, scalingFactor }: CementPlugOptions): Texture => {
445
+ const canvas = document.createElement('canvas');
446
+
447
+ const size = DEFAULT_TEXTURE_SIZE * scalingFactor;
448
+ canvas.width = size;
449
+ canvas.height = size;
450
+ const canvasCtx = canvas.getContext('2d');
451
+
452
+ canvasCtx.fillStyle = firstColor;
453
+ canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
454
+ canvasCtx.lineWidth = scalingFactor;
455
+ canvasCtx.strokeStyle = secondColor;
456
+ canvasCtx.beginPath();
457
+
458
+ canvasCtx.setLineDash([20, 10]); // eslint-disable-line no-magic-numbers
459
+ const distanceBetweenLines = size / 12; // eslint-disable-line no-magic-numbers
460
+ for (let i = -canvas.width; i < canvas.width; i++) {
461
+ canvasCtx.moveTo(-canvas.width + distanceBetweenLines * i, -canvas.height);
462
+ canvasCtx.lineTo(canvas.width + distanceBetweenLines * i, canvas.height * 2);
463
+ }
464
+ canvasCtx.stroke();
465
+
466
+ return Texture.from(canvas);
467
+ };
468
+
469
+ export const createCementSqueezeTexture = ({ firstColor, secondColor, scalingFactor }: CementSqueezeOptions): Texture => {
470
+ const canvas = document.createElement('canvas');
471
+
472
+ const size = DEFAULT_TEXTURE_SIZE * scalingFactor;
473
+ const lineWidth = scalingFactor;
474
+ canvas.width = size;
475
+ canvas.height = size;
476
+
477
+ const canvasCtx = canvas.getContext('2d');
478
+ canvasCtx.lineWidth = lineWidth;
479
+ canvasCtx.fillStyle = firstColor;
480
+ canvasCtx.strokeStyle = secondColor;
481
+
482
+ canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
483
+ canvasCtx.beginPath();
484
+
485
+ canvasCtx.setLineDash([20, 10]); // eslint-disable-line no-magic-numbers
486
+ const distanceBetweenLines = size / 12; // eslint-disable-line no-magic-numbers
487
+ for (let i = -canvas.width; i < canvas.width; i++) {
488
+ canvasCtx.moveTo(-canvas.width + distanceBetweenLines * i, -canvas.height);
489
+ canvasCtx.lineTo(canvas.width + distanceBetweenLines * i, canvas.height * 2);
490
+ }
491
+ canvasCtx.stroke();
492
+
493
+ return Texture.from(canvas);
494
+ };
495
+
496
+ export const createTubularRenderingObject = (radius: number, pathPoints: IPoint[]): TubularRenderingObject => {
497
+ const normals = createNormals(pathPoints);
498
+ const rightPath = offsetPoints(pathPoints, normals, radius);
499
+ const leftPath = offsetPoints(pathPoints, normals, -radius);
500
+
501
+ return { leftPath, rightPath };
502
+ };
503
+
504
+ export type CasingInterval = {
505
+ kind: 'casing' | 'casing-window';
506
+ start: number;
507
+ end: number;
508
+ };
509
+
510
+ const createCasingInterval = (start: number, end: number): CasingInterval => ({ kind: 'casing', start, end });
511
+ const createCasingWindowInterval = (start: number, end: number): CasingInterval => ({ kind: 'casing-window', start, end });
512
+
513
+ export const getCasingIntervalsWithWindows = (casing: Casing): CasingInterval[] => {
514
+ const result = (casing.windows || [])
515
+ .filter((cw: CasingWindow) => strictlyOverlaps(casing.start, casing.end, cw.start, cw.end))
516
+ .reduce<{ intervals: CasingInterval[]; lastBottom: number }>(
517
+ ({ intervals, lastBottom }, currentWindow: CasingWindow, index: number, list: CasingWindow[]) => {
518
+ const startCasingInterval: CasingInterval | null =
519
+ // last bottom before current start?
520
+ lastBottom < currentWindow.start ? createCasingInterval(lastBottom, currentWindow.start) : null;
521
+
522
+ const updatedLastBottom = startCasingInterval ? startCasingInterval.end : lastBottom;
523
+
524
+ const windowStart = Math.max(updatedLastBottom, currentWindow.start);
525
+ const windowEnd = Math.min(casing.end, currentWindow.end);
526
+ const windowInterval: CasingInterval = createCasingWindowInterval(windowStart, windowEnd);
527
+
528
+ const nextLastBottom = windowEnd;
529
+
530
+ const isLastWindow = index === list.length - 1;
531
+ const endCasingInterval: CasingInterval | null =
532
+ isLastWindow &&
533
+ // still room for a casing interval?
534
+ nextLastBottom < casing.end
535
+ ? createCasingInterval(nextLastBottom, casing.end)
536
+ : null;
537
+
538
+ const newIntervals: CasingInterval[] = [startCasingInterval, windowInterval, endCasingInterval].filter((i) => i);
539
+
540
+ return { intervals: [...intervals, ...newIntervals], lastBottom: nextLastBottom };
541
+ },
542
+ { intervals: [], lastBottom: casing.start },
543
+ );
544
+
545
+ if (!result.intervals.length) {
546
+ return [createCasingInterval(casing.start, casing.end)];
547
+ }
548
+
549
+ return result.intervals;
550
+ };
551
+
552
+ export const prepareCasingRenderObject = (
553
+ exaggerationFactor: number,
554
+ casing: Casing,
555
+ getPathPoints: (start: number, end: number) => Point[],
556
+ ): CasingRenderObject => {
557
+ const exaggeratedDiameter = casing.diameter * exaggerationFactor;
558
+ const exaggeratedRadius = exaggeratedDiameter / 2;
559
+ const exaggeratedInnerDiameter = casing.innerDiameter * exaggerationFactor;
560
+ const exaggeratedInnerRadius = exaggeratedInnerDiameter / 2;
561
+ const casingWallWidth = exaggeratedRadius - exaggeratedInnerRadius;
562
+
563
+ const sections = getCasingIntervalsWithWindows(casing).map((casingInterval: CasingInterval) => {
564
+ const pathPoints = getPathPoints(casingInterval.start, casingInterval.end);
565
+ const { leftPath, rightPath } = createTubularRenderingObject(exaggeratedRadius, pathPoints);
566
+ return { kind: casingInterval.kind, leftPath, rightPath, pathPoints };
567
+ });
568
+
569
+ return {
570
+ kind: 'casing',
571
+ id: casing.id,
572
+ referenceDiameter: exaggeratedDiameter,
573
+ referenceRadius: exaggeratedRadius,
574
+ sections,
575
+ casingWallWidth,
576
+ hasShoe: casing.hasShoe,
577
+ bottom: casing.end,
578
+ };
579
+ };
580
+
581
+ export const createComplexRopeSegmentsForPerforation = (
582
+ perforation: Perforation,
583
+ casings: Casing[],
584
+ holes: HoleSize[],
585
+ exaggerationFactor: number,
586
+ getPoints: (start: number, end: number) => Point[],
587
+ ): ComplexRopeSegment[] => {
588
+ const { overlappingOuterStrings, overlappingHoles } = findIntersectingItems(perforation.start, perforation.end, casings, holes);
589
+
590
+ const outerDiameterIntervals = [...overlappingOuterStrings, ...overlappingHoles].map((d) => ({
591
+ start: d.start,
592
+ end: d.end,
593
+ }));
594
+
595
+ const changeDepths = getUniqueDiameterChangeDepths([perforation.start, perforation.end], outerDiameterIntervals);
596
+
597
+ const diameterIntervals = changeDepths.flatMap((depth, index, list) => {
598
+ if (index === list.length - 1) {
599
+ return [];
600
+ }
601
+
602
+ const nextDepth = list[index + 1];
603
+
604
+ const diameterAtDepth = findPerforationOuterDiameterAtDepth(overlappingOuterStrings, overlappingHoles, depth, perforation.subKind);
605
+
606
+ return [{ top: depth, bottom: nextDepth, diameter: diameterAtDepth * exaggerationFactor }];
607
+ });
608
+
609
+ const ropeSegments = diameterIntervals.map((interval) => {
610
+ const points = getPoints(interval.top, interval.bottom);
611
+
612
+ const diameter = interval.diameter;
613
+
614
+ return {
615
+ diameter,
616
+ points,
617
+ };
618
+ });
619
+
620
+ return ropeSegments;
621
+ };
622
+
623
+ const drawPacking = (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, perforationOptions: PerforationOptions) => {
624
+ const { packingOpacity, yellow } = perforationOptions;
625
+
626
+ ctx.fillStyle = yellow;
627
+ ctx.strokeStyle = yellow;
628
+
629
+ const xy: [number, number] = [0, 0];
630
+ const wh: [number, number] = [canvas.width, canvas.height];
631
+ ctx.save();
632
+ ctx.globalAlpha = packingOpacity;
633
+ ctx.fillRect(...xy, ...wh);
634
+ ctx.restore();
635
+ };
636
+
637
+ const drawFracLines = (
638
+ canvas: HTMLCanvasElement,
639
+ ctx: CanvasRenderingContext2D,
640
+ extendedPerfShapeDiameter: number,
641
+ perforationOptions: PerforationOptions,
642
+ startAt: 'diameter' | 'spike',
643
+ ) => {
644
+ const { fracLineCurve } = perforationOptions;
645
+
646
+ const amountOfSpikes = 10;
647
+ const spikeWidth = canvas.width / amountOfSpikes;
648
+
649
+ const diameter = (extendedPerfShapeDiameter / 3) * perforationOptions.scalingFactor;
650
+
651
+ const fracLineLength = diameter / 4;
652
+ const spikeLength = diameter / 2;
653
+ const offsetX = 0;
654
+ const offsetY = startAt === 'diameter' ? 0 : spikeLength;
655
+
656
+ ctx.globalAlpha = perforationOptions.packingOpacity;
657
+
658
+ const fracLines = () => {
659
+ for (let i = -1; i < amountOfSpikes; i++) {
660
+ const bottom: [number, number] = [i * spikeWidth + offsetX + spikeWidth / 2, canvas.height / 2 - fracLineLength - offsetY - fracLineLength];
661
+
662
+ ctx.beginPath();
663
+
664
+ const start: [number, number] = [...bottom];
665
+ const controlPoint1: [number, number] = [bottom[0] - fracLineCurve * 2, bottom[1] - fracLineLength / 4];
666
+ const middle: [number, number] = [bottom[0], bottom[1] - fracLineLength / 2];
667
+
668
+ const controlPoint2: [number, number] = [bottom[0] + fracLineCurve * 2, bottom[1] - fracLineLength / 2 - fracLineLength / 4];
669
+ const end: [number, number] = [bottom[0], bottom[1] - fracLineLength];
670
+
671
+ ctx.bezierCurveTo(...start, ...controlPoint1, ...middle);
672
+ ctx.bezierCurveTo(...middle, ...controlPoint2, ...end);
673
+
674
+ ctx.stroke();
675
+ }
676
+
677
+ for (let i = -1; i < amountOfSpikes; i++) {
678
+ const bottom: [number, number] = [i * spikeWidth + spikeWidth + offsetX + spikeWidth / 2, canvas.height / 2 + diameter / 2 + offsetY];
679
+
680
+ ctx.beginPath();
681
+
682
+ const start: [number, number] = [...bottom];
683
+ const controlPoint1: [number, number] = [bottom[0] - fracLineCurve * 2, bottom[1] + fracLineLength / 4];
684
+ const middle: [number, number] = [bottom[0], bottom[1] + fracLineLength / 2];
685
+
686
+ const controlPoint2: [number, number] = [bottom[0] + fracLineCurve * 2, bottom[1] + fracLineLength / 2 + fracLineLength / 4];
687
+ const end: [number, number] = [bottom[0], bottom[1] + fracLineLength];
688
+
689
+ ctx.bezierCurveTo(...start, ...controlPoint1, ...middle);
690
+ ctx.bezierCurveTo(...middle, ...controlPoint2, ...end);
691
+
692
+ ctx.stroke();
693
+ }
694
+ };
695
+
696
+ ctx.strokeStyle = perforationOptions.yellow;
697
+ ctx.lineWidth = 6;
698
+ ctx.save();
699
+ fracLines();
700
+ ctx.restore();
701
+ ctx.lineWidth = 1;
702
+ ctx.strokeStyle = perforationOptions.outline;
703
+ fracLines();
704
+
705
+ ctx.closePath();
706
+ };
707
+
708
+ const drawSpikes = (
709
+ canvas: HTMLCanvasElement,
710
+ ctx: CanvasRenderingContext2D,
711
+ extendedPerfShapeDiameter: number,
712
+ perforationOptions: PerforationOptions,
713
+ ) => {
714
+ const amountOfSpikes = 4;
715
+ const spikeWidth = canvas.width / amountOfSpikes;
716
+ ctx.strokeStyle = perforationOptions.outline;
717
+
718
+ const diameter = (extendedPerfShapeDiameter / 3) * perforationOptions.scalingFactor;
719
+
720
+ ctx.lineWidth = 1;
721
+ const spikeLength = diameter / 2;
722
+
723
+ // left spikes
724
+ for (let i = 0; i <= amountOfSpikes; i++) {
725
+ const left: [number, number] = [i * spikeWidth, canvas.height / 2 - diameter / 2];
726
+ const bottom: [number, number] = [left[0] - spikeWidth / 2, left[1] - spikeLength];
727
+ const right: [number, number] = [left[0] - spikeWidth, left[1]];
728
+
729
+ ctx.beginPath();
730
+ ctx.moveTo(...left);
731
+ ctx.lineTo(...bottom);
732
+ ctx.lineTo(...right);
733
+ ctx.fill();
734
+
735
+ ctx.lineWidth = 1;
736
+ ctx.stroke();
737
+ }
738
+
739
+ // right spikes
740
+ for (let i = 0; i <= amountOfSpikes; i++) {
741
+ const left: [number, number] = [i * spikeWidth, canvas.height / 2 + diameter / 2];
742
+ const bottom: [number, number] = [left[0] - spikeWidth / 2, left[1] + spikeLength];
743
+ const right: [number, number] = [left[0] - spikeWidth, left[1]];
744
+
745
+ ctx.beginPath();
746
+ ctx.moveTo(...left);
747
+ ctx.lineTo(...bottom);
748
+ ctx.lineTo(...right);
749
+ ctx.fill();
750
+
751
+ ctx.lineWidth = 1;
752
+ ctx.stroke();
753
+ }
754
+
755
+ ctx.closePath();
756
+ };
757
+
758
+ // for visual debugging
759
+ // if this shoes up, something is wrong
760
+ const errorTexture = (errorMessage = 'Error!', existingContext?: { canvas: HTMLCanvasElement; canvasCtx: CanvasRenderingContext2D }) => {
761
+ console.error(`${errorMessage}`);
762
+ const canvas = existingContext?.canvas || document.createElement('canvas');
763
+
764
+ const size = DEFAULT_TEXTURE_SIZE;
765
+ canvas.width = size / 2;
766
+ canvas.height = size;
767
+ const canvasCtx = existingContext?.canvasCtx || canvas.getContext('2d');
768
+
769
+ const xy: [number, number] = [0, 0];
770
+ const wh: [number, number] = [canvas.width, canvas.height];
771
+
772
+ canvasCtx.fillStyle = '#ff00ff';
773
+ canvasCtx.fillRect(...xy, ...wh);
774
+
775
+ const texture = new Texture(
776
+ Texture.from(canvas, { wrapMode: WRAP_MODES.CLAMP }).baseTexture,
777
+ null,
778
+ new Rectangle(0, 0, canvas.width, canvas.height),
779
+ null,
780
+ groupD8.MIRROR_HORIZONTAL,
781
+ );
782
+ return texture;
783
+ };
784
+
785
+ const createPerforationCanvas = (
786
+ perfShape: ComplexRopeSegment,
787
+ options: PerforationOptions,
788
+ ): { canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D } => {
789
+ const canvas = document.createElement('canvas');
790
+ const perfShapeDiameter = perfShape.diameter;
791
+ const size = perfShapeDiameter * options.scalingFactor;
792
+ canvas.width = size / 2;
793
+ canvas.height = size;
794
+ const ctx = canvas.getContext('2d');
795
+
796
+ return { canvas, ctx };
797
+ };
798
+
799
+ const createPerforationTexture = (canvas: HTMLCanvasElement) => {
800
+ const texture = new Texture(
801
+ Texture.from(canvas, { wrapMode: WRAP_MODES.CLAMP }).baseTexture,
802
+ null,
803
+ new Rectangle(0, 0, canvas.width, canvas.height),
804
+ null,
805
+ groupD8.MIRROR_HORIZONTAL,
806
+ );
807
+ return texture;
808
+ };
809
+
810
+ const compareIntersectingPerforationsBy =
811
+ (targetPerf: Perforation, comparedPerforations: Perforation[]) => (compareFunc: (comparedPerf: Perforation) => boolean) =>
812
+ comparedPerforations.some((perf) => compareFunc(perf) && intersect(targetPerf, perf));
813
+
814
+ /**
815
+ * @Perforation
816
+ * If a perforation does not overlap with another perforations of type with gravel,
817
+ * the perforation spikes are either red when open or grey when closed.
818
+ * Open and closed refers to two fields on a perforation item referencing runs.
819
+ *
820
+ * If a perforation overlaps with another perforation of type with gravel and the perforation is open,
821
+ * the perforation spikes should be yellow. If closed the perforation remains grey.
822
+ *
823
+ * Cased Hole Frac Pack:
824
+ * Makes perforations of type "Perforation" yellow if overlapping and perforation are open.
825
+ * If a perforation of type "perforation" is overlapping, the fracturation lines extends from the tip of the perforation spikes into formation.
826
+ *
827
+ * Cased Hole Gravel Pack:
828
+ * Yellow gravel. Makes perforations of type "Perforation" yellow if overlapping and perforation are open.
829
+ *
830
+ * Cased Hole Fracturation:
831
+ * Makes perforations of type "Perforation" yellow if overlapping and perforation are open.
832
+ */
833
+ const createSubkindPerforationTexture = {
834
+ packing: () => errorTexture(),
835
+ fracLines: () => errorTexture(),
836
+ spikes: (
837
+ perforation: Perforation,
838
+ perfShape: ComplexRopeSegment,
839
+ otherPerforations: Perforation[],
840
+ perforationOptions: PerforationOptions,
841
+ ): Texture => {
842
+ const { canvas, ctx } = createPerforationCanvas(perfShape, perforationOptions);
843
+
844
+ const compareBy = compareIntersectingPerforationsBy(perforation, otherPerforations);
845
+
846
+ const intersectionsWithCasedHoleGravel: boolean = compareBy(isSubkindCasedHoleGravelPack);
847
+
848
+ const intersectsWithCasedHoleFracturation: boolean = compareBy(isSubKindCasedHoleFracturation);
849
+
850
+ const intersectionsWithCasedHoleFracPack: boolean = compareBy(isSubKindCasedHoleFracPack);
851
+
852
+ const intersectsWithPerforation = intersectionsWithCasedHoleGravel || intersectsWithCasedHoleFracturation || intersectionsWithCasedHoleFracPack;
853
+
854
+ const openPerforationSpikeColor = intersectsWithPerforation ? perforationOptions.yellow : perforationOptions.red;
855
+
856
+ ctx.globalAlpha = perforationOptions.packingOpacity;
857
+ if (perforation.isOpen) {
858
+ ctx.fillStyle = openPerforationSpikeColor;
859
+ ctx.strokeStyle = openPerforationSpikeColor;
860
+ } else {
861
+ ctx.fillStyle = perforationOptions.grey;
862
+ ctx.strokeStyle = perforationOptions.grey;
863
+ }
864
+
865
+ drawSpikes(canvas, ctx, perfShape.diameter, perforationOptions);
866
+
867
+ if (intersectionsWithCasedHoleFracPack) {
868
+ drawFracLines(canvas, ctx, perfShape.diameter, perforationOptions, 'spike');
869
+ }
870
+
871
+ return createPerforationTexture(canvas);
872
+ },
873
+ };
874
+
875
+ /**
876
+ * @Cased_hole_fracturation
877
+ * Yellow fracturation lines from casing OD into formation
878
+ */
879
+ const createSubkindCasedHoleFracturationTexture = {
880
+ packing: () => errorTexture(),
881
+ fracLines: (perfShape: ComplexRopeSegment, perforationOptions: PerforationOptions): Texture => {
882
+ const { canvas, ctx } = createPerforationCanvas(perfShape, perforationOptions);
883
+ drawFracLines(canvas, ctx, perfShape.diameter, perforationOptions, 'diameter');
884
+ return createPerforationTexture(canvas);
885
+ },
886
+ spikes: () => errorTexture(),
887
+ };
888
+
889
+ /**
890
+ * @Cased_hole_frac_pack
891
+ * Yellow gravel and fracturation lines.
892
+ * Makes perforations of type "Perforation" yellow if overlapping and perforation are open.
893
+ * If no perforation of type "perforation" are overlapping, there are no fracturation lines and no spikes.
894
+ * If a perforation of type "perforation" is overlapping, the fracturation lines extends from the tip of the perforation spikes into formation.
895
+ */
896
+ const createSubkindCasedHoleFracPackTexture = {
897
+ packing: (perfShape: ComplexRopeSegment, perforationOptions: PerforationOptions): Texture => {
898
+ const { canvas, ctx } = createPerforationCanvas(perfShape, perforationOptions);
899
+ drawPacking(canvas, ctx, perforationOptions);
900
+ return createPerforationTexture(canvas);
901
+ },
902
+ fracLines: (perfShape: ComplexRopeSegment, perforationOptions: PerforationOptions) => {
903
+ const { canvas } = createPerforationCanvas(perfShape, perforationOptions);
904
+ return createPerforationTexture(canvas);
905
+ },
906
+ spikes: () => errorTexture(),
907
+ };
908
+
909
+ /**
910
+ * @Cased_hole_gravel_pack
911
+ * Yellow gravel. Makes perforations of type "Perforation" yellow if overlapping and perforation are open.
912
+ */
913
+ const createSubkindCasedHoleGravelPackTexture = {
914
+ packing: (perfShape: ComplexRopeSegment, perforationOptions: PerforationOptions): Texture => {
915
+ const { canvas, ctx } = createPerforationCanvas(perfShape, perforationOptions);
916
+ drawPacking(canvas, ctx, perforationOptions);
917
+ return createPerforationTexture(canvas);
918
+ },
919
+ fracLines: () => errorTexture(),
920
+ spikes: () => errorTexture(),
921
+ };
922
+
923
+ /**
924
+ * @Open_hole_gravel_pack
925
+ * Yellow gravel
926
+ */
927
+ const createSubkindOpenHoleGravelPackTexture = {
928
+ packing: (perfShape: ComplexRopeSegment, perforationOptions: PerforationOptions) => {
929
+ const { canvas, ctx } = createPerforationCanvas(perfShape, perforationOptions);
930
+ drawPacking(canvas, ctx, perforationOptions);
931
+ return createPerforationTexture(canvas);
932
+ },
933
+ fracLines: () => errorTexture(),
934
+ spikes: () => errorTexture(),
935
+ };
936
+
937
+ /**
938
+ * @Open_hole_frac_pack
939
+ * Yellow gravel. Yellow frac lines from hole OD into formation
940
+ */
941
+ const createSubkindOpenHoleFracPackTexture = {
942
+ packing: (_perforation: Perforation, perfShape: ComplexRopeSegment, perforationOptions: PerforationOptions) => {
943
+ const { canvas, ctx } = createPerforationCanvas(perfShape, perforationOptions);
944
+ drawPacking(canvas, ctx, perforationOptions);
945
+ return createPerforationTexture(canvas);
946
+ },
947
+ fracLines: (perfShape: ComplexRopeSegment, perforationOptions: PerforationOptions): Texture => {
948
+ const { canvas, ctx } = createPerforationCanvas(perfShape, perforationOptions);
949
+ drawFracLines(canvas, ctx, perfShape.diameter, perforationOptions, 'diameter');
950
+ return createPerforationTexture(canvas);
951
+ },
952
+ spikes: () => errorTexture(),
953
+ };
954
+
955
+ export const createPerforationPackingTexture = (
956
+ perforation: Perforation,
957
+ perfShape: ComplexRopeSegment,
958
+ perforationOptions: PerforationOptions,
959
+ ): Texture => {
960
+ return foldPerforationSubKind(
961
+ {
962
+ Perforation: () => createSubkindPerforationTexture.packing(),
963
+ CasedHoleFracturation: () => createSubkindCasedHoleFracPackTexture.packing(perfShape, perforationOptions),
964
+ CasedHoleFracPack: () => createSubkindCasedHoleFracPackTexture.packing(perfShape, perforationOptions),
965
+ OpenHoleGravelPack: () => createSubkindOpenHoleGravelPackTexture.packing(perfShape, perforationOptions),
966
+ OpenHoleFracPack: () => createSubkindOpenHoleFracPackTexture.packing(perforation, perfShape, perforationOptions),
967
+ CasedHoleGravelPack: () => createSubkindCasedHoleGravelPackTexture.packing(perfShape, perforationOptions),
968
+ },
969
+ perforation.subKind,
970
+ );
971
+ };
972
+
973
+ export const createPerforationFracLineTexture = (
974
+ perforation: Perforation,
975
+ perfShape: ComplexRopeSegment,
976
+ perforationOptions: PerforationOptions,
977
+ ): Texture => {
978
+ return foldPerforationSubKind(
979
+ {
980
+ Perforation: () => createSubkindPerforationTexture.fracLines(),
981
+ OpenHoleGravelPack: () => createSubkindOpenHoleGravelPackTexture.fracLines(),
982
+ OpenHoleFracPack: () => createSubkindOpenHoleFracPackTexture.fracLines(perfShape, perforationOptions),
983
+ CasedHoleFracturation: () => createSubkindCasedHoleFracturationTexture.fracLines(perfShape, perforationOptions),
984
+ CasedHoleGravelPack: () => createSubkindCasedHoleGravelPackTexture.fracLines(),
985
+ CasedHoleFracPack: () => createSubkindCasedHoleFracPackTexture.fracLines(perfShape, perforationOptions),
986
+ },
987
+ perforation.subKind,
988
+ );
989
+ };
990
+
991
+ export const createPerforationSpikeTexture = (
992
+ perforation: Perforation,
993
+ otherPerforations: Perforation[],
994
+ perfShape: ComplexRopeSegment,
995
+ perforationOptions: PerforationOptions,
996
+ ): Texture => {
997
+ return foldPerforationSubKind(
998
+ {
999
+ Perforation: () => createSubkindPerforationTexture.spikes(perforation, perfShape, otherPerforations, perforationOptions),
1000
+ OpenHoleGravelPack: () => createSubkindOpenHoleGravelPackTexture.spikes(),
1001
+ OpenHoleFracPack: () => createSubkindOpenHoleFracPackTexture.spikes(),
1002
+ CasedHoleFracturation: () => createSubkindCasedHoleFracturationTexture.spikes(),
1003
+ CasedHoleGravelPack: () => createSubkindCasedHoleGravelPackTexture.spikes(),
1004
+ CasedHoleFracPack: () => createSubkindCasedHoleFracPackTexture.spikes(),
1005
+ },
1006
+ perforation.subKind,
1007
+ );
1008
+ };