@protomaze/time 1.5.1 → 1.5.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/engine/TimeSlider.d.ts +3 -0
- package/engine/TimeSlider.js +1 -1
- package/layer/TimeSliderLayerLoader.d.ts +2 -0
- package/package.json +1 -1
- package/three/layer/Three3DIntervalsTimeLayerLoader.d.ts +1 -0
- package/three/layer/Three3DIntervalsTimeLayerLoader.js +1 -1
- package/three/layer/ThreeIntervalsTimeLayerLoader.d.ts +1 -0
- package/three/layer/ThreeIntervalsTimeLayerLoader.js +1 -1
package/engine/TimeSlider.d.ts
CHANGED
|
@@ -122,6 +122,7 @@ export declare class TimeSlider extends TypedEventEmitter<{
|
|
|
122
122
|
private loaders;
|
|
123
123
|
private layerUnloaders;
|
|
124
124
|
private layerListenerStoppers;
|
|
125
|
+
private layerLoaders;
|
|
125
126
|
pressedKeys: Set<string>;
|
|
126
127
|
protected container: HTMLElement | null;
|
|
127
128
|
theme: 'dark' | 'light';
|
|
@@ -323,6 +324,8 @@ export declare class TimeSlider extends TypedEventEmitter<{
|
|
|
323
324
|
private loadLayer;
|
|
324
325
|
private unloadLayer;
|
|
325
326
|
addLayer(layer: TimeSliderLayer): void;
|
|
327
|
+
/** layer.min / layer.max changed: reload the layer unless its loader follows the band by itself. Unlike toggling visible, this keeps the element selection. */
|
|
328
|
+
private onLayerBandChange;
|
|
326
329
|
removeLayer(layer: string | {
|
|
327
330
|
id: string;
|
|
328
331
|
}): void;
|
package/engine/TimeSlider.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{v4 as e}from"uuid";import{Disposables as t}from"../common/Disposables.js";import{animateRangeTransition as i}from"../common/Easing.js";import{formatDate as s,formatDateRange as n,formatDuration as l,getDefaultLocale as a,getLargeTimeUnitRange as o,getMatchingTimeUnit as r,getTimeUnitRange as h,setDefaultLocale as d}from"../common/TimeTools.js";import{TimeSliderDynamicHistogram as m}from"../data/histogram/TimeSliderDynamicHistogram.js";import{getTimeArray as c}from"../data/TimeSliderElement.js";import{TimeSliderIntervalsModel as u}from"../data/TimeSliderIntervalsModel.js";import{addDuration as g,addPadding as p,applyTimeRangeUpdate as y,clampTimeRange as v,mergeTimeRanges as T,timeRangesOverlap as f,timeRangesUnion as E}from"../data/TimeSliderRange.js";import{TypedEventEmitter as x}from"../event/TypedEventEmitter.js";import{TimeSliderHistogramLayer as L}from"../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as R}from"../layer/TimeSliderIntervalsLayer.js";import{CENTURY as b,DECADE as w,MILLENIUM as S,ONE_DAY as C,ONE_HOUR as V,ONE_MINUTE as k,ONE_MONTH as M,ONE_SECOND as D,ONE_WEEK as B,ONE_YEAR as z,TODAY as I}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as O}from"../ticks/TimeTicksBar.js";import{YAxisTicks as A}from"../ticks/YAxisTicks.js";const H=D,_=10*S;export class TimeSlider extends x{getLayers(){return[...this.layers]}getVisibleLayers(){return this.getLayers().filter(e=>e.visible)}constructor(i){super(),this.valueRanges=new Map,this.layers=[],this.onDestroy=[],this.ticksBar=new O,this.loaders=new Set,this.layerUnloaders=new Map,this.layerListenerStoppers=new Map,this.pressedKeys=new Set,this.container=null,this.d=new t,this.id=e(),this.playing=!1,this.live=!1,this._showCurrent=!1,this.current=Date.now(),this.range={time:I,value:{minValue:0,maxValue:100}},this.size={width:100,height:100},this.sliderElement=document.createElement("div"),this.yAxisTicks=new A,this.hoverLine=document.createElement("div"),this.toolTip=document.createElement("div"),this.selectedLabel=document.createElement("div"),this.selectingRangeElement=document.createElement("div"),this.selectionOverlayContainer=document.createElement("div"),this._selectionRanges=[],this.selectionRangeElements=[],this.shadingElements=[],this.columnBandOverlayContainer=document.createElement("div"),this.columnBandElements=[],this.selectingRange=!1,this.selectingRangeVisible=!1,this.updateValueRangesQueue=new Map,this.showingStats=!1,this._engineReady=!1,this.mouse=null,this.updatingLabel=!1,this.hoverCleared=!0,this.dragging=!1,this.pinching=!1,this.hoverOptions=i?.hover||{},this.selectionOptions={highlight:"boolean"!=typeof i?.selection?.highlight||i.selection.highlight,label:"boolean"!=typeof i?.selection?.label||i.selection.label,shading:"boolean"!=typeof i?.selection?.shading||i.selection.shading,invertShading:"boolean"==typeof i?.selection?.invertShading&&i.selection.invertShading,pick:i?.selection?.pick||"element",columnBand:i?.selection?.columnBand},this.showCurrent=i?.showCurrent||!1,this.showTicks=i?.showTicks||!1,this.selectRangeMode=i?.selectRangeMode||"select",this.playSpeed=i?.playSpeed||"auto",this.liveRange=i?.liveRange||10*D,this.theme=i?.theme||"dark",this._locale=i?.locale||a(),d(this._locale),this.ticksBar.setLocale(this._locale),this.ticksBar.setWeekLabel(i?.weekLabel||"default",i?.weekLabelPrefix),this.ticksBar.setTickLabelStyler(i?.tickLabelStyler),this.setLimits(i?.limits),this.syncMaxValues(),this.on("time",()=>this.renderSelectionOverlay()),this.on("size",()=>this.renderSelectionOverlay())}get locale(){return this._locale}setLocale(e){this._locale=e,d(e),this.ticksBar.setLocale(e),this.emit("locale",e)}setWeekLabel(e,t){this.ticksBar.setWeekLabel(e,t)}setTickLabelStyler(e){this.ticksBar.setTickLabelStyler(e)}get limits(){return this._limits?{...this._limits}:void 0}setLimits(e){!e||e.endTime>e.startTime||(console.warn("Ignoring time slider limits whose endTime is not after startTime",e),e=void 0),this._limits=e?{startTime:e.startTime,endTime:e.endTime}:void 0,this._limits&&this.setTimeRange(this.getTimeRange())}setSelection(e){this._selectionRanges=e.map(e=>({...e})),this.renderSelectionOverlay(!0),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"})}getSelection(){return this._selectionRanges.map(e=>({...e}))}clearSelection(){this.setSelection([])}setPickMode(e){this.selectionOptions.pick=e,this.updateHover()}selectionTimeToX(e,t,i,s){return e===-1/0?0:e===1/0?s:Math.min(s,Math.max(0,(e-t)/(i-t)*s))}renderSelectionOverlay(e=!1){if(!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>this.selectionTimeToX(e,i,s,t.width);if((e||this.selectionRangeElements.length!==this._selectionRanges.length||this.selectionRangeElements.some((e,t)=>e.range!==this._selectionRanges[t]))&&(this.selectionRangeElements.forEach(({body:e,startHandle:t,endHandle:i})=>{e.remove(),t?.remove(),i?.remove()}),this.selectionRangeElements=this._selectionRanges.map(e=>this.createSelectionRangeElements(e))),this.selectionRangeElements.forEach(({range:e,body:t,startHandle:i,endHandle:s})=>{const l=n(e.startTime),a=n(e.endTime);t.style.left=l+"px",t.style.width=Math.max(0,a-l)+"px",i&&(i.style.left=l-4+"px"),s&&(s.style.left=a-4+"px")}),this.shadingElements.forEach(e=>e.remove()),this.shadingElements=[],this.selectionOptions.shading&&this._selectionRanges.length>0){const e=T(this._selectionRanges).map(e=>[n(e.startTime),n(e.endTime)]);(this.selectionOptions.invertShading?e:function(e,t){const i=[];let s=0;for(const[t,n]of e)t>s&&i.push([s,t]),s=Math.max(s,n);s<t&&i.push([s,t]);return i}(e,t.width)).forEach(([e,t])=>{if(t<=e)return;const i=document.createElement("div");i.style.position="absolute",i.style.top="0px",i.style.height="100%",i.style.left=e+"px",i.style.width=t-e+"px",i.style.background="rgba(0, 0, 0, 0.5)",i.style.pointerEvents="none",this.selectionOverlayContainer.appendChild(i),this.shadingElements.push(i)})}}renderColumnHoverBand(e){if(this.columnBandElements.forEach(e=>e.remove()),this.columnBandElements=[],"column"!==this.selectionOptions.pick||0===e.length||!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>Math.min(t.width,Math.max(0,(e-i)/(s-i)*t.width)),l=this.selectionOptions.columnBand?.color??"#fff",a=this.selectionOptions.columnBand?.opacity??.15;e.forEach(({layer:e,elements:t})=>{const i=t.map(c),s=Math.min(...i.map(e=>e[0])),o=Math.max(...i.map(e=>e[1])),r=document.createElement("div");r.style.position="absolute",r.style.left=n(s)+"px",r.style.width=Math.max(1,n(o)-n(s))+"px",r.style.top=100*(1-e.max)+"%",r.style.height=100*(e.max-e.min)+"%",r.style.background=l,r.style.opacity=String(a),r.style.pointerEvents="none",this.columnBandOverlayContainer.appendChild(r),this.columnBandElements.push(r)})}createSelectionRangeElements(e){const t=document.createElement("div");t.style.position="absolute",t.style.top="0px",t.style.height="100%",t.style.background="rgba(100, 180, 255, 0.15)",t.style.borderTop="2px solid rgba(100, 180, 255, 0.8)",t.style.borderBottom="2px solid rgba(100, 180, 255, 0.8)",t.style.boxSizing="border-box",t.style.cursor="grab",t.style.pointerEvents="auto",t.style.touchAction="none",this.selectionOverlayContainer.appendChild(t);const i=()=>{const e=document.createElement("div");return e.style.position="absolute",e.style.top="0px",e.style.height="100%",e.style.width="8px",e.style.cursor="ew-resize",e.style.pointerEvents="auto",e.style.touchAction="none",e.style.background="rgba(100, 180, 255, 0.9)",this.selectionOverlayContainer.appendChild(e),e},s=e.startTime===-1/0?null:i(),n=e.endTime===1/0?null:i();return this.wireSelectionDrag(e,t,s,n),{range:e,body:t,startHandle:s,endHandle:n}}wireSelectionDrag(e,t,i,s){const n=(e,t)=>{e.addEventListener("pointerdown",i=>{i.stopPropagation(),i.preventDefault(),e.setPointerCapture(i.pointerId);const s=t(i),n=e=>{s(e),this.renderSelectionOverlay(),this.emit("selectionchange",{ranges:this.getSelection(),phase:"drag"})};let l=!1;const a=t=>{l||(l=!0,e.hasPointerCapture(t.pointerId)&&e.releasePointerCapture(t.pointerId),e.removeEventListener("pointermove",n),e.removeEventListener("pointerup",a),e.removeEventListener("pointercancel",a),e.removeEventListener("lostpointercapture",a),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"}))};this.emit("selectionchange",{ranges:this.getSelection(),phase:"start"}),e.addEventListener("pointermove",n),e.addEventListener("pointerup",a),e.addEventListener("pointercancel",a),e.addEventListener("lostpointercapture",a)})};i&&n(i,()=>t=>{const{t:i}=this.mouseInSlider(t);e.startTime=e.endTime===1/0?i:Math.min(i,e.endTime)}),s&&n(s,()=>t=>{const{t:i}=this.mouseInSlider(t);e.endTime=e.startTime===-1/0?i:Math.max(i,e.startTime)}),n(t,t=>{const i=this.mouseInSlider(t).t,s=e.startTime,n=e.endTime;return t=>{const l=this.mouseInSlider(t).t-i;s===-1/0&&n!==1/0?e.endTime=n+l:n===1/0&&s!==-1/0?e.startTime=s+l:s!==-1/0&&n!==1/0&&(e.startTime=s+l,e.endTime=n+l)}})}startSelectingRange(e){this.selectingRange=!0,this.whenDoneSelectingRange=e}stopSelectingRange(){this.selectingRange=!1,this.whenDoneSelectingRange?.(),this.whenDoneSelectingRange=void 0,this.selectingRangeVisible&&(this.container.removeChild(this.selectingRangeElement),this.selectingRangeVisible=!1)}syncMaxValues(){return this.updateAllValueRanges(),this.on("time",()=>this.updateAllValueRanges())}syncMaxValuesForLayer(e){return this.updateValueRanges(e.yAxisGroup),e.model.on(()=>this.updateValueRanges(e.yAxisGroup))}updateAllValueRanges(){this.layers.filter(e=>e.visible).forEach(e=>{this.updateValueRanges(e.yAxisGroup,!0)}),this.renderYAxis()}updateValueRanges(e,t){this.updateValueRangesQueue.has(e)||this.updateValueRangesQueue.set(e,{lastUpdate:0,pending:void 0});const i=this.updateValueRangesQueue.get(e);if(Date.now()-i.lastUpdate<100)return i.pending&&clearTimeout(i.pending),void(i.pending=setTimeout(()=>{i.pending=void 0,this.updateValueRanges(e)},200));i.lastUpdate=Date.now();const s=new Map,n=new Map;this.layers.filter(e=>e.visible).forEach(e=>{const t=e.yAxis.group||e.id;s.has(t)||(s.set(t,-Number.MAX_VALUE),n.set(t,Number.MAX_VALUE));const i=e.model.getValueRange(this.getTimeRange());i.minValue<n.get(t)&&n.set(t,i.minValue),i.maxValue>s.get(t)&&s.set(t,i.maxValue)}),this.valueRanges.clear(),s.forEach((e,t)=>{this.valueRanges.set(t,{max:e,min:n.get(t)})}),t||this.renderYAxis(),this.emit("valueRanges",e)}renderYAxis(){const e=new Map;this.layers.filter(e=>e.visible).forEach(t=>{const i=t.yAxis.group||t.id;e.has(i)||e.set(i,{min:t.min,max:t.max,group:i}),e.set(i,{...e.get(i),...t.yAxis,min:t.min,max:t.max})});const t=[];e.forEach(e=>{if(e.visible){const i=this.valueRanges.get(e.group);i&&t.push({...e,minValue:i.min,maxValue:i.max})}}),this.yAxisTicks.render(t)}setTheme(e){this.theme=e,this.ticksBar.setDarkMode("dark"===e),this.engine?.setTheme(e)}addLayerLoader(e){this.loaders.add(e)}showStats(e){this.showingStats=e,this.engine?.showStats(e)}get engineReady(){return this._engineReady}whenReady(){return this._engineReady?Promise.resolve():new Promise(e=>{const t=this.on("ready",()=>{t(),e()})})}async setEngine(e){this._engineReady=!1,this.engine&&(this.layerUnloaders.forEach(e=>e()),this.layerUnloaders.clear(),this.engine.dispose()),this.initSliderContainer(),this.engine=e,this.engine.setTheme(this.theme),await this.engine.init(this.sliderElement),this.engine.setTheme(this.theme),this._engineReady=!0,this.emit("ready"),this.getVisibleLayers().forEach(e=>this.loadLayer(e)),this.engine.showStats(this.showingStats),this.engine.setTimeRange(this.getTimeRange()),this.engine.setValueRange(this.getValueRange()),this.engine.setSize(this.size.width,this.size.height)}initSliderContainer(){this.stopControls&&this.stopControls(),this.container?.removeChild(this.sliderElement);let e=0;this.showTicks&&(e+=24),this.showCurrent&&(e+=4),this.sliderElement=document.createElement("div"),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top="0px",this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${0+e}px)`,this.container?.appendChild(this.sliderElement),this.initSelectLabel(),this.stopControls=this.initControls(),this.onDestroy.push(()=>{this.container?.removeChild(this.sliderElement)})}get showCurrent(){return this._showCurrent}set showCurrent(e){if(e!==this._showCurrent){this._showCurrent=e,this.ticksBar.showLine=!this.showCurrent;let t=0,i=0;this.showTicks&&(i+=24),this.showCurrent&&(i+=4),this.sliderElement.style.height=`calc(100% - ${t+i}px)`}}async init(e,t){const i="string"==typeof e?document.getElementById(e):e;if(this.container=i,i){i.oncontextmenu=()=>!1;let e=0,s=0;if(this.showTicks){s+=24;const e=document.createElement("div");e.style.width="100%",e.style.height="24px",e.style.position="absolute",e.style.bottom="0px",e.style.touchAction="none",i.appendChild(e),this.ticksBar.init(e,e=>{const t=r(e.startTime,e.endTime);this.emit("tickclick",{startTime:e.startTime,endTime:e.endTime,unit:t}),this.fit({range:e,animate:1e3})}),this.ticksBar.showLine=!this.showCurrent}this.showCurrent&&(s+=4),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top=`${e}px`,this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.sliderElement),this.yAxisTicks.canvas.style.top=`${e}px`,this.yAxisTicks.canvas.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.yAxisTicks.canvas),this.selectionOverlayContainer.style.position="absolute",this.selectionOverlayContainer.style.width="100%",this.selectionOverlayContainer.style.overflow="hidden",this.selectionOverlayContainer.style.pointerEvents="none",this.selectionOverlayContainer.style.top=`${e}px`,this.selectionOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.selectionOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.selectionOverlayContainer)),this.columnBandOverlayContainer.style.position="absolute",this.columnBandOverlayContainer.style.width="100%",this.columnBandOverlayContainer.style.overflow="hidden",this.columnBandOverlayContainer.style.pointerEvents="none",this.columnBandOverlayContainer.style.top=`${e}px`,this.columnBandOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.columnBandOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.columnBandOverlayContainer)),this.initHoverTooltip(),this.initSelectLabel(),this.setSize(this.sliderElement.clientWidth,this.sliderElement.clientHeight),this.syncSize(),this.setTimeRange($(t)),this.setValueRange($(t))}else console.warn(`Element ${e} not found`)}initSelectLabel(){this.selectedLabel.style.position="absolute",this.selectedLabel.style.padding="4px 8px",this.selectedLabel.style.background="rgba(0, 0, 0, 0.75)",this.selectedLabel.style.color="white",this.selectedLabel.style.fontSize="12px",this.selectedLabel.style.maxWidth="200px",this.selectedLabel.style.fontFamily="monospace",this.selectedLabel.style.borderRadius="4px",this.selectedLabel.style.pointerEvents="none",this.selectedLabel.style.display="none",this.selectedLabel.style.zIndex="10",this.sliderElement.appendChild(this.selectedLabel)}getLabel(e,t,i,s){if(t.labeler){const n=t.labeler(e,i,s);return null===n?null:"string"==typeof n?`<div>${n}</div>`:n?n.outerHTML:`<div>${e.name||e.id}</div>`}return`<div>${e.name||e.id}</div>`}getLayerById(e){return this.layers.find(t=>t.id===e)}select(e,t){this.stopSelectingRange(),this.stopUpdatingLabel?.();const i=()=>{this.updatingLabel=!1,this.stopUpdatingLabel?.(),this.stopUpdatingLabel=void 0,this.selectedLabel.style.display="none",this.layers.forEach(e=>e.select([])),this.selectedLayerId=void 0,this.emit("select",[])},s=e=>{this.layers.filter(t=>!e.some(e=>t.id===e.layer.id)).forEach(e=>e.select([])),e.forEach(e=>{e.layer.select(e.elements)}),this.emit("select",e)};if(t){s(this.getVisibleLayers().map(e=>{const i=e.model.dataInTimeRange(t);return{layer:e,elements:i}}).filter(e=>e.elements.length>0))}else if(e){const t=this.findElements(e,24);if(s(t.map(e=>({layer:e.layer,elements:e.elements})).flat()),1===t.length&&t[0].elements.length>0&&this.selectionOptions.label){const e=t[0].elements[0],s=t[0].layer;this.selectedLabel.innerHTML=this.getLabel(e,s,t[0].t,t[0].v)||"",this.selectedLabel.style.display="block";const n=this.selectedLabel.offsetWidth,l=this.selectedLabel.offsetHeight;this.selectedLayerId=s.id;const a=Array.isArray(e.t);this.updatingLabel=!0;const o=()=>{if(!this.updatingLabel)return;const{startTime:i,duration:s,endTime:o}=this.getTimeRange(),r=t[0].layer.model.get(e.id);if(r){const e=f(this.getTimeRange(),r.t);if(e){const o=this.getSliderBounds();let h=(.5*(e.startTime+e.endTime)-i)/s*o.width;const d="number"==typeof r.v?r.v:.5*(r.v[1]+r.v[0]);let m=this.modelValueToPixels(d,t[0].layer);a?m-=.5*l:m+=8;const c=.5*n,u=.5*l,g=o.width-.5*n,p=o.height-.5*l,y=Math.min(g,Math.max(c,h)),v=Math.min(p,Math.max(u,m));this.selectedLabel.style.display="block",this.selectedLabel.style.left=y-.5*n+"px",this.selectedLabel.style.top=`${v}px`}else this.selectedLabel.style.display="none"}else this.selectedLabel.style.display="none"};o();const r=s instanceof L&&s.histogram instanceof m;this.stopUpdatingLabel=this.on("time",r?i:o)}}else i()}listenToClicks(){let e=null;const t=t=>{e={x:t.clientX,y:t.clientY}},i=t=>{if(!e)return;const i=t.clientX-e.x,s=t.clientY-e.y;i*i+s*s<=25&&(this.stopSelectingRange(),this.select(e)),e=null},s=()=>{e=null};return this.sliderElement.addEventListener("pointerdown",t),this.sliderElement.addEventListener("pointerup",i),this.sliderElement.addEventListener("pointercancel",s),()=>{this.sliderElement.removeEventListener("pointerdown",t),this.sliderElement.removeEventListener("pointerup",i),this.sliderElement.removeEventListener("pointercancel",s)}}getSliderBounds(){return this.sliderElement.getBoundingClientRect()}mouseInSlider(e){const t=this.getSliderBounds(),i="x"in e?e:{x:e.clientX,y:e.clientY},s={x:i.x-t.left,y:i.y-t.top},n={x:s.x/t.width,y:-s.y/t.height+1},{startTime:l,duration:a}=this.getTimeRange(),o=l+n.x*a,{minValue:r,maxValue:h}=this.getValueRange();return{absolute:s,normalized:n,t:o,v:r+n.x*(h-r)}}pixelsToLayerValue(e,t){if(!e)return{yInLayer:0,toLayerHeight:1};const i="normalized"in e?e.normalized:this.mouseInSlider(e).normalized;let s=t.min;const n=t.max-s;return{yInLayer:(i.y-s)/n,toLayerHeight:n/this.getSliderBounds().height}}pixelsToModelValue(e,t){const{yInLayer:i,toLayerHeight:s}=this.pixelsToLayerValue(e,t);let n=0,l=100;if(t.model instanceof u){const e=this.valueRanges.get(t.yAxisGroup)||{min:0,max:1e3};n=e.min,l=e.max-n}return{yInModel:n+i*l,toModelHeight:s*l}}layerValueToPixels(e,t){let i=t.min;return(1-(i+(t.max-i)*e))*this.getSliderBounds().height}modelValueToPixels(e,t){let i=0,s=100;if(t.model instanceof u){const e=t.model.getValueRange(this.getTimeRange());i=e.minValue,s=e.maxValue-i}const n=(e-i)/s;return this.layerValueToPixels(n,t)}findElements(e,t){if("column"===this.selectionOptions.pick)return this.findColumnElements(e);const{width:i,height:s}=this.getSliderBounds(),{normalized:n}=this.mouseInSlider(e),{startTime:l,endTime:a,duration:o}=this.getTimeRange(),r=l+n.x*o,h=t||12,d=h/i*o,m={startTime:r-1,endTime:r+1},c={startTime:r-.5*d,endTime:r+.5*d};return this.getVisibleLayers().map(e=>{const{yInModel:t,toModelHeight:i}=this.pixelsToModelValue({normalized:n},e),s=e instanceof R?0:h*i,l=e instanceof R?m:c,a={minValue:t-.5*s,maxValue:t+.5*s},o=e.model.dataInRange({...l,...a});return{layer:e,elements:o,t:r,v:t}}).filter(e=>e.elements.length>0)}findColumnElements(e,t){const{width:i}=this.getSliderBounds(),{normalized:s}=this.mouseInSlider(e),{startTime:n,duration:l}=this.getTimeRange(),a=n+s.x*l,o=(t||12)/i*l,r={startTime:a-1,endTime:a+1},h={startTime:a-.5*o,endTime:a+.5*o};return this.getVisibleLayers().map(e=>{const{yInModel:t}=this.pixelsToModelValue({normalized:s},e),i=e instanceof R?r:h,n=e.model.dataInTimeRange(i);return{layer:e,elements:n,t:a,v:t}}).filter(e=>e.elements.length>0)}clearHoverEvent(){this.hoverCleared||(this.hoverCleared=!0,this.renderColumnHoverBand([]),this.emit("hover",null))}updateHover(e){if(this.dragging)return this.hoverLine.style.display="none",this.toolTip.style.display="none",void this.clearHoverEvent();if(void 0!==e&&(this.mouse=e),this.mouse){const e=this.getSliderBounds(),{normalized:t}=this.mouseInSlider(this.mouse),{startTime:i,endTime:n,duration:l}=this.getTimeRange(),a=i+t.x*l,o=this.findElements(this.mouse);this.renderColumnHoverBand(o),this.hoverCleared=!1,this.emit("hover",{elements:o.map(e=>({layer:e.layer,elements:e.elements})),t:a,clientX:this.mouse.x,clientY:this.mouse.y});const r=o.map(e=>e.elements.map(t=>({e:t,l:e.layer,t:e.t,v:e.v}))).flat();let h;if(r.length>0)if(r.length>3){h=r.slice(0,2).map(e=>this.getLabel(e.e,e.l,e.t,e.v)).flat().filter(e=>null!==e).join(""),h+=`<div>And ${r.length-2} more...</div>`}else{h=o.map(e=>e.elements.map(t=>this.getLabel(t,e.layer,e.t,e.v))).flat().filter(e=>null!==e).join("")}if(!1===this.hoverOptions.tooltip)this.toolTip.style.display="none";else{const t=new Date(a),i=t.getHours(),n=t.getMinutes(),l=t.getSeconds(),o=s(a,!0,this._locale),r=`${(i<10?"0":"")+i}:${(n<10?"0":"")+n}:${(l<10?"0":"")+l}`;let d="";const m=this.ticksBar.cache.high.spacing;(void 0===this.hoverOptions.date?m>=V:this.hoverOptions.date)&&(d+=`<div>${o}</div>`),(void 0===this.hoverOptions.time?m<=C:this.hoverOptions.time)&&(d+=`<div>${r}</div>`),h&&(d+=h),this.toolTip.innerHTML=d,this.toolTip.style.display=d.length>0?"block":"none";const c=88,u=36;let g=this.mouse.x+10,p=this.mouse.y+10;const y=e.left+e.width-c-5,v=e.top+e.height-u-5;g>y&&(g=y),p>v&&(p=v),this.toolTip.style.left=`${g}px`,this.toolTip.style.top=`${p}px`}}else this.hoverLine.style.display="none",this.toolTip.style.display="none",this.clearHoverEvent()}initHoverTooltip(){this.toolTip.style.position="absolute",this.toolTip.style.padding="4px 8px",this.toolTip.style.background="rgba(0, 0, 0, 0.75)",this.toolTip.style.color="white",this.toolTip.style.fontSize="12px",this.toolTip.style.fontFamily="monospace",this.toolTip.style.borderRadius="4px",this.toolTip.style.pointerEvents="none",this.toolTip.style.display="none",this.toolTip.style.zIndex="10",this.toolTip.style.overflow="hidden",document.body.appendChild(this.toolTip)}initHoverListener(){const e=()=>{this.updateHover(null)},t=e=>{this.updateHover({x:e.clientX,y:e.clientY})};this.sliderElement.addEventListener("mouseleave",e),this.sliderElement.addEventListener("mousemove",t);const i=this.listenToClicks();return()=>{this.sliderElement.removeEventListener("mouseleave",e),this.sliderElement.removeEventListener("mousemove",t),i()}}syncSize(){const e=()=>{const{offsetWidth:e,offsetHeight:t}=this.sliderElement;this.setSize(e,t)},t=new ResizeObserver(()=>{e()});this.container&&(t.observe(this.container),e()),this.onDestroy.push(()=>{t.disconnect()})}getTimeRange(){return g(this.range.time)}getValueRange(){return this.range.value}setTimeRange(e,t){const i=this.getTimeRange(),s=y(i,e),n=this._limits?v(s,this._limits):s;n.startTime===i.startTime&&n.endTime===i.endTime||(this.range.time=n,this.engine?.setTimeRange(n),this.showTicks&&this.ticksBar.setTimeRange(n),this.emit("time",n),this.updateHover())}setValueRange(e){const t={...this.getValueRange(),...e};this.range.value=t,this.engine?.setValueRange(t),this.emit("value",this.getValueRange()),this.updateHover()}setSize(e,t){this.size={width:e,height:t},this.showTicks&&this.ticksBar.setWidth(e);const i=this.getTimeRange(),s=this.getValueRange();this.engine?.setSize(e,t),this.setTimeRange(i),this.setValueRange(s),this.updateHover(),this.emit("size",{width:e,height:t})}up(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=.5*(t+i);if(s>=.9*S){const e=U(10*s);n=o(l,e)}else{const e=[{threshold:b,unit:"millennium"},{threshold:w,unit:"century"},{threshold:z,unit:"decade"},{threshold:.9*M,unit:"year"},{threshold:B,unit:"month"},{threshold:C,unit:"week"},{threshold:V,unit:"day"},{threshold:k,unit:"hour"},{threshold:D,unit:"minute"},{threshold:-1,unit:"second"}];let t=0;for(;s<.9*e[t].threshold;)t++;const i=e[t].unit;n=h(l,i)}this.fit({...e,range:n})}down(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=t+("number"==typeof e?.xRatio?e.xRatio:.5)*s;if(s>=.9*S){const e=U(s/10);n=o(l,e)}else{let e=0;const t=[{threshold:S,unit:"century"},{threshold:b,unit:"decade"},{threshold:w,unit:"year"},{threshold:z,unit:"month"},{threshold:1.1*M,unit:"week"},{threshold:B,unit:"day"},{threshold:C,unit:"hour"},{threshold:V,unit:"minute"},{threshold:k,unit:"second"}].reverse();for(;s>1.1*t[e].threshold;)e++;const i=t[e].unit;n=h(l,i)}this.fit({...e,range:n})}next(e){this.skip(1,e)}previous(e){this.skip(-1,e)}skip(e,t){let i={startTime:0,endTime:Date.now()};const{startTime:s,endTime:n,duration:l}=this.getTimeRange(),a=.5*(s+n);if(l>=9*S){const e=U(l);i=o(a,e)}else{const t=r(s,n);i=t?h(a,t,Math.round(e)):{startTime:s+e*l,endTime:n+e*l}}this.fit({...t,range:i})}getTimeRangeLabel(){const{startTime:e,endTime:t}=this.getTimeRange(),i=new Date(e);return 0===i.getHours()&&0===i.getMinutes()&&0===i.getSeconds()&&0===i.getMilliseconds()?n([e,t],this._locale):void 0}cancelFit(){this.stopFit?.(),this.stopFit=void 0}fit(e){this.cancelFit();const t=e?.range||this.getLayersTimeRange(),s=e?.padding?p(t,e.padding):t;if(e?.animate){const t=this.getTimeRange();this.stopFit=i(t.startTime,t.endTime,s.startTime,s.endTime,e.animate,(e,t)=>this.setTimeRange({startTime:e,endTime:t}))}else this.setTimeRange(s)}getLayersTimeRange(){if(0===this.getVisibleLayers().length)return I;{let e=this.getVisibleLayers()[0].model.getTimeRange();return this.getVisibleLayers().slice(1).forEach(t=>e=E(e,t.model.getTimeRange())),e}}loadLayer(e){if(e.timeSlider&&e.timeSlider.id!==this.id)console.warn("Layer already has been loaded by another time slider, create a new layer with the same model and style if you want to add it to another time slider");else if(this.engine&&this._engineReady&&!this.layerUnloaders.has(e.id)){const t=[...this.loaders].filter(e=>this.engine instanceof e.engineClass).find(t=>t.canLoad(e));if(t){const i=Date.now(),s=t.load(e,this.engine.scene,this.engine,this);e.timeSlider=this,e.loaded=!0;const n=e.name||e.constructor.name+"#"+e.id.slice(e.id.length-4).toUpperCase();console.log(`Loaded ${n} in ${l(Date.now()-i)}`);const a=this.syncMaxValuesForLayer(e);this.layerUnloaders.set(e.id,()=>{a(),s(),e.loaded=!1})}else console.warn("No loader found for",{layer:e})}}unloadLayer(e){this.selectedLayerId===e.id&&this.select(null),this.layerUnloaders.has(e.id)&&(this.layerUnloaders.get(e.id)(),this.layerUnloaders.delete(e.id))}addLayer(e){if(this.layers.push(e),e.visible){this.loadLayer(e);const t=e.model.getTimeRange();if(1===this.getVisibleLayers().length)this.setTimeRange(t);else{const e=E(this.getTimeRange(),t);this.setTimeRange(e)}}const t=[e.on("visible",()=>{e.visible?this.loadLayer(e):this.unloadLayer(e),this.updateAllValueRanges()}),e.on("min",()=>this.updateAllValueRanges()),e.on("max",()=>this.updateAllValueRanges())];this.layerListenerStoppers.set(e.id,()=>t.forEach(e=>e()))}removeLayer(e){const t="string"==typeof e?e:e.id,i=this.layers.find(e=>e.id===t);i&&(this.unloadLayer(i),this.layerUnloaders.delete(t),this.layerListenerStoppers.get(t)?.(),this.layerListenerStoppers.delete(t),this.layers=this.layers.filter(e=>e.id!==t))}destroy(){this.layers.map(e=>e.id).forEach(e=>this.removeLayer(e)),this.onDestroy.forEach(e=>e())}addHorizontalPinchZoom(){let e=null,t=null;const i=()=>{this.pinching=!1,e=null,t=null},s=i=>{if(2===i.touches.length){i.preventDefault(),this.dragging=!1,this.pinching=!0;const s=this.mouseInSlider(i.touches[0]).t,n=this.mouseInSlider(i.touches[1]).t;e=Math.min(s,n),t=Math.max(s,n)}},n=s=>{if(2===s.touches.length){if(null===e||null===t)return;s.preventDefault(),this.dragging=!1;const i=this.mouseInSlider(s.touches[0]).normalized.x,n=this.mouseInSlider(s.touches[1]).normalized.x,l=Math.min(i,n),a=Math.max(i,n),o=(t-e)/(a-l);let r=e-l*o,h=t+(1-a)*o;if(o>_){const e=.5*(r+h);r=e-.5*_,h=e+.5*_}if(o<H){const e=.5*(r+h);r=e-.5*H,h=e+.5*H}this.setTimeRange({startTime:r,endTime:h})}else i()},l=()=>{i()};this.sliderElement.addEventListener("touchstart",s,{passive:!1}),this.sliderElement.addEventListener("touchmove",n,{passive:!1}),this.sliderElement.addEventListener("touchend",l),this.sliderElement.addEventListener("touchcancel",l),this.onDestroy.push(()=>{this.sliderElement.removeEventListener("touchstart",s),this.sliderElement.removeEventListener("touchmove",n),this.sliderElement.removeEventListener("touchend",l),this.sliderElement.removeEventListener("touchcancel",l)})}initControls(){const e=new t;this.dragging=!1;let i,s=0,n=0,l=0;e.add(this.initHoverListener());const a=e=>{this.sliderElement.focus(),e.preventDefault();const t=this.mouseInSlider(e);this.zoom(.001*e.deltaY,{x:t.normalized.x})};let o=0,r=-1;const h=e=>{const t=Date.now(),i=t-o;r>=0&&i<300&&i>0&&(this.select(null),e.preventDefault(),this.zoom(-.75,{animate:1e3,x:r})),o=t},d=e=>{e.preventDefault(),this.select(null);const t=this.mouseInSlider(e);this.zoom(-.75,{animate:1e3,x:t.normalized.x})};this.selectingRangeElement.style.height="calc(100% - 6px)",this.selectingRangeElement.style.pointerEvents="none",this.selectingRangeElement.style.position="absolute",this.selectingRangeElement.style.border="3px solid #df0",this.selectingRangeElement.style.background="#df04",this.selectingRangeElement.style.top="0px";const m=e=>{if(this.sliderElement.focus(),2===e.button||0===e.button){e.preventDefault();const t=this.mouseInSlider(e),i=t.normalized.x,{startTime:a,duration:o}=this.getTimeRange();s=a+i*o,n=Date.now(),l=t.absolute.x,(e.ctrlKey||2===e.button)&&(this.selectingRange=!0),this.selectingRange?(this.selectingRangeElement.style.left=t.absolute.x+"px",this.selectingRangeElement.style.width="0px",this.container.appendChild(this.selectingRangeElement),this.selectingRangeVisible=!0):this.dragging=!0}},c=e=>{const t=this.mouseInSlider(e);return i=t.normalized.x,t},u=e=>{const t=c(e);if(this.selectingRange){const e=Math.abs(l-t.absolute.x),i=Math.min(l,t.absolute.x);this.selectingRangeElement.style.left=i+"px",this.selectingRangeElement.style.width=e-6+"px"}if(!this.dragging||Date.now()-n<100&&Math.abs(l-t.absolute.x)<10)return;e.preventDefault();const i=t.normalized.x,{startTime:a,endTime:o}=this.getTimeRange(),r=a+i*(o-a)-s;this.setTimeRange({startTime:a-r,endTime:o-r})},g=e=>{const t=this.getSliderBounds(),n=t.height-(e.clientY-t.top);if(!this.dragging&&!this.pinching&&n<8)this.ticksBar.click(e);else if(i=void 0,this.dragging=!1,this.selectingRange){const t=this.mouseInSlider(e).normalized.x,{startTime:i,endTime:n}=this.getTimeRange(),l=i+t*(n-i);"zoom"===this.selectRangeMode&&this.fit({range:{startTime:Math.min(s,l),endTime:Math.max(s,l)},animate:1e3}),"select"===this.selectRangeMode&&this.select(null,{startTime:Math.min(s,l),endTime:Math.max(s,l)}),"createSelection"===this.selectRangeMode&&this.setSelection([...this.getSelection(),{startTime:Math.min(s,l),endTime:Math.max(s,l)}])}this.stopSelectingRange()},p=e=>{this.pressedKeys.add(e.code)},y=e=>{this.pressedKeys.delete(e.code),"Space"===e.code&&(e.preventDefault(),this.togglePlaying()),"KeyF"===e.code&&(e.preventDefault(),this.fit({animate:3e3}));const t=void 0;"ArrowRight"===e.code&&(e.preventDefault(),this.skip(1,{animate:t})),"ArrowLeft"===e.code&&(e.preventDefault(),this.skip(-1,{animate:t})),"ArrowUp"===e.code&&(e.preventDefault(),this.up({animate:t})),"ArrowDown"===e.code&&(e.preventDefault(),this.down({xRatio:i,animate:t}))};let v=!0;const T=()=>{if(v){const e=this.pressedKeys.has("ShiftLeft")?.05:.01,t=2*e,s=this.pressedKeys.has("KeyD"),n=this.pressedKeys.has("KeyA");s&&this.shift(-e),n&&this.shift(e),this.pressedKeys.has("KeyW")&&this.zoom(-t,{x:s||n?void 0:i}),this.pressedKeys.has("KeyS")&&this.zoom(t,{x:s||n?void 0:i}),requestAnimationFrame(T)}};T(),e.add(()=>v=!1);const f=e=>e.preventDefault();this.sliderElement.addEventListener("contextmenu",f),this.sliderElement.addEventListener("wheel",a),this.sliderElement.addEventListener("pointerdown",m),this.sliderElement.addEventListener("pointermove",u),this.sliderElement.addEventListener("pointerup",g),this.sliderElement.addEventListener("dblclick",d),this.sliderElement.addEventListener("touchend",h),this.sliderElement.addEventListener("touchstart",e=>{if(1===e.touches.length){const t=this.mouseInSlider(e.touches[0]);r=t.normalized.x}else r=-1}),this.sliderElement.addEventListener("keydown",p),this.sliderElement.addEventListener("keyup",y),this.addHorizontalPinchZoom();const E=()=>{this.sliderElement.removeEventListener("contextmenu",f),this.sliderElement.removeEventListener("wheel",a),this.sliderElement.removeEventListener("pointerdown",m),this.sliderElement.removeEventListener("pointermove",u),this.sliderElement.removeEventListener("pointerup",g),this.sliderElement.removeEventListener("dblclick",d),this.sliderElement.removeEventListener("touchend",h),this.sliderElement.removeEventListener("keydown",p),this.sliderElement.removeEventListener("keyup",y)};return this.onDestroy.push(E),e.add(E),e.disposeLater()}shift(e,t){const{startTime:i,endTime:s,duration:n}=this.getTimeRange(),l=t?.absolute?e:e*n;this.fit({...t,range:{startTime:i-l,endTime:s-l}})}zoom(e,t){const i="number"==typeof t?.x?t.x:.5,{startTime:s,endTime:n,duration:l}=this.getTimeRange(),a={startTime:s-e*l*i,endTime:n+e*l*(1-i)},o=a.endTime-a.startTime,r=a.startTime+.5*o;o>_&&(a.startTime=r-.5*_,a.endTime=r+.5*_),o<H&&(a.startTime=r-.5*H,a.endTime=r+.5*H),this.fit({range:a,animate:t?.animate})}togglePlaying(){this.playing?this.pause():this.play()}isPlaying(){return this.playing}play(){if(this.playing)return;this.playing=!0,this.emit("playing",!0);let e=Date.now();const t=()=>{if(this.playing){if(this.live){const{duration:e}=this.getTimeRange(),t=Date.now(),i=10/this.size.width;this.setTimeRange({startTime:t-(1-i)*e,endTime:t+i*e}),this.setCurrent(t)}else if("auto"!==this.playSpeed||this.showCurrent){const t=Date.now(),i=t-e,s=this.current-this.getTimeRange().startTime,n="auto"===this.playSpeed?1:this.playSpeed;this.shift(-n*i,{absolute:!0}),this.setCurrent(this.getTimeRange().startTime+s),e=t}else{const t=Date.now();let i=5e-5*(t-e);this.pressedKeys.has("ShiftLeft")&&(i*=5);const s=this.current-this.getTimeRange().startTime;this.shift(-i),this.setCurrent(this.getTimeRange().startTime+s),e=t}requestAnimationFrame(t)}};t()}setCurrent(e){this.current!==e&&(Math.abs(e-Date.now())>100&&this.live&&(this.pause(),this.setLive(!1)),this.current=e,this.emit("current",e))}pause(){this.playing&&(this.playing=!1,this.emit("playing",!1),this.setLive(!1))}setLive(e){if(this.live!==e){if(this.live=e,e){const e=this.getTimeRange();this.fit({range:{startTime:e.endTime-this.liveRange,endTime:e.endTime},animate:D}),this.play()}this.emit("live",e)}}}function $(e){return{startTime:"number"==typeof e?.startTime?e.startTime:"number"==typeof e?.endTime?e.duration?e.endTime-e.duration:e.endTime-C:e?.duration?Date.now()-.5*e.duration:Date.now()-.5*C,endTime:"number"==typeof e?.endTime?e.endTime:"number"==typeof e?.startTime?e.duration?e.startTime+e.duration:e.startTime+C:e?.duration?Date.now()+.5*e.duration:Date.now()+.5*C,duration:e?.duration?e.duration:"number"==typeof e?.startTime&&"number"==typeof e?.endTime?e.endTime+e.startTime:C,minValue:"number"==typeof e?.minValue?e.minValue:"number"==typeof e?.maxValue?e.maxValue>0?0:e.maxValue-100:0,maxValue:"number"==typeof e?.maxValue?e.maxValue:"number"==typeof e?.minValue?e.minValue<0?0:e.minValue+100:100}}function U(e){const t=e/315576e5,i=Math.log10(t),s=Math.ceil(i);return Math.pow(10,s)}
|
|
1
|
+
import{v4 as e}from"uuid";import{Disposables as t}from"../common/Disposables.js";import{animateRangeTransition as i}from"../common/Easing.js";import{formatDate as s,formatDateRange as n,formatDuration as l,getDefaultLocale as a,getLargeTimeUnitRange as o,getMatchingTimeUnit as r,getTimeUnitRange as h,setDefaultLocale as d}from"../common/TimeTools.js";import{TimeSliderDynamicHistogram as m}from"../data/histogram/TimeSliderDynamicHistogram.js";import{getTimeArray as c}from"../data/TimeSliderElement.js";import{TimeSliderIntervalsModel as g}from"../data/TimeSliderIntervalsModel.js";import{addDuration as u,addPadding as p,applyTimeRangeUpdate as y,clampTimeRange as v,mergeTimeRanges as T,timeRangesOverlap as f,timeRangesUnion as E}from"../data/TimeSliderRange.js";import{TypedEventEmitter as L}from"../event/TypedEventEmitter.js";import{TimeSliderHistogramLayer as x}from"../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as R}from"../layer/TimeSliderIntervalsLayer.js";import{CENTURY as b,DECADE as w,MILLENIUM as S,ONE_DAY as C,ONE_HOUR as V,ONE_MINUTE as k,ONE_MONTH as M,ONE_SECOND as B,ONE_WEEK as D,ONE_YEAR as z,TODAY as I}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as O}from"../ticks/TimeTicksBar.js";import{YAxisTicks as A}from"../ticks/YAxisTicks.js";const H=B,_=10*S;export class TimeSlider extends L{getLayers(){return[...this.layers]}getVisibleLayers(){return this.getLayers().filter(e=>e.visible)}constructor(i){super(),this.valueRanges=new Map,this.layers=[],this.onDestroy=[],this.ticksBar=new O,this.loaders=new Set,this.layerUnloaders=new Map,this.layerListenerStoppers=new Map,this.layerLoaders=new Map,this.pressedKeys=new Set,this.container=null,this.d=new t,this.id=e(),this.playing=!1,this.live=!1,this._showCurrent=!1,this.current=Date.now(),this.range={time:I,value:{minValue:0,maxValue:100}},this.size={width:100,height:100},this.sliderElement=document.createElement("div"),this.yAxisTicks=new A,this.hoverLine=document.createElement("div"),this.toolTip=document.createElement("div"),this.selectedLabel=document.createElement("div"),this.selectingRangeElement=document.createElement("div"),this.selectionOverlayContainer=document.createElement("div"),this._selectionRanges=[],this.selectionRangeElements=[],this.shadingElements=[],this.columnBandOverlayContainer=document.createElement("div"),this.columnBandElements=[],this.selectingRange=!1,this.selectingRangeVisible=!1,this.updateValueRangesQueue=new Map,this.showingStats=!1,this._engineReady=!1,this.mouse=null,this.updatingLabel=!1,this.hoverCleared=!0,this.dragging=!1,this.pinching=!1,this.hoverOptions=i?.hover||{},this.selectionOptions={highlight:"boolean"!=typeof i?.selection?.highlight||i.selection.highlight,label:"boolean"!=typeof i?.selection?.label||i.selection.label,shading:"boolean"!=typeof i?.selection?.shading||i.selection.shading,invertShading:"boolean"==typeof i?.selection?.invertShading&&i.selection.invertShading,pick:i?.selection?.pick||"element",columnBand:i?.selection?.columnBand},this.showCurrent=i?.showCurrent||!1,this.showTicks=i?.showTicks||!1,this.selectRangeMode=i?.selectRangeMode||"select",this.playSpeed=i?.playSpeed||"auto",this.liveRange=i?.liveRange||10*B,this.theme=i?.theme||"dark",this._locale=i?.locale||a(),d(this._locale),this.ticksBar.setLocale(this._locale),this.ticksBar.setWeekLabel(i?.weekLabel||"default",i?.weekLabelPrefix),this.ticksBar.setTickLabelStyler(i?.tickLabelStyler),this.setLimits(i?.limits),this.syncMaxValues(),this.on("time",()=>this.renderSelectionOverlay()),this.on("size",()=>this.renderSelectionOverlay())}get locale(){return this._locale}setLocale(e){this._locale=e,d(e),this.ticksBar.setLocale(e),this.emit("locale",e)}setWeekLabel(e,t){this.ticksBar.setWeekLabel(e,t)}setTickLabelStyler(e){this.ticksBar.setTickLabelStyler(e)}get limits(){return this._limits?{...this._limits}:void 0}setLimits(e){!e||e.endTime>e.startTime||(console.warn("Ignoring time slider limits whose endTime is not after startTime",e),e=void 0),this._limits=e?{startTime:e.startTime,endTime:e.endTime}:void 0,this._limits&&this.setTimeRange(this.getTimeRange())}setSelection(e){this._selectionRanges=e.map(e=>({...e})),this.renderSelectionOverlay(!0),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"})}getSelection(){return this._selectionRanges.map(e=>({...e}))}clearSelection(){this.setSelection([])}setPickMode(e){this.selectionOptions.pick=e,this.updateHover()}selectionTimeToX(e,t,i,s){return e===-1/0?0:e===1/0?s:Math.min(s,Math.max(0,(e-t)/(i-t)*s))}renderSelectionOverlay(e=!1){if(!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>this.selectionTimeToX(e,i,s,t.width);if((e||this.selectionRangeElements.length!==this._selectionRanges.length||this.selectionRangeElements.some((e,t)=>e.range!==this._selectionRanges[t]))&&(this.selectionRangeElements.forEach(({body:e,startHandle:t,endHandle:i})=>{e.remove(),t?.remove(),i?.remove()}),this.selectionRangeElements=this._selectionRanges.map(e=>this.createSelectionRangeElements(e))),this.selectionRangeElements.forEach(({range:e,body:t,startHandle:i,endHandle:s})=>{const l=n(e.startTime),a=n(e.endTime);t.style.left=l+"px",t.style.width=Math.max(0,a-l)+"px",i&&(i.style.left=l-4+"px"),s&&(s.style.left=a-4+"px")}),this.shadingElements.forEach(e=>e.remove()),this.shadingElements=[],this.selectionOptions.shading&&this._selectionRanges.length>0){const e=T(this._selectionRanges).map(e=>[n(e.startTime),n(e.endTime)]);(this.selectionOptions.invertShading?e:function(e,t){const i=[];let s=0;for(const[t,n]of e)t>s&&i.push([s,t]),s=Math.max(s,n);s<t&&i.push([s,t]);return i}(e,t.width)).forEach(([e,t])=>{if(t<=e)return;const i=document.createElement("div");i.style.position="absolute",i.style.top="0px",i.style.height="100%",i.style.left=e+"px",i.style.width=t-e+"px",i.style.background="rgba(0, 0, 0, 0.5)",i.style.pointerEvents="none",this.selectionOverlayContainer.appendChild(i),this.shadingElements.push(i)})}}renderColumnHoverBand(e){if(this.columnBandElements.forEach(e=>e.remove()),this.columnBandElements=[],"column"!==this.selectionOptions.pick||0===e.length||!this.container)return;const t=this.getSliderBounds();if(t.width<=0)return;const{startTime:i,endTime:s}=this.getTimeRange(),n=e=>Math.min(t.width,Math.max(0,(e-i)/(s-i)*t.width)),l=this.selectionOptions.columnBand?.color??"#fff",a=this.selectionOptions.columnBand?.opacity??.15;e.forEach(({layer:e,elements:t})=>{const i=t.map(c),s=Math.min(...i.map(e=>e[0])),o=Math.max(...i.map(e=>e[1])),r=document.createElement("div");r.style.position="absolute",r.style.left=n(s)+"px",r.style.width=Math.max(1,n(o)-n(s))+"px",r.style.top=100*(1-e.max)+"%",r.style.height=100*(e.max-e.min)+"%",r.style.background=l,r.style.opacity=String(a),r.style.pointerEvents="none",this.columnBandOverlayContainer.appendChild(r),this.columnBandElements.push(r)})}createSelectionRangeElements(e){const t=document.createElement("div");t.style.position="absolute",t.style.top="0px",t.style.height="100%",t.style.background="rgba(100, 180, 255, 0.15)",t.style.borderTop="2px solid rgba(100, 180, 255, 0.8)",t.style.borderBottom="2px solid rgba(100, 180, 255, 0.8)",t.style.boxSizing="border-box",t.style.cursor="grab",t.style.pointerEvents="auto",t.style.touchAction="none",this.selectionOverlayContainer.appendChild(t);const i=()=>{const e=document.createElement("div");return e.style.position="absolute",e.style.top="0px",e.style.height="100%",e.style.width="8px",e.style.cursor="ew-resize",e.style.pointerEvents="auto",e.style.touchAction="none",e.style.background="rgba(100, 180, 255, 0.9)",this.selectionOverlayContainer.appendChild(e),e},s=e.startTime===-1/0?null:i(),n=e.endTime===1/0?null:i();return this.wireSelectionDrag(e,t,s,n),{range:e,body:t,startHandle:s,endHandle:n}}wireSelectionDrag(e,t,i,s){const n=(e,t)=>{e.addEventListener("pointerdown",i=>{i.stopPropagation(),i.preventDefault(),e.setPointerCapture(i.pointerId);const s=t(i),n=e=>{s(e),this.renderSelectionOverlay(),this.emit("selectionchange",{ranges:this.getSelection(),phase:"drag"})};let l=!1;const a=t=>{l||(l=!0,e.hasPointerCapture(t.pointerId)&&e.releasePointerCapture(t.pointerId),e.removeEventListener("pointermove",n),e.removeEventListener("pointerup",a),e.removeEventListener("pointercancel",a),e.removeEventListener("lostpointercapture",a),this.emit("selectionchange",{ranges:this.getSelection(),phase:"end"}))};this.emit("selectionchange",{ranges:this.getSelection(),phase:"start"}),e.addEventListener("pointermove",n),e.addEventListener("pointerup",a),e.addEventListener("pointercancel",a),e.addEventListener("lostpointercapture",a)})};i&&n(i,()=>t=>{const{t:i}=this.mouseInSlider(t);e.startTime=e.endTime===1/0?i:Math.min(i,e.endTime)}),s&&n(s,()=>t=>{const{t:i}=this.mouseInSlider(t);e.endTime=e.startTime===-1/0?i:Math.max(i,e.startTime)}),n(t,t=>{const i=this.mouseInSlider(t).t,s=e.startTime,n=e.endTime;return t=>{const l=this.mouseInSlider(t).t-i;s===-1/0&&n!==1/0?e.endTime=n+l:n===1/0&&s!==-1/0?e.startTime=s+l:s!==-1/0&&n!==1/0&&(e.startTime=s+l,e.endTime=n+l)}})}startSelectingRange(e){this.selectingRange=!0,this.whenDoneSelectingRange=e}stopSelectingRange(){this.selectingRange=!1,this.whenDoneSelectingRange?.(),this.whenDoneSelectingRange=void 0,this.selectingRangeVisible&&(this.container.removeChild(this.selectingRangeElement),this.selectingRangeVisible=!1)}syncMaxValues(){return this.updateAllValueRanges(),this.on("time",()=>this.updateAllValueRanges())}syncMaxValuesForLayer(e){return this.updateValueRanges(e.yAxisGroup),e.model.on(()=>this.updateValueRanges(e.yAxisGroup))}updateAllValueRanges(){this.layers.filter(e=>e.visible).forEach(e=>{this.updateValueRanges(e.yAxisGroup,!0)}),this.renderYAxis()}updateValueRanges(e,t){this.updateValueRangesQueue.has(e)||this.updateValueRangesQueue.set(e,{lastUpdate:0,pending:void 0});const i=this.updateValueRangesQueue.get(e);if(Date.now()-i.lastUpdate<100)return i.pending&&clearTimeout(i.pending),void(i.pending=setTimeout(()=>{i.pending=void 0,this.updateValueRanges(e)},200));i.lastUpdate=Date.now();const s=new Map,n=new Map;this.layers.filter(e=>e.visible).forEach(e=>{const t=e.yAxis.group||e.id;s.has(t)||(s.set(t,-Number.MAX_VALUE),n.set(t,Number.MAX_VALUE));const i=e.model.getValueRange(this.getTimeRange());i.minValue<n.get(t)&&n.set(t,i.minValue),i.maxValue>s.get(t)&&s.set(t,i.maxValue)}),this.valueRanges.clear(),s.forEach((e,t)=>{this.valueRanges.set(t,{max:e,min:n.get(t)})}),t||this.renderYAxis(),this.emit("valueRanges",e)}renderYAxis(){const e=new Map;this.layers.filter(e=>e.visible).forEach(t=>{const i=t.yAxis.group||t.id;e.has(i)||e.set(i,{min:t.min,max:t.max,group:i}),e.set(i,{...e.get(i),...t.yAxis,min:t.min,max:t.max})});const t=[];e.forEach(e=>{if(e.visible){const i=this.valueRanges.get(e.group);i&&t.push({...e,minValue:i.min,maxValue:i.max})}}),this.yAxisTicks.render(t)}setTheme(e){this.theme=e,this.ticksBar.setDarkMode("dark"===e),this.engine?.setTheme(e)}addLayerLoader(e){this.loaders.add(e)}showStats(e){this.showingStats=e,this.engine?.showStats(e)}get engineReady(){return this._engineReady}whenReady(){return this._engineReady?Promise.resolve():new Promise(e=>{const t=this.on("ready",()=>{t(),e()})})}async setEngine(e){this._engineReady=!1,this.engine&&(this.layerUnloaders.forEach(e=>e()),this.layerUnloaders.clear(),this.engine.dispose()),this.initSliderContainer(),this.engine=e,this.engine.setTheme(this.theme),await this.engine.init(this.sliderElement),this.engine.setTheme(this.theme),this._engineReady=!0,this.emit("ready"),this.getVisibleLayers().forEach(e=>this.loadLayer(e)),this.engine.showStats(this.showingStats),this.engine.setTimeRange(this.getTimeRange()),this.engine.setValueRange(this.getValueRange()),this.engine.setSize(this.size.width,this.size.height)}initSliderContainer(){this.stopControls&&this.stopControls(),this.container?.removeChild(this.sliderElement);let e=0;this.showTicks&&(e+=24),this.showCurrent&&(e+=4),this.sliderElement=document.createElement("div"),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top="0px",this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${0+e}px)`,this.container?.appendChild(this.sliderElement),this.initSelectLabel(),this.stopControls=this.initControls(),this.onDestroy.push(()=>{this.container?.removeChild(this.sliderElement)})}get showCurrent(){return this._showCurrent}set showCurrent(e){if(e!==this._showCurrent){this._showCurrent=e,this.ticksBar.showLine=!this.showCurrent;let t=0,i=0;this.showTicks&&(i+=24),this.showCurrent&&(i+=4),this.sliderElement.style.height=`calc(100% - ${t+i}px)`}}async init(e,t){const i="string"==typeof e?document.getElementById(e):e;if(this.container=i,i){i.oncontextmenu=()=>!1;let e=0,s=0;if(this.showTicks){s+=24;const e=document.createElement("div");e.style.width="100%",e.style.height="24px",e.style.position="absolute",e.style.bottom="0px",e.style.touchAction="none",i.appendChild(e),this.ticksBar.init(e,e=>{const t=r(e.startTime,e.endTime);this.emit("tickclick",{startTime:e.startTime,endTime:e.endTime,unit:t}),this.fit({range:e,animate:1e3})}),this.ticksBar.showLine=!this.showCurrent}this.showCurrent&&(s+=4),this.sliderElement.oncontextmenu=()=>!1,this.sliderElement.tabIndex=0,this.sliderElement.style.top=`${e}px`,this.sliderElement.style.width="100%",this.sliderElement.style.outline="none",this.sliderElement.style.touchAction="none",this.sliderElement.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.sliderElement),this.yAxisTicks.canvas.style.top=`${e}px`,this.yAxisTicks.canvas.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.yAxisTicks.canvas),this.selectionOverlayContainer.style.position="absolute",this.selectionOverlayContainer.style.width="100%",this.selectionOverlayContainer.style.overflow="hidden",this.selectionOverlayContainer.style.pointerEvents="none",this.selectionOverlayContainer.style.top=`${e}px`,this.selectionOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.selectionOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.selectionOverlayContainer)),this.columnBandOverlayContainer.style.position="absolute",this.columnBandOverlayContainer.style.width="100%",this.columnBandOverlayContainer.style.overflow="hidden",this.columnBandOverlayContainer.style.pointerEvents="none",this.columnBandOverlayContainer.style.top=`${e}px`,this.columnBandOverlayContainer.style.height=`calc(100% - ${e+s}px)`,i.appendChild(this.columnBandOverlayContainer),this.onDestroy.push(()=>i.removeChild(this.columnBandOverlayContainer)),this.initHoverTooltip(),this.initSelectLabel(),this.setSize(this.sliderElement.clientWidth,this.sliderElement.clientHeight),this.syncSize(),this.setTimeRange($(t)),this.setValueRange($(t))}else console.warn(`Element ${e} not found`)}initSelectLabel(){this.selectedLabel.style.position="absolute",this.selectedLabel.style.padding="4px 8px",this.selectedLabel.style.background="rgba(0, 0, 0, 0.75)",this.selectedLabel.style.color="white",this.selectedLabel.style.fontSize="12px",this.selectedLabel.style.maxWidth="200px",this.selectedLabel.style.fontFamily="monospace",this.selectedLabel.style.borderRadius="4px",this.selectedLabel.style.pointerEvents="none",this.selectedLabel.style.display="none",this.selectedLabel.style.zIndex="10",this.sliderElement.appendChild(this.selectedLabel)}getLabel(e,t,i,s){if(t.labeler){const n=t.labeler(e,i,s);return null===n?null:"string"==typeof n?`<div>${n}</div>`:n?n.outerHTML:`<div>${e.name||e.id}</div>`}return`<div>${e.name||e.id}</div>`}getLayerById(e){return this.layers.find(t=>t.id===e)}select(e,t){this.stopSelectingRange(),this.stopUpdatingLabel?.();const i=()=>{this.updatingLabel=!1,this.stopUpdatingLabel?.(),this.stopUpdatingLabel=void 0,this.selectedLabel.style.display="none",this.layers.forEach(e=>e.select([])),this.selectedLayerId=void 0,this.emit("select",[])},s=e=>{this.layers.filter(t=>!e.some(e=>t.id===e.layer.id)).forEach(e=>e.select([])),e.forEach(e=>{e.layer.select(e.elements)}),this.emit("select",e)};if(t){s(this.getVisibleLayers().map(e=>{const i=e.model.dataInTimeRange(t);return{layer:e,elements:i}}).filter(e=>e.elements.length>0))}else if(e){const t=this.findElements(e,24);if(s(t.map(e=>({layer:e.layer,elements:e.elements})).flat()),1===t.length&&t[0].elements.length>0&&this.selectionOptions.label){const e=t[0].elements[0],s=t[0].layer;this.selectedLabel.innerHTML=this.getLabel(e,s,t[0].t,t[0].v)||"",this.selectedLabel.style.display="block";const n=this.selectedLabel.offsetWidth,l=this.selectedLabel.offsetHeight;this.selectedLayerId=s.id;const a=Array.isArray(e.t);this.updatingLabel=!0;const o=()=>{if(!this.updatingLabel)return;const{startTime:i,duration:s,endTime:o}=this.getTimeRange(),r=t[0].layer.model.get(e.id);if(r){const e=f(this.getTimeRange(),r.t);if(e){const o=this.getSliderBounds();let h=(.5*(e.startTime+e.endTime)-i)/s*o.width;const d="number"==typeof r.v?r.v:.5*(r.v[1]+r.v[0]);let m=this.modelValueToPixels(d,t[0].layer);a?m-=.5*l:m+=8;const c=.5*n,g=.5*l,u=o.width-.5*n,p=o.height-.5*l,y=Math.min(u,Math.max(c,h)),v=Math.min(p,Math.max(g,m));this.selectedLabel.style.display="block",this.selectedLabel.style.left=y-.5*n+"px",this.selectedLabel.style.top=`${v}px`}else this.selectedLabel.style.display="none"}else this.selectedLabel.style.display="none"};o();const r=s instanceof x&&s.histogram instanceof m;this.stopUpdatingLabel=this.on("time",r?i:o)}}else i()}listenToClicks(){let e=null;const t=t=>{e={x:t.clientX,y:t.clientY}},i=t=>{if(!e)return;const i=t.clientX-e.x,s=t.clientY-e.y;i*i+s*s<=25&&(this.stopSelectingRange(),this.select(e)),e=null},s=()=>{e=null};return this.sliderElement.addEventListener("pointerdown",t),this.sliderElement.addEventListener("pointerup",i),this.sliderElement.addEventListener("pointercancel",s),()=>{this.sliderElement.removeEventListener("pointerdown",t),this.sliderElement.removeEventListener("pointerup",i),this.sliderElement.removeEventListener("pointercancel",s)}}getSliderBounds(){return this.sliderElement.getBoundingClientRect()}mouseInSlider(e){const t=this.getSliderBounds(),i="x"in e?e:{x:e.clientX,y:e.clientY},s={x:i.x-t.left,y:i.y-t.top},n={x:s.x/t.width,y:-s.y/t.height+1},{startTime:l,duration:a}=this.getTimeRange(),o=l+n.x*a,{minValue:r,maxValue:h}=this.getValueRange();return{absolute:s,normalized:n,t:o,v:r+n.x*(h-r)}}pixelsToLayerValue(e,t){if(!e)return{yInLayer:0,toLayerHeight:1};const i="normalized"in e?e.normalized:this.mouseInSlider(e).normalized;let s=t.min;const n=t.max-s;return{yInLayer:(i.y-s)/n,toLayerHeight:n/this.getSliderBounds().height}}pixelsToModelValue(e,t){const{yInLayer:i,toLayerHeight:s}=this.pixelsToLayerValue(e,t);let n=0,l=100;if(t.model instanceof g){const e=this.valueRanges.get(t.yAxisGroup)||{min:0,max:1e3};n=e.min,l=e.max-n}return{yInModel:n+i*l,toModelHeight:s*l}}layerValueToPixels(e,t){let i=t.min;return(1-(i+(t.max-i)*e))*this.getSliderBounds().height}modelValueToPixels(e,t){let i=0,s=100;if(t.model instanceof g){const e=t.model.getValueRange(this.getTimeRange());i=e.minValue,s=e.maxValue-i}const n=(e-i)/s;return this.layerValueToPixels(n,t)}findElements(e,t){if("column"===this.selectionOptions.pick)return this.findColumnElements(e);const{width:i,height:s}=this.getSliderBounds(),{normalized:n}=this.mouseInSlider(e),{startTime:l,endTime:a,duration:o}=this.getTimeRange(),r=l+n.x*o,h=t||12,d=h/i*o,m={startTime:r-1,endTime:r+1},c={startTime:r-.5*d,endTime:r+.5*d};return this.getVisibleLayers().map(e=>{const{yInModel:t,toModelHeight:i}=this.pixelsToModelValue({normalized:n},e),s=e instanceof R?0:h*i,l=e instanceof R?m:c,a={minValue:t-.5*s,maxValue:t+.5*s},o=e.model.dataInRange({...l,...a});return{layer:e,elements:o,t:r,v:t}}).filter(e=>e.elements.length>0)}findColumnElements(e,t){const{width:i}=this.getSliderBounds(),{normalized:s}=this.mouseInSlider(e),{startTime:n,duration:l}=this.getTimeRange(),a=n+s.x*l,o=(t||12)/i*l,r={startTime:a-1,endTime:a+1},h={startTime:a-.5*o,endTime:a+.5*o};return this.getVisibleLayers().map(e=>{const{yInModel:t}=this.pixelsToModelValue({normalized:s},e),i=e instanceof R?r:h,n=e.model.dataInTimeRange(i);return{layer:e,elements:n,t:a,v:t}}).filter(e=>e.elements.length>0)}clearHoverEvent(){this.hoverCleared||(this.hoverCleared=!0,this.renderColumnHoverBand([]),this.emit("hover",null))}updateHover(e){if(this.dragging)return this.hoverLine.style.display="none",this.toolTip.style.display="none",void this.clearHoverEvent();if(void 0!==e&&(this.mouse=e),this.mouse){const e=this.getSliderBounds(),{normalized:t}=this.mouseInSlider(this.mouse),{startTime:i,endTime:n,duration:l}=this.getTimeRange(),a=i+t.x*l,o=this.findElements(this.mouse);this.renderColumnHoverBand(o),this.hoverCleared=!1,this.emit("hover",{elements:o.map(e=>({layer:e.layer,elements:e.elements})),t:a,clientX:this.mouse.x,clientY:this.mouse.y});const r=o.map(e=>e.elements.map(t=>({e:t,l:e.layer,t:e.t,v:e.v}))).flat();let h;if(r.length>0)if(r.length>3){h=r.slice(0,2).map(e=>this.getLabel(e.e,e.l,e.t,e.v)).flat().filter(e=>null!==e).join(""),h+=`<div>And ${r.length-2} more...</div>`}else{h=o.map(e=>e.elements.map(t=>this.getLabel(t,e.layer,e.t,e.v))).flat().filter(e=>null!==e).join("")}if(!1===this.hoverOptions.tooltip)this.toolTip.style.display="none";else{const t=new Date(a),i=t.getHours(),n=t.getMinutes(),l=t.getSeconds(),o=s(a,!0,this._locale),r=`${(i<10?"0":"")+i}:${(n<10?"0":"")+n}:${(l<10?"0":"")+l}`;let d="";const m=this.ticksBar.cache.high.spacing;(void 0===this.hoverOptions.date?m>=V:this.hoverOptions.date)&&(d+=`<div>${o}</div>`),(void 0===this.hoverOptions.time?m<=C:this.hoverOptions.time)&&(d+=`<div>${r}</div>`),h&&(d+=h),this.toolTip.innerHTML=d,this.toolTip.style.display=d.length>0?"block":"none";const c=88,g=36;let u=this.mouse.x+10,p=this.mouse.y+10;const y=e.left+e.width-c-5,v=e.top+e.height-g-5;u>y&&(u=y),p>v&&(p=v),this.toolTip.style.left=`${u}px`,this.toolTip.style.top=`${p}px`}}else this.hoverLine.style.display="none",this.toolTip.style.display="none",this.clearHoverEvent()}initHoverTooltip(){this.toolTip.style.position="absolute",this.toolTip.style.padding="4px 8px",this.toolTip.style.background="rgba(0, 0, 0, 0.75)",this.toolTip.style.color="white",this.toolTip.style.fontSize="12px",this.toolTip.style.fontFamily="monospace",this.toolTip.style.borderRadius="4px",this.toolTip.style.pointerEvents="none",this.toolTip.style.display="none",this.toolTip.style.zIndex="10",this.toolTip.style.overflow="hidden",document.body.appendChild(this.toolTip)}initHoverListener(){const e=()=>{this.updateHover(null)},t=e=>{this.updateHover({x:e.clientX,y:e.clientY})};this.sliderElement.addEventListener("mouseleave",e),this.sliderElement.addEventListener("mousemove",t);const i=this.listenToClicks();return()=>{this.sliderElement.removeEventListener("mouseleave",e),this.sliderElement.removeEventListener("mousemove",t),i()}}syncSize(){const e=()=>{const{offsetWidth:e,offsetHeight:t}=this.sliderElement;this.setSize(e,t)},t=new ResizeObserver(()=>{e()});this.container&&(t.observe(this.container),e()),this.onDestroy.push(()=>{t.disconnect()})}getTimeRange(){return u(this.range.time)}getValueRange(){return this.range.value}setTimeRange(e,t){const i=this.getTimeRange(),s=y(i,e),n=this._limits?v(s,this._limits):s;n.startTime===i.startTime&&n.endTime===i.endTime||(this.range.time=n,this.engine?.setTimeRange(n),this.showTicks&&this.ticksBar.setTimeRange(n),this.emit("time",n),this.updateHover())}setValueRange(e){const t={...this.getValueRange(),...e};this.range.value=t,this.engine?.setValueRange(t),this.emit("value",this.getValueRange()),this.updateHover()}setSize(e,t){this.size={width:e,height:t},this.showTicks&&this.ticksBar.setWidth(e);const i=this.getTimeRange(),s=this.getValueRange();this.engine?.setSize(e,t),this.setTimeRange(i),this.setValueRange(s),this.updateHover(),this.emit("size",{width:e,height:t})}up(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=.5*(t+i);if(s>=.9*S){const e=U(10*s);n=o(l,e)}else{const e=[{threshold:b,unit:"millennium"},{threshold:w,unit:"century"},{threshold:z,unit:"decade"},{threshold:.9*M,unit:"year"},{threshold:D,unit:"month"},{threshold:C,unit:"week"},{threshold:V,unit:"day"},{threshold:k,unit:"hour"},{threshold:B,unit:"minute"},{threshold:-1,unit:"second"}];let t=0;for(;s<.9*e[t].threshold;)t++;const i=e[t].unit;n=h(l,i)}this.fit({...e,range:n})}down(e){const{startTime:t,endTime:i,duration:s}=this.getTimeRange();let n={startTime:0,endTime:Date.now()};const l=t+("number"==typeof e?.xRatio?e.xRatio:.5)*s;if(s>=.9*S){const e=U(s/10);n=o(l,e)}else{let e=0;const t=[{threshold:S,unit:"century"},{threshold:b,unit:"decade"},{threshold:w,unit:"year"},{threshold:z,unit:"month"},{threshold:1.1*M,unit:"week"},{threshold:D,unit:"day"},{threshold:C,unit:"hour"},{threshold:V,unit:"minute"},{threshold:k,unit:"second"}].reverse();for(;s>1.1*t[e].threshold;)e++;const i=t[e].unit;n=h(l,i)}this.fit({...e,range:n})}next(e){this.skip(1,e)}previous(e){this.skip(-1,e)}skip(e,t){let i={startTime:0,endTime:Date.now()};const{startTime:s,endTime:n,duration:l}=this.getTimeRange(),a=.5*(s+n);if(l>=9*S){const e=U(l);i=o(a,e)}else{const t=r(s,n);i=t?h(a,t,Math.round(e)):{startTime:s+e*l,endTime:n+e*l}}this.fit({...t,range:i})}getTimeRangeLabel(){const{startTime:e,endTime:t}=this.getTimeRange(),i=new Date(e);return 0===i.getHours()&&0===i.getMinutes()&&0===i.getSeconds()&&0===i.getMilliseconds()?n([e,t],this._locale):void 0}cancelFit(){this.stopFit?.(),this.stopFit=void 0}fit(e){this.cancelFit();const t=e?.range||this.getLayersTimeRange(),s=e?.padding?p(t,e.padding):t;if(e?.animate){const t=this.getTimeRange();this.stopFit=i(t.startTime,t.endTime,s.startTime,s.endTime,e.animate,(e,t)=>this.setTimeRange({startTime:e,endTime:t}))}else this.setTimeRange(s)}getLayersTimeRange(){if(0===this.getVisibleLayers().length)return I;{let e=this.getVisibleLayers()[0].model.getTimeRange();return this.getVisibleLayers().slice(1).forEach(t=>e=E(e,t.model.getTimeRange())),e}}loadLayer(e){if(e.timeSlider&&e.timeSlider.id!==this.id)console.warn("Layer already has been loaded by another time slider, create a new layer with the same model and style if you want to add it to another time slider");else if(this.engine&&this._engineReady&&!this.layerUnloaders.has(e.id)){const t=[...this.loaders].filter(e=>this.engine instanceof e.engineClass).find(t=>t.canLoad(e));if(t){const i=Date.now(),s=t.load(e,this.engine.scene,this.engine,this);e.timeSlider=this,e.loaded=!0;const n=e.name||e.constructor.name+"#"+e.id.slice(e.id.length-4).toUpperCase();console.log(`Loaded ${n} in ${l(Date.now()-i)}`);const a=this.syncMaxValuesForLayer(e);this.layerLoaders.set(e.id,t),this.layerUnloaders.set(e.id,()=>{a(),s(),this.layerLoaders.delete(e.id),e.loaded=!1})}else console.warn("No loader found for",{layer:e})}}unloadLayer(e){this.selectedLayerId===e.id&&this.select(null),this.layerUnloaders.has(e.id)&&(this.layerUnloaders.get(e.id)(),this.layerUnloaders.delete(e.id))}addLayer(e){if(this.layers.push(e),e.visible){this.loadLayer(e);const t=e.model.getTimeRange();if(1===this.getVisibleLayers().length)this.setTimeRange(t);else{const e=E(this.getTimeRange(),t);this.setTimeRange(e)}}const t=[e.on("visible",()=>{e.visible?this.loadLayer(e):this.unloadLayer(e),this.updateAllValueRanges()}),e.on("min",()=>this.onLayerBandChange(e)),e.on("max",()=>this.onLayerBandChange(e))];this.layerListenerStoppers.set(e.id,()=>t.forEach(e=>e()))}onLayerBandChange(e){const t=this.layerLoaders.get(e.id);t&&!t.followsLayerBand&&(this.layerUnloaders.get(e.id)?.(),this.layerUnloaders.delete(e.id),this.loadLayer(e)),this.updateAllValueRanges()}removeLayer(e){const t="string"==typeof e?e:e.id,i=this.layers.find(e=>e.id===t);i&&(this.unloadLayer(i),this.layerUnloaders.delete(t),this.layerListenerStoppers.get(t)?.(),this.layerListenerStoppers.delete(t),this.layers=this.layers.filter(e=>e.id!==t))}destroy(){this.layers.map(e=>e.id).forEach(e=>this.removeLayer(e)),this.onDestroy.forEach(e=>e())}addHorizontalPinchZoom(){let e=null,t=null;const i=()=>{this.pinching=!1,e=null,t=null},s=i=>{if(2===i.touches.length){i.preventDefault(),this.dragging=!1,this.pinching=!0;const s=this.mouseInSlider(i.touches[0]).t,n=this.mouseInSlider(i.touches[1]).t;e=Math.min(s,n),t=Math.max(s,n)}},n=s=>{if(2===s.touches.length){if(null===e||null===t)return;s.preventDefault(),this.dragging=!1;const i=this.mouseInSlider(s.touches[0]).normalized.x,n=this.mouseInSlider(s.touches[1]).normalized.x,l=Math.min(i,n),a=Math.max(i,n),o=(t-e)/(a-l);let r=e-l*o,h=t+(1-a)*o;if(o>_){const e=.5*(r+h);r=e-.5*_,h=e+.5*_}if(o<H){const e=.5*(r+h);r=e-.5*H,h=e+.5*H}this.setTimeRange({startTime:r,endTime:h})}else i()},l=()=>{i()};this.sliderElement.addEventListener("touchstart",s,{passive:!1}),this.sliderElement.addEventListener("touchmove",n,{passive:!1}),this.sliderElement.addEventListener("touchend",l),this.sliderElement.addEventListener("touchcancel",l),this.onDestroy.push(()=>{this.sliderElement.removeEventListener("touchstart",s),this.sliderElement.removeEventListener("touchmove",n),this.sliderElement.removeEventListener("touchend",l),this.sliderElement.removeEventListener("touchcancel",l)})}initControls(){const e=new t;this.dragging=!1;let i,s=0,n=0,l=0;e.add(this.initHoverListener());const a=e=>{this.sliderElement.focus(),e.preventDefault();const t=this.mouseInSlider(e);this.zoom(.001*e.deltaY,{x:t.normalized.x})};let o=0,r=-1;const h=e=>{const t=Date.now(),i=t-o;r>=0&&i<300&&i>0&&(this.select(null),e.preventDefault(),this.zoom(-.75,{animate:1e3,x:r})),o=t},d=e=>{e.preventDefault(),this.select(null);const t=this.mouseInSlider(e);this.zoom(-.75,{animate:1e3,x:t.normalized.x})};this.selectingRangeElement.style.height="calc(100% - 6px)",this.selectingRangeElement.style.pointerEvents="none",this.selectingRangeElement.style.position="absolute",this.selectingRangeElement.style.border="3px solid #df0",this.selectingRangeElement.style.background="#df04",this.selectingRangeElement.style.top="0px";const m=e=>{if(this.sliderElement.focus(),2===e.button||0===e.button){e.preventDefault();const t=this.mouseInSlider(e),i=t.normalized.x,{startTime:a,duration:o}=this.getTimeRange();s=a+i*o,n=Date.now(),l=t.absolute.x,(e.ctrlKey||2===e.button)&&(this.selectingRange=!0),this.selectingRange?(this.selectingRangeElement.style.left=t.absolute.x+"px",this.selectingRangeElement.style.width="0px",this.container.appendChild(this.selectingRangeElement),this.selectingRangeVisible=!0):this.dragging=!0}},c=e=>{const t=this.mouseInSlider(e);return i=t.normalized.x,t},g=e=>{const t=c(e);if(this.selectingRange){const e=Math.abs(l-t.absolute.x),i=Math.min(l,t.absolute.x);this.selectingRangeElement.style.left=i+"px",this.selectingRangeElement.style.width=e-6+"px"}if(!this.dragging||Date.now()-n<100&&Math.abs(l-t.absolute.x)<10)return;e.preventDefault();const i=t.normalized.x,{startTime:a,endTime:o}=this.getTimeRange(),r=a+i*(o-a)-s;this.setTimeRange({startTime:a-r,endTime:o-r})},u=e=>{const t=this.getSliderBounds(),n=t.height-(e.clientY-t.top);if(!this.dragging&&!this.pinching&&n<8)this.ticksBar.click(e);else if(i=void 0,this.dragging=!1,this.selectingRange){const t=this.mouseInSlider(e).normalized.x,{startTime:i,endTime:n}=this.getTimeRange(),l=i+t*(n-i);"zoom"===this.selectRangeMode&&this.fit({range:{startTime:Math.min(s,l),endTime:Math.max(s,l)},animate:1e3}),"select"===this.selectRangeMode&&this.select(null,{startTime:Math.min(s,l),endTime:Math.max(s,l)}),"createSelection"===this.selectRangeMode&&this.setSelection([...this.getSelection(),{startTime:Math.min(s,l),endTime:Math.max(s,l)}])}this.stopSelectingRange()},p=e=>{this.pressedKeys.add(e.code)},y=e=>{this.pressedKeys.delete(e.code),"Space"===e.code&&(e.preventDefault(),this.togglePlaying()),"KeyF"===e.code&&(e.preventDefault(),this.fit({animate:3e3}));const t=void 0;"ArrowRight"===e.code&&(e.preventDefault(),this.skip(1,{animate:t})),"ArrowLeft"===e.code&&(e.preventDefault(),this.skip(-1,{animate:t})),"ArrowUp"===e.code&&(e.preventDefault(),this.up({animate:t})),"ArrowDown"===e.code&&(e.preventDefault(),this.down({xRatio:i,animate:t}))};let v=!0;const T=()=>{if(v){const e=this.pressedKeys.has("ShiftLeft")?.05:.01,t=2*e,s=this.pressedKeys.has("KeyD"),n=this.pressedKeys.has("KeyA");s&&this.shift(-e),n&&this.shift(e),this.pressedKeys.has("KeyW")&&this.zoom(-t,{x:s||n?void 0:i}),this.pressedKeys.has("KeyS")&&this.zoom(t,{x:s||n?void 0:i}),requestAnimationFrame(T)}};T(),e.add(()=>v=!1);const f=e=>e.preventDefault();this.sliderElement.addEventListener("contextmenu",f),this.sliderElement.addEventListener("wheel",a),this.sliderElement.addEventListener("pointerdown",m),this.sliderElement.addEventListener("pointermove",g),this.sliderElement.addEventListener("pointerup",u),this.sliderElement.addEventListener("dblclick",d),this.sliderElement.addEventListener("touchend",h),this.sliderElement.addEventListener("touchstart",e=>{if(1===e.touches.length){const t=this.mouseInSlider(e.touches[0]);r=t.normalized.x}else r=-1}),this.sliderElement.addEventListener("keydown",p),this.sliderElement.addEventListener("keyup",y),this.addHorizontalPinchZoom();const E=()=>{this.sliderElement.removeEventListener("contextmenu",f),this.sliderElement.removeEventListener("wheel",a),this.sliderElement.removeEventListener("pointerdown",m),this.sliderElement.removeEventListener("pointermove",g),this.sliderElement.removeEventListener("pointerup",u),this.sliderElement.removeEventListener("dblclick",d),this.sliderElement.removeEventListener("touchend",h),this.sliderElement.removeEventListener("keydown",p),this.sliderElement.removeEventListener("keyup",y)};return this.onDestroy.push(E),e.add(E),e.disposeLater()}shift(e,t){const{startTime:i,endTime:s,duration:n}=this.getTimeRange(),l=t?.absolute?e:e*n;this.fit({...t,range:{startTime:i-l,endTime:s-l}})}zoom(e,t){const i="number"==typeof t?.x?t.x:.5,{startTime:s,endTime:n,duration:l}=this.getTimeRange(),a={startTime:s-e*l*i,endTime:n+e*l*(1-i)},o=a.endTime-a.startTime,r=a.startTime+.5*o;o>_&&(a.startTime=r-.5*_,a.endTime=r+.5*_),o<H&&(a.startTime=r-.5*H,a.endTime=r+.5*H),this.fit({range:a,animate:t?.animate})}togglePlaying(){this.playing?this.pause():this.play()}isPlaying(){return this.playing}play(){if(this.playing)return;this.playing=!0,this.emit("playing",!0);let e=Date.now();const t=()=>{if(this.playing){if(this.live){const{duration:e}=this.getTimeRange(),t=Date.now(),i=10/this.size.width;this.setTimeRange({startTime:t-(1-i)*e,endTime:t+i*e}),this.setCurrent(t)}else if("auto"!==this.playSpeed||this.showCurrent){const t=Date.now(),i=t-e,s=this.current-this.getTimeRange().startTime,n="auto"===this.playSpeed?1:this.playSpeed;this.shift(-n*i,{absolute:!0}),this.setCurrent(this.getTimeRange().startTime+s),e=t}else{const t=Date.now();let i=5e-5*(t-e);this.pressedKeys.has("ShiftLeft")&&(i*=5);const s=this.current-this.getTimeRange().startTime;this.shift(-i),this.setCurrent(this.getTimeRange().startTime+s),e=t}requestAnimationFrame(t)}};t()}setCurrent(e){this.current!==e&&(Math.abs(e-Date.now())>100&&this.live&&(this.pause(),this.setLive(!1)),this.current=e,this.emit("current",e))}pause(){this.playing&&(this.playing=!1,this.emit("playing",!1),this.setLive(!1))}setLive(e){if(this.live!==e){if(this.live=e,e){const e=this.getTimeRange();this.fit({range:{startTime:e.endTime-this.liveRange,endTime:e.endTime},animate:B}),this.play()}this.emit("live",e)}}}function $(e){return{startTime:"number"==typeof e?.startTime?e.startTime:"number"==typeof e?.endTime?e.duration?e.endTime-e.duration:e.endTime-C:e?.duration?Date.now()-.5*e.duration:Date.now()-.5*C,endTime:"number"==typeof e?.endTime?e.endTime:"number"==typeof e?.startTime?e.duration?e.startTime+e.duration:e.startTime+C:e?.duration?Date.now()+.5*e.duration:Date.now()+.5*C,duration:e?.duration?e.duration:"number"==typeof e?.startTime&&"number"==typeof e?.endTime?e.endTime+e.startTime:C,minValue:"number"==typeof e?.minValue?e.minValue:"number"==typeof e?.maxValue?e.maxValue>0?0:e.maxValue-100:0,maxValue:"number"==typeof e?.maxValue?e.maxValue:"number"==typeof e?.minValue?e.minValue<0?0:e.minValue+100:100}}function U(e){const t=e/315576e5,i=Math.log10(t),s=Math.ceil(i);return Math.pow(10,s)}
|
|
@@ -4,5 +4,7 @@ import { TimeSliderLayer } from "./TimeSliderLayer";
|
|
|
4
4
|
export interface TimeSliderLayerLoader<T extends TimeSliderEngine<any>> {
|
|
5
5
|
engineClass: TimeSliderEngineClass<T>;
|
|
6
6
|
canLoad(layer: TimeSliderLayer): boolean;
|
|
7
|
+
/** True when a loaded layer follows changes to its band (layer.min / layer.max) by itself. Otherwise the slider reloads the layer on such a change. */
|
|
8
|
+
followsLayerBand?: boolean;
|
|
7
9
|
load(layer: TimeSliderLayer, scene: TimeSliderEngineScene<T>, engine: T, timeSlider: TimeSlider): () => void;
|
|
8
10
|
}
|
package/package.json
CHANGED
|
@@ -6,5 +6,6 @@ import { Three3DTimeSliderEngine } from "../Three3DTimeSliderEngine";
|
|
|
6
6
|
export declare class Three3DIntervalsTimeLayerLoader implements TimeSliderLayerLoader<Three3DTimeSliderEngine> {
|
|
7
7
|
engineClass: typeof Three3DTimeSliderEngine;
|
|
8
8
|
canLoad(layer: TimeSliderLayer): boolean;
|
|
9
|
+
followsLayerBand: boolean;
|
|
9
10
|
load(layer: TimeSliderLayer, scene: Scene, engine: Three3DTimeSliderEngine, timeSlider: TimeSlider): () => void;
|
|
10
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BoxGeometry as e,Color as n,DoubleSide as t,InstancedMesh as o,Object3D as i,ShaderMaterial as l,Vector2 as r}from"three";import{Disposables as a}from"../../common/Disposables.js";import{toGLSLColor as s,toGLSLFloat as d,toGLSLVec2 as c}from"../../common/GlslTools.js";import{getTimePartsInHours as m}from"../../common/TimeTools.js";import{TimeSliderSplittingFixedHistogram as f}from"../../data/histogram/TimeSliderSplittingFixedHistogram.js";import{TimeSliderHistogramLayer as v}from"../../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as g}from"../../layer/TimeSliderIntervalsLayer.js";import{Three3DTimeSliderEngine as h}from"../Three3DTimeSliderEngine.js";import{GrowingInstanceMatrix as p}from"./GrowingInstanceMatrix.js";const u=123456;export class Three3DIntervalsTimeLayerLoader{constructor(){this.engineClass=h}canLoad(e){return e instanceof g}load(g,h,x,b){return this.canLoad(g)?function(g,h,x,b){const y=new a,S=g.model,{material:w,dispose:C}=(g.style,function(e,o,i){const g=void 0!==e.styler,h=function(e){return e instanceof v&&void 0!==e.histogram.classifier&&e.histogram instanceof f}(e),p=new a,x=()=>{const n=e.style,t=n.border?n.border.width||1:0,o="boolean"!=typeof n.border?.centered||n.border?.centered;let i="",l="";if(h){const n=e.maxClassifications||4;for(let e=0;e<n;e+=4)i+=`attribute vec4 split${e/4};`,i+=`flat out vec4 vSplit${e/4};`,l+=`vSplit${e/4} = split${e/4};`;console.log("createVertexShader",{splitAttributes:i,maxSplits:n})}return`\n // precision mediump float;\n // precision highp float;\n \n const vec2 layerRange = ${c(e.min,e.max)};\n const bool borderCentered = ${o};\n const float borderWidth = ${d(t)};\n const float infinityDuration = ${d(u)};\n const float msInHour = 3600000.0;\n\n ${i}\n\n uniform vec2 originTime;\n uniform vec2 nowTime;\n uniform vec2 valueRange;\n uniform int selection;\n uniform vec2 worldPerPixel;\n // uniform float split;\n // attribute float split;\n \n ${g?"attribute vec3 borderColor;":""}\n ${g?"varying vec3 fillColor;":""}\n ${g?"varying vec3 strokeColor;":""}\n varying vec2 vUv;\n\n // flat out float vSplit;\n flat out int vSelected;\n flat out int vTopBottom;\n flat out vec2 vSize;\n flat out vec2 vTransform;\n\n void main() {\n ${g?"fillColor = instanceColor;":""}\n ${g?"strokeColor = borderColor;":""}\n vUv = uv;\n // vSplit = split;\n ${l}\n \n bool selected = gl_InstanceID == selection;\n vSelected = selected ? 1 : 0;\n\n float minValue = valueRange.x;\n float valueSize = valueRange.y - minValue;\n\n float width = length(instanceMatrix[0].xyz);\n float height = length(instanceMatrix[1].xyz) / valueSize;\n float depth = length(instanceMatrix[2].xyz);\n\n height *= (layerRange.y - layerRange.x);\n\n if (selected) {\n depth += 0.00001;\n }\n\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n float deltaHours = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n pos.x = relativeTime;\n \n pos.y = layerRange.x + ((pos.y - minValue) / valueSize) * (layerRange.y - layerRange.x);\n pos.z = 1.0 - 0.5 * depth;\n \n if (selected) {\n pos.z += 0.00001;\n }\n\n if (borderCentered) {\n vec3 borderOffset = borderWidth * vec3(worldPerPixel, 0.0);\n width += borderOffset.x;\n height += borderOffset.y;\n depth += borderOffset.z;\n }\n\n // Analyze the normal to determine the face\n // Use abs() to handle both positive and negative directions\n vec3 absNormal = abs(normal);\n\n if (absNormal.x > 0.99) {\n // Side is on the X-axis (left/right face)\n // The 2D plane is defined by the geometry's Depth and Height\n vSize = vec2(depth, height);\n vTransform = vec2(0.001, worldPerPixel.y);\n vTopBottom = 0;\n } else if (absNormal.y > 0.99) {\n // Side is on the Y-axis (top/bottom face)\n // The 2D plane is defined by the geometry's Width and Depth\n vSize = vec2(width, depth);\n vTransform = vec2(worldPerPixel.x, 0.001);\n vTopBottom = normal.y > 0.99 ? 1 : 2;\n } else {\n // Side is on the Z-axis (front/back face)\n // The 2D plane is defined by the geometry's Width and Height\n vSize = vec2(width, height);\n vTransform = vec2(worldPerPixel);\n vTopBottom = 0;\n }\n\n mat4 adjustedInstanceMatrix = instanceMatrix;\n adjustedInstanceMatrix[0].xyz *= width / length(instanceMatrix[0].xyz);\n adjustedInstanceMatrix[1].xyz *= height / length(instanceMatrix[1].xyz);\n adjustedInstanceMatrix[2].xyz *= depth / length(instanceMatrix[2].xyz);\n adjustedInstanceMatrix[3].xyz = pos;\n \n vec4 localPos = adjustedInstanceMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * modelViewMatrix * localPos;\n }\n `},b=()=>{const t=e.style,o=new n(t.fill?.color||"#df0"),i=t.fill?.opacity||.5,l=new n(t.border?.color||"#000"),r=t.border?t.border.opacity||1:0,a=t.border?t.border.width||1:0,c="number"==typeof t.border?.smoothing?Math.max(0,t.border.smoothing):1;let m="";if(h){const n=e.maxClassifications||4,t=e.classificationColors||[];m+=`const int maxSplits = ${n};`;let o="",i="";for(let e=0;e<t.length;e++)m+=`\n const vec3 splitColor${e} = ${s(t[e]||"#777")};\n `,i+=`\n if (idx == ${e}) return splitColor${e};\n `;for(let e=0;e<n;e+=4)m+=`\n flat in vec4 vSplit${e/4};\n `,o+=`\n if (idx < ${e+4}) return vSplit${e/4}[idx - ${e}];\n `;m+=`\n float getSplit(int idx) {\n ${o}\n return 0.0;\n }\n vec3 getSplitColor(int idx) {\n ${i}\n return fillColor; // Fallback to default fill color\n }\n `,console.log("createFragmentShader",{splitVars:m,maxSplits:n,classificationColors:t})}return`\n // precision mediump float;\n // precision highp float;\n const float fillOpacity = ${d(i)};\n const float borderOpacity = ${d(r)};\n const float borderWidth = ${d(a)};\n const float borderSmoothing = ${d(c)};\n const bool borderSidesVertical = ${"vertical"===t.border?.sides?"true":"false"};\n\n ${g?"varying vec3 fillColor;":`const vec3 fillColor = vec3(${d(o.r)},${d(o.g)},${d(o.b)});`}\n ${g?"varying vec3 strokeColor;":`const vec3 strokeColor = vec3(${d(l.r)},${d(l.g)},${d(l.b)});`}\n\n varying vec2 vUv;\n flat in int vSelected;\n flat in int vTopBottom;\n flat in vec2 vSize;\n flat in vec2 vTransform;\n\n ${m}\n\n // Coverage of the inside of an edge at lo; smoothing 0 gives a hard step (smoothstep is undefined for equal edges).\n float edgeAlpha(float lo, float smoothing, float x) {\n return smoothing > 0.0 ? smoothstep(lo, lo + smoothing, x) : step(lo, x);\n }\n\n void main() {\n ${h?"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n vec3 segmentFillColor;\n float segmentStart = 0.0;\n float segmentEnd = 1.0;\n if (vTopBottom < 1) {\n // Determine which segment the current fragment is in\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n float spliti = getSplit(i);\n if (vUv.y <= spliti) {\n segmentEnd = spliti;\n segment = i;\n break;\n }\n else {\n segmentStart = spliti;\n }\n // if (vUv.y <= segmentStart + spliti) {\n // segmentEnd = segmentStart + spliti;\n // segment = i;\n // break;\n // }\n // else {\n // segmentStart += spliti;\n // }\n }\n\n // if (segment == 0 && segmentStart > 0.0) {\n // segmentEnd = 1.0;\n // for (int i = maxSplits - 1; i >= 0; i--) {\n // float spliti = getSplit(i);\n // if (spliti > 0.0) {\n // segmentStart = 1.0 - spliti;\n // segment = i;\n // break;\n // }\n // }\n // }\n \n segmentFillColor = getSplitColor(segment);\n }\n else if (vTopBottom < 2) {\n // Determine the last segment with a value\n int segment = 0;\n for (int i = maxSplits - 1; i >= 0; i--) {\n if (getSplit(i) < 0.99999) {\n segment = i + 1;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n else {\n // Determine the first segment with a value\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n if (getSplit(i) > 0.0) {\n segment = i;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n\n vec3 segmentBorderColor = segmentFillColor;\n float segmentSize = segmentEnd - segmentStart;\n\n // Half border size in world units\n vec2 bwWorld = borderWidth * vTransform;\n // Half border UV width if centered, full if inside-only\n vec2 bw = (bwWorld / vSize) / vec2(1.0, segmentSize);\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n // Adjust UV coordinates for the current segment (y-axis only)\n float uvYAdjusted = (vUv.y - segmentStart) / segmentSize;\n\n // Calculate alpha for borders and fill for the adjusted UV\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n // borderSidesVertical: skip the border between stacked segments (top/bottom), keep only left/right.\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, uvYAdjusted) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - uvYAdjusted);\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(segmentBorderColor, segmentFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = vec4(color, alpha);\n ":"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n // Half border size in world units\n vec2 bwWorld = borderWidth * vTransform;\n\n // Half border UV width if centered, full if inside-only\n vec2 bw = bwWorld / vSize;\n\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, vUv.y) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - vUv.y);\n\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(finalBorderColor, finalFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = vec4(color, alpha);\n "}\n }\n `},y=new l({transparent:!0,side:t,uniforms:{worldPerPixel:{value:new r(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},selection:{value:-1},valueRange:{value:new r(0,1)},originTime:{value:new r(0,0)}},vertexShader:x(),fragmentShader:b()}),S=()=>{y.uniforms.worldPerPixel.value.set(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},w=()=>{const e=i.getTimeRange(),n=e.startTime+.5*e.duration,[t,o]=m(n);y.uniforms.originTime.value.set(t,o)},C=()=>{const n=o.valueRanges.get(e.yAxis.group||e.id);if(n){const e=y.uniforms.valueRange.value;e.x===n.min&&e.y===n.max||y.uniforms.valueRange.value.set(n.min,n.max)}},T=()=>{S(),w()};return p.add(o.on("valueRanges",C)),p.add(i.on("time",T)),p.add(i.on("value",S)),p.add(i.on("size",S)),p.add(e.on("loaded",C)),p.add(e.on("visible",C)),w(),C(),p.add(e.on("invalidateAll",()=>{y.vertexShader=x(),y.fragmentShader=b(),y.needsUpdate=!0})),{material:y,dispose:()=>p.dispose()}}(g,x,b));y.add(C);const T=new e(1,1,1),$=new o(T,w,S.data.length);$.frustumCulled=!1,$.receiveShadow=!0,$.castShadow=!0;const z=function(e,n,t){const o=new p(n,e=>{const n=new i,t=.5*(e.t[0]+e.t[1]),[o,l]=m(t),r=e.v[1]-e.v[0],a=e.v[0]+.5*r;n.position.set(o,a,l);const s=e.t[1]-e.t[0];return n.scale.set(s,r,.1),n.updateMatrix(),n.matrix},t);return o.registerMesh(e),o}($,S,{colorProvider:g.styler?e=>{const n=g.styler(e);return n?{fill:n.fill?.color,stroke:n.border?.color}:void 0}:void 0,defaultFill:g.style.fill?.color,defaultBorder:g.style.border?.color,splitProvider:g instanceof v&&g.histogram instanceof f?e=>e.attributes.splits||[1]:void 0,maxSplits:g instanceof v?g.maxClassifications:void 0});y.add(()=>z.dispose()),x.selectionOptions.highlight&&y.add(g.on("selection",e=>{if(e.length>0){const n=z.getIndex(e[0]);w.uniforms.selection.value="number"==typeof n?n:-1}else w.uniforms.selection.value=-1}));return h.add($),y.add(()=>h.remove($)),y.disposeLater()}(g,h,b,x):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:g}),()=>{})}}
|
|
1
|
+
import{BoxGeometry as e,Color as n,DoubleSide as t,InstancedMesh as o,Object3D as i,ShaderMaterial as l,Vector2 as r}from"three";import{Disposables as a}from"../../common/Disposables.js";import{toGLSLColor as s,toGLSLFloat as d}from"../../common/GlslTools.js";import{getTimePartsInHours as c}from"../../common/TimeTools.js";import{TimeSliderSplittingFixedHistogram as m}from"../../data/histogram/TimeSliderSplittingFixedHistogram.js";import{TimeSliderHistogramLayer as f}from"../../layer/TimeSliderHistogramLayer.js";import{TimeSliderIntervalsLayer as v}from"../../layer/TimeSliderIntervalsLayer.js";import{Three3DTimeSliderEngine as g}from"../Three3DTimeSliderEngine.js";import{GrowingInstanceMatrix as h}from"./GrowingInstanceMatrix.js";const p=123456;export class Three3DIntervalsTimeLayerLoader{constructor(){this.engineClass=g,this.followsLayerBand=!0}canLoad(e){return e instanceof v}load(v,g,u,x){return this.canLoad(v)?function(v,g,u,x){const b=new a,y=v.model,{material:S,dispose:w}=(v.style,function(e,o,i){const v=void 0!==e.styler,g=function(e){return e instanceof f&&void 0!==e.histogram.classifier&&e.histogram instanceof m}(e),h=new a,u=()=>{const n=e.style,t=n.border?n.border.width||1:0,o="boolean"!=typeof n.border?.centered||n.border?.centered;let i="",l="";if(g){const n=e.maxClassifications||4;for(let e=0;e<n;e+=4)i+=`attribute vec4 split${e/4};`,i+=`flat out vec4 vSplit${e/4};`,l+=`vSplit${e/4} = split${e/4};`;console.log("createVertexShader",{splitAttributes:i,maxSplits:n})}return`\n // precision mediump float;\n // precision highp float;\n \n uniform vec2 layerRange;\n const bool borderCentered = ${o};\n const float borderWidth = ${d(t)};\n const float infinityDuration = ${d(p)};\n const float msInHour = 3600000.0;\n\n ${i}\n\n uniform vec2 originTime;\n uniform vec2 nowTime;\n uniform vec2 valueRange;\n uniform int selection;\n uniform vec2 worldPerPixel;\n // uniform float split;\n // attribute float split;\n \n ${v?"attribute vec3 borderColor;":""}\n ${v?"varying vec3 fillColor;":""}\n ${v?"varying vec3 strokeColor;":""}\n varying vec2 vUv;\n\n // flat out float vSplit;\n flat out int vSelected;\n flat out int vTopBottom;\n flat out vec2 vSize;\n flat out vec2 vTransform;\n\n void main() {\n ${v?"fillColor = instanceColor;":""}\n ${v?"strokeColor = borderColor;":""}\n vUv = uv;\n // vSplit = split;\n ${l}\n \n bool selected = gl_InstanceID == selection;\n vSelected = selected ? 1 : 0;\n\n float minValue = valueRange.x;\n float valueSize = valueRange.y - minValue;\n\n float width = length(instanceMatrix[0].xyz);\n float height = length(instanceMatrix[1].xyz) / valueSize;\n float depth = length(instanceMatrix[2].xyz);\n\n height *= (layerRange.y - layerRange.x);\n\n if (selected) {\n depth += 0.00001;\n }\n\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n float deltaHours = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n pos.x = relativeTime;\n \n pos.y = layerRange.x + ((pos.y - minValue) / valueSize) * (layerRange.y - layerRange.x);\n pos.z = 1.0 - 0.5 * depth;\n \n if (selected) {\n pos.z += 0.00001;\n }\n\n if (borderCentered) {\n vec3 borderOffset = borderWidth * vec3(worldPerPixel, 0.0);\n width += borderOffset.x;\n height += borderOffset.y;\n depth += borderOffset.z;\n }\n\n // Analyze the normal to determine the face\n // Use abs() to handle both positive and negative directions\n vec3 absNormal = abs(normal);\n\n if (absNormal.x > 0.99) {\n // Side is on the X-axis (left/right face)\n // The 2D plane is defined by the geometry's Depth and Height\n vSize = vec2(depth, height);\n vTransform = vec2(0.001, worldPerPixel.y);\n vTopBottom = 0;\n } else if (absNormal.y > 0.99) {\n // Side is on the Y-axis (top/bottom face)\n // The 2D plane is defined by the geometry's Width and Depth\n vSize = vec2(width, depth);\n vTransform = vec2(worldPerPixel.x, 0.001);\n vTopBottom = normal.y > 0.99 ? 1 : 2;\n } else {\n // Side is on the Z-axis (front/back face)\n // The 2D plane is defined by the geometry's Width and Height\n vSize = vec2(width, height);\n vTransform = vec2(worldPerPixel);\n vTopBottom = 0;\n }\n\n mat4 adjustedInstanceMatrix = instanceMatrix;\n adjustedInstanceMatrix[0].xyz *= width / length(instanceMatrix[0].xyz);\n adjustedInstanceMatrix[1].xyz *= height / length(instanceMatrix[1].xyz);\n adjustedInstanceMatrix[2].xyz *= depth / length(instanceMatrix[2].xyz);\n adjustedInstanceMatrix[3].xyz = pos;\n \n vec4 localPos = adjustedInstanceMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * modelViewMatrix * localPos;\n }\n `},x=()=>{const t=e.style,o=new n(t.fill?.color||"#df0"),i=t.fill?.opacity||.5,l=new n(t.border?.color||"#000"),r=t.border?t.border.opacity||1:0,a=t.border?t.border.width||1:0,c="number"==typeof t.border?.smoothing?Math.max(0,t.border.smoothing):1;let m="";if(g){const n=e.maxClassifications||4,t=e.classificationColors||[];m+=`const int maxSplits = ${n};`;let o="",i="";for(let e=0;e<t.length;e++)m+=`\n const vec3 splitColor${e} = ${s(t[e]||"#777")};\n `,i+=`\n if (idx == ${e}) return splitColor${e};\n `;for(let e=0;e<n;e+=4)m+=`\n flat in vec4 vSplit${e/4};\n `,o+=`\n if (idx < ${e+4}) return vSplit${e/4}[idx - ${e}];\n `;m+=`\n float getSplit(int idx) {\n ${o}\n return 0.0;\n }\n vec3 getSplitColor(int idx) {\n ${i}\n return fillColor; // Fallback to default fill color\n }\n `,console.log("createFragmentShader",{splitVars:m,maxSplits:n,classificationColors:t})}return`\n // precision mediump float;\n // precision highp float;\n const float fillOpacity = ${d(i)};\n const float borderOpacity = ${d(r)};\n const float borderWidth = ${d(a)};\n const float borderSmoothing = ${d(c)};\n const bool borderSidesVertical = ${"vertical"===t.border?.sides?"true":"false"};\n\n ${v?"varying vec3 fillColor;":`const vec3 fillColor = vec3(${d(o.r)},${d(o.g)},${d(o.b)});`}\n ${v?"varying vec3 strokeColor;":`const vec3 strokeColor = vec3(${d(l.r)},${d(l.g)},${d(l.b)});`}\n\n varying vec2 vUv;\n flat in int vSelected;\n flat in int vTopBottom;\n flat in vec2 vSize;\n flat in vec2 vTransform;\n\n ${m}\n\n // Coverage of the inside of an edge at lo; smoothing 0 gives a hard step (smoothstep is undefined for equal edges).\n float edgeAlpha(float lo, float smoothing, float x) {\n return smoothing > 0.0 ? smoothstep(lo, lo + smoothing, x) : step(lo, x);\n }\n\n void main() {\n ${g?"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n vec3 segmentFillColor;\n float segmentStart = 0.0;\n float segmentEnd = 1.0;\n if (vTopBottom < 1) {\n // Determine which segment the current fragment is in\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n float spliti = getSplit(i);\n if (vUv.y <= spliti) {\n segmentEnd = spliti;\n segment = i;\n break;\n }\n else {\n segmentStart = spliti;\n }\n // if (vUv.y <= segmentStart + spliti) {\n // segmentEnd = segmentStart + spliti;\n // segment = i;\n // break;\n // }\n // else {\n // segmentStart += spliti;\n // }\n }\n\n // if (segment == 0 && segmentStart > 0.0) {\n // segmentEnd = 1.0;\n // for (int i = maxSplits - 1; i >= 0; i--) {\n // float spliti = getSplit(i);\n // if (spliti > 0.0) {\n // segmentStart = 1.0 - spliti;\n // segment = i;\n // break;\n // }\n // }\n // }\n \n segmentFillColor = getSplitColor(segment);\n }\n else if (vTopBottom < 2) {\n // Determine the last segment with a value\n int segment = 0;\n for (int i = maxSplits - 1; i >= 0; i--) {\n if (getSplit(i) < 0.99999) {\n segment = i + 1;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n else {\n // Determine the first segment with a value\n int segment = 0;\n for (int i = 0; i < maxSplits; i++) {\n if (getSplit(i) > 0.0) {\n segment = i;\n break;\n }\n }\n segmentFillColor = getSplitColor(segment);\n }\n\n vec3 segmentBorderColor = segmentFillColor;\n float segmentSize = segmentEnd - segmentStart;\n\n // Half border size in world units\n vec2 bwWorld = borderWidth * vTransform;\n // Half border UV width if centered, full if inside-only\n vec2 bw = (bwWorld / vSize) / vec2(1.0, segmentSize);\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n // Adjust UV coordinates for the current segment (y-axis only)\n float uvYAdjusted = (vUv.y - segmentStart) / segmentSize;\n\n // Calculate alpha for borders and fill for the adjusted UV\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n // borderSidesVertical: skip the border between stacked segments (top/bottom), keep only left/right.\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, uvYAdjusted) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - uvYAdjusted);\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(segmentBorderColor, segmentFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = vec4(color, alpha);\n ":"\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n // Half border size in world units\n vec2 bwWorld = borderWidth * vTransform;\n\n // Half border UV width if centered, full if inside-only\n vec2 bw = bwWorld / vSize;\n\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, vUv.y) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - vUv.y);\n\n float fillAlpha = alphaX * alphaY;\n\n vec3 color = mix(finalBorderColor, finalFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = vec4(color, alpha);\n "}\n }\n `},b=new l({transparent:!0,side:t,uniforms:{worldPerPixel:{value:new r(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},layerRange:{value:new r(e.min,e.max)},selection:{value:-1},valueRange:{value:new r(0,1)},originTime:{value:new r(0,0)}},vertexShader:u(),fragmentShader:x()}),y=()=>{b.uniforms.worldPerPixel.value.set(i.camera.scale.x/i.getSize().width,i.camera.scale.y/i.getSize().height)},S=()=>{const e=i.getTimeRange(),n=e.startTime+.5*e.duration,[t,o]=c(n);b.uniforms.originTime.value.set(t,o)},w=()=>{const n=o.valueRanges.get(e.yAxis.group||e.id);if(n){const e=b.uniforms.valueRange.value;e.x===n.min&&e.y===n.max||b.uniforms.valueRange.value.set(n.min,n.max)}},C=()=>{y(),S()};h.add(o.on("valueRanges",w)),h.add(i.on("time",C)),h.add(i.on("value",y)),h.add(i.on("size",y)),h.add(e.on("loaded",w)),h.add(e.on("visible",w));const T=()=>b.uniforms.layerRange.value.set(e.min,e.max);return h.add(e.on("min",T)),h.add(e.on("max",T)),S(),w(),h.add(e.on("invalidateAll",()=>{b.vertexShader=u(),b.fragmentShader=x(),b.needsUpdate=!0})),{material:b,dispose:()=>h.dispose()}}(v,u,x));b.add(w);const C=new e(1,1,1),T=new o(C,S,y.data.length);T.frustumCulled=!1,T.receiveShadow=!0,T.castShadow=!0;const $=function(e,n,t){const o=new h(n,e=>{const n=new i,t=.5*(e.t[0]+e.t[1]),[o,l]=c(t),r=e.v[1]-e.v[0],a=e.v[0]+.5*r;n.position.set(o,a,l);const s=e.t[1]-e.t[0];return n.scale.set(s,r,.1),n.updateMatrix(),n.matrix},t);return o.registerMesh(e),o}(T,y,{colorProvider:v.styler?e=>{const n=v.styler(e);return n?{fill:n.fill?.color,stroke:n.border?.color}:void 0}:void 0,defaultFill:v.style.fill?.color,defaultBorder:v.style.border?.color,splitProvider:v instanceof f&&v.histogram instanceof m?e=>e.attributes.splits||[1]:void 0,maxSplits:v instanceof f?v.maxClassifications:void 0});b.add(()=>$.dispose()),u.selectionOptions.highlight&&b.add(v.on("selection",e=>{if(e.length>0){const n=$.getIndex(e[0]);S.uniforms.selection.value="number"==typeof n?n:-1}else S.uniforms.selection.value=-1}));return g.add(T),b.add(()=>g.remove(T)),b.disposeLater()}(v,g,x,u):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:v}),()=>{})}}
|
|
@@ -5,5 +5,6 @@ import { ThreeTimeLayerLoader } from "./ThreeTimeLayerLoader";
|
|
|
5
5
|
import { TimeSlider } from "../../engine/TimeSlider";
|
|
6
6
|
export declare class ThreeIntervalsTimeLayerLoader extends ThreeTimeLayerLoader {
|
|
7
7
|
canLoad(layer: TimeSliderLayer): boolean;
|
|
8
|
+
followsLayerBand: boolean;
|
|
8
9
|
load(layer: TimeSliderLayer, scene: Scene, engine: ThreeTimeSliderEngine, timeSlider: TimeSlider): () => void;
|
|
9
10
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Color as e,InstancedMesh as o,Object3D as n,PlaneGeometry as r,ShaderMaterial as t,Vector2 as i}from"three";import{Disposables as l}from"../../common/Disposables.js";import{toGLSLFloat as a
|
|
1
|
+
import{Color as e,InstancedMesh as o,Object3D as n,PlaneGeometry as r,ShaderMaterial as t,Vector2 as i}from"three";import{Disposables as l}from"../../common/Disposables.js";import{toGLSLFloat as a}from"../../common/GlslTools.js";import{getTimePartsInHours as s}from"../../common/TimeTools.js";import{TimeSliderIntervalsLayer as d}from"../../layer/TimeSliderIntervalsLayer.js";import{GrowingInstanceMatrix as c}from"./GrowingInstanceMatrix.js";import{ThreeTimeLayerLoader as m}from"./ThreeTimeLayerLoader.js";const v=123456;export class ThreeIntervalsTimeLayerLoader extends m{constructor(){super(...arguments),this.followsLayerBand=!0}canLoad(e){return e instanceof d}load(d,m,f,u){return this.canLoad(d)?function(d,m,f,u){const g=new l,h=d.model,{material:p,dispose:x}=function(o,n,r){const d=void 0!==o.styler,c=new l,m=()=>{const e=o.style,n=e.border?e.border.width||1:0;return`\n // precision mediump float;\n // precision highp float;\n \n const bool borderCentered = ${"boolean"!=typeof e.border?.centered||e.border?.centered};\n const float borderWidth = ${a(n)};\n uniform vec2 layerRange;\n const float infinityDuration = ${a(v)};\n const float msInHour = 3600000.0;\n\n uniform vec2 originTime;\n uniform vec2 valueRange;\n uniform int selection;\n uniform vec2 worldPerPixel;\n \n ${d?"attribute vec3 borderColor;":""}\n ${d?"varying vec3 fillColor;":""}\n ${d?"varying vec3 strokeColor;":""}\n \n varying vec2 vUv;\n flat out int vSelected;\n flat out vec2 vSize;\n\n void main() {\n ${d?"fillColor = instanceColor;":""}\n ${d?"strokeColor = borderColor;":""}\n vUv = uv;\n \n bool selected = gl_InstanceID == selection;\n vSelected = selected ? 1 : 0;\n\n float minValue = valueRange.x;\n float valueSize = valueRange.y - minValue;\n \n vSize.x = length(instanceMatrix[0].xyz);\n vSize.y = length(instanceMatrix[1].xyz) / valueSize;\n\n vSize.y *= (layerRange.y - layerRange.x);\n \n vec3 pos = vec3(instanceMatrix[3].xyz);\n float deltaHours = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaHours * 3600000.0 + deltaMs;\n pos.x = relativeTime;\n pos.y = layerRange.x + ((pos.y - minValue) / valueSize) * (layerRange.y - layerRange.x);\n pos.z = 0.0;\n \n if (selected) {\n pos.z += 0.0001;\n }\n\n if (borderCentered) {\n vec3 borderOffset = borderWidth * vec3(worldPerPixel, 0.0);\n vSize += borderOffset.xy;\n }\n \n mat4 adjustedInstanceMatrix = instanceMatrix;\n adjustedInstanceMatrix[0].xyz *= vSize.x / length(instanceMatrix[0].xyz);\n adjustedInstanceMatrix[1].xyz *= vSize.y / length(instanceMatrix[1].xyz);\n adjustedInstanceMatrix[3].xyz = pos;\n \n vec4 localPos = adjustedInstanceMatrix * vec4(position, 1.0);\n gl_Position = projectionMatrix * modelViewMatrix * localPos;\n }\n `},f=()=>{const n=o.style,r=new e(n.fill?.color||"#df0"),t=n.fill?.opacity||.5,i=new e(n.border?.color||"#000"),l=n.border?n.border.opacity||1:0,s=n.border?n.border.width||1:0,c="number"==typeof n.border?.smoothing?Math.max(0,n.border.smoothing):1.5;return`\n // precision mediump float;\n // precision highp float;\n\n const float fillOpacity = ${a(t)};\n const float borderOpacity = ${a(l)};\n const float borderWidth = ${a(s)};\n const float borderSmoothing = ${a(c)};\n const bool borderSidesVertical = ${"vertical"===n.border?.sides?"true":"false"};\n\n ${d?"varying vec3 fillColor;":`const vec3 fillColor = vec3(${a(r.r)},${a(r.g)},${a(r.b)});`}\n ${d?"varying vec3 strokeColor;":`const vec3 strokeColor = vec3(${a(i.r)},${a(i.g)},${a(i.b)});`}\n \n uniform vec2 worldPerPixel;\n \n varying vec2 vUv;\n flat in int vSelected;\n flat in vec2 vSize;\n\n // Coverage of the inside of an edge at lo; smoothing 0 gives a hard step (smoothstep is undefined for equal edges).\n float edgeAlpha(float lo, float smoothing, float x) {\n return smoothing > 0.0 ? smoothstep(lo, lo + smoothing, x) : step(lo, x);\n }\n\n void main() {\n bool selected = vSelected > 0;\n vec3 finalFillColor = selected ? vec3(1.0 - fillColor) : fillColor;\n vec3 finalBorderColor = selected ? vec3(1.0 - strokeColor) : strokeColor;\n\n // Half border size in world units\n vec2 bwWorld = borderWidth * worldPerPixel;\n\n // Half border UV width if centered, full if inside-only\n vec2 bw = bwWorld / vSize;\n\n float smoothingPixels = borderSmoothing;\n vec2 smoothing = smoothingPixels * worldPerPixel / vSize;\n\n float alphaX = edgeAlpha(bw.x, smoothing.x, vUv.x) *\n edgeAlpha(bw.x, smoothing.x, 1.0 - vUv.x);\n float alphaY = borderSidesVertical ? 1.0 : edgeAlpha(bw.y, smoothing.y, vUv.y) *\n edgeAlpha(bw.y, smoothing.y, 1.0 - vUv.y);\n\n float fillAlpha = alphaX * alphaY;\n float borderAlpha = 1.0 - fillAlpha;\n\n vec3 color = mix(finalBorderColor, finalFillColor, fillAlpha);\n float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n gl_FragColor = vec4(color, alpha);\n }\n `},u=new t({transparent:!0,uniforms:{worldPerPixel:{value:new i((n.camera.right-n.camera.left)/n.getSize().width,(n.camera.top-n.camera.bottom)/n.getSize().height)},layerRange:{value:new i(o.min,o.max)},selection:{value:-1},valueRange:{value:new i(0,1)},originTime:{value:new i(0,0)}},vertexShader:m(),fragmentShader:f()}),g=()=>{u.uniforms.worldPerPixel.value.set((n.camera.right-n.camera.left)/n.getSize().width,(n.camera.top-n.camera.bottom)/n.getSize().height)},h=()=>{const e=n.getTimeRange(),o=e.startTime+.5*e.duration,[r,t]=s(o);u.uniforms.originTime.value.set(r,t)},p=()=>{const e=r.valueRanges.get(o.yAxis.group||o.id);if(e){const o=u.uniforms.valueRange.value;o.x===e.min&&o.y===e.max||u.uniforms.valueRange.value.set(e.min,e.max)}},x=()=>{g(),h(),p()};c.add(r.on("valueRanges",p)),c.add(n.on("time",x)),c.add(n.on("value",g)),c.add(n.on("size",g)),c.add(o.on("loaded",p)),c.add(o.on("visible",p));const y=()=>u.uniforms.layerRange.value.set(o.min,o.max);return c.add(o.on("min",y)),c.add(o.on("max",y)),h(),p(),c.add(o.on("invalidate",()=>{u.vertexShader=m(),u.fragmentShader=f(),u.needsUpdate=!0})),{material:u,dispose:()=>c.dispose()}}(d,m,u);g.add(x);const y=new r(1,1),b=new o(y,p,h.data.length);b.frustumCulled=!1;const w=function(e,o,r,t,i){const l=new c(o,e=>{const o=new n,r=e.v[1]-e.v[0],t=e.v[0]+.5*r,i=.5*(e.t[0]+e.t[1]),[l,a]=s(i);o.position.set(l,t,a);const d=e.t[1]-e.t[0];return o.scale.set(d,r,.1),o.updateMatrix(),o.matrix},i);return l.registerMesh(e),l}(b,h,d.min,d.max,{colorProvider:d.styler?e=>{const o=d.styler(e);return o?{fill:o.fill?.color,stroke:o.border?.color}:void 0}:void 0,defaultFill:d.style.fill?.color,defaultBorder:d.style.border?.color});u.selectionOptions.highlight&&g.add(d.on("selection",e=>{if(e.length>0){const o=w.getIndex(e[0]);p.uniforms.selection.value="number"==typeof o?o:-1}else p.uniforms.selection.value=-1}));return f.add(b),g.add(()=>f.remove(b)),g.disposeLater()}(d,f,m,u):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:d}),()=>{})}}
|