@protomaze/time 1.4.3 → 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/README.md +29 -1
- package/TimeSliderDemo.d.ts +12 -1
- package/TimeSliderDemo.js +1 -1
- package/common/TimeTools.d.ts +17 -5
- package/common/TimeTools.js +1 -1
- package/data/CompositeTimeSliderModel.js +1 -1
- package/data/TimeSliderBush.js +1 -1
- package/data/TimeSliderIntervalsModel.js +1 -1
- package/data/TimeSliderLanesModel.js +1 -1
- package/data/TimeSliderModel.js +1 -1
- package/data/TimeSliderPointsModel.js +1 -1
- package/data/TimeSliderRange.d.ts +8 -0
- package/data/TimeSliderRange.js +1 -1
- package/data/histogram/TimeSliderDynamicHistogram.js +1 -1
- package/data/histogram/TimeSliderFixedHistogram.js +1 -1
- package/data/histogram/TimeSliderHistogram.js +1 -1
- package/data/histogram/TimeSliderSplittingFixedHistogram.js +1 -1
- package/demo/Common.d.ts +5 -1
- package/demo/Common.js +1 -1
- package/demo/Metrics.d.ts +2 -2
- package/demo/Metrics.js +1 -1
- package/demo/Nasdaq.d.ts +5 -2
- package/demo/Nasdaq.js +1 -1
- package/demo/Quakes.d.ts +6 -3
- package/demo/Quakes.js +1 -1
- package/demo/QuakesFilters.d.ts +3 -1
- package/demo/QuakesFilters.js +1 -1
- package/demo/Random.d.ts +45 -35
- package/demo/Random.js +1 -1
- package/demo/Test.d.ts +5 -2
- package/demo/Test.js +1 -1
- package/demo/Wars.js +1 -1
- package/engine/TimeSlider.d.ts +131 -5
- package/engine/TimeSlider.js +1 -1
- package/engine/TimeSliderEngine.js +1 -1
- package/index.d.ts +62 -1
- package/index.js +1 -1
- package/layer/TimeSliderHistogramLayer.js +1 -1
- package/layer/TimeSliderHistogramLayerOld.js +1 -1
- package/layer/TimeSliderIntervalsLayer.js +1 -1
- package/layer/TimeSliderLayer.js +1 -1
- package/layer/TimeSliderLayerLoader.d.ts +2 -0
- package/layer/TimeSliderPointsLayer.js +1 -1
- package/package.json +60 -58
- package/react/DateLabelWithPicker.js +1 -1
- package/react/DateRangeLabelWithPicker.js +1 -1
- package/react/FullscreenButton.js +1 -1
- package/react/ReactTimeSlider.js +1 -1
- package/react/TimeLabelWithPicker.js +1 -1
- package/style/TimeSliderIntervalsStyle.d.ts +4 -0
- package/three/Three3DTimeSliderEngine.js +1 -1
- package/three/ThreeTimeSlider.js +1 -1
- package/three/ThreeTimeSliderEngine.js +1 -1
- package/three/layer/GrowingBufferGeometry.js +1 -1
- package/three/layer/GrowingInstanceMatrix.js +1 -1
- package/three/layer/Three3DIntervalsTimeLayerLoader.d.ts +2 -2
- package/three/layer/Three3DIntervalsTimeLayerLoader.js +1 -1
- package/three/layer/Three3DPointsTimeLayerLoader.d.ts +1 -2
- package/three/layer/Three3DPointsTimeLayerLoader.js +1 -1
- package/three/layer/ThreeIntervalsTimeLayerLoader.d.ts +2 -2
- package/three/layer/ThreeIntervalsTimeLayerLoader.js +1 -1
- package/three/layer/ThreePointsTimeLayerLoader.d.ts +1 -2
- package/three/layer/ThreePointsTimeLayerLoader.js +1 -1
- package/three/layer/ThreeTimeLayerLoader.js +1 -1
- package/three/react/ReactThreeTimeSlider.js +1 -1
- package/ticks/TimeTicksBar.d.ts +14 -1
- package/ticks/TimeTicksBar.js +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Scene } from "three";
|
|
2
|
-
import { TimeSliderIntervalsLayer } from "../../layer/TimeSliderIntervalsLayer";
|
|
3
2
|
import { TimeSliderLayer } from "../../layer/TimeSliderLayer";
|
|
4
3
|
import { ThreeTimeSliderEngine } from "../ThreeTimeSliderEngine";
|
|
5
4
|
import { ThreeTimeLayerLoader } from "./ThreeTimeLayerLoader";
|
|
6
5
|
import { TimeSlider } from "../../engine/TimeSlider";
|
|
7
6
|
export declare class ThreeIntervalsTimeLayerLoader extends ThreeTimeLayerLoader {
|
|
8
|
-
canLoad(layer: TimeSliderLayer):
|
|
7
|
+
canLoad(layer: TimeSliderLayer): boolean;
|
|
8
|
+
followsLayerBand: boolean;
|
|
9
9
|
load(layer: TimeSliderLayer, scene: Scene, engine: ThreeTimeSliderEngine, timeSlider: TimeSlider): () => void;
|
|
10
10
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Color as e,InstancedMesh as
|
|
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}),()=>{})}}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Scene } from "three";
|
|
2
2
|
import { TimeSliderLayer } from "../../layer/TimeSliderLayer";
|
|
3
|
-
import { TimeSliderPointsLayer } from "../../layer/TimeSliderPointsLayer";
|
|
4
3
|
import { ThreeTimeSliderEngine } from "../ThreeTimeSliderEngine";
|
|
5
4
|
import { ThreeTimeLayerLoader } from "./ThreeTimeLayerLoader";
|
|
6
5
|
export declare class ThreePointsTimeLayerLoader extends ThreeTimeLayerLoader {
|
|
7
|
-
canLoad(layer: TimeSliderLayer):
|
|
6
|
+
canLoad(layer: TimeSliderLayer): boolean;
|
|
8
7
|
load(layer: TimeSliderLayer, scene: Scene, engine: ThreeTimeSliderEngine): () => void;
|
|
9
8
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{InstancedMesh as e,Object3D as n,PlaneGeometry as o,Points as i,ShaderMaterial as t,Vector2 as r}from"three";import{Disposables as l}from"../../common/Disposables";import{toGLSLColor as a,toGLSLFloat as s}from"../../common/GlslTools";import{getTimePartsInHours as c}from"../../common/TimeTools";import{TimeSliderPointsLayer as d}from"../../layer/TimeSliderPointsLayer";import{GrowingBufferGeometry as f}from"./GrowingBufferGeometry";import{GrowingInstanceMatrix as m}from"./GrowingInstanceMatrix";import{ThreeTimeLayerLoader as v}from"./ThreeTimeLayerLoader";export class ThreePointsTimeLayerLoader extends v{canLoad(e){return e instanceof d}load(d,v,g){return this.canLoad(d)?function(d,v,g){const h=new l,x=g.getValueRange(),y=x.maxValue-x.minValue,z={offset:x.minValue+(d.min||0)*y,scale:(d.max||1)-(d.min||0)},P=function(e,n){return new f(e,e=>{const o=.01*(n.offset+e.v*n.scale),[i,t]=c(e.t);return[i,o,t]})}(d.model,z),S=function(e,o){return new m(e,i=>{const t=e.getNeightbor(i,-1);if(t){const e=new n,r=.5*(t.t+i.t),l=.01*(o.offset+t.v*o.scale),a=.01*(o.offset+i.v*o.scale),s=.5*(l+a),d=i.t-t.t,f=a-l,[m,v]=c(r);return e.position.set(m,s,v),e.scale.set(d,Math.abs(f),f<0?1:2),e.updateMatrix(),e.matrix}return null})}(d.model,z);d.style.area&&h.add(function(n,i,d,f){const m=new l,v=e=>{const n=e.min||0,o=(e.max||1)-(e.min||0),i=e.style,t=i.area?.color||"#df0",r=i.area?.opacity||.5,l=i.area?.base||!1,c=i.area?.height||.1;p.vertexShader=`\n // precision mediump float;\n \n const float baseLine = ${s(n)};\n const float height = ${s(o)};\n const bool toBase = ${l};\n const float areaHeight = ${s(c)};\n\n uniform vec2 originTime;\n uniform vec2 worldPerPixel;\n\n varying float vHeight;\n\n void main() {\n vec2 size = vec2(length(instanceMatrix[0].xyz), length(instanceMatrix[1].xyz));\n // TODO may be better to use attributes... Check later when implementing raycasting\n if (length(instanceMatrix[2].xyz) < 1.5) {\n size.y *= -1.0;\n }\n\n // Reconstruct time\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.z = 0.0;\n \n vec2 rotScaleOffset = toBase ? vec2(\n position.x * size.x,\n (position.y + 0.5) * position.x * size.y + (position.y - 0.5) * (pos.y - baseLine) // This is to fill the area to the baseline\n ) : vec2(\n position.x * size.x,\n position.x * size.y + (position.y - 0.5) * areaHeight * height\n );\n\n vHeight = (position.y + 0.5);\n\n vec3 totalPos = pos + vec3(rotScaleOffset.xy, 0.0);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(totalPos, 1.0);\n }\n `,p.fragmentShader=`\n // precision mediump float;\n // precision highp float;\n\n const vec3 fillColor = ${a(t)};\n const float fillOpacity = ${s(r)};\n\n varying float vHeight;\n\n void main() {\n gl_FragColor = vec4(fillColor, fillOpacity * vHeight);\n }\n `,p.needsUpdate=!0},p=new t({transparent:!0,uniforms:{worldPerPixel:{value:new r((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},originTime:{value:new r(0,0)}}});v(n),n.on("invalidate",()=>{v(n)});const u=()=>{p.uniforms.worldPerPixel.value.set((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},g=()=>{u();const e=d.getTimeRange(),n=e.startTime+.5*e.duration,[o,i]=c(n);p.uniforms.originTime.value.set(o,i)};m.add(d.on("time",g)),m.add(d.on("value",u)),m.add(d.on("size",u)),g();const h=new o(1,1),x=new e(h,p,n.model.data.length);return f.registerMesh(x),x.frustumCulled=!1,i.add(x),m.add(()=>i.remove(x)),m.disposeLater()}(d,v,g,S));d.style.line&&h.add(p(d,v,g,S));d.style.point&&h.add(function(e,n,o,d){const f=new l,m=e.style;if(!m.point)throw"cannot happen";if(m.point.multipass){const i=m.point.color||"#df0",t=m.point.opacity||1,r=m.point.size||12,l=m.point.border?.color||"#000",a=m.point.border?.opacity||1,s=m.point.border?m.point.border?.width||2:0,c=m.point.core?.color||"#f00",v=m.point.core?.opacity||1,p=m.point.core?m.point.core?.size||4:0;m.point.border&&f.add(u(e,n,o,d,l,a,r)),f.add(u(e,n,o,d,i,t,r-2*s)),m.point.core&&f.add(u(e,n,o,d,c,v,p))}else{const l=new t({transparent:!0,uniforms:{selection:{value:-1},originTime:{value:new r(0,0)}}}),m=()=>{const n=e.style.point,o=n.color||"#df0",i=n.opacity||1,t=n.size||12,r=n.border?.color||"#000",c=n.border?.opacity||1,d=n.border?n.border?.width||2:0,f=n.core?.color||"#f00",m=n.core?.opacity||1,v=n.core?n.core?.size||4:0;l.vertexShader=`\n // precision mediump float;\n\n const float iconSize = ${s(t)};\n const float msInHour = 3600000.0;\n\n uniform vec2 originTime;\n uniform int selection;\n \n flat out int vSelected;\n \n void main() {\n // Option 1:\n // const count = geometry.attributes.position.count;\n // const indices = new Float32Array(count);\n // for(let i = 0; i < count; i++) indices[i] = i;\n // geometry.setAttribute('pointIndex', new THREE.BufferAttribute(indices, 1));\n \n // Option 2: Use gl_VertexID (WebGL2 only)\n bool selected = gl_VertexID == selection;\n vSelected = selected ? 1 : 0;\n \n // float day = position.x;\n float hours = position.x;\n float value = position.y;\n float time = position.z;\n \n float deltaHours = hours - originTime.x;\n float deltaMs = time - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(relativeTime, value, 1.0, 1.0);\n \n // gl_PointSize = selected ? 2.0 * iconSize : iconSize;\n gl_PointSize = mix(iconSize, 2.0 * iconSize, float(vSelected));\n\n // gl_PointSize = 1.0;\n }\n `,l.fragmentShader=`\n // precision mediump float;\n \n const vec3 fillColor = ${a(o)};\n const float fillOpacity = ${s(i)};\n const float iconSize = ${s(t)};\n const vec3 borderColor = ${a(r)};\n const float borderOpacity = ${s(c)};\n const float borderWidth = ${s(d)};\n const vec3 coreColor = ${a(f)};\n const float coreOpacity = ${s(m)};\n const float coreSize = ${s(v)};\n \n flat in int vSelected;\n \n void main() {\n vec3 finalFillColor = mix(fillColor, vec3(1.0, 0.0, 1.0), float(vSelected));\n vec3 finalBorderColor = mix(borderColor, vec3(0.0, 1.0, 1.0), float(vSelected));\n \n // bool selected = vSelected > 0;\n // vec3 finalFillColor = selected ? vec3(1.0,0.0,1.0) : fillColor;\n // vec3 finalBorderColor = selected ? vec3(0.0,1.0,1.0) : borderColor;\n \n vec2 uv = gl_PointCoord * 2.0 - 1.0;\n float dist = dot(uv, uv);\n\n // Circle mask\n if (dist > 1.0) discard;\n \n \n // // NO AA\n // if (dist > 1.0) {\n // gl_FragColor = vec4(0.0);\n // return;\n // }\n // if (dist > 2.0 * (borderWidth / iconSize)) {\n // gl_FragColor = vec4(borderColor, borderOpacity);\n // }\n // else {\n // gl_FragColor = vec4(fillColor, fillOpacity);\n // }\n // if (dist > 1.0 - (2.0 * borderWidth / iconSize)) {\n // gl_FragColor = vec4(borderColor, borderOpacity);\n // }\n // else if (dist < (2.0 * coreSize / iconSize)) {\n // gl_FragColor = vec4(innerColor, fillOpacity);\n // }\n // else {\n // gl_FragColor = vec4(fillColor, fillOpacity);\n // }\n \n // Convert thicknesses to radii (in normalized distance)\n float coreRadius = coreSize / iconSize;\n float fillRadius = 1.0 - (2.0 * borderWidth) / iconSize;\n\n // Convert to squared radius\n float coreR2 = coreRadius * coreRadius;\n float fillR2 = fillRadius * fillRadius;\n\n float smoothing = fwidth(dist);\n // float smoothing = 0.0;\n \n // Step 1: Inner to fill\n float edgeInner = smoothstep(coreR2 - smoothing, coreR2 + smoothing, dist);\n vec3 coreFillColor = mix(coreColor, fillColor, edgeInner);\n float coreFillAlpha = mix(coreOpacity, fillOpacity, edgeInner);\n \n // Step 2: Fill to border\n float edgeOuter = smoothstep(fillR2 - smoothing, fillR2 + smoothing, dist);\n vec3 finalColor = mix(coreFillColor, borderColor, edgeOuter);\n float finalAlpha = mix(coreFillAlpha, borderOpacity, edgeOuter);\n \n gl_FragColor = vec4(finalColor, finalAlpha);\n \n \n \n \n // // ORIGINAL (2 colors: fill & border)\n // // Smooth border width in world units (relative to icon size)\n // float borderWorld = 2.0 * borderWidth / iconSize;\n // float smoothing = fwidth(dist) * 1.0; // fwidth = derivative for AA width\n \n // // Blend factor: 0 = inside, 1 = outside (border edge)\n // float edge = smoothstep(1.0 - borderWorld - smoothing, 1.0 - borderWorld + smoothing, dist);\n \n // // Mix border and fill based on smoothed edge\n // vec3 color = mix(finalFillColor, finalBorderColor, edge);\n // float alpha = mix(fillOpacity, borderOpacity, edge);\n \n // gl_FragColor = vec4(color, alpha);\n }\n `,l.needsUpdate=!0};m(),f.add(e.on("invalidate",m));const v=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=c(n);l.uniforms.originTime.value.set(i,t)};f.add(o.on("time",v)),v();const p=new i(d,l);p.frustumCulled=!1,f.add(e.on("selection",e=>{if(e.length>0){const n=d.elementIndex(e[0]);l.uniforms.selection.value="number"==typeof n?n:-1}else l.uniforms.selection.value=-1})),n.add(p),f.add(()=>n.remove(p))}return f.disposeLater()}(d,v,g,P));return h.disposeLater()}(d,v,g):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:d}),()=>{})}}function p(n,i,d,f,m){const v=new l,u=n.style,g=void 0!==u.line?.border;!m&&g&&v.add(p(n,i,d,f,!0));const h=new t({transparent:!0,uniforms:{worldPerPixel:{value:new r((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},originTime:{value:new r}}}),x=()=>{const e=u.line?.color||"#df0",n=u.line?.border?.color||"#000",o=u.line?.width||4,i=u.line?.border?.width||1,t=u.line?.opacity||1,r=u.line?.border?.opacity||1,l=g&&m?n:e,c=g&&m?r:t,d=g?o-(m?0:2*i):o;h.vertexShader=`\n // precision mediump float;\n \n const float lineWidth = ${s(d)};\n const float msPerHour = 3600000.0;\n\n uniform vec2 originTime;\n uniform vec2 worldPerPixel;\n\n varying vec2 vUv;\n flat out float vLength;\n flat out float vHeight;\n flat out float vAspect;\n\n void main() {\n vUv = uv;\n\n vec2 size = vec2(length(instanceMatrix[0].xyz), length(instanceMatrix[1].xyz));\n\n vLength = size.x;\n vHeight = size.y;\n\n vAspect = (size.x / worldPerPixel.x) / (size.y / worldPerPixel.y);\n\n // TODO may be better to use attributes... Check later when implementing raycasting\n if (length(instanceMatrix[2].xyz) < 1.5) {\n size.y *= -1.0;\n }\n\n // Reconstruct time\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n float deltaDay = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaDay * msPerHour + deltaMs;\n pos.x = relativeTime;\n pos.z = 0.0;\n\n // Step 1: direction in screen space\n vec2 dirScreen = size / worldPerPixel;\n\n // Step 2: perpendicular direction in screen space\n vec2 perpScreen = normalize(vec2(-dirScreen.y, dirScreen.x)); // 90° rotation\n\n // Step 3: offset in screen space (lineWidth = pixels)\n vec2 offsetScreen = perpScreen * 0.5 * lineWidth;\n\n // Step 4: convert offset back to world space\n vec2 offsetWorld = offsetScreen * worldPerPixel;\n \n // vec2 rotScaleOffset = position.x * size + 2.0 * position.y * offsetWorld;\n \n vec2 cornerScreen = normalize(dirScreen) * 0.5 * lineWidth;\n vec2 cornerWorld = cornerScreen * worldPerPixel;\n vec2 rotScaleOffset = position.x * size + 2.0 * position.y * offsetWorld + 2.0 * position.x * cornerWorld;\n\n vec3 totalPos = pos + vec3(rotScaleOffset.xy, 0.0);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(totalPos, 1.0);\n }\n `,h.fragmentShader=`\n // precision mediump float;\n // precision highp float;\n\n const vec3 fillColor = ${a(l)};\n const float lineWidth = ${s(d)};\n const float fillOpacity = ${s(c)};\n\n uniform vec2 worldPerPixel;\n \n varying vec2 vUv;\n flat in float vLength;\n flat in float vHeight;\n flat in float vAspect;\n\n void main() {\n float lengthPx = vLength / worldPerPixel.x;\n float heightPx = vHeight / worldPerPixel.y;\n float distPx = sqrt(pow(lengthPx, 2.0) + pow(heightPx, 2.0));\n\n vec2 sizePx = vec2(lineWidth + distPx, lineWidth);\n float radiusPx = 0.5 * sizePx.y;\n vec2 uvPx = vUv * sizePx;\n\n // Left end circle\n if (uvPx.x < radiusPx) {\n vec2 center = vec2(radiusPx, radiusPx); // center of left circle\n float dist = distance(uvPx, center);\n if (dist > radiusPx) discard;\n\n gl_FragColor = vec4(fillColor, fillOpacity);\n }\n\n // Right end circle\n else if (uvPx.x > sizePx.x - radiusPx) {\n discard;\n vec2 center = vec2(sizePx.x - radiusPx, radiusPx); // center of right circle\n float dist = distance(uvPx, center);\n if (dist > radiusPx) discard;\n // gl_FragColor = vec4(1.0,0.0,1.0,1.0);\n \n gl_FragColor = vec4(fillColor, fillOpacity);\n }\n\n else {\n gl_FragColor = vec4(fillColor, fillOpacity);\n }\n }\n `};x(),v.add(n.on("invalidate",x));const y=()=>{h.uniforms.worldPerPixel.value.set((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},z=()=>{y();const e=d.getTimeRange(),n=e.startTime+.5*e.duration,[o,i]=c(n);h.uniforms.originTime.value.set(o,i)};v.add(d.on("time",z)),v.add(d.on("value",y)),v.add(d.on("size",y)),z();const P=new o(1,1),S=new e(P,h,n.model.data.length);return f.registerMesh(S),S.frustumCulled=!1,i.add(S),v.add(()=>i.remove(S)),v.disposeLater()}function u(e,n,o,d,f,m,v){const p=new l;if(!e.style.point)throw"cannot happen";const u=new t({transparent:!0,uniforms:{selection:{value:-1},originTime:{value:new r(0,0)}}}),g=()=>{u.vertexShader=`\n // precision mediump float;\n\n const float iconSize = ${s(v)};\n \n uniform vec2 originTime;\n uniform int selection;\n \n flat out int vSelected;\n \n const float msInHour = 3600000.0;\n\n void main() {\n // Option 2: Use gl_VertexID (WebGL2 only)\n bool selected = gl_VertexID == selection;\n vSelected = selected ? 1 : 0;\n\n float hours = position.x;\n float value = position.y;\n float time = position.z;\n\n float deltaHours = hours - originTime.x;\n float deltaMs = time - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4(relativeTime, value, 1.0, 1.0);\n\n gl_PointSize = mix(iconSize, 2.0 * iconSize, float(vSelected));\n }\n `,u.fragmentShader=`\n // precision mediump float;\n\n const vec3 color = ${a(f)};\n const float opacity = ${s(m)};\n \n flat in int vSelected;\n\n void main() { \n // Convert point coords from [0,1] to [-1,1]\n vec2 uv = gl_PointCoord * 2.0 - 1.0;\n\n // Compute squared distance from center\n float dist = dot(uv, uv);\n \n if (dist > 1.0) discard; // circle mask\n\n // Edge smoothing (anti-aliasing)\n float smoothing = fwidth(dist); // Automatic smoothing width\n float alpha = smoothstep(1.0, 1.0 - smoothing, dist); // Inverted for solid center\n\n gl_FragColor = vec4(color, alpha * opacity);\n }\n `,u.needsUpdate=!0};g(),p.add(e.on("invalidate",g));const h=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=c(n);u.uniforms.originTime.value.set(i,t)};p.add(o.on("time",h)),h();const x=new i(d,u);return x.frustumCulled=!1,p.add(e.on("selection",e=>{if(e.length>0){const n=d.elementIndex(e[0]);u.uniforms.selection.value="number"==typeof n?n:-1}else u.uniforms.selection.value=-1})),n.add(x),p.add(()=>n.remove(x)),p.disposeLater()}
|
|
1
|
+
import{InstancedMesh as e,Object3D as n,PlaneGeometry as o,Points as i,ShaderMaterial as t,Vector2 as r}from"three";import{Disposables as l}from"../../common/Disposables.js";import{toGLSLColor as a,toGLSLFloat as s}from"../../common/GlslTools.js";import{getTimePartsInHours as c}from"../../common/TimeTools.js";import{TimeSliderPointsLayer as d}from"../../layer/TimeSliderPointsLayer.js";import{GrowingBufferGeometry as f}from"./GrowingBufferGeometry.js";import{GrowingInstanceMatrix as m}from"./GrowingInstanceMatrix.js";import{ThreeTimeLayerLoader as v}from"./ThreeTimeLayerLoader.js";export class ThreePointsTimeLayerLoader extends v{canLoad(e){return e instanceof d}load(d,v,g){return this.canLoad(d)?function(d,v,g){const h=new l,x=g.getValueRange(),y=x.maxValue-x.minValue,z={offset:x.minValue+(d.min||0)*y,scale:(d.max||1)-(d.min||0)},P=function(e,n){return new f(e,e=>{const o=.01*(n.offset+e.v*n.scale),[i,t]=c(e.t);return[i,o,t]})}(d.model,z),S=function(e,o){return new m(e,i=>{const t=e.getNeightbor(i,-1);if(t){const e=new n,r=.5*(t.t+i.t),l=.01*(o.offset+t.v*o.scale),a=.01*(o.offset+i.v*o.scale),s=.5*(l+a),d=i.t-t.t,f=a-l,[m,v]=c(r);return e.position.set(m,s,v),e.scale.set(d,Math.abs(f),f<0?1:2),e.updateMatrix(),e.matrix}return null})}(d.model,z);d.style.area&&h.add(function(n,i,d,f){const m=new l,v=e=>{const n=e.min||0,o=(e.max||1)-(e.min||0),i=e.style,t=i.area?.color||"#df0",r=i.area?.opacity||.5,l=i.area?.base||!1,c=i.area?.height||.1;p.vertexShader=`\n // precision mediump float;\n \n const float baseLine = ${s(n)};\n const float height = ${s(o)};\n const bool toBase = ${l};\n const float areaHeight = ${s(c)};\n\n uniform vec2 originTime;\n uniform vec2 worldPerPixel;\n\n varying float vHeight;\n\n void main() {\n vec2 size = vec2(length(instanceMatrix[0].xyz), length(instanceMatrix[1].xyz));\n // TODO may be better to use attributes... Check later when implementing raycasting\n if (length(instanceMatrix[2].xyz) < 1.5) {\n size.y *= -1.0;\n }\n\n // Reconstruct time\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.z = 0.0;\n \n vec2 rotScaleOffset = toBase ? vec2(\n position.x * size.x,\n (position.y + 0.5) * position.x * size.y + (position.y - 0.5) * (pos.y - baseLine) // This is to fill the area to the baseline\n ) : vec2(\n position.x * size.x,\n position.x * size.y + (position.y - 0.5) * areaHeight * height\n );\n\n vHeight = (position.y + 0.5);\n\n vec3 totalPos = pos + vec3(rotScaleOffset.xy, 0.0);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(totalPos, 1.0);\n }\n `,p.fragmentShader=`\n // precision mediump float;\n // precision highp float;\n\n const vec3 fillColor = ${a(t)};\n const float fillOpacity = ${s(r)};\n\n varying float vHeight;\n\n void main() {\n gl_FragColor = vec4(fillColor, fillOpacity * vHeight);\n }\n `,p.needsUpdate=!0},p=new t({transparent:!0,uniforms:{worldPerPixel:{value:new r((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},originTime:{value:new r(0,0)}}});v(n),n.on("invalidate",()=>{v(n)});const u=()=>{p.uniforms.worldPerPixel.value.set((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},g=()=>{u();const e=d.getTimeRange(),n=e.startTime+.5*e.duration,[o,i]=c(n);p.uniforms.originTime.value.set(o,i)};m.add(d.on("time",g)),m.add(d.on("value",u)),m.add(d.on("size",u)),g();const h=new o(1,1),x=new e(h,p,n.model.data.length);return f.registerMesh(x),x.frustumCulled=!1,i.add(x),m.add(()=>i.remove(x)),m.disposeLater()}(d,v,g,S));d.style.line&&h.add(p(d,v,g,S));d.style.point&&h.add(function(e,n,o,d){const f=new l,m=e.style;if(!m.point)throw"cannot happen";if(m.point.multipass){const i=m.point.color||"#df0",t=m.point.opacity||1,r=m.point.size||12,l=m.point.border?.color||"#000",a=m.point.border?.opacity||1,s=m.point.border?m.point.border?.width||2:0,c=m.point.core?.color||"#f00",v=m.point.core?.opacity||1,p=m.point.core?m.point.core?.size||4:0;m.point.border&&f.add(u(e,n,o,d,l,a,r)),f.add(u(e,n,o,d,i,t,r-2*s)),m.point.core&&f.add(u(e,n,o,d,c,v,p))}else{const l=new t({transparent:!0,uniforms:{selection:{value:-1},originTime:{value:new r(0,0)}}}),m=()=>{const n=e.style.point,o=n.color||"#df0",i=n.opacity||1,t=n.size||12,r=n.border?.color||"#000",c=n.border?.opacity||1,d=n.border?n.border?.width||2:0,f=n.core?.color||"#f00",m=n.core?.opacity||1,v=n.core?n.core?.size||4:0;l.vertexShader=`\n // precision mediump float;\n\n const float iconSize = ${s(t)};\n const float msInHour = 3600000.0;\n\n uniform vec2 originTime;\n uniform int selection;\n \n flat out int vSelected;\n \n void main() {\n // Option 1:\n // const count = geometry.attributes.position.count;\n // const indices = new Float32Array(count);\n // for(let i = 0; i < count; i++) indices[i] = i;\n // geometry.setAttribute('pointIndex', new THREE.BufferAttribute(indices, 1));\n \n // Option 2: Use gl_VertexID (WebGL2 only)\n bool selected = gl_VertexID == selection;\n vSelected = selected ? 1 : 0;\n \n // float day = position.x;\n float hours = position.x;\n float value = position.y;\n float time = position.z;\n \n float deltaHours = hours - originTime.x;\n float deltaMs = time - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(relativeTime, value, 1.0, 1.0);\n \n // gl_PointSize = selected ? 2.0 * iconSize : iconSize;\n gl_PointSize = mix(iconSize, 2.0 * iconSize, float(vSelected));\n\n // gl_PointSize = 1.0;\n }\n `,l.fragmentShader=`\n // precision mediump float;\n \n const vec3 fillColor = ${a(o)};\n const float fillOpacity = ${s(i)};\n const float iconSize = ${s(t)};\n const vec3 borderColor = ${a(r)};\n const float borderOpacity = ${s(c)};\n const float borderWidth = ${s(d)};\n const vec3 coreColor = ${a(f)};\n const float coreOpacity = ${s(m)};\n const float coreSize = ${s(v)};\n \n flat in int vSelected;\n \n void main() {\n vec3 finalFillColor = mix(fillColor, vec3(1.0, 0.0, 1.0), float(vSelected));\n vec3 finalBorderColor = mix(borderColor, vec3(0.0, 1.0, 1.0), float(vSelected));\n \n // bool selected = vSelected > 0;\n // vec3 finalFillColor = selected ? vec3(1.0,0.0,1.0) : fillColor;\n // vec3 finalBorderColor = selected ? vec3(0.0,1.0,1.0) : borderColor;\n \n vec2 uv = gl_PointCoord * 2.0 - 1.0;\n float dist = dot(uv, uv);\n\n // Circle mask\n if (dist > 1.0) discard;\n \n \n // // NO AA\n // if (dist > 1.0) {\n // gl_FragColor = vec4(0.0);\n // return;\n // }\n // if (dist > 2.0 * (borderWidth / iconSize)) {\n // gl_FragColor = vec4(borderColor, borderOpacity);\n // }\n // else {\n // gl_FragColor = vec4(fillColor, fillOpacity);\n // }\n // if (dist > 1.0 - (2.0 * borderWidth / iconSize)) {\n // gl_FragColor = vec4(borderColor, borderOpacity);\n // }\n // else if (dist < (2.0 * coreSize / iconSize)) {\n // gl_FragColor = vec4(innerColor, fillOpacity);\n // }\n // else {\n // gl_FragColor = vec4(fillColor, fillOpacity);\n // }\n \n // Convert thicknesses to radii (in normalized distance)\n float coreRadius = coreSize / iconSize;\n float fillRadius = 1.0 - (2.0 * borderWidth) / iconSize;\n\n // Convert to squared radius\n float coreR2 = coreRadius * coreRadius;\n float fillR2 = fillRadius * fillRadius;\n\n float smoothing = fwidth(dist);\n // float smoothing = 0.0;\n \n // Step 1: Inner to fill\n float edgeInner = smoothstep(coreR2 - smoothing, coreR2 + smoothing, dist);\n vec3 coreFillColor = mix(coreColor, fillColor, edgeInner);\n float coreFillAlpha = mix(coreOpacity, fillOpacity, edgeInner);\n \n // Step 2: Fill to border\n float edgeOuter = smoothstep(fillR2 - smoothing, fillR2 + smoothing, dist);\n vec3 finalColor = mix(coreFillColor, borderColor, edgeOuter);\n float finalAlpha = mix(coreFillAlpha, borderOpacity, edgeOuter);\n \n gl_FragColor = vec4(finalColor, finalAlpha);\n \n \n \n \n // // ORIGINAL (2 colors: fill & border)\n // // Smooth border width in world units (relative to icon size)\n // float borderWorld = 2.0 * borderWidth / iconSize;\n // float smoothing = fwidth(dist) * 1.0; // fwidth = derivative for AA width\n \n // // Blend factor: 0 = inside, 1 = outside (border edge)\n // float edge = smoothstep(1.0 - borderWorld - smoothing, 1.0 - borderWorld + smoothing, dist);\n \n // // Mix border and fill based on smoothed edge\n // vec3 color = mix(finalFillColor, finalBorderColor, edge);\n // float alpha = mix(fillOpacity, borderOpacity, edge);\n \n // gl_FragColor = vec4(color, alpha);\n }\n `,l.needsUpdate=!0};m(),f.add(e.on("invalidate",m));const v=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=c(n);l.uniforms.originTime.value.set(i,t)};f.add(o.on("time",v)),v();const p=new i(d,l);p.frustumCulled=!1,f.add(e.on("selection",e=>{if(e.length>0){const n=d.elementIndex(e[0]);l.uniforms.selection.value="number"==typeof n?n:-1}else l.uniforms.selection.value=-1})),n.add(p),f.add(()=>n.remove(p))}return f.disposeLater()}(d,v,g,P));return h.disposeLater()}(d,v,g):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:d}),()=>{})}}function p(n,i,d,f,m){const v=new l,u=n.style,g=void 0!==u.line?.border;!m&&g&&v.add(p(n,i,d,f,!0));const h=new t({transparent:!0,uniforms:{worldPerPixel:{value:new r((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},originTime:{value:new r}}}),x=()=>{const e=u.line?.color||"#df0",n=u.line?.border?.color||"#000",o=u.line?.width||4,i=u.line?.border?.width||1,t=u.line?.opacity||1,r=u.line?.border?.opacity||1,l=g&&m?n:e,c=g&&m?r:t,d=g?o-(m?0:2*i):o;h.vertexShader=`\n // precision mediump float;\n \n const float lineWidth = ${s(d)};\n const float msPerHour = 3600000.0;\n\n uniform vec2 originTime;\n uniform vec2 worldPerPixel;\n\n varying vec2 vUv;\n flat out float vLength;\n flat out float vHeight;\n flat out float vAspect;\n\n void main() {\n vUv = uv;\n\n vec2 size = vec2(length(instanceMatrix[0].xyz), length(instanceMatrix[1].xyz));\n\n vLength = size.x;\n vHeight = size.y;\n\n vAspect = (size.x / worldPerPixel.x) / (size.y / worldPerPixel.y);\n\n // TODO may be better to use attributes... Check later when implementing raycasting\n if (length(instanceMatrix[2].xyz) < 1.5) {\n size.y *= -1.0;\n }\n\n // Reconstruct time\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n float deltaDay = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaDay * msPerHour + deltaMs;\n pos.x = relativeTime;\n pos.z = 0.0;\n\n // Step 1: direction in screen space\n vec2 dirScreen = size / worldPerPixel;\n\n // Step 2: perpendicular direction in screen space\n vec2 perpScreen = normalize(vec2(-dirScreen.y, dirScreen.x)); // 90° rotation\n\n // Step 3: offset in screen space (lineWidth = pixels)\n vec2 offsetScreen = perpScreen * 0.5 * lineWidth;\n\n // Step 4: convert offset back to world space\n vec2 offsetWorld = offsetScreen * worldPerPixel;\n \n // vec2 rotScaleOffset = position.x * size + 2.0 * position.y * offsetWorld;\n \n vec2 cornerScreen = normalize(dirScreen) * 0.5 * lineWidth;\n vec2 cornerWorld = cornerScreen * worldPerPixel;\n vec2 rotScaleOffset = position.x * size + 2.0 * position.y * offsetWorld + 2.0 * position.x * cornerWorld;\n\n vec3 totalPos = pos + vec3(rotScaleOffset.xy, 0.0);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(totalPos, 1.0);\n }\n `,h.fragmentShader=`\n // precision mediump float;\n // precision highp float;\n\n const vec3 fillColor = ${a(l)};\n const float lineWidth = ${s(d)};\n const float fillOpacity = ${s(c)};\n\n uniform vec2 worldPerPixel;\n \n varying vec2 vUv;\n flat in float vLength;\n flat in float vHeight;\n flat in float vAspect;\n\n void main() {\n float lengthPx = vLength / worldPerPixel.x;\n float heightPx = vHeight / worldPerPixel.y;\n float distPx = sqrt(pow(lengthPx, 2.0) + pow(heightPx, 2.0));\n\n vec2 sizePx = vec2(lineWidth + distPx, lineWidth);\n float radiusPx = 0.5 * sizePx.y;\n vec2 uvPx = vUv * sizePx;\n\n // Left end circle\n if (uvPx.x < radiusPx) {\n vec2 center = vec2(radiusPx, radiusPx); // center of left circle\n float dist = distance(uvPx, center);\n if (dist > radiusPx) discard;\n\n gl_FragColor = vec4(fillColor, fillOpacity);\n }\n\n // Right end circle\n else if (uvPx.x > sizePx.x - radiusPx) {\n discard;\n vec2 center = vec2(sizePx.x - radiusPx, radiusPx); // center of right circle\n float dist = distance(uvPx, center);\n if (dist > radiusPx) discard;\n // gl_FragColor = vec4(1.0,0.0,1.0,1.0);\n \n gl_FragColor = vec4(fillColor, fillOpacity);\n }\n\n else {\n gl_FragColor = vec4(fillColor, fillOpacity);\n }\n }\n `};x(),v.add(n.on("invalidate",x));const y=()=>{h.uniforms.worldPerPixel.value.set((d.camera.right-d.camera.left)/d.getSize().width,(d.camera.top-d.camera.bottom)/d.getSize().height)},z=()=>{y();const e=d.getTimeRange(),n=e.startTime+.5*e.duration,[o,i]=c(n);h.uniforms.originTime.value.set(o,i)};v.add(d.on("time",z)),v.add(d.on("value",y)),v.add(d.on("size",y)),z();const P=new o(1,1),S=new e(P,h,n.model.data.length);return f.registerMesh(S),S.frustumCulled=!1,i.add(S),v.add(()=>i.remove(S)),v.disposeLater()}function u(e,n,o,d,f,m,v){const p=new l;if(!e.style.point)throw"cannot happen";const u=new t({transparent:!0,uniforms:{selection:{value:-1},originTime:{value:new r(0,0)}}}),g=()=>{u.vertexShader=`\n // precision mediump float;\n\n const float iconSize = ${s(v)};\n \n uniform vec2 originTime;\n uniform int selection;\n \n flat out int vSelected;\n \n const float msInHour = 3600000.0;\n\n void main() {\n // Option 2: Use gl_VertexID (WebGL2 only)\n bool selected = gl_VertexID == selection;\n vSelected = selected ? 1 : 0;\n\n float hours = position.x;\n float value = position.y;\n float time = position.z;\n\n float deltaHours = hours - originTime.x;\n float deltaMs = time - originTime.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4(relativeTime, value, 1.0, 1.0);\n\n gl_PointSize = mix(iconSize, 2.0 * iconSize, float(vSelected));\n }\n `,u.fragmentShader=`\n // precision mediump float;\n\n const vec3 color = ${a(f)};\n const float opacity = ${s(m)};\n \n flat in int vSelected;\n\n void main() { \n // Convert point coords from [0,1] to [-1,1]\n vec2 uv = gl_PointCoord * 2.0 - 1.0;\n\n // Compute squared distance from center\n float dist = dot(uv, uv);\n \n if (dist > 1.0) discard; // circle mask\n\n // Edge smoothing (anti-aliasing)\n float smoothing = fwidth(dist); // Automatic smoothing width\n float alpha = smoothstep(1.0, 1.0 - smoothing, dist); // Inverted for solid center\n\n gl_FragColor = vec4(color, alpha * opacity);\n }\n `,u.needsUpdate=!0};g(),p.add(e.on("invalidate",g));const h=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=c(n);u.uniforms.originTime.value.set(i,t)};p.add(o.on("time",h)),h();const x=new i(d,u);return x.frustumCulled=!1,p.add(e.on("selection",e=>{if(e.length>0){const n=d.elementIndex(e[0]);u.uniforms.selection.value="number"==typeof n?n:-1}else u.uniforms.selection.value=-1})),n.add(x),p.add(()=>n.remove(x)),p.disposeLater()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ThreeTimeSliderEngine as e}from"../ThreeTimeSliderEngine";export class ThreeTimeLayerLoader{constructor(){this.engineClass=e}}
|
|
1
|
+
import{ThreeTimeSliderEngine as e}from"../ThreeTimeSliderEngine.js";export class ThreeTimeLayerLoader{constructor(){this.engineClass=e}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e,{useMemo as r}from"react";import{ThreeTimeSliderEngine as t}from"../ThreeTimeSliderEngine";import{ReactTimeSlider as i}from"../../react/ReactTimeSlider";export function ReactThreeTimeSlider(m){const n=r(()=>new t,[]);return e.createElement(i,{...m,engine:n})}
|
|
1
|
+
import e,{useMemo as r}from"react";import{ThreeTimeSliderEngine as t}from"../ThreeTimeSliderEngine.js";import{ReactTimeSlider as i}from"../../react/ReactTimeSlider.js";export function ReactThreeTimeSlider(m){const n=r(()=>new t,[]);return e.createElement(i,{...m,engine:n})}
|
package/ticks/TimeTicksBar.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export declare class TimeTicksBar {
|
|
|
17
17
|
private updateTime;
|
|
18
18
|
showLine: boolean;
|
|
19
19
|
darkMode: boolean;
|
|
20
|
+
private locale;
|
|
21
|
+
private weekLabel;
|
|
22
|
+
private weekLabelPrefix?;
|
|
23
|
+
private tickLabelStyler?;
|
|
20
24
|
needsUpdate: boolean;
|
|
21
25
|
private animationFrameId?;
|
|
22
26
|
cache: {
|
|
@@ -25,6 +29,15 @@ export declare class TimeTicksBar {
|
|
|
25
29
|
low?: TimeTicksCache;
|
|
26
30
|
};
|
|
27
31
|
setDarkMode(isDarkMode: boolean): void;
|
|
32
|
+
setLocale(locale: string): void;
|
|
33
|
+
setWeekLabel(weekLabel: 'default' | 'calendarWeek', weekLabelPrefix?: string): void;
|
|
34
|
+
setTickLabelStyler(styler: ((range: {
|
|
35
|
+
startTime: number;
|
|
36
|
+
endTime: number;
|
|
37
|
+
}) => {
|
|
38
|
+
color?: string;
|
|
39
|
+
opacity?: number;
|
|
40
|
+
} | undefined) | undefined): void;
|
|
28
41
|
init(container: HTMLDivElement, updateTime: (newRange: TimeSliderTimeRange) => void): void;
|
|
29
42
|
dispose(): void;
|
|
30
43
|
click(e: MouseEvent): void;
|
|
@@ -43,4 +56,4 @@ export declare class TimeTicksBar {
|
|
|
43
56
|
static generateTicks(tickSpacingMs: number, startTimeMs: number, endTimeMs: number, forTick: (tickTime: number, nextTickTime: number) => void): void;
|
|
44
57
|
private drawTicks;
|
|
45
58
|
}
|
|
46
|
-
export declare function formatTimeLabel(time: Date | number, tickSpacing: number, availableSpace: number): string;
|
|
59
|
+
export declare function formatTimeLabel(time: Date | number, tickSpacing: number, availableSpace: number, locale?: string, weekLabel?: 'default' | 'calendarWeek', weekLabelPrefix?: string): string;
|
package/ticks/TimeTicksBar.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{getDefaultLocale as t,getISOWeekNumber as e,getISOWeekYear as i,getStartOfDay as a,getStartOfMonth as s,getStartOfWeek as n,getStartOfXYears as r,getTimeUnitInMs as o,getWeekLabelPrefix as l}from"../common/TimeTools.js";import{CENTURY as h,DECADE as c,MILLENIUM as $,ONE_DAY as g,ONE_HOUR as d,ONE_MINUTE as m,ONE_MONTH as u,ONE_SECOND as T,ONE_WEEK as p,ONE_YEAR as w}from"./TimeSliderConstants.js";export class TimeTicksBar{constructor(){this.canvas=document.createElement("canvas"),this.current={startTimeMs:Date.now(),endTimeMs:Date.now()+g,widthPx:100,heightPx:24},this.xHover=void 0,this.hoverRange=void 0,this.updateTime=()=>{},this.showLine=!0,this.darkMode=!0,this.locale=t(),this.weekLabel="default",this.needsUpdate=!0,this.cache={high:{segments:[],spacing:1},medium:{segments:[],spacing:1}}}setDarkMode(t){this.darkMode=t,this.draw()}setLocale(t){this.locale=t,this.draw()}setWeekLabel(t,e){this.weekLabel=t,this.weekLabelPrefix=e,this.draw()}setTickLabelStyler(t){this.tickLabelStyler=t,this.draw()}init(t,e){this.updateTime=e,this.canvas.style.position="absolute",this.canvas.style.top="0px",this.canvas.style.left="0px",this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.touchAction="none",this.canvas.style.cursor="pointer",t.appendChild(this.canvas),this.canvas.addEventListener("pointermove",t=>{this.xHover=(t.clientX-this.canvas.getBoundingClientRect().left)/this.canvas.width,this.draw()}),this.canvas.addEventListener("click",t=>{this.click(t)},{capture:!0}),this.canvas.addEventListener("pointerleave",t=>{this.xHover=void 0,this.hoverRange=void 0,this.draw()});const i=()=>{this.needsUpdate&&(this.actualDraw(),this.needsUpdate=!1),this.animationFrameId=requestAnimationFrame(i)};this.animationFrameId=requestAnimationFrame(i)}dispose(){this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0)}click(t){this.xHover=(t.clientX-this.canvas.getBoundingClientRect().left)/this.canvas.width,this.draw(),this.hoverRange&&this.updateTime(this.hoverRange),this.xHover=void 0}setTimeRange(t){this.current.startTimeMs=t.startTime,this.current.endTimeMs=t.endTime,this.draw()}setWidth(t){this.current.widthPx=t,this.draw()}static getSpacing(t,e){const i=e/t,a=[T,5*T,10*T,15*T,30*T,m,5*m,10*m,15*m,30*m,d,3*d,6*d,g,p,u,w,c,h,$,1e4*w];let s=a[0],n=a[0],r=a[0];for(const t of a){if(t/i>=(t<m?56:t<g?36:30)){s=t;break}r=n,n=t}return[s,n,r]}draw(){this.needsUpdate=!0}actualDraw(){const{startTimeMs:t,endTimeMs:e,widthPx:i,heightPx:a}=this.current;if(i<=0)return;const s=this.canvas;s.width=i,s.height=a;const n=s.getContext("2d");n.clearRect(0,0,i,a);const r=e-t,[o,l,h]=TimeTicksBar.getSpacing(i,r);this.cache.low=l<g?{segments:this.drawTicks(h,4,!1,t,e,r,i,n),spacing:h}:void 0,this.cache.medium={segments:this.drawTicks(l,6,!1,t,e,r,i,n),spacing:l},this.cache.high={segments:this.drawTicks(o,8,!0,t,e,r,i,n),spacing:o}}static splitInTimeUnits(t,e){const{startTime:i,endTime:a}=t.getTimeRange(),s=o(e),n=[];return TimeTicksBar.generateTicks(s,i,a,(t,e)=>{n.push({start:t,end:e})}),n}static generateTicks(t,e,i,o){let l=Math.floor(e/t)*t;t>d&&t<p?l=a(l):t===p?l=n(l):t===u?l=s(l):t>=w&&(l=r(l,t/w));let h=e=>e+t;t===u?h=e=>s(e+1.1*t):t>=w&&(h=e=>{const i=new Date(e),a=new Date(i.getFullYear()+t/w,0,1);return a.setFullYear(i.getFullYear()+t/w),a.setHours(0,0,0,0)});let c=l;for(;c<=i+t;){const t=h(c);o(c,t),c=t}}drawTicks(t,e,i,a,s,n,r,o){const l=[];return o.strokeStyle="#888",o.fillStyle=this.darkMode?"white":"black",o.font="12px monospace",o.textAlign="center",o.textBaseline="top",this.showLine&&(o.beginPath(),o.moveTo(0,0),o.lineWidth=1,o.lineTo(r,0),o.stroke()),TimeTicksBar.generateTicks(t,a,s,(h,c)=>{const $=(h-a)/n*r;l.push({start:h,end:c});const d=t>=g&&i?24:e,m=(c-h)/n*r,u=formatTimeLabel(h,t,m,this.locale,this.weekLabel,this.weekLabelPrefix);if(i){const e="number"==typeof this.xHover?a+this.xHover*(s-a):void 0,i="number"==typeof e&&h<e&&e<c;i&&(o.fillStyle=this.darkMode?"rgba(225,255,0,0.2)":"rgba(225,255,0,0.4)",o.fillRect($,0,m,24),o.fillStyle=t>=g?this.darkMode?"#df0":"black":this.darkMode?"white":"black",i&&(this.hoverRange={startTime:h,endTime:c}))}if(o.beginPath(),o.moveTo($,0),o.lineWidth=e/4,o.lineTo($,d),o.stroke(),i){const e=this.tickLabelStyler?.({startTime:h,endTime:c}),i=o.globalAlpha;if(e?.color&&(o.fillStyle=e.color),"number"==typeof e?.opacity&&(o.globalAlpha=e.opacity),t>=g){const t=.5*m;o.fillText(u,$+t,8)}else o.fillText(u,$,8);o.globalAlpha=i}o.fillStyle=this.darkMode?"white":"black"}),l}}export function formatTimeLabel(a,s,n,r,o,h){const c="number"==typeof a?new Date(a):a,$=r??t(),T=c.getFullYear().toString().slice(2),f=((c.getMonth()+1).toString().padStart(2,"0"),c.getDate()),k=c.getHours().toString().padStart(2,"0"),v=c.getMinutes().toString().padStart(2,"0"),S=c.getSeconds().toString().padStart(2,"0"),L=c.toLocaleString($,{month:"short"});if(s>w){const t=new Date(c.getTime()+s),e=c.getFullYear(),i=t.getFullYear();if(n<75){if(n<60){const t=(i-e).toString();return`${e.toString().padStart(4,"0").slice(0,4-t.length)}${"X".repeat(t.length)}`}return`${e}-${i}`}return`${e} - ${i}`}if(s===w)return`${c.getFullYear()}`;if(s===u){if(n<64){if(n<52){if(n<40){return`${c.toLocaleString($,{month:"narrow"})}${T}`}return`${L}${T}`}return`${L} ${T}`}return n<120?`${L} ${c.getFullYear()}`:`${c.toLocaleString($,{month:"long"})} ${c.getFullYear()}`}if(s===p){const t=new Date(c.getTime()+1e5);if("calendarWeek"===o){const a=e(t),s=h??l($);return n<28?`${a}`:n<60?`${s}${a}`:`${s}${a} ${i(t)}`}const a=t.toLocaleString($,{month:"short"}),s=t.getDate(),r=(t.getFullYear().toString().slice(2),new Date(c.getTime()+6*g)),d=r.toLocaleString($,{month:"short"}),m=r.getDate();r.getFullYear().toString().slice(2);return a===d?n<130?n<110?n<95?n<70?n<60?n<48?formatTimeLabel(t.getTime(),g,n,$):`${s}-${m}${d.slice(0,2)}`:`${s}-${m}${d}`:`${s} - ${m} ${d}`:`${s} - ${m} ${d} ${T}`:`${s} - ${m} ${d} ${r.getFullYear()}`:`${s} - ${m} ${r.toLocaleString($,{month:"long"})} ${r.getFullYear()}`:n<185?n<130?n<115?n<95?n<85?n<75?n<65?n<56?n<48?formatTimeLabel(t.getTime(),g,n,$):`${s}${a.slice(0,1)}${m}${d.slice(0,1)}`:`${s}${a.slice(0,1)}-${m}${d.slice(0,1)}`:`${s}${a.slice(0,2)}-${m}${d.slice(0,2)}`:`${s}${a}-${m}${d}`:`${s}${a} - ${m}${d}`:`${s} ${a} - ${m} ${d}`:`${s} ${a} - ${m} ${d} ${T}`:`${s} ${a} - ${m} ${d} ${r.getFullYear()}`:`${s} ${t.toLocaleString($,{month:"long"})} - ${m} ${r.toLocaleString($,{month:"long"})} ${r.getFullYear()}`}return s>=g||s>=d&&"12"===k?n<170?n<140?n<100?n<90?n<75?n<56&&s>=g?n<48?`${f}${L.slice(0,2)}`:`${f}${L}`:`${f} ${L}`:`${c.toLocaleString($,{weekday:"short"})} ${f} ${L}`:`${c.toLocaleString($,{weekday:"short"})} ${f} ${L} ${T}`:`${c.toLocaleString($,{weekday:"short"})} ${f} ${L} ${c.getFullYear()}`:`${c.toLocaleString($,{weekday:"long"})} ${f} ${L} ${c.getFullYear()}`:`${c.toLocaleString($,{weekday:"long"})} ${f} ${c.toLocaleString($,{month:"long"})} ${c.getFullYear()}`:s>=d?`${c.getHours()}h`:s>=m?"12"===k&&"00"===v?`${f} ${L}`:`${k}:${v}`:"12"===k&&"00"===v&&"00"===S?`${f} ${L}`:`${k}:${v}:${S}`}
|