@equinor/videx-wellog
Version:
Visualisation components for wellbore log data
1 lines • 94.5 kB
JavaScript
"use strict";var d3Selection=require("d3-selection"),d3Zoom=require("d3-zoom"),d3Scale=require("d3-scale"),d3Shape=require("d3-shape"),videxMath=require("@equinor/videx-math"),d3Color=require("d3-color");class ScaleHelper{static getPixelRatio(scale){const domain=scale.domain(),dmin=domain[0],dmax=domain[domain.length-1],range=scale.range(),rmin=range[0],rmax=range[range.length-1],deltaDomain=Math.abs(dmax-dmin);return Math.abs(rmax-rmin)/deltaDomain}static getDomainRatio(scale){const domain=scale.domain(),dmin=domain[0],dmax=domain[domain.length-1],range=scale.range(),rmin=range[0],rmax=range[range.length-1];return Math.abs(dmax-dmin)/Math.abs(rmax-rmin)}static getDomainSpan(scale,absoluteValue=!0){const domain=scale.domain(),d1=domain[0],span=domain[domain.length-1]-d1;return absoluteValue?Math.abs(span):span}static getDomainPixelSpan(scale,domain){const theDomain=domain||scale.domain(),d1=theDomain[0],d2=theDomain[theDomain.length-1],y1=scale(d1),y2=scale(d2);return Math.max(0,Math.abs(y2-y1))}static getRangeSpan(scale){const range=scale.range(),r0=range[0],r1=range[range.length-1];return Math.abs(r1-r0)}static createLogTicks(scale){const[,xmax]=scale.domain(),guides=[],ticks={major:[],minor:[]};let curr=1;for(;curr<xmax;)guides.push(curr),curr*=10;return scale.ticks().splice(1).forEach((t=>{guides.includes(t)?ticks.major.push(t):ticks.minor.push(t)})),ticks}static createLinearTicks(scale,num=10){const hTicks=num,hStep=ScaleHelper.getRangeSpan(scale)/hTicks,center=hTicks/2,ticks={major:[],minor:[]},rangeStart=Math.min(...scale.range());for(let i=1;i<hTicks;i+=1){const x=scale.invert(i*hStep+rangeStart);center&&i%center==0?ticks.major.push(x):ticks.minor.push(x)}return ticks}static createMajorTicks(scale){const ticks={major:[],minor:[]};return scale.ticks().forEach((t=>{ticks.major.push(t)})),ticks}static createMinorTicks(v,steps,stepSize){const res=[];for(let i=1;i<steps;i+=1){const mv=v+i*stepSize;res.push(mv)}return res}static createTicks(scale){const domain=scale.domain(),dmin=domain[0],dmax=domain[domain.length-1],height=ScaleHelper.getRangeSpan(scale),major=[],minor=[];if(height>0){const nTicks=Math.min(Math.ceil(height/60),2*Math.floor(Math.abs(dmax-dmin)));major.push(...scale.ticks(nTicks));const tickHeight=height/major.length,majorSize=major.length>1?major[1]-major[0]:major[0]||0;let numMinor=majorSize<=1?10*majorSize:Math.min(10,majorSize);if(numMinor){tickHeight<10*numMinor&&(numMinor=5*Math.round(tickHeight/10/5));const minorSize=majorSize/numMinor;minor.push(...ScaleHelper.createMinorTicks(major[0]-majorSize,numMinor,minorSize)),major.forEach((tick=>{minor.push(...ScaleHelper.createMinorTicks(tick,numMinor,minorSize))}))}}return{major:major,minor:minor.filter((t=>t>=dmin&&t<=dmax))}}}class DataHelper{static mean(segment){if(segment.length<=2)return segment;let avgV=0,avgD=0;for(let i=0;i<segment.length;i++)avgD+=segment[i][0],avgV+=segment[i][1];return[[avgD/segment.length,avgV/segment.length]]}static minmax(segment){if(segment.length<=2)return segment;let min=segment[0][1],max=-1/0,minLast=!1;for(let i=0;i<segment.length;i++)segment[i][1]>max?(max=segment[i][1],minLast=!1):segment[i][1]<min&&(min=segment[i][1],minLast=!0);return[[segment[0][0],minLast?max:min],[segment[segment.length-1][0],minLast?min:max]]}static isWithinBounds(scale,datapoints){if(!datapoints||datapoints.length<1)return!1;const[min,max]=scale.domain(),first=datapoints[0],last=datapoints[datapoints.length-1];return!(first[0]>max||last[0]<min)}static trimUndefinedValues(datapoints){let prev=0;return null==datapoints?void 0:datapoints.filter((pt=>{let include=!0;return null===pt[1]&&null===prev&&(include=!1),prev=pt[1],include}))}static filterData(datapoints,domain,overlapFactor=.5){const[d0,d1]=domain,overlap=overlapFactor*(d1-d0),dmin=d0-overlap,dmax=d1+overlap;return null==datapoints?void 0:datapoints.filter(((pt,i)=>{if(pt[0]>=dmin&&pt[0]<=dmax)return!0;const crossingMin=pt[0]<dmin&&i+1<datapoints.length&&datapoints[i+1][0]>dmin,crossingMax=pt[0]>dmax&&i-1>=0&&datapoints[i-1][0]<dmax;return crossingMin||crossingMax}))}static findNextDefined(data,start=0){for(let i=start;i<data.length;i++)if(Number.isFinite(data[i][1]))return i;return-1}static findNextUndefined(data,start=0){for(let i=start;i<data.length;i++)if(!Number.isFinite(data[i][1]))return i;return-1}static resample(datapoints,ratio,reducer=DataHelper.mean){if(ratio<=2||datapoints.length<100)return datapoints;ratio=Math.floor(ratio);const lastIndex=datapoints.length-1,first=datapoints[0],last=datapoints[lastIndex];let l=1;const reduced=[];let _safe=0;for(;l<lastIndex;){let r=Math.min(l+ratio,lastIndex);const segment=datapoints.slice(l,r),undefinedIdx=DataHelper.findNextUndefined(segment);if(undefinedIdx>-1){r=l+1;const undefinedEntry=segment[undefinedIdx],trailingEntry=segment[undefinedIdx+1];segment.splice(undefinedIdx),undefinedIdx>0&&(segment.pop(),r++),segment.length>0&&(reduced.push(...reducer(segment)),r+=segment.length),reduced.push(undefinedEntry),trailingEntry&&(reduced.push(trailingEntry),r++)}else segment.length>0&&reduced.push(...reducer(segment));if(l=r,_safe++,_safe>1e6)throw Error("Infinite loop terminated!")}return[first,...reduced,last]}static downsample(datapoints,scale,reducer=DataHelper.minmax){if(datapoints.length<10)return datapoints;const ratio=ScaleHelper.getDomainRatio(scale),lastIndex=datapoints.length-1,firstIndex=DataHelper.findNextDefined(datapoints),first=datapoints[firstIndex],last=datapoints[lastIndex],reduced=[];reduced.push(first);let l=firstIndex+1,r=l+1;for(;r<=lastIndex;){const rp=datapoints[r],isdef=Number.isFinite(rp[1]);if(!isdef||rp[0]-datapoints[l][0]>ratio||r===lastIndex){const segment=r-l>0?datapoints.slice(l,r):[];if(1===segment.length?reduced.push(segment[0]):0!==segment.length&&reduced.push(...reducer(segment)),isdef?l=r:(reduced.push(rp),l=DataHelper.findNextDefined(datapoints,r+1)),-1===l)break;r=l+1}else r++}return reduced.push(last),reduced}static mergeDataSeries(arr1,arr2){const res=[];let n=0;if(arr1.length>0&&arr2.length>0){let md=Math.min(arr1[0][0],arr2[0][0]),i=0,j=0;if(arr1[0][0]<arr2[0][0]&&arr1[arr1.length-1][0]<arr2[0][0])return[];if(arr2[0][0]<arr1[0][0]&&arr2[arr2.length-1][0]<arr1[0][0])return[];for(;i<arr1.length&&j<arr2.length;){md=arr1[i][0];let rv=0,rn=0;for(;arr2[j][0]>md&&i<arr1.length;)j>0?j--:(res[n++]=[md,arr1[i][1],null],md=arr1[++i][0]);for(;j<arr2.length&&arr2[j][0]<=md;){if(null===arr2[j][1]){res[n++]=[arr2[j][0],arr1[i][1],arr2[j][1]],j++;break}rv+=arr2[j][1],rn++,j++}rn>0&&(res[n++]=[md,arr1[i][1],rv/rn]),i++}for(;i<arr1.length;)res[n++]=[arr1[i][0],arr1[i][1],null],i++;for(;j<arr2.length;)res[n++]=[arr2[j][0],null,arr2[j][1]],j++;return res}return arr1.length>0?arr1.map((d=>[d[0],d[1],null])):arr2.length>0?arr2.map((d=>[d[0],null,d[1]])):[]}static queryContinuousData(queryVal,data){try{if(queryVal<data[0][0]||queryVal>data[data.length-1][0])return null;let idx=data.findIndex((d=>queryVal<=d[0]));if(idx>0){const qFound=data[idx][0],qBefore=data[idx-1][0];queryVal-qBefore<qFound-queryVal&&(idx-=1)}if(-1!==idx)return data[idx][1]}catch(err){console.error(err)}return null}static queryPointData(queryVal,data,threshold=0){try{const bestMatch=data.filter((d=>queryVal-threshold<=d[0]&&d[0]<=queryVal+threshold)).reduce(((match,d)=>{const rank=Math.abs(queryVal-d[0]);return null===match.rank||rank<match.rank?{data:d,rank:rank}:match}),{data:null,rank:null}).data;if(null!==bestMatch)return bestMatch[1]}catch(err){console.error(err)}return null}static queryZoneData(queryVal,data){try{const index=data.findIndex(((d,i,arr)=>i<arr.length-1&&null!==d[1]&&d[0]<=queryVal&&arr[i+1][0]>queryVal));return index>-1?data[index][1]:null}catch(err){console.error(err)}return null}}function applyMajor(ctx,lineScale=1){ctx.strokeStyle="#ccc",ctx.lineWidth=2*lineScale}function applyMinor(ctx,lineScale=1){ctx.strokeStyle="#ddd",ctx.lineWidth=1*lineScale}class GridHelper{static drawGrid(ctx,xscale,xticks,yscale,yticks){const xScaleRange=xscale.range(),yScaleRange=yscale.range();function drawVerticalTick(tick){const x=xscale(tick);ctx.beginPath();const[first,...rest]=yScaleRange;ctx.moveTo(x,first),rest.forEach((y=>ctx.lineTo(x,y))),ctx.stroke()}function drawHorizontalTick(tick){const y=yscale(tick);ctx.beginPath();const[first,...rest]=xScaleRange;ctx.moveTo(first,y),rest.forEach((x=>ctx.lineTo(x,y))),ctx.stroke()}ctx.save(),applyMinor(ctx,.5),xticks.minor.forEach(drawVerticalTick),applyMajor(ctx,.5),xticks.major.forEach(drawVerticalTick),applyMinor(ctx),yticks.minor.forEach(drawHorizontalTick),applyMajor(ctx),yticks.major.forEach(drawHorizontalTick),ctx.restore()}}class LegendHelper{static basicLegendSvgConfig(trackRowsFunc,updateFunc){return{elementType:"svg",getLegendRows:track=>trackRowsFunc(track),onInit:(elm,track,updateTrigger)=>{track.legendUpdate=updateTrigger;const lg=d3Selection.select(elm);lg.selectAll("*").remove(),lg.append("g").attr("class","svg-legend")},onUpdate:updateFunc}}static renderBasicVerticalSvgLabel(g,bounds,label,abbr,horizontal=!1){const{width:width,height:height,left:left=0,top:top=0}=bounds,y=top+.9*height,x=left+Math.max(0,width/2),textSize=Math.min(12,Math.min(width,40)/3),transform=horizontal?`translate(${y},${x})`:`translate(${x},${y})rotate(90)`,lbl=g.append("text").attr("transform",transform).attr("font-size",`${textSize}px`).attr("dominant-baseline","middle").style("text-anchor","end");lbl.text(label);return lbl.node().getBBox().width>.8*height&&lbl.text(abbr||label),lbl}static basicVerticalLabel(label,abbr){return LegendHelper.basicLegendSvgConfig((()=>3),((elm,bounds,track)=>{const g=d3Selection.select(elm);g.selectAll("*").remove(),LegendHelper.renderBasicVerticalSvgLabel(g,bounds,label||track.options.label,abbr||track.options.abbr,track.options.horizontal)}))}static basicVerticalLinkLabel({label:label,abbr:abbr,onClick:onClick,title:title=null}){return LegendHelper.basicLegendSvgConfig((()=>3),((elm,bounds,track)=>{const g=d3Selection.select(elm);g.selectAll("*").remove();const labelGroup=g.append("g").style("fill","#0000EE").style("text-decoration","underline").style("cursor","pointer");labelGroup.append("title").text(title);const labelElement=LegendHelper.renderBasicVerticalSvgLabel(labelGroup,bounds,label||track.options.label,abbr||track.options.abbr,track.options.horizontal);onClick&&"function"==typeof onClick&&labelElement.on("click",onClick)}))}}function round(v,decimals){const f=10**decimals;return Math.round(v*f)/f}function hashString(str){let hash=0;if(0===str.length)return hash;for(let i=0;i<str.length;i++){hash=(hash<<5)-hash+str.charCodeAt(i),hash&=hash}return hash}function debouncer(debounceInterval=20){const _debounces={};return function(func,...args){const funcName=hashString(func.name||func.toString()).toString();_debounces[funcName]&&clearTimeout(_debounces[funcName]),_debounces[funcName]=setTimeout((()=>{delete _debounces[funcName],func(...args)}),debounceInterval)}}function getContrastYIQ(rgbString){const rgb=rgbString.replace(/[^\d,.]/g,"").split(",");return(299*parseFloat(rgb[0])+587*parseFloat(rgb[1])+114*parseFloat(rgb[2]))/1e3>=110?"black":"white"}function setAttrs(selection,attrs){return"function"==typeof attrs?(selection.each((function(d){const obj=attrs(d);Object.entries(obj).forEach((a=>{this.setAttribute(a[0],a[1])}))})),selection):(Object.entries(attrs).forEach((a=>{selection.attr(a[0],a[1])})),selection)}function setStyles(selection,styles){return"function"==typeof styles?(selection.each((function(d){const obj=styles(d);Object.entries(obj).forEach((s=>{this.style.setProperty(s[0],s[1])}))})),selection):(Object.entries(styles).forEach((s=>{selection.style(s[0],s[1])})),selection)}function setProps(selection,props){return props.attrs&&setAttrs(selection,props.attrs),props.styles&&setStyles(selection,props.styles),selection}class BasicScaleHandler{constructor(baseDomain=[0,100]){this._baseDomain=baseDomain,this.scale=d3Scale.scaleLinear().domain(baseDomain).range([0,100]),this.rescale=this.rescale.bind(this),this.ticks=this.ticks.bind(this)}rescale(transform,axis="y"){const transScale=this.scale.copy().domain(this.baseDomain()),range=transScale.range().map("x"===axis?transform.invertX:transform.invertY,transform);if(range[0]===range[1])return this;const domain=range.map(transScale.invert,transScale);return this.scale.domain(domain),this}ticks(){return ScaleHelper.createTicks(this.scale)}baseDomain(newDomain){return newDomain?(this._baseDomain=newDomain,this.scale.domain(newDomain),this):this._baseDomain}range(newRange){return newRange?(this.scale.range(newRange),this):this.scale.range()}get dataScale(){return this.scale}}const noInterpolator={forward:v=>v,reverse:v=>v,forwardInterpolatedDomain:domain=>domain,reverseInterpolatedDomain:domain=>domain};const defaultOptions$3={domain:[0,1e3],showTitles:!0,showLegend:!0,autoResize:!0,horizontal:!1,transitionDuration:0};class LogController{constructor(options={}){this._deferredUpdate=null,this.options=Object.assign(Object.assign({},defaultOptions$3),options),this.tracks=[],this.container=null,this.zoom=null,this.width=0,this.height=0,this.legendRows=0,this.debounce=debouncer(),this.legends={},this._scaleHandler=this.options.scaleHandler||new BasicScaleHandler,this._scaleHandler.baseDomain(this.options.domain),this.init=this.init.bind(this),this.onMount=this.onMount.bind(this),this.onUnmount=this.onUnmount.bind(this),this.rescale=this.rescale.bind(this),this.refresh=this.refresh.bind(this),this.zoomTo=this.zoomTo.bind(this),this.zoomed=this.zoomed.bind(this),this.adjustZoomTransform=this.adjustZoomTransform.bind(this),this.addTrack=this.addTrack.bind(this),this.setTracks=this.setTracks.bind(this),this.removeTrack=this.removeTrack.bind(this),this.adjustToSize=this.adjustToSize.bind(this),this.updateTracks=this.updateTracks.bind(this),this.postUpdateTracks=this.postUpdateTracks.bind(this),this.updateLegendRows=this.updateLegendRows.bind(this),this.updateLegend=this.updateLegend.bind(this),this.processLegendConfig=this.processLegendConfig.bind(this),this.setup=this.setup.bind(this),this._trackExit=this._trackExit.bind(this),this._trackEnter=this._trackEnter.bind(this),this._trackUpdate=this._trackUpdate.bind(this)}static basic(showTitles=!0){return new LogController({showTitles:showTitles,showLegend:!1})}init(element){return this.onMount(element),this}onMount(element){this.setup(element),this.zoom=d3Zoom.zoom().on("zoom",this.zoomed),this.adjustToSize(),this._initialized=!0}onUnmount(){this._observer&&this._observer.disconnect()}setTracks(...tracks){return this.tracks=1===tracks.length&&Array.isArray(tracks[0])?tracks[0]:tracks,this.tracks.sort(((a,b)=>a.order-b.order)),this.options.showLegend&&this.updateLegendRows(),this._initialized&&this.updateTracks(),this}addTrack(track){return this.tracks.push(track),this.tracks.sort(((a,b)=>a.order-b.order)),this.options.showLegend&&track.options.legendConfig&&track.options.legendConfig.getLegendRows(track)>this.legendRows&&this.updateLegendRows(),this._initialized&&this.updateTracks(),this}removeTrack(track){const idx=this.tracks.findIndex((d=>d.id===track.id));return idx>=0&&(this.options.showLegend&&this.legends[track.id]&&delete this.legends[track.id],this.tracks.splice(idx,1),this.options.showLegend&&track.options.legendConfig&&track.options.legendConfig.getLegendRows(track)>=this.legendRows&&this.updateLegendRows(),this._initialized&&this.updateTracks()),this}adjustToSize(force=!1){const{container:container,width:oldWidth,height:oldHeight,options:{showLegend:showLegend,showTitles:showTitles},innerBounds:oldBounds,_trackHeight:oldTrackHeight}=this,{width:width,height:height}=container.node().getBoundingClientRect();if(this.width=width,this.height=height,!force&&oldWidth===this.width&&oldHeight===this.height)return;const bounds=this.innerBounds;this._uiScale=Math.max(Math.min(1,this.width/800),.7),this._titleHeight=showTitles?18*this._uiScale:0,this._titleFontSize=showTitles?.7*this._titleHeight:0,this._legendHeight=showLegend?25*this.legendRows*this._uiScale:0,this._trackHeight=bounds.length-this._titleHeight-this._legendHeight,this._trackHeight<=0||bounds.length<=0||(this._trackHeight!==oldTrackHeight&&(this.scale.range([0,this._trackHeight]),this.adjustZoomTransform(),this.rescale()),bounds.span!==oldBounds.span&&showTitles&&this.adjustTrackTitles(),bounds.span===oldBounds.span&&this._trackHeight===oldTrackHeight||this.updateTracks())}zoomTo(domain,duration=0,callback){const[d1,d2]=domain;if(d1===d2)return this;const current=d3Zoom.zoomTransform(this.zoomHandler.node()),[b1,b2]=this.scaleHandler.baseDomain(),k=Math.abs(b2-b1)/Math.abs(d2-d1),cp=this.options.horizontal?current.x:current.y,p=(this.scale(d1)-cp)/current.k*k;let transform;transform=this.options.horizontal?d3Zoom.zoomIdentity.translate(-p,0).scale(k):d3Zoom.zoomIdentity.translate(0,-p).scale(k);const zoomHandler=this.zoomHandler;return zoomHandler.interrupt(),Number.isFinite(duration)&&duration>0?this.zoom.transform(zoomHandler.transition().duration(duration).on("end",callback),transform):(this.zoom.transform(zoomHandler,transform),callback&&callback()),this}rescale(){const transform=d3Zoom.zoomTransform(this.zoomHandler.node()),scale=this.scaleHandler.dataScale;this.tracks.forEach((track=>{track.isMounted&&requestAnimationFrame((()=>{track.onRescale({scale:scale,transform:transform})}))}))}refresh(){this.updateTracks(),this.rescale()}updateTracks(){const{container:container,tracks:tracks,_trackEnter:trackEnter,_trackUpdate:trackUpdate,_trackExit:trackExit}=this,selection=container.selectAll(".track").data(tracks,(t=>t.id)),exit=selection.exit(),enter=selection.enter();return exit.call(trackExit),enter.call(trackEnter),selection.call(trackUpdate),exit.empty()&&enter.empty()&&this.postUpdateTracks(),this}reset(){return this.container.selectAll(".track").remove(),this.setTracks([])}setup(element){this.options.autoResize&&(this._observer=new ResizeObserver((()=>{this.debounce((()=>this.adjustToSize()))})),requestAnimationFrame((()=>this._observer.observe(element))));const container=d3Selection.select(element).classed("log-controller",!0).classed("horizontal",this.options.horizontal);this.container=container}processLegendConfig(track,element){const{legendConfig:legendConfig}=track.options;if(!legendConfig)return;let legendElement=element;if("svg"===legendConfig.elementType){const legendSelection=d3Selection.select(element).append("svg");setProps(legendSelection,{attrs:{width:"100%",height:"100%"},styles:{flex:"1 1 auto"}}),legendElement=legendSelection.node()}legendConfig.onInit&&legendConfig.onInit(legendElement,track,(()=>this.updateLegend(track.id))),this.legends[track.id]={elm:legendElement,track:track}}zoomed(event){const{transform:transform}=event;this.options.horizontal?this.scaleHandler.rescale(transform,"x"):this.scaleHandler.rescale(transform,"y"),this.rescale()}adjustZoomTransform(){const{zoomHandler:zoomHandler,scaleHandler:scaleHandler,options:{horizontal:horizontal}}=this,[d1,d2]=scaleHandler.baseDomain();if(d1===d2)return;const p1=scaleHandler.scale(d1),p2=scaleHandler.scale(d2),[r1,r2]=scaleHandler.scale.range(),dist=r2-r1;let transform;transform=horizontal?d3Zoom.zoomIdentity.translate(p1,0).scale((p2-p1)/dist):d3Zoom.zoomIdentity.translate(0,p1).scale((p2-p1)/dist),this.zoom.transform(zoomHandler,transform)}updateLegendRows(){const{tracks:tracks}=this,maxRows=tracks.reduce(((rows,track)=>{if(track.options.legendConfig){const tr=+track.options.legendConfig.getLegendRows(track);if(Number.isFinite(tr))return Math.max(rows,tr)}return rows}),0);maxRows!==this.legendRows&&(this.legendRows=maxRows,this.adjustToSize(!0))}updateLegend(id){if(this.options.showLegend&&this.legends&&this.legends[id]){const{legends:legends,_uiScale:uiScale,_legendHeight:legendHeight}=this,{elm:elm,track:track}=legends[id],{legendConfig:legendConfig,horizontal:horizontal}=track.options,height=25*(legendConfig.getLegendRows(track)||0)*uiScale,bbox=elm.getBoundingClientRect(),bounds={left:0,height:height,top:legendHeight-height,width:horizontal?bbox.height:bbox.width};legendConfig.onUpdate&&legendConfig.onUpdate(elm,bounds,track)}}adjustTrackTitles(){const{horizontal:horizontal}=this.options,isTooSmall=horizontal?element=>element.clientHeight<element.scrollHeight:element=>element.clientWidth<element.scrollWidth;this.container.selectAll(".track-title").text((d=>d.options.label||d.id)).each((function(d){isTooSmall(this)&&d.options.abbr&&d3Selection.select(this).text(d.options.abbr)}))}postUpdateTracks(){const{container:container,scaleHandler:scaleHandler,updateLegend:updateLegend,options:{showLegend:showLegend,showTitles:showTitles}}=this;0!==container.node().clientWidth&&(this.tracks&&this.tracks.some((d=>{var _a;return(null===(_a=null==d?void 0:d.elm)||void 0===_a?void 0:_a.clientWidth)>0}))?(this.tracks.forEach((track=>{const elm=track.elm;if(track.onUpdate){if(!track.isMounted)return;track.onUpdate({elm:elm,scale:scaleHandler.dataScale}),track.options.legendConfig&&showLegend&&updateLegend(track.id)}})),showTitles&&this.adjustTrackTitles()):(this._deferredUpdate&&clearTimeout(this._deferredUpdate),this._deferredUpdate=setTimeout((()=>{this._deferredUpdate=null,this.postUpdateTracks()}),200)))}_trackExit(selection){selection.each((d=>d.onUnmount&&d.onUnmount())),selection.interrupt().transition().duration(this.options.transitionDuration).style("flex","0 0 0%").end().catch((()=>({}))).finally((()=>{selection.remove(),this.postUpdateTracks()})),this.options.onTrackExit&&this.options.onTrackExit()}_trackEnter(selection){const{scaleHandler:scaleHandler,processLegendConfig:processLegendConfig,_titleHeight:titleHeight,_legendHeight:legendHeight,_titleFontSize:fontSize,options:{showTitles:showTitles,showLegend:showLegend,horizontal:horizontal}}=this,attr=horizontal?{maxSpan:"max-height",size:"width"}:{maxSpan:"max-width",size:"height"},newtracks=selection.append("div").attr("class","track");if(setStyles(newtracks,{flex:"0 0 0%",[attr.maxSpan]:d=>d.options.maxWidth?`${d.options.maxWidth}px`:null}),showTitles){setProps(newtracks.append("div"),{attrs:d=>({class:"track-title",title:d.options.tooltip||d.options.label||d.id}),styles:{[attr.size]:`${titleHeight}px`,"font-size":`${fontSize}px`}})}showLegend&&newtracks.append("div").classed("track-legend",!0).classed("hidden",legendHeight<=0).style(attr.size,`${legendHeight}px`),newtracks.append("div").attr("class","track-container");const self=this;newtracks.each((function(d){self.options.onTrackEnter&&self.options.onTrackEnter(this,d);const ev={elm:this.querySelector(".track-container"),scale:scaleHandler.dataScale,scaleHandler:scaleHandler};d.options.horizontal=horizontal,d.onMount&&d.onMount(ev),d.options.legendConfig&&processLegendConfig(d,this.querySelector(".track-legend"))})),newtracks.interrupt().transition().duration(this.options.transitionDuration).style("flex",(d=>`${d.options.width}`)).end().catch((()=>({}))).finally((()=>this.postUpdateTracks()))}_trackUpdate(selection){const{_titleHeight:titleHeight,_legendHeight:legendHeight,_titleFontSize:fontSize,options:{showTitles:showTitles,showLegend:showLegend,horizontal:horizontal}}=this,sizeAttr=horizontal?"width":"height";if(selection.interrupt().style("flex",(d=>`${d.options.width}`)),showTitles&&setStyles(selection.select(".track-title"),{[sizeAttr]:`${titleHeight}px`,"font-size":`${fontSize}px`}),showLegend){selection.select(".track-legend").style(sizeAttr,`${legendHeight}px`).classed("hidden",legendHeight<=0)}}get zoomHandler(){return this.container}get innerBounds(){return this.options.horizontal?{length:this.width,span:this.height}:{length:this.height,span:this.width}}get domain(){return this.scale.domain()}set domain(value){value&&Array.isArray(value)&&(this.scaleHandler.baseDomain(value),this.rescale())}get scaleHandler(){return this._scaleHandler}set scaleHandler(newHandler){newHandler.range(this._scaleHandler.range()),this._scaleHandler=newHandler}get scale(){return this.scaleHandler.scale}}class LogViewer extends LogController{constructor(options={}){super(options)}static basic(showTitles=!0){return new LogViewer({showTitles:showTitles,showLegend:!1})}onMount(element){super.setup(element);const overlay=function(caller,container){const overlay={elm:container.append("div").classed("overlay",!0),elements:{},listeners:{},enabled:!0},source=overlay.elm.node();return overlay.elm.on("click",(function(event){if(!overlay.enabled)return;const[mx,my]=d3Selection.pointer(event,this);Object.keys(overlay.listeners).forEach((key=>{const target=overlay.elements[key]||null,ops=overlay.listeners[key];ops&&ops.onClick&&requestAnimationFrame((()=>ops.onClick({x:mx,y:my,target:target,source:source,caller:caller})))}))})),overlay.elm.on("mousemove",(function(event){if(!overlay.enabled)return;const[mx,my]=d3Selection.pointer(event,this);Object.keys(overlay.listeners).forEach((key=>{const target=overlay.elements[key]||null,ops=overlay.listeners[key];ops&&ops.onMouseMove&&requestAnimationFrame((()=>ops.onMouseMove({x:mx,y:my,target:target,source:source,caller:caller})))}))})),overlay.elm.on("mouseout",(()=>{overlay.enabled&&Object.keys(overlay.listeners).forEach((key=>{const target=overlay.elements[key]||null,ops=overlay.listeners[key];ops&&ops.onMouseExit&&requestAnimationFrame((()=>ops.onMouseExit({target:target,source:source,caller:caller})))}))})),overlay.elm.on("resize",(event=>{const{top:top,left:left,width:width,height:height}=event.detail;setStyles(overlay.elm,{width:`${width}px`,height:`${height}px`,left:`${left}px`,top:`${top}px`}),overlay.enabled&&Object.keys(overlay.listeners).forEach((key=>{const target=overlay.elements[key]||null,ops=overlay.listeners[key];ops&&ops.onResize&&requestAnimationFrame((()=>ops.onResize({target:target,source:source,caller:caller,width:width,height:height})))}))})),overlay.elm.on("rescale",(event=>{if(!overlay.enabled)return;const{transform:transform}=event.detail;Object.keys(overlay.listeners).forEach((key=>{const target=overlay.elements[key]||null,ops=overlay.listeners[key];ops&&ops.onRescale&&requestAnimationFrame((()=>ops.onRescale({target:target,source:source,caller:caller,transform:transform})))}))})),Object.assign(Object.assign({},overlay),{create:function(key,callbacks){const newElm=overlay.elm.append("div").style("position","relative").style("pointer-events","none").node();return overlay.elements[key]=newElm,callbacks&&(overlay.listeners[key]=callbacks),newElm},register:function(key,callbacks){overlay.listeners[key]=callbacks},remove:function(key){const el=overlay.elements[key];el&&(d3Selection.select(el).remove(),delete overlay.elements[key]),delete overlay.listeners[key]}})}(this,this.container);this.zoom=d3Zoom.zoom().scaleExtent([1,this.options.maxZoom||256]).on("zoom",this.zoomed),overlay.elm.call(this.zoom);const wheelZoomFunc=overlay.elm.on("wheel.zoom").bind(overlay.elm.node()),dblClickZoomFunc=overlay.elm.on("dblclick.zoom").bind(overlay.elm.node());overlay.elm.on("dblclick.zoom",(event=>{this.overlay.enabled&&dblClickZoomFunc(event)})),overlay.elm.on("wheel.zoom",(event=>{if(this.overlay.enabled)if(event.ctrlKey||event.shiftKey){const scaleMod=d3Zoom.zoomTransform(overlay.elm.node()).k/3,transitionAmount=event.wheelDeltaY/50/scaleMod;this.options.horizontal?this.zoom.translateBy(overlay.elm,transitionAmount,0):this.zoom.translateBy(overlay.elm,0,transitionAmount)}else wheelZoomFunc(event);event.preventDefault()})),this.overlay=overlay,this.adjustToSize(),this._initialized=!0}adjustToSize(force=!1){super.adjustToSize(force);const{container:container,innerBounds:innerBounds,width:width,height:height,overlay:overlay,_trackHeight:_trackHeight,options:{horizontal:horizontal,onResize:onResize}}=this,overlaySize={top:0,left:0,width:0,height:0};horizontal?(overlaySize.left=innerBounds.length-_trackHeight+1,overlaySize.width=_trackHeight,overlaySize.height=innerBounds.span):(overlaySize.top=innerBounds.length-_trackHeight+1,overlaySize.height=_trackHeight,overlaySize.width=innerBounds.span),overlay.elm.dispatch("resize",{detail:overlaySize}),onResize&&onResize({elm:container.node(),width:width,height:height,trackHeight:_trackHeight,source:this})}rescale(){super.rescale();const transform=d3Zoom.zoomTransform(this.zoomHandler.node());this.overlay.elm.dispatch("rescale",{detail:{transform:transform}})}zoomed(event){const{transform:transform,sourceEvent:sourceEvent}=event;if(!this.overlay.enabled&&null!==sourceEvent)return;const{k:k}=transform,panExcess=this.options.panExcess||[0,0];this.options.horizontal?(this.scaleHandler.rescale(transform,"x"),this.zoom.translateExtent([[-Math.abs(panExcess[0]/k),0],[this._trackHeight+Math.abs(panExcess[1]/k),0]])):(this.scaleHandler.rescale(transform,"y"),this.zoom.translateExtent([[0,-Math.abs(panExcess[0]/k)],[0,this._trackHeight+Math.abs(panExcess[1]/k)]])),this.rescale()}get zoomHandler(){return this.overlay.elm}}const defaults={width:3,maxWidth:null,horizontal:!1};class Track{constructor(id,options={}){this.options=Object.assign(Object.assign({},defaults),options),this.id=void 0===id?Math.round(Math.random()*Date.now()):id,this.elm=null,this.order=0,this.loader=options.loader||null,this._isLoading=!1,this._data=null,this.transform={k:1,y:0,x:0},this.onMount=this.onMount.bind(this),this.onUnmount=this.onUnmount.bind(this),this.onRescale=this.onRescale.bind(this),this.onUpdate=this.onUpdate.bind(this),this.refresh=this.refresh.bind(this),this.loadData=this.loadData.bind(this),this.onError=this.onError.bind(this)}init(elm,scale){this.onMount({elm:elm,scale:scale}),this.onUpdate({elm:elm,scale:scale})}onMount(trackEvent){const{elm:elm,scale:scale}=trackEvent;this.elm=elm,this.scale=scale,this.options.onMount&&this.options.onMount(trackEvent,this),this._mounted=!0}onUnmount(trackEvent={}){this.options.onUnmount&&this.options.onUnmount(trackEvent,this),d3Selection.select(this.elm).selectAll("*").remove()}onUpdate(trackEvent){this._mounted&&(this.scale=trackEvent.scale,this.options.onUpdate&&this.options.onUpdate(trackEvent,this))}onRescale(trackEvent){const{domain:domain,scale:scale,transform:transform}=trackEvent;if(this._mounted){if(scale)this.scale=scale;else{if(!domain)throw Error("Event is missing updated scale or domain!");this.scale.domain(domain)}this.transform=transform,this.options.onRescale&&this.options.onRescale(trackEvent,this)}}onError(error){this._mounted=!1,this.isLoading=!1,this.error=error,this.elm&&d3Selection.select(this.elm).classed("error",!0).selectAll("*").remove(),this.options.onError&&this.options.onError(error,this)}onDataLoaded(data){this.isLoading&&(this.isLoading=!1)}loadData(data,showLoader=!0){showLoader&&(this.isLoading=!0);const onSuccess=d=>{this._data=d,this.isLoading&&(this.isLoading=!1),this.onDataLoaded&&this.onDataLoaded(d),this.legendUpdate&&this.legendUpdate()},res=data();res.then?res.then(onSuccess,this.onError):"object"==typeof res?onSuccess(res):this.onError("Invalid data")}clearData(){this._data=null,this.onDataLoaded(null)}refresh(){const{scale:scale}=this;scale&&this.onUpdate({elm:this.elm,scale:scale})}get isLoading(){return this._isLoading}set isLoading(val){this._isLoading=!!val,this.loader&&d3Selection.select(this.loader).classed("hidden",!this._isLoading),d3Selection.select(this.elm).classed("hidden",this._isLoading),this.refresh()}get isMounted(){return this._mounted}get data(){return this._data}set data(data){"function"==typeof data?this.loadData(data,!1):(this._data=data,this.legendUpdate&&this.legendUpdate(),this.onDataLoaded&&this.onDataLoaded(data))}}class SvgTrack extends Track{onMount(trackEvent){super.onMount(trackEvent),this.plotGroup=d3Selection.select(trackEvent.elm).append("svg").style("position","absolute")}onUpdate(trackEvent){super.onUpdate(trackEvent);const boundingClient=this.elm.getBoundingClientRect();setStyles(this.plotGroup,{height:`${this.elm.clientHeight}px`,width:`${boundingClient.width}px`})}}class CanvasTrack extends Track{onMount(trackEvent){super.onMount(trackEvent);const canvas=d3Selection.select(trackEvent.elm).append("canvas").style("position","absolute");this.ctx=canvas.node().getContext("2d")}onUpdate(trackEvent){super.onUpdate(trackEvent);const{ctx:ctx,elm:elm}=this,boundingClient=elm.getBoundingClientRect();if(ctx){setProps(d3Selection.select(ctx.canvas),{styles:{width:`${boundingClient.width}px`,height:`${elm.clientHeight}px`},attrs:{width:boundingClient.width,height:elm.clientHeight}})}}}class Plot{constructor(id,options={}){this.id=id,this.options=Object.assign({defined:v=>null!==v,horizontal:!0},options),this.data=[],this.scale=options.scale&&"function"!=typeof options.domain?createScale(options.scale,options.domain||[0,1]):null,this.setRange=this.setRange.bind(this),this.setData=this.setData.bind(this)}get offset(){return this.options.offset||0}setRange(range){return this.scale&&this.scale.range(range),this.range=range,this}setData(data,scale,plotOptions){let plotData=data;if(this.options.dataAccessor&&"function"==typeof this.options.dataAccessor&&(plotData=this.options.dataAccessor(data,plotOptions)),this.options.filterToScale&&scale){const filterOverlapFactor=this.options.filterOverlapFactor||.5;plotData=DataHelper.filterData(plotData,scale.domain(),filterOverlapFactor)}return this.data=plotData,this}setOption(key,value){if(this.options||(this.options={}),this.options[key]=value,"domain"===key&&this.scale)this.scale.domain(value);else if("scale"===key&&this.scale){const domain=this.scale.domain(),range=this.scale.range();this.scale=createScale(value,domain),this.scale.range(range.slice())}return this}setOptions(options){return Object.entries(options).forEach((o=>{this.setOption(o[0],o[1])})),this}updateDynamicScale(data,graphOptions){const{options:options,range:range}=this;if("function"==typeof options.domain){const domain=options.domain(data);this.scale=createScale(options.scale||graphOptions.scale,domain)}range&&this.scale.range(range)}plot(ctx,scale){}}class LinePlot extends Plot{plot(ctx,scale){const{scale:xscale,data:plotdata,options:options}=this;if(!xscale||options.hidden)return;ctx.save();const lineFunction=d3Shape.line().defined((d=>options.defined(d[1],d[0]))).context(ctx);if(options.horizontal?lineFunction.y((d=>xscale(d[1]))).x((d=>scale(d[0]))):lineFunction.x((d=>xscale(d[1]))).y((d=>scale(d[0]))),ctx.beginPath(),lineFunction(plotdata),ctx.lineWidth=options.width,ctx.strokeStyle=options.color,options.dash&&Array.isArray(options.dash)?(ctx.setLineDash(options.dash),ctx.stroke(),ctx.setLineDash([])):ctx.stroke(),options.allowWrapping&&this.plotWrapped(ctx,lineFunction),options.showIsolatedPoints){const arcL=2*Math.PI;ctx.fillStyle=options.color,plotdata.filter(((t,i)=>{var _a,_b;if(!options.defined(t[1],t[0]))return!1;const prev=null===(_a=plotdata[i-1])||void 0===_a?void 0:_a[1],next=null===(_b=plotdata[i+1])||void 0===_b?void 0:_b[1];return 0===i?!options.defined(next):i===plotdata.length-1?!options.defined(prev):!options.defined(prev)&&!options.defined(next)})).forEach((d=>{ctx.beginPath(),options.horizontal?ctx.arc(scale(d[0]),xscale(d[1]),1,0,arcL):ctx.arc(xscale(d[1]),scale(d[0]),1,0,arcL),ctx.fill()}))}ctx.restore()}plotWrapped(ctx,lineFunction){const{scale:xscale,data:plotdata,options:options}=this,isLogarithmic="log"===options.scale;if(!(null==plotdata?void 0:plotdata.length)||options.horizontal)return;ctx.setLineDash(options.dashWrapped||[2,3]);const plotSegment=(segment,disp)=>{ctx.beginPath(),lineFunction(segment.map((([y,x])=>isLogarithmic?[y,10**(Math.log10(x)+disp)]:[y,x+disp]))),ctx.stroke()};let[min,max]=isLogarithmic?xscale.domain().map(Math.log10):xscale.domain();min>max&&([min,max]=[max,min]);const range=max-min;let prev,segmentDisp,segment=[];for(let i=0;i<plotdata.length;i++){const cur=plotdata[i];if(null===cur[1]||void 0===cur[1]){prev=null;continue}const curX=isLogarithmic?Math.log10(cur[1]):cur[1];curX>max||curX<min?(segmentDisp=curX>max?-range:range,0===segment.length&&prev&&segment.push(prev),segment.push(cur)):segment.length>0&&(segment.push(cur),plotSegment(segment,segmentDisp),segment=[]),prev=cur}segment.length>0&&plotSegment(segment,segmentDisp),ctx.setLineDash([])}}class AreaPlot extends Plot{plot(ctx,scale){const{scale:xscale,data:plotdata,options:options}=this;if(!xscale||options.hidden)return;const useMinAsBase=void 0===options.useMinAsBase||options.useMinAsBase,[d0,d1]=xscale.domain(),dmin=Math.min(d0,d1),dmax=Math.max(d0,d1),rmin=xscale(dmin),rmax=xscale(dmax),zeroValue=useMinAsBase?rmin:rmax;let index=0;plotdata.reduce(((acc,v)=>(acc.length>0&&null===v[1]?acc[++index]=[]:acc[index].push(v),acc)),[[]]).forEach((data=>{ctx.save();const areaFunction=d3Shape.area().context(ctx);if(options.horizontal?areaFunction.y1((d=>xscale(d[1]))).y0(zeroValue).x((d=>scale(d[0]))):areaFunction.x1((d=>xscale(d[1]))).x0(zeroValue).y((d=>scale(d[0]))),ctx.globalAlpha=options.fillOpacity||1,options.inverseColor){const inverseValue=useMinAsBase?rmax:rmin,inverseAreaFunction=d3Shape.area().context(ctx);options.horizontal?inverseAreaFunction.y1((d=>xscale(d[1]))).y0(inverseValue).x((d=>scale(d[0]))):inverseAreaFunction.x1((d=>xscale(d[1]))).x0(inverseValue).y((d=>scale(d[0]))),ctx.beginPath(),inverseAreaFunction(data),ctx.fillStyle=options.inverseColor,ctx.fill()}ctx.beginPath(),areaFunction(data),ctx.lineWidth=options.width,ctx.fillStyle=options.fill||options.color,ctx.fill(),ctx.globalAlpha=1,ctx.strokeStyle=options.color,ctx.stroke(),ctx.restore()}))}}var DipPlotShape;!function(DipPlotShape){DipPlotShape.BALL="ball",DipPlotShape.BREAKOUT="breakout",DipPlotShape.CIRCLE="circle",DipPlotShape.SQUARE="square",DipPlotShape.TEE="tee",DipPlotShape.TENSILE="tensile",DipPlotShape.TRIANGLE="triangle"}(DipPlotShape||(DipPlotShape={}));class DipShape{constructor(ctx,category,azimuth,x1,y1,dipSize=5){this.ctx=ctx,this.category=category,this.azimuth=azimuth,this.x1=x1,this.y1=y1,this.dipSize=dipSize,this.radius=.67*this.dipSize,this.barLength=1.25*this.dipSize,this.teeLength=1.8*this.dipSize,this.tailLength=2.5*this.dipSize}draw(){const{category:category,dipSize:dipSize}=this;switch(this.drawTail(),category.shape){case DipPlotShape.TRIANGLE:this.drawPolygon(dipSize,3);break;case DipPlotShape.SQUARE:this.drawPolygon(dipSize,4);break;case DipPlotShape.CIRCLE:this.drawCircle();break;case DipPlotShape.BALL:this.drawSphere();break;case DipPlotShape.TENSILE:this.drawArc(),this.addStroke();break;case DipPlotShape.BREAKOUT:this.drawBreakout();break;case DipPlotShape.TEE:this.drawTee();break;default:this.drawCircle()}}drawArc(){const{radius:radius,x1:x1,y1:y1}=this,arcL=2*Math.PI;this.ctx.beginPath(),this.ctx.arc(x1,y1,radius,0,arcL)}addFill(color=this.category.color){this.ctx.fillStyle=color,this.ctx.fill()}addStroke(color=this.category.color){this.ctx.strokeStyle=color,this.ctx.stroke()}drawTail(){const{azimuth:azimuth,barLength:barLength,category:category,ctx:ctx,tailLength:tailLength,x1:x1,y1:y1}=this;if(ctx.beginPath(),ctx.lineWidth=2,[DipPlotShape.BREAKOUT,DipPlotShape.TENSILE].includes(category.shape)){const x2=x1+Math.cos(azimuth)*barLength,y2=y1+Math.sin(azimuth)*barLength;ctx.moveTo(x2,y2);const x3=x1-Math.cos(azimuth)*barLength,y3=y1-Math.sin(azimuth)*barLength;ctx.lineTo(x3,y3)}else if([DipPlotShape.TEE].includes(category.shape)){ctx.moveTo(x1,y1);const x2=x1+Math.cos(azimuth)*barLength,y2=y1+Math.sin(azimuth)*barLength;ctx.lineTo(x2,y2)}else{ctx.moveTo(x1,y1);const x2=x1+Math.cos(azimuth)*tailLength,y2=y1+Math.sin(azimuth)*tailLength;ctx.lineTo(x2,y2)}ctx.closePath(),this.addStroke()}drawPolygon(radius,sides,x1=this.x1,y1=this.y1,azimuth=this.azimuth){const{ctx:ctx}=this;ctx.beginPath(),ctx.lineWidth=2;const verticesAngle=2*Math.PI/sides,rotate=4===sides?azimuth+.7854:azimuth;for(let i=0;i<sides;i++){const rotPos=verticesAngle*i+rotate;ctx.lineTo(x1+radius*Math.cos(rotPos),y1+radius*Math.sin(rotPos))}ctx.closePath(),this.addFill()}drawCircle(){this.drawArc(),this.addFill("#fff"),this.addStroke()}drawSphere(){this.drawArc(),this.addFill()}drawBreakout(){const{azimuth:azimuth,dipSize:dipSize,x1:x1,y1:y1}=this;this.drawArc(),this.addStroke();const triX1=x1+Math.cos(azimuth)*dipSize,triY1=y1+Math.sin(azimuth)*dipSize,triX2=x1-Math.cos(azimuth)*dipSize,triY2=y1-Math.sin(azimuth)*dipSize;this.drawPolygon(4,3,triX1,triY1),this.drawPolygon(4,3,triX2,triY2,azimuth+3.14159)}drawTee(){const{azimuth:azimuth,ctx:ctx,teeLength:teeLength,x1:x1,y1:y1}=this;ctx.beginPath(),ctx.lineWidth=3;const xLeft=x1+Math.cos(azimuth-67.5)*teeLength,yLeft=y1+Math.sin(azimuth-67.5)*teeLength;ctx.moveTo(xLeft,yLeft);const xRight=x1+Math.cos(azimuth+67.5)*teeLength,yRight=y1+Math.sin(azimuth+67.5)*teeLength;ctx.lineTo(xRight,yRight),this.addStroke()}}class DipPlot extends Plot{plot(ctx,scale){const{scale:xscale,data:plotdata,options:options}=this;xscale&&!options.hidden&&(ctx.save(),null==plotdata||plotdata.forEach((d=>{if(!options.defined(...d))return;const category=d[3],azimuthInRadians=(d[2]-90)*(Math.PI/180),x1=options.horizontal?scale(d[0]):xscale(d[1]),y1=options.horizontal?xscale(d[1]):scale(d[0]),dipSize=options.dipSize;new DipShape(ctx,category,azimuthInRadians,x1,y1,dipSize).draw()})),ctx.restore())}}class DotPlot extends Plot{plot(ctx,scale){const{scale:xscale,data:plotdata,options:options}=this;if(!xscale||options.hidden)return;const r=options.radius||Math.min(5,.04*ScaleHelper.getRangeSpan(xscale));ctx.save(),ctx.fillStyle=options.color;const arcL=2*Math.PI;null==plotdata||plotdata.forEach((d=>{options.defined(d[1],d[0])&&(ctx.beginPath(),options.horizontal?ctx.arc(scale(d[0]),xscale(d[1]),r,0,arcL):ctx.arc(xscale(d[1]),scale(d[0]),r,0,arcL),ctx.fill())})),ctx.restore()}}class DifferentialPlot extends Plot{constructor(id,options={}){super(id,Object.assign({serie1:{color:"red",fill:"red"},serie2:{color:"darkblue",fill:"darkblue"}},options)),this.scale1=null,this.scale2=null,this.setRange=this.setRange.bind(this);const{serie1:serie1,serie2:serie2}=options;options.scale&&options.domain&&"function"!=typeof options.domain&&(this.scale=createScale(options.scale,options.domain||[0,1]),this.scale1=this.scale,this.scale2=this.scale),(null==serie1?void 0:serie1.scale)&&"function"!=typeof serie1.domain&&(this.scale1=createScale(serie1.scale,serie1.domain||[0,1])),(null==serie2?void 0:serie2.scale)&&"function"!=typeof serie2.domain&&(this.scale2=createScale(serie2.scale,serie2.domain||[0,1]))}setData(data,scale,plotOptions){let diffplotData=data;if(this.options.dataAccessor&&"function"==typeof this.options.dataAccessor&&(diffplotData=this.options.dataAccessor(data,plotOptions)),this.options.filterToScale&&scale){const filterOverlapFactor=this.options.filterOverlapFactor||.5;diffplotData=diffplotData.map((d=>DataHelper.filterData(d,scale.domain(),filterOverlapFactor)))}return this.data=diffplotData,this}setRange(range){return this.scale1&&this.scale1.range(range),this.scale2&&this.scale2.range(range),this.range=range,this}setOption(key,value){this.options||(this.options={});let ops=this.options;const path=key.split("."),blockedKeys=["__proto__","constructor","prototype"];if(path.some((p=>blockedKeys.includes(p))))throw new Error("Prototype pollution attempt detected");if(2===path.length&&path[0].match(/serie(1|2)/)){if(ops[path[0]]||(ops[path[0]]={}),ops=ops[path[0]],"domain"===path[1])"serie2"===path[0]&&this.scale2?this.scale2.domain(value):"serie1"===path[0]&&this.scale1&&this.scale1.domain(value);else if("scale"===path[1])if("serie2"===path[0]&&this.scale2){const range=this.scale2.range();this.scale2=createScale(value,this.scale2.domain()).range(range)}else if("serie1"===path[0]&&this.scale1){const range=this.scale1.range();this.scale1=createScale(value,this.scale1.domain()).range(range)}key=path[1]}return ops[key]=value,this}updateDynamicScale(data,graphOptions){const{options:options,range:range}=this;if("function"==typeof options.domain){const domain=options.domain(data);this.scale=createScale(options.scale||graphOptions.scale,domain),this.scale1=this.scale,this.scale2=this.scale}if("function"==typeof options.serie1.domain){const domain=options.serie1.domain(data);this.scale1=createScale(options.scale||graphOptions.scale,domain)}if("function"==typeof options.serie2.domain){const domain=options.serie2.domain(data);this.scale2=createScale(options.scale||graphOptions.scale,domain)}range&&(this.scale1.range(range),this.scale2.range(range))}plot(ctx,scale){const{scale1:xscale1,scale2:xscale2,data:plotdata,options:{serie1:serie1,serie2:serie2,fillOpacity:fillOpacity,horizontal:horizontal,defined:def,hidden:hidden}}=this;if(2!==plotdata.length||!xscale1||!xscale2||hidden)return;const l=Math.max(plotdata[0].length,plotdata[1].length),scaleddata=[[],[]];let min=1/0,max=-1/0;for(let i=0;i<l;i++){const a=plotdata[0][i],b=plotdata[1][i];a&&(scaleddata[0][i]=def(a[1],a[0])?[scale(a[0]),xscale1(a[1])]:[scale(a[0]),a[1]],scaleddata[0][i][1]<min&&(min=scaleddata[0][i][1]),scaleddata[0][i][1]>max&&(max=scaleddata[0][i][1])),b&&(scaleddata[1][i]=def(b[1],b[0])?[scale(b[0]),xscale2(b[1])]:[scale(b[0]),b[1]],scaleddata[1][i][1]<min&&(min=scaleddata[1][i][1]),scaleddata[1][i][1]>max&&(max=scaleddata[1][i][1]))}const areaFunction1=d3Shape.area().context(ctx),areaFunction2=d3Shape.area().context(ctx);horizontal?(areaFunction1.defined((d=>def(d[1],d[0]))).y0(max).y1((d=>d[1])).x((d=>d[0])),areaFunction2.defined((d=>def(d[1],d[0]))).y0(min).y1((d=>d[1])).x((d=>d[0]))):(areaFunction1.defined((d=>def(d[1],d[0]))).x0(min).x1((d=>d[1])).y((d=>d[0])),areaFunction2.defined((d=>def(d[1],d[0]))).x0(max).x1((d=>d[1])).y((d=>d[0]))),ctx.save(),ctx.globalAlpha=fillOpacity||.5,ctx.beginPath(),areaFunction2(scaleddata[1]),ctx.clip(),ctx.beginPath(),areaFunction1(scaleddata[0]),ctx.fillStyle=serie1.fill,ctx.fill(),ctx.restore(),horizontal?(areaFunction1.y0(min),areaFunction2.y0(max)):(areaFunction1.x0(max),areaFunction2.x0(min)),ctx.save(),ctx.globalAlpha=fillOpacity||.5,ctx.beginPath(),areaFunction1(scaleddata[0]),ctx.clip(),ctx.beginPath(),areaFunction2(scaleddata[1]),ctx.fillStyle=serie2.fill,ctx.fill(),ctx.restore();const lineFunction1=d3Shape.line().defined((d=>def(d[1],d[0]))).context(ctx);horizontal?lineFunction1.y((d=>d[1])).x((d=>d[0])):lineFunction1.x((d=>d[1])).y((d=>d[0])),ctx.save(),ctx.globalAlpha=1,ctx.beginPath(),lineFunction1(scaleddata[0]),ctx.lineWidth=serie1.lineWidth||1,ctx.strokeStyle=serie1.color||"red",ctx.stroke();const lineFunction2=d3Shape.line().defined((d=>def(d[1],d[0]))).context(ctx);horizontal?lineFunction2.y((d=>d[1])).x((d=>d[0])):lineFunction2.x((d=>d[1])).y((d=>d[0])),ctx.beginPath(),lineFunction2(scaleddata[1]),ctx.lineWidth=serie2.lineWidth||1,ctx.strokeStyle=serie2.color||"blue",ctx.stroke(),ctx.restore()}}class StepCustom{constructor(ctx){this._ctx=ctx,this._line=void 0,this._x=NaN,this._y=NaN}areaStart(){this._line=!0}areaEnd(){this._line=!1}lineStart(){this._x=NaN,this._y=NaN}lineEnd(){this._line&&this._ctx.closePath()}point(x,y){Number.isNaN(x)||Number.isNaN(y)?Number.isNaN(x)||Number.isNaN(this._x)||Number.isNaN(this._y)||this._ctx.lineTo(x,this._y):Number.isNaN(this._y)?this._ctx.moveTo(x,y):(y!==this._y&&this._ctx.lineTo(x,this._y),this._ctx.lineTo(x,y)),this._x=x,this._y=y}}function stepCustom(ctx){return new StepCustom(ctx)}class LineStepPlot extends Plot{plot(ctx,scale){const{scale:xscale,data:plotdata,options:options}=this;if(!xscale||options.hidden)return;ctx.save();const lineFunction=d3Shape.line().defined((()=>!0)).curve(stepCustom).context(ctx);options.horizontal?lineFunction.y((d=>null===d[1]?NaN:xscale(d[1]))).x((d=>null===d[0]?NaN:scale(d[0]))):lineFunction.x((d=>null==d[1]?NaN:xscale(d[1]))).y((d=>null==d[0]?NaN:scale(d[0]))),ctx.beginPath(),lineFunction(plotdata),ctx.lineWidth=options.width,ctx.lineCap="square",ctx.strokeStyle=options.color,options.dash&&Array.isArray(options.dash)?(ctx.setLineDash(options.dash),ctx.stroke(),ctx.setLineDash([])):ctx.stroke(),ctx.restore()}}function patchPlotOptions(options){return options?(options&&options.data&&(options.dataAccessor=options.data,delete options.data),options):{}}function createScale(type,domain){if("linear"===type)return d3Scale.scaleLinear().domain(domain);if("log"===type)return d3Scale.scaleLog().domain(domain);throw Error("Invalid input!")}function createPlotType(PlotType){return(config,trackScale)=>{const options=Object.assign({legendRows:1,filterToScale:!0,dataAccessor:d=>d},patchPlotOptions(config.options)),p=new PlotType(config.id,options);return options.scale||(p.scale=trackScale),p}}const plotFactory={line:createPlotType(LinePlot),area:createPlotType(AreaPlot),dip:createPlotType(DipPlot),dot:createPlotType(DotPlot),differential:function(config,trackScale){const options=Object.assign({legendRows:2,filterToScale:!0},patchPlotOptions(config.options)),p=new DifferentialPlot(config.id,options);return options.serie1.scale||(p.scale1=trackScale),options.serie2.scale||(p.scale2=trackScale),p},linestep:createPlotType(LineStepPlot)},defaultOptions$2={scale:"linear",domain:[0,100],togglePlotFromLegend:!0,forceDataUpdateOnToggle:!1,plotFactory:plotFactory};const setupDomainBackground=(bg,text)=>{const bbox=text.node().getBBox();setProps(bg,{styles:{fill:"white"},attrs:{x:bbox.x-2,y:bbox.y-2,width:bbox.width+4,height:bbox.height+4}})};function renderTextLabels(g,bounds,label,unit,domain,color,{addLabelBg:addLabelBg,largeFontEnabled:largeFontEnabled}={}){const{height:h,width:w,top:top,left:left}=bounds,lineY=top+.5*h,textSize=.35*h,centerX=left+w/2,min=domain[0],max=domain[domain.length-1],isLargeFont=largeFontEnabled&&w>90,unitTextSixe=.85*textSize,unitY=lineY+unitTextSixe,domainTextSize=isLargeFont?1.1*textSize:unitTextSixe,subY=isLargeFont?lineY+domainTextSize/2-.05*h:lineY+domainTextSize,labelTransform=`translate(${centerX},${top+textSize})`;let labelBg;addLabelBg&&(labelBg=g.append("rect").classed("label-bg",!0).attr("fill","white"));const labelText=g.append("text").text(label);if(setProps(labelText,{styles:{"text-anchor":"middle",fill:color},attrs:{class:"legend-label","font-size":`${textSize}px`,transform:labelTransform}}),addLabelBg){const bbox=labelText.node().getBBox();setAttrs(labelBg,{x:centerX+bbox.x-1,y:top+1,wi