@protomaze/time 1.4.3 → 1.5.1

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.
Files changed (66) hide show
  1. package/README.md +29 -1
  2. package/TimeSliderDemo.d.ts +12 -1
  3. package/TimeSliderDemo.js +1 -1
  4. package/common/TimeTools.d.ts +17 -5
  5. package/common/TimeTools.js +1 -1
  6. package/data/CompositeTimeSliderModel.js +1 -1
  7. package/data/TimeSliderBush.js +1 -1
  8. package/data/TimeSliderIntervalsModel.js +1 -1
  9. package/data/TimeSliderLanesModel.js +1 -1
  10. package/data/TimeSliderModel.js +1 -1
  11. package/data/TimeSliderPointsModel.js +1 -1
  12. package/data/TimeSliderRange.d.ts +8 -0
  13. package/data/TimeSliderRange.js +1 -1
  14. package/data/histogram/TimeSliderDynamicHistogram.js +1 -1
  15. package/data/histogram/TimeSliderFixedHistogram.js +1 -1
  16. package/data/histogram/TimeSliderHistogram.js +1 -1
  17. package/data/histogram/TimeSliderSplittingFixedHistogram.js +1 -1
  18. package/demo/Common.d.ts +5 -1
  19. package/demo/Common.js +1 -1
  20. package/demo/Metrics.d.ts +2 -2
  21. package/demo/Metrics.js +1 -1
  22. package/demo/Nasdaq.d.ts +5 -2
  23. package/demo/Nasdaq.js +1 -1
  24. package/demo/Quakes.d.ts +6 -3
  25. package/demo/Quakes.js +1 -1
  26. package/demo/QuakesFilters.d.ts +3 -1
  27. package/demo/QuakesFilters.js +1 -1
  28. package/demo/Random.d.ts +45 -35
  29. package/demo/Random.js +1 -1
  30. package/demo/Test.d.ts +5 -2
  31. package/demo/Test.js +1 -1
  32. package/demo/Wars.js +1 -1
  33. package/engine/TimeSlider.d.ts +128 -5
  34. package/engine/TimeSlider.js +1 -1
  35. package/engine/TimeSliderEngine.js +1 -1
  36. package/index.d.ts +62 -1
  37. package/index.js +1 -1
  38. package/layer/TimeSliderHistogramLayer.js +1 -1
  39. package/layer/TimeSliderHistogramLayerOld.js +1 -1
  40. package/layer/TimeSliderIntervalsLayer.js +1 -1
  41. package/layer/TimeSliderLayer.js +1 -1
  42. package/layer/TimeSliderPointsLayer.js +1 -1
  43. package/package.json +60 -58
  44. package/react/DateLabelWithPicker.js +1 -1
  45. package/react/DateRangeLabelWithPicker.js +1 -1
  46. package/react/FullscreenButton.js +1 -1
  47. package/react/ReactTimeSlider.js +1 -1
  48. package/react/TimeLabelWithPicker.js +1 -1
  49. package/style/TimeSliderIntervalsStyle.d.ts +4 -0
  50. package/three/Three3DTimeSliderEngine.js +1 -1
  51. package/three/ThreeTimeSlider.js +1 -1
  52. package/three/ThreeTimeSliderEngine.js +1 -1
  53. package/three/layer/GrowingBufferGeometry.js +1 -1
  54. package/three/layer/GrowingInstanceMatrix.js +1 -1
  55. package/three/layer/Three3DIntervalsTimeLayerLoader.d.ts +1 -2
  56. package/three/layer/Three3DIntervalsTimeLayerLoader.js +1 -1
  57. package/three/layer/Three3DPointsTimeLayerLoader.d.ts +1 -2
  58. package/three/layer/Three3DPointsTimeLayerLoader.js +1 -1
  59. package/three/layer/ThreeIntervalsTimeLayerLoader.d.ts +1 -2
  60. package/three/layer/ThreeIntervalsTimeLayerLoader.js +1 -1
  61. package/three/layer/ThreePointsTimeLayerLoader.d.ts +1 -2
  62. package/three/layer/ThreePointsTimeLayerLoader.js +1 -1
  63. package/three/layer/ThreeTimeLayerLoader.js +1 -1
  64. package/three/react/ReactThreeTimeSlider.js +1 -1
  65. package/ticks/TimeTicksBar.d.ts +14 -1
  66. package/ticks/TimeTicksBar.js +1 -1
package/package.json CHANGED
@@ -1,58 +1,60 @@
1
- {
2
- "name": "@protomaze/time",
3
- "version": "1.4.3",
4
- "description": "Protomaze Time sliders",
5
- "main": "index.ts",
6
- "types": "index.d.ts",
7
- "files": [
8
- "**/*.js",
9
- "**/*.d.ts"
10
- ],
11
- "scripts": {
12
- "dev": "webpack serve --mode development",
13
- "compile": "tsc && node dev/copy_files.js && node dev/minify_files.js",
14
- "pub": "npm version patch --no-git-tag-version && rimraf build && npm run compile && cd build && npm --registry http://localhost:4873/ publish",
15
- "deploy": "rimraf build && npm run compile && cd build && npm publish --access public",
16
- "build": "webpack --mode production && npm run deploy",
17
- "release": "npm version patch --no-git-tag-version && npm run build",
18
- "release:minor": "npm version minor --no-git-tag-version && npm run build",
19
- "release:major": "npm version major --no-git-tag-version && npm run build"
20
- },
21
- "keywords": [
22
- "webpack",
23
- "typescript"
24
- ],
25
- "author": "Tom Crauwels",
26
- "dependencies": {
27
- "rbush": "^4.0.1",
28
- "three": "0.178.0",
29
- "uuid": "^14.0.1"
30
- },
31
- "peerDependencies": {
32
- "react": "18.2.0",
33
- "react-dom": "18.2.0"
34
- },
35
- "devDependencies": {
36
- "@types/rbush": "4.0.0",
37
- "@types/react": "18.0.34",
38
- "@types/react-dom": "18.0.11",
39
- "@types/three": "0.178.0",
40
- "copy-webpack-plugin": "^14.0.0",
41
- "css-loader": "^6.2.0",
42
- "file-loader": "6.2.0",
43
- "html-webpack-plugin": "^5.3.2",
44
- "react": "18.2.0",
45
- "react-dom": "18.2.0",
46
- "style-loader": "^3.2.1",
47
- "terser": "^5.43.1",
48
- "ts-loader": "^9.6.2",
49
- "typescript": "^6.0.3",
50
- "url-loader": "^4.1.1",
51
- "webpack": "^5.64.4",
52
- "webpack-cli": "^6.0.1",
53
- "webpack-dev-server": "^5.2.2"
54
- },
55
- "overrides": {
56
- "@types/scheduler": "< 0.23.0"
57
- }
58
- }
1
+ {
2
+ "name": "@protomaze/time",
3
+ "version": "1.5.1",
4
+ "description": "Protomaze Time sliders",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "files": [
8
+ "**/*.js",
9
+ "**/*.d.ts"
10
+ ],
11
+ "scripts": {
12
+ "dev": "webpack serve --mode development",
13
+ "compile": "tsc -p tsconfig.build.json && node dev/fix_extensions.js && node dev/copy_files.js && node dev/minify_files.js",
14
+ "pub": "npm version patch --no-git-tag-version && rimraf build && npm run compile && cd build && npm --registry http://localhost:4873/ publish",
15
+ "deploy": "rimraf build && npm run compile && cd build && npm publish --access public",
16
+ "build": "webpack --mode production && npm run deploy",
17
+ "release": "npm version patch --no-git-tag-version && npm run build",
18
+ "release:minor": "npm version minor --no-git-tag-version && npm run build",
19
+ "release:major": "npm version major --no-git-tag-version && npm run build"
20
+ },
21
+ "keywords": [
22
+ "webpack",
23
+ "typescript"
24
+ ],
25
+ "author": "Tom Crauwels",
26
+ "dependencies": {
27
+ "rbush": "^4.0.1",
28
+ "three": "0.178.0",
29
+ "uuid": "^14.0.1"
30
+ },
31
+ "peerDependencies": {
32
+ "react": "18.2.0",
33
+ "react-dom": "18.2.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/rbush": "4.0.0",
37
+ "@types/react": "18.0.34",
38
+ "@types/react-dom": "18.0.11",
39
+ "@types/three": "0.178.0",
40
+ "copy-webpack-plugin": "^14.0.0",
41
+ "css-loader": "^6.2.0",
42
+ "file-loader": "6.2.0",
43
+ "html-webpack-plugin": "^5.3.2",
44
+ "react": "18.2.0",
45
+ "react-dom": "18.2.0",
46
+ "rimraf": "^6.1.3",
47
+ "style-loader": "^3.2.1",
48
+ "terser": "^5.43.1",
49
+ "ts-loader": "^9.6.2",
50
+ "typescript": "^6.0.3",
51
+ "url-loader": "^4.1.1",
52
+ "webpack": "^5.64.4",
53
+ "webpack-cli": "^6.0.1",
54
+ "webpack-dev-server": "^5.2.2"
55
+ },
56
+ "overrides": {
57
+ "@types/scheduler": "< 0.23.0"
58
+ },
59
+ "type": "module"
60
+ }
@@ -1 +1 @@
1
- import e,{useEffect as t,useRef as r,useState as n}from"react";import{formatDateWithWeekDay as i}from"../common/TimeTools";import{LABEL_WITH_PICKER_HOVER_STYLE as o,LABEL_WITH_PICKER_SPAN_STYLE as a}from"./TimeLabelWithPicker";export function DateLabelWithPicker({time:c,setTime:l,theme:s,...u}){const m=r(c),p=r(null),[d,h]=n(!1);t(()=>{m.current=c},[c]),t(()=>{if(p.current){const e=()=>{const e=p.current.value,[t,r,n]=e.split("-").map(e=>parseInt(e)),i=new Date(m.current);i.setDate(n),i.setMonth(r-1),i.setFullYear(t),l(i.getTime())};return p.current.addEventListener("input",e),()=>{p.current?.removeEventListener("input",e)}}},[]);return e.createElement("div",{...u,style:{position:"relative",...u.style}},e.createElement("span",{style:d?o(s||"dark"):a(s||"dark"),onClick:()=>{if(p.current){const e=new Date(m.current),t=e.getDate().toString().padStart(2,"0"),r=(e.getMonth()+1).toString().padStart(2,"0"),n=e.getFullYear().toString().padStart(4,"0");p.current.value=n+"-"+r+"-"+t,p.current.showPicker()}},onMouseEnter:()=>h(!0),onMouseLeave:()=>h(!1)},i(c)),e.createElement("input",{type:"date",ref:p,style:{position:"absolute",left:"0px",visibility:"hidden",colorScheme:"dark"===s?"dark":"light"}}))}
1
+ import e,{useEffect as t,useRef as r,useState as n}from"react";import{formatDateWithWeekDay as i}from"../common/TimeTools.js";import{LABEL_WITH_PICKER_HOVER_STYLE as o,LABEL_WITH_PICKER_SPAN_STYLE as a}from"./TimeLabelWithPicker.js";export function DateLabelWithPicker({time:s,setTime:c,theme:l,...u}){const m=r(s),p=r(null),[d,h]=n(!1);t(()=>{m.current=s},[s]),t(()=>{if(p.current){const e=()=>{const e=p.current.value,[t,r,n]=e.split("-").map(e=>parseInt(e)),i=new Date(m.current);i.setDate(n),i.setMonth(r-1),i.setFullYear(t),c(i.getTime())};return p.current.addEventListener("input",e),()=>{p.current?.removeEventListener("input",e)}}},[]);return e.createElement("div",{...u,style:{position:"relative",...u.style}},e.createElement("span",{style:d?o(l||"dark"):a(l||"dark"),onClick:()=>{if(p.current){const e=new Date(m.current),t=e.getDate().toString().padStart(2,"0"),r=(e.getMonth()+1).toString().padStart(2,"0"),n=e.getFullYear().toString().padStart(4,"0");p.current.value=n+"-"+r+"-"+t,p.current.showPicker()}},onMouseEnter:()=>h(!0),onMouseLeave:()=>h(!1)},i(s)),e.createElement("input",{type:"date",ref:p,style:{position:"absolute",left:"0px",visibility:"hidden",colorScheme:"dark"===l?"dark":"light"}}))}
@@ -1 +1 @@
1
- import e,{useEffect as t,useRef as n,useState as r}from"react";import a from"./YearScroller";import{LABEL_WITH_PICKER_HOVER_STYLE as l,LABEL_WITH_PICKER_SPAN_STYLE as u}from"./TimeLabelWithPicker";export function DateRangeLabelWithPicker({range:c,setRange:i,theme:s,...g}){const p=n(c),m=n(null),d=n(null),h=n(null),D=n(null),S=n(null),[f,F]=r(!1);t(()=>{p.current=c},[c]),t(()=>{if(d.current){const e=()=>{const e=new Date(p.current[0]),t=new Date(p.current[1]-1),n=d.current.value,[r,a,l]=n.split("-").map(e=>parseInt(e));e.toDateString()===t.toDateString()&&(t.setDate(l),t.setMonth(a-1),t.setFullYear(r)),e.setDate(l),e.setMonth(a-1),e.setFullYear(r),i([e.getTime(),t.getTime()+1])};d.current.addEventListener("input",e)}if(h.current){const e=()=>{const e=new Date(p.current[0]),t=new Date(p.current[1]-1),n=h.current.value,[r,a,l]=n.split("-").map(e=>parseInt(e));t.setDate(l),t.setMonth(a-1),t.setFullYear(r),i([e.getTime(),t.getTime()+1])};h.current.addEventListener("input",e)}if(D.current){const e=()=>{const e=new Date(p.current[0]),t=new Date(p.current[1]-1),n=D.current.value,[r,a]=n.split("-").map(e=>parseInt(e));t.setDate(o(a-1,r)),t.setMonth(a-1),t.setFullYear(r),e.setMonth(a-1),e.setFullYear(r),i([e.getTime(),t.getTime()+1])};D.current.addEventListener("change",e),D.current.addEventListener("input",e)}if(S.current){const e=e=>{const t=new Date(p.current[0]),n=new Date(p.current[1]-1);n.setDate(o(11,e)),n.setFullYear(e),t.setFullYear(e),i([t.getTime(),n.getTime()+1])};S.current.onSelect(e)}},[]);const w=e=>{if(m.current){const t=m.current.getBoundingClientRect(),n=(e.clientX-t.left)/t.width;F(n<=.5?"start":"end")}};return e.createElement("div",{...g,style:{position:"relative",...g.style}},e.createElement("span",{ref:m,style:f?l(s||"dark"):u(s||"dark"),onClick:e=>{if(d.current&&h.current&&D.current&&S.current&&m.current){const t=new Date(p.current[0]);if(function(e){const[t,n]=e,r=new Date(t),a=new Date(n),l=new Date(n-1);return r.getFullYear()===l.getFullYear()&&r.getMonth()===l.getMonth()&&1===r.getDate()&&1===a.getDate()}(p.current)){const e=(t.getMonth()+1).toString().padStart(2,"0"),n=t.getFullYear().toString().padStart(4,"0");D.current.value=n+"-"+e,D.current.showPicker()}else if(function(e){const[t,n]=e,r=new Date(t),a=new Date(n),l=new Date(n-1);return r.getFullYear()===l.getFullYear()&&1===r.getDate()&&1===a.getDate()&&0===r.getMonth()&&0===a.getMonth()}(p.current)){const e=t.getFullYear();S.current.value=e,S.current.show()}else{const n=new Date(p.current[1]),r=m.current.getBoundingClientRect();if((e.clientX-r.left)/r.width<=.5||t.toDateString()===n.toDateString()){const e=t.getDate().toString().padStart(2,"0"),n=(t.getMonth()+1).toString().padStart(2,"0"),r=t.getFullYear().toString().padStart(4,"0");d.current.value=r+"-"+n+"-"+e,d.current.showPicker()}else{const e=n.getDate().toString().padStart(2,"0"),t=(n.getMonth()+1).toString().padStart(2,"0"),r=n.getFullYear().toString().padStart(4,"0");h.current.value=r+"-"+t+"-"+e,h.current.focus(),h.current.showPicker()}}}},onMouseMove:w,onMouseEnter:w,onMouseLeave:()=>F(!1)},function(t,n){const[r,a]=t,l=new Date(r),u=new Date(a),o=new Date(a-1),c=l.toLocaleString("default",{month:"short"}),i=o.toLocaleString("default",{month:"short"}),s=l.getDate().toString().padStart(2,"0"),g=o.getDate().toString().padStart(2,"0"),p={color:"#888"},m="end"===n?p:void 0,d="start"===n?p:void 0;return l.getFullYear()===o.getFullYear()?l.getMonth()===o.getMonth()?l.getDate()===o.getDate()?`${g} ${i} ${o.getFullYear()}`:1===l.getDate()&&1===u.getDate()?`${i} ${o.getFullYear()}`:e.createElement(e.Fragment,null,e.createElement("span",{style:m},`${s}`),e.createElement("span",null," - "),e.createElement("span",{style:d},`${g}`),e.createElement("span",null,` ${i} ${o.getFullYear()}`)):1===l.getDate()&&1===u.getDate()&&0===l.getMonth()&&0===u.getMonth()?`${o.getFullYear()}`:e.createElement(e.Fragment,null,e.createElement("span",{style:m},`${s} ${c}`),e.createElement("span",null," - "),e.createElement("span",{style:d},`${g} ${i}`),e.createElement("span",null,` ${o.getFullYear()}`)):1===l.getDate()&&1===u.getDate()&&0===l.getMonth()&&0===u.getMonth()?` ${l.getFullYear()} - ${o.getFullYear()}`:e.createElement(e.Fragment,null,e.createElement("span",{style:m},`${s} ${c} ${l.getFullYear()}`),e.createElement("span",null," - "),e.createElement("span",{style:d},`${g} ${i} ${o.getFullYear()}`))}(c,f)),e.createElement("input",{type:"date",ref:d,style:{position:"absolute",left:"0px",visibility:"hidden",colorScheme:"dark"===s?"dark":"light"}}),e.createElement("input",{type:"date",ref:h,style:{position:"absolute",right:"0px",visibility:"hidden",colorScheme:"dark"===s?"dark":"light"}}),e.createElement("input",{type:"month",ref:D,style:{position:"absolute",right:"0px",visibility:"hidden",colorScheme:"dark"===s?"dark":"light"}}),e.createElement(a,{ref:S}))}function o(e,t){if(e<0||e>11)throw new Error("Month must be between 0 and 11");if(1===e){return t%4==0&&t%100!=0||t%400==0?29:28}return[31,28,31,30,31,30,31,31,30,31,30,31][e]}
1
+ import e,{useEffect as t,useRef as n,useState as r}from"react";import{getDefaultLocale as a}from"../common/TimeTools.js";import l from"./YearScroller.js";import{LABEL_WITH_PICKER_HOVER_STYLE as o,LABEL_WITH_PICKER_SPAN_STYLE as u}from"./TimeLabelWithPicker.js";export function DateRangeLabelWithPicker({range:i,setRange:s,theme:g,...m}){const p=n(i),h=n(null),D=n(null),d=n(null),S=n(null),f=n(null),[F,w]=r(!1);t(()=>{p.current=i},[i]),t(()=>{if(D.current){const e=()=>{const e=new Date(p.current[0]),t=new Date(p.current[1]-1),n=D.current.value,[r,a,l]=n.split("-").map(e=>parseInt(e));e.toDateString()===t.toDateString()&&(t.setDate(l),t.setMonth(a-1),t.setFullYear(r)),e.setDate(l),e.setMonth(a-1),e.setFullYear(r),s([e.getTime(),t.getTime()+1])};D.current.addEventListener("input",e)}if(d.current){const e=()=>{const e=new Date(p.current[0]),t=new Date(p.current[1]-1),n=d.current.value,[r,a,l]=n.split("-").map(e=>parseInt(e));t.setDate(l),t.setMonth(a-1),t.setFullYear(r),s([e.getTime(),t.getTime()+1])};d.current.addEventListener("input",e)}if(S.current){const e=()=>{const e=new Date(p.current[0]),t=new Date(p.current[1]-1),n=S.current.value,[r,a]=n.split("-").map(e=>parseInt(e));t.setDate(c(a-1,r)),t.setMonth(a-1),t.setFullYear(r),e.setMonth(a-1),e.setFullYear(r),s([e.getTime(),t.getTime()+1])};S.current.addEventListener("change",e),S.current.addEventListener("input",e)}if(f.current){const e=e=>{const t=new Date(p.current[0]),n=new Date(p.current[1]-1);n.setDate(c(11,e)),n.setFullYear(e),t.setFullYear(e),s([t.getTime(),n.getTime()+1])};f.current.onSelect(e)}},[]);const Y=e=>{if(h.current){const t=h.current.getBoundingClientRect(),n=(e.clientX-t.left)/t.width;w(n<=.5?"start":"end")}};return e.createElement("div",{...m,style:{position:"relative",...m.style}},e.createElement("span",{ref:h,style:F?o(g||"dark"):u(g||"dark"),onClick:e=>{if(D.current&&d.current&&S.current&&f.current&&h.current){const t=new Date(p.current[0]);if(function(e){const[t,n]=e,r=new Date(t),a=new Date(n),l=new Date(n-1);return r.getFullYear()===l.getFullYear()&&r.getMonth()===l.getMonth()&&1===r.getDate()&&1===a.getDate()}(p.current)){const e=(t.getMonth()+1).toString().padStart(2,"0"),n=t.getFullYear().toString().padStart(4,"0");S.current.value=n+"-"+e,S.current.showPicker()}else if(function(e){const[t,n]=e,r=new Date(t),a=new Date(n),l=new Date(n-1);return r.getFullYear()===l.getFullYear()&&1===r.getDate()&&1===a.getDate()&&0===r.getMonth()&&0===a.getMonth()}(p.current)){const e=t.getFullYear();f.current.value=e,f.current.show()}else{const n=new Date(p.current[1]),r=h.current.getBoundingClientRect();if((e.clientX-r.left)/r.width<=.5||t.toDateString()===n.toDateString()){const e=t.getDate().toString().padStart(2,"0"),n=(t.getMonth()+1).toString().padStart(2,"0"),r=t.getFullYear().toString().padStart(4,"0");D.current.value=r+"-"+n+"-"+e,D.current.showPicker()}else{const e=n.getDate().toString().padStart(2,"0"),t=(n.getMonth()+1).toString().padStart(2,"0"),r=n.getFullYear().toString().padStart(4,"0");d.current.value=r+"-"+t+"-"+e,d.current.focus(),d.current.showPicker()}}}},onMouseMove:Y,onMouseEnter:Y,onMouseLeave:()=>w(!1)},function(t,n){const[r,l]=t,o=new Date(r),u=new Date(l),c=new Date(l-1),i=a(),s=o.toLocaleString(i,{month:"short"}),g=c.toLocaleString(i,{month:"short"}),m=o.getDate().toString().padStart(2,"0"),p=c.getDate().toString().padStart(2,"0"),h={color:"#888"},D="end"===n?h:void 0,d="start"===n?h:void 0;return o.getFullYear()===c.getFullYear()?o.getMonth()===c.getMonth()?o.getDate()===c.getDate()?`${p} ${g} ${c.getFullYear()}`:1===o.getDate()&&1===u.getDate()?`${g} ${c.getFullYear()}`:e.createElement(e.Fragment,null,e.createElement("span",{style:D},`${m}`),e.createElement("span",null," - "),e.createElement("span",{style:d},`${p}`),e.createElement("span",null,` ${g} ${c.getFullYear()}`)):1===o.getDate()&&1===u.getDate()&&0===o.getMonth()&&0===u.getMonth()?`${c.getFullYear()}`:e.createElement(e.Fragment,null,e.createElement("span",{style:D},`${m} ${s}`),e.createElement("span",null," - "),e.createElement("span",{style:d},`${p} ${g}`),e.createElement("span",null,` ${c.getFullYear()}`)):1===o.getDate()&&1===u.getDate()&&0===o.getMonth()&&0===u.getMonth()?` ${o.getFullYear()} - ${c.getFullYear()}`:e.createElement(e.Fragment,null,e.createElement("span",{style:D},`${m} ${s} ${o.getFullYear()}`),e.createElement("span",null," - "),e.createElement("span",{style:d},`${p} ${g} ${c.getFullYear()}`))}(i,F)),e.createElement("input",{type:"date",ref:D,style:{position:"absolute",left:"0px",visibility:"hidden",colorScheme:"dark"===g?"dark":"light"}}),e.createElement("input",{type:"date",ref:d,style:{position:"absolute",right:"0px",visibility:"hidden",colorScheme:"dark"===g?"dark":"light"}}),e.createElement("input",{type:"month",ref:S,style:{position:"absolute",right:"0px",visibility:"hidden",colorScheme:"dark"===g?"dark":"light"}}),e.createElement(l,{ref:f}))}function c(e,t){if(e<0||e>11)throw new Error("Month must be between 0 and 11");if(1===e){return t%4==0&&t%100!=0||t%400==0?29:28}return[31,28,31,30,31,30,31,31,30,31,30,31][e]}
@@ -1 +1 @@
1
- import e,{useState as l,useEffect as n}from"react";import{MaterialIconButton as t}from"./MaterialIconButton";export const FullscreenButton=()=>{const[c,u]=l(!1);return n(()=>{c?function(){const e=document.documentElement;e.requestFullscreen?e.requestFullscreen():e.mozRequestFullScreen?e.mozRequestFullScreen():e.webkitRequestFullscreen?e.webkitRequestFullscreen():e.msRequestFullscreen&&e.msRequestFullscreen()}():function(){const e=document;e.exitFullscreen?document.fullscreenElement&&document.hasFocus()&&document.exitFullscreen().catch(e=>{console.warn("Failed to exit fullscreen:",e)}):e.mozCancelFullScreen?e.mozCancelFullScreen():e.webkitExitFullscreen?e.webkitExitFullscreen():e.msExitFullscreen&&e.msExitFullscreen()}()},[c]),e.createElement(e.Fragment,null,c&&e.createElement(t,{iconName:"fullscreen_exit",onClick:()=>u(!1)}),!c&&e.createElement(t,{iconName:"fullscreen",onClick:()=>u(!0)}))};
1
+ import e,{useState as l,useEffect as n}from"react";import{MaterialIconButton as t}from"./MaterialIconButton.js";export const FullscreenButton=()=>{const[c,u]=l(!1);return n(()=>{c?function(){const e=document.documentElement;e.requestFullscreen?e.requestFullscreen():e.mozRequestFullScreen?e.mozRequestFullScreen():e.webkitRequestFullscreen?e.webkitRequestFullscreen():e.msRequestFullscreen&&e.msRequestFullscreen()}():function(){const e=document;e.exitFullscreen?document.fullscreenElement&&document.hasFocus()&&document.exitFullscreen().catch(e=>{console.warn("Failed to exit fullscreen:",e)}):e.mozCancelFullScreen?e.mozCancelFullScreen():e.webkitExitFullscreen?e.webkitExitFullscreen():e.msExitFullscreen&&e.msExitFullscreen()}()},[c]),e.createElement(e.Fragment,null,c&&e.createElement(t,{iconName:"fullscreen_exit",onClick:()=>u(!1)}),!c&&e.createElement(t,{iconName:"fullscreen",onClick:()=>u(!0)}))};
@@ -1 +1 @@
1
- import e,{forwardRef as t,useCallback as n,useEffect as r,useImperativeHandle as i,useLayoutEffect as a,useMemo as o,useRef as l,useState as s}from"react";import{Disposables as c}from"../common/Disposables";import{formatDuration as m}from"../common/TimeTools";import{TimeSlider as d}from"../engine/TimeSlider";import{ONE_YEAR as p}from"../ticks/TimeSliderConstants";import{ColoredSlider as u}from"./ColoredSlider";import{DateLabelWithPicker as h}from"./DateLabelWithPicker";import{DateRangeLabelWithPicker as g}from"./DateRangeLabelWithPicker";import{MaterialIconButton as f}from"./MaterialIconButton";import{MaterialTextButton as x}from"./MaterialTextButton";import{TimeLabelWithPicker as w}from"./TimeLabelWithPicker";const y={padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px"},v=[Date.now()-p,Date.now()];function E(e,t){return e[0]===t[0]&&e[1]===t[1]}function b(e){return JSON.stringify(e)}export const ReactTimeSlider=t(({timeRange:t,onTimeRange:n,selectRangeButton:a,noPlayButton:p,noTimeLabels:C,noRangeLabel:k,noDateLabels:R,theme:L,canHideLabels:S,verticalGap:N,engine:I,layers:_,loaders:D,showStats:z,currentSlider:M,...B},P)=>{const W=o(()=>new d(B),[]);r(()=>{B?.showCurrent&&(W.showCurrent=B?.showCurrent)},[B.showCurrent]),r(()=>{B?.showTicks&&(W.showTicks=B?.showTicks)},[B.showTicks]),r(()=>{B?.playSpeed&&(W.playSpeed=B?.playSpeed)},[B.playSpeed]),r(()=>{B?.liveRange&&(W.liveRange=B?.liveRange)},[B.liveRange]);const F=N||4,G=22+F+8+"px",H=22+F+22+F+8+"px",O=l(null),X=l(null),A=l(null),[J,U]=s(!1),[V,Y]=s(!1),[$,j]=s(!1),[q,K]=s(!1),[Q,Z]=s(!1),[ee,te]=s(Date.now()),ne=l(null);useMaterialIcons();const[re,ie]=s(t||v),ae=l(t||v),oe=void 0!==t,le=l(new Set),[se,ce]=s(!1);r(()=>{if(oe){const e=b(t);if(J&&!E(t,ae.current)&&!le.current.has(e)){ae.current=t;const[e,n]=t;W.setTimeRange({startTime:e,endTime:n})}le.current.delete(e)}},[J,t,oe]),r(()=>{const e=new c;return e.add(W.on("time",e=>{const{startTime:t,endTime:r}=e,i=[t,r];E(i,ae.current)||(ae.current=i,le.current.add(b(i)),ie(i),n?.(i))})),e.disposeLater()},[W]),r(()=>{U(!1);const e=new c;return W.init(X.current).then(()=>U(!0)),e.add(W.on("time",e=>{if(e.startTime!==ae.current[0]||e.endTime!==ae.current[1]){const{startTime:t,endTime:r}=e,i=[t,r];ae.current=i,ie(i),n?.(i)}})),e.add(W.on("playing",Y)),e.add(W.on("live",j)),e.add(W.on("current",te)),e.disposeLater()},[W]),r(()=>{D?.forEach(e=>W.addLayerLoader(e))},[D]),r(()=>{"boolean"==typeof B.showCurrent&&(W.showCurrent=B.showCurrent)},[B.showCurrent]),i(P,()=>({get timeSlider(){return W}})),r(()=>{if(B.showCurrent){const e=setInterval(()=>{A.current&&(A.current.available=Date.now())},10);return()=>clearInterval(e)}},[B.showCurrent]),r(()=>{J&&W.showStats(!0===z)},[J,z]),r(()=>{},[J]),r(()=>{J&&W.setEngine(I)},[J,I]),r(()=>{if(J){const e=new Set,t=new Set;W.getLayers().forEach(e=>t.add(e.id)),_.forEach(n=>{t.delete(n.id),e.add(n.id)}),W.getLayers().forEach(t=>e.delete(t.id));const n=_.filter(t=>e.has(t.id)),r=W.getLayers().filter(e=>t.has(e.id));r.length===W.getLayers().length&&n.length>0&&W.setTimeRange(n[0].model.getTimeRange()),r.forEach(e=>W.removeLayer(e)),n.forEach(e=>W.addLayer(e))}},[J,_]);r(()=>{const e=e=>{ne.current&&e.target&&e.target.parentElement?.getAttribute("layerpanelbutton")!==W.id&&!ne.current.contains(e.target)&&Z(!1)};if(Q)return document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[Q]);const me=e=>{const t=W.current,{duration:n,startTime:r}=W.getTimeRange(),i=(t-r)/n;i>1||i<0?W.setTimeRange({startTime:e-.5*n,endTime:e+.5*n}):W.setTimeRange({startTime:e-i*n,endTime:e+(1-i)*n}),W.setCurrent(e)};r(()=>{const e=()=>{if(O.current){let e=560;B.showCurrent&&(e+=160),p&&(e-=30),k&&(e-=160),C&&(e-=140),R&&(e-=185);const t=O.current.getBoundingClientRect().width;K(t<e)}};e();const t=new ResizeObserver(e);return O.current&&t.observe(O.current),window.addEventListener("resize",e),()=>{t.disconnect(),window.removeEventListener("resize",e)}},[B.showCurrent,p,k,C,R]);let de=42;B.showCurrent&&(de+=4),q&&!S&&(de+=26);const pe="dark"===L;return r(()=>{L&&W.setTheme(L)},[L]),e.createElement("div",{ref:O,style:{display:"block",position:"relative",borderRadius:"inherit",background:pe?"#222":"#ddd",width:"100%",height:"100%",padding:"8px",boxSizing:"border-box"}},Q&&e.createElement("div",{ref:ne,style:{position:"absolute",top:G,left:"8px",maxWidth:"200px",display:"flex",gap:"8px",alignItems:"center",paddingRight:"8px",paddingBottom:"8px",paddingTop:"8px",flexDirection:"column",zIndex:1e3,background:pe?"#222":"#ddd"}},W.getLayers().map(t=>e.createElement(T,{key:t.id,layer:t,timeSlider:W,isDarkMode:pe}))),e.createElement("div",{style:{position:"absolute",top:"8px",left:"8px",display:"flex",gap:"8px",alignItems:"center"}},e.createElement(f,{layerpanelbutton:W.id,iconName:"layers",onClick:e=>Z(e=>!e)}),e.createElement(f,{iconName:"skip_previous",onClick:()=>W.up({animate:1e3}),rotate:90}),!q&&!k&&e.createElement("span",{style:{...y}},m(re)),e.createElement(f,{iconName:"arrow_range",onClick:()=>W.fit({animate:3e3})}),a&&e.createElement(f,{iconName:"ink_selection",active:se,onClick:()=>{ce(!0),W.startSelectingRange(()=>ce(!1))}})),q&&!S&&B.showCurrent&&!k&&e.createElement("div",{style:{position:"absolute",top:G,left:"8px",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(re))),q&&!S&&!B.showCurrent&&!k&&e.createElement("div",{style:{position:"absolute",top:"8px",left:"50%",transform:"translateX(-50%)",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(re))),(!q||!S)&&e.createElement("div",{style:{position:"absolute",top:q?G:"8px",right:q?"8px":"50%",transform:q?void 0:"translateX(50%)",padding:0,display:"flex",gap:"8px",height:"22px",width:!q||B.showCurrent?void 0:"calc(100% - 16px)"}},!B.showCurrent&&(!C||!C)&&e.createElement(e.Fragment,null,!C&&e.createElement(w,{theme:L,time:re[0],setTime:e=>W.setTimeRange({startTime:e})}),q&&!C&&!R&&!B.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!R&&e.createElement(g,{theme:L,range:[re[0],re[1]],setRange:e=>W.setTimeRange({startTime:e[0],endTime:e[1]})}),q&&!C&&!R&&!B.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!C&&e.createElement(w,{theme:L,time:re[1]-1,setTime:e=>W.setTimeRange({endTime:e+1})})),B.showCurrent&&e.createElement(e.Fragment,null,!R&&e.createElement(h,{theme:L,time:ee,setTime:me}),!C&&e.createElement(w,{theme:L,time:ee,setTime:me}))),e.createElement("div",{style:{position:"absolute",top:"8px",right:"8px",display:"flex",gap:"8px",alignItems:"center"}},B.showCurrent&&e.createElement(e.Fragment,null,e.createElement(x,{text:"Live",onClick:()=>{W.live?W.pause():W.setLive(!0)},color:$?"#fff":pe?"#888":"#bbb"},e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",zIndex:1,lineHeight:1}},"radio_button_unchecked"),$&&e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",lineHeight:1,marginLeft:"-24px",color:"red"}},"radio_button_checked"))),e.createElement(f,{iconName:"skip_previous",onClick:()=>W.previous({animate:1e3})}),!p&&!V&&e.createElement(f,{iconName:"play_arrow",onClick:()=>W.play()}),!p&&V&&e.createElement(f,{iconName:"pause",onClick:()=>W.pause()}),e.createElement(f,{iconName:"skip_next",onClick:()=>W.next({animate:1e3})})),B.showCurrent&&e.createElement(u,{ref:A,value:ee,min:re[0],max:re[1],onValue:e=>{W.pause(),W.setCurrent(e)},onPointerMove:e=>W.updateHover({x:e.clientX,y:e.clientY}),onPointerLeave:()=>W.updateHover(null),matchWidth:!0,style:{width:"calc(100% - 16px)",left:"8px",position:"absolute",bottom:"26px",zIndex:1},...M}),e.createElement("div",{ref:X,style:{position:"absolute",top:q&&!S?H:G,left:"8px",borderRadius:"inherit",width:"calc(100% - 16px)",height:`calc(100% - ${de}px)`,background:pe?"#111":"#eee",overflow:"hidden"}}))});function T({layer:t,timeSlider:n,isDarkMode:i}){const[a,o]=s(t.visible);return r(()=>{t.on("visible",e=>o(e))},[]),e.createElement("div",{style:{display:"flex",gap:"8px",alignItems:"center",background:i?"#222":"#ddd",borderRadius:"4px",width:"100%"}},a&&e.createElement(f,{iconName:"visibility",onClick:()=>t.visible=!1}),!a&&e.createElement(f,{iconName:"visibility_off",color:"#888",onClick:()=>t.visible=!0}),e.createElement("span",{style:{...y,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",flexGrow:1,minWidth:0}},t.name||t.constructor.name+"#"+t.id.slice(t.id.length-4).toUpperCase()),e.createElement(f,{iconName:"center_focus_weak",onClick:()=>n.fit({animate:3e3,range:t.model.getTimeRange()})}))}export function useMaterialIcons(e="outlined"){r(()=>{const e=document.createElement("link");return e.rel="stylesheet",e.href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200",document.head.appendChild(e),()=>{document.head.removeChild(e)}},[e])}
1
+ import e,{forwardRef as t,useCallback as n,useEffect as r,useImperativeHandle as i,useLayoutEffect as a,useMemo as o,useRef as l,useState as s}from"react";import{Disposables as c}from"../common/Disposables.js";import{formatDuration as m}from"../common/TimeTools.js";import{TimeSlider as d}from"../engine/TimeSlider.js";import{ONE_YEAR as p}from"../ticks/TimeSliderConstants.js";import{ColoredSlider as u}from"./ColoredSlider.js";import{DateLabelWithPicker as h}from"./DateLabelWithPicker.js";import{DateRangeLabelWithPicker as g}from"./DateRangeLabelWithPicker.js";import{MaterialIconButton as f}from"./MaterialIconButton.js";import{MaterialTextButton as x}from"./MaterialTextButton.js";import{TimeLabelWithPicker as w}from"./TimeLabelWithPicker.js";const y={padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px"},b=[Date.now()-p,Date.now()];function v(e,t){return e[0]===t[0]&&e[1]===t[1]}function E(e){return JSON.stringify(e)}export const ReactTimeSlider=t(({timeRange:t,onTimeRange:n,selectRangeButton:a,noPlayButton:p,noTimeLabels:k,noRangeLabel:C,noDateLabels:L,theme:R,canHideLabels:S,verticalGap:N,engine:I,layers:_,loaders:D,showStats:z,currentSlider:j,...M},P)=>{const W=o(()=>new d(M),[]);r(()=>{M?.showCurrent&&(W.showCurrent=M?.showCurrent)},[M.showCurrent]),r(()=>{M?.showTicks&&(W.showTicks=M?.showTicks)},[M.showTicks]),r(()=>{M?.playSpeed&&(W.playSpeed=M?.playSpeed)},[M.playSpeed]),r(()=>{M?.liveRange&&(W.liveRange=M?.liveRange)},[M.liveRange]),r(()=>{M?.locale&&W.setLocale(M.locale)},[M.locale]),r(()=>{M?.weekLabel&&W.setWeekLabel(M.weekLabel,M.weekLabelPrefix)},[M.weekLabel,M.weekLabelPrefix]),r(()=>{W.setTickLabelStyler(M?.tickLabelStyler)},[M.tickLabelStyler]),r(()=>{W.setLimits(M?.limits)},[M.limits?.startTime,M.limits?.endTime]);const[,B]=s(0);r(()=>W.on("locale",()=>B(e=>e+1)),[W]);const F=N||4,G=22+F+8+"px",H=22+F+22+F+8+"px",O=l(null),X=l(null),A=l(null),[J,U]=s(!1),[V,Y]=s(!1),[$,q]=s(!1),[K,Q]=s(!1),[Z,ee]=s(!1),[te,ne]=s(Date.now()),re=l(null);useMaterialIcons();const[ie,ae]=s(t||b),oe=l(t||b),le=void 0!==t,se=l(new Set),[ce,me]=s(!1);r(()=>{if(le){const e=E(t);if(J&&!v(t,oe.current)&&!se.current.has(e)){oe.current=t;const[e,n]=t;W.setTimeRange({startTime:e,endTime:n})}se.current.delete(e)}},[J,t,le]),r(()=>{const e=new c;return e.add(W.on("time",e=>{const{startTime:t,endTime:r}=e,i=[t,r];v(i,oe.current)||(oe.current=i,se.current.add(E(i)),ae(i),n?.(i))})),e.disposeLater()},[W]),r(()=>{U(!1);const e=new c;return W.init(X.current).then(()=>U(!0)),e.add(W.on("time",e=>{if(e.startTime!==oe.current[0]||e.endTime!==oe.current[1]){const{startTime:t,endTime:r}=e,i=[t,r];oe.current=i,ae(i),n?.(i)}})),e.add(W.on("playing",Y)),e.add(W.on("live",q)),e.add(W.on("current",ne)),e.disposeLater()},[W]),r(()=>{D?.forEach(e=>W.addLayerLoader(e))},[D]),r(()=>{"boolean"==typeof M.showCurrent&&(W.showCurrent=M.showCurrent)},[M.showCurrent]),i(P,()=>({get timeSlider(){return W}})),r(()=>{if(M.showCurrent){const e=setInterval(()=>{A.current&&(A.current.available=Date.now())},10);return()=>clearInterval(e)}},[M.showCurrent]),r(()=>{J&&W.showStats(!0===z)},[J,z]),r(()=>{},[J]),r(()=>{J&&W.setEngine(I)},[J,I]),r(()=>{if(J){const e=new Set,t=new Set;W.getLayers().forEach(e=>t.add(e.id)),_.forEach(n=>{t.delete(n.id),e.add(n.id)}),W.getLayers().forEach(t=>e.delete(t.id));const n=_.filter(t=>e.has(t.id)),r=W.getLayers().filter(e=>t.has(e.id));r.length===W.getLayers().length&&n.length>0&&W.setTimeRange(n[0].model.getTimeRange()),r.forEach(e=>W.removeLayer(e)),n.forEach(e=>W.addLayer(e))}},[J,_]);r(()=>{const e=e=>{re.current&&e.target&&e.target.parentElement?.getAttribute("layerpanelbutton")!==W.id&&!re.current.contains(e.target)&&ee(!1)};if(Z)return document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[Z]);const de=e=>{const t=W.current,{duration:n,startTime:r}=W.getTimeRange(),i=(t-r)/n;i>1||i<0?W.setTimeRange({startTime:e-.5*n,endTime:e+.5*n}):W.setTimeRange({startTime:e-i*n,endTime:e+(1-i)*n}),W.setCurrent(e)};r(()=>{const e=()=>{if(O.current){let e=560;M.showCurrent&&(e+=160),p&&(e-=30),C&&(e-=160),k&&(e-=140),L&&(e-=185);const t=O.current.getBoundingClientRect().width;Q(t<e)}};e();const t=new ResizeObserver(e);return O.current&&t.observe(O.current),window.addEventListener("resize",e),()=>{t.disconnect(),window.removeEventListener("resize",e)}},[M.showCurrent,p,C,k,L]);let pe=42;M.showCurrent&&(pe+=4),K&&!S&&(pe+=26);const ue="dark"===R;return r(()=>{R&&W.setTheme(R)},[R]),e.createElement("div",{ref:O,style:{display:"block",position:"relative",borderRadius:"inherit",background:ue?"#222":"#ddd",width:"100%",height:"100%",padding:"8px",boxSizing:"border-box"}},Z&&e.createElement("div",{ref:re,style:{position:"absolute",top:G,left:"8px",maxWidth:"200px",display:"flex",gap:"8px",alignItems:"center",paddingRight:"8px",paddingBottom:"8px",paddingTop:"8px",flexDirection:"column",zIndex:1e3,background:ue?"#222":"#ddd"}},W.getLayers().map(t=>e.createElement(T,{key:t.id,layer:t,timeSlider:W,isDarkMode:ue}))),e.createElement("div",{style:{position:"absolute",top:"8px",left:"8px",display:"flex",gap:"8px",alignItems:"center"}},e.createElement(f,{layerpanelbutton:W.id,iconName:"layers",onClick:e=>ee(e=>!e)}),e.createElement(f,{iconName:"skip_previous",onClick:()=>W.up({animate:1e3}),rotate:90}),!K&&!C&&e.createElement("span",{style:{...y}},m(ie)),e.createElement(f,{iconName:"arrow_range",onClick:()=>W.fit({animate:3e3})}),a&&e.createElement(f,{iconName:"ink_selection",active:ce,onClick:()=>{me(!0),W.startSelectingRange(()=>me(!1))}})),K&&!S&&M.showCurrent&&!C&&e.createElement("div",{style:{position:"absolute",top:G,left:"8px",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(ie))),K&&!S&&!M.showCurrent&&!C&&e.createElement("div",{style:{position:"absolute",top:"8px",left:"50%",transform:"translateX(-50%)",display:"flex",gap:"8px",height:"22px"}},e.createElement("span",{style:{...y}},m(ie))),(!K||!S)&&e.createElement("div",{style:{position:"absolute",top:K?G:"8px",right:K?"8px":"50%",transform:K?void 0:"translateX(50%)",padding:0,display:"flex",gap:"8px",height:"22px",width:!K||M.showCurrent?void 0:"calc(100% - 16px)"}},!M.showCurrent&&(!k||!k)&&e.createElement(e.Fragment,null,!k&&e.createElement(w,{theme:R,time:ie[0],setTime:e=>W.setTimeRange({startTime:e})}),K&&!k&&!L&&!M.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!L&&e.createElement(g,{theme:R,range:[ie[0],ie[1]],setRange:e=>W.setTimeRange({startTime:e[0],endTime:e[1]})}),K&&!k&&!L&&!M.showCurrent&&e.createElement("div",{style:{flexGrow:1}}),!k&&e.createElement(w,{theme:R,time:ie[1]-1,setTime:e=>W.setTimeRange({endTime:e+1})})),M.showCurrent&&e.createElement(e.Fragment,null,!L&&e.createElement(h,{theme:R,time:te,setTime:de}),!k&&e.createElement(w,{theme:R,time:te,setTime:de}))),e.createElement("div",{style:{position:"absolute",top:"8px",right:"8px",display:"flex",gap:"8px",alignItems:"center"}},M.showCurrent&&e.createElement(e.Fragment,null,e.createElement(x,{text:"Live",onClick:()=>{W.live?W.pause():W.setLive(!0)},color:$?"#fff":ue?"#888":"#bbb"},e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",zIndex:1,lineHeight:1}},"radio_button_unchecked"),$&&e.createElement("span",{className:"material-symbols-outlined",style:{fontSize:"18px",lineHeight:1,marginLeft:"-24px",color:"red"}},"radio_button_checked"))),e.createElement(f,{iconName:"skip_previous",onClick:()=>W.previous({animate:1e3})}),!p&&!V&&e.createElement(f,{iconName:"play_arrow",onClick:()=>W.play()}),!p&&V&&e.createElement(f,{iconName:"pause",onClick:()=>W.pause()}),e.createElement(f,{iconName:"skip_next",onClick:()=>W.next({animate:1e3})})),M.showCurrent&&e.createElement(u,{ref:A,value:te,min:ie[0],max:ie[1],onValue:e=>{W.pause(),W.setCurrent(e)},onPointerMove:e=>W.updateHover({x:e.clientX,y:e.clientY}),onPointerLeave:()=>W.updateHover(null),matchWidth:!0,style:{width:"calc(100% - 16px)",left:"8px",position:"absolute",bottom:"26px",zIndex:1},...j}),e.createElement("div",{ref:X,style:{position:"absolute",top:K&&!S?H:G,left:"8px",borderRadius:"inherit",width:"calc(100% - 16px)",height:`calc(100% - ${pe}px)`,background:ue?"#111":"#eee",overflow:"hidden"}}))});function T({layer:t,timeSlider:n,isDarkMode:i}){const[a,o]=s(t.visible);return r(()=>{t.on("visible",e=>o(e))},[]),e.createElement("div",{style:{display:"flex",gap:"8px",alignItems:"center",background:i?"#222":"#ddd",borderRadius:"4px",width:"100%"}},a&&e.createElement(f,{iconName:"visibility",onClick:()=>t.visible=!1}),!a&&e.createElement(f,{iconName:"visibility_off",color:"#888",onClick:()=>t.visible=!0}),e.createElement("span",{style:{...y,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",flexGrow:1,minWidth:0}},t.name||t.constructor.name+"#"+t.id.slice(t.id.length-4).toUpperCase()),e.createElement(f,{iconName:"center_focus_weak",onClick:()=>n.fit({animate:3e3,range:t.model.getTimeRange()})}))}export function useMaterialIcons(e="outlined"){r(()=>{const e=document.createElement("link");return e.rel="stylesheet",e.href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200",document.head.appendChild(e),()=>{document.head.removeChild(e)}},[e])}
@@ -1 +1 @@
1
- import e,{useEffect as t,useRef as o,useState as r}from"react";import{formatTime as n}from"../common/TimeTools";export const LABEL_WITH_PICKER_SPAN_STYLE=e=>({padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer"});export const LABEL_WITH_PICKER_HOVER_STYLE=e=>({padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer",background:"#df0",color:"#000",fontWeight:"bold"});export function TimeLabelWithPicker({time:i,setTime:c,theme:p,...a}){const s=o(i),u=o(null),[d,l]=r(!1);t(()=>{s.current=i},[i]),t(()=>{if(u.current){const e=()=>{const e=u.current.value,[t,o,r]=e.split(":").map(e=>parseInt(e)),n=new Date(s.current);n.setHours(t),n.setMinutes(o),n.setSeconds(r),c(n.getTime())};return u.current.addEventListener("input",e),()=>{u.current?.removeEventListener("input",e)}}},[]);return e.createElement("div",{...a,style:{position:"relative",...a.style}},e.createElement("span",{style:d?{padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer",background:"#df0",color:"#000",fontWeight:"bold"}:{padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer"},onClick:()=>{if(u.current){const e=new Date(s.current),t=e.getHours().toString().padStart(2,"0"),o=e.getMinutes().toString().padStart(2,"0"),r=e.getSeconds().toString().padStart(2,"0");u.current.value=t+":"+o+":"+r,u.current.showPicker()}},onMouseEnter:()=>l(!0),onMouseLeave:()=>l(!1)},n(i)),e.createElement("input",{ref:u,step:1,type:"time",style:{position:"absolute",left:"0px",visibility:"hidden",colorScheme:"dark"===p?"dark":"light"}}))}
1
+ import e,{useEffect as t,useRef as o,useState as r}from"react";import{formatTime as n}from"../common/TimeTools.js";export const LABEL_WITH_PICKER_SPAN_STYLE=e=>({padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer"});export const LABEL_WITH_PICKER_HOVER_STYLE=e=>({padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer",background:"#df0",color:"#000",fontWeight:"bold"});export function TimeLabelWithPicker({time:i,setTime:c,theme:p,...s}){const a=o(i),u=o(null),[d,l]=r(!1);t(()=>{a.current=i},[i]),t(()=>{if(u.current){const e=()=>{const e=u.current.value,[t,o,r]=e.split(":").map(e=>parseInt(e)),n=new Date(a.current);n.setHours(t),n.setMinutes(o),n.setSeconds(r),c(n.getTime())};return u.current.addEventListener("input",e),()=>{u.current?.removeEventListener("input",e)}}},[]);return e.createElement("div",{...s,style:{position:"relative",...s.style}},e.createElement("span",{style:d?{padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer",background:"#df0",color:"#000",fontWeight:"bold"}:{padding:"4px 8px",background:"#0008",borderRadius:"4px",color:"#fff",fontFamily:"monospace",fontSize:"12px",cursor:"pointer"},onClick:()=>{if(u.current){const e=new Date(a.current),t=e.getHours().toString().padStart(2,"0"),o=e.getMinutes().toString().padStart(2,"0"),r=e.getSeconds().toString().padStart(2,"0");u.current.value=t+":"+o+":"+r,u.current.showPicker()}},onMouseEnter:()=>l(!0),onMouseLeave:()=>l(!1)},n(i)),e.createElement("input",{ref:u,step:1,type:"time",style:{position:"absolute",left:"0px",visibility:"hidden",colorScheme:"dark"===p?"dark":"light"}}))}
@@ -9,5 +9,9 @@ export type TimeSliderIntervalsStyle = TimeSliderStyle & {
9
9
  color?: string;
10
10
  opacity?: number;
11
11
  width?: number;
12
+ /** 'all' (default) draws a border on every side, including between stacked segments. 'vertical' only draws left/right, between neighbouring bars. */
13
+ sides?: 'all' | 'vertical';
14
+ /** Anti-aliasing ramp between border and fill, in pixels. 0 gives a hard edge (crisp borders between neighbouring bars). Defaults to 1.5 (2D) / 1 (3D). */
15
+ smoothing?: number;
12
16
  };
13
17
  };
@@ -1 +1 @@
1
- import{BufferGeometry as e,GridHelper as t,Group as s,Line as i,LineBasicMaterial as o,Mesh as a,MeshBasicMaterial as n,PerspectiveCamera as r,PlaneGeometry as d,Scene as h,Vector2 as l,Vector3 as m,WebGLRenderer as c}from"three";import p from"three/examples/jsm/libs/stats.module.js";import{Disposables as w}from"../common/Disposables";import{getStartOfWeek as u}from"../common/TimeTools";import{TimeSliderEngine as f}from"../engine/TimeSliderEngine";import{ONE_MONTH as x,ONE_SECOND as b,ONE_WEEK as v}from"../ticks/TimeSliderConstants";import{TimeTicksBar as g}from"../ticks/TimeTicksBar";export class Three3DTimeSliderEngine extends f{constructor(){super(new h),this.camera=new r}showStats(e){if(this.container)if(e){this.stats=new p;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(r){const h=new c({antialias:!0});this.renderer=h,this.disposables=new w,this.container=r,this.renderer.setClearColor("#111"),r.appendChild(h.domElement),this.disposables.add(()=>r.removeChild(h.domElement)),this.camera.position.z=2.073,this.container.addEventListener("keydown",e=>{const t=1/this.camera.scale.x;"KeyQ"===e.code&&(e.preventDefault(),this.camera.rotateY(t)),"KeyE"===e.code&&(e.preventDefault(),this.camera.rotateY(-t))});const l=new s,p=new s;l.scale.x=b,l.position.z=.9,this.scene.add(l);const f=new d(100,1),y=new n({color:"#444"}),T=new a(f,y);T.position.z=.5,T.position.y=-.001,T.rotateX(-.5*Math.PI),l.add(T);const z=new a(f,y);z.position.z=.5,z.position.y=-.001,z.rotateX(-.5*Math.PI),p.add(z);const C=new o({color:"#444"}),D=(new e).setFromPoints([new m(0,0,0),new m(0,1,0),new m(0,1,1)]),S=(new e).setFromPoints([new m(-1,0,0),new m(1,0,0)]),V=new i(S,C),k=new i(S,C);k.position.y=1,p.add(V),p.add(k),p.position.z=.9,this.scene.add(p);const P=[],E=new o({color:"#000"}),M=(new e).setFromPoints([new m(0,0,1),new m(0,0,-1e3)]),j=new s;p.add(j);const F=[];let R,I,X,K;const O=()=>{Date.now();const e=this.getSize().width,{startTime:t,duration:s,endTime:o}=this.getTimeRange(),a=s,[n]=g.getSpacing(e,a),r=.5*(o+t);if(n<x){p.visible=!1,l.scale.x=n;let e=r%n;n>=v&&(e=r-u(r)),l.position.x=-e,l.visible=!0}else{l.visible=!1;let e=0,a=0;g.generateTicks(n,t-3*s,o+3*s,(n,r)=>{t-s<=n&&n<=o+s&&(P[e]||(P[e]=new i(D,C),p.add(P[e])),P[e].position.x=n,P[e].visible=!0,e++),F[a]||(F[a]=new i(M,E),p.add(F[a])),F[a].position.x=n,F[a].visible=!0,a++});for(let e=a;e<F.length;e++)F[e].visible=!1;for(let t=e;t<P.length;t++)P[t].visible=!1;K&&(K.scale.x=1e3*s),z.scale.x=s,z.position.x=r,K&&(K.position.x=-500*s),V.scale.x=1e3*s,k.scale.x=1e3*s,p.position.x=-r,p.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#000":"#fff",i="dark"===e?"#444":"#bbb";this.renderer?.setClearColor("dark"===e?"#111":"#eee"),R&&l.remove(R),R?.dispose(),R=new t(1e3,1e3,s,s),l.add(R),X&&l.remove(X),X?.dispose(),X=new t(1e3,1e3,i,i),X.position.z=500,X.position.y=1,l.add(X),I&&l.remove(I),I?.dispose(),I=new t(1e3,1e3,i,i),I.position.y=-499,I.rotateX(-.5*Math.PI),l.add(I),K&&p.remove(K),K?.dispose(),K=new t(200,200,s,s),K.position.z=-101,p.add(K),y.color.set(i),C.color.set(i),E.color.set(s),O()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",O)),this.disposables.add(this.on("size",O)),this.updateValueRange({minValue:0,maxValue:1});let Y=!0;this.disposables.add(()=>Y=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>h.dispose());const q=()=>{Y&&(h.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(q))};q()}updateTimeRange(e){const t=this.camera.scale.x,{duration:s}=e;this.camera.scale.x=s,t!==s&&(this.camera.rotation.y=this.camera.rotation.y*t/s),this.camera.updateProjectionMatrix()}updateValueRange(e){const t=e.maxValue-e.minValue;this.camera.scale.y=t,this.camera.position.y=e.minValue+.5*t,this.camera.updateProjectionMatrix()}updateSize(e,t){this.renderer?.setSize(e,t,!0)}destroy(){this.disposables?.dispose()}setHorizontalCompression(e){if(!this.renderer)return;if(e<=1)return void this.camera.clearViewOffset();const{width:t,height:s}=this.renderer.getSize(new l),i=t*e,o=s,a=(i-t)/2,n=t,r=s;this.camera.setViewOffset(i,o,a,0,n,r)}}
1
+ import{BufferGeometry as e,GridHelper as s,Group as t,Line as i,LineBasicMaterial as o,Mesh as a,MeshBasicMaterial as n,PerspectiveCamera as r,PlaneGeometry as d,Scene as h,Vector2 as l,Vector3 as m,WebGLRenderer as c}from"three";import p from"three/examples/jsm/libs/stats.module.js";import{Disposables as w}from"../common/Disposables.js";import{getStartOfWeek as u}from"../common/TimeTools.js";import{TimeSliderEngine as f}from"../engine/TimeSliderEngine.js";import{ONE_MONTH as x,ONE_SECOND as b,ONE_WEEK as v}from"../ticks/TimeSliderConstants.js";import{TimeTicksBar as g}from"../ticks/TimeTicksBar.js";export class Three3DTimeSliderEngine extends f{constructor(){super(new h),this.camera=new r}showStats(e){if(this.container)if(e){this.stats=new p;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(r){const h=new c({antialias:!0});this.renderer=h,this.disposables=new w,this.container=r,this.renderer.setClearColor("#111"),r.appendChild(h.domElement),this.disposables.add(()=>r.removeChild(h.domElement)),this.camera.position.z=2.073,this.container.addEventListener("keydown",e=>{const s=1/this.camera.scale.x;"KeyQ"===e.code&&(e.preventDefault(),this.camera.rotateY(s)),"KeyE"===e.code&&(e.preventDefault(),this.camera.rotateY(-s))});const l=new t,p=new t;l.scale.x=b,l.position.z=.9,this.scene.add(l);const f=new d(100,1),y=new n({color:"#444"}),T=new a(f,y);T.position.z=.5,T.position.y=-.001,T.rotateX(-.5*Math.PI),l.add(T);const z=new a(f,y);z.position.z=.5,z.position.y=-.001,z.rotateX(-.5*Math.PI),p.add(z);const C=new o({color:"#444"}),j=(new e).setFromPoints([new m(0,0,0),new m(0,1,0),new m(0,1,1)]),D=(new e).setFromPoints([new m(-1,0,0),new m(1,0,0)]),S=new i(D,C),V=new i(D,C);V.position.y=1,p.add(S),p.add(V),p.position.z=.9,this.scene.add(p);const k=[],P=new o({color:"#000"}),E=(new e).setFromPoints([new m(0,0,1),new m(0,0,-1e3)]),M=new t;p.add(M);const F=[];let R,I,X,K;const O=()=>{Date.now();const e=this.getSize().width,{startTime:s,duration:t,endTime:o}=this.getTimeRange(),a=t,[n]=g.getSpacing(e,a),r=.5*(o+s);if(n<x){p.visible=!1,l.scale.x=n;let e=r%n;n>=v&&(e=r-u(r)),l.position.x=-e,l.visible=!0}else{l.visible=!1;let e=0,a=0;g.generateTicks(n,s-3*t,o+3*t,(n,r)=>{s-t<=n&&n<=o+t&&(k[e]||(k[e]=new i(j,C),p.add(k[e])),k[e].position.x=n,k[e].visible=!0,e++),F[a]||(F[a]=new i(E,P),p.add(F[a])),F[a].position.x=n,F[a].visible=!0,a++});for(let e=a;e<F.length;e++)F[e].visible=!1;for(let s=e;s<k.length;s++)k[s].visible=!1;K&&(K.scale.x=1e3*t),z.scale.x=t,z.position.x=r,K&&(K.position.x=-500*t),S.scale.x=1e3*t,V.scale.x=1e3*t,p.position.x=-r,p.visible=!0}};this.onThemeChange=e=>{const t="dark"===e?"#000":"#fff",i="dark"===e?"#444":"#bbb";this.renderer?.setClearColor("dark"===e?"#111":"#eee"),R&&l.remove(R),R?.dispose(),R=new s(1e3,1e3,t,t),l.add(R),X&&l.remove(X),X?.dispose(),X=new s(1e3,1e3,i,i),X.position.z=500,X.position.y=1,l.add(X),I&&l.remove(I),I?.dispose(),I=new s(1e3,1e3,i,i),I.position.y=-499,I.rotateX(-.5*Math.PI),l.add(I),K&&p.remove(K),K?.dispose(),K=new s(200,200,t,t),K.position.z=-101,p.add(K),y.color.set(i),C.color.set(i),P.color.set(t),O()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",O)),this.disposables.add(this.on("size",O)),this.updateValueRange({minValue:0,maxValue:1});let Y=!0;this.disposables.add(()=>Y=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>h.dispose());const q=()=>{Y&&(h.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(q))};q()}updateTimeRange(e){const s=this.camera.scale.x,{duration:t}=e;this.camera.scale.x=t,s!==t&&(this.camera.rotation.y=this.camera.rotation.y*s/t),this.camera.updateProjectionMatrix()}updateValueRange(e){const s=e.maxValue-e.minValue;this.camera.scale.y=s,this.camera.position.y=e.minValue+.5*s,this.camera.updateProjectionMatrix()}updateSize(e,s){this.renderer?.setSize(e,s,!0)}destroy(){this.disposables?.dispose()}setHorizontalCompression(e){if(!this.renderer)return;if(e<=1)return void this.camera.clearViewOffset();const{width:s,height:t}=this.renderer.getSize(new l),i=s*e,o=t,a=(i-s)/2,n=s,r=t;this.camera.setViewOffset(i,o,a,0,n,r)}}
@@ -1 +1 @@
1
- import{TimeSlider as e}from"../engine/TimeSlider";import{ThreeTimeSliderEngine as r}from"./ThreeTimeSliderEngine";export class ThreeTimeSlider extends e{constructor(e){super(e),this.setEngine(new r)}}
1
+ import{TimeSlider as e}from"../engine/TimeSlider.js";import{ThreeTimeSliderEngine as r}from"./ThreeTimeSliderEngine.js";export class ThreeTimeSlider extends e{constructor(e){super(e),this.setEngine(new r)}}
@@ -1 +1 @@
1
- import{BufferGeometry as e,GridHelper as t,Group as s,Line as i,LineBasicMaterial as o,OrthographicCamera as a,Scene as n,Vector3 as r,WebGLRenderer as h}from"three";import m from"three/examples/jsm/libs/stats.module.js";import{Disposables as d}from"../common/Disposables";import{TimeSliderEngine as l}from"../engine/TimeSliderEngine";import{TimeTicksBar as c}from"../ticks/TimeTicksBar";import{getStartOfWeek as p}from"../common/TimeTools";import{ONE_MONTH as b,ONE_SECOND as T,ONE_WEEK as u}from"../ticks/TimeSliderConstants";export class ThreeTimeSliderEngine extends l{constructor(){super(new n),this.camera=new a(0,1,1,0)}showStats(e){if(this.container)if(e){this.stats=new m;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(a){const n="dark"===this.theme;this.container=a;const m=new h({antialias:!0});this.renderer=m,this.disposables=new d,this.renderer.setClearColor(n?"#111":"#eee"),a.appendChild(this.renderer.domElement),this.disposables.add(()=>a.removeChild(m.domElement)),this.camera.position.z=10;const l=new s,g=new s;l.scale.x=T,this.scene.add(l);const w=new o({color:n?"#444":"#bbb"}),f=(new e).setFromPoints([new r(0,0,0),new r(0,1,0),new r(0,1,1)]);this.scene.add(g);const x=[];let C;const v=()=>{const e=this.getSize().width,{startTime:t,duration:s,endTime:o}=this.getTimeRange(),a=s,[n]=c.getSpacing(e,a),r=.5*(o+t);if(n<b){g.visible=!1,l.scale.x=n;let e=r%n;n>=u&&(e=r-p(r)),l.position.x=-e,l.visible=!0}else{l.visible=!1;let e=0;c.generateTicks(n,t-3*s,o+3*s,(a,n)=>{t-s<=a&&a<=o+s&&(x[e]||(x[e]=new i(f,w),g.add(x[e])),x[e].position.x=a,x[e].visible=!0,e++)});for(let t=e;t<x.length;t++)x[t].visible=!1;g.position.x=-r,g.visible=!0}};this.onThemeChange=e=>{const s="dark"===e?"#444":"#bbb";this.renderer?.setClearColor("dark"===e?"#111":"#eee"),C&&l.remove(C),C=new t(1e3,1e3,s,s),C.position.y=-499,C.rotateX(-.5*Math.PI),l.add(C),w.color.set(s),v()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",v)),this.disposables.add(this.on("size",v));let S=!0;this.disposables.add(()=>S=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>m.dispose());const k=()=>{S&&(m.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(k))};k()}destroy(){this.disposables?.dispose()}updateTimeRange(e){const{startTime:t,duration:s}=e;this.camera.left=-.5*s,this.camera.right=.5*s,this.camera.updateProjectionMatrix()}updateValueRange(e){this.camera.top=e.maxValue,this.camera.bottom=e.minValue,this.camera.updateProjectionMatrix()}updateSize(e,t){this.renderer?.setSize(e,t)}}
1
+ import{BufferGeometry as e,GridHelper as s,Group as t,Line as i,LineBasicMaterial as o,OrthographicCamera as a,Scene as n,Vector3 as r,WebGLRenderer as h}from"three";import m from"three/examples/jsm/libs/stats.module.js";import{Disposables as d}from"../common/Disposables.js";import{TimeSliderEngine as l}from"../engine/TimeSliderEngine.js";import{TimeTicksBar as c}from"../ticks/TimeTicksBar.js";import{getStartOfWeek as p}from"../common/TimeTools.js";import{ONE_MONTH as b,ONE_SECOND as T,ONE_WEEK as u}from"../ticks/TimeSliderConstants.js";export class ThreeTimeSliderEngine extends l{constructor(){super(new n),this.camera=new a(0,1,1,0)}showStats(e){if(this.container)if(e){this.stats=new m;const e=this.stats.dom;e.style.position="absolute",e.style.top="auto",e.style.bottom="32px",e.style.left="8px",this.container.appendChild(e),this.statsDom=e}else this.statsDom&&(this.container.removeChild(this.statsDom),this.statsDom=void 0,this.stats=void 0)}setTheme(e){super.setTheme(e),this.onThemeChange?.(e)}async init(a){const n="dark"===this.theme;this.container=a;const m=new h({antialias:!0});this.renderer=m,this.disposables=new d,this.renderer.setClearColor(n?"#111":"#eee"),a.appendChild(this.renderer.domElement),this.disposables.add(()=>a.removeChild(m.domElement)),this.camera.position.z=10;const l=new t,g=new t;l.scale.x=T,this.scene.add(l);const w=new o({color:n?"#444":"#bbb"}),f=(new e).setFromPoints([new r(0,0,0),new r(0,1,0),new r(0,1,1)]);this.scene.add(g);const x=[];let C;const v=()=>{const e=this.getSize().width,{startTime:s,duration:t,endTime:o}=this.getTimeRange(),a=t,[n]=c.getSpacing(e,a),r=.5*(o+s);if(n<b){g.visible=!1,l.scale.x=n;let e=r%n;n>=u&&(e=r-p(r)),l.position.x=-e,l.visible=!0}else{l.visible=!1;let e=0;c.generateTicks(n,s-3*t,o+3*t,(a,n)=>{s-t<=a&&a<=o+t&&(x[e]||(x[e]=new i(f,w),g.add(x[e])),x[e].position.x=a,x[e].visible=!0,e++)});for(let s=e;s<x.length;s++)x[s].visible=!1;g.position.x=-r,g.visible=!0}};this.onThemeChange=e=>{const t="dark"===e?"#444":"#bbb";this.renderer?.setClearColor("dark"===e?"#111":"#eee"),C&&l.remove(C),C=new s(1e3,1e3,t,t),C.position.y=-499,C.rotateX(-.5*Math.PI),l.add(C),w.color.set(t),v()},this.onThemeChange(this.theme),this.disposables.add(this.on("time",v)),this.disposables.add(this.on("size",v));let j=!0;this.disposables.add(()=>j=!1),this.disposables.add(()=>this.scene.clear()),this.disposables.add(()=>m.dispose());const S=()=>{j&&(m.render(this.scene,this.camera),this.stats&&this.stats.update(),requestAnimationFrame(S))};S()}destroy(){this.disposables?.dispose()}updateTimeRange(e){const{startTime:s,duration:t}=e;this.camera.left=-.5*t,this.camera.right=.5*t,this.camera.updateProjectionMatrix()}updateValueRange(e){this.camera.top=e.maxValue,this.camera.bottom=e.minValue,this.camera.updateProjectionMatrix()}updateSize(e,s){this.renderer?.setSize(e,s)}}
@@ -1 +1 @@
1
- import{BufferAttribute as t,BufferGeometry as e,DynamicDrawUsage as i}from"three";import{ONE_MINUTE as n}from"../../ticks/TimeSliderConstants";export class GrowingBufferGeometry extends e{constructor(e,s,r){super(),this.idToIndex=new Map;const d=Date.now();this.bufferSize=r||1e3;const o=Math.ceil(e.data.length/this.bufferSize),h=o*this.bufferSize;this.positionAttribute=new t(new Float32Array(3*h),3),this.positionAttribute.setUsage(i);const a=[],u=new GrowingIndexAttribute(this,e.data.length,this.bufferSize,o),x=e.data,f=x.map(s).filter(t=>null!==t).flat();this.positionAttribute.array.set(f),x.forEach((t,e)=>this.idToIndex.set(t.id,e)),this.positionAttribute.needsUpdate=!0,this.setAttribute("position",this.positionAttribute);const A=()=>{const e=this.idToIndex.size+a.length,s=this.positionAttribute.array.length/3;if(e>s||e<s-this.bufferSize&&Date.now()-d>n){const n=Math.ceil(e/this.bufferSize),s=new Float32Array(n*this.bufferSize*3);s.set(this.positionAttribute.array.slice(0,s.length)),this.positionAttribute=new t(s,3),this.positionAttribute.setUsage(i),this.setAttribute("position",this.positionAttribute)}},c=(t,e,i)=>{this.positionAttribute.array.set(e,3*t),i||this.positionAttribute.addUpdateRange(3*t,3)},b=(t,e,i)=>{if(t)if(this.idToIndex.has(e.id))p(t,e);else{let n=this.idToIndex.size;a.length>0&&(n=a.shift()),u.showIndex(n),this.idToIndex.set(e.id,n),A(),c(n,t,i)}else I(e.id)},p=(t,e)=>{if(t)if(this.idToIndex.has(e.id)){const i=this.idToIndex.get(e.id);c(i,t)}else b(t,e);else I(e.id)},I=t=>{if(this.idToIndex.has(t)){const e=this.idToIndex.get(t);this.idToIndex.delete(t),a.push(e),u.hideIndex(e)}};e.on("added",t=>{b(s(t),t),this.positionAttribute.needsUpdate=!0}),e.on("updated",t=>{p(s(t.updated),t.updated),this.positionAttribute.needsUpdate=!0}),e.on("removed",t=>{I(t),this.positionAttribute.needsUpdate=!0})}elementIndex(t){const e="string"==typeof t?t:t.id;return this.idToIndex.get(e)}}export class GrowingIndexAttribute{constructor(e,i,n,s){this.geometry=e,this.creationTime=Date.now(),this.bufferSize=n||1e3,this.currentIndexCount=i||0;const r=s||1;this.indexArray=new Uint32Array(this.bufferSize*r),this.currentIndexCount&&this.indexArray.set(Array.from({length:this.currentIndexCount},(t,e)=>e)),this.indexAttribute=new t(this.indexArray,1),e.setIndex(this.indexAttribute),e.setDrawRange(0,this.currentIndexCount)}showIndex(t){for(let e=0;e<this.currentIndexCount;e++)if(this.indexArray[e]===t)return;this.countChanged(this.currentIndexCount++),this.indexArray[this.currentIndexCount-1]=t,this.indexAttribute.addUpdateRange(this.currentIndexCount-1,1),this.indexAttribute.needsUpdate=!0}hideIndex(t){for(let e=0;e<this.currentIndexCount;e++)if(this.indexArray[e]===t){this.countChanged(this.currentIndexCount--),this.indexArray[e]=this.indexArray[this.currentIndexCount],this.indexAttribute.addUpdateRange(e,1),this.indexAttribute.needsUpdate=!0;break}}countChanged(e){const i=this.indexAttribute.array.length;if(e>i||e<i-this.bufferSize&&Date.now()-this.creationTime>n){const i=Math.ceil(e/this.bufferSize),n=new Uint32Array(i*this.bufferSize);n.set(this.indexArray.slice(0,n.length)),this.indexArray=n,this.indexAttribute=new t(this.indexArray,1),this.geometry.setIndex(this.indexAttribute)}this.geometry.setDrawRange(0,this.currentIndexCount)}}
1
+ import{BufferAttribute as t,BufferGeometry as e,DynamicDrawUsage as i}from"three";import{ONE_MINUTE as n}from"../../ticks/TimeSliderConstants.js";export class GrowingBufferGeometry extends e{constructor(e,s,r){super(),this.idToIndex=new Map;const d=Date.now();this.bufferSize=r||1e3;const o=Math.ceil(e.data.length/this.bufferSize),h=o*this.bufferSize;this.positionAttribute=new t(new Float32Array(3*h),3),this.positionAttribute.setUsage(i);const a=[],u=new GrowingIndexAttribute(this,e.data.length,this.bufferSize,o),x=e.data,f=x.map(s).filter(t=>null!==t).flat();this.positionAttribute.array.set(f),x.forEach((t,e)=>this.idToIndex.set(t.id,e)),this.positionAttribute.needsUpdate=!0,this.setAttribute("position",this.positionAttribute);const A=()=>{const e=this.idToIndex.size+a.length,s=this.positionAttribute.array.length/3;if(e>s||e<s-this.bufferSize&&Date.now()-d>n){const n=Math.ceil(e/this.bufferSize),s=new Float32Array(n*this.bufferSize*3);s.set(this.positionAttribute.array.slice(0,s.length)),this.positionAttribute=new t(s,3),this.positionAttribute.setUsage(i),this.setAttribute("position",this.positionAttribute)}},c=(t,e,i)=>{this.positionAttribute.array.set(e,3*t),i||this.positionAttribute.addUpdateRange(3*t,3)},b=(t,e,i)=>{if(t)if(this.idToIndex.has(e.id))p(t,e);else{let n=this.idToIndex.size;a.length>0&&(n=a.shift()),u.showIndex(n),this.idToIndex.set(e.id,n),A(),c(n,t,i)}else I(e.id)},p=(t,e)=>{if(t)if(this.idToIndex.has(e.id)){const i=this.idToIndex.get(e.id);c(i,t)}else b(t,e);else I(e.id)},I=t=>{if(this.idToIndex.has(t)){const e=this.idToIndex.get(t);this.idToIndex.delete(t),a.push(e),u.hideIndex(e)}};e.on("added",t=>{b(s(t),t),this.positionAttribute.needsUpdate=!0}),e.on("updated",t=>{p(s(t.updated),t.updated),this.positionAttribute.needsUpdate=!0}),e.on("removed",t=>{I(t),this.positionAttribute.needsUpdate=!0})}elementIndex(t){const e="string"==typeof t?t:t.id;return this.idToIndex.get(e)}}export class GrowingIndexAttribute{constructor(e,i,n,s){this.geometry=e,this.creationTime=Date.now(),this.bufferSize=n||1e3,this.currentIndexCount=i||0;const r=s||1;this.indexArray=new Uint32Array(this.bufferSize*r),this.currentIndexCount&&this.indexArray.set(Array.from({length:this.currentIndexCount},(t,e)=>e)),this.indexAttribute=new t(this.indexArray,1),e.setIndex(this.indexAttribute),e.setDrawRange(0,this.currentIndexCount)}showIndex(t){for(let e=0;e<this.currentIndexCount;e++)if(this.indexArray[e]===t)return;this.countChanged(this.currentIndexCount++),this.indexArray[this.currentIndexCount-1]=t,this.indexAttribute.addUpdateRange(this.currentIndexCount-1,1),this.indexAttribute.needsUpdate=!0}hideIndex(t){for(let e=0;e<this.currentIndexCount;e++)if(this.indexArray[e]===t){this.countChanged(this.currentIndexCount--),this.indexArray[e]=this.indexArray[this.currentIndexCount],this.indexAttribute.addUpdateRange(e,1),this.indexAttribute.needsUpdate=!0;break}}countChanged(e){const i=this.indexAttribute.array.length;if(e>i||e<i-this.bufferSize&&Date.now()-this.creationTime>n){const i=Math.ceil(e/this.bufferSize),n=new Uint32Array(i*this.bufferSize);n.set(this.indexArray.slice(0,n.length)),this.indexArray=n,this.indexAttribute=new t(this.indexArray,1),this.geometry.setIndex(this.indexAttribute)}this.geometry.setDrawRange(0,this.currentIndexCount)}}
@@ -1 +1 @@
1
- import{Color as t,DynamicDrawUsage as e,InstancedBufferAttribute as s,Matrix4 as i,Vector4 as r}from"three";import{Disposables as o}from"../../common/Disposables";import{ONE_MINUTE as n}from"../../ticks/TimeSliderConstants";const a=(new i).makeScale(0,0,0).makeTranslation(-1e3,-1e3,-1e3);export class GrowingInstanceMatrix{constructor(i,h,l){this.listeners=new Set,this.countListeners=new Set,this.idToIndex=new Map,this.disposables=new o,this.splits=[],this.meshes=[];const{bufferSize:c,colorProvider:p,defaultFill:f,defaultBorder:x,splitProvider:g,maxSplits:b}=l||{};this.colorProvider=p,this.splitProvider=g,this.maxSplits=b||4;const u=Date.now(),C=i.data;this.count=C.length;const w=[];this.bufferSize=c||1e3;const m=Math.ceil(i.data.length/this.bufferSize)*this.bufferSize,M=new Float32Array(16*m),U=C.map(t=>({m:h(t),id:t.id})).filter(t=>null!==t.m);if(U.forEach(t=>this.idToIndex.set(t.id,this.idToIndex.size)),U.forEach((t,e)=>t.m.toArray(M,16*e)),this.instanceMatrix=new s(M,16),this.colorProvider){const e=new Float32Array(3*m),i=new Float32Array(3*m);C.forEach((s,r)=>{const o=this.colorProvider(s),n=new t(o?.fill||f||"#df0");e.set([n.r,n.g,n.b],3*r);const a=new t(o?.stroke||x||"#000");i.set([a.r,a.g,a.b],3*r)}),this.instanceColor=new s(e,3),this.borderColor=new s(i,3)}else this.instanceColor=new s(new Float32Array,3),this.borderColor=new s(new Float32Array,3);if(this.splitProvider){const t=[];for(let e=0;e<this.maxSplits;e+=4)t.push(new Float32Array(4*m));C.forEach((e,s)=>{const i=this.splitProvider(e);for(let e=0;e<this.maxSplits;e+=4){const r=i.slice(e,e+4);t[e/4].set(r,4*s)}}),this.splits=t.map(t=>new s(t,4))}const y=new t,v=(new r,()=>{const t=this.idToIndex.size+w.length;if(t!==this.count){this.count=t;const i=this.instanceMatrix.array.length/16;if(t>i||t<i-this.bufferSize&&Date.now()-u>n){const i=Math.ceil(t/this.bufferSize),r=i*this.bufferSize,o=new Float32Array(16*r);if(o.set(this.instanceMatrix.array),this.instanceMatrix=new s(o,16),this.instanceMatrix.setUsage(e),this.instanceMatrix.needsUpdate=!0,this.colorProvider){const t=new Float32Array(i*this.bufferSize*3);t.set(this.instanceColor.array),this.instanceColor=new s(t,3),this.instanceColor.setUsage(e),this.instanceColor.needsUpdate=!0;const r=new Float32Array(i*this.bufferSize*3);r.set(this.borderColor.array),this.borderColor=new s(r,3),this.borderColor.setUsage(e),this.borderColor.needsUpdate=!0}this.splitProvider&&(this.splits=this.splits.map(t=>{const r=new Float32Array(i*this.bufferSize*4);r.set(t.array);const o=new s(r,1);return o.setUsage(e),o.needsUpdate=!0,o})),this.matrixChanged()}this.countListeners.forEach(t=>t(this.count))}}),P=(e,s)=>{if(e)if(this.idToIndex.has(s.id))S(e,s);else{let i=this.idToIndex.size;if(w.length>0&&(i=w.shift()),this.idToIndex.set(s.id,i),v(),e.toArray(this.instanceMatrix.array,16*i),this.instanceMatrix.addUpdateRange(16*i,16),this.colorProvider){const e=this.colorProvider(s),r=new t(e?.fill||f||"#df0");this.instanceColor.setXYZ(i,r.r,r.g,r.b),this.instanceColor.addUpdateRange(3*i,3),this.instanceColor.needsUpdate=!0;const o=new t(e?.stroke||x||"#000");this.borderColor.setXYZ(i,o.r,o.g,o.b),this.borderColor.addUpdateRange(3*i,3),this.borderColor.needsUpdate=!0}if(this.splitProvider){const t=this.splitProvider(s);for(let e=0;e<this.maxSplits;e+=4){const s=e/4,r=this.splits[s],o=t.slice(e,e+4);for(let t=o.length;t<4;t++)o[t]=0;const n=4*i+e;r.set(o,n),r.addUpdateRange(n,4),r.needsUpdate=!0}}}else A(s.id)},S=(t,e)=>{if(t)if(this.idToIndex.has(e.id)){const s=this.idToIndex.get(e.id);t.toArray(this.instanceMatrix.array,16*s),this.instanceMatrix.addUpdateRange(16*s,16)}else P(t,e);else A(e.id)},I=e=>{if(this.idToIndex.has(e.id)){const s=this.idToIndex.get(e.id);if(this.colorProvider){const i=this.colorProvider(e),r=new t(i?.fill||f||"#df0");this.meshes.forEach(t=>t.getColorAt(s,y)),d(r,y)||(this.instanceColor.setXYZ(s,r.r,r.g,r.b),this.instanceColor.addUpdateRange(3*s,3),this.instanceColor.needsUpdate=!0);const o=new t(i?.stroke||x||"#000");y.set(this.borderColor.getX(s),this.borderColor.getY(s),this.borderColor.getZ(s)),d(o,y)||(this.borderColor.setXYZ(s,o.r,o.g,o.b),this.borderColor.addUpdateRange(3*s,3),this.borderColor.needsUpdate=!0)}}else P(h(e),e)},T=t=>{if(this.idToIndex.has(t.id)){const e=this.idToIndex.get(t.id);if(this.splitProvider){const s=this.splitProvider(t);let i=0;for(let t=0;t<this.maxSplits;t+=4){const r=t/4,o=this.splits[r],n=s.slice(t,t+4);for(let t=n.length;t<4;t++)n[t]=0;for(let t=0;t<n.length;t++)i+=n[t];const a=4*e+t;o.set(n,a),o.needsUpdate=!0}}}else P(h(t),t)},A=t=>{if(this.idToIndex.has(t)){const e=this.idToIndex.get(t);this.idToIndex.delete(t),w.push(e),a.toArray(this.instanceMatrix.array,16*e),this.instanceMatrix.addUpdateRange(16*e,16)}};this.disposables.add(i.on("added",t=>{t&&(P(h(t),t),this.instanceMatrix.needsUpdate=!0)})),this.disposables.add(i.on("updated",t=>{const e=t.updated;e&&((t.update.t||t.update.v)&&(S(h(e),e),this.instanceMatrix.needsUpdate=!0),this.colorProvider&&I(e),this.splitProvider&&T(e))})),this.disposables.add(i.on("removed",t=>{A(t),this.instanceMatrix.needsUpdate=!0}))}getIndex(t){const e="string"==typeof t?t:t.id;return this.idToIndex.get(e)}matrixChanged(){this.listeners.forEach(t=>t(this.instanceMatrix))}onNewMatrix(t){return t(this.instanceMatrix),this.listeners.add(t),()=>{this.listeners.delete(t)}}onCountChange(t){return t(this.count),this.countListeners.add(t),()=>{this.countListeners.delete(t)}}registerMesh(t){this.meshes.push(t),this.onNewMatrix(e=>{t.instanceMatrix=e,this.colorProvider&&(t.instanceColor=this.instanceColor,t.geometry.setAttribute("borderColor",this.borderColor)),this.splitProvider&&this.splits.forEach((e,s)=>{t.geometry.setAttribute("split"+s,e)})}),this.onCountChange(e=>t.count=e),t.instanceMatrix.clearUpdateRanges(),t.instanceMatrix.needsUpdate=!0}dispose(){this.disposables.dispose()}}function d(t,e,s=1e-6){return Math.abs(t.r-e.r)<s&&Math.abs(t.g-e.g)<s&&Math.abs(t.b-e.b)<s}
1
+ import{Color as t,DynamicDrawUsage as e,InstancedBufferAttribute as s,Matrix4 as i,Vector4 as r}from"three";import{Disposables as o}from"../../common/Disposables.js";import{ONE_MINUTE as n}from"../../ticks/TimeSliderConstants.js";const a=(new i).makeScale(0,0,0).makeTranslation(-1e3,-1e3,-1e3);export class GrowingInstanceMatrix{constructor(i,h,l){this.listeners=new Set,this.countListeners=new Set,this.idToIndex=new Map,this.disposables=new o,this.splits=[],this.meshes=[];const{bufferSize:c,colorProvider:p,defaultFill:f,defaultBorder:x,splitProvider:g,maxSplits:b}=l||{};this.colorProvider=p,this.splitProvider=g,this.maxSplits=b||4;const u=Date.now(),C=i.data;this.count=C.length;const w=[];this.bufferSize=c||1e3;const m=Math.ceil(i.data.length/this.bufferSize)*this.bufferSize,M=new Float32Array(16*m),U=C.map(t=>({m:h(t),id:t.id})).filter(t=>null!==t.m);if(U.forEach(t=>this.idToIndex.set(t.id,this.idToIndex.size)),U.forEach((t,e)=>t.m.toArray(M,16*e)),this.instanceMatrix=new s(M,16),this.colorProvider){const e=new Float32Array(3*m),i=new Float32Array(3*m);C.forEach((s,r)=>{const o=this.colorProvider(s),n=new t(o?.fill||f||"#df0");e.set([n.r,n.g,n.b],3*r);const a=new t(o?.stroke||x||"#000");i.set([a.r,a.g,a.b],3*r)}),this.instanceColor=new s(e,3),this.borderColor=new s(i,3)}else this.instanceColor=new s(new Float32Array,3),this.borderColor=new s(new Float32Array,3);if(this.splitProvider){const t=[];for(let e=0;e<this.maxSplits;e+=4)t.push(new Float32Array(4*m));C.forEach((e,s)=>{const i=this.splitProvider(e);for(let e=0;e<this.maxSplits;e+=4){const r=i.slice(e,e+4);t[e/4].set(r,4*s)}}),this.splits=t.map(t=>new s(t,4))}const y=new t,v=(new r,()=>{const t=this.idToIndex.size+w.length;if(t!==this.count){this.count=t;const i=this.instanceMatrix.array.length/16;if(t>i||t<i-this.bufferSize&&Date.now()-u>n){const i=Math.ceil(t/this.bufferSize),r=i*this.bufferSize,o=new Float32Array(16*r);if(o.set(this.instanceMatrix.array),this.instanceMatrix=new s(o,16),this.instanceMatrix.setUsage(e),this.instanceMatrix.needsUpdate=!0,this.colorProvider){const t=new Float32Array(i*this.bufferSize*3);t.set(this.instanceColor.array),this.instanceColor=new s(t,3),this.instanceColor.setUsage(e),this.instanceColor.needsUpdate=!0;const r=new Float32Array(i*this.bufferSize*3);r.set(this.borderColor.array),this.borderColor=new s(r,3),this.borderColor.setUsage(e),this.borderColor.needsUpdate=!0}this.splitProvider&&(this.splits=this.splits.map(t=>{const r=new Float32Array(i*this.bufferSize*4);r.set(t.array);const o=new s(r,1);return o.setUsage(e),o.needsUpdate=!0,o})),this.matrixChanged()}this.countListeners.forEach(t=>t(this.count))}}),P=(e,s)=>{if(e)if(this.idToIndex.has(s.id))S(e,s);else{let i=this.idToIndex.size;if(w.length>0&&(i=w.shift()),this.idToIndex.set(s.id,i),v(),e.toArray(this.instanceMatrix.array,16*i),this.instanceMatrix.addUpdateRange(16*i,16),this.colorProvider){const e=this.colorProvider(s),r=new t(e?.fill||f||"#df0");this.instanceColor.setXYZ(i,r.r,r.g,r.b),this.instanceColor.addUpdateRange(3*i,3),this.instanceColor.needsUpdate=!0;const o=new t(e?.stroke||x||"#000");this.borderColor.setXYZ(i,o.r,o.g,o.b),this.borderColor.addUpdateRange(3*i,3),this.borderColor.needsUpdate=!0}if(this.splitProvider){const t=this.splitProvider(s);for(let e=0;e<this.maxSplits;e+=4){const s=e/4,r=this.splits[s],o=t.slice(e,e+4);for(let t=o.length;t<4;t++)o[t]=0;const n=4*i+e;r.set(o,n),r.addUpdateRange(n,4),r.needsUpdate=!0}}}else A(s.id)},S=(t,e)=>{if(t)if(this.idToIndex.has(e.id)){const s=this.idToIndex.get(e.id);t.toArray(this.instanceMatrix.array,16*s),this.instanceMatrix.addUpdateRange(16*s,16)}else P(t,e);else A(e.id)},I=e=>{if(this.idToIndex.has(e.id)){const s=this.idToIndex.get(e.id);if(this.colorProvider){const i=this.colorProvider(e),r=new t(i?.fill||f||"#df0");this.meshes.forEach(t=>t.getColorAt(s,y)),d(r,y)||(this.instanceColor.setXYZ(s,r.r,r.g,r.b),this.instanceColor.addUpdateRange(3*s,3),this.instanceColor.needsUpdate=!0);const o=new t(i?.stroke||x||"#000");y.set(this.borderColor.getX(s),this.borderColor.getY(s),this.borderColor.getZ(s)),d(o,y)||(this.borderColor.setXYZ(s,o.r,o.g,o.b),this.borderColor.addUpdateRange(3*s,3),this.borderColor.needsUpdate=!0)}}else P(h(e),e)},T=t=>{if(this.idToIndex.has(t.id)){const e=this.idToIndex.get(t.id);if(this.splitProvider){const s=this.splitProvider(t);let i=0;for(let t=0;t<this.maxSplits;t+=4){const r=t/4,o=this.splits[r],n=s.slice(t,t+4);for(let t=n.length;t<4;t++)n[t]=0;for(let t=0;t<n.length;t++)i+=n[t];const a=4*e+t;o.set(n,a),o.needsUpdate=!0}}}else P(h(t),t)},A=t=>{if(this.idToIndex.has(t)){const e=this.idToIndex.get(t);this.idToIndex.delete(t),w.push(e),a.toArray(this.instanceMatrix.array,16*e),this.instanceMatrix.addUpdateRange(16*e,16)}};this.disposables.add(i.on("added",t=>{t&&(P(h(t),t),this.instanceMatrix.needsUpdate=!0)})),this.disposables.add(i.on("updated",t=>{const e=t.updated;e&&((t.update.t||t.update.v)&&(S(h(e),e),this.instanceMatrix.needsUpdate=!0),this.colorProvider&&I(e),this.splitProvider&&T(e))})),this.disposables.add(i.on("removed",t=>{A(t),this.instanceMatrix.needsUpdate=!0}))}getIndex(t){const e="string"==typeof t?t:t.id;return this.idToIndex.get(e)}matrixChanged(){this.listeners.forEach(t=>t(this.instanceMatrix))}onNewMatrix(t){return t(this.instanceMatrix),this.listeners.add(t),()=>{this.listeners.delete(t)}}onCountChange(t){return t(this.count),this.countListeners.add(t),()=>{this.countListeners.delete(t)}}registerMesh(t){this.meshes.push(t),this.onNewMatrix(e=>{t.instanceMatrix=e,this.colorProvider&&(t.instanceColor=this.instanceColor,t.geometry.setAttribute("borderColor",this.borderColor)),this.splitProvider&&this.splits.forEach((e,s)=>{t.geometry.setAttribute("split"+s,e)})}),this.onCountChange(e=>t.count=e),t.instanceMatrix.clearUpdateRanges(),t.instanceMatrix.needsUpdate=!0}dispose(){this.disposables.dispose()}}function d(t,e,s=1e-6){return Math.abs(t.r-e.r)<s&&Math.abs(t.g-e.g)<s&&Math.abs(t.b-e.b)<s}
@@ -1,11 +1,10 @@
1
1
  import { Scene } from "three";
2
2
  import { TimeSlider } from "../../engine/TimeSlider";
3
- import { TimeSliderIntervalsLayer } from "../../layer/TimeSliderIntervalsLayer";
4
3
  import { TimeSliderLayer } from "../../layer/TimeSliderLayer";
5
4
  import { TimeSliderLayerLoader } from "../../layer/TimeSliderLayerLoader";
6
5
  import { Three3DTimeSliderEngine } from "../Three3DTimeSliderEngine";
7
6
  export declare class Three3DIntervalsTimeLayerLoader implements TimeSliderLayerLoader<Three3DTimeSliderEngine> {
8
7
  engineClass: typeof Three3DTimeSliderEngine;
9
- canLoad(layer: TimeSliderLayer): layer is TimeSliderIntervalsLayer<any, any>;
8
+ canLoad(layer: TimeSliderLayer): boolean;
10
9
  load(layer: TimeSliderLayer, scene: Scene, engine: Three3DTimeSliderEngine, timeSlider: TimeSlider): () => void;
11
10
  }
@@ -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";import{toGLSLColor as s,toGLSLFloat as d,toGLSLVec2 as c}from"../../common/GlslTools";import{getTimePartsInHours as m}from"../../common/TimeTools";import{TimeSliderSplittingFixedHistogram as f}from"../../data/histogram/TimeSliderSplittingFixedHistogram";import{TimeSliderHistogramLayer as v}from"../../layer/TimeSliderHistogramLayer";import{TimeSliderIntervalsLayer as g}from"../../layer/TimeSliderIntervalsLayer";import{Three3DTimeSliderEngine as p}from"../Three3DTimeSliderEngine";import{GrowingInstanceMatrix as h}from"./GrowingInstanceMatrix";const u=123456;export class Three3DIntervalsTimeLayerLoader{constructor(){this.engineClass=p}canLoad(e){return e instanceof g}load(g,p,x,y){return this.canLoad(g)?function(g,p,x,y){const b=new a,S=g.model,{material:w,dispose:C}=(g.style,function(e,o,i){const g=void 0!==e.styler,p=function(e){return e instanceof v&&void 0!==e.histogram.classifier&&e.histogram instanceof f}(e),h=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(p){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 `},y=()=>{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;let c="";if(p){const n=e.maxClassifications||4,t=e.classificationColors||[];c+=`const int maxSplits = ${n};`;let o="",i="";for(let e=0;e<t.length;e++)c+=`\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)c+=`\n flat in vec4 vSplit${e/4};\n `,o+=`\n if (idx < ${e+4}) return vSplit${e/4}[idx - ${e}];\n `;c+=`\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:c,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\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 ${c}\n\n void main() {\n ${p?"\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 = 1.0;\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 = smoothstep(bw.x, bw.x + smoothing.x, vUv.x) *\n smoothstep(1.0 - bw.x, 1.0 - bw.x - smoothing.x, vUv.x);\n float alphaY = smoothstep(bw.y, bw.y + smoothing.y, uvYAdjusted) *\n smoothstep(1.0 - bw.y, 1.0 - bw.y - smoothing.y, 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 = 1.0;\n vec2 smoothing = smoothingPixels * vTransform / vSize;\n\n float alphaX = smoothstep(bw.x, bw.x + smoothing.x, vUv.x) *\n smoothstep(1.0 - bw.x, 1.0 - bw.x - smoothing.x, vUv.x);\n float alphaY = smoothstep(bw.y, bw.y + smoothing.y, vUv.y) *\n smoothstep(1.0 - bw.y, 1.0 - bw.y - smoothing.y, 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)},selection:{value:-1},valueRange:{value:new r(0,1)},originTime:{value:new r(0,0)}},vertexShader:x(),fragmentShader:y()}),S=()=>{b.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);b.uniforms.originTime.value.set(t,o)},C=()=>{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)}},T=()=>{S(),w()};return h.add(o.on("valueRanges",C)),h.add(i.on("time",T)),h.add(i.on("value",S)),h.add(i.on("size",S)),h.add(e.on("loaded",C)),h.add(e.on("visible",C)),w(),C(),h.add(e.on("invalidateAll",()=>{b.vertexShader=x(),b.fragmentShader=y(),b.needsUpdate=!0})),{material:b,dispose:()=>h.dispose()}}(g,x,y));b.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 h(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});b.add(()=>z.dispose()),x.selectionOptions.highlight&&b.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 p.add($),b.add(()=>p.remove($)),b.disposeLater()}(g,p,y,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,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,10 +1,9 @@
1
1
  import { Scene } from "three";
2
2
  import { TimeSliderLayer } from "../../layer/TimeSliderLayer";
3
3
  import { TimeSliderLayerLoader } from "../../layer/TimeSliderLayerLoader";
4
- import { TimeSliderPointsLayer } from "../../layer/TimeSliderPointsLayer";
5
4
  import { Three3DTimeSliderEngine } from "../Three3DTimeSliderEngine";
6
5
  export declare class Three3DPointsTimeLayerLoader implements TimeSliderLayerLoader<Three3DTimeSliderEngine> {
7
6
  engineClass: typeof Three3DTimeSliderEngine;
8
- canLoad(layer: TimeSliderLayer): layer is TimeSliderPointsLayer;
7
+ canLoad(layer: TimeSliderLayer): boolean;
9
8
  load(layer: TimeSliderLayer, scene: Scene, engine: Three3DTimeSliderEngine): () => void;
10
9
  }
@@ -1 +1 @@
1
- import{BoxGeometry as e,Color as n,DoubleSide as o,InstancedMesh as i,Object3D as t,Points as r,ShaderMaterial as l,Vector2 as a}from"three";import{Disposables as c}from"../../common/Disposables";import{toGLSLColor as s,toGLSLFloat as d}from"../../common/GlslTools";import{getTimePartsInHours as f,SPLIT_TIME_3 as v,SPLIT_TIME_3_POW2 as m}from"../../common/TimeTools";import{TimeSliderPointsLayer as g}from"../../layer/TimeSliderPointsLayer";import{Three3DTimeSliderEngine as p}from"../Three3DTimeSliderEngine";import{GrowingBufferGeometry as h}from"./GrowingBufferGeometry";import{GrowingInstanceMatrix as u}from"./GrowingInstanceMatrix";export class Three3DPointsTimeLayerLoader{constructor(){this.engineClass=p}canLoad(e){return e instanceof g}load(e,n,o){return this.canLoad(e)?function(e,n,o){const i=new c,v=o.getValueRange(),m=v.maxValue-v.minValue,g={offset:v.minValue+(e.min||0)*m,scale:(e.max||1)-(e.min||0)},p=function(e,n){return new h(e,e=>{const o=.01*(n.offset+e.v*n.scale),[i,t]=f(e.t);return[i,o,t]})}(e.model,g),P=function(e,n){return new u(e,o=>{const i=e.getNeightbor(o,-1);if(i){const e=new t,r=.5*(i.t+o.t),l=.01*(n.offset+i.v*n.scale),a=.01*(n.offset+o.v*n.scale),c=.5*(l+a),s=o.t-i.t,d=a-l,[v,m]=f(r);return e.position.set(v,c,m),e.scale.set(s,d,d<0?1:0),e.updateMatrix(),e.matrix}return null})}(e.model,g);e.style.line&&i.add(x(e,n,o,P));e.style.point&&i.add(function(e,n,o,i){const t=new c,v=e.style;if(!v.point)throw"cannot happen";if(v.point.multipass){const r=v.point.color||"#df0",l=v.point.opacity||1,a=v.point.size||12,c=v.point.border?.color||"#000",s=v.point.border?.opacity||1,d=v.point.border?v.point.border?.width||2:0,f=v.point.core?.color||"#f00",m=v.point.core?.opacity||1,g=v.point.core?v.point.core?.size||4:0;v.point.border&&t.add(y(e,n,o,i,c,s,a)),t.add(y(e,n,o,i,r,l,a-2*d)),v.point.core&&t.add(y(e,n,o,i,f,m,g))}else{const c=new l({transparent:!0,uniforms:{selection:{value:-1},originTime:{value:new a(0,0)}}}),v=()=>{const n=e.style.point,o=n.color||"#df0",i=n.opacity||1,t=n.size||12,r=n.border?.color||"#000",l=n.border?.opacity||1,a=n.border?n.border?.width||2:0,f=n.core?.color||"#f00",v=n.core?.opacity||1,m=n.core?n.core?.size||4:0;c.vertexShader=`\n // precision mediump float;\n\n const float iconSize = ${d(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 `,c.fragmentShader=`\n // precision mediump float;\n \n const vec3 fillColor = ${s(o)};\n const float fillOpacity = ${d(i)};\n const float iconSize = ${d(t)};\n const vec3 borderColor = ${s(r)};\n const float borderOpacity = ${d(l)};\n const float borderWidth = ${d(a)};\n const vec3 coreColor = ${s(f)};\n const float coreOpacity = ${d(v)};\n const float coreSize = ${d(m)};\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 `,c.needsUpdate=!0};v(),t.add(e.on("invalidate",v));const m=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=f(n);c.uniforms.originTime.value.set(i,t)};t.add(o.on("time",m)),m();const g=new r(i,c);g.frustumCulled=!1,t.add(e.on("selection",e=>{if(e.length>0){const n=i.elementIndex(e[0]);c.uniforms.selection.value="number"==typeof n?n:-1}else c.uniforms.selection.value=-1})),n.add(g),t.add(()=>n.remove(g))}return t.disposeLater()}(e,n,o,p));return i.disposeLater()}(e,n,o):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:e}),()=>{})}}function x(n,t,r,v,m){const g=new c,p=n.style,h=void 0!==p.line?.border;!m&&h&&g.add(x(n,t,r,v,!0));const u=new l({transparent:!0,side:o,uniforms:{worldPerPixel:{value:new a(r.camera.scale.x/r.getSize().width,r.camera.scale.y/r.getSize().height)},originTime:{value:new a}}}),y=()=>{const e=p.line?.color||"#df0",n=p.line?.border?.color||"#000",o=p.line?.width||4,i=p.line?.border?.width||1,t=p.line?.opacity||1,r=p.line?.border?.opacity||1,l=h&&m?n:e,a=h&&m?r:t,c=h?o-(m?0:2*i):o,f=void 0!==p.area&&(m||!h),v=p.area?.color||"#df0",g=p.area?.opacity||1;u.vertexShader=`\n // precision mediump float;\n \n const float lineWidth = ${d(c)};\n uniform vec2 originTime;\n uniform vec2 worldPerPixel;\n\n varying vec2 vUv;\n varying float vLength;\n varying float vHeight;\n \n varying float vAspect;\n varying float vBackArea;\n\n void main() {\n vUv = uv;\n vBackArea = position.z;\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) > 0.0) {\n size.y *= -1.0;\n }\n\n // Reconstruct time\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n // vMsHour = pos.z / 3600000.0;\n // vMsHour = mod(pos.z, 60000.0) / 60000.0;\n\n float deltaDay = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaDay * 3600000.0 + deltaMs;\n pos.x = relativeTime;\n pos.z = 0.95;\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.0999 * position.z);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(totalPos, 1.0);\n }\n `,u.fragmentShader=`\n // precision mediump float;\n // precision highp float;\n\n const float lineWidth = ${d(c)};\n const vec3 fillColor = ${s(l)};\n const vec3 areaColor = ${s(v)};\n const float areaOpacity = ${d(g)};\n const float fillOpacity = ${d(a)};\n const bool showArea = ${f};\n uniform vec2 worldPerPixel;\n \n varying float vAspect;\n varying vec2 vUv;\n varying float vLength;\n varying float vHeight;\n varying float vBackArea;\n\n void main() {\n if (vBackArea < 0.48) {\n if (showArea) {\n float backRatio = 2.0 * vBackArea;\n if (vBackArea < 0.0) {\n backRatio = 0.0;\n }\n float alpha = mix(0.0, 1.0, backRatio);\n gl_FragColor = vec4(areaColor, alpha);\n }\n else {\n discard;\n }\n }\n else {\n // vec3 color = mix(vec3(0.0,1.0,0.0),vec3(1.0,0.0,0.0), vUv.x);\n // gl_FragColor = vec4(color,1.0);\n\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 + vLength / worldPerPixel.x, lineWidth);\n vec2 sizePx = vec2(lineWidth + distPx, lineWidth);\n // vec2 sizePx = vec2(lineWidth + vLength / worldPerPixel.x, lineWidth);\n\n // float lengthPx = lineWidth + vLength / worldPerPixel.x;\n // float heightPx = lineWidth;\n float radiusPx = 0.5 * sizePx.y;\n // vec2 uvPx = vec2(vUv.x * lengthPx, vUv.y * heightPx);\n vec2 uvPx = vUv * sizePx;\n\n \n // vec3 invertedColor = vec3(1.0 - fillColor.r, 1.0 - fillColor.g, 1.0 - fillColor.b);\n // vec3 minuteColor = mix(fillColor, invertedColor, vMsHour);\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 // gl_FragColor = vec4(0.0,1.0,1.0,1.0);\n\n\n // gl_FragColor = vec4(minuteColor, fillOpacity);\n\n // if (dist > radiusPx - borderWidth) {\n // gl_FragColor = vec4(borderColor, borderOpacity);\n // }\n // else {\n // gl_FragColor = vec4(fillColor, fillOpacity);\n // }\n // gl_FragColor = vec4(0.0,1.0,1.0,1.0);\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 \n gl_FragColor = vec4(fillColor, fillOpacity);\n // gl_FragColor = vec4(minuteColor, fillOpacity);\n \n\n // // float radiusX = (vHeight * worldPerPixel.y) / (vLength * worldPerPixel.x);\n \n // // float radius = 0.5; // Half of the full height\n\n // // // Left end circle\n // // if (vUv.x < radius) {\n // // vec2 center = vec2(radius, 0.5); // center of left circle\n // // float dist = distance(vUv, center);\n // // if (dist > radius) discard;\n // // }\n\n // // // Right end circle\n // // else if (vUv.x > 1.0 - radius) {\n // // vec2 center = vec2(1.0 - radius, 0.5); // center of right circle\n // // float dist = distance(vUv, center);\n // // if (dist > radius) discard;\n // // }\n\n // // vec2 sizePx = vec2(vLength / worldPerPixel.x, lineWidth);\n // // vec2 size = vec2(vLength, lineWidth * worldPerPixel.y);\n // // vec2 bw = borderWidth * worldPerPixel / size;\n // // vec2 bw = borderWidth * sizePx;\n\n // float bwy = borderWidth / sizePx.y;\n\n // float smoothingPixels = 1.5;\n // // vec2 smoothing = smoothingPixels * worldPerPixel / size;\n // // vec2 smoothing = smoothingPixels * sizePx;\n // float smoothingY = smoothingPixels / sizePx.y;\n\n // float alphaX = 1.0;\n // float alphaY = smoothstep(bwy, bwy + smoothingY, vUv.y) *\n // smoothstep(1.0 - bwy, 1.0 - bwy - smoothingY, vUv.y);\n // // float alphaY = smoothstep(bw.y, bw.y + smoothing.y, vUv.y) *\n // // smoothstep(1.0 - bw.y, 1.0 - bw.y - smoothing.y, vUv.y);\n \n // float fillAlpha = alphaX * alphaY;\n // float borderAlpha = 1.0 - fillAlpha;\n\n // vec3 color = mix(borderColor, fillColor, fillAlpha);\n\n // // vec3 color = mix(vec3(0.0,1.0,0.0),vec3(1.0,0.0,0.0), vUv.x);\n // // vec3 color = mix(vec3(0.0,1.0,0.0),vec3(1.0,0.0,0.0), uvPx.x / sizePx.x);\n // float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n // gl_FragColor = vec4(color, alpha);\n }\n }\n }\n `};y(),g.add(n.on("invalidate",y));const P=()=>{u.uniforms.worldPerPixel.value.set(r.camera.scale.x/r.getSize().width,r.camera.scale.y/r.getSize().height)},b=()=>{P();const e=r.getTimeRange(),n=e.startTime+.5*e.duration,[o,i]=f(n);u.uniforms.originTime.value.set(o,i)};g.add(r.on("time",b)),g.add(r.on("value",P)),g.add(r.on("size",P)),b();const w=new e(1,1,1),C=new i(w,u,n.model.data.length);return v.registerMesh(C),C.frustumCulled=!1,t.add(C),g.add(()=>t.remove(C)),g.disposeLater()}function y(e,n,o,i,t,g,p){const h=new c;if(!e.style.point)throw"cannot happen";const u=new l({transparent:!0,uniforms:{selection:{value:-1},origin:{value:new a(0,0)}}}),x=()=>{u.vertexShader=`\n precision highp float;\n\n const float iconSize = ${d(p)};\n \n uniform vec3 origin;\n uniform int selection;\n \n flat out int vSelected;\n \n // const float SPLIT_PART = ${v}.0;\n // const float SPLIT_PART_X2 = ${m}.0;\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 value = 0.5;\n // vec3 delta = position - origin;\n // float relativeTime = delta.x * SPLIT_PART_X2 + delta.y * SPLIT_PART + delta.z;\n\n float hours = position.x;\n float value = position.y;\n float ms = position.z;\n\n float deltaHours = hours - origin.x;\n float deltaMs = ms - origin.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(relativeTime, value, 1.0, 1.0);\n gl_PointSize = mix(iconSize, 2.0 * iconSize, float(vSelected));\n }\n `,u.fragmentShader=`\n precision highp float;\n\n const vec3 color = ${s(t)};\n const float opacity = ${d(g)};\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};x(),h.add(e.on("invalidate",x));const y=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=f(n);u.uniforms.origin.value.set(i,t)};h.add(o.on("time",y)),y();const P=new r(i,u);return P.frustumCulled=!1,h.add(e.on("selection",e=>{if(e.length>0){const n=i.elementIndex(e[0]);u.uniforms.selection.value="number"==typeof n?n:-1}else u.uniforms.selection.value=-1})),n.add(P),h.add(()=>n.remove(P)),h.disposeLater()}
1
+ import{BoxGeometry as e,Color as n,DoubleSide as o,InstancedMesh as i,Object3D as t,Points as r,ShaderMaterial as l,Vector2 as a}from"three";import{Disposables as c}from"../../common/Disposables.js";import{toGLSLColor as s,toGLSLFloat as d}from"../../common/GlslTools.js";import{getTimePartsInHours as f,SPLIT_TIME_3 as v,SPLIT_TIME_3_POW2 as m}from"../../common/TimeTools.js";import{TimeSliderPointsLayer as g}from"../../layer/TimeSliderPointsLayer.js";import{Three3DTimeSliderEngine as p}from"../Three3DTimeSliderEngine.js";import{GrowingBufferGeometry as h}from"./GrowingBufferGeometry.js";import{GrowingInstanceMatrix as u}from"./GrowingInstanceMatrix.js";export class Three3DPointsTimeLayerLoader{constructor(){this.engineClass=p}canLoad(e){return e instanceof g}load(e,n,o){return this.canLoad(e)?function(e,n,o){const i=new c,v=o.getValueRange(),m=v.maxValue-v.minValue,g={offset:v.minValue+(e.min||0)*m,scale:(e.max||1)-(e.min||0)},p=function(e,n){return new h(e,e=>{const o=.01*(n.offset+e.v*n.scale),[i,t]=f(e.t);return[i,o,t]})}(e.model,g),P=function(e,n){return new u(e,o=>{const i=e.getNeightbor(o,-1);if(i){const e=new t,r=.5*(i.t+o.t),l=.01*(n.offset+i.v*n.scale),a=.01*(n.offset+o.v*n.scale),c=.5*(l+a),s=o.t-i.t,d=a-l,[v,m]=f(r);return e.position.set(v,c,m),e.scale.set(s,d,d<0?1:0),e.updateMatrix(),e.matrix}return null})}(e.model,g);e.style.line&&i.add(x(e,n,o,P));e.style.point&&i.add(function(e,n,o,i){const t=new c,v=e.style;if(!v.point)throw"cannot happen";if(v.point.multipass){const r=v.point.color||"#df0",l=v.point.opacity||1,a=v.point.size||12,c=v.point.border?.color||"#000",s=v.point.border?.opacity||1,d=v.point.border?v.point.border?.width||2:0,f=v.point.core?.color||"#f00",m=v.point.core?.opacity||1,g=v.point.core?v.point.core?.size||4:0;v.point.border&&t.add(y(e,n,o,i,c,s,a)),t.add(y(e,n,o,i,r,l,a-2*d)),v.point.core&&t.add(y(e,n,o,i,f,m,g))}else{const c=new l({transparent:!0,uniforms:{selection:{value:-1},originTime:{value:new a(0,0)}}}),v=()=>{const n=e.style.point,o=n.color||"#df0",i=n.opacity||1,t=n.size||12,r=n.border?.color||"#000",l=n.border?.opacity||1,a=n.border?n.border?.width||2:0,f=n.core?.color||"#f00",v=n.core?.opacity||1,m=n.core?n.core?.size||4:0;c.vertexShader=`\n // precision mediump float;\n\n const float iconSize = ${d(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 `,c.fragmentShader=`\n // precision mediump float;\n \n const vec3 fillColor = ${s(o)};\n const float fillOpacity = ${d(i)};\n const float iconSize = ${d(t)};\n const vec3 borderColor = ${s(r)};\n const float borderOpacity = ${d(l)};\n const float borderWidth = ${d(a)};\n const vec3 coreColor = ${s(f)};\n const float coreOpacity = ${d(v)};\n const float coreSize = ${d(m)};\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 `,c.needsUpdate=!0};v(),t.add(e.on("invalidate",v));const m=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=f(n);c.uniforms.originTime.value.set(i,t)};t.add(o.on("time",m)),m();const g=new r(i,c);g.frustumCulled=!1,t.add(e.on("selection",e=>{if(e.length>0){const n=i.elementIndex(e[0]);c.uniforms.selection.value="number"==typeof n?n:-1}else c.uniforms.selection.value=-1})),n.add(g),t.add(()=>n.remove(g))}return t.disposeLater()}(e,n,o,p));return i.disposeLater()}(e,n,o):(console.error("Layer not supported by ThreePointsTimeLayerLoader",{layer:e}),()=>{})}}function x(n,t,r,v,m){const g=new c,p=n.style,h=void 0!==p.line?.border;!m&&h&&g.add(x(n,t,r,v,!0));const u=new l({transparent:!0,side:o,uniforms:{worldPerPixel:{value:new a(r.camera.scale.x/r.getSize().width,r.camera.scale.y/r.getSize().height)},originTime:{value:new a}}}),y=()=>{const e=p.line?.color||"#df0",n=p.line?.border?.color||"#000",o=p.line?.width||4,i=p.line?.border?.width||1,t=p.line?.opacity||1,r=p.line?.border?.opacity||1,l=h&&m?n:e,a=h&&m?r:t,c=h?o-(m?0:2*i):o,f=void 0!==p.area&&(m||!h),v=p.area?.color||"#df0",g=p.area?.opacity||1;u.vertexShader=`\n // precision mediump float;\n \n const float lineWidth = ${d(c)};\n uniform vec2 originTime;\n uniform vec2 worldPerPixel;\n\n varying vec2 vUv;\n varying float vLength;\n varying float vHeight;\n \n varying float vAspect;\n varying float vBackArea;\n\n void main() {\n vUv = uv;\n vBackArea = position.z;\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) > 0.0) {\n size.y *= -1.0;\n }\n\n // Reconstruct time\n vec3 pos = vec3(instanceMatrix[3].xyz);\n\n // vMsHour = pos.z / 3600000.0;\n // vMsHour = mod(pos.z, 60000.0) / 60000.0;\n\n float deltaDay = pos.x - originTime.x;\n float deltaMs = pos.z - originTime.y;\n float relativeTime = deltaDay * 3600000.0 + deltaMs;\n pos.x = relativeTime;\n pos.z = 0.95;\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.0999 * position.z);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(totalPos, 1.0);\n }\n `,u.fragmentShader=`\n // precision mediump float;\n // precision highp float;\n\n const float lineWidth = ${d(c)};\n const vec3 fillColor = ${s(l)};\n const vec3 areaColor = ${s(v)};\n const float areaOpacity = ${d(g)};\n const float fillOpacity = ${d(a)};\n const bool showArea = ${f};\n uniform vec2 worldPerPixel;\n \n varying float vAspect;\n varying vec2 vUv;\n varying float vLength;\n varying float vHeight;\n varying float vBackArea;\n\n void main() {\n if (vBackArea < 0.48) {\n if (showArea) {\n float backRatio = 2.0 * vBackArea;\n if (vBackArea < 0.0) {\n backRatio = 0.0;\n }\n float alpha = mix(0.0, 1.0, backRatio);\n gl_FragColor = vec4(areaColor, alpha);\n }\n else {\n discard;\n }\n }\n else {\n // vec3 color = mix(vec3(0.0,1.0,0.0),vec3(1.0,0.0,0.0), vUv.x);\n // gl_FragColor = vec4(color,1.0);\n\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 + vLength / worldPerPixel.x, lineWidth);\n vec2 sizePx = vec2(lineWidth + distPx, lineWidth);\n // vec2 sizePx = vec2(lineWidth + vLength / worldPerPixel.x, lineWidth);\n\n // float lengthPx = lineWidth + vLength / worldPerPixel.x;\n // float heightPx = lineWidth;\n float radiusPx = 0.5 * sizePx.y;\n // vec2 uvPx = vec2(vUv.x * lengthPx, vUv.y * heightPx);\n vec2 uvPx = vUv * sizePx;\n\n \n // vec3 invertedColor = vec3(1.0 - fillColor.r, 1.0 - fillColor.g, 1.0 - fillColor.b);\n // vec3 minuteColor = mix(fillColor, invertedColor, vMsHour);\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 // gl_FragColor = vec4(0.0,1.0,1.0,1.0);\n\n\n // gl_FragColor = vec4(minuteColor, fillOpacity);\n\n // if (dist > radiusPx - borderWidth) {\n // gl_FragColor = vec4(borderColor, borderOpacity);\n // }\n // else {\n // gl_FragColor = vec4(fillColor, fillOpacity);\n // }\n // gl_FragColor = vec4(0.0,1.0,1.0,1.0);\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 \n gl_FragColor = vec4(fillColor, fillOpacity);\n // gl_FragColor = vec4(minuteColor, fillOpacity);\n \n\n // // float radiusX = (vHeight * worldPerPixel.y) / (vLength * worldPerPixel.x);\n \n // // float radius = 0.5; // Half of the full height\n\n // // // Left end circle\n // // if (vUv.x < radius) {\n // // vec2 center = vec2(radius, 0.5); // center of left circle\n // // float dist = distance(vUv, center);\n // // if (dist > radius) discard;\n // // }\n\n // // // Right end circle\n // // else if (vUv.x > 1.0 - radius) {\n // // vec2 center = vec2(1.0 - radius, 0.5); // center of right circle\n // // float dist = distance(vUv, center);\n // // if (dist > radius) discard;\n // // }\n\n // // vec2 sizePx = vec2(vLength / worldPerPixel.x, lineWidth);\n // // vec2 size = vec2(vLength, lineWidth * worldPerPixel.y);\n // // vec2 bw = borderWidth * worldPerPixel / size;\n // // vec2 bw = borderWidth * sizePx;\n\n // float bwy = borderWidth / sizePx.y;\n\n // float smoothingPixels = 1.5;\n // // vec2 smoothing = smoothingPixels * worldPerPixel / size;\n // // vec2 smoothing = smoothingPixels * sizePx;\n // float smoothingY = smoothingPixels / sizePx.y;\n\n // float alphaX = 1.0;\n // float alphaY = smoothstep(bwy, bwy + smoothingY, vUv.y) *\n // smoothstep(1.0 - bwy, 1.0 - bwy - smoothingY, vUv.y);\n // // float alphaY = smoothstep(bw.y, bw.y + smoothing.y, vUv.y) *\n // // smoothstep(1.0 - bw.y, 1.0 - bw.y - smoothing.y, vUv.y);\n \n // float fillAlpha = alphaX * alphaY;\n // float borderAlpha = 1.0 - fillAlpha;\n\n // vec3 color = mix(borderColor, fillColor, fillAlpha);\n\n // // vec3 color = mix(vec3(0.0,1.0,0.0),vec3(1.0,0.0,0.0), vUv.x);\n // // vec3 color = mix(vec3(0.0,1.0,0.0),vec3(1.0,0.0,0.0), uvPx.x / sizePx.x);\n // float alpha = mix(borderOpacity, fillOpacity, fillAlpha);\n\n // gl_FragColor = vec4(color, alpha);\n }\n }\n }\n `};y(),g.add(n.on("invalidate",y));const P=()=>{u.uniforms.worldPerPixel.value.set(r.camera.scale.x/r.getSize().width,r.camera.scale.y/r.getSize().height)},b=()=>{P();const e=r.getTimeRange(),n=e.startTime+.5*e.duration,[o,i]=f(n);u.uniforms.originTime.value.set(o,i)};g.add(r.on("time",b)),g.add(r.on("value",P)),g.add(r.on("size",P)),b();const w=new e(1,1,1),C=new i(w,u,n.model.data.length);return v.registerMesh(C),C.frustumCulled=!1,t.add(C),g.add(()=>t.remove(C)),g.disposeLater()}function y(e,n,o,i,t,g,p){const h=new c;if(!e.style.point)throw"cannot happen";const u=new l({transparent:!0,uniforms:{selection:{value:-1},origin:{value:new a(0,0)}}}),x=()=>{u.vertexShader=`\n precision highp float;\n\n const float iconSize = ${d(p)};\n \n uniform vec3 origin;\n uniform int selection;\n \n flat out int vSelected;\n \n // const float SPLIT_PART = ${v}.0;\n // const float SPLIT_PART_X2 = ${m}.0;\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 value = 0.5;\n // vec3 delta = position - origin;\n // float relativeTime = delta.x * SPLIT_PART_X2 + delta.y * SPLIT_PART + delta.z;\n\n float hours = position.x;\n float value = position.y;\n float ms = position.z;\n\n float deltaHours = hours - origin.x;\n float deltaMs = ms - origin.y;\n float relativeTime = deltaHours * msInHour + deltaMs;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(relativeTime, value, 1.0, 1.0);\n gl_PointSize = mix(iconSize, 2.0 * iconSize, float(vSelected));\n }\n `,u.fragmentShader=`\n precision highp float;\n\n const vec3 color = ${s(t)};\n const float opacity = ${d(g)};\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};x(),h.add(e.on("invalidate",x));const y=()=>{const e=o.getTimeRange(),n=e.startTime+.5*e.duration,[i,t]=f(n);u.uniforms.origin.value.set(i,t)};h.add(o.on("time",y)),y();const P=new r(i,u);return P.frustumCulled=!1,h.add(e.on("selection",e=>{if(e.length>0){const n=i.elementIndex(e[0]);u.uniforms.selection.value="number"==typeof n?n:-1}else u.uniforms.selection.value=-1})),n.add(P),h.add(()=>n.remove(P)),h.disposeLater()}