@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.
- package/README.md +18 -3
- package/dist/components/axis.d.ts +48 -0
- package/dist/components/axis.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/control/ExtendedCurveInterpolator.d.ts +59 -0
- package/dist/control/ExtendedCurveInterpolator.d.ts.map +1 -0
- package/dist/control/IntersectionReferenceSystem.d.ts +97 -0
- package/dist/control/IntersectionReferenceSystem.d.ts.map +1 -0
- package/dist/control/LayerManager.d.ts +77 -0
- package/dist/control/LayerManager.d.ts.map +1 -0
- package/dist/control/MainController.d.ts +155 -0
- package/dist/control/MainController.d.ts.map +1 -0
- package/dist/control/ZoomPanHandler.d.ts +159 -0
- package/dist/control/ZoomPanHandler.d.ts.map +1 -0
- package/dist/control/index.d.ts +6 -0
- package/dist/control/index.d.ts.map +1 -0
- package/dist/control/interfaces.d.ts +38 -0
- package/dist/control/interfaces.d.ts.map +1 -0
- package/dist/control/overlay.d.ts +21 -0
- package/dist/control/overlay.d.ts.map +1 -0
- package/dist/datautils/colortable.d.ts +2 -0
- package/dist/datautils/colortable.d.ts.map +1 -0
- package/dist/datautils/findsample.d.ts +3 -0
- package/dist/datautils/findsample.d.ts.map +1 -0
- package/dist/datautils/index.d.ts +7 -0
- package/dist/datautils/index.d.ts.map +1 -0
- package/dist/datautils/interfaces.d.ts +64 -0
- package/dist/datautils/interfaces.d.ts.map +1 -0
- package/dist/datautils/picks.d.ts +75 -0
- package/dist/datautils/picks.d.ts.map +1 -0
- package/dist/datautils/schematicShapeGenerator.d.ts +60 -0
- package/dist/datautils/schematicShapeGenerator.d.ts.map +1 -0
- package/dist/datautils/seismicimage.d.ts +46 -0
- package/dist/datautils/seismicimage.d.ts.map +1 -0
- package/dist/datautils/surfacedata.d.ts +11 -0
- package/dist/datautils/surfacedata.d.ts.map +1 -0
- package/dist/datautils/trajectory.d.ts +15 -0
- package/dist/datautils/trajectory.d.ts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +162 -160
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -0
- package/dist/layers/CalloutCanvasLayer.d.ts +61 -0
- package/dist/layers/CalloutCanvasLayer.d.ts.map +1 -0
- package/dist/layers/CustomDisplayObjects/ComplexRope.d.ts +22 -0
- package/dist/layers/CustomDisplayObjects/ComplexRope.d.ts.map +1 -0
- package/dist/layers/CustomDisplayObjects/ComplexRopeGeometry.d.ts +24 -0
- package/dist/layers/CustomDisplayObjects/ComplexRopeGeometry.d.ts.map +1 -0
- package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRope.d.ts +21 -0
- package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRope.d.ts.map +1 -0
- package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.d.ts +27 -0
- package/dist/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.d.ts.map +1 -0
- package/dist/layers/CustomDisplayObjects/UniformTextureStretchRope.d.ts +18 -0
- package/dist/layers/CustomDisplayObjects/UniformTextureStretchRope.d.ts.map +1 -0
- package/dist/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.d.ts +25 -0
- package/dist/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.d.ts.map +1 -0
- package/dist/layers/GeomodelCanvasLayer.d.ts +29 -0
- package/dist/layers/GeomodelCanvasLayer.d.ts.map +1 -0
- package/dist/layers/GeomodelLabelsLayer.d.ts +50 -0
- package/dist/layers/GeomodelLabelsLayer.d.ts.map +1 -0
- package/dist/layers/GeomodelLayerV2.d.ts +13 -0
- package/dist/layers/GeomodelLayerV2.d.ts.map +1 -0
- package/dist/layers/GridLayer.d.ts +30 -0
- package/dist/layers/GridLayer.d.ts.map +1 -0
- package/dist/layers/ImageCanvasLayer.d.ts +21 -0
- package/dist/layers/ImageCanvasLayer.d.ts.map +1 -0
- package/dist/layers/ReferenceLineLayer.d.ts +30 -0
- package/dist/layers/ReferenceLineLayer.d.ts.map +1 -0
- package/dist/layers/SchematicLayer.d.ts +114 -0
- package/dist/layers/SchematicLayer.d.ts.map +1 -0
- package/dist/layers/SeismicCanvasLayer.d.ts +19 -0
- package/dist/layers/SeismicCanvasLayer.d.ts.map +1 -0
- package/dist/layers/WellborePathLayer.d.ts +18 -0
- package/dist/layers/WellborePathLayer.d.ts.map +1 -0
- package/dist/layers/base/CanvasLayer.d.ts +20 -0
- package/dist/layers/base/CanvasLayer.d.ts.map +1 -0
- package/dist/layers/base/HTMLLayer.d.ts +14 -0
- package/dist/layers/base/HTMLLayer.d.ts.map +1 -0
- package/dist/layers/base/Layer.d.ts +70 -0
- package/dist/layers/base/Layer.d.ts.map +1 -0
- package/dist/layers/base/PixiLayer.d.ts +33 -0
- package/dist/layers/base/PixiLayer.d.ts.map +1 -0
- package/dist/layers/base/SVGLayer.d.ts +14 -0
- package/dist/layers/base/SVGLayer.d.ts.map +1 -0
- package/dist/layers/base/index.d.ts +6 -0
- package/dist/layers/base/index.d.ts.map +1 -0
- package/dist/layers/index.d.ts +17 -0
- package/dist/layers/index.d.ts.map +1 -0
- package/dist/layers/schematicInterfaces.d.ts +210 -0
- package/dist/layers/schematicInterfaces.d.ts.map +1 -0
- package/dist/utils/arc-length.d.ts +24 -0
- package/dist/utils/arc-length.d.ts.map +1 -0
- package/dist/utils/binary-search.d.ts +9 -0
- package/dist/utils/binary-search.d.ts.map +1 -0
- package/dist/utils/color.d.ts +6 -0
- package/dist/utils/color.d.ts.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/root-finder.d.ts +35 -0
- package/dist/utils/root-finder.d.ts.map +1 -0
- package/dist/utils/text.d.ts +15 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/vectorUtils.d.ts +16 -0
- package/dist/utils/vectorUtils.d.ts.map +1 -0
- package/dist/vendor/pixi-dashed-line/index.d.ts +57 -0
- package/dist/vendor/pixi-dashed-line/index.d.ts.map +1 -0
- package/package.json +29 -21
- package/src/.eslintrc.json +5 -0
- package/src/components/axis.ts +247 -0
- package/src/components/index.ts +1 -0
- package/src/control/ExtendedCurveInterpolator.ts +155 -0
- package/src/control/IntersectionReferenceSystem.ts +391 -0
- package/src/control/LayerManager.ts +294 -0
- package/src/control/MainController.ts +296 -0
- package/src/control/ZoomPanHandler.ts +436 -0
- package/src/control/index.ts +5 -0
- package/src/control/interfaces.ts +42 -0
- package/src/control/overlay.ts +118 -0
- package/src/datautils/colortable.ts +14 -0
- package/src/datautils/findsample.ts +72 -0
- package/src/datautils/index.ts +6 -0
- package/src/datautils/interfaces.ts +68 -0
- package/src/datautils/picks.ts +328 -0
- package/src/datautils/schematicShapeGenerator.ts +1008 -0
- package/src/datautils/seismicimage.ts +180 -0
- package/src/datautils/surfacedata.ts +317 -0
- package/src/datautils/trajectory.ts +206 -0
- package/src/layers/CalloutCanvasLayer.ts +338 -0
- package/src/layers/CustomDisplayObjects/ComplexRope.ts +44 -0
- package/src/layers/CustomDisplayObjects/ComplexRopeGeometry.ts +184 -0
- package/src/layers/CustomDisplayObjects/FixedWidthSimpleRope.ts +41 -0
- package/src/layers/CustomDisplayObjects/FixedWidthSimpleRopeGeometry.ts +149 -0
- package/src/layers/CustomDisplayObjects/UniformTextureStretchRope.ts +39 -0
- package/src/layers/CustomDisplayObjects/UniformTextureStretchRopeGeometry.ts +174 -0
- package/src/layers/GeomodelCanvasLayer.ts +176 -0
- package/src/layers/GeomodelLabelsLayer.ts +615 -0
- package/src/layers/GeomodelLayerV2.ts +111 -0
- package/src/layers/GridLayer.ts +145 -0
- package/src/layers/ImageCanvasLayer.ts +55 -0
- package/src/layers/ReferenceLineLayer.ts +185 -0
- package/src/layers/SchematicLayer.ts +870 -0
- package/src/layers/SeismicCanvasLayer.ts +46 -0
- package/src/layers/WellborePathLayer.ts +129 -0
- package/src/layers/base/CanvasLayer.ts +102 -0
- package/src/layers/base/HTMLLayer.ts +70 -0
- package/src/layers/base/Layer.ts +217 -0
- package/src/layers/base/PixiLayer.ts +190 -0
- package/src/layers/base/SVGLayer.ts +63 -0
- package/src/layers/base/index.ts +5 -0
- package/src/layers/index.ts +16 -0
- package/src/layers/schematicInterfaces.ts +472 -0
- package/src/tsconfig.json +9 -0
- package/src/utils/arc-length.ts +66 -0
- package/src/utils/binary-search.ts +26 -0
- package/src/utils/color.ts +22 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/root-finder.ts +78 -0
- package/src/utils/text.ts +88 -0
- package/src/utils/vectorUtils.ts +67 -0
- 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
|
+
};
|