@genome-spy/core 0.73.0 → 0.74.0

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 (359) hide show
  1. package/dist/bundle/AbortablePromiseCache-3gHJdF3E.js +96 -0
  2. package/dist/bundle/browser-BTgw5ieH.js +126 -0
  3. package/dist/bundle/chunk-DmhlhrBa.js +11 -0
  4. package/dist/bundle/esm-BDFRLEuD.js +1248 -0
  5. package/dist/bundle/esm-BygJiwh0.js +573 -0
  6. package/dist/bundle/esm-CGX-qz1d.js +155 -0
  7. package/dist/bundle/esm-CgfVIRJ-.js +121 -0
  8. package/dist/bundle/esm-CuMSzCHy.js +298 -0
  9. package/dist/bundle/esm-DMXpJXM4.js +369 -0
  10. package/dist/bundle/esm-DQiq2Zhd.js +1426 -0
  11. package/dist/bundle/esm-DtE8VqAv.js +1015 -0
  12. package/dist/bundle/esm-sIoQYZ21.js +461 -0
  13. package/dist/bundle/index.es.js +21071 -25935
  14. package/dist/bundle/index.js +363 -379
  15. package/dist/bundle/parquetRead-DG_-F5j5.js +1609 -0
  16. package/dist/schema.json +13098 -7095
  17. package/dist/src/config/axisConfig.d.ts +16 -0
  18. package/dist/src/config/axisConfig.d.ts.map +1 -0
  19. package/dist/src/config/axisConfig.js +84 -0
  20. package/dist/src/config/defaultConfig.d.ts +3 -0
  21. package/dist/src/config/defaultConfig.d.ts.map +1 -0
  22. package/dist/src/config/defaultConfig.js +38 -0
  23. package/dist/src/config/defaults/axisDefaults.d.ts +5 -0
  24. package/dist/src/config/defaults/axisDefaults.d.ts.map +1 -0
  25. package/dist/src/config/defaults/axisDefaults.js +72 -0
  26. package/dist/src/config/defaults/markDefaults.d.ts +15 -0
  27. package/dist/src/config/defaults/markDefaults.d.ts.map +1 -0
  28. package/dist/src/config/defaults/markDefaults.js +121 -0
  29. package/dist/src/config/defaults/scaleDefaults.d.ts +5 -0
  30. package/dist/src/config/defaults/scaleDefaults.d.ts.map +1 -0
  31. package/dist/src/config/defaults/scaleDefaults.js +18 -0
  32. package/dist/src/config/defaults/titleDefaults.d.ts +5 -0
  33. package/dist/src/config/defaults/titleDefaults.d.ts.map +1 -0
  34. package/dist/src/config/defaults/titleDefaults.js +47 -0
  35. package/dist/src/config/defaults/viewDefaults.d.ts +3 -0
  36. package/dist/src/config/defaults/viewDefaults.d.ts.map +1 -0
  37. package/dist/src/config/defaults/viewDefaults.js +2 -0
  38. package/dist/src/config/markConfig.d.ts +8 -0
  39. package/dist/src/config/markConfig.d.ts.map +1 -0
  40. package/dist/src/config/markConfig.js +27 -0
  41. package/dist/src/config/mergeConfig.d.ts +8 -0
  42. package/dist/src/config/mergeConfig.d.ts.map +1 -0
  43. package/dist/src/config/mergeConfig.js +81 -0
  44. package/dist/src/config/resolveConfig.d.ts +22 -0
  45. package/dist/src/config/resolveConfig.d.ts.map +1 -0
  46. package/dist/src/config/resolveConfig.js +32 -0
  47. package/dist/src/config/scaleConfig.d.ts +40 -0
  48. package/dist/src/config/scaleConfig.d.ts.map +1 -0
  49. package/dist/src/config/scaleConfig.js +220 -0
  50. package/dist/src/config/styleUtils.d.ts +6 -0
  51. package/dist/src/config/styleUtils.d.ts.map +1 -0
  52. package/dist/src/config/styleUtils.js +10 -0
  53. package/dist/src/config/themes.d.ts +15 -0
  54. package/dist/src/config/themes.d.ts.map +1 -0
  55. package/dist/src/config/themes.js +293 -0
  56. package/dist/src/config/titleConfig.d.ts +12 -0
  57. package/dist/src/config/titleConfig.d.ts.map +1 -0
  58. package/dist/src/config/titleConfig.js +42 -0
  59. package/dist/src/config/viewConfig.d.ts +7 -0
  60. package/dist/src/config/viewConfig.d.ts.map +1 -0
  61. package/dist/src/config/viewConfig.js +29 -0
  62. package/dist/src/data/flowNode.d.ts +22 -1
  63. package/dist/src/data/flowNode.d.ts.map +1 -1
  64. package/dist/src/data/flowNode.js +37 -1
  65. package/dist/src/data/formats/bed.d.ts.map +1 -1
  66. package/dist/src/data/formats/bed.js +6 -1
  67. package/dist/src/data/formats/bedpe.d.ts.map +1 -1
  68. package/dist/src/data/formats/bedpe.js +4 -0
  69. package/dist/src/data/formats/fasta.d.ts.map +1 -1
  70. package/dist/src/data/formats/fasta.js +4 -0
  71. package/dist/src/data/formats/parquet.d.ts.map +1 -1
  72. package/dist/src/data/formats/parquet.js +4 -0
  73. package/dist/src/data/sources/dataSourceFactory.d.ts +2 -13
  74. package/dist/src/data/sources/dataSourceFactory.d.ts.map +1 -1
  75. package/dist/src/data/sources/dataSourceFactory.js +5 -141
  76. package/dist/src/data/sources/lazy/axisGenomeSource.d.ts.map +1 -1
  77. package/dist/src/data/sources/lazy/axisGenomeSource.js +11 -0
  78. package/dist/src/data/sources/lazy/axisTickSource.d.ts +1 -1
  79. package/dist/src/data/sources/lazy/axisTickSource.d.ts.map +1 -1
  80. package/dist/src/data/sources/lazy/axisTickSource.js +19 -8
  81. package/dist/src/data/sources/lazy/bamSource.d.ts.map +1 -1
  82. package/dist/src/data/sources/lazy/bamSource.js +11 -0
  83. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  84. package/dist/src/data/sources/lazy/bigBedSource.js +12 -1
  85. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  86. package/dist/src/data/sources/lazy/bigWigSource.js +11 -0
  87. package/dist/src/data/sources/lazy/gff3Source.d.ts.map +1 -1
  88. package/dist/src/data/sources/lazy/gff3Source.js +12 -1
  89. package/dist/src/data/sources/lazy/indexedFastaSource.d.ts.map +1 -1
  90. package/dist/src/data/sources/lazy/indexedFastaSource.js +11 -0
  91. package/dist/src/data/sources/lazy/lazyDataSourceRegistry.d.ts +27 -0
  92. package/dist/src/data/sources/lazy/lazyDataSourceRegistry.d.ts.map +1 -0
  93. package/dist/src/data/sources/lazy/lazyDataSourceRegistry.js +65 -0
  94. package/dist/src/data/sources/lazy/registerBuiltInLazySources.d.ts +2 -0
  95. package/dist/src/data/sources/lazy/registerBuiltInLazySources.d.ts.map +1 -0
  96. package/dist/src/data/sources/lazy/registerBuiltInLazySources.js +8 -0
  97. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  98. package/dist/src/data/sources/lazy/singleAxisLazySource.js +11 -2
  99. package/dist/src/data/sources/lazy/vcfSource.d.ts.map +1 -1
  100. package/dist/src/data/sources/lazy/vcfSource.js +11 -0
  101. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  102. package/dist/src/data/sources/urlSource.js +5 -2
  103. package/dist/src/data/transforms/aggregate.d.ts +1 -0
  104. package/dist/src/data/transforms/aggregate.d.ts.map +1 -1
  105. package/dist/src/data/transforms/aggregate.js +30 -8
  106. package/dist/src/data/transforms/aggregateOps.d.ts.map +1 -1
  107. package/dist/src/data/transforms/aggregateOps.js +12 -1
  108. package/dist/src/data/transforms/coverage.js +2 -2
  109. package/dist/src/data/transforms/filter.js +1 -1
  110. package/dist/src/data/transforms/filterScoredLabels.d.ts +6 -0
  111. package/dist/src/data/transforms/filterScoredLabels.d.ts.map +1 -1
  112. package/dist/src/data/transforms/filterScoredLabels.js +9 -0
  113. package/dist/src/data/transforms/measureText.d.ts +1 -0
  114. package/dist/src/data/transforms/measureText.d.ts.map +1 -1
  115. package/dist/src/data/transforms/measureText.js +14 -5
  116. package/dist/src/data/transforms/pileup.d.ts.map +1 -1
  117. package/dist/src/data/transforms/pileup.js +1 -2
  118. package/dist/src/data/transforms/regexFold.d.ts.map +1 -1
  119. package/dist/src/data/transforms/regexFold.js +0 -1
  120. package/dist/src/embedFactory.d.ts +13 -0
  121. package/dist/src/embedFactory.d.ts.map +1 -0
  122. package/dist/src/embedFactory.js +127 -0
  123. package/dist/src/encoder/accessor.d.ts +3 -12
  124. package/dist/src/encoder/accessor.d.ts.map +1 -1
  125. package/dist/src/encoder/accessor.js +10 -65
  126. package/dist/src/encoder/encoder.d.ts +51 -8
  127. package/dist/src/encoder/encoder.d.ts.map +1 -1
  128. package/dist/src/encoder/encoder.js +179 -55
  129. package/dist/src/fonts/bmFontManager.js +1 -1
  130. package/dist/src/full.d.ts +2 -0
  131. package/dist/src/full.d.ts.map +1 -0
  132. package/dist/src/full.js +2 -0
  133. package/dist/src/genome/genome.d.ts +8 -0
  134. package/dist/src/genome/genome.d.ts.map +1 -1
  135. package/dist/src/genome/genome.js +16 -2
  136. package/dist/src/genome/genomeStore.js +1 -1
  137. package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -1
  138. package/dist/src/genome/rootGenomeConfig.js +6 -2
  139. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  140. package/dist/src/genome/scaleLocus.js +26 -7
  141. package/dist/src/genomeSpy/cursorManager.d.ts +69 -0
  142. package/dist/src/genomeSpy/cursorManager.d.ts.map +1 -0
  143. package/dist/src/genomeSpy/cursorManager.js +131 -0
  144. package/dist/src/genomeSpy/headlessBootstrap.d.ts +113 -0
  145. package/dist/src/genomeSpy/headlessBootstrap.d.ts.map +1 -0
  146. package/dist/src/genomeSpy/headlessBootstrap.js +246 -0
  147. package/dist/src/genomeSpy/interactionController.d.ts +5 -0
  148. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  149. package/dist/src/genomeSpy/interactionController.js +212 -19
  150. package/dist/src/genomeSpy/interactionDispatcher.d.ts +50 -0
  151. package/dist/src/genomeSpy/interactionDispatcher.d.ts.map +1 -0
  152. package/dist/src/genomeSpy/interactionDispatcher.js +203 -0
  153. package/dist/src/genomeSpy/viewContextFactory.d.ts +4 -2
  154. package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -1
  155. package/dist/src/genomeSpy/viewContextFactory.js +12 -4
  156. package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -1
  157. package/dist/src/genomeSpy/viewDataInit.js +7 -3
  158. package/dist/src/genomeSpy.d.ts +1 -124
  159. package/dist/src/genomeSpy.d.ts.map +1 -1
  160. package/dist/src/genomeSpy.js +7 -688
  161. package/dist/src/genomeSpyBase.d.ts +133 -0
  162. package/dist/src/genomeSpyBase.d.ts.map +1 -0
  163. package/dist/src/genomeSpyBase.js +719 -0
  164. package/dist/src/gl/arrayBuilder.d.ts.map +1 -1
  165. package/dist/src/gl/arrayBuilder.js +0 -3
  166. package/dist/src/gl/colorUtils.d.ts.map +1 -1
  167. package/dist/src/gl/colorUtils.js +3 -0
  168. package/dist/src/gl/dataToVertices.d.ts.map +1 -1
  169. package/dist/src/gl/dataToVertices.js +13 -8
  170. package/dist/src/gl/glslScaleGenerator.d.ts +2 -2
  171. package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
  172. package/dist/src/gl/glslScaleGenerator.js +5 -7
  173. package/dist/src/index.d.ts +3 -9
  174. package/dist/src/index.d.ts.map +1 -1
  175. package/dist/src/index.js +5 -114
  176. package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +4462 -0
  177. package/dist/src/marks/link.d.ts.map +1 -1
  178. package/dist/src/marks/link.js +0 -23
  179. package/dist/src/marks/mark.d.ts +8 -1
  180. package/dist/src/marks/mark.d.ts.map +1 -1
  181. package/dist/src/marks/mark.js +61 -20
  182. package/dist/src/marks/markUtils.d.ts +18 -1
  183. package/dist/src/marks/markUtils.d.ts.map +1 -1
  184. package/dist/src/marks/markUtils.js +52 -4
  185. package/dist/src/marks/point.d.ts.map +1 -1
  186. package/dist/src/marks/point.js +6 -26
  187. package/dist/src/marks/rect.d.ts.map +1 -1
  188. package/dist/src/marks/rect.js +13 -21
  189. package/dist/src/marks/rule.d.ts +7 -2
  190. package/dist/src/marks/rule.d.ts.map +1 -1
  191. package/dist/src/marks/rule.js +125 -16
  192. package/dist/src/marks/text.d.ts.map +1 -1
  193. package/dist/src/marks/text.js +5 -47
  194. package/dist/src/minimal.d.ts +8 -0
  195. package/dist/src/minimal.d.ts.map +1 -0
  196. package/dist/src/minimal.js +21 -0
  197. package/dist/src/paramRuntime/viewParamRuntime.d.ts +19 -0
  198. package/dist/src/paramRuntime/viewParamRuntime.d.ts.map +1 -1
  199. package/dist/src/paramRuntime/viewParamRuntime.js +35 -0
  200. package/dist/src/scale/scale.d.ts.map +1 -1
  201. package/dist/src/scale/scale.js +13 -7
  202. package/dist/src/scales/axisResolution.d.ts.map +1 -1
  203. package/dist/src/scales/axisResolution.js +9 -5
  204. package/dist/src/scales/domainPlanner.d.ts +28 -8
  205. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  206. package/dist/src/scales/domainPlanner.js +207 -73
  207. package/dist/src/scales/indexLikeDomainUtils.d.ts +29 -0
  208. package/dist/src/scales/indexLikeDomainUtils.d.ts.map +1 -0
  209. package/dist/src/scales/indexLikeDomainUtils.js +67 -0
  210. package/dist/src/scales/resolutionMemberOrder.d.ts +15 -0
  211. package/dist/src/scales/resolutionMemberOrder.d.ts.map +1 -0
  212. package/dist/src/scales/resolutionMemberOrder.js +22 -0
  213. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  214. package/dist/src/scales/scaleInteractionController.js +43 -4
  215. package/dist/src/scales/scalePropsResolver.d.ts +3 -1
  216. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
  217. package/dist/src/scales/scalePropsResolver.js +83 -6
  218. package/dist/src/scales/scaleResolution.d.ts +23 -0
  219. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  220. package/dist/src/scales/scaleResolution.js +220 -58
  221. package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -1
  222. package/dist/src/scales/scaleResolutionTestUtils.js +6 -2
  223. package/dist/src/scales/scaleRules.d.ts.map +1 -1
  224. package/dist/src/scales/scaleRules.js +16 -2
  225. package/dist/src/scales/selectionDomainUtils.d.ts +30 -0
  226. package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -1
  227. package/dist/src/scales/selectionDomainUtils.js +116 -1
  228. package/dist/src/screenshotHarness.js +3 -4
  229. package/dist/src/spec/axis.d.ts +41 -30
  230. package/dist/src/spec/channel.d.ts +15 -9
  231. package/dist/src/spec/config.d.ts +264 -0
  232. package/dist/src/spec/data.d.ts +7 -0
  233. package/dist/src/spec/decoration.d.ts +51 -0
  234. package/dist/src/spec/exampleFiles.d.ts +12 -0
  235. package/dist/src/spec/exampleFiles.d.ts.map +1 -0
  236. package/dist/src/spec/exampleFiles.js +52 -0
  237. package/dist/src/spec/font.d.ts +1 -1
  238. package/dist/src/spec/mark.d.ts +97 -13
  239. package/dist/src/spec/parameter.d.ts +30 -10
  240. package/dist/src/spec/root.d.ts +14 -0
  241. package/dist/src/spec/scale.d.ts +18 -13
  242. package/dist/src/spec/title.d.ts +13 -2
  243. package/dist/src/spec/tooltip.d.ts +1 -1
  244. package/dist/src/spec/transform.d.ts +39 -4
  245. package/dist/src/spec/view.d.ts +67 -19
  246. package/dist/src/styles/genome-spy.css +55 -55
  247. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  248. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  249. package/dist/src/styles/genome-spy.css.js +23 -22
  250. package/dist/src/testSetup.d.ts +2 -0
  251. package/dist/src/testSetup.d.ts.map +1 -0
  252. package/dist/src/testSetup.js +5 -0
  253. package/dist/src/tooltip/dataTooltipHandler.js +8 -2
  254. package/dist/src/tooltip/tooltipContext.d.ts.map +1 -1
  255. package/dist/src/tooltip/tooltipContext.js +3 -2
  256. package/dist/src/types/embedApi.d.ts +7 -0
  257. package/dist/src/types/encoder.d.ts +17 -15
  258. package/dist/src/types/scaleResolutionApi.d.ts +20 -0
  259. package/dist/src/types/viewContext.d.ts +23 -1
  260. package/dist/src/utils/expression.d.ts +2 -2
  261. package/dist/src/utils/expression.d.ts.map +1 -1
  262. package/dist/src/utils/expression.js +63 -8
  263. package/dist/src/utils/field.d.ts.map +1 -1
  264. package/dist/src/utils/field.js +0 -1
  265. package/dist/src/utils/inertia.d.ts.map +1 -1
  266. package/dist/src/utils/inertia.js +0 -1
  267. package/dist/src/utils/inputBinding.d.ts +1 -1
  268. package/dist/src/utils/interaction.d.ts +109 -0
  269. package/dist/src/utils/interaction.d.ts.map +1 -0
  270. package/dist/src/utils/interaction.js +200 -0
  271. package/dist/src/utils/interactionEvent.d.ts +21 -42
  272. package/dist/src/utils/interactionEvent.d.ts.map +1 -1
  273. package/dist/src/utils/interactionEvent.js +43 -66
  274. package/dist/src/utils/kWayMerge.js +1 -1
  275. package/dist/src/utils/mergeObjects.d.ts.map +1 -1
  276. package/dist/src/utils/mergeObjects.js +0 -2
  277. package/dist/src/utils/radixSort.d.ts.map +1 -1
  278. package/dist/src/utils/radixSort.js +0 -2
  279. package/dist/src/utils/throttle.d.ts.map +1 -1
  280. package/dist/src/utils/throttle.js +0 -2
  281. package/dist/src/utils/ui/tooltip.d.ts +1 -0
  282. package/dist/src/utils/ui/tooltip.d.ts.map +1 -1
  283. package/dist/src/utils/ui/tooltip.js +1 -0
  284. package/dist/src/utils/url.js +1 -1
  285. package/dist/src/view/axisGridView.d.ts +1 -1
  286. package/dist/src/view/axisGridView.d.ts.map +1 -1
  287. package/dist/src/view/axisGridView.js +2 -47
  288. package/dist/src/view/axisView.d.ts +2 -3
  289. package/dist/src/view/axisView.d.ts.map +1 -1
  290. package/dist/src/view/axisView.js +251 -106
  291. package/dist/src/view/concatView.d.ts +2 -1
  292. package/dist/src/view/concatView.d.ts.map +1 -1
  293. package/dist/src/view/concatView.js +4 -2
  294. package/dist/src/view/containerMutationHelper.d.ts +3 -0
  295. package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
  296. package/dist/src/view/containerMutationHelper.js +4 -1
  297. package/dist/src/view/facetView.d.ts +1 -1
  298. package/dist/src/view/facetView.js +3 -3
  299. package/dist/src/view/flowBuilder.js +2 -2
  300. package/dist/src/view/gridView/gridChild.d.ts +6 -0
  301. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  302. package/dist/src/view/gridView/gridChild.js +72 -43
  303. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  304. package/dist/src/view/gridView/gridView.js +255 -101
  305. package/dist/src/view/gridView/keyboardZoomController.d.ts +2 -2
  306. package/dist/src/view/gridView/keyboardZoomController.d.ts.map +1 -1
  307. package/dist/src/view/gridView/keyboardZoomController.js +1 -1
  308. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  309. package/dist/src/view/gridView/scrollbar.js +4 -2
  310. package/dist/src/view/gridView/selectionRect.d.ts +4 -0
  311. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  312. package/dist/src/view/gridView/selectionRect.js +20 -1
  313. package/dist/src/view/gridView/separatorView.d.ts +1 -0
  314. package/dist/src/view/gridView/separatorView.d.ts.map +1 -1
  315. package/dist/src/view/gridView/separatorView.js +9 -0
  316. package/dist/src/view/interactionRouting.d.ts +20 -0
  317. package/dist/src/view/interactionRouting.d.ts.map +1 -0
  318. package/dist/src/view/interactionRouting.js +53 -0
  319. package/dist/src/view/layerView.d.ts.map +1 -1
  320. package/dist/src/view/layerView.js +12 -9
  321. package/dist/src/view/layout/grid.js +1 -1
  322. package/dist/src/view/renderingContext/bufferedViewRenderingContext.d.ts.map +1 -1
  323. package/dist/src/view/renderingContext/bufferedViewRenderingContext.js +0 -2
  324. package/dist/src/view/testUtils.d.ts +17 -3
  325. package/dist/src/view/testUtils.d.ts.map +1 -1
  326. package/dist/src/view/testUtils.js +62 -69
  327. package/dist/src/view/title.d.ts +8 -1
  328. package/dist/src/view/title.d.ts.map +1 -1
  329. package/dist/src/view/title.js +66 -76
  330. package/dist/src/view/unitView.d.ts +1 -1
  331. package/dist/src/view/unitView.d.ts.map +1 -1
  332. package/dist/src/view/unitView.js +67 -17
  333. package/dist/src/view/view.d.ts +76 -30
  334. package/dist/src/view/view.d.ts.map +1 -1
  335. package/dist/src/view/view.js +136 -47
  336. package/dist/src/view/viewFactory.d.ts +11 -3
  337. package/dist/src/view/viewFactory.d.ts.map +1 -1
  338. package/dist/src/view/viewFactory.js +37 -11
  339. package/dist/src/view/viewUtils.d.ts.map +1 -1
  340. package/dist/src/view/viewUtils.js +41 -5
  341. package/dist/src/view/zoom.d.ts +2 -2
  342. package/dist/src/view/zoom.d.ts.map +1 -1
  343. package/dist/src/view/zoom.js +21 -23
  344. package/package.json +18 -10
  345. package/dist/bundle/AbortablePromiseCache-Dj0vzLnp.js +0 -149
  346. package/dist/bundle/browser-0iNU5Wit.js +0 -138
  347. package/dist/bundle/index-BYsZN7b0.js +0 -1597
  348. package/dist/bundle/index-C3kClAEN.js +0 -1771
  349. package/dist/bundle/index-C7wOh6y1.js +0 -657
  350. package/dist/bundle/index-CRaQAuki.js +0 -326
  351. package/dist/bundle/index-D9v1PCj9.js +0 -507
  352. package/dist/bundle/index-GDOuv_D5.js +0 -266
  353. package/dist/bundle/index-Gt44EOIH.js +0 -628
  354. package/dist/bundle/inflate-GtwLkvSP.js +0 -1048
  355. package/dist/bundle/parquetRead-BnAGCa4_.js +0 -1663
  356. package/dist/bundle/unzip-Bac01w6X.js +0 -1492
  357. package/dist/src/config/scaleDefaults.d.ts +0 -8
  358. package/dist/src/config/scaleDefaults.d.ts.map +0 -1
  359. package/dist/src/config/scaleDefaults.js +0 -45
@@ -0,0 +1,4462 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`generated shader snapshots > interval selection example 1`] = `
4
+ {
5
+ "fragment": "precision highp float;
6
+ precision highp int;
7
+
8
+ // view: viewRoot
9
+
10
+ layout(std140) uniform Mark {
11
+ /**
12
+ * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.
13
+ * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with
14
+ * geometric zoom, etc.
15
+ */
16
+ uniform bool uInwardStroke;
17
+
18
+ /** The minimum point size in pixels when rendering into the picking buffer */
19
+ uniform float uMinPickingSize;
20
+
21
+ /** Scale factor for geometric zoom */
22
+ uniform mediump float uScaleFactor;
23
+
24
+ uniform mediump float uZoomLevel;
25
+ uniform highp float uSemanticThreshold;
26
+
27
+ uniform mediump float uGradientStrength;
28
+
29
+ // Selection parameter
30
+ uniform highp float[2] uParam_brush_x;
31
+ mediump float uDomain_x[2];
32
+
33
+ mediump float uDomain_y[2];
34
+
35
+ };
36
+
37
+
38
+ #define PI 3.141593
39
+
40
+ uniform View {
41
+ /** Offset in "unit" units */
42
+ mediump vec2 uViewOffset;
43
+ mediump vec2 uViewScale;
44
+ /** Size of the logical viewport in pixels, i.e., the view */
45
+ mediump vec2 uViewportSize;
46
+ lowp float uDevicePixelRatio;
47
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
48
+ // that is rendered with the specified opacity.
49
+ lowp float uViewOpacity;
50
+ bool uPickingEnabled;
51
+ };
52
+
53
+
54
+ /**
55
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
56
+ * (0, 0) is at the bottom left corner.
57
+ */
58
+ vec4 unitToNdc(vec2 coord) {
59
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
60
+ }
61
+
62
+ vec4 unitToNdc(float x, float y) {
63
+ return unitToNdc(vec2(x, y));
64
+ }
65
+
66
+ vec4 pixelsToNdc(vec2 coord) {
67
+ return unitToNdc(coord / uViewportSize);
68
+ }
69
+
70
+ vec4 pixelsToNdc(float x, float y) {
71
+ return pixelsToNdc(vec2(x, y));
72
+ }
73
+
74
+ float linearstep(float edge0, float edge1, float x) {
75
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
76
+ }
77
+
78
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
79
+
80
+ highp uint hash32(highp uint key) {
81
+ highp uint v = key;
82
+ v ^= v >> 16u;
83
+ v *= 0x7feb352du;
84
+ v ^= v >> 15u;
85
+ v *= 0x846ca68bu;
86
+ v ^= v >> 16u;
87
+ return v;
88
+ }
89
+
90
+ bool isEmptyHashTexture(highp usampler2D s) {
91
+ // Empty selections are encoded as a single empty hash slot.
92
+ ivec2 texSize = textureSize(s, 0);
93
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
94
+ }
95
+
96
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
97
+ ivec2 texSize = textureSize(s, 0);
98
+ highp uint width = uint(texSize.x);
99
+ highp uint size = width * uint(texSize.y);
100
+ highp uint mask = size - 1u;
101
+ highp uint index = hash32(value) & mask;
102
+
103
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
104
+ ivec2 coord = ivec2(int(index % width), int(index / width));
105
+ highp uint entry = texelFetch(s, coord, 0).r;
106
+ if (entry == value) {
107
+ return true;
108
+ }
109
+ if (entry == HASH_EMPTY_KEY) {
110
+ return false;
111
+ }
112
+ index = (index + 1u) & mask;
113
+ }
114
+
115
+ return false;
116
+ }
117
+
118
+ /**
119
+ * Calculates a gamma for antialiasing opacity based on the color.
120
+ */
121
+ float getGammaForColor(vec3 rgb) {
122
+ return mix(
123
+ 1.25,
124
+ 0.75,
125
+ // RGB should be linearized but this is good enough for now
126
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
127
+ }
128
+
129
+ // Fragment shader stuff ////////////////////////////////////////////////////////
130
+
131
+ // TODO: include the following only in fragment shaders
132
+
133
+ /**
134
+ * Specialized linearstep for doing antialiasing
135
+ */
136
+ float distanceToRatio(float d) {
137
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
138
+ }
139
+
140
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
141
+ if (halfStrokeWidth > 0.0) {
142
+ // Distance to stroke's edge. Negative inside the stroke.
143
+ float sd = abs(d) - halfStrokeWidth;
144
+ return mix(
145
+ stroke,
146
+ d <= 0.0 ? fill : background,
147
+ distanceToRatio(sd));
148
+ } else {
149
+ return mix(background, fill, distanceToRatio(-d));
150
+ }
151
+ }
152
+
153
+
154
+ in highp vec4 vPickingColor;
155
+
156
+
157
+ const lowp vec4 white = vec4(1.0);
158
+ const lowp vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
159
+
160
+ flat in float vRadius;
161
+ flat in float vRadiusWithPadding;
162
+
163
+ flat in lowp vec4 vFillColor;
164
+ flat in lowp vec4 vStrokeColor;
165
+ flat in lowp float vShape;
166
+ flat in lowp float vHalfStrokeWidth;
167
+
168
+ flat in mat2 vRotationMatrix;
169
+
170
+ out lowp vec4 fragColor;
171
+
172
+ // Copypaste from vertex shader
173
+ const float CIRCLE = 0.0;
174
+ const float SQUARE = 1.0;
175
+ const float CROSS = 2.0;
176
+ const float DIAMOND = 3.0;
177
+ const float TRIANGLE_UP = 4.0;
178
+ const float TICK_UP = 8.0;
179
+
180
+ // The distance functions are inspired by:
181
+ // http://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
182
+ // However, these are not true distance functions, because the corners need to be sharp.
183
+
184
+ float circle(vec2 p, float r) {
185
+ return length(p) - r;
186
+ }
187
+
188
+ float square(vec2 p, float r) {
189
+ p = abs(p);
190
+ return max(p.x, p.y) - r;
191
+ }
192
+
193
+ float tickUp(vec2 p, float r) {
194
+ float halfR = r * 0.5;
195
+ p.y += halfR;
196
+ p = abs(p);
197
+ return max(p.x - r * 0.15, p.y - halfR);
198
+ }
199
+
200
+ float equilateralTriangle(vec2 p, float r) {
201
+ p.y = -p.y;
202
+ float k = sqrt(3.0);
203
+ float kr = k * r;
204
+ p.y -= kr / 2.0;
205
+ return max((abs(p.x) * k + p.y) / 2.0, -p.y - kr);
206
+ }
207
+
208
+ float crossShape(vec2 p, float r) {
209
+ p = abs(p);
210
+
211
+ vec2 b = vec2(0.4, 1.0) * r;
212
+ vec2 v = abs(p) - b.xy;
213
+ vec2 h = abs(p) - b.yx;
214
+ return min(max(v.x, v.y), max(h.x, h.y));
215
+ }
216
+
217
+ float diamond(vec2 p, float r) {
218
+ p = abs(p);
219
+ return (max(abs(p.x - p.y), abs(p.x + p.y)) - r) / sqrt(2.0);
220
+ }
221
+
222
+ void main() {
223
+ float d;
224
+
225
+ /** Normalized point coord */
226
+ vec2 p = vRotationMatrix * (2.0 * gl_PointCoord - 1.0) * vRadiusWithPadding;
227
+ float r = vRadius;
228
+
229
+ // We could also use textures here. Could even be faster, because we have plenty of branching here.
230
+ if (vShape == CIRCLE) {
231
+ d = circle(p, r);
232
+
233
+ } else if (vShape == SQUARE) {
234
+ d = square(p, r);
235
+
236
+ } else if (vShape == CROSS) {
237
+ d = crossShape(p, r);
238
+
239
+ } else if (vShape == DIAMOND) {
240
+ d = diamond(p, r);
241
+
242
+ } else if (vShape == TRIANGLE_UP) {
243
+ d = equilateralTriangle(p, r);
244
+
245
+ } else if (vShape == TICK_UP) {
246
+ d = tickUp(p, r);
247
+
248
+ } else {
249
+ d = 0.0;
250
+ }
251
+
252
+ if (!uPickingEnabled) {
253
+ lowp vec4 fillColor = mix(vFillColor, white, -d * uGradientStrength / vRadius);
254
+
255
+ fragColor = distanceToColor(
256
+ d + (uInwardStroke ? vHalfStrokeWidth : 0.0),
257
+ fillColor,
258
+ vStrokeColor,
259
+ vec4(0.0),
260
+ vHalfStrokeWidth);
261
+
262
+ } else if (d - vHalfStrokeWidth <= 0.0) {
263
+ fragColor = vPickingColor;
264
+
265
+ } else {
266
+ discard;
267
+ }
268
+ }
269
+
270
+ ",
271
+ "vertex": "precision highp float;
272
+ precision highp int;
273
+
274
+ // view: viewRoot
275
+
276
+ layout(std140) uniform Mark {
277
+ /**
278
+ * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.
279
+ * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with
280
+ * geometric zoom, etc.
281
+ */
282
+ uniform bool uInwardStroke;
283
+
284
+ /** The minimum point size in pixels when rendering into the picking buffer */
285
+ uniform float uMinPickingSize;
286
+
287
+ /** Scale factor for geometric zoom */
288
+ uniform mediump float uScaleFactor;
289
+
290
+ uniform mediump float uZoomLevel;
291
+ uniform highp float uSemanticThreshold;
292
+
293
+ uniform mediump float uGradientStrength;
294
+
295
+ // Selection parameter
296
+ uniform highp float[2] uParam_brush_x;
297
+ mediump float uDomain_x[2];
298
+
299
+ mediump float uDomain_y[2];
300
+
301
+ };
302
+
303
+
304
+ #define PI 3.141593
305
+
306
+ uniform View {
307
+ /** Offset in "unit" units */
308
+ mediump vec2 uViewOffset;
309
+ mediump vec2 uViewScale;
310
+ /** Size of the logical viewport in pixels, i.e., the view */
311
+ mediump vec2 uViewportSize;
312
+ lowp float uDevicePixelRatio;
313
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
314
+ // that is rendered with the specified opacity.
315
+ lowp float uViewOpacity;
316
+ bool uPickingEnabled;
317
+ };
318
+
319
+
320
+ /**
321
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
322
+ * (0, 0) is at the bottom left corner.
323
+ */
324
+ vec4 unitToNdc(vec2 coord) {
325
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
326
+ }
327
+
328
+ vec4 unitToNdc(float x, float y) {
329
+ return unitToNdc(vec2(x, y));
330
+ }
331
+
332
+ vec4 pixelsToNdc(vec2 coord) {
333
+ return unitToNdc(coord / uViewportSize);
334
+ }
335
+
336
+ vec4 pixelsToNdc(float x, float y) {
337
+ return pixelsToNdc(vec2(x, y));
338
+ }
339
+
340
+ float linearstep(float edge0, float edge1, float x) {
341
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
342
+ }
343
+
344
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
345
+
346
+ highp uint hash32(highp uint key) {
347
+ highp uint v = key;
348
+ v ^= v >> 16u;
349
+ v *= 0x7feb352du;
350
+ v ^= v >> 15u;
351
+ v *= 0x846ca68bu;
352
+ v ^= v >> 16u;
353
+ return v;
354
+ }
355
+
356
+ bool isEmptyHashTexture(highp usampler2D s) {
357
+ // Empty selections are encoded as a single empty hash slot.
358
+ ivec2 texSize = textureSize(s, 0);
359
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
360
+ }
361
+
362
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
363
+ ivec2 texSize = textureSize(s, 0);
364
+ highp uint width = uint(texSize.x);
365
+ highp uint size = width * uint(texSize.y);
366
+ highp uint mask = size - 1u;
367
+ highp uint index = hash32(value) & mask;
368
+
369
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
370
+ ivec2 coord = ivec2(int(index % width), int(index / width));
371
+ highp uint entry = texelFetch(s, coord, 0).r;
372
+ if (entry == value) {
373
+ return true;
374
+ }
375
+ if (entry == HASH_EMPTY_KEY) {
376
+ return false;
377
+ }
378
+ index = (index + 1u) & mask;
379
+ }
380
+
381
+ return false;
382
+ }
383
+
384
+ /**
385
+ * Calculates a gamma for antialiasing opacity based on the color.
386
+ */
387
+ float getGammaForColor(vec3 rgb) {
388
+ return mix(
389
+ 1.25,
390
+ 0.75,
391
+ // RGB should be linearized but this is good enough for now
392
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
393
+ }
394
+
395
+ // Fragment shader stuff ////////////////////////////////////////////////////////
396
+
397
+ // TODO: include the following only in fragment shaders
398
+
399
+ /**
400
+ * Specialized linearstep for doing antialiasing
401
+ */
402
+ float distanceToRatio(float d) {
403
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
404
+ }
405
+
406
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
407
+ if (halfStrokeWidth > 0.0) {
408
+ // Distance to stroke's edge. Negative inside the stroke.
409
+ float sd = abs(d) - halfStrokeWidth;
410
+ return mix(
411
+ stroke,
412
+ d <= 0.0 ? fill : background,
413
+ distanceToRatio(sd));
414
+ } else {
415
+ return mix(background, fill, distanceToRatio(-d));
416
+ }
417
+ }
418
+
419
+
420
+ uniform highp float uZero;
421
+
422
+ // Utils ------------
423
+
424
+ vec3 getDiscreteColor(sampler2D s, int index) {
425
+ return texelFetch(s, ivec2(index % textureSize(s, 0).x, 0), 0).rgb;
426
+ }
427
+
428
+ vec3 getInterpolatedColor(sampler2D s, float unitValue) {
429
+ return texture(s, vec2(unitValue, 0.0)).rgb;
430
+ }
431
+
432
+ float clampToRange(float value, vec2 range) {
433
+ return clamp(value, min(range[0], range[1]), max(range[0], range[1]));
434
+ }
435
+
436
+ // Scales ------------
437
+ // Based on d3 scales: https://github.com/d3/d3-scale
438
+
439
+ float scaleIdentity(float value) {
440
+ return value;
441
+ }
442
+
443
+ float scaleIdentity(uint value) {
444
+ return float(value);
445
+ }
446
+
447
+ float scaleLinear(float value, vec2 domain, vec2 range) {
448
+ float domainSpan = domain[1] - domain[0];
449
+ float rangeSpan = range[1] - range[0];
450
+ return (value - domain[0]) / domainSpan * rangeSpan + range[0];
451
+ }
452
+
453
+ float scaleLog(float value, vec2 domain, vec2 range, float base) {
454
+ // y = m log(x) + b
455
+ // TODO: Perf optimization: precalculate log domain in js.
456
+ // TODO: Reversed domain, etc
457
+ return scaleLinear(log(value) / log(base), log(domain) / log(base), range);
458
+ }
459
+
460
+ float symlog(float value, float constant) {
461
+ // WARNING: emulating log1p with log(x + 1). Small numbers are likely to
462
+ // have significant precision problems.
463
+ return sign(value) * log(abs(value / constant) + 1.0);
464
+ }
465
+
466
+ float scaleSymlog(float value, vec2 domain, vec2 range, float constant) {
467
+ return scaleLinear(
468
+ symlog(value, constant),
469
+ vec2(symlog(domain[0], constant), symlog(domain[1], constant)),
470
+ range
471
+ );
472
+ }
473
+
474
+ float scalePow(float value, vec2 domain, vec2 range, float exponent) {
475
+ // y = mx^k + b
476
+ // TODO: Perf optimization: precalculate pow domain in js.
477
+ // TODO: Reversed domain, etc
478
+ return scaleLinear(
479
+ pow(abs(value), exponent) * sign(value),
480
+ pow(abs(domain), vec2(exponent)) * sign(domain),
481
+ range
482
+ );
483
+ }
484
+
485
+ // TODO: scaleThreshold
486
+ // TODO: scaleQuantile (special case of threshold scale)
487
+
488
+ // TODO: domainExtent should be uint
489
+ float scaleBand(uint value, vec2 domainExtent, vec2 range,
490
+ float paddingInner, float paddingOuter,
491
+ float align, float band) {
492
+
493
+ // TODO: reverse
494
+ float start = range[0];
495
+ float stop = range[1];
496
+ float rangeSpan = stop - start;
497
+
498
+ float n = domainExtent[1] - domainExtent[0];
499
+
500
+ // This fix departs from Vega and d3: https://github.com/vega/vega/issues/3357#issuecomment-1063253596
501
+ paddingInner = int(n) > 1 ? paddingInner : 0.0;
502
+
503
+ // Adapted from: https://github.com/d3/d3-scale/blob/master/src/band.js
504
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
505
+ start += (rangeSpan - step * (n - paddingInner)) * align;
506
+ float bandwidth = step * (1.0 - paddingInner);
507
+
508
+ return start + (float(value) - domainExtent[0]) * step + bandwidth * band;
509
+ }
510
+
511
+ const int lowBits = 12;
512
+ const float lowDivisor = pow(2.0, float(lowBits));
513
+ const uint lowMask = uint(lowDivisor - 1.0);
514
+
515
+ vec2 splitUint(uint value) {
516
+ uint valueLo = value & lowMask;
517
+ uint valueHi = value - valueLo;
518
+ return vec2(float(valueHi), float(valueLo));
519
+ }
520
+
521
+ /**
522
+ * High precision variant of scaleBand for index/locus scales
523
+ */
524
+ float scaleBandHp(uint value, vec3 domainExtent, vec2 range,
525
+ float paddingInner, float paddingOuter,
526
+ float align, float band) {
527
+
528
+ // TODO: reverse
529
+ float start = range[0];
530
+ float stop = range[1];
531
+ float rangeSpan = stop - start;
532
+
533
+ vec2 domainStart = domainExtent.xy;
534
+ float n = domainExtent[2];
535
+
536
+ // The following computation is identical for every vertex. Could be done on the JS side.
537
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
538
+ start += (rangeSpan - step * (n - paddingInner)) * align;
539
+ float bandwidth = step * (1.0 - paddingInner);
540
+
541
+ // Split into to values with each having a reduced number of significant digits
542
+ // to mitigate the lack of precision in float32 calculations.
543
+ vec2 splitValue = splitUint(value);
544
+
545
+ // Using max to prevent the shader compiler from wrecking the precision.
546
+ // Othwewise the compiler could optimize the sum of the four terms into
547
+ // some equivalent form that does premature rounding.
548
+ float inf = 1.0 / uZero;
549
+ float hi = max(splitValue[0] - domainStart[0], -inf);
550
+ float lo = max(splitValue[1] - domainStart[1], -inf);
551
+
552
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
553
+ }
554
+
555
+ /**
556
+ * High precision variant of scaleBand for index/locus scales for large
557
+ * domains where 32bit uints are not sufficient to represent the domain.
558
+ */
559
+ float scaleBandHp(uvec2 value, vec3 domainExtent, vec2 range,
560
+ float paddingInner, float paddingOuter,
561
+ float align, float band) {
562
+
563
+ // TODO: reverse
564
+ float start = range[0];
565
+ float stop = range[1];
566
+ float rangeSpan = stop - start;
567
+
568
+ vec2 domainStart = domainExtent.xy;
569
+ float n = domainExtent[2];
570
+
571
+ // The following computation is identical for every vertex. Could be done on the JS side.
572
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
573
+ start += (rangeSpan - step * (n - paddingInner)) * align;
574
+ float bandwidth = step * (1.0 - paddingInner);
575
+
576
+ // Split into to values with each having a reduced number of significant digits
577
+ // to mitigate the lack of precision in float32 calculations.
578
+ vec2 splitValue = vec2(float(value[0]) * lowDivisor, float(value[1]));
579
+
580
+ // Using max to prevent the shader compiler from wrecking the precision.
581
+ // Othwewise the compiler could optimize the sum of the four terms into
582
+ // some equivalent form that does premature rounding.
583
+ float inf = 1.0 / uZero;
584
+ float hi = max(splitValue[0] - domainStart[0], -inf);
585
+ float lo = max(splitValue[1] - domainStart[1], -inf);
586
+
587
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
588
+ }
589
+
590
+
591
+ in highp uint attr_uniqueId;
592
+ in highp float attr_x;
593
+ in highp float attr_y;
594
+
595
+ bool checkSelection_brush(bool empty) {
596
+ return (uParam_brush_x[0] <= attr_x && attr_x <= uParam_brush_x[1]) || (empty && (uParam_brush_x[0] > uParam_brush_x[1]));
597
+ }
598
+
599
+
600
+ uint accessor_uniqueId_0() {
601
+ return attr_uniqueId;
602
+ }
603
+
604
+ uint getScaled_uniqueId() {
605
+ return accessor_uniqueId_0();
606
+ }
607
+
608
+ #define uniqueId_DEFINED
609
+
610
+
611
+ float accessor_x_0() {
612
+ return attr_x;
613
+ }
614
+
615
+
616
+ //////////////////////////////////////////////////////////////////////
617
+ // Channel: x
618
+
619
+ const vec2 range_x = vec2(0.0, 1.0);
620
+
621
+ float scale_x(float value) {
622
+ int slot = 0;
623
+ vec2 domain = vec2(uDomain_x[slot], uDomain_x[slot + 1]);
624
+ float transformed = scaleLinear(value, domain, range_x);
625
+ return transformed;
626
+
627
+ }
628
+
629
+ float getScaled_x() {
630
+ return scale_x(accessor_x_0());
631
+ }
632
+
633
+ #define x_DEFINED
634
+
635
+
636
+ float accessor_y_0() {
637
+ return attr_y;
638
+ }
639
+
640
+
641
+ //////////////////////////////////////////////////////////////////////
642
+ // Channel: y
643
+
644
+ const vec2 range_y = vec2(0.0, 1.0);
645
+
646
+ float scale_y(float value) {
647
+ int slot = 0;
648
+ vec2 domain = vec2(uDomain_y[slot], uDomain_y[slot + 1]);
649
+ float transformed = scaleLinear(value, domain, range_y);
650
+ return transformed;
651
+
652
+ }
653
+
654
+ float getScaled_y() {
655
+ return scale_y(accessor_y_0());
656
+ }
657
+
658
+ #define y_DEFINED
659
+
660
+
661
+ float accessor_size_0() {
662
+ // Constant value
663
+ return float(100.0);
664
+ }
665
+
666
+ float getScaled_size() {
667
+ return accessor_size_0();
668
+ }
669
+
670
+ #define size_DEFINED
671
+
672
+
673
+ float accessor_semanticScore_0() {
674
+ // Constant value
675
+ return float(0.0);
676
+ }
677
+
678
+ float getScaled_semanticScore() {
679
+ return accessor_semanticScore_0();
680
+ }
681
+
682
+ #define semanticScore_DEFINED
683
+
684
+
685
+ float accessor_shape_0() {
686
+ // Constant value
687
+ return float(0.0);
688
+ }
689
+
690
+ float getScaled_shape() {
691
+ return accessor_shape_0();
692
+ }
693
+
694
+ #define shape_DEFINED
695
+
696
+
697
+ float accessor_strokeWidth_0() {
698
+ // Constant value
699
+ return float(0.0);
700
+ }
701
+
702
+ float getScaled_strokeWidth() {
703
+ return accessor_strokeWidth_0();
704
+ }
705
+
706
+ #define strokeWidth_DEFINED
707
+
708
+
709
+ float accessor_dx_0() {
710
+ // Constant value
711
+ return float(0.0);
712
+ }
713
+
714
+ float getScaled_dx() {
715
+ return accessor_dx_0();
716
+ }
717
+
718
+ #define dx_DEFINED
719
+
720
+
721
+ float accessor_dy_0() {
722
+ // Constant value
723
+ return float(0.0);
724
+ }
725
+
726
+ float getScaled_dy() {
727
+ return accessor_dy_0();
728
+ }
729
+
730
+ #define dy_DEFINED
731
+
732
+
733
+ float accessor_angle_0() {
734
+ // Constant value
735
+ return float(0.0);
736
+ }
737
+
738
+ float getScaled_angle() {
739
+ return accessor_angle_0();
740
+ }
741
+
742
+ #define angle_DEFINED
743
+
744
+
745
+ vec3 accessor_stroke_0() {
746
+ // Constant value
747
+ return vec3(0.0, 0.0, 0.0);
748
+ }
749
+
750
+ vec3 getScaled_stroke() {
751
+ return accessor_stroke_0();
752
+ }
753
+
754
+ #define stroke_DEFINED
755
+
756
+
757
+ float accessor_strokeOpacity_0() {
758
+ // Constant value
759
+ return float(1.0);
760
+ }
761
+
762
+ float getScaled_strokeOpacity() {
763
+ return accessor_strokeOpacity_0();
764
+ }
765
+
766
+ #define strokeOpacity_DEFINED
767
+
768
+
769
+ vec3 accessor_fill_0() {
770
+ // Constant value
771
+ return vec3(0.2, 0.5333333333333333, 0.8);
772
+ }
773
+
774
+
775
+ vec3 accessor_fill_1() {
776
+ // Constant value
777
+ return vec3(0.8666666666666667, 0.8666666666666667, 0.8666666666666667);
778
+ }
779
+
780
+ vec3 getScaled_fill() {
781
+ if (checkSelection_brush(true)) {
782
+ return accessor_fill_0();
783
+ }
784
+ else {
785
+ return accessor_fill_1();
786
+ }
787
+ }
788
+
789
+ #define fill_DEFINED
790
+
791
+
792
+ float accessor_fillOpacity_0() {
793
+ // Constant value
794
+ return float(1.0);
795
+ }
796
+
797
+ float getScaled_fillOpacity() {
798
+ return accessor_fillOpacity_0();
799
+ }
800
+
801
+ #define fillOpacity_DEFINED
802
+
803
+ bool isPointSelected() {
804
+ return checkSelection_brush(false);
805
+ }
806
+
807
+
808
+ /**
809
+ * Describes where a sample facet should be shown. Interpolating between the
810
+ * current and target positions/heights allows for transitioning between facet
811
+ * configurations.
812
+ */
813
+ struct SampleFacetPosition {
814
+ float pos;
815
+ float height;
816
+ float targetPos;
817
+ float targetHeight;
818
+ };
819
+
820
+ /**
821
+ * Trasition fraction [0, 1] between the current and target configurations.
822
+ */
823
+ uniform float uTransitionOffset;
824
+
825
+
826
+ // ----------------------------------------------------------------------------
827
+
828
+ #if !defined(SAMPLE_FACET_UNIFORM) && !defined(SAMPLE_FACET_TEXTURE)
829
+
830
+ SampleFacetPosition getSampleFacetPos() {
831
+ return SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
832
+ }
833
+
834
+ #elif defined(SAMPLE_FACET_UNIFORM)
835
+
836
+ /**
837
+ * Location and height of the band on the Y axis on a normalized [0, 1] scale.
838
+ * Elements: curr pos, curr height, target pos, target height
839
+ */
840
+ uniform vec4 uSampleFacet;
841
+
842
+ SampleFacetPosition getSampleFacetPos() {
843
+ return SampleFacetPosition(
844
+ 1.0 - uSampleFacet.x - uSampleFacet.y,
845
+ uSampleFacet.y,
846
+ 1.0 - uSampleFacet.z - uSampleFacet.w,
847
+ uSampleFacet.w
848
+ );
849
+ }
850
+
851
+ #elif defined(SAMPLE_FACET_TEXTURE)
852
+
853
+ uniform sampler2D uSampleFacetTexture;
854
+
855
+ SampleFacetPosition getSampleFacetPos() {
856
+ vec4 texel = texelFetch(uSampleFacetTexture, ivec2(int(attr_facetIndex), 0), 0);
857
+ return SampleFacetPosition(
858
+ 1.0 - texel.r - texel.g,
859
+ texel.g,
860
+ 1.0 - texel.r - texel.g,
861
+ texel.g);
862
+ }
863
+
864
+ #endif
865
+
866
+ // ----------------------------------------------------------------------------
867
+
868
+ bool isFacetedSamples(SampleFacetPosition facetPos) {
869
+ return facetPos != SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
870
+ }
871
+
872
+ bool isFacetedSamples() {
873
+ return isFacetedSamples(getSampleFacetPos());
874
+ }
875
+
876
+ bool isInTransit() {
877
+ return uTransitionOffset > 0.0;
878
+ }
879
+
880
+ float getTransitionFraction(float xPos) {
881
+ return smoothstep(0.0, 0.7 + uTransitionOffset, (xPos - uTransitionOffset) * 2.0);
882
+ }
883
+
884
+ vec2 applySampleFacet(vec2 pos) {
885
+ SampleFacetPosition facetPos = getSampleFacetPos();
886
+
887
+ if (!isFacetedSamples(facetPos)) {
888
+ return pos;
889
+ } else if (isInTransit()) {
890
+ vec2 interpolated = mix(
891
+ vec2(facetPos.pos, facetPos.height),
892
+ vec2(facetPos.targetPos, facetPos.targetHeight),
893
+ getTransitionFraction(pos.x));
894
+ return vec2(pos.x, interpolated[0] + pos.y * interpolated[1]);
895
+ } else {
896
+ return vec2(pos.x, facetPos.pos + pos.y * facetPos.height);
897
+ }
898
+ }
899
+
900
+ float getSampleFacetHeight(vec2 pos) {
901
+ SampleFacetPosition facetPos = getSampleFacetPos();
902
+
903
+ if (!isFacetedSamples(facetPos)) {
904
+ return 1.0;
905
+ } else if (isInTransit()) {
906
+ return mix(
907
+ facetPos.height,
908
+ facetPos.targetHeight,
909
+ getTransitionFraction(pos.x));
910
+ } else {
911
+ return facetPos.height;
912
+ }
913
+ }
914
+
915
+
916
+ /*
917
+ * Based on concepts presented at:
918
+ * https://webglfundamentals.org/webgl/lessons/webgl-picking.html
919
+ * https://deck.gl/docs/developer-guide/custom-layers/picking
920
+ */
921
+
922
+ out highp vec4 vPickingColor;
923
+
924
+ /**
925
+ * Passes the unique id to the fragment shader as a color if picking is enabled.
926
+ * Returns true if picking is enabled.
927
+ */
928
+ bool setupPicking() {
929
+ if (uPickingEnabled) {
930
+ #ifdef uniqueId_DEFINED
931
+ uint id = attr_uniqueId;
932
+ vPickingColor = vec4(
933
+ ivec4(id >> 0, id >> 8, id >> 16, id >> 24) & 0xFF
934
+ ) / float(0xFF);
935
+ #else
936
+ vPickingColor = vec4(1.0);
937
+ #endif
938
+ return true;
939
+ }
940
+ return false;
941
+ }
942
+
943
+
944
+ flat out float vRadius;
945
+ flat out float vRadiusWithPadding;
946
+ flat out lowp vec4 vFillColor;
947
+ flat out lowp vec4 vStrokeColor;
948
+ flat out lowp float vShape;
949
+ flat out lowp float vHalfStrokeWidth;
950
+ flat out mat2 vRotationMatrix;
951
+
952
+ // Copypaste from fragment shader
953
+ const float CIRCLE = 0.0;
954
+ const float SQUARE = 1.0;
955
+ const float CROSS = 2.0;
956
+ const float DIAMOND = 3.0;
957
+ const float TRIANGLE_UP = 4.0;
958
+ const float TRIANGLE_RIGHT = 5.0;
959
+ const float TRIANGLE_DOWN = 6.0;
960
+ const float TRIANGLE_LEFT = 7.0;
961
+ const float TICK_UP = 8.0;
962
+ const float TICK_RIGHT = 9.0;
963
+ const float TICK_DOWN = 10.0;
964
+ const float TICK_LEFT = 11.0;
965
+
966
+ float computeSemanticThresholdFactor() {
967
+ // TODO: add smooth transition
968
+ return getScaled_semanticScore() >= uSemanticThreshold ? 1.0 : 0.0;
969
+ }
970
+
971
+ // TODO: Move this into common.glsl or something
972
+ vec2 getDxDy() {
973
+ #if defined(dx_DEFINED) || defined(dy_DEFINED)
974
+ return vec2(getScaled_dx(), getScaled_dy()) / uViewportSize;
975
+ #else
976
+ return vec2(0.0, 0.0);
977
+ #endif
978
+ }
979
+
980
+ void main(void) {
981
+ float shapeAngle = 0.0;
982
+
983
+ // Selected points should always be visible
984
+ float semanticThresholdFactor = isPointSelected()
985
+ ? 1.0
986
+ : computeSemanticThresholdFactor();
987
+
988
+ if (semanticThresholdFactor <= 0.0) {
989
+ gl_PointSize = 0.0;
990
+ // Place the vertex outside the viewport. The default (0, 0) makes this super-slow
991
+ // on Apple Silicon. Probably related to the tile-based GPU architecture.
992
+ gl_Position = vec4(100.0, 0.0, 0.0, 0.0);
993
+ // Exit early. MAY prevent some unnecessary calculations.
994
+ return;
995
+ }
996
+
997
+ float size = getScaled_size();
998
+ vec2 pos = vec2(getScaled_x(), getScaled_y()) + getDxDy();
999
+
1000
+ gl_Position = unitToNdc(applySampleFacet(pos));
1001
+
1002
+ float strokeWidth = getScaled_strokeWidth();
1003
+
1004
+ float diameter = sqrt(size) *
1005
+ uScaleFactor *
1006
+ semanticThresholdFactor;
1007
+
1008
+ // Clamp minimum size and adjust opacity instead. Yields more pleasing result,
1009
+ // no flickering etc.
1010
+ float opacity = uViewOpacity;
1011
+ if (strokeWidth <= 0.0 || uInwardStroke) {
1012
+ float minDiameter = 1.0 / uDevicePixelRatio;
1013
+ if (diameter < minDiameter) {
1014
+ // We do some "cheap" gamma correction here. It breaks on dark background, though.
1015
+ // First we take a square of the size and then apply "gamma" of 1.5.
1016
+ opacity *= pow(diameter / minDiameter, 2.5);
1017
+ diameter = minDiameter;
1018
+ }
1019
+ }
1020
+
1021
+ float fillOpa = getScaled_fillOpacity() * opacity;
1022
+ float strokeOpa = getScaled_strokeOpacity() * opacity;
1023
+
1024
+ vShape = getScaled_shape();
1025
+
1026
+ // Circle doesn't have sharp corners. Do some special optimizations to minimize the point size.
1027
+ bool circle = vShape == 0.0;
1028
+
1029
+ if (vShape > TICK_UP && vShape <= TICK_LEFT) {
1030
+ shapeAngle = (vShape - TICK_UP) * 90.0;
1031
+ vShape = TICK_UP;
1032
+ } else if (vShape > TRIANGLE_UP && vShape <= TRIANGLE_LEFT) {
1033
+ shapeAngle = (vShape - TRIANGLE_UP) * 90.0;
1034
+ vShape = TRIANGLE_UP;
1035
+ }
1036
+
1037
+ float angleInDegrees = getScaled_angle();
1038
+ float angle = -(shapeAngle + angleInDegrees) * PI / 180.0;
1039
+ float sinTheta = sin(angle);
1040
+ float cosTheta = cos(angle);
1041
+ vRotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);
1042
+
1043
+ // Not needed if we would draw rotated quads instead of gl.POINTS
1044
+ float roomForRotation = circle ? 1.0 : sin(mod(angle, PI / 2.0) + PI / 4.0) / sin(PI / 4.0);
1045
+
1046
+ float aaPadding = 1.0 / uDevicePixelRatio;
1047
+ float rotationPadding = (diameter * roomForRotation) - diameter;
1048
+ // sqrt(3.0) ensures that the angles of equilateral triangles have enough room
1049
+ float strokePadding = uInwardStroke ? 0.0 : strokeWidth * (circle ? 1.0 : sqrt(3.0));
1050
+ float padding = rotationPadding + strokePadding + aaPadding;
1051
+
1052
+ gl_PointSize = max(
1053
+ (diameter + padding),
1054
+ uPickingEnabled ? uMinPickingSize : 0.0
1055
+ ) * uDevicePixelRatio;
1056
+
1057
+ vRadius = diameter / 2.0;
1058
+ vRadiusWithPadding = vRadius + padding / 2.0;
1059
+
1060
+ vHalfStrokeWidth = strokeWidth / 2.0;
1061
+
1062
+ vFillColor = vec4(getScaled_fill() * fillOpa, fillOpa);
1063
+ vStrokeColor = vec4(getScaled_stroke() * strokeOpa, strokeOpa);
1064
+
1065
+ setupPicking();
1066
+ }
1067
+ ",
1068
+ }
1069
+ `;
1070
+
1071
+ exports[`generated shader snapshots > penguins scatter plot example 1`] = `
1072
+ {
1073
+ "fragment": "precision highp float;
1074
+ precision highp int;
1075
+
1076
+ // view: viewRoot/scatterPlot
1077
+
1078
+ layout(std140) uniform Mark {
1079
+ /**
1080
+ * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.
1081
+ * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with
1082
+ * geometric zoom, etc.
1083
+ */
1084
+ uniform bool uInwardStroke;
1085
+
1086
+ /** The minimum point size in pixels when rendering into the picking buffer */
1087
+ uniform float uMinPickingSize;
1088
+
1089
+ /** Scale factor for geometric zoom */
1090
+ uniform mediump float uScaleFactor;
1091
+
1092
+ uniform mediump float uZoomLevel;
1093
+ uniform highp float uSemanticThreshold;
1094
+
1095
+ uniform mediump float uGradientStrength;
1096
+
1097
+ // Selection parameter
1098
+ uniform highp float[2] uParam_brush_x;
1099
+ // Selection parameter
1100
+ uniform highp float[2] uParam_brush_y;
1101
+ mediump float uDomain_x[2];
1102
+
1103
+ mediump float uDomain_y[2];
1104
+
1105
+
1106
+
1107
+
1108
+
1109
+ };
1110
+
1111
+
1112
+ #define PI 3.141593
1113
+
1114
+ uniform View {
1115
+ /** Offset in "unit" units */
1116
+ mediump vec2 uViewOffset;
1117
+ mediump vec2 uViewScale;
1118
+ /** Size of the logical viewport in pixels, i.e., the view */
1119
+ mediump vec2 uViewportSize;
1120
+ lowp float uDevicePixelRatio;
1121
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
1122
+ // that is rendered with the specified opacity.
1123
+ lowp float uViewOpacity;
1124
+ bool uPickingEnabled;
1125
+ };
1126
+
1127
+
1128
+ /**
1129
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
1130
+ * (0, 0) is at the bottom left corner.
1131
+ */
1132
+ vec4 unitToNdc(vec2 coord) {
1133
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
1134
+ }
1135
+
1136
+ vec4 unitToNdc(float x, float y) {
1137
+ return unitToNdc(vec2(x, y));
1138
+ }
1139
+
1140
+ vec4 pixelsToNdc(vec2 coord) {
1141
+ return unitToNdc(coord / uViewportSize);
1142
+ }
1143
+
1144
+ vec4 pixelsToNdc(float x, float y) {
1145
+ return pixelsToNdc(vec2(x, y));
1146
+ }
1147
+
1148
+ float linearstep(float edge0, float edge1, float x) {
1149
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
1150
+ }
1151
+
1152
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
1153
+
1154
+ highp uint hash32(highp uint key) {
1155
+ highp uint v = key;
1156
+ v ^= v >> 16u;
1157
+ v *= 0x7feb352du;
1158
+ v ^= v >> 15u;
1159
+ v *= 0x846ca68bu;
1160
+ v ^= v >> 16u;
1161
+ return v;
1162
+ }
1163
+
1164
+ bool isEmptyHashTexture(highp usampler2D s) {
1165
+ // Empty selections are encoded as a single empty hash slot.
1166
+ ivec2 texSize = textureSize(s, 0);
1167
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
1168
+ }
1169
+
1170
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
1171
+ ivec2 texSize = textureSize(s, 0);
1172
+ highp uint width = uint(texSize.x);
1173
+ highp uint size = width * uint(texSize.y);
1174
+ highp uint mask = size - 1u;
1175
+ highp uint index = hash32(value) & mask;
1176
+
1177
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
1178
+ ivec2 coord = ivec2(int(index % width), int(index / width));
1179
+ highp uint entry = texelFetch(s, coord, 0).r;
1180
+ if (entry == value) {
1181
+ return true;
1182
+ }
1183
+ if (entry == HASH_EMPTY_KEY) {
1184
+ return false;
1185
+ }
1186
+ index = (index + 1u) & mask;
1187
+ }
1188
+
1189
+ return false;
1190
+ }
1191
+
1192
+ /**
1193
+ * Calculates a gamma for antialiasing opacity based on the color.
1194
+ */
1195
+ float getGammaForColor(vec3 rgb) {
1196
+ return mix(
1197
+ 1.25,
1198
+ 0.75,
1199
+ // RGB should be linearized but this is good enough for now
1200
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
1201
+ }
1202
+
1203
+ // Fragment shader stuff ////////////////////////////////////////////////////////
1204
+
1205
+ // TODO: include the following only in fragment shaders
1206
+
1207
+ /**
1208
+ * Specialized linearstep for doing antialiasing
1209
+ */
1210
+ float distanceToRatio(float d) {
1211
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
1212
+ }
1213
+
1214
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
1215
+ if (halfStrokeWidth > 0.0) {
1216
+ // Distance to stroke's edge. Negative inside the stroke.
1217
+ float sd = abs(d) - halfStrokeWidth;
1218
+ return mix(
1219
+ stroke,
1220
+ d <= 0.0 ? fill : background,
1221
+ distanceToRatio(sd));
1222
+ } else {
1223
+ return mix(background, fill, distanceToRatio(-d));
1224
+ }
1225
+ }
1226
+
1227
+
1228
+ in highp vec4 vPickingColor;
1229
+
1230
+
1231
+ const lowp vec4 white = vec4(1.0);
1232
+ const lowp vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
1233
+
1234
+ flat in float vRadius;
1235
+ flat in float vRadiusWithPadding;
1236
+
1237
+ flat in lowp vec4 vFillColor;
1238
+ flat in lowp vec4 vStrokeColor;
1239
+ flat in lowp float vShape;
1240
+ flat in lowp float vHalfStrokeWidth;
1241
+
1242
+ flat in mat2 vRotationMatrix;
1243
+
1244
+ out lowp vec4 fragColor;
1245
+
1246
+ // Copypaste from vertex shader
1247
+ const float CIRCLE = 0.0;
1248
+ const float SQUARE = 1.0;
1249
+ const float CROSS = 2.0;
1250
+ const float DIAMOND = 3.0;
1251
+ const float TRIANGLE_UP = 4.0;
1252
+ const float TICK_UP = 8.0;
1253
+
1254
+ // The distance functions are inspired by:
1255
+ // http://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
1256
+ // However, these are not true distance functions, because the corners need to be sharp.
1257
+
1258
+ float circle(vec2 p, float r) {
1259
+ return length(p) - r;
1260
+ }
1261
+
1262
+ float square(vec2 p, float r) {
1263
+ p = abs(p);
1264
+ return max(p.x, p.y) - r;
1265
+ }
1266
+
1267
+ float tickUp(vec2 p, float r) {
1268
+ float halfR = r * 0.5;
1269
+ p.y += halfR;
1270
+ p = abs(p);
1271
+ return max(p.x - r * 0.15, p.y - halfR);
1272
+ }
1273
+
1274
+ float equilateralTriangle(vec2 p, float r) {
1275
+ p.y = -p.y;
1276
+ float k = sqrt(3.0);
1277
+ float kr = k * r;
1278
+ p.y -= kr / 2.0;
1279
+ return max((abs(p.x) * k + p.y) / 2.0, -p.y - kr);
1280
+ }
1281
+
1282
+ float crossShape(vec2 p, float r) {
1283
+ p = abs(p);
1284
+
1285
+ vec2 b = vec2(0.4, 1.0) * r;
1286
+ vec2 v = abs(p) - b.xy;
1287
+ vec2 h = abs(p) - b.yx;
1288
+ return min(max(v.x, v.y), max(h.x, h.y));
1289
+ }
1290
+
1291
+ float diamond(vec2 p, float r) {
1292
+ p = abs(p);
1293
+ return (max(abs(p.x - p.y), abs(p.x + p.y)) - r) / sqrt(2.0);
1294
+ }
1295
+
1296
+ void main() {
1297
+ float d;
1298
+
1299
+ /** Normalized point coord */
1300
+ vec2 p = vRotationMatrix * (2.0 * gl_PointCoord - 1.0) * vRadiusWithPadding;
1301
+ float r = vRadius;
1302
+
1303
+ // We could also use textures here. Could even be faster, because we have plenty of branching here.
1304
+ if (vShape == CIRCLE) {
1305
+ d = circle(p, r);
1306
+
1307
+ } else if (vShape == SQUARE) {
1308
+ d = square(p, r);
1309
+
1310
+ } else if (vShape == CROSS) {
1311
+ d = crossShape(p, r);
1312
+
1313
+ } else if (vShape == DIAMOND) {
1314
+ d = diamond(p, r);
1315
+
1316
+ } else if (vShape == TRIANGLE_UP) {
1317
+ d = equilateralTriangle(p, r);
1318
+
1319
+ } else if (vShape == TICK_UP) {
1320
+ d = tickUp(p, r);
1321
+
1322
+ } else {
1323
+ d = 0.0;
1324
+ }
1325
+
1326
+ if (!uPickingEnabled) {
1327
+ lowp vec4 fillColor = mix(vFillColor, white, -d * uGradientStrength / vRadius);
1328
+
1329
+ fragColor = distanceToColor(
1330
+ d + (uInwardStroke ? vHalfStrokeWidth : 0.0),
1331
+ fillColor,
1332
+ vStrokeColor,
1333
+ vec4(0.0),
1334
+ vHalfStrokeWidth);
1335
+
1336
+ } else if (d - vHalfStrokeWidth <= 0.0) {
1337
+ fragColor = vPickingColor;
1338
+
1339
+ } else {
1340
+ discard;
1341
+ }
1342
+ }
1343
+
1344
+ ",
1345
+ "vertex": "precision highp float;
1346
+ precision highp int;
1347
+
1348
+ // view: viewRoot/scatterPlot
1349
+
1350
+ layout(std140) uniform Mark {
1351
+ /**
1352
+ * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.
1353
+ * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with
1354
+ * geometric zoom, etc.
1355
+ */
1356
+ uniform bool uInwardStroke;
1357
+
1358
+ /** The minimum point size in pixels when rendering into the picking buffer */
1359
+ uniform float uMinPickingSize;
1360
+
1361
+ /** Scale factor for geometric zoom */
1362
+ uniform mediump float uScaleFactor;
1363
+
1364
+ uniform mediump float uZoomLevel;
1365
+ uniform highp float uSemanticThreshold;
1366
+
1367
+ uniform mediump float uGradientStrength;
1368
+
1369
+ // Selection parameter
1370
+ uniform highp float[2] uParam_brush_x;
1371
+ // Selection parameter
1372
+ uniform highp float[2] uParam_brush_y;
1373
+ mediump float uDomain_x[2];
1374
+
1375
+ mediump float uDomain_y[2];
1376
+
1377
+
1378
+
1379
+
1380
+
1381
+ };
1382
+
1383
+
1384
+ #define PI 3.141593
1385
+
1386
+ uniform View {
1387
+ /** Offset in "unit" units */
1388
+ mediump vec2 uViewOffset;
1389
+ mediump vec2 uViewScale;
1390
+ /** Size of the logical viewport in pixels, i.e., the view */
1391
+ mediump vec2 uViewportSize;
1392
+ lowp float uDevicePixelRatio;
1393
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
1394
+ // that is rendered with the specified opacity.
1395
+ lowp float uViewOpacity;
1396
+ bool uPickingEnabled;
1397
+ };
1398
+
1399
+
1400
+ /**
1401
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
1402
+ * (0, 0) is at the bottom left corner.
1403
+ */
1404
+ vec4 unitToNdc(vec2 coord) {
1405
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
1406
+ }
1407
+
1408
+ vec4 unitToNdc(float x, float y) {
1409
+ return unitToNdc(vec2(x, y));
1410
+ }
1411
+
1412
+ vec4 pixelsToNdc(vec2 coord) {
1413
+ return unitToNdc(coord / uViewportSize);
1414
+ }
1415
+
1416
+ vec4 pixelsToNdc(float x, float y) {
1417
+ return pixelsToNdc(vec2(x, y));
1418
+ }
1419
+
1420
+ float linearstep(float edge0, float edge1, float x) {
1421
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
1422
+ }
1423
+
1424
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
1425
+
1426
+ highp uint hash32(highp uint key) {
1427
+ highp uint v = key;
1428
+ v ^= v >> 16u;
1429
+ v *= 0x7feb352du;
1430
+ v ^= v >> 15u;
1431
+ v *= 0x846ca68bu;
1432
+ v ^= v >> 16u;
1433
+ return v;
1434
+ }
1435
+
1436
+ bool isEmptyHashTexture(highp usampler2D s) {
1437
+ // Empty selections are encoded as a single empty hash slot.
1438
+ ivec2 texSize = textureSize(s, 0);
1439
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
1440
+ }
1441
+
1442
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
1443
+ ivec2 texSize = textureSize(s, 0);
1444
+ highp uint width = uint(texSize.x);
1445
+ highp uint size = width * uint(texSize.y);
1446
+ highp uint mask = size - 1u;
1447
+ highp uint index = hash32(value) & mask;
1448
+
1449
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
1450
+ ivec2 coord = ivec2(int(index % width), int(index / width));
1451
+ highp uint entry = texelFetch(s, coord, 0).r;
1452
+ if (entry == value) {
1453
+ return true;
1454
+ }
1455
+ if (entry == HASH_EMPTY_KEY) {
1456
+ return false;
1457
+ }
1458
+ index = (index + 1u) & mask;
1459
+ }
1460
+
1461
+ return false;
1462
+ }
1463
+
1464
+ /**
1465
+ * Calculates a gamma for antialiasing opacity based on the color.
1466
+ */
1467
+ float getGammaForColor(vec3 rgb) {
1468
+ return mix(
1469
+ 1.25,
1470
+ 0.75,
1471
+ // RGB should be linearized but this is good enough for now
1472
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
1473
+ }
1474
+
1475
+ // Fragment shader stuff ////////////////////////////////////////////////////////
1476
+
1477
+ // TODO: include the following only in fragment shaders
1478
+
1479
+ /**
1480
+ * Specialized linearstep for doing antialiasing
1481
+ */
1482
+ float distanceToRatio(float d) {
1483
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
1484
+ }
1485
+
1486
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
1487
+ if (halfStrokeWidth > 0.0) {
1488
+ // Distance to stroke's edge. Negative inside the stroke.
1489
+ float sd = abs(d) - halfStrokeWidth;
1490
+ return mix(
1491
+ stroke,
1492
+ d <= 0.0 ? fill : background,
1493
+ distanceToRatio(sd));
1494
+ } else {
1495
+ return mix(background, fill, distanceToRatio(-d));
1496
+ }
1497
+ }
1498
+
1499
+
1500
+ uniform highp float uZero;
1501
+
1502
+ // Utils ------------
1503
+
1504
+ vec3 getDiscreteColor(sampler2D s, int index) {
1505
+ return texelFetch(s, ivec2(index % textureSize(s, 0).x, 0), 0).rgb;
1506
+ }
1507
+
1508
+ vec3 getInterpolatedColor(sampler2D s, float unitValue) {
1509
+ return texture(s, vec2(unitValue, 0.0)).rgb;
1510
+ }
1511
+
1512
+ float clampToRange(float value, vec2 range) {
1513
+ return clamp(value, min(range[0], range[1]), max(range[0], range[1]));
1514
+ }
1515
+
1516
+ // Scales ------------
1517
+ // Based on d3 scales: https://github.com/d3/d3-scale
1518
+
1519
+ float scaleIdentity(float value) {
1520
+ return value;
1521
+ }
1522
+
1523
+ float scaleIdentity(uint value) {
1524
+ return float(value);
1525
+ }
1526
+
1527
+ float scaleLinear(float value, vec2 domain, vec2 range) {
1528
+ float domainSpan = domain[1] - domain[0];
1529
+ float rangeSpan = range[1] - range[0];
1530
+ return (value - domain[0]) / domainSpan * rangeSpan + range[0];
1531
+ }
1532
+
1533
+ float scaleLog(float value, vec2 domain, vec2 range, float base) {
1534
+ // y = m log(x) + b
1535
+ // TODO: Perf optimization: precalculate log domain in js.
1536
+ // TODO: Reversed domain, etc
1537
+ return scaleLinear(log(value) / log(base), log(domain) / log(base), range);
1538
+ }
1539
+
1540
+ float symlog(float value, float constant) {
1541
+ // WARNING: emulating log1p with log(x + 1). Small numbers are likely to
1542
+ // have significant precision problems.
1543
+ return sign(value) * log(abs(value / constant) + 1.0);
1544
+ }
1545
+
1546
+ float scaleSymlog(float value, vec2 domain, vec2 range, float constant) {
1547
+ return scaleLinear(
1548
+ symlog(value, constant),
1549
+ vec2(symlog(domain[0], constant), symlog(domain[1], constant)),
1550
+ range
1551
+ );
1552
+ }
1553
+
1554
+ float scalePow(float value, vec2 domain, vec2 range, float exponent) {
1555
+ // y = mx^k + b
1556
+ // TODO: Perf optimization: precalculate pow domain in js.
1557
+ // TODO: Reversed domain, etc
1558
+ return scaleLinear(
1559
+ pow(abs(value), exponent) * sign(value),
1560
+ pow(abs(domain), vec2(exponent)) * sign(domain),
1561
+ range
1562
+ );
1563
+ }
1564
+
1565
+ // TODO: scaleThreshold
1566
+ // TODO: scaleQuantile (special case of threshold scale)
1567
+
1568
+ // TODO: domainExtent should be uint
1569
+ float scaleBand(uint value, vec2 domainExtent, vec2 range,
1570
+ float paddingInner, float paddingOuter,
1571
+ float align, float band) {
1572
+
1573
+ // TODO: reverse
1574
+ float start = range[0];
1575
+ float stop = range[1];
1576
+ float rangeSpan = stop - start;
1577
+
1578
+ float n = domainExtent[1] - domainExtent[0];
1579
+
1580
+ // This fix departs from Vega and d3: https://github.com/vega/vega/issues/3357#issuecomment-1063253596
1581
+ paddingInner = int(n) > 1 ? paddingInner : 0.0;
1582
+
1583
+ // Adapted from: https://github.com/d3/d3-scale/blob/master/src/band.js
1584
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
1585
+ start += (rangeSpan - step * (n - paddingInner)) * align;
1586
+ float bandwidth = step * (1.0 - paddingInner);
1587
+
1588
+ return start + (float(value) - domainExtent[0]) * step + bandwidth * band;
1589
+ }
1590
+
1591
+ const int lowBits = 12;
1592
+ const float lowDivisor = pow(2.0, float(lowBits));
1593
+ const uint lowMask = uint(lowDivisor - 1.0);
1594
+
1595
+ vec2 splitUint(uint value) {
1596
+ uint valueLo = value & lowMask;
1597
+ uint valueHi = value - valueLo;
1598
+ return vec2(float(valueHi), float(valueLo));
1599
+ }
1600
+
1601
+ /**
1602
+ * High precision variant of scaleBand for index/locus scales
1603
+ */
1604
+ float scaleBandHp(uint value, vec3 domainExtent, vec2 range,
1605
+ float paddingInner, float paddingOuter,
1606
+ float align, float band) {
1607
+
1608
+ // TODO: reverse
1609
+ float start = range[0];
1610
+ float stop = range[1];
1611
+ float rangeSpan = stop - start;
1612
+
1613
+ vec2 domainStart = domainExtent.xy;
1614
+ float n = domainExtent[2];
1615
+
1616
+ // The following computation is identical for every vertex. Could be done on the JS side.
1617
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
1618
+ start += (rangeSpan - step * (n - paddingInner)) * align;
1619
+ float bandwidth = step * (1.0 - paddingInner);
1620
+
1621
+ // Split into to values with each having a reduced number of significant digits
1622
+ // to mitigate the lack of precision in float32 calculations.
1623
+ vec2 splitValue = splitUint(value);
1624
+
1625
+ // Using max to prevent the shader compiler from wrecking the precision.
1626
+ // Othwewise the compiler could optimize the sum of the four terms into
1627
+ // some equivalent form that does premature rounding.
1628
+ float inf = 1.0 / uZero;
1629
+ float hi = max(splitValue[0] - domainStart[0], -inf);
1630
+ float lo = max(splitValue[1] - domainStart[1], -inf);
1631
+
1632
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
1633
+ }
1634
+
1635
+ /**
1636
+ * High precision variant of scaleBand for index/locus scales for large
1637
+ * domains where 32bit uints are not sufficient to represent the domain.
1638
+ */
1639
+ float scaleBandHp(uvec2 value, vec3 domainExtent, vec2 range,
1640
+ float paddingInner, float paddingOuter,
1641
+ float align, float band) {
1642
+
1643
+ // TODO: reverse
1644
+ float start = range[0];
1645
+ float stop = range[1];
1646
+ float rangeSpan = stop - start;
1647
+
1648
+ vec2 domainStart = domainExtent.xy;
1649
+ float n = domainExtent[2];
1650
+
1651
+ // The following computation is identical for every vertex. Could be done on the JS side.
1652
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
1653
+ start += (rangeSpan - step * (n - paddingInner)) * align;
1654
+ float bandwidth = step * (1.0 - paddingInner);
1655
+
1656
+ // Split into to values with each having a reduced number of significant digits
1657
+ // to mitigate the lack of precision in float32 calculations.
1658
+ vec2 splitValue = vec2(float(value[0]) * lowDivisor, float(value[1]));
1659
+
1660
+ // Using max to prevent the shader compiler from wrecking the precision.
1661
+ // Othwewise the compiler could optimize the sum of the four terms into
1662
+ // some equivalent form that does premature rounding.
1663
+ float inf = 1.0 / uZero;
1664
+ float hi = max(splitValue[0] - domainStart[0], -inf);
1665
+ float lo = max(splitValue[1] - domainStart[1], -inf);
1666
+
1667
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
1668
+ }
1669
+
1670
+
1671
+ in highp uint attr_uniqueId;
1672
+ in highp float attr_x;
1673
+ in highp float attr_y;
1674
+ in highp uint attr_stroke;
1675
+ in highp uint attr_fill;
1676
+
1677
+ bool checkSelection_brush(bool empty) {
1678
+ return (uParam_brush_x[0] <= attr_x && attr_x <= uParam_brush_x[1]) && (uParam_brush_y[0] <= attr_y && attr_y <= uParam_brush_y[1]) || (empty && (uParam_brush_x[0] > uParam_brush_x[1] || uParam_brush_y[0] > uParam_brush_y[1]));
1679
+ }
1680
+
1681
+
1682
+ uint accessor_uniqueId_0() {
1683
+ return attr_uniqueId;
1684
+ }
1685
+
1686
+ uint getScaled_uniqueId() {
1687
+ return accessor_uniqueId_0();
1688
+ }
1689
+
1690
+ #define uniqueId_DEFINED
1691
+
1692
+
1693
+ float accessor_x_0() {
1694
+ return attr_x;
1695
+ }
1696
+
1697
+
1698
+ //////////////////////////////////////////////////////////////////////
1699
+ // Channel: x
1700
+
1701
+ const vec2 range_x = vec2(0.0, 1.0);
1702
+
1703
+ float scale_x(float value) {
1704
+ int slot = 0;
1705
+ vec2 domain = vec2(uDomain_x[slot], uDomain_x[slot + 1]);
1706
+ float transformed = scaleLinear(value, domain, range_x);
1707
+ return transformed;
1708
+
1709
+ }
1710
+
1711
+ float getScaled_x() {
1712
+ return scale_x(accessor_x_0());
1713
+ }
1714
+
1715
+ #define x_DEFINED
1716
+
1717
+
1718
+ float accessor_y_0() {
1719
+ return attr_y;
1720
+ }
1721
+
1722
+
1723
+ //////////////////////////////////////////////////////////////////////
1724
+ // Channel: y
1725
+
1726
+ const vec2 range_y = vec2(0.0, 1.0);
1727
+
1728
+ float scale_y(float value) {
1729
+ int slot = 0;
1730
+ vec2 domain = vec2(uDomain_y[slot], uDomain_y[slot + 1]);
1731
+ float transformed = scaleLinear(value, domain, range_y);
1732
+ return transformed;
1733
+
1734
+ }
1735
+
1736
+ float getScaled_y() {
1737
+ return scale_y(accessor_y_0());
1738
+ }
1739
+
1740
+ #define y_DEFINED
1741
+
1742
+
1743
+ float accessor_size_0() {
1744
+ // Constant value
1745
+ return float(40.0);
1746
+ }
1747
+
1748
+ float getScaled_size() {
1749
+ return accessor_size_0();
1750
+ }
1751
+
1752
+ #define size_DEFINED
1753
+
1754
+
1755
+ float accessor_semanticScore_0() {
1756
+ // Constant value
1757
+ return float(0.0);
1758
+ }
1759
+
1760
+ float getScaled_semanticScore() {
1761
+ return accessor_semanticScore_0();
1762
+ }
1763
+
1764
+ #define semanticScore_DEFINED
1765
+
1766
+
1767
+ float accessor_shape_0() {
1768
+ // Constant value
1769
+ return float(0.0);
1770
+ }
1771
+
1772
+ float getScaled_shape() {
1773
+ return accessor_shape_0();
1774
+ }
1775
+
1776
+ #define shape_DEFINED
1777
+
1778
+
1779
+ float accessor_strokeWidth_0() {
1780
+ // Constant value
1781
+ return float(2.0);
1782
+ }
1783
+
1784
+ float getScaled_strokeWidth() {
1785
+ return accessor_strokeWidth_0();
1786
+ }
1787
+
1788
+ #define strokeWidth_DEFINED
1789
+
1790
+
1791
+ float accessor_dx_0() {
1792
+ // Constant value
1793
+ return float(0.0);
1794
+ }
1795
+
1796
+ float getScaled_dx() {
1797
+ return accessor_dx_0();
1798
+ }
1799
+
1800
+ #define dx_DEFINED
1801
+
1802
+
1803
+ float accessor_dy_0() {
1804
+ // Constant value
1805
+ return float(0.0);
1806
+ }
1807
+
1808
+ float getScaled_dy() {
1809
+ return accessor_dy_0();
1810
+ }
1811
+
1812
+ #define dy_DEFINED
1813
+
1814
+
1815
+ float accessor_angle_0() {
1816
+ // Constant value
1817
+ return float(0.0);
1818
+ }
1819
+
1820
+ float getScaled_angle() {
1821
+ return accessor_angle_0();
1822
+ }
1823
+
1824
+ #define angle_DEFINED
1825
+
1826
+
1827
+ uint accessor_stroke_0() {
1828
+ return attr_stroke;
1829
+ }
1830
+
1831
+
1832
+ vec3 accessor_stroke_1() {
1833
+ // Constant value
1834
+ return vec3(0.8274509803921568, 0.8274509803921568, 0.8274509803921568);
1835
+ }
1836
+
1837
+
1838
+ //////////////////////////////////////////////////////////////////////
1839
+ // Channel: stroke
1840
+
1841
+ uniform sampler2D uRangeTexture_stroke;
1842
+
1843
+ vec3 scale_stroke(uint value) {
1844
+ int slot = 0;
1845
+ float transformed = scaleIdentity(value);
1846
+ return getDiscreteColor(uRangeTexture_stroke, int(transformed));
1847
+
1848
+ }
1849
+
1850
+ vec3 getScaled_stroke() {
1851
+ if (checkSelection_brush(true)) {
1852
+ return scale_stroke(accessor_stroke_0());
1853
+ }
1854
+ else {
1855
+ return accessor_stroke_1();
1856
+ }
1857
+ }
1858
+
1859
+ #define stroke_DEFINED
1860
+
1861
+
1862
+ float accessor_strokeOpacity_0() {
1863
+ // Constant value
1864
+ return float(0.7);
1865
+ }
1866
+
1867
+ float getScaled_strokeOpacity() {
1868
+ return accessor_strokeOpacity_0();
1869
+ }
1870
+
1871
+ #define strokeOpacity_DEFINED
1872
+
1873
+
1874
+ uint accessor_fill_0() {
1875
+ return attr_fill;
1876
+ }
1877
+
1878
+
1879
+ vec3 accessor_fill_1() {
1880
+ // Constant value
1881
+ return vec3(0.8274509803921568, 0.8274509803921568, 0.8274509803921568);
1882
+ }
1883
+
1884
+
1885
+ //////////////////////////////////////////////////////////////////////
1886
+ // Channel: fill
1887
+
1888
+ uniform sampler2D uRangeTexture_fill;
1889
+
1890
+ vec3 scale_fill(uint value) {
1891
+ int slot = 0;
1892
+ float transformed = scaleIdentity(value);
1893
+ return getDiscreteColor(uRangeTexture_fill, int(transformed));
1894
+
1895
+ }
1896
+
1897
+ vec3 getScaled_fill() {
1898
+ if (checkSelection_brush(true)) {
1899
+ return scale_fill(accessor_fill_0());
1900
+ }
1901
+ else {
1902
+ return accessor_fill_1();
1903
+ }
1904
+ }
1905
+
1906
+ #define fill_DEFINED
1907
+
1908
+
1909
+ float accessor_fillOpacity_0() {
1910
+ // Constant value
1911
+ return float(0.0);
1912
+ }
1913
+
1914
+ float getScaled_fillOpacity() {
1915
+ return accessor_fillOpacity_0();
1916
+ }
1917
+
1918
+ #define fillOpacity_DEFINED
1919
+
1920
+ bool isPointSelected() {
1921
+ return checkSelection_brush(false);
1922
+ }
1923
+
1924
+
1925
+ /**
1926
+ * Describes where a sample facet should be shown. Interpolating between the
1927
+ * current and target positions/heights allows for transitioning between facet
1928
+ * configurations.
1929
+ */
1930
+ struct SampleFacetPosition {
1931
+ float pos;
1932
+ float height;
1933
+ float targetPos;
1934
+ float targetHeight;
1935
+ };
1936
+
1937
+ /**
1938
+ * Trasition fraction [0, 1] between the current and target configurations.
1939
+ */
1940
+ uniform float uTransitionOffset;
1941
+
1942
+
1943
+ // ----------------------------------------------------------------------------
1944
+
1945
+ #if !defined(SAMPLE_FACET_UNIFORM) && !defined(SAMPLE_FACET_TEXTURE)
1946
+
1947
+ SampleFacetPosition getSampleFacetPos() {
1948
+ return SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
1949
+ }
1950
+
1951
+ #elif defined(SAMPLE_FACET_UNIFORM)
1952
+
1953
+ /**
1954
+ * Location and height of the band on the Y axis on a normalized [0, 1] scale.
1955
+ * Elements: curr pos, curr height, target pos, target height
1956
+ */
1957
+ uniform vec4 uSampleFacet;
1958
+
1959
+ SampleFacetPosition getSampleFacetPos() {
1960
+ return SampleFacetPosition(
1961
+ 1.0 - uSampleFacet.x - uSampleFacet.y,
1962
+ uSampleFacet.y,
1963
+ 1.0 - uSampleFacet.z - uSampleFacet.w,
1964
+ uSampleFacet.w
1965
+ );
1966
+ }
1967
+
1968
+ #elif defined(SAMPLE_FACET_TEXTURE)
1969
+
1970
+ uniform sampler2D uSampleFacetTexture;
1971
+
1972
+ SampleFacetPosition getSampleFacetPos() {
1973
+ vec4 texel = texelFetch(uSampleFacetTexture, ivec2(int(attr_facetIndex), 0), 0);
1974
+ return SampleFacetPosition(
1975
+ 1.0 - texel.r - texel.g,
1976
+ texel.g,
1977
+ 1.0 - texel.r - texel.g,
1978
+ texel.g);
1979
+ }
1980
+
1981
+ #endif
1982
+
1983
+ // ----------------------------------------------------------------------------
1984
+
1985
+ bool isFacetedSamples(SampleFacetPosition facetPos) {
1986
+ return facetPos != SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
1987
+ }
1988
+
1989
+ bool isFacetedSamples() {
1990
+ return isFacetedSamples(getSampleFacetPos());
1991
+ }
1992
+
1993
+ bool isInTransit() {
1994
+ return uTransitionOffset > 0.0;
1995
+ }
1996
+
1997
+ float getTransitionFraction(float xPos) {
1998
+ return smoothstep(0.0, 0.7 + uTransitionOffset, (xPos - uTransitionOffset) * 2.0);
1999
+ }
2000
+
2001
+ vec2 applySampleFacet(vec2 pos) {
2002
+ SampleFacetPosition facetPos = getSampleFacetPos();
2003
+
2004
+ if (!isFacetedSamples(facetPos)) {
2005
+ return pos;
2006
+ } else if (isInTransit()) {
2007
+ vec2 interpolated = mix(
2008
+ vec2(facetPos.pos, facetPos.height),
2009
+ vec2(facetPos.targetPos, facetPos.targetHeight),
2010
+ getTransitionFraction(pos.x));
2011
+ return vec2(pos.x, interpolated[0] + pos.y * interpolated[1]);
2012
+ } else {
2013
+ return vec2(pos.x, facetPos.pos + pos.y * facetPos.height);
2014
+ }
2015
+ }
2016
+
2017
+ float getSampleFacetHeight(vec2 pos) {
2018
+ SampleFacetPosition facetPos = getSampleFacetPos();
2019
+
2020
+ if (!isFacetedSamples(facetPos)) {
2021
+ return 1.0;
2022
+ } else if (isInTransit()) {
2023
+ return mix(
2024
+ facetPos.height,
2025
+ facetPos.targetHeight,
2026
+ getTransitionFraction(pos.x));
2027
+ } else {
2028
+ return facetPos.height;
2029
+ }
2030
+ }
2031
+
2032
+
2033
+ /*
2034
+ * Based on concepts presented at:
2035
+ * https://webglfundamentals.org/webgl/lessons/webgl-picking.html
2036
+ * https://deck.gl/docs/developer-guide/custom-layers/picking
2037
+ */
2038
+
2039
+ out highp vec4 vPickingColor;
2040
+
2041
+ /**
2042
+ * Passes the unique id to the fragment shader as a color if picking is enabled.
2043
+ * Returns true if picking is enabled.
2044
+ */
2045
+ bool setupPicking() {
2046
+ if (uPickingEnabled) {
2047
+ #ifdef uniqueId_DEFINED
2048
+ uint id = attr_uniqueId;
2049
+ vPickingColor = vec4(
2050
+ ivec4(id >> 0, id >> 8, id >> 16, id >> 24) & 0xFF
2051
+ ) / float(0xFF);
2052
+ #else
2053
+ vPickingColor = vec4(1.0);
2054
+ #endif
2055
+ return true;
2056
+ }
2057
+ return false;
2058
+ }
2059
+
2060
+
2061
+ flat out float vRadius;
2062
+ flat out float vRadiusWithPadding;
2063
+ flat out lowp vec4 vFillColor;
2064
+ flat out lowp vec4 vStrokeColor;
2065
+ flat out lowp float vShape;
2066
+ flat out lowp float vHalfStrokeWidth;
2067
+ flat out mat2 vRotationMatrix;
2068
+
2069
+ // Copypaste from fragment shader
2070
+ const float CIRCLE = 0.0;
2071
+ const float SQUARE = 1.0;
2072
+ const float CROSS = 2.0;
2073
+ const float DIAMOND = 3.0;
2074
+ const float TRIANGLE_UP = 4.0;
2075
+ const float TRIANGLE_RIGHT = 5.0;
2076
+ const float TRIANGLE_DOWN = 6.0;
2077
+ const float TRIANGLE_LEFT = 7.0;
2078
+ const float TICK_UP = 8.0;
2079
+ const float TICK_RIGHT = 9.0;
2080
+ const float TICK_DOWN = 10.0;
2081
+ const float TICK_LEFT = 11.0;
2082
+
2083
+ float computeSemanticThresholdFactor() {
2084
+ // TODO: add smooth transition
2085
+ return getScaled_semanticScore() >= uSemanticThreshold ? 1.0 : 0.0;
2086
+ }
2087
+
2088
+ // TODO: Move this into common.glsl or something
2089
+ vec2 getDxDy() {
2090
+ #if defined(dx_DEFINED) || defined(dy_DEFINED)
2091
+ return vec2(getScaled_dx(), getScaled_dy()) / uViewportSize;
2092
+ #else
2093
+ return vec2(0.0, 0.0);
2094
+ #endif
2095
+ }
2096
+
2097
+ void main(void) {
2098
+ float shapeAngle = 0.0;
2099
+
2100
+ // Selected points should always be visible
2101
+ float semanticThresholdFactor = isPointSelected()
2102
+ ? 1.0
2103
+ : computeSemanticThresholdFactor();
2104
+
2105
+ if (semanticThresholdFactor <= 0.0) {
2106
+ gl_PointSize = 0.0;
2107
+ // Place the vertex outside the viewport. The default (0, 0) makes this super-slow
2108
+ // on Apple Silicon. Probably related to the tile-based GPU architecture.
2109
+ gl_Position = vec4(100.0, 0.0, 0.0, 0.0);
2110
+ // Exit early. MAY prevent some unnecessary calculations.
2111
+ return;
2112
+ }
2113
+
2114
+ float size = getScaled_size();
2115
+ vec2 pos = vec2(getScaled_x(), getScaled_y()) + getDxDy();
2116
+
2117
+ gl_Position = unitToNdc(applySampleFacet(pos));
2118
+
2119
+ float strokeWidth = getScaled_strokeWidth();
2120
+
2121
+ float diameter = sqrt(size) *
2122
+ uScaleFactor *
2123
+ semanticThresholdFactor;
2124
+
2125
+ // Clamp minimum size and adjust opacity instead. Yields more pleasing result,
2126
+ // no flickering etc.
2127
+ float opacity = uViewOpacity;
2128
+ if (strokeWidth <= 0.0 || uInwardStroke) {
2129
+ float minDiameter = 1.0 / uDevicePixelRatio;
2130
+ if (diameter < minDiameter) {
2131
+ // We do some "cheap" gamma correction here. It breaks on dark background, though.
2132
+ // First we take a square of the size and then apply "gamma" of 1.5.
2133
+ opacity *= pow(diameter / minDiameter, 2.5);
2134
+ diameter = minDiameter;
2135
+ }
2136
+ }
2137
+
2138
+ float fillOpa = getScaled_fillOpacity() * opacity;
2139
+ float strokeOpa = getScaled_strokeOpacity() * opacity;
2140
+
2141
+ vShape = getScaled_shape();
2142
+
2143
+ // Circle doesn't have sharp corners. Do some special optimizations to minimize the point size.
2144
+ bool circle = vShape == 0.0;
2145
+
2146
+ if (vShape > TICK_UP && vShape <= TICK_LEFT) {
2147
+ shapeAngle = (vShape - TICK_UP) * 90.0;
2148
+ vShape = TICK_UP;
2149
+ } else if (vShape > TRIANGLE_UP && vShape <= TRIANGLE_LEFT) {
2150
+ shapeAngle = (vShape - TRIANGLE_UP) * 90.0;
2151
+ vShape = TRIANGLE_UP;
2152
+ }
2153
+
2154
+ float angleInDegrees = getScaled_angle();
2155
+ float angle = -(shapeAngle + angleInDegrees) * PI / 180.0;
2156
+ float sinTheta = sin(angle);
2157
+ float cosTheta = cos(angle);
2158
+ vRotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);
2159
+
2160
+ // Not needed if we would draw rotated quads instead of gl.POINTS
2161
+ float roomForRotation = circle ? 1.0 : sin(mod(angle, PI / 2.0) + PI / 4.0) / sin(PI / 4.0);
2162
+
2163
+ float aaPadding = 1.0 / uDevicePixelRatio;
2164
+ float rotationPadding = (diameter * roomForRotation) - diameter;
2165
+ // sqrt(3.0) ensures that the angles of equilateral triangles have enough room
2166
+ float strokePadding = uInwardStroke ? 0.0 : strokeWidth * (circle ? 1.0 : sqrt(3.0));
2167
+ float padding = rotationPadding + strokePadding + aaPadding;
2168
+
2169
+ gl_PointSize = max(
2170
+ (diameter + padding),
2171
+ uPickingEnabled ? uMinPickingSize : 0.0
2172
+ ) * uDevicePixelRatio;
2173
+
2174
+ vRadius = diameter / 2.0;
2175
+ vRadiusWithPadding = vRadius + padding / 2.0;
2176
+
2177
+ vHalfStrokeWidth = strokeWidth / 2.0;
2178
+
2179
+ vFillColor = vec4(getScaled_fill() * fillOpa, fillOpa);
2180
+ vStrokeColor = vec4(getScaled_stroke() * strokeOpa, strokeOpa);
2181
+
2182
+ setupPicking();
2183
+ }
2184
+ ",
2185
+ }
2186
+ `;
2187
+
2188
+ exports[`generated shader snapshots > point mark control spec 1`] = `
2189
+ {
2190
+ "fragment": "precision highp float;
2191
+ precision highp int;
2192
+
2193
+ // view: viewRoot
2194
+
2195
+ layout(std140) uniform Mark {
2196
+ /**
2197
+ * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.
2198
+ * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with
2199
+ * geometric zoom, etc.
2200
+ */
2201
+ uniform bool uInwardStroke;
2202
+
2203
+ /** The minimum point size in pixels when rendering into the picking buffer */
2204
+ uniform float uMinPickingSize;
2205
+
2206
+ /** Scale factor for geometric zoom */
2207
+ uniform mediump float uScaleFactor;
2208
+
2209
+ uniform mediump float uZoomLevel;
2210
+ uniform highp float uSemanticThreshold;
2211
+
2212
+ uniform mediump float uGradientStrength;
2213
+
2214
+ mediump float uDomain_x[2];
2215
+
2216
+ mediump float uDomain_y[2];
2217
+
2218
+
2219
+
2220
+ };
2221
+
2222
+
2223
+ #define PI 3.141593
2224
+
2225
+ uniform View {
2226
+ /** Offset in "unit" units */
2227
+ mediump vec2 uViewOffset;
2228
+ mediump vec2 uViewScale;
2229
+ /** Size of the logical viewport in pixels, i.e., the view */
2230
+ mediump vec2 uViewportSize;
2231
+ lowp float uDevicePixelRatio;
2232
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
2233
+ // that is rendered with the specified opacity.
2234
+ lowp float uViewOpacity;
2235
+ bool uPickingEnabled;
2236
+ };
2237
+
2238
+
2239
+ /**
2240
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
2241
+ * (0, 0) is at the bottom left corner.
2242
+ */
2243
+ vec4 unitToNdc(vec2 coord) {
2244
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
2245
+ }
2246
+
2247
+ vec4 unitToNdc(float x, float y) {
2248
+ return unitToNdc(vec2(x, y));
2249
+ }
2250
+
2251
+ vec4 pixelsToNdc(vec2 coord) {
2252
+ return unitToNdc(coord / uViewportSize);
2253
+ }
2254
+
2255
+ vec4 pixelsToNdc(float x, float y) {
2256
+ return pixelsToNdc(vec2(x, y));
2257
+ }
2258
+
2259
+ float linearstep(float edge0, float edge1, float x) {
2260
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
2261
+ }
2262
+
2263
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
2264
+
2265
+ highp uint hash32(highp uint key) {
2266
+ highp uint v = key;
2267
+ v ^= v >> 16u;
2268
+ v *= 0x7feb352du;
2269
+ v ^= v >> 15u;
2270
+ v *= 0x846ca68bu;
2271
+ v ^= v >> 16u;
2272
+ return v;
2273
+ }
2274
+
2275
+ bool isEmptyHashTexture(highp usampler2D s) {
2276
+ // Empty selections are encoded as a single empty hash slot.
2277
+ ivec2 texSize = textureSize(s, 0);
2278
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
2279
+ }
2280
+
2281
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
2282
+ ivec2 texSize = textureSize(s, 0);
2283
+ highp uint width = uint(texSize.x);
2284
+ highp uint size = width * uint(texSize.y);
2285
+ highp uint mask = size - 1u;
2286
+ highp uint index = hash32(value) & mask;
2287
+
2288
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
2289
+ ivec2 coord = ivec2(int(index % width), int(index / width));
2290
+ highp uint entry = texelFetch(s, coord, 0).r;
2291
+ if (entry == value) {
2292
+ return true;
2293
+ }
2294
+ if (entry == HASH_EMPTY_KEY) {
2295
+ return false;
2296
+ }
2297
+ index = (index + 1u) & mask;
2298
+ }
2299
+
2300
+ return false;
2301
+ }
2302
+
2303
+ /**
2304
+ * Calculates a gamma for antialiasing opacity based on the color.
2305
+ */
2306
+ float getGammaForColor(vec3 rgb) {
2307
+ return mix(
2308
+ 1.25,
2309
+ 0.75,
2310
+ // RGB should be linearized but this is good enough for now
2311
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
2312
+ }
2313
+
2314
+ // Fragment shader stuff ////////////////////////////////////////////////////////
2315
+
2316
+ // TODO: include the following only in fragment shaders
2317
+
2318
+ /**
2319
+ * Specialized linearstep for doing antialiasing
2320
+ */
2321
+ float distanceToRatio(float d) {
2322
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
2323
+ }
2324
+
2325
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
2326
+ if (halfStrokeWidth > 0.0) {
2327
+ // Distance to stroke's edge. Negative inside the stroke.
2328
+ float sd = abs(d) - halfStrokeWidth;
2329
+ return mix(
2330
+ stroke,
2331
+ d <= 0.0 ? fill : background,
2332
+ distanceToRatio(sd));
2333
+ } else {
2334
+ return mix(background, fill, distanceToRatio(-d));
2335
+ }
2336
+ }
2337
+
2338
+
2339
+ in highp vec4 vPickingColor;
2340
+
2341
+
2342
+ const lowp vec4 white = vec4(1.0);
2343
+ const lowp vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
2344
+
2345
+ flat in float vRadius;
2346
+ flat in float vRadiusWithPadding;
2347
+
2348
+ flat in lowp vec4 vFillColor;
2349
+ flat in lowp vec4 vStrokeColor;
2350
+ flat in lowp float vShape;
2351
+ flat in lowp float vHalfStrokeWidth;
2352
+
2353
+ flat in mat2 vRotationMatrix;
2354
+
2355
+ out lowp vec4 fragColor;
2356
+
2357
+ // Copypaste from vertex shader
2358
+ const float CIRCLE = 0.0;
2359
+ const float SQUARE = 1.0;
2360
+ const float CROSS = 2.0;
2361
+ const float DIAMOND = 3.0;
2362
+ const float TRIANGLE_UP = 4.0;
2363
+ const float TICK_UP = 8.0;
2364
+
2365
+ // The distance functions are inspired by:
2366
+ // http://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
2367
+ // However, these are not true distance functions, because the corners need to be sharp.
2368
+
2369
+ float circle(vec2 p, float r) {
2370
+ return length(p) - r;
2371
+ }
2372
+
2373
+ float square(vec2 p, float r) {
2374
+ p = abs(p);
2375
+ return max(p.x, p.y) - r;
2376
+ }
2377
+
2378
+ float tickUp(vec2 p, float r) {
2379
+ float halfR = r * 0.5;
2380
+ p.y += halfR;
2381
+ p = abs(p);
2382
+ return max(p.x - r * 0.15, p.y - halfR);
2383
+ }
2384
+
2385
+ float equilateralTriangle(vec2 p, float r) {
2386
+ p.y = -p.y;
2387
+ float k = sqrt(3.0);
2388
+ float kr = k * r;
2389
+ p.y -= kr / 2.0;
2390
+ return max((abs(p.x) * k + p.y) / 2.0, -p.y - kr);
2391
+ }
2392
+
2393
+ float crossShape(vec2 p, float r) {
2394
+ p = abs(p);
2395
+
2396
+ vec2 b = vec2(0.4, 1.0) * r;
2397
+ vec2 v = abs(p) - b.xy;
2398
+ vec2 h = abs(p) - b.yx;
2399
+ return min(max(v.x, v.y), max(h.x, h.y));
2400
+ }
2401
+
2402
+ float diamond(vec2 p, float r) {
2403
+ p = abs(p);
2404
+ return (max(abs(p.x - p.y), abs(p.x + p.y)) - r) / sqrt(2.0);
2405
+ }
2406
+
2407
+ void main() {
2408
+ float d;
2409
+
2410
+ /** Normalized point coord */
2411
+ vec2 p = vRotationMatrix * (2.0 * gl_PointCoord - 1.0) * vRadiusWithPadding;
2412
+ float r = vRadius;
2413
+
2414
+ // We could also use textures here. Could even be faster, because we have plenty of branching here.
2415
+ if (vShape == CIRCLE) {
2416
+ d = circle(p, r);
2417
+
2418
+ } else if (vShape == SQUARE) {
2419
+ d = square(p, r);
2420
+
2421
+ } else if (vShape == CROSS) {
2422
+ d = crossShape(p, r);
2423
+
2424
+ } else if (vShape == DIAMOND) {
2425
+ d = diamond(p, r);
2426
+
2427
+ } else if (vShape == TRIANGLE_UP) {
2428
+ d = equilateralTriangle(p, r);
2429
+
2430
+ } else if (vShape == TICK_UP) {
2431
+ d = tickUp(p, r);
2432
+
2433
+ } else {
2434
+ d = 0.0;
2435
+ }
2436
+
2437
+ if (!uPickingEnabled) {
2438
+ lowp vec4 fillColor = mix(vFillColor, white, -d * uGradientStrength / vRadius);
2439
+
2440
+ fragColor = distanceToColor(
2441
+ d + (uInwardStroke ? vHalfStrokeWidth : 0.0),
2442
+ fillColor,
2443
+ vStrokeColor,
2444
+ vec4(0.0),
2445
+ vHalfStrokeWidth);
2446
+
2447
+ } else if (d - vHalfStrokeWidth <= 0.0) {
2448
+ fragColor = vPickingColor;
2449
+
2450
+ } else {
2451
+ discard;
2452
+ }
2453
+ }
2454
+
2455
+ ",
2456
+ "vertex": "precision highp float;
2457
+ precision highp int;
2458
+
2459
+ // view: viewRoot
2460
+
2461
+ layout(std140) uniform Mark {
2462
+ /**
2463
+ * The stroke should only grow inwards, e.g, the diameter/outline is not affected by the stroke width.
2464
+ * Thus, a point that has a zero size has no visible stroke. This allows strokes to be used with
2465
+ * geometric zoom, etc.
2466
+ */
2467
+ uniform bool uInwardStroke;
2468
+
2469
+ /** The minimum point size in pixels when rendering into the picking buffer */
2470
+ uniform float uMinPickingSize;
2471
+
2472
+ /** Scale factor for geometric zoom */
2473
+ uniform mediump float uScaleFactor;
2474
+
2475
+ uniform mediump float uZoomLevel;
2476
+ uniform highp float uSemanticThreshold;
2477
+
2478
+ uniform mediump float uGradientStrength;
2479
+
2480
+ mediump float uDomain_x[2];
2481
+
2482
+ mediump float uDomain_y[2];
2483
+
2484
+
2485
+
2486
+ };
2487
+
2488
+
2489
+ #define PI 3.141593
2490
+
2491
+ uniform View {
2492
+ /** Offset in "unit" units */
2493
+ mediump vec2 uViewOffset;
2494
+ mediump vec2 uViewScale;
2495
+ /** Size of the logical viewport in pixels, i.e., the view */
2496
+ mediump vec2 uViewportSize;
2497
+ lowp float uDevicePixelRatio;
2498
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
2499
+ // that is rendered with the specified opacity.
2500
+ lowp float uViewOpacity;
2501
+ bool uPickingEnabled;
2502
+ };
2503
+
2504
+
2505
+ /**
2506
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
2507
+ * (0, 0) is at the bottom left corner.
2508
+ */
2509
+ vec4 unitToNdc(vec2 coord) {
2510
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
2511
+ }
2512
+
2513
+ vec4 unitToNdc(float x, float y) {
2514
+ return unitToNdc(vec2(x, y));
2515
+ }
2516
+
2517
+ vec4 pixelsToNdc(vec2 coord) {
2518
+ return unitToNdc(coord / uViewportSize);
2519
+ }
2520
+
2521
+ vec4 pixelsToNdc(float x, float y) {
2522
+ return pixelsToNdc(vec2(x, y));
2523
+ }
2524
+
2525
+ float linearstep(float edge0, float edge1, float x) {
2526
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
2527
+ }
2528
+
2529
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
2530
+
2531
+ highp uint hash32(highp uint key) {
2532
+ highp uint v = key;
2533
+ v ^= v >> 16u;
2534
+ v *= 0x7feb352du;
2535
+ v ^= v >> 15u;
2536
+ v *= 0x846ca68bu;
2537
+ v ^= v >> 16u;
2538
+ return v;
2539
+ }
2540
+
2541
+ bool isEmptyHashTexture(highp usampler2D s) {
2542
+ // Empty selections are encoded as a single empty hash slot.
2543
+ ivec2 texSize = textureSize(s, 0);
2544
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
2545
+ }
2546
+
2547
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
2548
+ ivec2 texSize = textureSize(s, 0);
2549
+ highp uint width = uint(texSize.x);
2550
+ highp uint size = width * uint(texSize.y);
2551
+ highp uint mask = size - 1u;
2552
+ highp uint index = hash32(value) & mask;
2553
+
2554
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
2555
+ ivec2 coord = ivec2(int(index % width), int(index / width));
2556
+ highp uint entry = texelFetch(s, coord, 0).r;
2557
+ if (entry == value) {
2558
+ return true;
2559
+ }
2560
+ if (entry == HASH_EMPTY_KEY) {
2561
+ return false;
2562
+ }
2563
+ index = (index + 1u) & mask;
2564
+ }
2565
+
2566
+ return false;
2567
+ }
2568
+
2569
+ /**
2570
+ * Calculates a gamma for antialiasing opacity based on the color.
2571
+ */
2572
+ float getGammaForColor(vec3 rgb) {
2573
+ return mix(
2574
+ 1.25,
2575
+ 0.75,
2576
+ // RGB should be linearized but this is good enough for now
2577
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
2578
+ }
2579
+
2580
+ // Fragment shader stuff ////////////////////////////////////////////////////////
2581
+
2582
+ // TODO: include the following only in fragment shaders
2583
+
2584
+ /**
2585
+ * Specialized linearstep for doing antialiasing
2586
+ */
2587
+ float distanceToRatio(float d) {
2588
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
2589
+ }
2590
+
2591
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
2592
+ if (halfStrokeWidth > 0.0) {
2593
+ // Distance to stroke's edge. Negative inside the stroke.
2594
+ float sd = abs(d) - halfStrokeWidth;
2595
+ return mix(
2596
+ stroke,
2597
+ d <= 0.0 ? fill : background,
2598
+ distanceToRatio(sd));
2599
+ } else {
2600
+ return mix(background, fill, distanceToRatio(-d));
2601
+ }
2602
+ }
2603
+
2604
+
2605
+ uniform highp float uZero;
2606
+
2607
+ // Utils ------------
2608
+
2609
+ vec3 getDiscreteColor(sampler2D s, int index) {
2610
+ return texelFetch(s, ivec2(index % textureSize(s, 0).x, 0), 0).rgb;
2611
+ }
2612
+
2613
+ vec3 getInterpolatedColor(sampler2D s, float unitValue) {
2614
+ return texture(s, vec2(unitValue, 0.0)).rgb;
2615
+ }
2616
+
2617
+ float clampToRange(float value, vec2 range) {
2618
+ return clamp(value, min(range[0], range[1]), max(range[0], range[1]));
2619
+ }
2620
+
2621
+ // Scales ------------
2622
+ // Based on d3 scales: https://github.com/d3/d3-scale
2623
+
2624
+ float scaleIdentity(float value) {
2625
+ return value;
2626
+ }
2627
+
2628
+ float scaleIdentity(uint value) {
2629
+ return float(value);
2630
+ }
2631
+
2632
+ float scaleLinear(float value, vec2 domain, vec2 range) {
2633
+ float domainSpan = domain[1] - domain[0];
2634
+ float rangeSpan = range[1] - range[0];
2635
+ return (value - domain[0]) / domainSpan * rangeSpan + range[0];
2636
+ }
2637
+
2638
+ float scaleLog(float value, vec2 domain, vec2 range, float base) {
2639
+ // y = m log(x) + b
2640
+ // TODO: Perf optimization: precalculate log domain in js.
2641
+ // TODO: Reversed domain, etc
2642
+ return scaleLinear(log(value) / log(base), log(domain) / log(base), range);
2643
+ }
2644
+
2645
+ float symlog(float value, float constant) {
2646
+ // WARNING: emulating log1p with log(x + 1). Small numbers are likely to
2647
+ // have significant precision problems.
2648
+ return sign(value) * log(abs(value / constant) + 1.0);
2649
+ }
2650
+
2651
+ float scaleSymlog(float value, vec2 domain, vec2 range, float constant) {
2652
+ return scaleLinear(
2653
+ symlog(value, constant),
2654
+ vec2(symlog(domain[0], constant), symlog(domain[1], constant)),
2655
+ range
2656
+ );
2657
+ }
2658
+
2659
+ float scalePow(float value, vec2 domain, vec2 range, float exponent) {
2660
+ // y = mx^k + b
2661
+ // TODO: Perf optimization: precalculate pow domain in js.
2662
+ // TODO: Reversed domain, etc
2663
+ return scaleLinear(
2664
+ pow(abs(value), exponent) * sign(value),
2665
+ pow(abs(domain), vec2(exponent)) * sign(domain),
2666
+ range
2667
+ );
2668
+ }
2669
+
2670
+ // TODO: scaleThreshold
2671
+ // TODO: scaleQuantile (special case of threshold scale)
2672
+
2673
+ // TODO: domainExtent should be uint
2674
+ float scaleBand(uint value, vec2 domainExtent, vec2 range,
2675
+ float paddingInner, float paddingOuter,
2676
+ float align, float band) {
2677
+
2678
+ // TODO: reverse
2679
+ float start = range[0];
2680
+ float stop = range[1];
2681
+ float rangeSpan = stop - start;
2682
+
2683
+ float n = domainExtent[1] - domainExtent[0];
2684
+
2685
+ // This fix departs from Vega and d3: https://github.com/vega/vega/issues/3357#issuecomment-1063253596
2686
+ paddingInner = int(n) > 1 ? paddingInner : 0.0;
2687
+
2688
+ // Adapted from: https://github.com/d3/d3-scale/blob/master/src/band.js
2689
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
2690
+ start += (rangeSpan - step * (n - paddingInner)) * align;
2691
+ float bandwidth = step * (1.0 - paddingInner);
2692
+
2693
+ return start + (float(value) - domainExtent[0]) * step + bandwidth * band;
2694
+ }
2695
+
2696
+ const int lowBits = 12;
2697
+ const float lowDivisor = pow(2.0, float(lowBits));
2698
+ const uint lowMask = uint(lowDivisor - 1.0);
2699
+
2700
+ vec2 splitUint(uint value) {
2701
+ uint valueLo = value & lowMask;
2702
+ uint valueHi = value - valueLo;
2703
+ return vec2(float(valueHi), float(valueLo));
2704
+ }
2705
+
2706
+ /**
2707
+ * High precision variant of scaleBand for index/locus scales
2708
+ */
2709
+ float scaleBandHp(uint value, vec3 domainExtent, vec2 range,
2710
+ float paddingInner, float paddingOuter,
2711
+ float align, float band) {
2712
+
2713
+ // TODO: reverse
2714
+ float start = range[0];
2715
+ float stop = range[1];
2716
+ float rangeSpan = stop - start;
2717
+
2718
+ vec2 domainStart = domainExtent.xy;
2719
+ float n = domainExtent[2];
2720
+
2721
+ // The following computation is identical for every vertex. Could be done on the JS side.
2722
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
2723
+ start += (rangeSpan - step * (n - paddingInner)) * align;
2724
+ float bandwidth = step * (1.0 - paddingInner);
2725
+
2726
+ // Split into to values with each having a reduced number of significant digits
2727
+ // to mitigate the lack of precision in float32 calculations.
2728
+ vec2 splitValue = splitUint(value);
2729
+
2730
+ // Using max to prevent the shader compiler from wrecking the precision.
2731
+ // Othwewise the compiler could optimize the sum of the four terms into
2732
+ // some equivalent form that does premature rounding.
2733
+ float inf = 1.0 / uZero;
2734
+ float hi = max(splitValue[0] - domainStart[0], -inf);
2735
+ float lo = max(splitValue[1] - domainStart[1], -inf);
2736
+
2737
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
2738
+ }
2739
+
2740
+ /**
2741
+ * High precision variant of scaleBand for index/locus scales for large
2742
+ * domains where 32bit uints are not sufficient to represent the domain.
2743
+ */
2744
+ float scaleBandHp(uvec2 value, vec3 domainExtent, vec2 range,
2745
+ float paddingInner, float paddingOuter,
2746
+ float align, float band) {
2747
+
2748
+ // TODO: reverse
2749
+ float start = range[0];
2750
+ float stop = range[1];
2751
+ float rangeSpan = stop - start;
2752
+
2753
+ vec2 domainStart = domainExtent.xy;
2754
+ float n = domainExtent[2];
2755
+
2756
+ // The following computation is identical for every vertex. Could be done on the JS side.
2757
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
2758
+ start += (rangeSpan - step * (n - paddingInner)) * align;
2759
+ float bandwidth = step * (1.0 - paddingInner);
2760
+
2761
+ // Split into to values with each having a reduced number of significant digits
2762
+ // to mitigate the lack of precision in float32 calculations.
2763
+ vec2 splitValue = vec2(float(value[0]) * lowDivisor, float(value[1]));
2764
+
2765
+ // Using max to prevent the shader compiler from wrecking the precision.
2766
+ // Othwewise the compiler could optimize the sum of the four terms into
2767
+ // some equivalent form that does premature rounding.
2768
+ float inf = 1.0 / uZero;
2769
+ float hi = max(splitValue[0] - domainStart[0], -inf);
2770
+ float lo = max(splitValue[1] - domainStart[1], -inf);
2771
+
2772
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
2773
+ }
2774
+
2775
+
2776
+ in highp uint attr_uniqueId;
2777
+ in highp float attr_x;
2778
+ in highp float attr_y;
2779
+ in highp uint attr_fill;
2780
+
2781
+
2782
+ uint accessor_uniqueId_0() {
2783
+ return attr_uniqueId;
2784
+ }
2785
+
2786
+ uint getScaled_uniqueId() {
2787
+ return accessor_uniqueId_0();
2788
+ }
2789
+
2790
+ #define uniqueId_DEFINED
2791
+
2792
+
2793
+ float accessor_x_0() {
2794
+ return attr_x;
2795
+ }
2796
+
2797
+
2798
+ //////////////////////////////////////////////////////////////////////
2799
+ // Channel: x
2800
+
2801
+ const vec2 range_x = vec2(0.0, 1.0);
2802
+
2803
+ float scale_x(float value) {
2804
+ int slot = 0;
2805
+ vec2 domain = vec2(uDomain_x[slot], uDomain_x[slot + 1]);
2806
+ float transformed = scaleLinear(value, domain, range_x);
2807
+ return transformed;
2808
+
2809
+ }
2810
+
2811
+ float getScaled_x() {
2812
+ return scale_x(accessor_x_0());
2813
+ }
2814
+
2815
+ #define x_DEFINED
2816
+
2817
+
2818
+ float accessor_y_0() {
2819
+ return attr_y;
2820
+ }
2821
+
2822
+
2823
+ //////////////////////////////////////////////////////////////////////
2824
+ // Channel: y
2825
+
2826
+ const vec2 range_y = vec2(0.0, 1.0);
2827
+
2828
+ float scale_y(float value) {
2829
+ int slot = 0;
2830
+ vec2 domain = vec2(uDomain_y[slot], uDomain_y[slot + 1]);
2831
+ float transformed = scaleLinear(value, domain, range_y);
2832
+ return transformed;
2833
+
2834
+ }
2835
+
2836
+ float getScaled_y() {
2837
+ return scale_y(accessor_y_0());
2838
+ }
2839
+
2840
+ #define y_DEFINED
2841
+
2842
+
2843
+ float accessor_size_0() {
2844
+ // Constant value
2845
+ return float(100.0);
2846
+ }
2847
+
2848
+ float getScaled_size() {
2849
+ return accessor_size_0();
2850
+ }
2851
+
2852
+ #define size_DEFINED
2853
+
2854
+
2855
+ float accessor_semanticScore_0() {
2856
+ // Constant value
2857
+ return float(0.0);
2858
+ }
2859
+
2860
+ float getScaled_semanticScore() {
2861
+ return accessor_semanticScore_0();
2862
+ }
2863
+
2864
+ #define semanticScore_DEFINED
2865
+
2866
+
2867
+ float accessor_shape_0() {
2868
+ // Constant value
2869
+ return float(0.0);
2870
+ }
2871
+
2872
+ float getScaled_shape() {
2873
+ return accessor_shape_0();
2874
+ }
2875
+
2876
+ #define shape_DEFINED
2877
+
2878
+
2879
+ float accessor_strokeWidth_0() {
2880
+ // Constant value
2881
+ return float(0.0);
2882
+ }
2883
+
2884
+ float getScaled_strokeWidth() {
2885
+ return accessor_strokeWidth_0();
2886
+ }
2887
+
2888
+ #define strokeWidth_DEFINED
2889
+
2890
+
2891
+ float accessor_dx_0() {
2892
+ // Constant value
2893
+ return float(0.0);
2894
+ }
2895
+
2896
+ float getScaled_dx() {
2897
+ return accessor_dx_0();
2898
+ }
2899
+
2900
+ #define dx_DEFINED
2901
+
2902
+
2903
+ float accessor_dy_0() {
2904
+ // Constant value
2905
+ return float(0.0);
2906
+ }
2907
+
2908
+ float getScaled_dy() {
2909
+ return accessor_dy_0();
2910
+ }
2911
+
2912
+ #define dy_DEFINED
2913
+
2914
+
2915
+ float accessor_angle_0() {
2916
+ // Constant value
2917
+ return float(0.0);
2918
+ }
2919
+
2920
+ float getScaled_angle() {
2921
+ return accessor_angle_0();
2922
+ }
2923
+
2924
+ #define angle_DEFINED
2925
+
2926
+
2927
+ vec3 accessor_stroke_0() {
2928
+ // Constant value
2929
+ return vec3(0.0, 0.0, 0.0);
2930
+ }
2931
+
2932
+ vec3 getScaled_stroke() {
2933
+ return accessor_stroke_0();
2934
+ }
2935
+
2936
+ #define stroke_DEFINED
2937
+
2938
+
2939
+ float accessor_strokeOpacity_0() {
2940
+ // Constant value
2941
+ return float(1.0);
2942
+ }
2943
+
2944
+ float getScaled_strokeOpacity() {
2945
+ return accessor_strokeOpacity_0();
2946
+ }
2947
+
2948
+ #define strokeOpacity_DEFINED
2949
+
2950
+
2951
+ uint accessor_fill_0() {
2952
+ return attr_fill;
2953
+ }
2954
+
2955
+
2956
+ //////////////////////////////////////////////////////////////////////
2957
+ // Channel: fill
2958
+
2959
+ uniform sampler2D uRangeTexture_fill;
2960
+
2961
+ vec3 scale_fill(uint value) {
2962
+ int slot = 0;
2963
+ float transformed = scaleIdentity(value);
2964
+ return getDiscreteColor(uRangeTexture_fill, int(transformed));
2965
+
2966
+ }
2967
+
2968
+ vec3 getScaled_fill() {
2969
+ return scale_fill(accessor_fill_0());
2970
+ }
2971
+
2972
+ #define fill_DEFINED
2973
+
2974
+
2975
+ float accessor_fillOpacity_0() {
2976
+ // Constant value
2977
+ return float(1.0);
2978
+ }
2979
+
2980
+ float getScaled_fillOpacity() {
2981
+ return accessor_fillOpacity_0();
2982
+ }
2983
+
2984
+ #define fillOpacity_DEFINED
2985
+
2986
+ bool isPointSelected() {
2987
+ return false;
2988
+ }
2989
+
2990
+
2991
+ /**
2992
+ * Describes where a sample facet should be shown. Interpolating between the
2993
+ * current and target positions/heights allows for transitioning between facet
2994
+ * configurations.
2995
+ */
2996
+ struct SampleFacetPosition {
2997
+ float pos;
2998
+ float height;
2999
+ float targetPos;
3000
+ float targetHeight;
3001
+ };
3002
+
3003
+ /**
3004
+ * Trasition fraction [0, 1] between the current and target configurations.
3005
+ */
3006
+ uniform float uTransitionOffset;
3007
+
3008
+
3009
+ // ----------------------------------------------------------------------------
3010
+
3011
+ #if !defined(SAMPLE_FACET_UNIFORM) && !defined(SAMPLE_FACET_TEXTURE)
3012
+
3013
+ SampleFacetPosition getSampleFacetPos() {
3014
+ return SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
3015
+ }
3016
+
3017
+ #elif defined(SAMPLE_FACET_UNIFORM)
3018
+
3019
+ /**
3020
+ * Location and height of the band on the Y axis on a normalized [0, 1] scale.
3021
+ * Elements: curr pos, curr height, target pos, target height
3022
+ */
3023
+ uniform vec4 uSampleFacet;
3024
+
3025
+ SampleFacetPosition getSampleFacetPos() {
3026
+ return SampleFacetPosition(
3027
+ 1.0 - uSampleFacet.x - uSampleFacet.y,
3028
+ uSampleFacet.y,
3029
+ 1.0 - uSampleFacet.z - uSampleFacet.w,
3030
+ uSampleFacet.w
3031
+ );
3032
+ }
3033
+
3034
+ #elif defined(SAMPLE_FACET_TEXTURE)
3035
+
3036
+ uniform sampler2D uSampleFacetTexture;
3037
+
3038
+ SampleFacetPosition getSampleFacetPos() {
3039
+ vec4 texel = texelFetch(uSampleFacetTexture, ivec2(int(attr_facetIndex), 0), 0);
3040
+ return SampleFacetPosition(
3041
+ 1.0 - texel.r - texel.g,
3042
+ texel.g,
3043
+ 1.0 - texel.r - texel.g,
3044
+ texel.g);
3045
+ }
3046
+
3047
+ #endif
3048
+
3049
+ // ----------------------------------------------------------------------------
3050
+
3051
+ bool isFacetedSamples(SampleFacetPosition facetPos) {
3052
+ return facetPos != SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
3053
+ }
3054
+
3055
+ bool isFacetedSamples() {
3056
+ return isFacetedSamples(getSampleFacetPos());
3057
+ }
3058
+
3059
+ bool isInTransit() {
3060
+ return uTransitionOffset > 0.0;
3061
+ }
3062
+
3063
+ float getTransitionFraction(float xPos) {
3064
+ return smoothstep(0.0, 0.7 + uTransitionOffset, (xPos - uTransitionOffset) * 2.0);
3065
+ }
3066
+
3067
+ vec2 applySampleFacet(vec2 pos) {
3068
+ SampleFacetPosition facetPos = getSampleFacetPos();
3069
+
3070
+ if (!isFacetedSamples(facetPos)) {
3071
+ return pos;
3072
+ } else if (isInTransit()) {
3073
+ vec2 interpolated = mix(
3074
+ vec2(facetPos.pos, facetPos.height),
3075
+ vec2(facetPos.targetPos, facetPos.targetHeight),
3076
+ getTransitionFraction(pos.x));
3077
+ return vec2(pos.x, interpolated[0] + pos.y * interpolated[1]);
3078
+ } else {
3079
+ return vec2(pos.x, facetPos.pos + pos.y * facetPos.height);
3080
+ }
3081
+ }
3082
+
3083
+ float getSampleFacetHeight(vec2 pos) {
3084
+ SampleFacetPosition facetPos = getSampleFacetPos();
3085
+
3086
+ if (!isFacetedSamples(facetPos)) {
3087
+ return 1.0;
3088
+ } else if (isInTransit()) {
3089
+ return mix(
3090
+ facetPos.height,
3091
+ facetPos.targetHeight,
3092
+ getTransitionFraction(pos.x));
3093
+ } else {
3094
+ return facetPos.height;
3095
+ }
3096
+ }
3097
+
3098
+
3099
+ /*
3100
+ * Based on concepts presented at:
3101
+ * https://webglfundamentals.org/webgl/lessons/webgl-picking.html
3102
+ * https://deck.gl/docs/developer-guide/custom-layers/picking
3103
+ */
3104
+
3105
+ out highp vec4 vPickingColor;
3106
+
3107
+ /**
3108
+ * Passes the unique id to the fragment shader as a color if picking is enabled.
3109
+ * Returns true if picking is enabled.
3110
+ */
3111
+ bool setupPicking() {
3112
+ if (uPickingEnabled) {
3113
+ #ifdef uniqueId_DEFINED
3114
+ uint id = attr_uniqueId;
3115
+ vPickingColor = vec4(
3116
+ ivec4(id >> 0, id >> 8, id >> 16, id >> 24) & 0xFF
3117
+ ) / float(0xFF);
3118
+ #else
3119
+ vPickingColor = vec4(1.0);
3120
+ #endif
3121
+ return true;
3122
+ }
3123
+ return false;
3124
+ }
3125
+
3126
+
3127
+ flat out float vRadius;
3128
+ flat out float vRadiusWithPadding;
3129
+ flat out lowp vec4 vFillColor;
3130
+ flat out lowp vec4 vStrokeColor;
3131
+ flat out lowp float vShape;
3132
+ flat out lowp float vHalfStrokeWidth;
3133
+ flat out mat2 vRotationMatrix;
3134
+
3135
+ // Copypaste from fragment shader
3136
+ const float CIRCLE = 0.0;
3137
+ const float SQUARE = 1.0;
3138
+ const float CROSS = 2.0;
3139
+ const float DIAMOND = 3.0;
3140
+ const float TRIANGLE_UP = 4.0;
3141
+ const float TRIANGLE_RIGHT = 5.0;
3142
+ const float TRIANGLE_DOWN = 6.0;
3143
+ const float TRIANGLE_LEFT = 7.0;
3144
+ const float TICK_UP = 8.0;
3145
+ const float TICK_RIGHT = 9.0;
3146
+ const float TICK_DOWN = 10.0;
3147
+ const float TICK_LEFT = 11.0;
3148
+
3149
+ float computeSemanticThresholdFactor() {
3150
+ // TODO: add smooth transition
3151
+ return getScaled_semanticScore() >= uSemanticThreshold ? 1.0 : 0.0;
3152
+ }
3153
+
3154
+ // TODO: Move this into common.glsl or something
3155
+ vec2 getDxDy() {
3156
+ #if defined(dx_DEFINED) || defined(dy_DEFINED)
3157
+ return vec2(getScaled_dx(), getScaled_dy()) / uViewportSize;
3158
+ #else
3159
+ return vec2(0.0, 0.0);
3160
+ #endif
3161
+ }
3162
+
3163
+ void main(void) {
3164
+ float shapeAngle = 0.0;
3165
+
3166
+ // Selected points should always be visible
3167
+ float semanticThresholdFactor = isPointSelected()
3168
+ ? 1.0
3169
+ : computeSemanticThresholdFactor();
3170
+
3171
+ if (semanticThresholdFactor <= 0.0) {
3172
+ gl_PointSize = 0.0;
3173
+ // Place the vertex outside the viewport. The default (0, 0) makes this super-slow
3174
+ // on Apple Silicon. Probably related to the tile-based GPU architecture.
3175
+ gl_Position = vec4(100.0, 0.0, 0.0, 0.0);
3176
+ // Exit early. MAY prevent some unnecessary calculations.
3177
+ return;
3178
+ }
3179
+
3180
+ float size = getScaled_size();
3181
+ vec2 pos = vec2(getScaled_x(), getScaled_y()) + getDxDy();
3182
+
3183
+ gl_Position = unitToNdc(applySampleFacet(pos));
3184
+
3185
+ float strokeWidth = getScaled_strokeWidth();
3186
+
3187
+ float diameter = sqrt(size) *
3188
+ uScaleFactor *
3189
+ semanticThresholdFactor;
3190
+
3191
+ // Clamp minimum size and adjust opacity instead. Yields more pleasing result,
3192
+ // no flickering etc.
3193
+ float opacity = uViewOpacity;
3194
+ if (strokeWidth <= 0.0 || uInwardStroke) {
3195
+ float minDiameter = 1.0 / uDevicePixelRatio;
3196
+ if (diameter < minDiameter) {
3197
+ // We do some "cheap" gamma correction here. It breaks on dark background, though.
3198
+ // First we take a square of the size and then apply "gamma" of 1.5.
3199
+ opacity *= pow(diameter / minDiameter, 2.5);
3200
+ diameter = minDiameter;
3201
+ }
3202
+ }
3203
+
3204
+ float fillOpa = getScaled_fillOpacity() * opacity;
3205
+ float strokeOpa = getScaled_strokeOpacity() * opacity;
3206
+
3207
+ vShape = getScaled_shape();
3208
+
3209
+ // Circle doesn't have sharp corners. Do some special optimizations to minimize the point size.
3210
+ bool circle = vShape == 0.0;
3211
+
3212
+ if (vShape > TICK_UP && vShape <= TICK_LEFT) {
3213
+ shapeAngle = (vShape - TICK_UP) * 90.0;
3214
+ vShape = TICK_UP;
3215
+ } else if (vShape > TRIANGLE_UP && vShape <= TRIANGLE_LEFT) {
3216
+ shapeAngle = (vShape - TRIANGLE_UP) * 90.0;
3217
+ vShape = TRIANGLE_UP;
3218
+ }
3219
+
3220
+ float angleInDegrees = getScaled_angle();
3221
+ float angle = -(shapeAngle + angleInDegrees) * PI / 180.0;
3222
+ float sinTheta = sin(angle);
3223
+ float cosTheta = cos(angle);
3224
+ vRotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);
3225
+
3226
+ // Not needed if we would draw rotated quads instead of gl.POINTS
3227
+ float roomForRotation = circle ? 1.0 : sin(mod(angle, PI / 2.0) + PI / 4.0) / sin(PI / 4.0);
3228
+
3229
+ float aaPadding = 1.0 / uDevicePixelRatio;
3230
+ float rotationPadding = (diameter * roomForRotation) - diameter;
3231
+ // sqrt(3.0) ensures that the angles of equilateral triangles have enough room
3232
+ float strokePadding = uInwardStroke ? 0.0 : strokeWidth * (circle ? 1.0 : sqrt(3.0));
3233
+ float padding = rotationPadding + strokePadding + aaPadding;
3234
+
3235
+ gl_PointSize = max(
3236
+ (diameter + padding),
3237
+ uPickingEnabled ? uMinPickingSize : 0.0
3238
+ ) * uDevicePixelRatio;
3239
+
3240
+ vRadius = diameter / 2.0;
3241
+ vRadiusWithPadding = vRadius + padding / 2.0;
3242
+
3243
+ vHalfStrokeWidth = strokeWidth / 2.0;
3244
+
3245
+ vFillColor = vec4(getScaled_fill() * fillOpa, fillOpa);
3246
+ vStrokeColor = vec4(getScaled_stroke() * strokeOpa, strokeOpa);
3247
+
3248
+ setupPicking();
3249
+ }
3250
+ ",
3251
+ }
3252
+ `;
3253
+
3254
+ exports[`generated shader snapshots > point selection example 1`] = `
3255
+ {
3256
+ "fragment": "precision highp float;
3257
+ precision highp int;
3258
+
3259
+ // view: viewRoot
3260
+
3261
+ layout(std140) uniform Mark {
3262
+ /** Minimum size (width, height) of the displayed rectangle in pixels */
3263
+ uniform float uMinWidth;
3264
+ uniform float uMinHeight;
3265
+
3266
+ /** Minimum opacity for the size clamping */
3267
+ uniform float uMinOpacity;
3268
+
3269
+ uniform float uCornerRadiusTopRight;
3270
+ uniform float uCornerRadiusBottomRight;
3271
+ uniform float uCornerRadiusTopLeft;
3272
+ uniform float uCornerRadiusBottomLeft;
3273
+
3274
+ uniform int uHatchPattern;
3275
+
3276
+ uniform vec3 uShadowColor;
3277
+ uniform float uShadowOpacity;
3278
+ uniform float uShadowBlur;
3279
+ uniform float uShadowOffsetX;
3280
+ uniform float uShadowOffsetY;
3281
+
3282
+ // Selection parameter
3283
+ uniform highp uint uParam_highlight;
3284
+ mediump float uDomain_x[2];
3285
+
3286
+ mediump float uDomain_y[2];
3287
+
3288
+
3289
+
3290
+ uniform highp float attr_y2;
3291
+
3292
+
3293
+ };
3294
+
3295
+
3296
+ #define STROKED
3297
+
3298
+ #define PI 3.141593
3299
+
3300
+ uniform View {
3301
+ /** Offset in "unit" units */
3302
+ mediump vec2 uViewOffset;
3303
+ mediump vec2 uViewScale;
3304
+ /** Size of the logical viewport in pixels, i.e., the view */
3305
+ mediump vec2 uViewportSize;
3306
+ lowp float uDevicePixelRatio;
3307
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
3308
+ // that is rendered with the specified opacity.
3309
+ lowp float uViewOpacity;
3310
+ bool uPickingEnabled;
3311
+ };
3312
+
3313
+
3314
+ /**
3315
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
3316
+ * (0, 0) is at the bottom left corner.
3317
+ */
3318
+ vec4 unitToNdc(vec2 coord) {
3319
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
3320
+ }
3321
+
3322
+ vec4 unitToNdc(float x, float y) {
3323
+ return unitToNdc(vec2(x, y));
3324
+ }
3325
+
3326
+ vec4 pixelsToNdc(vec2 coord) {
3327
+ return unitToNdc(coord / uViewportSize);
3328
+ }
3329
+
3330
+ vec4 pixelsToNdc(float x, float y) {
3331
+ return pixelsToNdc(vec2(x, y));
3332
+ }
3333
+
3334
+ float linearstep(float edge0, float edge1, float x) {
3335
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
3336
+ }
3337
+
3338
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
3339
+
3340
+ highp uint hash32(highp uint key) {
3341
+ highp uint v = key;
3342
+ v ^= v >> 16u;
3343
+ v *= 0x7feb352du;
3344
+ v ^= v >> 15u;
3345
+ v *= 0x846ca68bu;
3346
+ v ^= v >> 16u;
3347
+ return v;
3348
+ }
3349
+
3350
+ bool isEmptyHashTexture(highp usampler2D s) {
3351
+ // Empty selections are encoded as a single empty hash slot.
3352
+ ivec2 texSize = textureSize(s, 0);
3353
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
3354
+ }
3355
+
3356
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
3357
+ ivec2 texSize = textureSize(s, 0);
3358
+ highp uint width = uint(texSize.x);
3359
+ highp uint size = width * uint(texSize.y);
3360
+ highp uint mask = size - 1u;
3361
+ highp uint index = hash32(value) & mask;
3362
+
3363
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
3364
+ ivec2 coord = ivec2(int(index % width), int(index / width));
3365
+ highp uint entry = texelFetch(s, coord, 0).r;
3366
+ if (entry == value) {
3367
+ return true;
3368
+ }
3369
+ if (entry == HASH_EMPTY_KEY) {
3370
+ return false;
3371
+ }
3372
+ index = (index + 1u) & mask;
3373
+ }
3374
+
3375
+ return false;
3376
+ }
3377
+
3378
+ /**
3379
+ * Calculates a gamma for antialiasing opacity based on the color.
3380
+ */
3381
+ float getGammaForColor(vec3 rgb) {
3382
+ return mix(
3383
+ 1.25,
3384
+ 0.75,
3385
+ // RGB should be linearized but this is good enough for now
3386
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
3387
+ }
3388
+
3389
+ // Fragment shader stuff ////////////////////////////////////////////////////////
3390
+
3391
+ // TODO: include the following only in fragment shaders
3392
+
3393
+ /**
3394
+ * Specialized linearstep for doing antialiasing
3395
+ */
3396
+ float distanceToRatio(float d) {
3397
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
3398
+ }
3399
+
3400
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
3401
+ if (halfStrokeWidth > 0.0) {
3402
+ // Distance to stroke's edge. Negative inside the stroke.
3403
+ float sd = abs(d) - halfStrokeWidth;
3404
+ return mix(
3405
+ stroke,
3406
+ d <= 0.0 ? fill : background,
3407
+ distanceToRatio(sd));
3408
+ } else {
3409
+ return mix(background, fill, distanceToRatio(-d));
3410
+ }
3411
+ }
3412
+
3413
+
3414
+ in highp vec4 vPickingColor;
3415
+
3416
+
3417
+ #if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)
3418
+ in vec2 vPosInPixels;
3419
+ #endif
3420
+
3421
+ flat in vec2 vHalfSizeInPixels;
3422
+
3423
+ flat in lowp vec4 vFillColor;
3424
+ flat in lowp vec4 vStrokeColor;
3425
+ flat in float vHalfStrokeWidth;
3426
+ flat in vec4 vCornerRadii;
3427
+
3428
+ out lowp vec4 fragColor;
3429
+
3430
+ // ----------------------------------------------------------------------------
3431
+ // Shadow source: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/
3432
+ // License: CC0 (http://creativecommons.org/publicdomain/zero/1.0/)
3433
+
3434
+ #ifdef SHADOW
3435
+
3436
+ // A standard gaussian function, used for weighting samples
3437
+ float gaussian(float x, float sigma) {
3438
+ const float pi = 3.141592653589793;
3439
+ return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);
3440
+ }
3441
+
3442
+ // This approximates the error function, needed for the gaussian integral
3443
+ vec2 erf(vec2 x) {
3444
+ vec2 s = sign(x), a = abs(x);
3445
+ x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
3446
+ x *= x;
3447
+ return s - s / (x * x);
3448
+ }
3449
+
3450
+ // Return the blurred mask along the x dimension
3451
+ float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) {
3452
+ float delta = min(halfSize.y - corner - abs(y), 0.0);
3453
+ float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
3454
+ vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));
3455
+ return integral.y - integral.x;
3456
+ }
3457
+
3458
+ // Return the mask for the shadow of a box from lower to upper
3459
+ float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner) {
3460
+ // Center everything to make the math easier
3461
+ vec2 center = (lower + upper) * 0.5;
3462
+ vec2 halfSize = (upper - lower) * 0.5;
3463
+ point -= center;
3464
+
3465
+ // The signal is only non-zero in a limited range, so don't waste samples
3466
+ float low = point.y - halfSize.y;
3467
+ float high = point.y + halfSize.y;
3468
+ float start = clamp(-3.0 * sigma, low, high);
3469
+ float end = clamp(3.0 * sigma, low, high);
3470
+
3471
+ // Accumulate samples (we can get away with surprisingly few samples)
3472
+ float step = (end - start) / 4.0;
3473
+ float y = start + step * 0.5;
3474
+ float value = 0.0;
3475
+ for (int i = 0; i < 4; i++) {
3476
+ value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;
3477
+ y += step;
3478
+ }
3479
+
3480
+ return value;
3481
+ }
3482
+
3483
+ // ----------------------------------------------------------------------------
3484
+
3485
+ #endif
3486
+
3487
+ // Source: https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
3488
+ float sdRoundedBox(vec2 p, vec2 b, vec4 r) {
3489
+ r.xy = p.x > 0.0 ? r.xy : r.zw;
3490
+ r.x = p.y > 0.0 ? r.x : r.y;
3491
+ vec2 q = abs(p) - b + r.x;
3492
+ return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;
3493
+ }
3494
+
3495
+ // Not a true SDF. Makes the corners of strokes sharp and is faster.
3496
+ float sdSharpBox(vec2 p, vec2 b) {
3497
+ vec2 q = abs(p) - b;
3498
+ return max(q.x, q.y);
3499
+ }
3500
+
3501
+ float diagonalPattern(vec2 uv, float spacing) {
3502
+ // Using 1.5 to approximate sqrt(2.0) to reduce aliasing artifacts.
3503
+ float divisor = spacing * vHalfStrokeWidth * 2.0 * 1.5;
3504
+ return abs(mod(uv.x - uv.y, divisor) - 0.5 * divisor) / 1.5;
3505
+ }
3506
+
3507
+ float verticalPattern(float x, float spacing) {
3508
+ float divisor = spacing * vHalfStrokeWidth * 2.0;
3509
+ return abs(mod(x, divisor)) / 2.0;
3510
+ }
3511
+
3512
+ float circle(vec2 p, float r) {
3513
+ return length(p) - r;
3514
+ }
3515
+
3516
+ float masonryCirclePattern(vec2 uv, float spacing, float radius) {
3517
+ float halfSpacing = 0.5 * spacing;
3518
+
3519
+ float row = floor(uv.y / spacing);
3520
+ float shift = mod(row, 2.0) * halfSpacing;
3521
+
3522
+ vec2 shifted = vec2(uv.x + shift, uv.y + halfSpacing);
3523
+ vec2 cell = mod(shifted + 0.5 * spacing, spacing) - halfSpacing;
3524
+
3525
+ return abs(circle(cell, radius));
3526
+ }
3527
+
3528
+ /**
3529
+ * Patterns:
3530
+ * 0 none
3531
+ * 1 diagonal (/)
3532
+ * 2 antiDiagonal (\\)
3533
+ * 3 cross (X)
3534
+ * 4 vertical (|)
3535
+ * 5 horizontal (-)
3536
+ * 6 grid (+)
3537
+ * 7 dots (.)
3538
+ * 8 rings (o)
3539
+ * 9 ringsLarge (O)
3540
+ */
3541
+ float pattern() {
3542
+ #ifdef STROKED
3543
+ int patternType = uHatchPattern;
3544
+ vec2 uv = vPosInPixels;
3545
+ float spacing = 4.0;
3546
+
3547
+ switch (patternType) {
3548
+ case 1:
3549
+ return diagonalPattern(vec2(uv.x, -uv.y), spacing);
3550
+ case 2:
3551
+ return diagonalPattern(uv, spacing);
3552
+ case 3:
3553
+ return min(
3554
+ diagonalPattern(uv, spacing),
3555
+ diagonalPattern(vec2(uv.x, -uv.y), spacing)
3556
+ );
3557
+ case 4:
3558
+ return verticalPattern(uv.x, spacing);
3559
+ case 5:
3560
+ return verticalPattern(uv.y, spacing);
3561
+ case 6:
3562
+ return min(
3563
+ verticalPattern(uv.x, spacing),
3564
+ verticalPattern(uv.y, spacing)
3565
+ );
3566
+ case 7:
3567
+ case 8:
3568
+ case 9: {
3569
+ float spacing = vHalfStrokeWidth * 14.0;
3570
+ float radius = spacing * (
3571
+ patternType == 8 ? 0.2 :
3572
+ patternType == 9 ? 0.35 :
3573
+ 0.07
3574
+ );
3575
+ return masonryCirclePattern(uv, spacing, radius);
3576
+ }
3577
+ default:
3578
+ break;
3579
+ }
3580
+ #endif
3581
+ return 1.0 / 0.0; // Infinity
3582
+ }
3583
+
3584
+ void main(void) {
3585
+
3586
+ #if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)
3587
+ #ifdef ROUNDED_CORNERS
3588
+ // Distance from rectangle's edge in pixels. Negative inside the rectangle.
3589
+ float d = sdRoundedBox(vPosInPixels, vHalfSizeInPixels, vCornerRadii);
3590
+ #else
3591
+ float d = sdSharpBox(vPosInPixels, vHalfSizeInPixels);
3592
+ #endif
3593
+
3594
+ vec4 backgroundColor = vec4(0.0, 0.0, 0.0, 0.0);
3595
+
3596
+ #ifdef SHADOW
3597
+ float maxCornerRadius = max(vCornerRadii.x, max(vCornerRadii.y, max(vCornerRadii.z, vCornerRadii.w)));
3598
+
3599
+ float shadow = 0.0;
3600
+ // Only calculate shadow for the region outside the stroke.
3601
+ if (d >= vHalfStrokeWidth - 1.0 && uShadowOpacity > 0.0) {
3602
+ shadow = roundedBoxShadow(
3603
+ -vHalfSizeInPixels - vHalfStrokeWidth,
3604
+ vHalfSizeInPixels + vHalfStrokeWidth,
3605
+ vPosInPixels - vec2(uShadowOffsetX, -uShadowOffsetY),
3606
+ max(uShadowBlur / 2.5, 0.25),
3607
+ maxCornerRadius + vHalfStrokeWidth
3608
+ ) * uShadowOpacity * uViewOpacity;
3609
+ }
3610
+ backgroundColor = vec4(uShadowColor * shadow, shadow);
3611
+ #endif
3612
+
3613
+ if (vHalfStrokeWidth > 0.0 && uHatchPattern > 0) {
3614
+ d = max(d, -pattern());
3615
+ }
3616
+
3617
+ fragColor = distanceToColor(
3618
+ d,
3619
+ vFillColor,
3620
+ vStrokeColor,
3621
+ backgroundColor,
3622
+ vHalfStrokeWidth
3623
+ );
3624
+
3625
+ if (uPickingEnabled) {
3626
+ if (d < vHalfStrokeWidth) {
3627
+ fragColor = vPickingColor;
3628
+ }
3629
+ } else if (fragColor.a == 0.0) {
3630
+ discard;
3631
+ }
3632
+ #else
3633
+ // The trivial, non-decorated case
3634
+ fragColor = vFillColor;
3635
+ if (uPickingEnabled) {
3636
+ fragColor = vPickingColor;
3637
+ }
3638
+ #endif
3639
+ }
3640
+ ",
3641
+ "vertex": "precision highp float;
3642
+ precision highp int;
3643
+
3644
+ // view: viewRoot
3645
+
3646
+ layout(std140) uniform Mark {
3647
+ /** Minimum size (width, height) of the displayed rectangle in pixels */
3648
+ uniform float uMinWidth;
3649
+ uniform float uMinHeight;
3650
+
3651
+ /** Minimum opacity for the size clamping */
3652
+ uniform float uMinOpacity;
3653
+
3654
+ uniform float uCornerRadiusTopRight;
3655
+ uniform float uCornerRadiusBottomRight;
3656
+ uniform float uCornerRadiusTopLeft;
3657
+ uniform float uCornerRadiusBottomLeft;
3658
+
3659
+ uniform int uHatchPattern;
3660
+
3661
+ uniform vec3 uShadowColor;
3662
+ uniform float uShadowOpacity;
3663
+ uniform float uShadowBlur;
3664
+ uniform float uShadowOffsetX;
3665
+ uniform float uShadowOffsetY;
3666
+
3667
+ // Selection parameter
3668
+ uniform highp uint uParam_highlight;
3669
+ mediump float uDomain_x[2];
3670
+
3671
+ mediump float uDomain_y[2];
3672
+
3673
+
3674
+
3675
+ uniform highp float attr_y2;
3676
+
3677
+
3678
+ };
3679
+
3680
+
3681
+ #define STROKED
3682
+
3683
+ #define PI 3.141593
3684
+
3685
+ uniform View {
3686
+ /** Offset in "unit" units */
3687
+ mediump vec2 uViewOffset;
3688
+ mediump vec2 uViewScale;
3689
+ /** Size of the logical viewport in pixels, i.e., the view */
3690
+ mediump vec2 uViewportSize;
3691
+ lowp float uDevicePixelRatio;
3692
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
3693
+ // that is rendered with the specified opacity.
3694
+ lowp float uViewOpacity;
3695
+ bool uPickingEnabled;
3696
+ };
3697
+
3698
+
3699
+ /**
3700
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
3701
+ * (0, 0) is at the bottom left corner.
3702
+ */
3703
+ vec4 unitToNdc(vec2 coord) {
3704
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
3705
+ }
3706
+
3707
+ vec4 unitToNdc(float x, float y) {
3708
+ return unitToNdc(vec2(x, y));
3709
+ }
3710
+
3711
+ vec4 pixelsToNdc(vec2 coord) {
3712
+ return unitToNdc(coord / uViewportSize);
3713
+ }
3714
+
3715
+ vec4 pixelsToNdc(float x, float y) {
3716
+ return pixelsToNdc(vec2(x, y));
3717
+ }
3718
+
3719
+ float linearstep(float edge0, float edge1, float x) {
3720
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
3721
+ }
3722
+
3723
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
3724
+
3725
+ highp uint hash32(highp uint key) {
3726
+ highp uint v = key;
3727
+ v ^= v >> 16u;
3728
+ v *= 0x7feb352du;
3729
+ v ^= v >> 15u;
3730
+ v *= 0x846ca68bu;
3731
+ v ^= v >> 16u;
3732
+ return v;
3733
+ }
3734
+
3735
+ bool isEmptyHashTexture(highp usampler2D s) {
3736
+ // Empty selections are encoded as a single empty hash slot.
3737
+ ivec2 texSize = textureSize(s, 0);
3738
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
3739
+ }
3740
+
3741
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
3742
+ ivec2 texSize = textureSize(s, 0);
3743
+ highp uint width = uint(texSize.x);
3744
+ highp uint size = width * uint(texSize.y);
3745
+ highp uint mask = size - 1u;
3746
+ highp uint index = hash32(value) & mask;
3747
+
3748
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
3749
+ ivec2 coord = ivec2(int(index % width), int(index / width));
3750
+ highp uint entry = texelFetch(s, coord, 0).r;
3751
+ if (entry == value) {
3752
+ return true;
3753
+ }
3754
+ if (entry == HASH_EMPTY_KEY) {
3755
+ return false;
3756
+ }
3757
+ index = (index + 1u) & mask;
3758
+ }
3759
+
3760
+ return false;
3761
+ }
3762
+
3763
+ /**
3764
+ * Calculates a gamma for antialiasing opacity based on the color.
3765
+ */
3766
+ float getGammaForColor(vec3 rgb) {
3767
+ return mix(
3768
+ 1.25,
3769
+ 0.75,
3770
+ // RGB should be linearized but this is good enough for now
3771
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
3772
+ }
3773
+
3774
+ // Fragment shader stuff ////////////////////////////////////////////////////////
3775
+
3776
+ // TODO: include the following only in fragment shaders
3777
+
3778
+ /**
3779
+ * Specialized linearstep for doing antialiasing
3780
+ */
3781
+ float distanceToRatio(float d) {
3782
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
3783
+ }
3784
+
3785
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
3786
+ if (halfStrokeWidth > 0.0) {
3787
+ // Distance to stroke's edge. Negative inside the stroke.
3788
+ float sd = abs(d) - halfStrokeWidth;
3789
+ return mix(
3790
+ stroke,
3791
+ d <= 0.0 ? fill : background,
3792
+ distanceToRatio(sd));
3793
+ } else {
3794
+ return mix(background, fill, distanceToRatio(-d));
3795
+ }
3796
+ }
3797
+
3798
+
3799
+ uniform highp float uZero;
3800
+
3801
+ // Utils ------------
3802
+
3803
+ vec3 getDiscreteColor(sampler2D s, int index) {
3804
+ return texelFetch(s, ivec2(index % textureSize(s, 0).x, 0), 0).rgb;
3805
+ }
3806
+
3807
+ vec3 getInterpolatedColor(sampler2D s, float unitValue) {
3808
+ return texture(s, vec2(unitValue, 0.0)).rgb;
3809
+ }
3810
+
3811
+ float clampToRange(float value, vec2 range) {
3812
+ return clamp(value, min(range[0], range[1]), max(range[0], range[1]));
3813
+ }
3814
+
3815
+ // Scales ------------
3816
+ // Based on d3 scales: https://github.com/d3/d3-scale
3817
+
3818
+ float scaleIdentity(float value) {
3819
+ return value;
3820
+ }
3821
+
3822
+ float scaleIdentity(uint value) {
3823
+ return float(value);
3824
+ }
3825
+
3826
+ float scaleLinear(float value, vec2 domain, vec2 range) {
3827
+ float domainSpan = domain[1] - domain[0];
3828
+ float rangeSpan = range[1] - range[0];
3829
+ return (value - domain[0]) / domainSpan * rangeSpan + range[0];
3830
+ }
3831
+
3832
+ float scaleLog(float value, vec2 domain, vec2 range, float base) {
3833
+ // y = m log(x) + b
3834
+ // TODO: Perf optimization: precalculate log domain in js.
3835
+ // TODO: Reversed domain, etc
3836
+ return scaleLinear(log(value) / log(base), log(domain) / log(base), range);
3837
+ }
3838
+
3839
+ float symlog(float value, float constant) {
3840
+ // WARNING: emulating log1p with log(x + 1). Small numbers are likely to
3841
+ // have significant precision problems.
3842
+ return sign(value) * log(abs(value / constant) + 1.0);
3843
+ }
3844
+
3845
+ float scaleSymlog(float value, vec2 domain, vec2 range, float constant) {
3846
+ return scaleLinear(
3847
+ symlog(value, constant),
3848
+ vec2(symlog(domain[0], constant), symlog(domain[1], constant)),
3849
+ range
3850
+ );
3851
+ }
3852
+
3853
+ float scalePow(float value, vec2 domain, vec2 range, float exponent) {
3854
+ // y = mx^k + b
3855
+ // TODO: Perf optimization: precalculate pow domain in js.
3856
+ // TODO: Reversed domain, etc
3857
+ return scaleLinear(
3858
+ pow(abs(value), exponent) * sign(value),
3859
+ pow(abs(domain), vec2(exponent)) * sign(domain),
3860
+ range
3861
+ );
3862
+ }
3863
+
3864
+ // TODO: scaleThreshold
3865
+ // TODO: scaleQuantile (special case of threshold scale)
3866
+
3867
+ // TODO: domainExtent should be uint
3868
+ float scaleBand(uint value, vec2 domainExtent, vec2 range,
3869
+ float paddingInner, float paddingOuter,
3870
+ float align, float band) {
3871
+
3872
+ // TODO: reverse
3873
+ float start = range[0];
3874
+ float stop = range[1];
3875
+ float rangeSpan = stop - start;
3876
+
3877
+ float n = domainExtent[1] - domainExtent[0];
3878
+
3879
+ // This fix departs from Vega and d3: https://github.com/vega/vega/issues/3357#issuecomment-1063253596
3880
+ paddingInner = int(n) > 1 ? paddingInner : 0.0;
3881
+
3882
+ // Adapted from: https://github.com/d3/d3-scale/blob/master/src/band.js
3883
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
3884
+ start += (rangeSpan - step * (n - paddingInner)) * align;
3885
+ float bandwidth = step * (1.0 - paddingInner);
3886
+
3887
+ return start + (float(value) - domainExtent[0]) * step + bandwidth * band;
3888
+ }
3889
+
3890
+ const int lowBits = 12;
3891
+ const float lowDivisor = pow(2.0, float(lowBits));
3892
+ const uint lowMask = uint(lowDivisor - 1.0);
3893
+
3894
+ vec2 splitUint(uint value) {
3895
+ uint valueLo = value & lowMask;
3896
+ uint valueHi = value - valueLo;
3897
+ return vec2(float(valueHi), float(valueLo));
3898
+ }
3899
+
3900
+ /**
3901
+ * High precision variant of scaleBand for index/locus scales
3902
+ */
3903
+ float scaleBandHp(uint value, vec3 domainExtent, vec2 range,
3904
+ float paddingInner, float paddingOuter,
3905
+ float align, float band) {
3906
+
3907
+ // TODO: reverse
3908
+ float start = range[0];
3909
+ float stop = range[1];
3910
+ float rangeSpan = stop - start;
3911
+
3912
+ vec2 domainStart = domainExtent.xy;
3913
+ float n = domainExtent[2];
3914
+
3915
+ // The following computation is identical for every vertex. Could be done on the JS side.
3916
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
3917
+ start += (rangeSpan - step * (n - paddingInner)) * align;
3918
+ float bandwidth = step * (1.0 - paddingInner);
3919
+
3920
+ // Split into to values with each having a reduced number of significant digits
3921
+ // to mitigate the lack of precision in float32 calculations.
3922
+ vec2 splitValue = splitUint(value);
3923
+
3924
+ // Using max to prevent the shader compiler from wrecking the precision.
3925
+ // Othwewise the compiler could optimize the sum of the four terms into
3926
+ // some equivalent form that does premature rounding.
3927
+ float inf = 1.0 / uZero;
3928
+ float hi = max(splitValue[0] - domainStart[0], -inf);
3929
+ float lo = max(splitValue[1] - domainStart[1], -inf);
3930
+
3931
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
3932
+ }
3933
+
3934
+ /**
3935
+ * High precision variant of scaleBand for index/locus scales for large
3936
+ * domains where 32bit uints are not sufficient to represent the domain.
3937
+ */
3938
+ float scaleBandHp(uvec2 value, vec3 domainExtent, vec2 range,
3939
+ float paddingInner, float paddingOuter,
3940
+ float align, float band) {
3941
+
3942
+ // TODO: reverse
3943
+ float start = range[0];
3944
+ float stop = range[1];
3945
+ float rangeSpan = stop - start;
3946
+
3947
+ vec2 domainStart = domainExtent.xy;
3948
+ float n = domainExtent[2];
3949
+
3950
+ // The following computation is identical for every vertex. Could be done on the JS side.
3951
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
3952
+ start += (rangeSpan - step * (n - paddingInner)) * align;
3953
+ float bandwidth = step * (1.0 - paddingInner);
3954
+
3955
+ // Split into to values with each having a reduced number of significant digits
3956
+ // to mitigate the lack of precision in float32 calculations.
3957
+ vec2 splitValue = vec2(float(value[0]) * lowDivisor, float(value[1]));
3958
+
3959
+ // Using max to prevent the shader compiler from wrecking the precision.
3960
+ // Othwewise the compiler could optimize the sum of the four terms into
3961
+ // some equivalent form that does premature rounding.
3962
+ float inf = 1.0 / uZero;
3963
+ float hi = max(splitValue[0] - domainStart[0], -inf);
3964
+ float lo = max(splitValue[1] - domainStart[1], -inf);
3965
+
3966
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
3967
+ }
3968
+
3969
+
3970
+ in highp uint attr_uniqueId;
3971
+ in highp uint attr_x;
3972
+ in highp float attr_y;
3973
+ in highp uint attr_x2;
3974
+
3975
+ // Selection texture
3976
+ uniform highp usampler2D uSelectionTexture_select;
3977
+
3978
+ bool checkSelection_select(bool empty) {
3979
+ return hashContainsTexture(uSelectionTexture_select, attr_uniqueId) || (empty && isEmptyHashTexture(uSelectionTexture_select));
3980
+ }
3981
+
3982
+ bool checkSelection_highlight(bool empty) {
3983
+ return uParam_highlight == attr_uniqueId || (empty && uParam_highlight == 0u);
3984
+ }
3985
+
3986
+
3987
+ uint accessor_uniqueId_0() {
3988
+ return attr_uniqueId;
3989
+ }
3990
+
3991
+ uint getScaled_uniqueId() {
3992
+ return accessor_uniqueId_0();
3993
+ }
3994
+
3995
+ #define uniqueId_DEFINED
3996
+
3997
+
3998
+ vec3 accessor_fill_0() {
3999
+ // Constant value
4000
+ return vec3(0.2980392156862745, 0.47058823529411764, 0.6588235294117647);
4001
+ }
4002
+
4003
+ vec3 getScaled_fill() {
4004
+ return accessor_fill_0();
4005
+ }
4006
+
4007
+ #define fill_DEFINED
4008
+
4009
+
4010
+ vec3 accessor_stroke_0() {
4011
+ // Constant value
4012
+ return vec3(0.0, 0.0, 0.0);
4013
+ }
4014
+
4015
+ vec3 getScaled_stroke() {
4016
+ return accessor_stroke_0();
4017
+ }
4018
+
4019
+ #define stroke_DEFINED
4020
+
4021
+
4022
+ float accessor_strokeWidth_0() {
4023
+ // Constant value
4024
+ return float(2.0);
4025
+ }
4026
+
4027
+
4028
+ float accessor_strokeWidth_1() {
4029
+ // Constant value
4030
+ return float(1.0);
4031
+ }
4032
+
4033
+
4034
+ float accessor_strokeWidth_2() {
4035
+ // Constant value
4036
+ return float(0.0);
4037
+ }
4038
+
4039
+ float getScaled_strokeWidth() {
4040
+ if (checkSelection_select(false)) {
4041
+ return accessor_strokeWidth_0();
4042
+ }
4043
+ else if (checkSelection_highlight(false)) {
4044
+ return accessor_strokeWidth_1();
4045
+ }
4046
+ else {
4047
+ return accessor_strokeWidth_2();
4048
+ }
4049
+ }
4050
+
4051
+ #define strokeWidth_DEFINED
4052
+
4053
+
4054
+ uint accessor_x_0() {
4055
+ return attr_x;
4056
+ }
4057
+
4058
+
4059
+ //////////////////////////////////////////////////////////////////////
4060
+ // Channel: x
4061
+
4062
+ const vec2 range_x = vec2(0.0, 1.0);
4063
+
4064
+ float scale_x(uint value) {
4065
+ int slot = 0;
4066
+ vec2 domain = vec2(uDomain_x[slot], uDomain_x[slot + 1]);
4067
+ float transformed = scaleBand(value, domain, range_x, 0.2, 0.2, 0.5, 0.0);
4068
+ return transformed;
4069
+
4070
+ }
4071
+
4072
+ float getScaled_x() {
4073
+ return scale_x(accessor_x_0());
4074
+ }
4075
+
4076
+ #define x_DEFINED
4077
+
4078
+
4079
+ float accessor_y_0() {
4080
+ return attr_y;
4081
+ }
4082
+
4083
+
4084
+ //////////////////////////////////////////////////////////////////////
4085
+ // Channel: y
4086
+
4087
+ const vec2 range_y = vec2(0.0, 1.0);
4088
+
4089
+ float scale_y(float value) {
4090
+ int slot = 0;
4091
+ vec2 domain = vec2(uDomain_y[slot], uDomain_y[slot + 1]);
4092
+ float transformed = scaleLinear(value, domain, range_y);
4093
+ return transformed;
4094
+
4095
+ }
4096
+
4097
+ float getScaled_y() {
4098
+ return scale_y(accessor_y_0());
4099
+ }
4100
+
4101
+ #define y_DEFINED
4102
+
4103
+
4104
+ float accessor_fillOpacity_0() {
4105
+ // Constant value
4106
+ return float(1.0);
4107
+ }
4108
+
4109
+
4110
+ float accessor_fillOpacity_1() {
4111
+ // Constant value
4112
+ return float(0.3);
4113
+ }
4114
+
4115
+ float getScaled_fillOpacity() {
4116
+ if (checkSelection_select(true)) {
4117
+ return accessor_fillOpacity_0();
4118
+ }
4119
+ else {
4120
+ return accessor_fillOpacity_1();
4121
+ }
4122
+ }
4123
+
4124
+ #define fillOpacity_DEFINED
4125
+
4126
+
4127
+ uint accessor_x2_0() {
4128
+ return attr_x2;
4129
+ }
4130
+
4131
+
4132
+ //////////////////////////////////////////////////////////////////////
4133
+ // Channel: x2
4134
+
4135
+
4136
+ float scale_x2(uint value) {
4137
+ int slot = 0;
4138
+ vec2 domain = vec2(uDomain_x[slot], uDomain_x[slot + 1]);
4139
+ float transformed = scaleBand(value, domain, range_x, 0.2, 0.2, 0.5, 1.0);
4140
+ return transformed;
4141
+
4142
+ }
4143
+
4144
+ float getScaled_x2() {
4145
+ return scale_x2(accessor_x2_0());
4146
+ }
4147
+
4148
+ #define x2_DEFINED
4149
+
4150
+
4151
+ float accessor_y2_0() {
4152
+ return attr_y2;
4153
+ }
4154
+
4155
+
4156
+ //////////////////////////////////////////////////////////////////////
4157
+ // Channel: y2
4158
+
4159
+
4160
+ float scale_y2(float value) {
4161
+ int slot = 0;
4162
+ vec2 domain = vec2(uDomain_y[slot], uDomain_y[slot + 1]);
4163
+ float transformed = scaleLinear(value, domain, range_y);
4164
+ return transformed;
4165
+
4166
+ }
4167
+
4168
+ float getScaled_y2() {
4169
+ return scale_y2(accessor_y2_0());
4170
+ }
4171
+
4172
+ #define y2_DEFINED
4173
+
4174
+
4175
+ float accessor_strokeOpacity_0() {
4176
+ // Constant value
4177
+ return float(1.0);
4178
+ }
4179
+
4180
+ float getScaled_strokeOpacity() {
4181
+ return accessor_strokeOpacity_0();
4182
+ }
4183
+
4184
+ #define strokeOpacity_DEFINED
4185
+
4186
+ bool isPointSelected() {
4187
+ return checkSelection_select(false) || checkSelection_highlight(false);
4188
+ }
4189
+
4190
+
4191
+ /**
4192
+ * Describes where a sample facet should be shown. Interpolating between the
4193
+ * current and target positions/heights allows for transitioning between facet
4194
+ * configurations.
4195
+ */
4196
+ struct SampleFacetPosition {
4197
+ float pos;
4198
+ float height;
4199
+ float targetPos;
4200
+ float targetHeight;
4201
+ };
4202
+
4203
+ /**
4204
+ * Trasition fraction [0, 1] between the current and target configurations.
4205
+ */
4206
+ uniform float uTransitionOffset;
4207
+
4208
+
4209
+ // ----------------------------------------------------------------------------
4210
+
4211
+ #if !defined(SAMPLE_FACET_UNIFORM) && !defined(SAMPLE_FACET_TEXTURE)
4212
+
4213
+ SampleFacetPosition getSampleFacetPos() {
4214
+ return SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
4215
+ }
4216
+
4217
+ #elif defined(SAMPLE_FACET_UNIFORM)
4218
+
4219
+ /**
4220
+ * Location and height of the band on the Y axis on a normalized [0, 1] scale.
4221
+ * Elements: curr pos, curr height, target pos, target height
4222
+ */
4223
+ uniform vec4 uSampleFacet;
4224
+
4225
+ SampleFacetPosition getSampleFacetPos() {
4226
+ return SampleFacetPosition(
4227
+ 1.0 - uSampleFacet.x - uSampleFacet.y,
4228
+ uSampleFacet.y,
4229
+ 1.0 - uSampleFacet.z - uSampleFacet.w,
4230
+ uSampleFacet.w
4231
+ );
4232
+ }
4233
+
4234
+ #elif defined(SAMPLE_FACET_TEXTURE)
4235
+
4236
+ uniform sampler2D uSampleFacetTexture;
4237
+
4238
+ SampleFacetPosition getSampleFacetPos() {
4239
+ vec4 texel = texelFetch(uSampleFacetTexture, ivec2(int(attr_facetIndex), 0), 0);
4240
+ return SampleFacetPosition(
4241
+ 1.0 - texel.r - texel.g,
4242
+ texel.g,
4243
+ 1.0 - texel.r - texel.g,
4244
+ texel.g);
4245
+ }
4246
+
4247
+ #endif
4248
+
4249
+ // ----------------------------------------------------------------------------
4250
+
4251
+ bool isFacetedSamples(SampleFacetPosition facetPos) {
4252
+ return facetPos != SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
4253
+ }
4254
+
4255
+ bool isFacetedSamples() {
4256
+ return isFacetedSamples(getSampleFacetPos());
4257
+ }
4258
+
4259
+ bool isInTransit() {
4260
+ return uTransitionOffset > 0.0;
4261
+ }
4262
+
4263
+ float getTransitionFraction(float xPos) {
4264
+ return smoothstep(0.0, 0.7 + uTransitionOffset, (xPos - uTransitionOffset) * 2.0);
4265
+ }
4266
+
4267
+ vec2 applySampleFacet(vec2 pos) {
4268
+ SampleFacetPosition facetPos = getSampleFacetPos();
4269
+
4270
+ if (!isFacetedSamples(facetPos)) {
4271
+ return pos;
4272
+ } else if (isInTransit()) {
4273
+ vec2 interpolated = mix(
4274
+ vec2(facetPos.pos, facetPos.height),
4275
+ vec2(facetPos.targetPos, facetPos.targetHeight),
4276
+ getTransitionFraction(pos.x));
4277
+ return vec2(pos.x, interpolated[0] + pos.y * interpolated[1]);
4278
+ } else {
4279
+ return vec2(pos.x, facetPos.pos + pos.y * facetPos.height);
4280
+ }
4281
+ }
4282
+
4283
+ float getSampleFacetHeight(vec2 pos) {
4284
+ SampleFacetPosition facetPos = getSampleFacetPos();
4285
+
4286
+ if (!isFacetedSamples(facetPos)) {
4287
+ return 1.0;
4288
+ } else if (isInTransit()) {
4289
+ return mix(
4290
+ facetPos.height,
4291
+ facetPos.targetHeight,
4292
+ getTransitionFraction(pos.x));
4293
+ } else {
4294
+ return facetPos.height;
4295
+ }
4296
+ }
4297
+
4298
+
4299
+ /*
4300
+ * Based on concepts presented at:
4301
+ * https://webglfundamentals.org/webgl/lessons/webgl-picking.html
4302
+ * https://deck.gl/docs/developer-guide/custom-layers/picking
4303
+ */
4304
+
4305
+ out highp vec4 vPickingColor;
4306
+
4307
+ /**
4308
+ * Passes the unique id to the fragment shader as a color if picking is enabled.
4309
+ * Returns true if picking is enabled.
4310
+ */
4311
+ bool setupPicking() {
4312
+ if (uPickingEnabled) {
4313
+ #ifdef uniqueId_DEFINED
4314
+ uint id = attr_uniqueId;
4315
+ vPickingColor = vec4(
4316
+ ivec4(id >> 0, id >> 8, id >> 16, id >> 24) & 0xFF
4317
+ ) / float(0xFF);
4318
+ #else
4319
+ vPickingColor = vec4(1.0);
4320
+ #endif
4321
+ return true;
4322
+ }
4323
+ return false;
4324
+ }
4325
+
4326
+
4327
+ flat out lowp vec4 vFillColor;
4328
+ flat out lowp vec4 vStrokeColor;
4329
+ flat out float vHalfStrokeWidth;
4330
+ flat out vec4 vCornerRadii;
4331
+
4332
+ #if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)
4333
+ /** Position for SDF-strokes */
4334
+ out vec2 vPosInPixels;
4335
+ #endif
4336
+
4337
+ /** Size of the rect in pixels */
4338
+ flat out vec2 vHalfSizeInPixels;
4339
+
4340
+ /**
4341
+ * Adjusts the vertex position to ensure that the rectangle is at least \`minSpan\`
4342
+ * wide or high. Returns a value that reflects the amount of clamping and can be
4343
+ * used to adjust the opacity of the rectangle.
4344
+ *
4345
+ * pos: vertex position
4346
+ * frac: vertex position within the rectangle, [0, 1]
4347
+ * size: width or height of the rectangle
4348
+ * minSize: minimum width or height of the rectangle
4349
+ */
4350
+ float clampMinSize(inout float pos, float frac, float size, float minSize) {
4351
+ if (minSize > 0.0 && size < minSize) {
4352
+ pos += (frac - 0.5) * (minSize - size);
4353
+ return size / minSize;
4354
+ }
4355
+
4356
+ return 1.0;
4357
+ }
4358
+
4359
+ void sort(inout float a, inout float b) {
4360
+ if (a > b) {
4361
+ float tmp = b;
4362
+ b = a;
4363
+ a = tmp;
4364
+ }
4365
+ }
4366
+
4367
+ /**
4368
+ * The vertex position wrt the rectangle specified by (x, x2, y, y2).
4369
+ * [0, 0] = [x, y], [1, 1] = [x2, y2].
4370
+ * The x or y component may contain fractional values if the rectangle
4371
+ * have been tessellated.
4372
+ */
4373
+ vec2 getVertexPos() {
4374
+ int index = gl_VertexID % 6;
4375
+ return vec2(
4376
+ index == 0 || index == 1 || index == 3 ? 0.0 : 1.0,
4377
+ index == 0 || index == 1 || index == 2 ? 0.0 : 1.0
4378
+ );
4379
+
4380
+ }
4381
+
4382
+ void main(void) {
4383
+ vec2 frac = getVertexPos();
4384
+
4385
+ vec2 normalizedMinSize = vec2(uMinWidth, uMinHeight) / uViewportSize;
4386
+ vec4 cornerRadii = vec4(
4387
+ uCornerRadiusTopRight,
4388
+ uCornerRadiusBottomRight,
4389
+ uCornerRadiusTopLeft,
4390
+ uCornerRadiusBottomLeft
4391
+ );
4392
+
4393
+ float x = getScaled_x();
4394
+ float x2 = getScaled_x2();
4395
+ float y = getScaled_y();
4396
+ float y2 = getScaled_y2();
4397
+
4398
+ sort(x, x2);
4399
+ sort(y, y2);
4400
+
4401
+ // Clamp x to prevent precision artifacts when the scale is zoomed very close.
4402
+ // TODO: clamp y as well
4403
+ float clampMargin = 1.0;
4404
+ vec2 pos1 = vec2(clamp(x, 0.0 - clampMargin, 1.0 + clampMargin), y);
4405
+ vec2 pos2 = vec2(clamp(x2, 0.0 - clampMargin, 1.0 + clampMargin), y2);
4406
+
4407
+ vec2 size = pos2 - pos1;
4408
+
4409
+ if (size.x <= 0.0 || size.y <= 0.0) {
4410
+ // Early exit. May increase performance or not...
4411
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
4412
+ return;
4413
+ }
4414
+
4415
+ vec2 pos = pos1 + frac * size;
4416
+
4417
+ size.y *= getSampleFacetHeight(pos);
4418
+
4419
+ // Clamp to minimum size, optionally compensate with opacity
4420
+ float opaFactor = uViewOpacity * max(uMinOpacity,
4421
+ clampMinSize(pos.x, frac.x, size.x, normalizedMinSize.x) *
4422
+ clampMinSize(pos.y, frac.y, size.y, normalizedMinSize.y));
4423
+
4424
+ size = max(size, normalizedMinSize);
4425
+
4426
+ pos = applySampleFacet(pos);
4427
+
4428
+ #if defined(ROUNDED_CORNERS) || defined(STROKED) || defined(SHADOW)
4429
+ // Add an extra pixel to the stroke width to accommodate edge antialiasing
4430
+ float aaPadding = 1.0 / uDevicePixelRatio;
4431
+
4432
+ // TODO: Only expand to the offset direction. Now high offsets result in
4433
+ // a large expansion in all directions.
4434
+ float shadowPadding = uShadowBlur + max(abs(uShadowOffsetX), abs(uShadowOffsetY));
4435
+
4436
+ float strokeWidth = getScaled_strokeWidth();
4437
+ float strokeOpacity = getScaled_strokeOpacity() * opaFactor;
4438
+
4439
+ vec2 centeredFrac = frac - 0.5;
4440
+ vec2 expand = centeredFrac * (strokeWidth + aaPadding + shadowPadding * 2.0) / uViewportSize;
4441
+ pos += expand;
4442
+
4443
+ vec2 sizeInPixels = size * uViewportSize;
4444
+ vPosInPixels = (centeredFrac + expand / size) * sizeInPixels;
4445
+
4446
+ vHalfSizeInPixels = sizeInPixels / 2.0;
4447
+
4448
+ vCornerRadii = min(cornerRadii, min(vHalfSizeInPixels.x, vHalfSizeInPixels.y));
4449
+ vHalfStrokeWidth = strokeWidth / 2.0;
4450
+ vStrokeColor = vec4(getScaled_stroke() * strokeOpacity, strokeOpacity);
4451
+ #endif
4452
+
4453
+ gl_Position = unitToNdc(pos);
4454
+
4455
+ float fillOpacity = getScaled_fillOpacity() * opaFactor;
4456
+ vFillColor = vec4(getScaled_fill() * fillOpacity, fillOpacity);
4457
+
4458
+ setupPicking();
4459
+ }
4460
+ ",
4461
+ }
4462
+ `;