@welldot/render 0.1.0-next.20260509.039db95 → 0.2.2

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.cjs CHANGED
@@ -325,19 +325,163 @@ var index_exports = {};
325
325
  __export(index_exports, {
326
326
  DEFAULT_WELL_THEME: () => DEFAULT_WELL_THEME,
327
327
  INTERACTIVE_RENDER_CONFIG: () => INTERACTIVE_RENDER_CONFIG,
328
+ RENDER_LABELS: () => RENDER_LABELS,
328
329
  STATIC_RENDER_CONFIG: () => STATIC_RENDER_CONFIG,
329
330
  WellRenderer: () => WellRenderer,
331
+ applyRenderLocale: () => applyRenderLocale,
330
332
  drawWellLegend: () => drawWellLegend,
331
333
  formatDiameter: () => formatDiameter,
332
334
  formatLength: () => formatLength,
333
335
  getDiameterUnit: () => getDiameterUnit,
334
336
  getLengthUnit: () => getLengthUnit,
335
- importFgdcTextures: () => importFgdcTextures
337
+ importFgdcTextures: () => importFgdcTextures,
338
+ resolveRenderLabel: () => resolveRenderLabel
336
339
  });
337
340
  module.exports = __toCommonJS(index_exports);
338
341
 
339
342
  // src/configs/render.configs.ts
340
343
  var import_d3 = require("d3");
344
+
345
+ // src/utils/format.utils.ts
346
+ var import_utils = require("@welldot/utils");
347
+ var MM_TO_INCHES = 0.0393701;
348
+ var M_TO_FT = 3.28084;
349
+ function formatLength(m, units, locale) {
350
+ return units === "ft" ? (0, import_utils.formatNumber)(m * M_TO_FT, { maximumFractionDigits: 1, locale }) : (0, import_utils.formatNumber)(m, { maximumFractionDigits: 2, locale });
351
+ }
352
+ function formatDiameter(mm, units, locale) {
353
+ return units === "inches" ? (0, import_utils.formatNumber)(mm * MM_TO_INCHES, { maximumFractionDigits: 2, locale }) : (0, import_utils.formatNumber)(mm, { maximumFractionDigits: 2, locale });
354
+ }
355
+ function getLengthUnit(units) {
356
+ return units === "ft" ? "ft" : "m";
357
+ }
358
+ function getDiameterUnit(units, locale) {
359
+ if (units !== "inches") return "mm";
360
+ return locale === "en" ? "in." : '"';
361
+ }
362
+ function resolveRenderLabel(value, locale) {
363
+ if (typeof value === "string") return value;
364
+ return locale === "en" && value.en ? value.en : value.pt;
365
+ }
366
+
367
+ // src/configs/render.configs.ts
368
+ var RENDER_LABELS = {
369
+ constructionLabels: {
370
+ wellCasePrefix: { pt: "Revest.", en: "Casing" },
371
+ wellScreenPrefix: { pt: "Filtro", en: "Screen" },
372
+ wellScreenSlotPrefix: { pt: "Ranhura:", en: "Slot:" }
373
+ },
374
+ legend: {
375
+ title: { pt: "LEGENDA", en: "LEGEND" },
376
+ labels: {
377
+ fractureSingle: { pt: "Fratura simples", en: "Single fracture" },
378
+ fractureSwarm: { pt: "Enxame de fraturas", en: "Fracture swarm" },
379
+ fractureWater: { pt: "Entrada d'\xE1gua", en: "Water intake" },
380
+ caveDry: { pt: "Caverna seca", en: "Dry cave" },
381
+ caveWet: { pt: "Caverna c/ \xE1gua", en: "Wet cave" },
382
+ boreHole: { pt: "Perfura\xE7\xE3o", en: "Borehole" },
383
+ surfaceCase: { pt: "Tubo guia", en: "Surface casing" },
384
+ holeFillGravel: { pt: "Pr\xE9-filtro", en: "Gravel pack" },
385
+ holeFillSeal: { pt: "Veda\xE7\xE3o", en: "Seal" },
386
+ wellCase: { pt: "Revestimento", en: "Casing" },
387
+ wellScreen: { pt: "Filtro", en: "Screen" },
388
+ reduction: { pt: "Redu\xE7\xE3o", en: "Reduction" },
389
+ cementPad: { pt: "Laje de cimento", en: "Cement pad" },
390
+ conflict: { pt: "Conflito", en: "Conflict" }
391
+ }
392
+ },
393
+ tooltipLabels: {
394
+ common: {
395
+ from: { pt: "De", en: "From" },
396
+ to: { pt: "at\xE9", en: "to" },
397
+ description: { pt: "Descri\xE7\xE3o:", en: "Description:" },
398
+ diameter: { pt: "Di\xE2metro:", en: "Diameter:" },
399
+ type: { pt: "Tipo:", en: "Type:" }
400
+ },
401
+ geology: {
402
+ title: { pt: "Litologia", en: "Lithology" },
403
+ geologicUnit: { pt: "Unidade geol\xF3gica:", en: "Geologic unit:" },
404
+ aquiferUnit: { pt: "Unidade aqu\xEDfera:", en: "Aquifer unit:" }
405
+ },
406
+ hole: { title: { pt: "FURO", en: "BOREHOLE" } },
407
+ surfaceCase: { title: { pt: "TUBO DE BOCA", en: "SURFACE CASING" } },
408
+ holeFill: { title: { pt: "ESP. ANULAR", en: "ANNULAR SPACE" } },
409
+ wellCase: { title: { pt: "REVESTIMENTO", en: "CASING" } },
410
+ wellScreen: {
411
+ title: { pt: "FILTROS", en: "SCREENS" },
412
+ slot: { pt: "Ranhura:", en: "Slot:" }
413
+ },
414
+ reduction: { title: { pt: "REDU\xC7\xC3O", en: "REDUCTION" } },
415
+ conflict: { title: { pt: "CONFLITO", en: "CONFLICT" } },
416
+ fracture: {
417
+ title: { pt: "FRATURA", en: "FRACTURE" },
418
+ titleSwarm: { pt: "ENXAME DE FRATURAS", en: "FRACTURE SWARM" },
419
+ depth: { pt: "Profundidade:", en: "Depth:" },
420
+ waterIntake: { pt: "Entrada d'\xE1gua:", en: "Water intake:" },
421
+ dip: { pt: "Mergulho:", en: "Dip:" },
422
+ azimuth: { pt: "Azimute:", en: "Azimuth:" }
423
+ },
424
+ cementPad: {
425
+ title: { pt: "LAJE DE PROTE\xC7\xC3O", en: "PROTECTION SLAB" },
426
+ thickness: { pt: "Espessura:", en: "Thickness:" },
427
+ width: { pt: "Largura:", en: "Width:" },
428
+ length: { pt: "Comprimento:", en: "Length:" }
429
+ },
430
+ cave: {
431
+ title: { pt: "CAVERNA", en: "CAVE" },
432
+ waterIntake: { pt: "Entrada d'\xE1gua", en: "Water intake" }
433
+ }
434
+ },
435
+ typeLabels: {
436
+ fracture: { pt: "fratura", en: "fracture" },
437
+ fractureWater: { pt: "fratura aberta", en: "open fracture" },
438
+ cave: { pt: "caverna", en: "cave" },
439
+ caveWater: { pt: "caverna \xFAmida", en: "wet cave" }
440
+ }
441
+ };
442
+ function resolveGroup(group, locale) {
443
+ const result = {};
444
+ for (const key in group) {
445
+ result[key] = resolveRenderLabel(group[key], locale);
446
+ }
447
+ return result;
448
+ }
449
+ function resolveTooltipLabels(locale) {
450
+ const t = RENDER_LABELS.tooltipLabels;
451
+ return {
452
+ common: resolveGroup(t.common, locale),
453
+ geology: resolveGroup(t.geology, locale),
454
+ hole: resolveGroup(t.hole, locale),
455
+ surfaceCase: resolveGroup(t.surfaceCase, locale),
456
+ holeFill: resolveGroup(t.holeFill, locale),
457
+ wellCase: resolveGroup(t.wellCase, locale),
458
+ wellScreen: resolveGroup(t.wellScreen, locale),
459
+ reduction: resolveGroup(t.reduction, locale),
460
+ conflict: resolveGroup(t.conflict, locale),
461
+ fracture: resolveGroup(t.fracture, locale),
462
+ cementPad: resolveGroup(t.cementPad, locale),
463
+ cave: resolveGroup(t.cave, locale)
464
+ };
465
+ }
466
+ function applyRenderLocale(config, locale) {
467
+ return {
468
+ ...config,
469
+ constructionLabels: {
470
+ ...config.constructionLabels,
471
+ labels: resolveGroup(RENDER_LABELS.constructionLabels, locale)
472
+ },
473
+ legend: {
474
+ ...config.legend,
475
+ title: resolveRenderLabel(RENDER_LABELS.legend.title, locale),
476
+ labels: resolveGroup(RENDER_LABELS.legend.labels, locale)
477
+ },
478
+ tooltipLabels: resolveTooltipLabels(locale),
479
+ labels: {
480
+ ...config.labels,
481
+ typeLabels: resolveGroup(RENDER_LABELS.typeLabels, locale)
482
+ }
483
+ };
484
+ }
341
485
  var DEFAULT_WELL_THEME = {
342
486
  lithology: { stroke: "#101010", strokeWidth: 1 },
343
487
  lithologyTexture: { size: 150, strokeWidth: 0.8, stroke: "#303030" },
@@ -372,6 +516,7 @@ var DEFAULT_WELL_THEME = {
372
516
  holeFill: { stroke: "#303030", strokeWidth: 2 },
373
517
  wellCase: { fill: "#ffffff", stroke: "#303030", strokeWidth: 2 },
374
518
  wellScreen: { stroke: "#303030", strokeWidth: 2 },
519
+ reduction: { fill: "#ffffff", stroke: "#303030", strokeWidth: 2 },
375
520
  conflict: { stroke: "#e52117", strokeWidth: 4 },
376
521
  unitLabels: {
377
522
  geologicFill: "#f0f0f0",
@@ -435,6 +580,8 @@ var STATIC_RENDER_CONFIG = {
435
580
  zoom: true,
436
581
  pan: true,
437
582
  zoomLevel: 1,
583
+ maxZoomScale: 150,
584
+ minZoomScale: 0,
438
585
  animation: { duration: 600, ease: import_d3.easeCubic },
439
586
  geologic: { xLeft: 20, xRightInset: 300 },
440
587
  layout: {
@@ -465,9 +612,10 @@ var STATIC_RENDER_CONFIG = {
465
612
  cementPad: { widthMultiplier: 0.9, thicknessMultiplier: 1.3 },
466
613
  surfaceCase: { diameterPaddingRatio: 0.1 }
467
614
  },
615
+ tooltipLabels: resolveTooltipLabels("pt"),
468
616
  labels: {
469
617
  active: true,
470
- typeLabels: { fracture: "fratura", cave: "caverna" },
618
+ typeLabels: resolveGroup(RENDER_LABELS.typeLabels, "pt"),
471
619
  depthTipHeight: 11,
472
620
  depthTipPadX: 2,
473
621
  descriptionXOffset: 20,
@@ -532,6 +680,7 @@ var STATIC_RENDER_CONFIG = {
532
680
  holeFillSeal: "Veda\xE7\xE3o",
533
681
  wellCase: "Revestimento",
534
682
  wellScreen: "Filtro",
683
+ reduction: "Redu\xE7\xE3o",
535
684
  cementPad: "Laje de cimento",
536
685
  conflict: "Conflito"
537
686
  }
@@ -542,6 +691,8 @@ var INTERACTIVE_RENDER_CONFIG = {
542
691
  zoom: true,
543
692
  pan: true,
544
693
  zoomLevel: 1,
694
+ maxZoomScale: 150,
695
+ minZoomScale: 0,
545
696
  animation: { duration: 600, ease: import_d3.easeCubic },
546
697
  geologic: { xLeft: 6, xRightInset: 56 },
547
698
  layout: {
@@ -572,9 +723,10 @@ var INTERACTIVE_RENDER_CONFIG = {
572
723
  cementPad: { widthMultiplier: 0.9, thicknessMultiplier: 1.3 },
573
724
  surfaceCase: { diameterPaddingRatio: 0.1 }
574
725
  },
726
+ tooltipLabels: resolveTooltipLabels("pt"),
575
727
  labels: {
576
728
  active: false,
577
- typeLabels: { fracture: "fratura", cave: "caverna" },
729
+ typeLabels: resolveGroup(RENDER_LABELS.typeLabels, "pt"),
578
730
  depthTipHeight: 11,
579
731
  depthTipPadX: 2,
580
732
  descriptionXOffset: 78,
@@ -636,6 +788,7 @@ var INTERACTIVE_RENDER_CONFIG = {
636
788
  holeFillSeal: "Veda\xE7\xE3o",
637
789
  wellCase: "Revestimento",
638
790
  wellScreen: "Filtro",
791
+ reduction: "Redu\xE7\xE3o",
639
792
  cementPad: "Laje de cimento",
640
793
  conflict: "Conflito"
641
794
  }
@@ -650,7 +803,7 @@ async function importFgdcTextures() {
650
803
  }
651
804
 
652
805
  // src/Renderer.ts
653
- var d3module3 = __toESM(require("d3"), 1);
806
+ var d3module2 = __toESM(require("d3"), 1);
654
807
  var import_d3_tip2 = __toESM(require("d3-tip"), 1);
655
808
  var import_defu2 = require("defu");
656
809
 
@@ -716,23 +869,7 @@ function createWellTextures(cfg) {
716
869
 
717
870
  // src/Renderer.ts
718
871
  var import_core = require("@welldot/core");
719
- var import_utils4 = require("@welldot/utils");
720
-
721
- // src/utils/format.utils.ts
722
- var MM_TO_INCHES = 0.0393701;
723
- var M_TO_FT = 3.28084;
724
- function formatLength(m, units) {
725
- return units === "ft" ? (m * M_TO_FT).toFixed(1) : String(m);
726
- }
727
- function formatDiameter(mm, units) {
728
- return units === "inches" ? (mm * MM_TO_INCHES).toFixed(2) : String(mm);
729
- }
730
- function getLengthUnit(units) {
731
- return units === "ft" ? "ft" : "m";
732
- }
733
- function getDiameterUnit(units) {
734
- return units === "inches" ? '"' : "mm";
735
- }
872
+ var import_utils6 = require("@welldot/utils");
736
873
 
737
874
  // src/configs/render.classnames.ts
738
875
  var DEFAULT_COMPONENTS_CLASS_NAMES = {
@@ -790,6 +927,10 @@ var DEFAULT_COMPONENTS_CLASS_NAMES = {
790
927
  group: "well-screen",
791
928
  rect: "well-screen-rect"
792
929
  },
930
+ reduction: {
931
+ group: "reduction",
932
+ item: "reduction-poly"
933
+ },
793
934
  conflict: {
794
935
  group: "conflict",
795
936
  rect: "conflict-rect"
@@ -899,92 +1040,102 @@ var makeSeededPrng = (seed, scramble = 6271) => {
899
1040
  };
900
1041
 
901
1042
  // src/utils/tooltips.utils.ts
902
- var d3module = __toESM(require("d3"), 1);
903
1043
  var import_d3_tip = __toESM(require("d3-tip"), 1);
904
1044
  var import_sanitize_html = __toESM(require("sanitize-html"), 1);
905
- var d3 = Object.assign(d3module, { tip: import_d3_tip.default });
1045
+ var import_utils2 = require("@welldot/utils");
906
1046
  var esc = (v) => (0, import_sanitize_html.default)(String(v ?? ""), { allowedTags: [], allowedAttributes: {} });
907
- var populateTooltips = (svg, customClasses, units, tooltipConfig) => {
1047
+ var _tooltipCache = /* @__PURE__ */ new WeakMap();
1048
+ var populateTooltips = (svg, customClasses, units, tooltipConfig, labels, locale) => {
908
1049
  const svgEl = typeof svg.node === "function" ? svg.node() : null;
909
- if (svgEl?.getAttribute("data-tooltips-init") === "true") {
910
- return {};
1050
+ const signature = JSON.stringify({ units, tooltipConfig, labels, locale });
1051
+ const cached = svgEl ? _tooltipCache.get(svgEl) : void 0;
1052
+ if (cached && cached.signature === signature) {
1053
+ return cached.tooltips;
911
1054
  }
912
1055
  const tipsText = {
913
1056
  geology: (_, d) => `
914
- <span class="${customClasses.tooltip.title}">Litologia</span>
915
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
916
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Descri\xE7\xE3o:</strong> ${esc(d.description)}</span>
917
- ${d.geologic_unit ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>Unidade geol\xF3gica:</strong> ${esc(d.geologic_unit)}</span>` : ""}
918
- ${d.aquifer_unit ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>Unidade aqu\xEDfera:</strong> ${esc(d.aquifer_unit)}</span>` : ""}
1057
+ <span class="${customClasses.tooltip.title}">${labels.geology.title}</span>
1058
+ <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>
1059
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.description}</strong> ${esc(d.description)}</span>
1060
+ ${d.geologic_unit ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.geology.geologicUnit}</strong> ${esc(d.geologic_unit)}</span>` : ""}
1061
+ ${d.aquifer_unit ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.geology.aquiferUnit}</strong> ${esc(d.aquifer_unit)}</span>` : ""}
919
1062
  `,
920
1063
  hole: (_, d) => `
921
- <span class="${customClasses.tooltip.title}">FURO</span>
922
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
923
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Di\xE2metro:</strong>${esc(formatDiameter(d.diameter, units.diameter))} ${esc(units.diameter)}</span>
1064
+ <span class="${customClasses.tooltip.title}">${labels.hole.title}</span>
1065
+ <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>
1066
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.diameter}</strong>${esc(formatDiameter(d.diameter, units.diameter, locale))} ${esc(getDiameterUnit(units.diameter, locale))}</span>
924
1067
  `,
925
1068
  surfaceCase: (_, d) => `
926
- <span class="${customClasses.tooltip.title}">TUBO DE BOCA</span>
927
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
928
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Di\xE2metro:</strong>${esc(formatDiameter(d.diameter, units.diameter))} ${esc(units.diameter)}</span>
1069
+ <span class="${customClasses.tooltip.title}">${labels.surfaceCase.title}</span>
1070
+ <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>
1071
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.diameter}</strong>${esc(formatDiameter(d.diameter, units.diameter, locale))} ${esc(getDiameterUnit(units.diameter, locale))}</span>
929
1072
  `,
930
1073
  holeFill: (_, d) => `
931
- <span class="${customClasses.tooltip.title}">ESP. ANULAR</span>
932
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
933
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Di\xE2metro:</strong>${esc(formatDiameter(d.diameter, units.diameter))} ${esc(units.diameter)}</span>
1074
+ <span class="${customClasses.tooltip.title}">${labels.holeFill.title}</span>
1075
+ <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>
1076
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.diameter}</strong>${esc(formatDiameter(d.diameter, units.diameter, locale))} ${esc(getDiameterUnit(units.diameter, locale))}</span>
934
1077
  <span class="${customClasses.tooltip.secondaryInfo}">
935
- <strong>Descri\xE7\xE3o:</strong> ${esc(d.description)}
1078
+ <strong>${labels.common.description}</strong> ${esc(d.description)}
936
1079
  </span>
937
1080
  `,
938
1081
  wellCase: (_, d) => `
939
- <span class="${customClasses.tooltip.title}">REVESTIMENTO</span>
940
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
1082
+ <span class="${customClasses.tooltip.title}">${labels.wellCase.title}</span>
1083
+ <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>
941
1084
  <span class="${customClasses.tooltip.secondaryInfo}">
942
- <strong>Di\xE2metro:</strong> ${esc(formatDiameter(d.diameter, units.diameter))} ${esc(units.diameter)}
1085
+ <strong>${labels.common.diameter}</strong> ${esc(formatDiameter(d.diameter, units.diameter, locale))} ${esc(getDiameterUnit(units.diameter, locale))}
943
1086
  </span>
944
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Tipo:</strong> ${esc(d.type)}</span>
1087
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.type}</strong> ${esc(d.type)}</span>
945
1088
  `,
946
1089
  wellScreen: (_, d) => `
947
- <span class="${customClasses.tooltip.title}">FILTROS</span>
948
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
1090
+ <span class="${customClasses.tooltip.title}">${labels.wellScreen.title}</span>
1091
+ <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>
949
1092
  <span class="${customClasses.tooltip.secondaryInfo}">
950
- <strong>Di\xE2metro:</strong> ${esc(formatDiameter(d.diameter, units.diameter))} ${esc(units.diameter)}</span>
951
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Tipo:</strong> ${esc(d.type)}</span>
1093
+ <strong>${labels.common.diameter}</strong> ${esc(formatDiameter(d.diameter, units.diameter, locale))} ${esc(getDiameterUnit(units.diameter, locale))}</span>
1094
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.type}</strong> ${esc(d.type)}</span>
952
1095
  <span class="${customClasses.tooltip.secondaryInfo}">
953
- <strong>Ranhura:</strong> ${esc(d.screen_slot_mm)} mm
1096
+ <strong>${labels.wellScreen.slot}</strong> ${esc((0, import_utils2.formatNumber)(d.screen_slot, { maximumFractionDigits: 2, locale }))} mm
954
1097
  </span>
955
1098
  `,
1099
+ reduction: (_, d) => `
1100
+ <span class="${customClasses.tooltip.title}">${labels.reduction.title}</span>
1101
+ <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>
1102
+ <span class="${customClasses.tooltip.secondaryInfo}">
1103
+ <strong>${labels.common.diameter}</strong> ${esc(formatDiameter(d.diam_from, units.diameter, locale))} \u2192 ${esc(formatDiameter(d.diam_to, units.diameter, locale))} ${esc(getDiameterUnit(units.diameter, locale))}
1104
+ </span>
1105
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.type}</strong> ${esc(d.type)}</span>
1106
+ `,
956
1107
  conflict: (_, d) => `
957
- <span class="${customClasses.tooltip.title}">CONFLITO</span>
958
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
1108
+ <span class="${customClasses.tooltip.title}">${labels.conflict.title}</span>
1109
+ <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>
959
1110
  `,
960
1111
  fracture: (_, d) => {
961
- const title = d.swarm ? "ENXAME DE FRATURAS" : "FRATURA";
1112
+ const title = d.swarm ? labels.fracture.titleSwarm : labels.fracture.title;
962
1113
  return `
963
1114
  <span class="${customClasses.tooltip.title}">${title}</span>
964
- <span class="${customClasses.tooltip.primaryInfo}"><strong>Profundidade:</strong> ${esc(formatLength(d.depth, units.length))} ${esc(units.length)}</span>
965
- ${d.water_intake ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>Entrada d'\xE1gua:</strong> ${esc(formatLength(d.depth, units.length))} ${esc(units.length)}</span>` : ""}
966
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Mergulho:</strong> ${esc(d.dip)}\xB0</span>
967
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Azimute:</strong> ${esc(d.azimuth)}\xB0</span>
968
- ${d.description ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>Descri\xE7\xE3o:</strong> ${esc(d.description)}</span>` : ""}
1115
+ <span class="${customClasses.tooltip.primaryInfo}"><strong>${labels.fracture.depth}</strong> ${esc(formatLength(d.depth, units.length, locale))} ${esc(getLengthUnit(units.length))}</span>
1116
+ ${d.water_intake ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.fracture.waterIntake}</strong> ${esc(formatLength(d.depth, units.length, locale))} ${esc(getLengthUnit(units.length))}</span>` : ""}
1117
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.fracture.dip}</strong> ${esc(d.dip)}\xB0</span>
1118
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.fracture.azimuth}</strong> ${esc(d.azimuth)}\xB0</span>
1119
+ ${d.description ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.description}</strong> ${esc(d.description)}</span>` : ""}
969
1120
  `;
970
1121
  },
971
1122
  cementPad: (_, d) => `
972
- <span class="${customClasses.tooltip.title}">LAJE DE PROTE\xC7\xC3O</span>
1123
+ <span class="${customClasses.tooltip.title}">${labels.cementPad.title}</span>
973
1124
  <span class="${customClasses.tooltip.primaryInfo}">${esc(d.type)}</span>
974
- <span class="${customClasses.tooltip.secondaryInfo}"><strong>Espessura:</strong>
975
- ${esc(formatLength(d.thickness, units.length))} ${esc(units.length)}</span>
1125
+ <span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.cementPad.thickness}</strong>
1126
+ ${esc(formatLength(d.thickness, units.length, locale))} ${esc(getLengthUnit(units.length))}</span>
976
1127
  <span class="${customClasses.tooltip.secondaryInfo}">
977
- <strong>Largura:</strong> ${esc(formatLength(d.width, units.length))} ${esc(units.length)}
1128
+ <strong>${labels.cementPad.width}</strong> ${esc(formatLength(d.width, units.length, locale))} ${esc(getLengthUnit(units.length))}
978
1129
  </span>
979
1130
  <span class="${customClasses.tooltip.secondaryInfo}">
980
- <strong>Comprimento:</strong> ${esc(formatLength(d.length, units.length))} ${esc(units.length)}
1131
+ <strong>${labels.cementPad.length}</strong> ${esc(formatLength(d.length, units.length, locale))} ${esc(getLengthUnit(units.length))}
981
1132
  </span>
982
1133
  `,
983
1134
  cave: (_, d) => `
984
- <span class="${customClasses.tooltip.title}">CAVERNA</span>
985
- <span class="${customClasses.tooltip.primaryInfo}">De ${esc(formatLength(d.from, units.length))} ${esc(units.length)} at\xE9 ${esc(formatLength(d.to, units.length))} ${esc(units.length)}</span>
986
- ${d.water_intake ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>Entrada d'\xE1gua</strong></span>` : ""}
987
- ${d.description ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>Descri\xE7\xE3o:</strong> ${esc(d.description)}</span>` : ""}
1135
+ <span class="${customClasses.tooltip.title}">${labels.cave.title}</span>
1136
+ <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>
1137
+ ${d.water_intake ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.cave.waterIntake}</strong></span>` : ""}
1138
+ ${d.description ? `<span class="${customClasses.tooltip.secondaryInfo}"><strong>${labels.common.description}</strong> ${esc(d.description)}</span>` : ""}
988
1139
  `
989
1140
  };
990
1141
  const noop = { show: () => {
@@ -997,12 +1148,13 @@ var populateTooltips = (svg, customClasses, units, tooltipConfig) => {
997
1148
  tooltips[tipTextKey] = noop;
998
1149
  return;
999
1150
  }
1000
- tooltips[tipTextKey] = d3.tip().attr("class", customClasses.tooltip.root).direction("e").html(tipsText[tipTextKey]);
1151
+ tooltips[tipTextKey] = (0, import_d3_tip.default)().attr("class", customClasses.tooltip.root).direction("e").html(tipsText[tipTextKey]);
1001
1152
  svg.call(
1002
1153
  tooltips[tipTextKey]
1003
1154
  );
1004
1155
  });
1005
1156
  svgEl?.setAttribute("data-tooltips-init", "true");
1157
+ if (svgEl) _tooltipCache.set(svgEl, { signature, tooltips });
1006
1158
  return tooltips;
1007
1159
  };
1008
1160
 
@@ -1018,21 +1170,31 @@ var preloadFgdcTextures = () => {
1018
1170
  return _fgdcTexturesPromise;
1019
1171
  };
1020
1172
  var getLithologicalFillList = (data, opts) => {
1021
- const uniqueTextureCodes = [...new Set(data.map((d) => d.fgdc_texture))];
1173
+ const textureKey = (d) => String(d.texture.code);
1174
+ const isFgdc = (d) => !d.texture.vocabulary || d.texture.vocabulary === "fgdc";
1175
+ const uniqueTextureCodes = [...new Set(data.filter(isFgdc).map(textureKey))];
1022
1176
  const texturesLoaded = Object.fromEntries(
1023
- uniqueTextureCodes.filter((code) => _fgdcTextures[code]).map((code) => [code, _fgdcTextures[code]])
1177
+ uniqueTextureCodes.filter((code) => {
1178
+ const path = _fgdcTextures[code];
1179
+ return path && path !== "-" && /^[Mm]/.test(path);
1180
+ }).map((code) => [code, _fgdcTextures[code]])
1024
1181
  );
1025
1182
  return Object.fromEntries(
1026
- data.map((d) => [
1027
- `${d.fgdc_texture}.${d.from}`,
1028
- import_textures2.default.paths().d(() => texturesLoaded[d.fgdc_texture]).size(opts.size).strokeWidth(opts.strokeWidth).stroke(opts.stroke).background(d.color)
1029
- ])
1183
+ data.map((d) => {
1184
+ const key = textureKey(d);
1185
+ const pathData = isFgdc(d) ? texturesLoaded[key] : void 0;
1186
+ if (!pathData) return [`${key}.${d.from}`, d.color];
1187
+ return [
1188
+ `${key}.${d.from}`,
1189
+ import_textures2.default.paths().d(() => pathData).size(opts.size).strokeWidth(opts.strokeWidth).stroke(opts.stroke).background(d.color)
1190
+ ];
1191
+ })
1030
1192
  );
1031
1193
  };
1032
1194
  var getLithologyFill = (geologyData, svg, opts) => {
1033
1195
  const lithologicalFill = getLithologicalFillList(geologyData, opts);
1034
1196
  return (d) => {
1035
- const fill = lithologicalFill[`${d.fgdc_texture}.${d.from}`];
1197
+ const fill = lithologicalFill[`${String(d.texture.code)}.${d.from}`];
1036
1198
  if (!fill.url) return fill;
1037
1199
  svg.call(fill);
1038
1200
  return fill.url();
@@ -1095,7 +1257,7 @@ function drawAnnotationLabels(ctx, data) {
1095
1257
  ctx.groups.lithologyLabelsGroup.selectAll("*").remove();
1096
1258
  if (active.has("lithology") && show.has("depth")) {
1097
1259
  data.lithology.filter((d) => d.to <= ctx.depthTo).forEach((d) => {
1098
- const text = `${d.to}`;
1260
+ const text = `${formatLength(d.to, ctx.units.length, ctx.locale)}${getLengthUnit(ctx.units.length)}`;
1099
1261
  const approxW = text.length * (st.fontSize * 0.52) + s.depthTipPadX * 2;
1100
1262
  const y = ctx.yScale(d.to) - s.depthTipHeight / 2;
1101
1263
  const tipG = ctx.groups.lithologyLabelsGroup.append("g").attr("class", ctx.classes.labels.lithology.depth);
@@ -1111,20 +1273,20 @@ function drawAnnotationLabels(ctx, data) {
1111
1273
  baseY: ctx.yScale(Math.max(d.from, ctx.depthFrom)),
1112
1274
  originX: ctx.geoXRight,
1113
1275
  originY: ctx.yScale(Math.max(d.from, ctx.depthFrom)),
1114
- header: `${d.from}\u2013${d.to} m`,
1276
+ header: `${formatLength(d.from, ctx.units.length, ctx.locale)}\u2013${formatLength(d.to, ctx.units.length, ctx.locale)}${getLengthUnit(ctx.units.length)}`,
1115
1277
  description: d.description || ""
1116
1278
  });
1117
1279
  });
1118
1280
  }
1119
1281
  if (active.has("fractures")) {
1120
1282
  data.fractures.filter((d) => d.depth >= ctx.depthFrom && d.depth < ctx.depthTo).forEach((d) => {
1121
- const typeLabel = d.water_intake ? "fratura aberta" : "fratura";
1283
+ const typeLabel = d.water_intake ? ctx.renderConfig.labels.typeLabels?.fractureWater ?? "fratura aberta" : ctx.renderConfig.labels.typeLabels?.fracture ?? "fratura";
1122
1284
  items.push({
1123
1285
  sortDepth: d.depth,
1124
1286
  baseY: ctx.yScale(d.depth),
1125
1287
  originX: ctx.pocoCenterX,
1126
1288
  originY: ctx.yScale(d.depth),
1127
- header: `${d.depth} m \xB7 ${typeLabel}`,
1289
+ header: `${formatLength(d.depth, ctx.units.length, ctx.locale)}${getLengthUnit(ctx.units.length)} \xB7 ${typeLabel}`,
1128
1290
  description: d.description || ""
1129
1291
  });
1130
1292
  });
@@ -1132,13 +1294,13 @@ function drawAnnotationLabels(ctx, data) {
1132
1294
  if (active.has("caves")) {
1133
1295
  data.caves.filter((d) => d.from < ctx.depthTo).forEach((d) => {
1134
1296
  const midDepth = (d.from + d.to) / 2;
1135
- const typeLabel = d.water_intake ? "caverna \xFAmida" : "caverna";
1297
+ const typeLabel = d.water_intake ? ctx.renderConfig.labels.typeLabels?.caveWater ?? "caverna \xFAmida" : ctx.renderConfig.labels.typeLabels?.cave ?? "caverna";
1136
1298
  items.push({
1137
1299
  sortDepth: d.from,
1138
1300
  baseY: ctx.yScale(Math.max(d.from, ctx.depthFrom)),
1139
1301
  originX: ctx.geoXRight,
1140
1302
  originY: ctx.yScale(Math.max(midDepth, ctx.depthFrom)),
1141
- header: `${d.from}\u2013${d.to} m \xB7 ${typeLabel}`,
1303
+ header: `${formatLength(d.from, ctx.units.length, ctx.locale)}\u2013${formatLength(d.to, ctx.units.length, ctx.locale)}${getLengthUnit(ctx.units.length)} \xB7 ${typeLabel}`,
1142
1304
  description: d.description || ""
1143
1305
  });
1144
1306
  });
@@ -1184,6 +1346,19 @@ function drawAnnotationLabels(ctx, data) {
1184
1346
  }
1185
1347
  }
1186
1348
 
1349
+ // src/renderers/caves.renderer.ts
1350
+ var d3 = __toESM(require("d3"), 1);
1351
+
1352
+ // src/utils/key.utils.ts
1353
+ var makeIntervalKey = (type) => (d, _i) => {
1354
+ const { key, from, to } = d;
1355
+ return key ? `${key}` : `${type}:${from}:${to}`;
1356
+ };
1357
+ var makePointKey = (type) => (d, i) => {
1358
+ const { key, depth } = d;
1359
+ return key ? `${key}` : `${type}:${depth}:${i}`;
1360
+ };
1361
+
1187
1362
  // src/renderers/caves.renderer.ts
1188
1363
  function drawCaves(ctx, data) {
1189
1364
  if (!data.length) return;
@@ -1200,9 +1375,14 @@ function drawCaves(ctx, data) {
1200
1375
  classes,
1201
1376
  tooltips
1202
1377
  } = ctx;
1203
- cavesGroup.selectAll(`g.${classes.caves.item}`).remove();
1204
1378
  const rc = renderConfig.caves;
1205
- data.forEach((cave) => {
1379
+ const groups = cavesGroup.selectAll(`g.${classes.caves.item}`).data(data, makeIntervalKey("cave"));
1380
+ groups.exit().transition(ctx.transition).style("opacity", 0).remove();
1381
+ const entered = groups.enter().append("g").attr("class", classes.caves.item).style("cursor", "pointer").on("mouseover", tooltips.cave.show).on("mouseout", tooltips.cave.hide).style("opacity", 0);
1382
+ const merged = mergeEnter(entered, groups);
1383
+ merged.each(function(cave) {
1384
+ const g = d3.select(this);
1385
+ g.selectAll("*").remove();
1206
1386
  const seedTop = cave.from * 100 + cave.to;
1207
1387
  const seedBot = cave.to * 100 + cave.from + 999;
1208
1388
  const rngTop = makeSeededPrng(seedTop);
@@ -1229,25 +1409,25 @@ function drawCaves(ctx, data) {
1229
1409
  const botReversed = botCut ? `L ${geoXLeft.toFixed(1)},${yBot.toFixed(1)}` : ptsToSmoothPath([...botPts].reverse(), { omitMoveTo: true });
1230
1410
  const closedPath = `${topPath} L ${geoXRight.toFixed(1)},${yBot.toFixed(1)}${botReversed} L ${geoXLeft.toFixed(1)},${yTop.toFixed(1)} Z`;
1231
1411
  const caveTexture = cave.water_intake ? textures3.cave_wet : textures3.cave_dry;
1232
- const g = cavesGroup.append("g").attr("class", classes.caves.item).datum(cave).style("cursor", "pointer").on("mouseover", tooltips.cave.show).on("mouseout", tooltips.cave.hide);
1233
1412
  const caveStroke = cave.water_intake ? theme.cave.wetStroke : theme.cave.dryStroke;
1234
1413
  g.append("path").attr("class", classes.caves.fill).attr("d", closedPath).attr("fill", caveTexture.url()).attr("fill-opacity", theme.cave.fillOpacity).attr("stroke", "none");
1235
1414
  g.append("path").attr("class", classes.caves.contact).attr("d", topPath).attr("fill", "none").attr("stroke", caveStroke).attr("stroke-width", theme.cave.contactStrokeWidth).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
1236
1415
  g.append("path").attr("class", classes.caves.contact).attr("d", botPath).attr("fill", "none").attr("stroke", caveStroke).attr("stroke-width", theme.cave.contactStrokeWidth).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
1237
1416
  });
1417
+ merged.transition(ctx.transition).style("opacity", 1);
1238
1418
  }
1239
1419
 
1240
1420
  // src/renderers/construction-labels.renderer.ts
1241
1421
  var d32 = __toESM(require("d3"), 1);
1242
- var import_utils = require("@welldot/utils");
1422
+ var import_utils3 = require("@welldot/utils");
1243
1423
  function drawConstructionLabels(ctx, data) {
1244
1424
  if (!ctx.renderConfig.constructionLabels.active) return;
1245
1425
  ctx.groups.constructionLabelsGroup.selectAll("*").remove();
1246
1426
  const clc = ctx.renderConfig.constructionLabels;
1247
1427
  const clct = ctx.theme.constructionLabels;
1248
- const fmtD = (mm) => formatDiameter(mm, ctx.units.diameter);
1249
- const dUnit = getDiameterUnit(ctx.units.diameter);
1250
- const maxXValues = (0, import_utils.getProfileDiamValues)(ctx.constructionData);
1428
+ const fmtD = (mm) => formatDiameter(mm, ctx.units.diameter, ctx.locale);
1429
+ const dUnit = getDiameterUnit(ctx.units.diameter, ctx.locale);
1430
+ const maxXValues = (0, import_utils3.getProfileDiamValues)(ctx.constructionData);
1251
1431
  const xScale = d32.scaleLinear().domain([0, d32.max(maxXValues) || 0]).range([0, ctx.POCO_WIDTH]);
1252
1432
  const occupied = [];
1253
1433
  const placeLabel = (d, text) => {
@@ -1308,26 +1488,27 @@ function drawConstructionLabels(ctx, data) {
1308
1488
  }).forEach(
1309
1489
  (d) => placeLabel(
1310
1490
  d,
1311
- `${clc.labels.wellScreenPrefix} ${fmtD(d.diameter)}${dUnit} ${d.type} ${clc.labels.wellScreenSlotPrefix} ${fmtD(d.screen_slot_mm)}${dUnit}`
1491
+ `${clc.labels.wellScreenPrefix} ${fmtD(d.diameter)}${dUnit} ${d.type} ${clc.labels.wellScreenSlotPrefix} ${fmtD(d.screen_slot)}${dUnit}`
1312
1492
  )
1313
1493
  );
1314
1494
  }
1315
1495
 
1316
1496
  // src/renderers/construction.renderer.ts
1317
1497
  var d33 = __toESM(require("d3"), 1);
1318
- var import_utils2 = require("@welldot/utils");
1498
+ var import_utils4 = require("@welldot/utils");
1319
1499
  function drawConstructive(ctx, data) {
1320
1500
  const { getHeight, getYPos } = getYAxisFunctions(ctx.yScale, {
1321
1501
  from: ctx.depthFrom,
1322
1502
  to: ctx.depthTo
1323
1503
  });
1324
- const maxXValues = (0, import_utils2.getProfileDiamValues)(ctx.constructionData);
1504
+ const maxXValues = (0, import_utils4.getProfileDiamValues)(ctx.constructionData);
1325
1505
  const maxXValueConstruction = d33.max(maxXValues) || 0;
1326
1506
  const xScale = d33.scaleLinear().domain([0, maxXValueConstruction]).range([0, ctx.POCO_WIDTH]);
1327
1507
  const rcc = ctx.renderConfig.construction;
1508
+ const yPos = getYPos;
1328
1509
  ctx.groups.constructionGroup.selectAll(`.${ctx.classes.cementPad.item}`).remove();
1329
1510
  if (data.cement_pad && data.cement_pad.thickness && ctx.depthFrom === 0) {
1330
- const cementPad = ctx.groups.cementPadGroup.selectAll("rect").data([data.cement_pad]);
1511
+ const cementPad = ctx.groups.cementPadGroup.selectAll("rect").data([data.cement_pad], () => "cement_pad");
1331
1512
  cementPad.exit().remove();
1332
1513
  const newCementPad = cementPad.enter().append("rect").attr("class", ctx.classes.cementPad.item).attr(
1333
1514
  "x",
@@ -1342,15 +1523,15 @@ function drawConstructive(ctx, data) {
1342
1523
  }).attr("fill", ctx.textures.pad.url()).attr("stroke", ctx.theme.cementPad.stroke).attr("stroke-width", ctx.theme.cementPad.strokeWidth);
1343
1524
  newCementPad.on("mouseover", ctx.tooltips.cementPad.show).on("mouseout", ctx.tooltips.cementPad.hide);
1344
1525
  }
1345
- const hole = ctx.groups.holeGroup.selectAll(`.${ctx.classes.boreHole.rect}`).data(data.bore_hole);
1346
- hole.exit().remove();
1347
- const newHole = hole.enter().append("rect").attr("class", ctx.classes.boreHole.rect).attr("fill", ctx.theme.boreHole.fill).attr("stroke", ctx.theme.boreHole.stroke).attr("opacity", ctx.theme.boreHole.opacity).attr("stroke-width", ctx.theme.boreHole.strokeWidth).attr("stroke-dasharray", ctx.theme.boreHole.strokeDasharray).on("mouseover", ctx.tooltips.hole.show).on("mouseout", ctx.tooltips.hole.hide);
1526
+ const hole = ctx.groups.holeGroup.selectAll(`.${ctx.classes.boreHole.rect}`).data(data.bore_hole, makeIntervalKey("bore_hole"));
1527
+ hole.exit().transition(ctx.transition).attr("height", 0).remove();
1528
+ const newHole = hole.enter().append("rect").attr("class", ctx.classes.boreHole.rect).attr("fill", ctx.theme.boreHole.fill).attr("stroke", ctx.theme.boreHole.stroke).attr("opacity", ctx.theme.boreHole.opacity).attr("stroke-width", ctx.theme.boreHole.strokeWidth).attr("stroke-dasharray", ctx.theme.boreHole.strokeDasharray).on("mouseover", ctx.tooltips.hole.show).on("mouseout", ctx.tooltips.hole.hide).attr("y", yPos).attr("height", 0);
1348
1529
  const mergedHole = mergeEnter(newHole, hole).attr("x", (d) => (ctx.POCO_CENTER - xScale(d.diameter)) / 2).attr("width", (d) => xScale(d.diameter));
1349
1530
  withTransition(mergedHole, ctx.transition).attr("y", getYPos).attr("height", getHeight);
1350
- const surfaceCaseGs = ctx.groups.surfaceCaseGroup.selectAll(`g.${ctx.classes.surfaceCase.rect}`).data(data.surface_case);
1531
+ const surfaceCaseGs = ctx.groups.surfaceCaseGroup.selectAll(`g.${ctx.classes.surfaceCase.rect}`).data(data.surface_case, makeIntervalKey("surface_case"));
1351
1532
  surfaceCaseGs.exit().transition(ctx.transition).style("opacity", 0).remove();
1352
1533
  const newSC = surfaceCaseGs.enter().append("g").attr("class", ctx.classes.surfaceCase.rect).on("mouseover", ctx.tooltips.surfaceCase.show).on("mouseout", ctx.tooltips.surfaceCase.hide);
1353
- newSC.append("rect").attr("class", "surface-case-fill").attr("stroke", "none");
1534
+ newSC.append("rect").attr("class", "surface-case-fill").attr("stroke", "none").attr("y", yPos).attr("height", 0);
1354
1535
  newSC.append("line").attr("class", "surface-case-side").attr("stroke", ctx.theme.surfaceCase.stroke).attr("stroke-width", ctx.theme.surfaceCase.strokeWidth);
1355
1536
  newSC.append("line").attr("class", "surface-case-side").attr("stroke", ctx.theme.surfaceCase.stroke).attr("stroke-width", ctx.theme.surfaceCase.strokeWidth);
1356
1537
  const mergedSC = mergeEnter(newSC, surfaceCaseGs);
@@ -1375,33 +1556,54 @@ function drawConstructive(ctx, data) {
1375
1556
  "y2",
1376
1557
  (d) => getYPos(d) + getHeight(d)
1377
1558
  );
1378
- const holeFill = ctx.groups.holeFillGroup.selectAll(`.${ctx.classes.holeFill.rect}`).data(data.hole_fill);
1379
- holeFill.exit().remove();
1380
- const newHoleFill = holeFill.enter().append("rect").attr("class", ctx.classes.holeFill.rect).attr("stroke", ctx.theme.holeFill.stroke).attr("stroke-width", ctx.theme.holeFill.strokeWidth).on("mouseover", ctx.tooltips.holeFill.show).on("mouseout", ctx.tooltips.holeFill.hide);
1559
+ const holeFill = ctx.groups.holeFillGroup.selectAll(`.${ctx.classes.holeFill.rect}`).data(data.hole_fill, makeIntervalKey("hole_fill"));
1560
+ holeFill.exit().transition(ctx.transition).attr("height", 0).remove();
1561
+ const newHoleFill = holeFill.enter().append("rect").attr("class", ctx.classes.holeFill.rect).attr("stroke", ctx.theme.holeFill.stroke).attr("stroke-width", ctx.theme.holeFill.strokeWidth).on("mouseover", ctx.tooltips.holeFill.show).on("mouseout", ctx.tooltips.holeFill.hide).attr("y", yPos).attr("height", 0);
1381
1562
  const mergedHoleFill = mergeEnter(newHoleFill, holeFill).attr("x", (d) => (ctx.POCO_CENTER - xScale(d.diameter)) / 2).attr("width", (d) => xScale(d.diameter));
1382
1563
  withTransition(mergedHoleFill, ctx.transition).attr("y", getYPos).attr("height", getHeight).attr("fill", (d) => ctx.textures[d.type].url());
1383
- const wellCase = ctx.groups.wellCaseGroup.selectAll(`.${ctx.classes.wellCase.rect}`).data(data.well_case);
1384
- wellCase.exit().remove();
1385
- const newWellCase = wellCase.enter().append("rect").attr("class", ctx.classes.wellCase.rect).attr("fill", ctx.theme.wellCase.fill).attr("stroke", ctx.theme.wellCase.stroke).attr("stroke-width", ctx.theme.wellCase.strokeWidth).on("mouseover", ctx.tooltips.wellCase.show).on("mouseout", ctx.tooltips.wellCase.hide);
1564
+ const wellCase = ctx.groups.wellCaseGroup.selectAll(`.${ctx.classes.wellCase.rect}`).data(data.well_case, makeIntervalKey("well_case"));
1565
+ wellCase.exit().transition(ctx.transition).attr("height", 0).remove();
1566
+ const newWellCase = wellCase.enter().append("rect").attr("class", ctx.classes.wellCase.rect).attr("fill", ctx.theme.wellCase.fill).attr("stroke", ctx.theme.wellCase.stroke).attr("stroke-width", ctx.theme.wellCase.strokeWidth).on("mouseover", ctx.tooltips.wellCase.show).on("mouseout", ctx.tooltips.wellCase.hide).attr("y", yPos).attr("height", 0);
1386
1567
  const mergedWellCase = mergeEnter(newWellCase, wellCase).attr("x", (d) => (ctx.POCO_CENTER - xScale(d.diameter)) / 2).attr("width", (d) => xScale(d.diameter));
1387
1568
  withTransition(mergedWellCase, ctx.transition).attr("y", getYPos).attr("height", getHeight);
1388
- const wellScreen = ctx.groups.wellScreenGroup.selectAll(`.${ctx.classes.wellScreen.rect}`).data(data.well_screen);
1389
- wellScreen.exit().remove();
1390
- const newWellScreen = wellScreen.enter().append("rect").attr("class", ctx.classes.wellScreen.rect).attr("fill", ctx.textures.well_screen.url()).attr("stroke", ctx.theme.wellScreen.stroke).attr("stroke-width", ctx.theme.wellScreen.strokeWidth).on("mouseover", ctx.tooltips.wellScreen.show).on("mouseout", ctx.tooltips.wellScreen.hide);
1569
+ const wellScreen = ctx.groups.wellScreenGroup.selectAll(`.${ctx.classes.wellScreen.rect}`).data(data.well_screen, makeIntervalKey("well_screen"));
1570
+ wellScreen.exit().transition(ctx.transition).attr("height", 0).remove();
1571
+ const newWellScreen = wellScreen.enter().append("rect").attr("class", ctx.classes.wellScreen.rect).attr("fill", ctx.textures.well_screen.url()).attr("stroke", ctx.theme.wellScreen.stroke).attr("stroke-width", ctx.theme.wellScreen.strokeWidth).on("mouseover", ctx.tooltips.wellScreen.show).on("mouseout", ctx.tooltips.wellScreen.hide).attr("y", yPos).attr("height", 0);
1391
1572
  const mergedWellScreen = mergeEnter(newWellScreen, wellScreen).attr("x", (d) => (ctx.POCO_CENTER - xScale(d.diameter)) / 2).attr("width", (d) => xScale(d.diameter));
1392
1573
  withTransition(mergedWellScreen, ctx.transition).attr("y", getYPos).attr("height", getHeight);
1574
+ const reductionPoints = (d, yTop, yBot) => {
1575
+ const xTopLeft = (ctx.POCO_CENTER - xScale(d.diam_from)) / 2;
1576
+ const xTopRight = (ctx.POCO_CENTER + xScale(d.diam_from)) / 2;
1577
+ const xBotLeft = (ctx.POCO_CENTER - xScale(d.diam_to)) / 2;
1578
+ const xBotRight = (ctx.POCO_CENTER + xScale(d.diam_to)) / 2;
1579
+ return `${xTopLeft},${yTop} ${xTopRight},${yTop} ${xBotRight},${yBot} ${xBotLeft},${yBot}`;
1580
+ };
1581
+ const reduction = ctx.groups.reductionGroup.selectAll(`.${ctx.classes.reduction.item}`).data(data.reduction, makeIntervalKey("reduction"));
1582
+ reduction.exit().transition(ctx.transition).style("opacity", 0).remove();
1583
+ const collapsedPoints = (d) => {
1584
+ const r = d;
1585
+ const y = getYPos(r);
1586
+ return reductionPoints(r, y, y);
1587
+ };
1588
+ const newReduction = reduction.enter().append("polygon").attr("class", ctx.classes.reduction.item).attr("fill", ctx.theme.reduction.fill).attr("stroke", ctx.theme.reduction.stroke).attr("stroke-width", ctx.theme.reduction.strokeWidth).on("mouseover", ctx.tooltips.reduction.show).on("mouseout", ctx.tooltips.reduction.hide).attr("points", collapsedPoints);
1589
+ const mergedReduction = mergeEnter(newReduction, reduction);
1590
+ withTransition(mergedReduction, ctx.transition).attr(
1591
+ "points",
1592
+ (d) => reductionPoints(d, getYPos(d), getYPos(d) + getHeight(d))
1593
+ );
1393
1594
  const conflictAreas = [];
1394
1595
  conflictAreas.push(...getConflictAreas(data.well_case, data.well_screen));
1395
1596
  conflictAreas.push(...getConflictAreas(data.well_screen, data.well_case));
1396
1597
  const mergedConflicts = mergeConflicts(conflictAreas, 1);
1397
- const conflict = ctx.groups.conflictGroup.selectAll(`.${ctx.classes.conflict.rect}`).data(mergedConflicts);
1398
- conflict.exit().remove();
1399
- const newConflict = conflict.enter().append("rect").attr("class", ctx.classes.conflict.rect).attr("fill", ctx.textures.conflict.url()).attr("stroke", ctx.theme.conflict.stroke).attr("stroke-width", ctx.theme.conflict.strokeWidth).on("mouseover", ctx.tooltips.conflict.show).on("mouseout", ctx.tooltips.conflict.hide);
1598
+ const conflict = ctx.groups.conflictGroup.selectAll(`.${ctx.classes.conflict.rect}`).data(mergedConflicts, makeIntervalKey("conflict"));
1599
+ conflict.exit().transition(ctx.transition).attr("height", 0).remove();
1600
+ const newConflict = conflict.enter().append("rect").attr("class", ctx.classes.conflict.rect).attr("fill", ctx.textures.conflict.url()).attr("stroke", ctx.theme.conflict.stroke).attr("stroke-width", ctx.theme.conflict.strokeWidth).on("mouseover", ctx.tooltips.conflict.show).on("mouseout", ctx.tooltips.conflict.hide).attr("y", ctx.yScale(0)).attr("height", 0);
1400
1601
  const mergedConflict = mergeEnter(newConflict, conflict).attr("x", (d) => (ctx.POCO_CENTER - xScale(d.diameter)) / 2).attr("width", (d) => xScale(d.diameter));
1401
1602
  withTransition(mergedConflict, ctx.transition).attr("y", ctx.yScale(0)).attr("height", getHeight);
1402
1603
  }
1403
1604
 
1404
1605
  // src/renderers/fractures.renderer.ts
1606
+ var d34 = __toESM(require("d3"), 1);
1405
1607
  function wavyLine(rng, steps, baseY, jitter, startInset, endInset) {
1406
1608
  const pts = [];
1407
1609
  for (let i = 0; i < steps; i++) {
@@ -1424,19 +1626,23 @@ function drawFractures(ctx, data) {
1424
1626
  classes,
1425
1627
  tooltips
1426
1628
  } = ctx;
1427
- fracturesGroup.selectAll(`g.${classes.fractures.item}`).remove();
1428
1629
  const rf = renderConfig.fractures;
1429
1630
  const halfWidth = POCO_WIDTH * rf.widthMultiplier / 2;
1430
1631
  const xa = pocoCenterX - halfWidth;
1431
1632
  const w = halfWidth * 2;
1432
1633
  const normToAbsX = (normX) => xa + normX * w;
1433
- data.forEach((fracture) => {
1634
+ const groups = fracturesGroup.selectAll(`g.${classes.fractures.item}`).data(data, makePointKey("fracture"));
1635
+ groups.exit().transition(ctx.transition).style("opacity", 0).remove();
1636
+ const entered = groups.enter().append("g").attr("class", classes.fractures.item).on("mouseover", tooltips.fracture.show).on("mouseout", tooltips.fracture.hide).style("cursor", "pointer").style("opacity", 0);
1637
+ const merged = mergeEnter(entered, groups);
1638
+ merged.attr(
1639
+ "transform",
1640
+ (fracture) => `translate(0,${yScale(fracture.depth)}) rotate(${fracture.dip},${pocoCenterX},0)`
1641
+ );
1642
+ merged.each(function(fracture) {
1643
+ const g = d34.select(this);
1644
+ g.selectAll("*").remove();
1434
1645
  const rng = makeSeededPrng(fracture.depth, 7919);
1435
- const cy = yScale(fracture.depth);
1436
- const g = fracturesGroup.append("g").attr("class", classes.fractures.item).datum(fracture).attr(
1437
- "transform",
1438
- `translate(0,${cy}) rotate(${fracture.dip},${pocoCenterX},0)`
1439
- ).on("mouseover", tooltips.fracture.show).on("mouseout", tooltips.fracture.hide).style("cursor", "pointer");
1440
1646
  const hitBuffer = fracture.swarm ? rf.hitBuffer.swarm : rf.hitBuffer.single;
1441
1647
  g.append("rect").attr("class", classes.fractures.hitArea).attr("x", xa).attr("y", -hitBuffer).attr("width", w).attr("height", hitBuffer * 2).attr("fill", "transparent").style("pointer-events", "all");
1442
1648
  const fractureStroke = fracture.water_intake ? theme.fracture.wetStroke : theme.fracture.dryStroke;
@@ -1509,11 +1715,12 @@ function drawFractures(ctx, data) {
1509
1715
  }
1510
1716
  }
1511
1717
  });
1718
+ merged.transition(ctx.transition).style("opacity", 1);
1512
1719
  }
1513
1720
 
1514
1721
  // src/renderers/highlights.renderer.ts
1515
- var d34 = __toESM(require("d3"), 1);
1516
- var import_utils3 = require("@welldot/utils");
1722
+ var d35 = __toESM(require("d3"), 1);
1723
+ var import_utils5 = require("@welldot/utils");
1517
1724
  function drawHighlightItem(ctx, group, x, y, w, h, label) {
1518
1725
  if (h <= 0 || w <= 0) return;
1519
1726
  const hlConfig = ctx.renderConfig.highlights;
@@ -1572,8 +1779,8 @@ function drawHighlights(ctx, hl) {
1572
1779
  h.label
1573
1780
  );
1574
1781
  });
1575
- const maxXVals = (0, import_utils3.getProfileDiamValues)(ctx.constructionData);
1576
- const xScaleConstr = d34.scaleLinear().domain([0, d34.max(maxXVals) || 0]).range([0, ctx.POCO_WIDTH]);
1782
+ const maxXVals = (0, import_utils5.getProfileDiamValues)(ctx.constructionData);
1783
+ const xScaleConstr = d35.scaleLinear().domain([0, d35.max(maxXVals) || 0]).range([0, ctx.POCO_WIDTH]);
1577
1784
  const constructionConfig = ctx.renderConfig.construction;
1578
1785
  if (hl.cement_pad && ctx.depthFrom === 0 && ctx.constructionData.cement_pad?.thickness) {
1579
1786
  const cp = ctx.constructionData.cement_pad;
@@ -1653,18 +1860,29 @@ function drawHighlights(ctx, hl) {
1653
1860
  stdX,
1654
1861
  stdW
1655
1862
  );
1863
+ drawConstrHighlights(
1864
+ hl.reduction,
1865
+ ctx.constructionData.reduction.map((d) => ({
1866
+ from: d.from,
1867
+ to: d.to,
1868
+ diameter: Math.max(d.diam_from, d.diam_to)
1869
+ })),
1870
+ stdX,
1871
+ stdW
1872
+ );
1656
1873
  }
1657
1874
 
1658
1875
  // src/renderers/legend.renderer.ts
1659
- var d3module2 = __toESM(require("d3"), 1);
1876
+ var d3module = __toESM(require("d3"), 1);
1660
1877
  var import_defu = require("defu");
1661
- var d35 = { ...d3module2 };
1878
+ var d36 = { ...d3module };
1662
1879
  function drawWellLegend(selector, profile, options = {}) {
1663
1880
  const fractures = profile.fractures ?? [];
1664
1881
  const caves = profile.caves ?? [];
1665
1882
  const boreHoles = profile.bore_hole ?? [];
1666
1883
  const wellCases = profile.well_case ?? [];
1667
1884
  const wellScreens = profile.well_screen ?? [];
1885
+ const reductions = profile.reduction ?? [];
1668
1886
  const holeFills = profile.hole_fill ?? [];
1669
1887
  const hasSimple = fractures.some((f) => !f.swarm && !f.water_intake);
1670
1888
  const hasSwarm = fractures.some((f) => f.swarm);
@@ -1677,6 +1895,7 @@ function drawWellLegend(selector, profile, options = {}) {
1677
1895
  const hasHoleFillSeal = holeFills.some((h) => h.type === "seal");
1678
1896
  const hasWellCase = wellCases.length > 0;
1679
1897
  const hasWellScreen = wellScreens.length > 0;
1898
+ const hasReduction = reductions.length > 0;
1680
1899
  const hasCementPad = !!profile.cement_pad?.thickness;
1681
1900
  const hasConflict = mergeConflicts(
1682
1901
  [
@@ -1763,13 +1982,19 @@ function drawWellLegend(selector, profile, options = {}) {
1763
1982
  label: cfg.labels.wellScreen,
1764
1983
  subKind: "wellScreen"
1765
1984
  });
1985
+ if (hasReduction)
1986
+ items.push({
1987
+ kind: "construction",
1988
+ label: cfg.labels.reduction,
1989
+ subKind: "reduction"
1990
+ });
1766
1991
  if (hasConflict)
1767
1992
  items.push({
1768
1993
  kind: "construction",
1769
1994
  label: cfg.labels.conflict,
1770
1995
  subKind: "conflict"
1771
1996
  });
1772
- const svgEl = d35.select(selector);
1997
+ const svgEl = d36.select(selector);
1773
1998
  svgEl.selectAll("*").remove();
1774
1999
  if (items.length === 0) {
1775
2000
  svgEl.attr("width", 0).attr("height", 0);
@@ -1821,6 +2046,15 @@ function drawWellLegend(selector, profile, options = {}) {
1821
2046
  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");
1822
2047
  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);
1823
2048
  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);
2049
+ } else if (item.subKind === "reduction") {
2050
+ const topW = rw * 0.5;
2051
+ const y1 = rowSymY - rh / 2;
2052
+ const y2 = rowSymY + rh / 2;
2053
+ const midX = cx + rw / 2;
2054
+ symG.append("polygon").attr("class", cls.constructionRect).attr(
2055
+ "points",
2056
+ `${midX - topW / 2},${y1} ${midX + topW / 2},${y1} ${cx + rw},${y2} ${cx},${y2}`
2057
+ ).attr("fill", theme.reduction.fill).attr("stroke", theme.reduction.stroke).attr("stroke-width", theme.legend.itemStrokeWidth);
1824
2058
  } else {
1825
2059
  const symMap = {
1826
2060
  boreHole: {
@@ -1878,11 +2112,11 @@ function drawLithology(ctx, data) {
1878
2112
  from: depthFrom,
1879
2113
  to: depthTo
1880
2114
  });
1881
- const rects = lithologyGroup.selectAll(`.${classes.lithology.rect}`).data(data);
1882
- rects.exit().remove();
1883
- const newLayers = rects.enter().append("rect").attr("class", classes.lithology.rect).attr("x", geoXLeft).attr("width", geoXRight - geoXLeft).attr("stroke", theme.lithology.stroke).attr("stroke-width", theme.lithology.strokeWidth).on("mouseover", tooltips.geology.show).on("mouseout", tooltips.geology.hide);
1884
- const merged = mergeEnter(newLayers, rects).attr("y", getYPos);
1885
- withTransition(merged, transition).attr("height", getHeight).attr("fill", getLithologyFill(data, svg, ctx.theme.lithologyTexture));
2115
+ const rects = lithologyGroup.selectAll(`.${classes.lithology.rect}`).data(data, makeIntervalKey("lithology"));
2116
+ rects.exit().transition(transition).attr("height", 0).remove();
2117
+ const newLayers = rects.enter().append("rect").attr("class", classes.lithology.rect).attr("x", geoXLeft).attr("width", geoXRight - geoXLeft).attr("stroke", theme.lithology.stroke).attr("stroke-width", theme.lithology.strokeWidth).on("mouseover", tooltips.geology.show).on("mouseout", tooltips.geology.hide).attr("y", getYPos).attr("height", 0);
2118
+ const merged = mergeEnter(newLayers, rects);
2119
+ withTransition(merged, transition).attr("y", getYPos).attr("height", getHeight).attr("fill", getLithologyFill(data, svg, ctx.theme.lithologyTexture));
1886
2120
  }
1887
2121
 
1888
2122
  // src/renderers/unit-labels.renderer.ts
@@ -1969,20 +2203,26 @@ function buildSvgStyleBlock(theme) {
1969
2203
  `.wp-annotation-label{font-size:${s(labels.fontSize)}px}`,
1970
2204
  `.wp-annotation-header{font-weight:${s(labels.headerFontWeight ?? 600)};font-family:${s(labels.headerFont)};font-size:${s(labels.fontSize)}px;color:${s(labels.color)}}`,
1971
2205
  `.wp-annotation-body{font-weight:${s(labels.bodyFontWeight ?? 400)};font-family:${s(labels.bodyFont ?? "sans-serif")};font-size:${s(labels.fontSize)}px;color:${s(labels.bodyColor)}}`,
1972
- `.wp-depth-tip-text{font-family:${s(labels.bodyFont ?? "sans-serif")};font-size:${s(labels.fontSize)}px;color:${s(labels.color)}}`
2206
+ `.wp-depth-tip-text{font-family:${s(labels.bodyFont ?? "sans-serif")};font-size:${s(labels.fontSize)}px;color:${s(labels.color)}}`,
2207
+ `.wp-tooltip{position:absolute;z-index:1000;display:flex;flex-direction:column;gap:2px;max-width:240px;padding:8px 10px;border-radius:6px;background:${s(labels.annotationBg)};border:1px solid ${s(labels.annotationBorderColor ?? "rgba(0,0,0,0.08)")};box-shadow:0 4px 12px rgba(0,0,0,0.15);font-family:${s(labels.bodyFont ?? "sans-serif")};line-height:1.45}`,
2208
+ `.wp-tooltip-title{font-family:${s(labels.headerFont)};font-weight:${s(labels.headerFontWeight ?? 600)};font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:${s(labels.color)}}`,
2209
+ `.wp-tooltip-primary{font-weight:${s(labels.headerFontWeight ?? 600)};font-size:12px;color:${s(labels.color)}}`,
2210
+ `.wp-tooltip-secondary{font-size:11px;color:${s(labels.bodyColor)}}`
1973
2211
  ].join("");
1974
2212
  }
1975
2213
 
1976
2214
  // src/Renderer.ts
1977
- var d36 = Object.assign(d3module3, { tip: import_d3_tip2.default });
2215
+ var d37 = { ...d3module2, tip: import_d3_tip2.default };
1978
2216
  var WellRenderer = class {
1979
2217
  /**
1980
2218
  * @param svgs - One entry per SVG panel; multi-entry for split-panel layouts.
1981
2219
  * @param options.classNames - Override default BEM CSS class names.
1982
2220
  * @param options.units - Active measurement units (`length` and `diameter`).
2221
+ * @param options.locale - Locale for renderer-drawn text (diameter symbol, and any `RenderConfig` fields resolved via `applyRenderLocale`). Defaults to `'pt'`, matching the package's historical output.
1983
2222
  * @param options.renderConfig - Rendering behaviour (zoom, pan, animation, layout). Defaults to `INTERACTIVE_RENDER_CONFIG`.
1984
2223
  * @param options.theme - Visual theme overrides. Merged on top of `DEFAULT_WELL_THEME`.
1985
2224
  * @param options.onError - Called when a draw error occurs (e.g., a renderer throws). Falls back to `console.error` if omitted.
2225
+ * @param options.onZoom - Called with the current zoom scale (`1` = initial/fit) whenever the user zooms or pans, and whenever `zoomBy`/`resetZoom` are called. No-op if `renderConfig.zoom`/`pan` are both disabled.
1986
2226
  */
1987
2227
  constructor(svgs, options = {}) {
1988
2228
  this.svgInstances = [];
@@ -1995,6 +2235,7 @@ var WellRenderer = class {
1995
2235
  length: "m",
1996
2236
  diameter: "mm"
1997
2237
  };
2238
+ this.locale = "pt";
1998
2239
  if (svgs.length === 0) {
1999
2240
  console.warn(
2000
2241
  "[WellRenderer] No SVG instances provided. The renderer will be a no-op."
@@ -2003,6 +2244,7 @@ var WellRenderer = class {
2003
2244
  }
2004
2245
  this.svgInstances = svgs;
2005
2246
  if (options.onError) this.onError = options.onError;
2247
+ if (options.onZoom) this.onZoom = options.onZoom;
2006
2248
  if (options.classNames) {
2007
2249
  this.classes = (0, import_defu2.defu)(
2008
2250
  options.classNames,
@@ -2012,6 +2254,9 @@ var WellRenderer = class {
2012
2254
  if (options.units) {
2013
2255
  this.units = { ...this.units, ...options.units };
2014
2256
  }
2257
+ if (options.locale) {
2258
+ this.locale = options.locale;
2259
+ }
2015
2260
  if (options.renderConfig) {
2016
2261
  this.renderConfig = (0, import_defu2.defu)(
2017
2262
  options.renderConfig,
@@ -2027,7 +2272,7 @@ var WellRenderer = class {
2027
2272
  const { selector, height, width, margins } = inst;
2028
2273
  const svgWidth = width + margins.left + margins.right;
2029
2274
  const svgHeight = height + margins.top + margins.bottom;
2030
- const svg = d36.select(selector);
2275
+ const svg = d37.select(selector);
2031
2276
  svg.selectAll("*").remove();
2032
2277
  svg.attr("height", svgHeight).attr("width", svgWidth);
2033
2278
  const defs = svg.append("defs");
@@ -2047,14 +2292,15 @@ var WellRenderer = class {
2047
2292
  );
2048
2293
  const fracturesGroup = poco.append("g").attr("class", this.classes.fractures.group).attr("transform", `translate(${margins.left}, ${margins.top})`).attr("clip-path", `url(#${clipId})`);
2049
2294
  fracturesGroup.append("g").attr("class", this.classes.highlights.fracturesGroup);
2050
- construction.append("g").attr("class", this.classes.constructionLabels.group);
2051
2295
  construction.append("g").attr("class", this.classes.cementPad.group);
2052
2296
  construction.append("g").attr("class", this.classes.boreHole.group);
2053
2297
  construction.append("g").attr("class", this.classes.surfaceCase.group);
2054
2298
  construction.append("g").attr("class", this.classes.holeFill.group);
2055
2299
  construction.append("g").attr("class", this.classes.wellCase.group);
2056
2300
  construction.append("g").attr("class", this.classes.wellScreen.group);
2301
+ construction.append("g").attr("class", this.classes.reduction.group);
2057
2302
  construction.append("g").attr("class", this.classes.conflict.group);
2303
+ construction.append("g").attr("class", this.classes.constructionLabels.group);
2058
2304
  construction.append("g").attr("class", this.classes.highlights.constructionGroup);
2059
2305
  Object.values(this.textures).forEach((texture) => svg.call(texture));
2060
2306
  return { svg, height, width, margins, clipId, clipRectId };
@@ -2068,6 +2314,36 @@ var WellRenderer = class {
2068
2314
  textures: this.renderConfig.textures
2069
2315
  });
2070
2316
  }
2317
+ /**
2318
+ * Multiplies the current zoom scale by `factor` (e.g. `1.25` to zoom in,
2319
+ * `1 / 1.25` to zoom out) on every panel. No-op if both `renderConfig.zoom`
2320
+ * and `renderConfig.pan` are disabled (no zoom behavior was attached).
2321
+ */
2322
+ zoomBy(factor) {
2323
+ for (const state of this.instanceStates) {
2324
+ if (!state.zoomNode) continue;
2325
+ asSvgElement(state.svg).transition().call(state.zoomNode.scaleBy, factor);
2326
+ }
2327
+ }
2328
+ /**
2329
+ * Resets zoom/pan on every panel back to `renderConfig.zoomLevel`
2330
+ * (defaults to `1`, i.e. the initial fit-to-container view).
2331
+ */
2332
+ resetZoom() {
2333
+ const target = d37.zoomIdentity.scale(this.renderConfig.zoomLevel ?? 1);
2334
+ for (const state of this.instanceStates) {
2335
+ if (!state.zoomNode) continue;
2336
+ asSvgElement(state.svg).transition().call(state.zoomNode.transform, target);
2337
+ }
2338
+ }
2339
+ /**
2340
+ * Current zoom scale factor of the first panel (`1` = initial/fit).
2341
+ * Returns `1` if zoom/pan is disabled or `prepareSvg()` hasn't run yet.
2342
+ */
2343
+ getZoomScale() {
2344
+ const node = this.instanceStates[0]?.svg.node();
2345
+ return node ? d37.zoomTransform(node).k : 1;
2346
+ }
2071
2347
  /**
2072
2348
  * Initialises the SVG DOM structure and preloads FGDC textures.
2073
2349
  * Must be called once before the first `draw()`.
@@ -2082,14 +2358,23 @@ var WellRenderer = class {
2082
2358
  * Renders the well profile into all configured SVG panels.
2083
2359
  * @param profile - The well data to render.
2084
2360
  * @param options.units - Override the instance-level measurement units for this render.
2361
+ * @param options.locale - Override the instance-level locale for this render.
2085
2362
  * @param options.highlights - Highlight overlays to display on top of the profile.
2086
2363
  */
2087
2364
  draw(profile, options = {}) {
2088
2365
  if ((0, import_core.isWellEmpty)(profile)) return;
2366
+ if (profile.version !== 2) {
2367
+ console.warn(
2368
+ `[WellRenderer] Received a .well file with version ${profile.version ?? "unknown"}. Only v2 is supported. Normalize with deserializeWell() from @welldot/core before rendering.`
2369
+ );
2370
+ }
2089
2371
  this.units = { ...this.units, ...options.units };
2372
+ if (options.locale) {
2373
+ this.locale = options.locale;
2374
+ }
2090
2375
  const highlights = options.highlights ?? {};
2091
- const maxValues = (0, import_utils4.getProfileLastItemsDepths)(profile);
2092
- const maxYValues = d36.max(maxValues) || 0;
2376
+ const maxValues = (0, import_utils6.getProfileLastItemsDepths)(profile);
2377
+ const maxYValues = d37.max(maxValues) || 0;
2093
2378
  if (this.instanceStates.length === 1) {
2094
2379
  this.drawLogToInstance(
2095
2380
  this.instanceStates[0],
@@ -2101,7 +2386,7 @@ var WellRenderer = class {
2101
2386
  return;
2102
2387
  }
2103
2388
  const totalHeight = this.instanceStates.reduce((s, st) => s + st.height, 0) || 1;
2104
- const yScaleGlobal = d36.scaleLinear().domain([0, maxYValues]).range([0, totalHeight]);
2389
+ const yScaleGlobal = d37.scaleLinear().domain([0, maxYValues]).range([0, totalHeight]);
2105
2390
  let currentDepth = 0;
2106
2391
  for (const state of this.instanceStates) {
2107
2392
  const maxSvgDepth = yScaleGlobal.invert(
@@ -2133,6 +2418,7 @@ var WellRenderer = class {
2133
2418
  const holeFillGroup = svg.select(`.${this.classes.holeFill.group}`);
2134
2419
  const wellCaseGroup = svg.select(`.${this.classes.wellCase.group}`);
2135
2420
  const wellScreenGroup = svg.select(`.${this.classes.wellScreen.group}`);
2421
+ const reductionGroup = svg.select(`.${this.classes.reduction.group}`);
2136
2422
  const conflictGroup = svg.select(`.${this.classes.conflict.group}`);
2137
2423
  const constructionLabelsGroup = svg.select(
2138
2424
  `.${this.classes.constructionLabels.group}`
@@ -2153,12 +2439,14 @@ var WellRenderer = class {
2153
2439
  const POCO_WIDTH = svgWidth * pocoWidthRatio;
2154
2440
  const POCO_CENTER = svgWidth * pocoCenterRatio;
2155
2441
  const { duration, ease } = this.renderConfig.animation;
2156
- const transition = d36.transition().duration(duration).ease(ease);
2442
+ const transition = d37.transition().duration(duration).ease(ease);
2157
2443
  const tooltips = populateTooltips(
2158
2444
  svg,
2159
2445
  this.classes,
2160
2446
  this.units,
2161
- this.renderConfig.tooltips
2447
+ this.renderConfig.tooltips,
2448
+ this.renderConfig.tooltipLabels,
2449
+ this.locale
2162
2450
  );
2163
2451
  const { xLeft: geoXLeft, xRightInset: geoXRightInset } = this.renderConfig.geologic;
2164
2452
  const geoXRight = svgWidth - geoXRightInset;
@@ -2179,11 +2467,14 @@ var WellRenderer = class {
2179
2467
  fractures: profile.fractures
2180
2468
  };
2181
2469
  const contentHeight = svgHeight - state.margins.top - state.margins.bottom;
2182
- const yScaleLocal = d36.scaleLinear().domain([depthFrom, depthTo]).range([0, contentHeight]);
2470
+ const yScaleLocal = d37.scaleLinear().domain([depthFrom, depthTo]).range([0, contentHeight]);
2183
2471
  const depthFactor = this.units.length === "ft" ? 3.28084 : 1;
2184
- const yScaleAxis = d36.scaleLinear().domain([depthFrom * depthFactor, depthTo * depthFactor]).range([0, contentHeight]);
2185
- const yAxis = d36.axisLeft(yScaleAxis).tickFormat(
2186
- (d) => `${d}${getLengthUnit(this.units.length)}`
2472
+ const yScaleAxis = d37.scaleLinear().domain([depthFrom * depthFactor, depthTo * depthFactor]).range([0, contentHeight]);
2473
+ const yAxis = d37.axisLeft(yScaleAxis).tickFormat(
2474
+ (d) => `${(0, import_utils6.formatNumber)(Number(d), {
2475
+ maximumFractionDigits: this.units.length === "ft" ? 1 : 2,
2476
+ locale: this.locale
2477
+ })}${getLengthUnit(this.units.length)}`
2187
2478
  );
2188
2479
  const gY = svg.select(`.${this.classes.yAxis}`).call(yAxis);
2189
2480
  gY.select(".domain").attr("stroke", this.theme.labels.color).attr("fill", "none");
@@ -2220,6 +2511,7 @@ var WellRenderer = class {
2220
2511
  holeFillGroup,
2221
2512
  wellCaseGroup,
2222
2513
  wellScreenGroup,
2514
+ reductionGroup,
2223
2515
  conflictGroup,
2224
2516
  highlightsGeologicGroup,
2225
2517
  highlightsConstructionGroup,
@@ -2245,6 +2537,7 @@ var WellRenderer = class {
2245
2537
  classes: this.classes,
2246
2538
  textures: this.textures,
2247
2539
  units: this.units,
2540
+ locale: this.locale,
2248
2541
  constructionData,
2249
2542
  groups
2250
2543
  };
@@ -2275,12 +2568,24 @@ var WellRenderer = class {
2275
2568
  surfaceCaseGroup.selectAll(`g.${this.classes.surfaceCase.rect}`).each((d, i, nodes) => {
2276
2569
  if (!d) return;
2277
2570
  const sc = d;
2278
- const g = d36.select(nodes[i]);
2571
+ const g = d37.select(nodes[i]);
2279
2572
  const y = transform.applyY(spanY(sc));
2280
2573
  const h = transform.k * spanH(sc);
2281
2574
  g.select(".surface-case-fill").attr("y", y).attr("height", h);
2282
2575
  g.selectAll(".surface-case-side").attr("y1", y).attr("y2", y + h);
2283
2576
  });
2577
+ const maxXValsReduction = (0, import_utils6.getProfileDiamValues)(constructionData);
2578
+ const xScaleReduction = d37.scaleLinear().domain([0, d37.max(maxXValsReduction) || 0]).range([0, POCO_WIDTH]);
2579
+ reductionGroup.selectAll(`polygon.${this.classes.reduction.item}`).attr("points", (d) => {
2580
+ const r = d;
2581
+ const yTop = transform.applyY(spanY(r));
2582
+ const yBot = yTop + transform.k * spanH(r);
2583
+ const xTopLeft = (POCO_CENTER - xScaleReduction(r.diam_from)) / 2;
2584
+ const xTopRight = (POCO_CENTER + xScaleReduction(r.diam_from)) / 2;
2585
+ const xBotLeft = (POCO_CENTER - xScaleReduction(r.diam_to)) / 2;
2586
+ const xBotRight = (POCO_CENTER + xScaleReduction(r.diam_to)) / 2;
2587
+ return `${xTopLeft},${yTop} ${xTopRight},${yTop} ${xBotRight},${yBot} ${xBotLeft},${yBot}`;
2588
+ });
2284
2589
  fracturesGroup.selectAll(`g.${this.classes.fractures.item}`).attr("transform", (d) => {
2285
2590
  if (!d) return null;
2286
2591
  const f = d;
@@ -2302,6 +2607,7 @@ var WellRenderer = class {
2302
2607
  well_screen: constructionData.well_screen.filter(inDepth)
2303
2608
  });
2304
2609
  drawHighlights(zoomedCtx, highlights);
2610
+ this.onZoom?.(transform.k);
2305
2611
  };
2306
2612
  const drawProfile = () => {
2307
2613
  drawLithology(ctx, profile.lithology?.filter(inDepth) ?? []);
@@ -2341,17 +2647,23 @@ var WellRenderer = class {
2341
2647
  }
2342
2648
  const { zoom: zoomEnabled, pan: panEnabled } = this.renderConfig;
2343
2649
  if (zoomEnabled || panEnabled) {
2344
- const zoomNode = d36.zoom();
2650
+ const zoomNode = d37.zoom();
2345
2651
  if (zoomEnabled || panEnabled) zoomNode.on("zoom", zooming);
2346
- if (!zoomEnabled) zoomNode.scaleExtent([1, 1]);
2652
+ zoomNode.scaleExtent(
2653
+ zoomEnabled ? [
2654
+ this.renderConfig.minZoomScale ?? 0,
2655
+ this.renderConfig.maxZoomScale ?? Infinity
2656
+ ] : [1, 1]
2657
+ );
2347
2658
  if (!panEnabled)
2348
2659
  zoomNode.filter((e) => e.type === "wheel" || e.type === "dblclick");
2349
2660
  asSvgElement(svg).call(zoomNode);
2661
+ state.zoomNode = zoomNode;
2350
2662
  const initialZoom = this.renderConfig.zoomLevel ?? 1;
2351
2663
  if (initialZoom !== 1) {
2352
2664
  asSvgElement(svg).call(
2353
2665
  zoomNode.transform,
2354
- d36.zoomIdentity.scale(initialZoom)
2666
+ d37.zoomIdentity.scale(initialZoom)
2355
2667
  );
2356
2668
  }
2357
2669
  }
@@ -2361,13 +2673,16 @@ var WellRenderer = class {
2361
2673
  0 && (module.exports = {
2362
2674
  DEFAULT_WELL_THEME,
2363
2675
  INTERACTIVE_RENDER_CONFIG,
2676
+ RENDER_LABELS,
2364
2677
  STATIC_RENDER_CONFIG,
2365
2678
  WellRenderer,
2679
+ applyRenderLocale,
2366
2680
  drawWellLegend,
2367
2681
  formatDiameter,
2368
2682
  formatLength,
2369
2683
  getDiameterUnit,
2370
2684
  getLengthUnit,
2371
- importFgdcTextures
2685
+ importFgdcTextures,
2686
+ resolveRenderLabel
2372
2687
  });
2373
2688
  //# sourceMappingURL=index.cjs.map