@welldot/render 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -135,6 +135,11 @@ type ConstructionTheme = {
135
135
  stroke: string;
136
136
  strokeWidth: number;
137
137
  };
138
+ centralizer: {
139
+ fill: string;
140
+ stroke: string;
141
+ strokeWidth: number;
142
+ };
138
143
  conflict: {
139
144
  stroke: string;
140
145
  strokeWidth: number;
@@ -266,6 +271,10 @@ type ComponentsClassNames = {
266
271
  group: string;
267
272
  item: string;
268
273
  };
274
+ centralizer: {
275
+ group: string;
276
+ item: string;
277
+ };
269
278
  conflict: {
270
279
  group: string;
271
280
  rect: string;
@@ -296,7 +305,7 @@ type ComponentsClassNames = {
296
305
  };
297
306
  };
298
307
  /** Keys identifying which tooltip types are active. Pass to `RenderConfig.tooltips` to control visibility. */
299
- type TooltipKey = 'geology' | 'hole' | 'surfaceCase' | 'holeFill' | 'wellCase' | 'wellScreen' | 'reduction' | 'conflict' | 'fracture' | 'cementPad' | 'cave';
308
+ type TooltipKey = 'geology' | 'hole' | 'surfaceCase' | 'holeFill' | 'wellCase' | 'wellScreen' | 'reduction' | 'centralizer' | 'conflict' | 'fracture' | 'cementPad' | 'cave';
300
309
  /**
301
310
  * A paired-locale string for renderer-drawn text, or a bare string when the
302
311
  * value doesn't vary by locale. `pt` is the required fallback locale in the
@@ -344,6 +353,11 @@ type TooltipLabels<T = string> = {
344
353
  reduction: {
345
354
  title: T;
346
355
  };
356
+ centralizer: {
357
+ title: T;
358
+ depth: T;
359
+ spacing: T;
360
+ };
347
361
  conflict: {
348
362
  title: T;
349
363
  };
@@ -443,6 +457,10 @@ type RenderConfig = {
443
457
  surfaceCase: {
444
458
  diameterPaddingRatio: number;
445
459
  };
460
+ centralizer: {
461
+ /** Half the marker height in pixels (constant regardless of zoom). */
462
+ markerHalfHeight: number;
463
+ };
446
464
  };
447
465
  textures?: TexturesConfig;
448
466
  constructionLabels: {
@@ -526,6 +544,7 @@ type LegendRenderConfig = {
526
544
  wellCase: string;
527
545
  wellScreen: string;
528
546
  reduction: string;
547
+ centralizer: string;
529
548
  cementPad: string;
530
549
  conflict: string;
531
550
  };
package/dist/index.d.ts CHANGED
@@ -135,6 +135,11 @@ type ConstructionTheme = {
135
135
  stroke: string;
136
136
  strokeWidth: number;
137
137
  };
138
+ centralizer: {
139
+ fill: string;
140
+ stroke: string;
141
+ strokeWidth: number;
142
+ };
138
143
  conflict: {
139
144
  stroke: string;
140
145
  strokeWidth: number;
@@ -266,6 +271,10 @@ type ComponentsClassNames = {
266
271
  group: string;
267
272
  item: string;
268
273
  };
274
+ centralizer: {
275
+ group: string;
276
+ item: string;
277
+ };
269
278
  conflict: {
270
279
  group: string;
271
280
  rect: string;
@@ -296,7 +305,7 @@ type ComponentsClassNames = {
296
305
  };
297
306
  };
298
307
  /** Keys identifying which tooltip types are active. Pass to `RenderConfig.tooltips` to control visibility. */
299
- type TooltipKey = 'geology' | 'hole' | 'surfaceCase' | 'holeFill' | 'wellCase' | 'wellScreen' | 'reduction' | 'conflict' | 'fracture' | 'cementPad' | 'cave';
308
+ type TooltipKey = 'geology' | 'hole' | 'surfaceCase' | 'holeFill' | 'wellCase' | 'wellScreen' | 'reduction' | 'centralizer' | 'conflict' | 'fracture' | 'cementPad' | 'cave';
300
309
  /**
301
310
  * A paired-locale string for renderer-drawn text, or a bare string when the
302
311
  * value doesn't vary by locale. `pt` is the required fallback locale in the
@@ -344,6 +353,11 @@ type TooltipLabels<T = string> = {
344
353
  reduction: {
345
354
  title: T;
346
355
  };
356
+ centralizer: {
357
+ title: T;
358
+ depth: T;
359
+ spacing: T;
360
+ };
347
361
  conflict: {
348
362
  title: T;
349
363
  };
@@ -443,6 +457,10 @@ type RenderConfig = {
443
457
  surfaceCase: {
444
458
  diameterPaddingRatio: number;
445
459
  };
460
+ centralizer: {
461
+ /** Half the marker height in pixels (constant regardless of zoom). */
462
+ markerHalfHeight: number;
463
+ };
446
464
  };
447
465
  textures?: TexturesConfig;
448
466
  constructionLabels: {
@@ -526,6 +544,7 @@ type LegendRenderConfig = {
526
544
  wellCase: string;
527
545
  wellScreen: string;
528
546
  reduction: string;
547
+ centralizer: string;
529
548
  cementPad: string;
530
549
  conflict: string;
531
550
  };
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ var RENDER_LABELS = {
45
45
  wellCase: { pt: "Revestimento", en: "Casing" },
46
46
  wellScreen: { pt: "Filtro", en: "Screen" },
47
47
  reduction: { pt: "Redu\xE7\xE3o", en: "Reduction" },
48
+ centralizer: { pt: "Centralizador", en: "Centralizer" },
48
49
  cementPad: { pt: "Laje de cimento", en: "Cement pad" },
49
50
  conflict: { pt: "Conflito", en: "Conflict" }
50
51
  }
@@ -71,6 +72,11 @@ var RENDER_LABELS = {
71
72
  slot: { pt: "Ranhura:", en: "Slot:" }
72
73
  },
73
74
  reduction: { title: { pt: "REDU\xC7\xC3O", en: "REDUCTION" } },
75
+ centralizer: {
76
+ title: { pt: "CENTRALIZADOR", en: "CENTRALIZER" },
77
+ depth: { pt: "Profundidade:", en: "Depth:" },
78
+ spacing: { pt: "Espa\xE7amento:", en: "Spacing:" }
79
+ },
74
80
  conflict: { title: { pt: "CONFLITO", en: "CONFLICT" } },
75
81
  fracture: {
76
82
  title: { pt: "FRATURA", en: "FRACTURE" },
@@ -116,6 +122,7 @@ function resolveTooltipLabels(locale) {
116
122
  wellCase: resolveGroup(t.wellCase, locale),
117
123
  wellScreen: resolveGroup(t.wellScreen, locale),
118
124
  reduction: resolveGroup(t.reduction, locale),
125
+ centralizer: resolveGroup(t.centralizer, locale),
119
126
  conflict: resolveGroup(t.conflict, locale),
120
127
  fracture: resolveGroup(t.fracture, locale),
121
128
  cementPad: resolveGroup(t.cementPad, locale),
@@ -176,6 +183,7 @@ var DEFAULT_WELL_THEME = {
176
183
  wellCase: { fill: "#ffffff", stroke: "#303030", strokeWidth: 2 },
177
184
  wellScreen: { stroke: "#303030", strokeWidth: 2 },
178
185
  reduction: { fill: "#ffffff", stroke: "#303030", strokeWidth: 2 },
186
+ centralizer: { fill: "none", stroke: "#303030", strokeWidth: 1.4 },
179
187
  conflict: { stroke: "#e52117", strokeWidth: 4 },
180
188
  unitLabels: {
181
189
  geologicFill: "#f0f0f0",
@@ -269,7 +277,8 @@ var STATIC_RENDER_CONFIG = {
269
277
  },
270
278
  construction: {
271
279
  cementPad: { widthMultiplier: 0.9, thicknessMultiplier: 1.3 },
272
- surfaceCase: { diameterPaddingRatio: 0.1 }
280
+ surfaceCase: { diameterPaddingRatio: 0.1 },
281
+ centralizer: { markerHalfHeight: 4 }
273
282
  },
274
283
  tooltipLabels: resolveTooltipLabels("pt"),
275
284
  labels: {
@@ -340,6 +349,7 @@ var STATIC_RENDER_CONFIG = {
340
349
  wellCase: "Revestimento",
341
350
  wellScreen: "Filtro",
342
351
  reduction: "Redu\xE7\xE3o",
352
+ centralizer: "Centralizador",
343
353
  cementPad: "Laje de cimento",
344
354
  conflict: "Conflito"
345
355
  }
@@ -380,7 +390,8 @@ var INTERACTIVE_RENDER_CONFIG = {
380
390
  },
381
391
  construction: {
382
392
  cementPad: { widthMultiplier: 0.9, thicknessMultiplier: 1.3 },
383
- surfaceCase: { diameterPaddingRatio: 0.1 }
393
+ surfaceCase: { diameterPaddingRatio: 0.1 },
394
+ centralizer: { markerHalfHeight: 4 }
384
395
  },
385
396
  tooltipLabels: resolveTooltipLabels("pt"),
386
397
  labels: {
@@ -448,6 +459,7 @@ var INTERACTIVE_RENDER_CONFIG = {
448
459
  wellCase: "Revestimento",
449
460
  wellScreen: "Filtro",
450
461
  reduction: "Redu\xE7\xE3o",
462
+ centralizer: "Centralizador",
451
463
  cementPad: "Laje de cimento",
452
464
  conflict: "Conflito"
453
465
  }
@@ -532,7 +544,7 @@ import {
532
544
  } from "@welldot/core";
533
545
  import {
534
546
  formatNumber as formatNumber3,
535
- getProfileDiamValues as getProfileDiamValues4,
547
+ getProfileDiamValues as getProfileDiamValues5,
536
548
  getProfileLastItemsDepths
537
549
  } from "@welldot/utils";
538
550
 
@@ -592,6 +604,10 @@ var DEFAULT_COMPONENTS_CLASS_NAMES = {
592
604
  group: "well-screen",
593
605
  rect: "well-screen-rect"
594
606
  },
607
+ centralizer: {
608
+ group: "centralizer",
609
+ item: "centralizer-marker"
610
+ },
595
611
  reduction: {
596
612
  group: "reduction",
597
613
  item: "reduction-poly"
@@ -769,6 +785,15 @@ var populateTooltips = (svg, customClasses, units, tooltipConfig, labels, locale
769
785
  </span>
770
786
  <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.type}</strong> ${esc(d.type)}</span>
771
787
  `,
788
+ centralizer: (_, d) => `
789
+ <span class="${customClasses.tooltip.title}">${labels.centralizer.title}</span>
790
+ <span class="${customClasses.tooltip.primaryInfo}"><strong>${labels.centralizer.depth}</strong> ${esc(formatLength(d.depth, units.length, locale))} ${esc(getLengthUnit(units.length))}</span>
791
+ ${d.from !== d.to ? `<span class="${customClasses.tooltip.secondaryInfo}">${labels.common.from} ${esc(formatLength(d.from, units.length, locale))} ${esc(getLengthUnit(units.length))} ${labels.common.to} ${esc(formatLength(d.to, units.length, locale))} ${esc(getLengthUnit(units.length))}</span>` : ""}
792
+ ${d.spacing ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.centralizer.spacing}</strong> ${esc(formatLength(d.spacing, units.length, locale))} ${esc(getLengthUnit(units.length))}</span>` : ""}
793
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.type}</strong> ${esc(d.type)}</span>
794
+ ${d.diameter ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.diameter}</strong> ${esc(formatDiameter(d.diameter, units.diameter, locale))} ${esc(getDiameterUnit(units.diameter, locale))}</span>` : ""}
795
+ ${d.description ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.description}</strong> ${esc(d.description)}</span>` : ""}
796
+ `,
772
797
  conflict: (_, d) => `
773
798
  <span class="${customClasses.tooltip.title}">${labels.conflict.title}</span>
774
799
  <span class="${customClasses.tooltip.primaryInfo}">${labels.common.from} ${esc(formatLength(d.from, units.length, locale))} ${esc(getLengthUnit(units.length))} ${labels.common.to} ${esc(formatLength(d.to, units.length, locale))} ${esc(getLengthUnit(units.length))}</span>
@@ -1082,9 +1107,51 @@ function drawCaves(ctx, data) {
1082
1107
  merged.transition(ctx.transition).style("opacity", 1);
1083
1108
  }
1084
1109
 
1085
- // src/renderers/construction-labels.renderer.ts
1110
+ // src/renderers/centralizers.renderer.ts
1086
1111
  import * as d32 from "d3";
1087
- import { getProfileDiamValues } from "@welldot/utils";
1112
+ import { getCentralizerDepths, getProfileDiamValues } from "@welldot/utils";
1113
+ function diameterAt(items, depth) {
1114
+ return items.find((i) => depth >= i.from && depth <= i.to)?.diameter;
1115
+ }
1116
+ function drawCentralizers(ctx, data) {
1117
+ const group = ctx.groups.centralizerGroup;
1118
+ const cls = ctx.classes.centralizer;
1119
+ const maxXValue = d32.max(getProfileDiamValues(ctx.constructionData)) || 0;
1120
+ const xScale = d32.scaleLinear().domain([0, maxXValue]).range([0, ctx.POCO_WIDTH]);
1121
+ const pipes = [
1122
+ ...ctx.constructionData.well_case,
1123
+ ...ctx.constructionData.well_screen
1124
+ ];
1125
+ const holes = ctx.constructionData.bore_hole;
1126
+ const markers = (data.centralizers ?? []).flatMap(
1127
+ (c) => getCentralizerDepths(c).filter((depth) => depth >= ctx.depthFrom && depth < ctx.depthTo).map((depth) => ({ ...c, depth }))
1128
+ );
1129
+ const halfH = ctx.renderConfig.construction.centralizer.markerHalfHeight;
1130
+ const bowPoints = (m, side) => {
1131
+ const outerD = m.diameter ?? diameterAt(holes, m.depth) ?? maxXValue;
1132
+ const pipeD = diameterAt(pipes, m.depth) ?? outerD * 0.75;
1133
+ const pipeX = (ctx.POCO_CENTER + side * xScale(pipeD)) / 2;
1134
+ const outerX = (ctx.POCO_CENTER + side * xScale(outerD)) / 2;
1135
+ const y = ctx.yScale(m.depth);
1136
+ return `${pipeX},${y - halfH} ${outerX},${y} ${pipeX},${y + halfH}`;
1137
+ };
1138
+ const selection = group.selectAll(`g.${cls.item}`).data(markers, (d) => `${d.from}:${d.to}:${d.depth}`);
1139
+ selection.exit().remove();
1140
+ const entered = selection.enter().append("g").attr("class", cls.item).on("mouseover", ctx.tooltips.centralizer.show).on("mouseout", ctx.tooltips.centralizer.hide);
1141
+ entered.append("polyline").attr("class", `${cls.item}-left`);
1142
+ entered.append("polyline").attr("class", `${cls.item}-right`);
1143
+ const merged = entered.merge(selection);
1144
+ merged.selectAll("polyline").attr("fill", ctx.theme.centralizer.fill).attr("stroke", ctx.theme.centralizer.stroke).attr("stroke-width", ctx.theme.centralizer.strokeWidth).attr("stroke-linejoin", "round");
1145
+ merged.each((d, i, nodes) => {
1146
+ const g = d32.select(nodes[i]);
1147
+ g.select(`.${cls.item}-left`).attr("points", bowPoints(d, -1));
1148
+ g.select(`.${cls.item}-right`).attr("points", bowPoints(d, 1));
1149
+ });
1150
+ }
1151
+
1152
+ // src/renderers/construction-labels.renderer.ts
1153
+ import * as d33 from "d3";
1154
+ import { getProfileDiamValues as getProfileDiamValues2 } from "@welldot/utils";
1088
1155
  function drawConstructionLabels(ctx, data) {
1089
1156
  if (!ctx.renderConfig.constructionLabels.active) return;
1090
1157
  ctx.groups.constructionLabelsGroup.selectAll("*").remove();
@@ -1092,8 +1159,8 @@ function drawConstructionLabels(ctx, data) {
1092
1159
  const clct = ctx.theme.constructionLabels;
1093
1160
  const fmtD = (mm) => formatDiameter(mm, ctx.units.diameter, ctx.locale);
1094
1161
  const dUnit = getDiameterUnit(ctx.units.diameter, ctx.locale);
1095
- const maxXValues = getProfileDiamValues(ctx.constructionData);
1096
- const xScale = d32.scaleLinear().domain([0, d32.max(maxXValues) || 0]).range([0, ctx.POCO_WIDTH]);
1162
+ const maxXValues = getProfileDiamValues2(ctx.constructionData);
1163
+ const xScale = d33.scaleLinear().domain([0, d33.max(maxXValues) || 0]).range([0, ctx.POCO_WIDTH]);
1097
1164
  const occupied = [];
1098
1165
  const placeLabel = (d, text) => {
1099
1166
  const clampedFrom = Math.max(d.from, ctx.depthFrom);
@@ -1159,16 +1226,16 @@ function drawConstructionLabels(ctx, data) {
1159
1226
  }
1160
1227
 
1161
1228
  // src/renderers/construction.renderer.ts
1162
- import * as d33 from "d3";
1163
- import { getProfileDiamValues as getProfileDiamValues2 } from "@welldot/utils";
1229
+ import * as d34 from "d3";
1230
+ import { getProfileDiamValues as getProfileDiamValues3 } from "@welldot/utils";
1164
1231
  function drawConstructive(ctx, data) {
1165
1232
  const { getHeight, getYPos } = getYAxisFunctions(ctx.yScale, {
1166
1233
  from: ctx.depthFrom,
1167
1234
  to: ctx.depthTo
1168
1235
  });
1169
- const maxXValues = getProfileDiamValues2(ctx.constructionData);
1170
- const maxXValueConstruction = d33.max(maxXValues) || 0;
1171
- const xScale = d33.scaleLinear().domain([0, maxXValueConstruction]).range([0, ctx.POCO_WIDTH]);
1236
+ const maxXValues = getProfileDiamValues3(ctx.constructionData);
1237
+ const maxXValueConstruction = d34.max(maxXValues) || 0;
1238
+ const xScale = d34.scaleLinear().domain([0, maxXValueConstruction]).range([0, ctx.POCO_WIDTH]);
1172
1239
  const rcc = ctx.renderConfig.construction;
1173
1240
  const yPos = getYPos;
1174
1241
  ctx.groups.constructionGroup.selectAll(`.${ctx.classes.cementPad.item}`).remove();
@@ -1207,11 +1274,11 @@ function drawConstructive(ctx, data) {
1207
1274
  const w = xScale(
1208
1275
  d.diameter + d.diameter * rcc.surfaceCase.diameterPaddingRatio
1209
1276
  );
1210
- const g = d33.select(nodes[i]);
1277
+ const g = d34.select(nodes[i]);
1211
1278
  g.select(".surface-case-fill").attr("x", x).attr("width", w).attr("fill", ctx.textures.surface_case.url());
1212
1279
  const sideNodes = g.selectAll(".surface-case-side").nodes();
1213
- d33.select(sideNodes[0]).attr("x1", x).attr("x2", x);
1214
- d33.select(sideNodes[1]).attr("x1", x + w).attr("x2", x + w);
1280
+ d34.select(sideNodes[0]).attr("x1", x).attr("x2", x);
1281
+ d34.select(sideNodes[1]).attr("x1", x + w).attr("x2", x + w);
1215
1282
  });
1216
1283
  withTransition(
1217
1284
  mergedSC.select(".surface-case-fill"),
@@ -1268,7 +1335,7 @@ function drawConstructive(ctx, data) {
1268
1335
  }
1269
1336
 
1270
1337
  // src/renderers/fractures.renderer.ts
1271
- import * as d34 from "d3";
1338
+ import * as d35 from "d3";
1272
1339
  function wavyLine(rng, steps, baseY, jitter, startInset, endInset) {
1273
1340
  const pts = [];
1274
1341
  for (let i = 0; i < steps; i++) {
@@ -1305,7 +1372,7 @@ function drawFractures(ctx, data) {
1305
1372
  (fracture) => `translate(0,${yScale(fracture.depth)}) rotate(${fracture.dip},${pocoCenterX},0)`
1306
1373
  );
1307
1374
  merged.each(function(fracture) {
1308
- const g = d34.select(this);
1375
+ const g = d35.select(this);
1309
1376
  g.selectAll("*").remove();
1310
1377
  const rng = makeSeededPrng(fracture.depth, 7919);
1311
1378
  const hitBuffer = fracture.swarm ? rf.hitBuffer.swarm : rf.hitBuffer.single;
@@ -1384,8 +1451,8 @@ function drawFractures(ctx, data) {
1384
1451
  }
1385
1452
 
1386
1453
  // src/renderers/highlights.renderer.ts
1387
- import * as d35 from "d3";
1388
- import { getProfileDiamValues as getProfileDiamValues3 } from "@welldot/utils";
1454
+ import * as d36 from "d3";
1455
+ import { getProfileDiamValues as getProfileDiamValues4 } from "@welldot/utils";
1389
1456
  function drawHighlightItem(ctx, group, x, y, w, h, label) {
1390
1457
  if (h <= 0 || w <= 0) return;
1391
1458
  const hlConfig = ctx.renderConfig.highlights;
@@ -1444,8 +1511,8 @@ function drawHighlights(ctx, hl) {
1444
1511
  h.label
1445
1512
  );
1446
1513
  });
1447
- const maxXVals = getProfileDiamValues3(ctx.constructionData);
1448
- const xScaleConstr = d35.scaleLinear().domain([0, d35.max(maxXVals) || 0]).range([0, ctx.POCO_WIDTH]);
1514
+ const maxXVals = getProfileDiamValues4(ctx.constructionData);
1515
+ const xScaleConstr = d36.scaleLinear().domain([0, d36.max(maxXVals) || 0]).range([0, ctx.POCO_WIDTH]);
1449
1516
  const constructionConfig = ctx.renderConfig.construction;
1450
1517
  if (hl.cement_pad && ctx.depthFrom === 0 && ctx.constructionData.cement_pad?.thickness) {
1451
1518
  const cp = ctx.constructionData.cement_pad;
@@ -1540,7 +1607,7 @@ function drawHighlights(ctx, hl) {
1540
1607
  // src/renderers/legend.renderer.ts
1541
1608
  import * as d3module from "d3";
1542
1609
  import { defu } from "defu";
1543
- var d36 = { ...d3module };
1610
+ var d37 = { ...d3module };
1544
1611
  function drawWellLegend(selector, profile, options = {}) {
1545
1612
  const fractures = profile.fractures ?? [];
1546
1613
  const caves = profile.caves ?? [];
@@ -1561,6 +1628,7 @@ function drawWellLegend(selector, profile, options = {}) {
1561
1628
  const hasWellCase = wellCases.length > 0;
1562
1629
  const hasWellScreen = wellScreens.length > 0;
1563
1630
  const hasReduction = reductions.length > 0;
1631
+ const hasCentralizer = (profile.centralizers ?? []).length > 0;
1564
1632
  const hasCementPad = !!profile.cement_pad?.thickness;
1565
1633
  const hasConflict = mergeConflicts(
1566
1634
  [
@@ -1653,13 +1721,19 @@ function drawWellLegend(selector, profile, options = {}) {
1653
1721
  label: cfg.labels.reduction,
1654
1722
  subKind: "reduction"
1655
1723
  });
1724
+ if (hasCentralizer)
1725
+ items.push({
1726
+ kind: "construction",
1727
+ label: cfg.labels.centralizer,
1728
+ subKind: "centralizer"
1729
+ });
1656
1730
  if (hasConflict)
1657
1731
  items.push({
1658
1732
  kind: "construction",
1659
1733
  label: cfg.labels.conflict,
1660
1734
  subKind: "conflict"
1661
1735
  });
1662
- const svgEl = d36.select(selector);
1736
+ const svgEl = d37.select(selector);
1663
1737
  svgEl.selectAll("*").remove();
1664
1738
  if (items.length === 0) {
1665
1739
  svgEl.attr("width", 0).attr("height", 0);
@@ -1711,6 +1785,25 @@ function drawWellLegend(selector, profile, options = {}) {
1711
1785
  symG.append("rect").attr("class", cls.constructionRect).attr("x", cx).attr("y", rowSymY - rh / 2).attr("width", rw).attr("height", rh).attr("fill", legendTextures.surface_case.url()).attr("stroke", "none");
1712
1786
  symG.append("line").attr("class", cls.constructionRect).attr("x1", cx).attr("x2", cx).attr("y1", rowSymY - rh / 2).attr("y2", rowSymY + rh / 2).attr("stroke", theme.surfaceCase.stroke).attr("stroke-width", 2);
1713
1787
  symG.append("line").attr("class", cls.constructionRect).attr("x1", cx + rw).attr("x2", cx + rw).attr("y1", rowSymY - rh / 2).attr("y2", rowSymY + rh / 2).attr("stroke", theme.surfaceCase.stroke).attr("stroke-width", 2);
1788
+ } else if (item.subKind === "centralizer") {
1789
+ const pipeW = rw * 0.4;
1790
+ const pipeL = cx + (rw - pipeW) / 2;
1791
+ const pipeR = pipeL + pipeW;
1792
+ const y1 = rowSymY - rh / 2;
1793
+ const y2 = rowSymY + rh / 2;
1794
+ const bowH = rh * 0.3;
1795
+ for (const x of [pipeL, pipeR]) {
1796
+ symG.append("line").attr("class", cls.constructionRect).attr("x1", x).attr("x2", x).attr("y1", y1).attr("y2", y2).attr("stroke", theme.wellCase.stroke).attr("stroke-width", theme.legend.itemStrokeWidth);
1797
+ }
1798
+ for (const [from, to] of [
1799
+ [pipeL, cx],
1800
+ [pipeR, cx + rw]
1801
+ ]) {
1802
+ symG.append("polyline").attr("class", cls.constructionRect).attr(
1803
+ "points",
1804
+ `${from},${rowSymY - bowH} ${to},${rowSymY} ${from},${rowSymY + bowH}`
1805
+ ).attr("fill", theme.centralizer.fill).attr("stroke", theme.centralizer.stroke).attr("stroke-width", theme.centralizer.strokeWidth);
1806
+ }
1714
1807
  } else if (item.subKind === "reduction") {
1715
1808
  const topW = rw * 0.5;
1716
1809
  const y1 = rowSymY - rh / 2;
@@ -1877,7 +1970,7 @@ function buildSvgStyleBlock(theme) {
1877
1970
  }
1878
1971
 
1879
1972
  // src/Renderer.ts
1880
- var d37 = { ...d3module2, tip: d3tip2 };
1973
+ var d38 = { ...d3module2, tip: d3tip2 };
1881
1974
  var WellRenderer = class {
1882
1975
  /**
1883
1976
  * @param svgs - One entry per SVG panel; multi-entry for split-panel layouts.
@@ -1937,7 +2030,7 @@ var WellRenderer = class {
1937
2030
  const { selector, height, width, margins } = inst;
1938
2031
  const svgWidth = width + margins.left + margins.right;
1939
2032
  const svgHeight = height + margins.top + margins.bottom;
1940
- const svg = d37.select(selector);
2033
+ const svg = d38.select(selector);
1941
2034
  svg.selectAll("*").remove();
1942
2035
  svg.attr("height", svgHeight).attr("width", svgWidth);
1943
2036
  const defs = svg.append("defs");
@@ -1964,6 +2057,7 @@ var WellRenderer = class {
1964
2057
  construction.append("g").attr("class", this.classes.wellCase.group);
1965
2058
  construction.append("g").attr("class", this.classes.wellScreen.group);
1966
2059
  construction.append("g").attr("class", this.classes.reduction.group);
2060
+ construction.append("g").attr("class", this.classes.centralizer.group);
1967
2061
  construction.append("g").attr("class", this.classes.conflict.group);
1968
2062
  construction.append("g").attr("class", this.classes.constructionLabels.group);
1969
2063
  construction.append("g").attr("class", this.classes.highlights.constructionGroup);
@@ -1995,7 +2089,7 @@ var WellRenderer = class {
1995
2089
  * (defaults to `1`, i.e. the initial fit-to-container view).
1996
2090
  */
1997
2091
  resetZoom() {
1998
- const target = d37.zoomIdentity.scale(this.renderConfig.zoomLevel ?? 1);
2092
+ const target = d38.zoomIdentity.scale(this.renderConfig.zoomLevel ?? 1);
1999
2093
  for (const state of this.instanceStates) {
2000
2094
  if (!state.zoomNode) continue;
2001
2095
  asSvgElement(state.svg).transition().call(state.zoomNode.transform, target);
@@ -2007,7 +2101,7 @@ var WellRenderer = class {
2007
2101
  */
2008
2102
  getZoomScale() {
2009
2103
  const node = this.instanceStates[0]?.svg.node();
2010
- return node ? d37.zoomTransform(node).k : 1;
2104
+ return node ? d38.zoomTransform(node).k : 1;
2011
2105
  }
2012
2106
  /**
2013
2107
  * Initialises the SVG DOM structure and preloads FGDC textures.
@@ -2039,7 +2133,7 @@ var WellRenderer = class {
2039
2133
  }
2040
2134
  const highlights = options.highlights ?? {};
2041
2135
  const maxValues = getProfileLastItemsDepths(profile);
2042
- const maxYValues = d37.max(maxValues) || 0;
2136
+ const maxYValues = d38.max(maxValues) || 0;
2043
2137
  if (this.instanceStates.length === 1) {
2044
2138
  this.drawLogToInstance(
2045
2139
  this.instanceStates[0],
@@ -2051,7 +2145,7 @@ var WellRenderer = class {
2051
2145
  return;
2052
2146
  }
2053
2147
  const totalHeight = this.instanceStates.reduce((s, st) => s + st.height, 0) || 1;
2054
- const yScaleGlobal = d37.scaleLinear().domain([0, maxYValues]).range([0, totalHeight]);
2148
+ const yScaleGlobal = d38.scaleLinear().domain([0, maxYValues]).range([0, totalHeight]);
2055
2149
  let currentDepth = 0;
2056
2150
  for (const state of this.instanceStates) {
2057
2151
  const maxSvgDepth = yScaleGlobal.invert(
@@ -2084,6 +2178,7 @@ var WellRenderer = class {
2084
2178
  const wellCaseGroup = svg.select(`.${this.classes.wellCase.group}`);
2085
2179
  const wellScreenGroup = svg.select(`.${this.classes.wellScreen.group}`);
2086
2180
  const reductionGroup = svg.select(`.${this.classes.reduction.group}`);
2181
+ const centralizerGroup = svg.select(`.${this.classes.centralizer.group}`);
2087
2182
  const conflictGroup = svg.select(`.${this.classes.conflict.group}`);
2088
2183
  const constructionLabelsGroup = svg.select(
2089
2184
  `.${this.classes.constructionLabels.group}`
@@ -2104,7 +2199,7 @@ var WellRenderer = class {
2104
2199
  const POCO_WIDTH = svgWidth * pocoWidthRatio;
2105
2200
  const POCO_CENTER = svgWidth * pocoCenterRatio;
2106
2201
  const { duration, ease } = this.renderConfig.animation;
2107
- const transition = d37.transition().duration(duration).ease(ease);
2202
+ const transition = d38.transition().duration(duration).ease(ease);
2108
2203
  const tooltips = populateTooltips(
2109
2204
  svg,
2110
2205
  this.classes,
@@ -2129,13 +2224,14 @@ var WellRenderer = class {
2129
2224
  well_case: profile.well_case,
2130
2225
  well_screen: profile.well_screen,
2131
2226
  reduction: profile.reduction,
2227
+ centralizers: profile.centralizers ?? [],
2132
2228
  fractures: profile.fractures
2133
2229
  };
2134
2230
  const contentHeight = svgHeight - state.margins.top - state.margins.bottom;
2135
- const yScaleLocal = d37.scaleLinear().domain([depthFrom, depthTo]).range([0, contentHeight]);
2231
+ const yScaleLocal = d38.scaleLinear().domain([depthFrom, depthTo]).range([0, contentHeight]);
2136
2232
  const depthFactor = this.units.length === "ft" ? 3.28084 : 1;
2137
- const yScaleAxis = d37.scaleLinear().domain([depthFrom * depthFactor, depthTo * depthFactor]).range([0, contentHeight]);
2138
- const yAxis = d37.axisLeft(yScaleAxis).tickFormat(
2233
+ const yScaleAxis = d38.scaleLinear().domain([depthFrom * depthFactor, depthTo * depthFactor]).range([0, contentHeight]);
2234
+ const yAxis = d38.axisLeft(yScaleAxis).tickFormat(
2139
2235
  (d) => `${formatNumber3(Number(d), {
2140
2236
  maximumFractionDigits: this.units.length === "ft" ? 1 : 2,
2141
2237
  locale: this.locale
@@ -2177,6 +2273,7 @@ var WellRenderer = class {
2177
2273
  wellCaseGroup,
2178
2274
  wellScreenGroup,
2179
2275
  reductionGroup,
2276
+ centralizerGroup,
2180
2277
  conflictGroup,
2181
2278
  highlightsGeologicGroup,
2182
2279
  highlightsConstructionGroup,
@@ -2214,7 +2311,8 @@ var WellRenderer = class {
2214
2311
  surface_case: constructionData.surface_case.filter(inDepth),
2215
2312
  well_case: constructionData.well_case.filter(inDepth),
2216
2313
  well_screen: constructionData.well_screen.filter(inDepth),
2217
- reduction: constructionData.reduction?.filter(inDepth) ?? []
2314
+ reduction: constructionData.reduction?.filter(inDepth) ?? [],
2315
+ centralizers: constructionData.centralizers?.filter(inDepth) ?? []
2218
2316
  };
2219
2317
  const zooming = (e) => {
2220
2318
  const transform = e.transform;
@@ -2233,14 +2331,14 @@ var WellRenderer = class {
2233
2331
  surfaceCaseGroup.selectAll(`g.${this.classes.surfaceCase.rect}`).each((d, i, nodes) => {
2234
2332
  if (!d) return;
2235
2333
  const sc = d;
2236
- const g = d37.select(nodes[i]);
2334
+ const g = d38.select(nodes[i]);
2237
2335
  const y = transform.applyY(spanY(sc));
2238
2336
  const h = transform.k * spanH(sc);
2239
2337
  g.select(".surface-case-fill").attr("y", y).attr("height", h);
2240
2338
  g.selectAll(".surface-case-side").attr("y1", y).attr("y2", y + h);
2241
2339
  });
2242
- const maxXValsReduction = getProfileDiamValues4(constructionData);
2243
- const xScaleReduction = d37.scaleLinear().domain([0, d37.max(maxXValsReduction) || 0]).range([0, POCO_WIDTH]);
2340
+ const maxXValsReduction = getProfileDiamValues5(constructionData);
2341
+ const xScaleReduction = d38.scaleLinear().domain([0, d38.max(maxXValsReduction) || 0]).range([0, POCO_WIDTH]);
2244
2342
  reductionGroup.selectAll(`polygon.${this.classes.reduction.item}`).attr("points", (d) => {
2245
2343
  const r = d;
2246
2344
  const yTop = transform.applyY(spanY(r));
@@ -2267,6 +2365,7 @@ var WellRenderer = class {
2267
2365
  ) ?? [],
2268
2366
  caves: profile.caves?.filter(inDepth) ?? []
2269
2367
  });
2368
+ drawCentralizers(zoomedCtx, filteredConstruction);
2270
2369
  drawConstructionLabels(zoomedCtx, {
2271
2370
  well_case: constructionData.well_case.filter(inDepth),
2272
2371
  well_screen: constructionData.well_screen.filter(inDepth)
@@ -2293,6 +2392,7 @@ var WellRenderer = class {
2293
2392
  ) ?? []
2294
2393
  );
2295
2394
  drawConstructive(ctx, filteredConstruction);
2395
+ drawCentralizers(ctx, filteredConstruction);
2296
2396
  drawConstructionLabels(ctx, {
2297
2397
  well_case: filteredConstruction.well_case,
2298
2398
  well_screen: filteredConstruction.well_screen
@@ -2312,7 +2412,7 @@ var WellRenderer = class {
2312
2412
  }
2313
2413
  const { zoom: zoomEnabled, pan: panEnabled } = this.renderConfig;
2314
2414
  if (zoomEnabled || panEnabled) {
2315
- const zoomNode = d37.zoom();
2415
+ const zoomNode = d38.zoom();
2316
2416
  if (zoomEnabled || panEnabled) zoomNode.on("zoom", zooming);
2317
2417
  zoomNode.scaleExtent(
2318
2418
  zoomEnabled ? [
@@ -2328,7 +2428,7 @@ var WellRenderer = class {
2328
2428
  if (initialZoom !== 1) {
2329
2429
  asSvgElement(svg).call(
2330
2430
  zoomNode.transform,
2331
- d37.zoomIdentity.scale(initialZoom)
2431
+ d38.zoomIdentity.scale(initialZoom)
2332
2432
  );
2333
2433
  }
2334
2434
  }