TableView Interaction

This Table View Interaction tutorial demonstrates how the WellLog widget can interact with the TableView widget. Both these widgets are created separately and added to the canvas. Multiple layers in one canvas can be synchronized automatically because they are located in the same plot, correlating the depths of points in the WellLog with the corresponding values of the TableView. If the widgets are located in different plots, then the scale can be synchronized similar to plot synchronization.

# TableView Interaction

In the TableView below the depth range and curve values are calculated basing on the visible limits and current scale of the WellLog widget. Highlighting depth and the curves are synchronized for both widgets.

import { from } from "@int/geotoolkit/selection/from.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { MathUtil } from "@int/geotoolkit/util/MathUtil.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { AdaptiveLogCurveVisualHeader, Elements } from "@int/geotoolkit/welllog/header/AdaptiveLogCurveVisualHeader.ts";
import { TrackType as WellLogTrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { Events as SplitterEvents } from "@int/geotoolkit/welllog/widgets/tools/Splitter.ts";
import { Point } from "@int/geotoolkit/util/Point.ts";
import { Events as CrossHairEvents } from "@int/geotoolkit/controls/tools/CrossHair.ts";
import { Events as WellLogEvents } from "@int/geotoolkit/welllog/widgets/Events.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { CssLayout } from "@int/geotoolkit/layout/CssLayout.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Events as SelectionEvents } from "@int/geotoolkit/controls/tools/Selection.ts";
import { Events as AbstractToolEvents } from "@int/geotoolkit/controls/tools/AbstractTool.ts";
import { Events as HighlightEvents } from "@int/geotoolkit/controls/tools/tableview/Highlight.ts";
import { TableView } from "@int/geotoolkit/widgets/TableView.ts";
import { Events as ScrollEvents } from "@int/geotoolkit/controls/tools/scroll/AbstractScroll.ts";
import { Events as PanningEvents } from "@int/geotoolkit/controls/tools/Panning.ts";
import { FontSubType } from "@int/geotoolkit/pdf/FontSubType.ts";
import { HttpClient } from "@int/geotoolkit/http/HttpClient.ts";
import { TableElement } from "@int/geotoolkit/report/dom/elements/TableElement.ts";
import { DataTable } from "@int/geotoolkit/data/DataTable.ts";
import { DataTableView } from "@int/geotoolkit/data/DataTableView.ts";
import { DOMParser } from "@int/geotoolkit/report/dom/DOMParser.ts";
import { Parser } from "@int/geotoolkit/report/Parser.ts";
import { ReportML } from "/src/code/WellLog/AdditionalFunctionality/TableViewInteraction/reports/ReportML.ts";
import intLogoPng from "/src/code/WellLog/AdditionalFunctionality/TableViewInteraction/reports/templates/int-logo.png?import";
import { ImageMap } from "@int/geotoolkit/report/resources/ImageMap.ts";
import xmlDocumentTemplate from "/src/code/WellLog/AdditionalFunctionality/TableViewInteraction/reports/templates/composite-template.xml?import";
import { Registry } from "@int/geotoolkit/report/dom/elements/Registry.ts";
import { obfuscate } from "@int/geotoolkit/lib.js";
import { DataProvider } from "@int/geotoolkit/controls/shapes/tableview/DataProvider.ts";
import { curveData } from "/src/code/WellLog/utils/curveData.ts";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
import { Sections } from "@int/geotoolkit/welllog/header/AdaptiveLogVisualHeader.ts";
function reInitializeDomRegistry() {
  Registry.getDefaultInstance().register("well-log", ReportML.Nodes.WellLog);
}
const COLS = ["GR", "CALI", "SP"];
function createTestData(from2, step, curveMnemonic) {
  const depths = [];
  const values = [];
  const curveDat = curveData[curveMnemonic];
  const amountOfPoints = curveDat.length;
  for (let i = 0; i < amountOfPoints; i++) {
    depths.push(i * step + from2);
    values.push(curveDat[i]);
  }
  return new LogData({
    "name": curveMnemonic,
    "depths": depths,
    "values": values
  });
}
class TableViewInteraction {
  constructor(setZoomButton) {
    reInitializeDomRegistry();
    this._wellLogWidget = null;
    this._tableWidget = null;
    this.setZoomButton = setZoomButton;
    this._dataSources = {};
    this._longestLogDataSource = null;
    let curveMnemonic;
    for (curveMnemonic in curveData) {
      if (curveData.hasOwnProperty(curveMnemonic) === false) {
        continue;
      }
      const logDataSource = createTestData(4500, 10, curveMnemonic);
      this._dataSources[curveMnemonic] = logDataSource;
      if (this._longestLogDataSource == null) {
        this._longestLogDataSource = logDataSource;
      } else if (logDataSource.getMaxDepth() > this._longestLogDataSource.getMaxDepth()) {
        this._longestLogDataSource = logDataSource;
      }
    }
    this._depths = this._longestLogDataSource.getDepths();
    this._curves = {};
  }
  synchronizeTableData() {
    const depthLimits = this._wellLogWidget.getDepthLimits();
    const visibleDepthLimits = this._wellLogWidget.getVisibleDepthLimits();
    const step = Math.ceil(visibleDepthLimits.getSize() / 10) / 2;
    let low = Math.floor(depthLimits.getLow() / step);
    const high = Math.ceil(depthLimits.getHigh() / step);
    if (low * step <= depthLimits.getLow()) {
      low++;
    }
    this._depths = [];
    for (let i = low; i <= high; i++) {
      this._depths.push(i * step);
    }
    const self = this;
    this._tableWidget.setData({
      "dataprovider": new (obfuscate(class extends DataProvider {
        getContentData(col, row) {
          const src = self._dataSources[COLS[col]];
          const value = src.getValueAt(self._depths[row]);
          return isNaN(value) ? "-" : value.toFixed(2);
        }
        getIndexData(row) {
          return self._depths[row].toFixed();
        }
        getHeaderData(id) {
          return COLS[id];
        }
      }))(),
      "rows": this._depths.length
    });
    const position = this._wellLogWidget.getToolByName("cross-hair").getPosition();
    if (!isNaN(position.getY()) && !isNaN(position.getX())) {
      const depth = position.getY();
      const closest = this._depths.reduce((acc, val, id) => val <= depth ? id : acc, 0);
      this._tableWidget.highlightRow(closest);
    }
  }
  initializeWellLog() {
    const widget = createWellLogWidget().setDepthLimits(this._longestLogDataSource.getMinDepth(), this._longestLogDataSource.getMaxDepth());
    widget.getHeaderContainer().getHeaderProvider().registerHeaderProvider(LogCurve.getClassName(), new AdaptiveLogCurveVisualHeader().setElement([Elements.ScaleFrom, Elements.ScaleTo], {
      "section": Sections.Top
    }).setElement(Elements.Tracking, {
      "section": Sections.Top
    }));
    widget.getToolByName("splitter").setEnabled(true).on(SplitterEvents.onCanResize, (evt, sender, event) => {
      event.reject();
    });
    widget.getToolByName("cross-hair").on(CrossHairEvents.onPositionChanged, (evt, sender, event) => {
      const position = event.getPosition();
      if (isNaN(position.getY()) || isNaN(position.getX())) {
        return;
      }
      const depth = position.getY();
      const closest = this._depths.reduce((acc, val, id) => val <= depth ? id : acc, 0);
      this._tableWidget.highlightRow(closest);
    });
    widget.getToolByName("TrackPlotVerticalScroll").on(ScrollEvents.onScroll, () => {
      const limits = widget.getVisibleDepthLimits();
      const step = this._depths[1] - this._depths[0];
      const top = (limits.getLow() - this._depths[0]) / step;
      const bottom = top + this._tableWidget.getVisibleTableLimits().getHeight();
      this._tableWidget.setVisibleTableLimits(Math.ceil(top));
      this._tableWidget.setVisibleTableLimits(Math.floor(bottom));
    });
    widget.getToolByName("pick").on(SelectionEvents.onSelectionChanged, () => {
      const selection = this._wellLogWidget.getSelectedVisuals();
      const ids = selection.map((visual) => COLS.indexOf(visual.getName()));
      this._tableWidget.highlightColumn(ids.length > 0 ? ids[0] : -1);
    });
    widget.getToolByName("rubberband").setAutoDisabled(true).on(AbstractToolEvents.onEnabledStateChanged, (evt, sender) => {
      this.setZoomButton(sender.isEnabled());
    });
    widget.on(WellLogEvents.VisibleDepthLimitsChanged, (event, sender, eventArgs) => {
      if (Math.abs(eventArgs["old"].getSize() - eventArgs["new"].getSize()) < 1e-3)
        return;
      this.synchronizeTableData();
    });
    widget.addTrack(WellLogTrackType.IndexTrack).setWidth(80);
    widget.addTrack(WellLogTrackType.LinearTrack).addChild([
      this.createCurve("GR", KnownColors.Green),
      this.createCurve("CALI", KnownColors.Orange),
      this.createCurve("SP", KnownColors.Blue)
    ]);
    return widget;
  }
  initializeTableWidget() {
    const tableWidget = new TableView({
      "horizontalscroll": "floating",
      "verticalscroll": "floating",
      "indexwidth": 50,
      "cols": COLS.length,
      "bounds": new Rect(300, 0, 665, 400)
    });
    const crosshair = this._wellLogWidget.getToolByName("cross-hair");
    tableWidget.getToolByName("TableViewHighlight").on(HighlightEvents.onHover, (evt, sender, eventArgs) => {
      const depth = this._depths[eventArgs.getRowNumber()] || NaN;
      crosshair.setPosition(new Point(0, depth));
    }).on(AbstractToolEvents.onLeave, () => {
      crosshair.setPosition(new Point(0, NaN));
    });
    const pick = this._wellLogWidget.getToolByName("pick");
    tableWidget.getToolByName("TableViewSelection").on(SelectionEvents.onPick, () => {
      const col = tableWidget.getHighlightedColumn();
      pick.setSelection(col >= 0 ? [this._curves[COLS[col]]] : []);
    });
    const synchronizeLimits = () => {
      const limits = tableWidget.getVisibleTableLimits();
      const top = Math.floor(limits.getTop());
      const low = (top + 1 - limits.getTop()) * this._depths[top] + (limits.getTop() - top) * this._depths[top + 1];
      const high = low + this._wellLogWidget.getVisibleDepthLimits().getSize();
      this._wellLogWidget.setVisibleDepthLimits(low, high);
    };
    tableWidget.getToolByName("VerticalScroll").on(ScrollEvents.onScroll, synchronizeLimits);
    tableWidget.getToolByName("TableViewPanning").on(PanningEvents.onPanning, synchronizeLimits);
    return tableWidget;
  }
  disableRubberBand() {
    this._wellLogWidget.getToolByName("rubberband").setEnabled(false);
  }
  zoomIn() {
    this._wellLogWidget.scale(5 / 4);
    this.disableRubberBand();
  }
  zoomOut() {
    this._wellLogWidget.scale(4 / 5);
    this.disableRubberBand();
  }
  fitToHeight() {
    this._wellLogWidget.fitToHeight();
    this.disableRubberBand();
  }
  rubberBandZoom() {
    this._wellLogWidget.getToolByName("rubberband").setEnabled(true);
  }
  createCurve(name, color) {
    const dataSource = this._dataSources[name];
    const limits = MathUtil.calculateNeatLimits(dataSource.getMinValue(), dataSource.getMaxValue(), false, false);
    this._curves[name] = new LogCurve(dataSource).setLineStyle(color).setNormalizationLimits(limits.getLow(), limits.getHigh());
    return this._curves[name];
  }
  initialize(canvas) {
    this._plot = new Plot({
      "canvaselement": canvas,
      "root": new Group().setAutoModelLimitsMode(true).setLayout(new CssLayout()).addChild([
        this._wellLogWidget = this.initializeWellLog().setLayoutStyle({
          "left": 0,
          "top": 0,
          "width": 300,
          "bottom": 0
        }),
        this._tableWidget = this.initializeTableWidget()
      ])
    });
    this._wellLogWidget.setHeaderHeight("auto").fitToHeight();
    this.synchronizeTableData();
  }
  loadFonts(exportOptions) {
    return Promise.all([{
      "fontUrl": "./src/assets/fonts/roboto.ttf",
      "fontName": "Roboto"
    }, {
      "fontUrl": "./src/assets/fonts/Roboto-Bold.ttf",
      "fontName": "Roboto",
      "fontWeight": "bold"
    }].map(
      (font) => HttpClient.getInstance().getHttp().get(font["fontUrl"], {
        "responsetype": "blob"
      }).then((response) => {
        const fileReader = new FileReader();
        fileReader.onloadend = () => {
          exportOptions["embededFonts"] = exportOptions["embededFonts"] || [];
          exportOptions["embededFonts"].push({
            "subType": FontSubType.TrueType,
            "fontName": font["fontName"],
            "fontWeight": font["fontWeight"] || "normal",
            "fontStyle": "normal",
            "fontBase64EncodedFile": fileReader.result,
            "encoding": "Identity-H"
          });
        };
        return fileReader.readAsDataURL(response.data);
      })
    ));
  }
  exportToPDF(options, printDecimatedDepths) {
    const exportOptions = {
      "output": "Widget",
      "printsettings": options != null ? options["printSettings"] : null,
      "progress": options != null ? options["progress"] : null
    };
    return this.loadFonts(exportOptions).then(
      () => this.processTemplate(xmlDocumentTemplate, printDecimatedDepths).then((geoDocument) => geoDocument.exportToPdf(exportOptions))
    );
  }
  processTemplate(xml, printDecimatedDepths) {
    return DOMParser.parse(xml).then((cgDomDocument) => {
      this.preProcessDomDocument(cgDomDocument, printDecimatedDepths);
      const imageMap = new ImageMap().registerImage("int-logo.png", intLogoPng);
      return new Parser(cgDomDocument).setResourceManager(imageMap).parse();
    }, (exception) => {
      let message = "ERROR cannot load the template";
      if (typeof exception === "string") {
        message = exception;
      } else if (typeof exception.toString === "function") {
        message = exception.toString();
      }
      global.console.log(message);
    });
  }
  preProcessTableDomElement(tableNode, printDecimatedDepths) {
    const paramColumns = [];
    const header = tableNode.rows.item(0);
    const depthLimits = this._wellLogWidget.getDepthLimits();
    const visibleDepthLimits = this._wellLogWidget.getVisibleDepthLimits();
    const step = Math.ceil(visibleDepthLimits.getSize() / 10) / 2;
    let low = Math.floor(depthLimits.getLow() / step);
    const high = Math.ceil(depthLimits.getHigh() / step);
    if (low * step <= depthLimits.getLow()) {
      low++;
    }
    for (let i = 0; i < header.cells.length; i++) {
      const name = header.cells.item(i).getAttribute("name");
      let data = null;
      let dataSource = null;
      if (name != null) {
        if (name.toLowerCase() === "depth") {
          data = this._longestLogDataSource.getDepths();
          dataSource = new LogData({
            "depths": data,
            "values": data
          });
        } else if (this._dataSources[name] != null) {
          dataSource = this._dataSources[name];
          data = this._dataSources[name].getValues();
        }
      }
      if (data == null) {
        continue;
      }
      if (printDecimatedDepths) {
        data = [];
        for (let i2 = low; i2 <= high; i2++) {
          let value = dataSource.getValueAt(i2 * step);
          if (Number.isNaN(value)) {
            continue;
          }
          value = value.toFixed(2);
          data.push(value);
        }
      }
      paramColumns.push({
        name: name ? name.toLowerCase() : "",
        type: "number",
        data
      });
    }
    const paramsDataTable = new DataTable({
      "cols": paramColumns
    });
    const paramsDataTableView = new DataTableView(paramsDataTable);
    tableNode.setDataTable(paramsDataTableView, true);
  }
  preProcessDomDocument(cgDomDocument, printDecimatedDepths) {
    const wellLogNode = from(cgDomDocument).where((node) => node instanceof ReportML.Nodes.WellLog).selectFirst();
    if (wellLogNode != null) {
      const template = this._wellLogWidget.saveTemplate();
      const templateJSON = JSON.parse(template);
      wellLogNode.setDataSources(this._dataSources).setDepthLimits(this._wellLogWidget.getDepthLimits()).setVisibleDepthLimits(this._wellLogWidget.getDepthLimits()).setTemplate(templateJSON);
    }
    const tableNode = from(cgDomDocument).where((node) => node instanceof TableElement && node.getAttribute("type") === "datatable").selectFirst();
    if (tableNode != null) {
      this.preProcessTableDomElement(tableNode, printDecimatedDepths);
    }
    return cgDomDocument;
  }
  getPlot() {
    return this._plot;
  }
}
export function createScene(canvas, setZoomButton) {
  const app = new TableViewInteraction(setZoomButton);
  app.initialize(canvas);
  return app;
}

createScene(document.querySelector('[ref="plot"]'), this.setRubberBandSelectionButton, this.setZoomButton);

# WellLog Widget Initialization

The following code shows how to initialize WellLog Widget tools for interaction with TableView.

# TableView Widget Initialization

The following code shows how to initialize TableView tools for interaction with WellLog.

# Export To PDF

The following code shows how to export to PDF entire scene.

# Real-Time Interaction

Here's the example of real-time depth based WellLog Widget and TableView with autoscroll and synchronized data.

import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { LogData } from "@int/geotoolkit/welllog/data/LogData.ts";
import { LogCurve } from "@int/geotoolkit/welllog/LogCurve.ts";
import { TrackType as WellLogTrackType } from "@int/geotoolkit/welllog/TrackType.ts";
import { Events as SplitterEvents } from "@int/geotoolkit/welllog/widgets/tools/Splitter.ts";
import { Point } from "@int/geotoolkit/util/Point.ts";
import { Events as CrossHairEvents } from "@int/geotoolkit/controls/tools/CrossHair.ts";
import { Events as WellLogEvents } from "@int/geotoolkit/welllog/widgets/Events.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { CssLayout } from "@int/geotoolkit/layout/CssLayout.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { TableView } from "@int/geotoolkit/widgets/TableView.ts";
import { Events as HighlightEvents } from "@int/geotoolkit/controls/tools/tableview/Highlight.ts";
import { Events as ScrollEvents } from "@int/geotoolkit/controls/tools/scroll/AbstractScroll.ts";
import { Events as PanningEvents } from "@int/geotoolkit/controls/tools/Panning.ts";
import { Events as AbstractToolEvents } from "@int/geotoolkit/controls/tools/AbstractTool.ts";
import { ScrollToLocation } from "@int/geotoolkit/welllog/TrackContainer.ts";
import { HeaderType } from "@int/geotoolkit/welllog/header/LogAxisVisualHeader.ts";
import { obfuscate } from "@int/geotoolkit/lib.js";
import { DataProvider } from "@int/geotoolkit/controls/shapes/tableview/DataProvider.ts";
import { curveData } from "/src/code/WellLog/utils/curveData.ts";
import { createWellLogWidget } from "/src/code/WellLog/utils/common.ts";
const COLS = ["GR", "CALI", "SP"];
let startIndex = 0;
const maxCachedSize = 500;
const startDepth = 5800;
const stepDepth = 1;
const TIME_REFRESH = 500;
class RealTimeInteraction {
  constructor() {
    this._wellLogWidget = null;
    this._tableWidget = null;
    this.timer = null;
    this.caliData = new LogData("CALI");
    this.grData = new LogData("GR");
    this.spData = new LogData("SP");
    this.curveData = curveData["CALI"];
    this.depthAutoScroll = true;
    this.runData();
  }
  synchronizeTableData() {
    const depth = this.caliData.getDepths() || [];
    this._tableWidget.setData({
      "dataprovider": new (obfuscate(class extends DataProvider {
        getContentData(col, row) {
          return curveData[COLS[col]][row].toFixed(2);
        }
        getIndexData(row) {
          return (startDepth + row * stepDepth).toString();
        }
        getHeaderData(id) {
          return COLS[id];
        }
      }))(),
      "rows": depth.length
    });
    if (this.depthAutoScroll) {
      this._tableWidget.highlightRow(depth.length - 1);
    }
  }
  initializeWellLog() {
    const widget = createWellLogWidget().setAxisHeaderType(HeaderType.Simple).setDepthLimits(startDepth, startDepth + stepDepth);
    widget.addTrack(WellLogTrackType.IndexTrack).setWidth(80);
    widget.addTrack(WellLogTrackType.LinearTrack).addChild([
      this.createCurve(this.grData, KnownColors.Orange).setNormalizationLimits(0, 200),
      this.createCurve(this.caliData, KnownColors.Green).setNormalizationLimits(6, 12),
      this.createCurve(this.spData, KnownColors.Blue).setNormalizationLimits(-90, -10)
    ]);
    widget.on(WellLogEvents.VisibleDepthLimitsChanged, this.onLimitsChange.bind(this));
    widget.on(WellLogEvents.VisibleDepthLimitsChanged, () => {
      this.synchronizeTableData();
    });
    widget.getToolByName("cross-hair").on(CrossHairEvents.onPositionChanged, (evt, sender, event) => {
      const position = event.getPosition();
      if (isNaN(position.getY()) || isNaN(position.getX())) {
        return;
      }
      const depth = position.getY();
      const closest = Math.floor((depth - startDepth) / stepDepth);
      this._tableWidget.highlightRow(closest);
    });
    widget.getToolByName("TrackPlotVerticalScroll").on(ScrollEvents.onScroll, () => {
      const limits = widget.getVisibleDepthLimits();
      const top = (limits.getLow() - startDepth) / stepDepth;
      const bottom = top + this._tableWidget.getVisibleTableLimits().getHeight();
      this._tableWidget.setVisibleTableLimits(Math.ceil(top));
      this._tableWidget.setVisibleTableLimits(Math.floor(bottom));
    });
    widget.getToolByName("splitter").setEnabled(true).on(SplitterEvents.onCanResize, (evt, sender, event) => {
      event.reject();
    });
    return widget;
  }
  initializeTableWidget() {
    const tableWidget = new TableView({
      "horizontalscroll": "floating",
      "verticalscroll": "floating",
      "indexwidth": 50,
      "cols": COLS.length,
      "bounds": new Rect(300, 0, 665, 400)
    });
    const crosshair = this._wellLogWidget.getToolByName("cross-hair");
    tableWidget.getToolByName("TableViewHighlight").on(HighlightEvents.onHover, (evt, sender, eventArgs) => {
      const depth = startDepth + stepDepth * eventArgs.getRowNumber();
      crosshair.setPosition(new Point(0, depth));
    }).on(AbstractToolEvents.onLeave, () => {
      crosshair.setPosition(new Point(0, NaN));
    });
    const synchronizeLimits = () => {
      const limits = tableWidget.getVisibleTableLimits();
      const top = Math.floor(limits.getTop());
      const low = startDepth + stepDepth * top + stepDepth * (limits.getTop() - top);
      const high = low + this._wellLogWidget.getVisibleDepthLimits().getSize();
      this._wellLogWidget.setVisibleDepthLimits(low, high);
    };
    tableWidget.getToolByName("VerticalScroll").on(ScrollEvents.onScroll, synchronizeLimits);
    tableWidget.getToolByName("TableViewPanning").on(PanningEvents.onPanning, synchronizeLimits);
    return tableWidget;
  }
  onLimitsChange(type, src, args) {
    if (args["new"].getHigh() < this.caliData.getMaxDepth()) {
      this.depthAutoScroll = false;
    } else {
      this.depthAutoScroll = true;
    }
  }
  disableRubberBand() {
    this._wellLogWidget.getToolByName("rubberband").setEnabled(false);
  }
  zoomIn() {
    this._wellLogWidget.scale(5 / 4);
    this.disableRubberBand();
  }
  zoomOut() {
    this._wellLogWidget.scale(4 / 5);
    this.disableRubberBand();
  }
  fitToHeight() {
    this._wellLogWidget.fitToHeight();
    this.disableRubberBand();
  }
  rubberBandZoom() {
    this._wellLogWidget.getToolByName("rubberband").setEnabled(true);
  }
  createCurve(dataSource, color) {
    return new LogCurve(dataSource).setLineStyle({
      "color": color,
      "width": 2
    });
  }
  initialize(canvas) {
    this._plot = new Plot({
      "canvaselement": canvas,
      "root": new Group().setAutoModelLimitsMode(true).setLayout(new CssLayout()).addChild([
        this._wellLogWidget = this.initializeWellLog().setLayoutStyle({
          "left": 0,
          "top": 0,
          "width": 300,
          "bottom": 0
        }),
        this._tableWidget = this.initializeTableWidget()
      ])
    });
    this._wellLogWidget.setHeaderHeight("auto").setDepthScale(5);
    this.synchronizeTableData();
  }
  runData() {
    let index = 0;
    const updateData = () => {
      if (this.caliData != null) {
        if (index >= this.curveData.length) {
          this.caliData.clear();
          index = 0;
          startIndex = 0;
        }
        if (this.caliData.getSize() >= maxCachedSize) {
          startIndex = index - maxCachedSize / 2;
          this.caliData.trimValues(this.caliData.getMinDepth(), startDepth + startIndex * stepDepth);
        }
        const start = startDepth + startIndex * stepDepth;
        const depth = startDepth + index * stepDepth;
        const depthOffset = 5;
        this.caliData.addValue(depth, curveData["CALI"][index]);
        this.grData.addValue(depth, curveData["GR"][index]);
        this.spData.addValue(depth, curveData["SP"][index]);
        this._wellLogWidget.setDepthLimits(start, depth + depthOffset);
        if (this.depthAutoScroll) {
          this._wellLogWidget.scrollToIndex(depth + depthOffset, ScrollToLocation.BOTTOM, false);
        }
        this.synchronizeTableData();
        index++;
      }
    };
    this.timer = window.setInterval(updateData, TIME_REFRESH);
  }
  getPlot() {
    return this._plot;
  }
  dispose() {
    if (this.timer) {
      clearInterval(this.timer);
    }
    if (this._plot) {
      this._plot.dispose();
    }
  }
}
function createScene(canvas) {
  const app = new RealTimeInteraction();
  app.initialize(canvas);
  return app;
}
export { createScene };

createScene(document.querySelector('[ref="plot"]'));