@genome-spy/core 0.74.0 → 0.76.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 (143) hide show
  1. package/dist/bundle/{esm-CgfVIRJ-.js → esm-BimDEpBb.js} +1 -1
  2. package/dist/bundle/{esm-DtE8VqAv.js → esm-Bvlm1uVk.js} +1 -1
  3. package/dist/bundle/{esm-sIoQYZ21.js → esm-CngqBe45.js} +17 -17
  4. package/dist/bundle/{esm-DQiq2Zhd.js → esm-D_euN86T.js} +43 -43
  5. package/dist/bundle/index.es.js +6064 -5756
  6. package/dist/bundle/index.js +104 -103
  7. package/dist/schema.json +572 -12
  8. package/dist/src/config/defaults/markDefaults.d.ts.map +1 -1
  9. package/dist/src/config/defaults/markDefaults.js +1 -12
  10. package/dist/src/config/defaults/scaleDefaults.d.ts.map +1 -1
  11. package/dist/src/config/defaults/scaleDefaults.js +1 -0
  12. package/dist/src/config/markConfig.d.ts.map +1 -1
  13. package/dist/src/config/markConfig.js +16 -8
  14. package/dist/src/config/themes.d.ts.map +1 -1
  15. package/dist/src/config/themes.js +15 -2
  16. package/dist/src/data/sources/dataUtils.d.ts +25 -0
  17. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  18. package/dist/src/data/sources/dataUtils.js +23 -0
  19. package/dist/src/data/sources/inlineSource.js +2 -2
  20. package/dist/src/data/sources/lazy/registerBuiltInLazySources.js +2 -2
  21. package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts +2 -0
  22. package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts.map +1 -0
  23. package/dist/src/data/sources/lazy/registerCoreLazySources.js +2 -0
  24. package/dist/src/data/sources/lazy/tabixSource.d.ts +7 -0
  25. package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
  26. package/dist/src/data/sources/lazy/tabixSource.js +18 -0
  27. package/dist/src/data/sources/lazy/tabixTsvSource.d.ts +37 -0
  28. package/dist/src/data/sources/lazy/tabixTsvSource.d.ts.map +1 -0
  29. package/dist/src/data/sources/lazy/tabixTsvSource.js +163 -0
  30. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  31. package/dist/src/data/sources/urlSource.js +8 -3
  32. package/dist/src/encoder/encoder.d.ts +2 -2
  33. package/dist/src/encoder/encoder.d.ts.map +1 -1
  34. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  35. package/dist/src/genome/scaleLocus.js +8 -3
  36. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  37. package/dist/src/genomeSpy/interactionController.js +91 -51
  38. package/dist/src/genomeSpyBase.d.ts.map +1 -1
  39. package/dist/src/genomeSpyBase.js +4 -1
  40. package/dist/src/gl/dataToVertices.d.ts +12 -14
  41. package/dist/src/gl/dataToVertices.d.ts.map +1 -1
  42. package/dist/src/gl/dataToVertices.js +116 -95
  43. package/dist/src/gl/glslScaleGenerator.d.ts +3 -0
  44. package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
  45. package/dist/src/gl/glslScaleGenerator.js +10 -8
  46. package/dist/src/gl/vertexRangeIndex.d.ts +23 -0
  47. package/dist/src/gl/vertexRangeIndex.d.ts.map +1 -0
  48. package/dist/src/gl/vertexRangeIndex.js +150 -0
  49. package/dist/src/gl/webGLHelper.d.ts +5 -2
  50. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  51. package/dist/src/gl/webGLHelper.js +20 -3
  52. package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +1082 -0
  53. package/dist/src/marks/link.vertex.glsl.js +1 -1
  54. package/dist/src/marks/mark.d.ts +1 -1
  55. package/dist/src/minimal.d.ts.map +1 -1
  56. package/dist/src/minimal.js +5 -4
  57. package/dist/src/paramRuntime/expressionCompiler.d.ts +2 -1
  58. package/dist/src/paramRuntime/expressionCompiler.d.ts.map +1 -1
  59. package/dist/src/paramRuntime/expressionCompiler.js +3 -2
  60. package/dist/src/paramRuntime/expressionRef.d.ts +4 -1
  61. package/dist/src/paramRuntime/expressionRef.d.ts.map +1 -1
  62. package/dist/src/paramRuntime/expressionRef.js +10 -3
  63. package/dist/src/paramRuntime/graphRuntime.d.ts.map +1 -1
  64. package/dist/src/paramRuntime/graphRuntime.js +15 -6
  65. package/dist/src/paramRuntime/paramRuntime.d.ts +8 -2
  66. package/dist/src/paramRuntime/paramRuntime.d.ts.map +1 -1
  67. package/dist/src/paramRuntime/paramRuntime.js +10 -5
  68. package/dist/src/paramRuntime/types.d.ts +1 -0
  69. package/dist/src/paramRuntime/types.d.ts.map +1 -1
  70. package/dist/src/paramRuntime/types.js +1 -0
  71. package/dist/src/paramRuntime/viewParamRuntime.d.ts +5 -4
  72. package/dist/src/paramRuntime/viewParamRuntime.d.ts.map +1 -1
  73. package/dist/src/paramRuntime/viewParamRuntime.js +17 -6
  74. package/dist/src/scale/scale.d.ts.map +1 -1
  75. package/dist/src/scale/scale.js +11 -2
  76. package/dist/src/scales/domainPlanner.d.ts +57 -11
  77. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  78. package/dist/src/scales/domainPlanner.js +183 -84
  79. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  80. package/dist/src/scales/scaleInstanceManager.js +7 -2
  81. package/dist/src/scales/scalePropsResolver.d.ts +3 -3
  82. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
  83. package/dist/src/scales/scalePropsResolver.js +28 -5
  84. package/dist/src/scales/scaleResolution.d.ts +12 -1
  85. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  86. package/dist/src/scales/scaleResolution.js +180 -21
  87. package/dist/src/scales/selectionDomainUtils.d.ts +10 -0
  88. package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -1
  89. package/dist/src/scales/selectionDomainUtils.js +32 -3
  90. package/dist/src/screenshotExport.d.ts +23 -0
  91. package/dist/src/screenshotExport.d.ts.map +1 -0
  92. package/dist/src/screenshotExport.js +44 -0
  93. package/dist/src/screenshotHarness.d.ts.map +1 -1
  94. package/dist/src/screenshotHarness.js +26 -24
  95. package/dist/src/spec/axis.d.ts +2 -2
  96. package/dist/src/spec/channel.d.ts +34 -4
  97. package/dist/src/spec/data.d.ts +52 -0
  98. package/dist/src/spec/parameter.d.ts +6 -0
  99. package/dist/src/spec/scale.d.ts +13 -1
  100. package/dist/src/spec/transform.d.ts +6 -0
  101. package/dist/src/utils/expression.d.ts +16 -8
  102. package/dist/src/utils/expression.d.ts.map +1 -1
  103. package/dist/src/utils/expression.js +291 -11
  104. package/dist/src/view/axisGridView.d.ts.map +1 -1
  105. package/dist/src/view/axisGridView.js +2 -1
  106. package/dist/src/view/axisView.d.ts.map +1 -1
  107. package/dist/src/view/axisView.js +2 -1
  108. package/dist/src/view/facetView.d.ts.map +1 -1
  109. package/dist/src/view/facetView.js +2 -1
  110. package/dist/src/view/flowBuilder.d.ts +1 -1
  111. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  112. package/dist/src/view/flowBuilder.js +11 -7
  113. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  114. package/dist/src/view/gridView/gridChild.js +9 -1
  115. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  116. package/dist/src/view/gridView/gridView.js +198 -32
  117. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  118. package/dist/src/view/gridView/scrollbar.js +5 -1
  119. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  120. package/dist/src/view/gridView/selectionRect.js +5 -1
  121. package/dist/src/view/gridView/separatorView.d.ts.map +1 -1
  122. package/dist/src/view/gridView/separatorView.js +5 -1
  123. package/dist/src/view/resolutionPlanner.d.ts +9 -0
  124. package/dist/src/view/resolutionPlanner.d.ts.map +1 -0
  125. package/dist/src/view/resolutionPlanner.js +302 -0
  126. package/dist/src/view/testUtils.d.ts +30 -3
  127. package/dist/src/view/testUtils.d.ts.map +1 -1
  128. package/dist/src/view/testUtils.js +51 -2
  129. package/dist/src/view/unitView.d.ts +1 -1
  130. package/dist/src/view/unitView.d.ts.map +1 -1
  131. package/dist/src/view/unitView.js +5 -152
  132. package/dist/src/view/view.d.ts.map +1 -1
  133. package/dist/src/view/view.js +2 -1
  134. package/dist/src/view/viewSelectors.d.ts +38 -10
  135. package/dist/src/view/viewSelectors.d.ts.map +1 -1
  136. package/dist/src/view/viewSelectors.js +67 -2
  137. package/dist/src/view/viewUtilTypes.d.ts +15 -0
  138. package/dist/src/view/viewUtils.d.ts.map +1 -1
  139. package/dist/src/view/viewUtils.js +10 -0
  140. package/package.json +2 -2
  141. package/LICENSE +0 -21
  142. /package/dist/bundle/{esm-BDFRLEuD.js → esm-C49STiCR.js} +0 -0
  143. /package/dist/bundle/{esm-CGX-qz1d.js → esm-CuVa5T98.js} +0 -0
@@ -1068,6 +1068,1088 @@ void main(void) {
1068
1068
  }
1069
1069
  `;
1070
1070
 
1071
+ exports[`generated shader snapshots > link mark control spec 1`] = `
1072
+ {
1073
+ "fragment": "precision highp float;
1074
+ precision highp int;
1075
+
1076
+ // view: viewRoot
1077
+
1078
+ layout(std140) uniform Mark {
1079
+ uniform float uArcHeightFactor;
1080
+
1081
+ /** Make very small arcs visible */
1082
+ uniform float uMinArcHeight;
1083
+
1084
+ /** The minimum stroke width in pixels when rendering into the picking buffer */
1085
+ uniform float uMinPickingSize;
1086
+
1087
+ uniform int uShape;
1088
+ uniform int uOrient;
1089
+ uniform bool uClampApex;
1090
+
1091
+ // In pixels
1092
+ uniform float uMaxChordLength;
1093
+ // In pixels
1094
+ uniform vec2 uArcFadingDistance;
1095
+ uniform bool uNoFadingOnPointSelection;
1096
+
1097
+ uniform int uSegmentBreaks;
1098
+
1099
+ mediump float uDomain_x[2];
1100
+
1101
+ mediump float uDomain_y[2];
1102
+
1103
+
1104
+
1105
+
1106
+
1107
+ };
1108
+
1109
+
1110
+ #define PI 3.141593
1111
+
1112
+ uniform View {
1113
+ /** Offset in "unit" units */
1114
+ mediump vec2 uViewOffset;
1115
+ mediump vec2 uViewScale;
1116
+ /** Size of the logical viewport in pixels, i.e., the view */
1117
+ mediump vec2 uViewportSize;
1118
+ lowp float uDevicePixelRatio;
1119
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
1120
+ // that is rendered with the specified opacity.
1121
+ lowp float uViewOpacity;
1122
+ bool uPickingEnabled;
1123
+ };
1124
+
1125
+
1126
+ /**
1127
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
1128
+ * (0, 0) is at the bottom left corner.
1129
+ */
1130
+ vec4 unitToNdc(vec2 coord) {
1131
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
1132
+ }
1133
+
1134
+ vec4 unitToNdc(float x, float y) {
1135
+ return unitToNdc(vec2(x, y));
1136
+ }
1137
+
1138
+ vec4 pixelsToNdc(vec2 coord) {
1139
+ return unitToNdc(coord / uViewportSize);
1140
+ }
1141
+
1142
+ vec4 pixelsToNdc(float x, float y) {
1143
+ return pixelsToNdc(vec2(x, y));
1144
+ }
1145
+
1146
+ float linearstep(float edge0, float edge1, float x) {
1147
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
1148
+ }
1149
+
1150
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
1151
+
1152
+ highp uint hash32(highp uint key) {
1153
+ highp uint v = key;
1154
+ v ^= v >> 16u;
1155
+ v *= 0x7feb352du;
1156
+ v ^= v >> 15u;
1157
+ v *= 0x846ca68bu;
1158
+ v ^= v >> 16u;
1159
+ return v;
1160
+ }
1161
+
1162
+ bool isEmptyHashTexture(highp usampler2D s) {
1163
+ // Empty selections are encoded as a single empty hash slot.
1164
+ ivec2 texSize = textureSize(s, 0);
1165
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
1166
+ }
1167
+
1168
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
1169
+ ivec2 texSize = textureSize(s, 0);
1170
+ highp uint width = uint(texSize.x);
1171
+ highp uint size = width * uint(texSize.y);
1172
+ highp uint mask = size - 1u;
1173
+ highp uint index = hash32(value) & mask;
1174
+
1175
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
1176
+ ivec2 coord = ivec2(int(index % width), int(index / width));
1177
+ highp uint entry = texelFetch(s, coord, 0).r;
1178
+ if (entry == value) {
1179
+ return true;
1180
+ }
1181
+ if (entry == HASH_EMPTY_KEY) {
1182
+ return false;
1183
+ }
1184
+ index = (index + 1u) & mask;
1185
+ }
1186
+
1187
+ return false;
1188
+ }
1189
+
1190
+ /**
1191
+ * Calculates a gamma for antialiasing opacity based on the color.
1192
+ */
1193
+ float getGammaForColor(vec3 rgb) {
1194
+ return mix(
1195
+ 1.25,
1196
+ 0.75,
1197
+ // RGB should be linearized but this is good enough for now
1198
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
1199
+ }
1200
+
1201
+ // Fragment shader stuff ////////////////////////////////////////////////////////
1202
+
1203
+ // TODO: include the following only in fragment shaders
1204
+
1205
+ /**
1206
+ * Specialized linearstep for doing antialiasing
1207
+ */
1208
+ float distanceToRatio(float d) {
1209
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
1210
+ }
1211
+
1212
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
1213
+ if (halfStrokeWidth > 0.0) {
1214
+ // Distance to stroke's edge. Negative inside the stroke.
1215
+ float sd = abs(d) - halfStrokeWidth;
1216
+ return mix(
1217
+ stroke,
1218
+ d <= 0.0 ? fill : background,
1219
+ distanceToRatio(sd));
1220
+ } else {
1221
+ return mix(background, fill, distanceToRatio(-d));
1222
+ }
1223
+ }
1224
+
1225
+
1226
+ in highp vec4 vPickingColor;
1227
+
1228
+
1229
+ flat in vec4 vColor;
1230
+ flat in float vSize;
1231
+ in float vNormalLengthInPixels;
1232
+ flat in float vGamma;
1233
+
1234
+ out lowp vec4 fragColor;
1235
+
1236
+ void main(void) {
1237
+ float dpr = uDevicePixelRatio;
1238
+
1239
+ float distance = abs(vNormalLengthInPixels);
1240
+ float opacity = clamp(((vSize / 2.0 - distance) * dpr), 0.0, 1.0);
1241
+
1242
+ opacity = pow(opacity, vGamma);
1243
+
1244
+ fragColor = vColor * opacity;
1245
+
1246
+ if (uPickingEnabled) {
1247
+ fragColor = vPickingColor;
1248
+ }
1249
+ }
1250
+ ",
1251
+ "vertex": "precision highp float;
1252
+ precision highp int;
1253
+
1254
+ // view: viewRoot
1255
+
1256
+ layout(std140) uniform Mark {
1257
+ uniform float uArcHeightFactor;
1258
+
1259
+ /** Make very small arcs visible */
1260
+ uniform float uMinArcHeight;
1261
+
1262
+ /** The minimum stroke width in pixels when rendering into the picking buffer */
1263
+ uniform float uMinPickingSize;
1264
+
1265
+ uniform int uShape;
1266
+ uniform int uOrient;
1267
+ uniform bool uClampApex;
1268
+
1269
+ // In pixels
1270
+ uniform float uMaxChordLength;
1271
+ // In pixels
1272
+ uniform vec2 uArcFadingDistance;
1273
+ uniform bool uNoFadingOnPointSelection;
1274
+
1275
+ uniform int uSegmentBreaks;
1276
+
1277
+ mediump float uDomain_x[2];
1278
+
1279
+ mediump float uDomain_y[2];
1280
+
1281
+
1282
+
1283
+
1284
+
1285
+ };
1286
+
1287
+
1288
+ #define PI 3.141593
1289
+
1290
+ uniform View {
1291
+ /** Offset in "unit" units */
1292
+ mediump vec2 uViewOffset;
1293
+ mediump vec2 uViewScale;
1294
+ /** Size of the logical viewport in pixels, i.e., the view */
1295
+ mediump vec2 uViewportSize;
1296
+ lowp float uDevicePixelRatio;
1297
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
1298
+ // that is rendered with the specified opacity.
1299
+ lowp float uViewOpacity;
1300
+ bool uPickingEnabled;
1301
+ };
1302
+
1303
+
1304
+ /**
1305
+ * Maps a coordinate on the unit scale to a normalized device coordinate.
1306
+ * (0, 0) is at the bottom left corner.
1307
+ */
1308
+ vec4 unitToNdc(vec2 coord) {
1309
+ return vec4((coord * uViewScale + uViewOffset) * 2.0 - 1.0, 0.0, 1.0);
1310
+ }
1311
+
1312
+ vec4 unitToNdc(float x, float y) {
1313
+ return unitToNdc(vec2(x, y));
1314
+ }
1315
+
1316
+ vec4 pixelsToNdc(vec2 coord) {
1317
+ return unitToNdc(coord / uViewportSize);
1318
+ }
1319
+
1320
+ vec4 pixelsToNdc(float x, float y) {
1321
+ return pixelsToNdc(vec2(x, y));
1322
+ }
1323
+
1324
+ float linearstep(float edge0, float edge1, float x) {
1325
+ return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
1326
+ }
1327
+
1328
+ const highp uint HASH_EMPTY_KEY = 0xffffffffu;
1329
+
1330
+ highp uint hash32(highp uint key) {
1331
+ highp uint v = key;
1332
+ v ^= v >> 16u;
1333
+ v *= 0x7feb352du;
1334
+ v ^= v >> 15u;
1335
+ v *= 0x846ca68bu;
1336
+ v ^= v >> 16u;
1337
+ return v;
1338
+ }
1339
+
1340
+ bool isEmptyHashTexture(highp usampler2D s) {
1341
+ // Empty selections are encoded as a single empty hash slot.
1342
+ ivec2 texSize = textureSize(s, 0);
1343
+ return texSize.x == 1 && texSize.y == 1 && texelFetch(s, ivec2(0, 0), 0).r == HASH_EMPTY_KEY;
1344
+ }
1345
+
1346
+ bool hashContainsTexture(highp usampler2D s, highp uint value) {
1347
+ ivec2 texSize = textureSize(s, 0);
1348
+ highp uint width = uint(texSize.x);
1349
+ highp uint size = width * uint(texSize.y);
1350
+ highp uint mask = size - 1u;
1351
+ highp uint index = hash32(value) & mask;
1352
+
1353
+ for (highp uint probe = 0u; probe < size; probe += 1u) {
1354
+ ivec2 coord = ivec2(int(index % width), int(index / width));
1355
+ highp uint entry = texelFetch(s, coord, 0).r;
1356
+ if (entry == value) {
1357
+ return true;
1358
+ }
1359
+ if (entry == HASH_EMPTY_KEY) {
1360
+ return false;
1361
+ }
1362
+ index = (index + 1u) & mask;
1363
+ }
1364
+
1365
+ return false;
1366
+ }
1367
+
1368
+ /**
1369
+ * Calculates a gamma for antialiasing opacity based on the color.
1370
+ */
1371
+ float getGammaForColor(vec3 rgb) {
1372
+ return mix(
1373
+ 1.25,
1374
+ 0.75,
1375
+ // RGB should be linearized but this is good enough for now
1376
+ smoothstep(0.0, 1.0, dot(rgb, vec3(0.299, 0.587, 0.114))));
1377
+ }
1378
+
1379
+ // Fragment shader stuff ////////////////////////////////////////////////////////
1380
+
1381
+ // TODO: include the following only in fragment shaders
1382
+
1383
+ /**
1384
+ * Specialized linearstep for doing antialiasing
1385
+ */
1386
+ float distanceToRatio(float d) {
1387
+ return clamp(d * uDevicePixelRatio + 0.5, 0.0, 1.0);
1388
+ }
1389
+
1390
+ vec4 distanceToColor(float d, vec4 fill, vec4 stroke, vec4 background, float halfStrokeWidth) {
1391
+ if (halfStrokeWidth > 0.0) {
1392
+ // Distance to stroke's edge. Negative inside the stroke.
1393
+ float sd = abs(d) - halfStrokeWidth;
1394
+ return mix(
1395
+ stroke,
1396
+ d <= 0.0 ? fill : background,
1397
+ distanceToRatio(sd));
1398
+ } else {
1399
+ return mix(background, fill, distanceToRatio(-d));
1400
+ }
1401
+ }
1402
+
1403
+
1404
+ uniform highp float uZero;
1405
+
1406
+ // Utils ------------
1407
+
1408
+ vec3 getDiscreteColor(sampler2D s, int index) {
1409
+ return texelFetch(s, ivec2(index % textureSize(s, 0).x, 0), 0).rgb;
1410
+ }
1411
+
1412
+ vec3 getInterpolatedColor(sampler2D s, float unitValue) {
1413
+ return texture(s, vec2(unitValue, 0.0)).rgb;
1414
+ }
1415
+
1416
+ float clampToRange(float value, vec2 range) {
1417
+ return clamp(value, min(range[0], range[1]), max(range[0], range[1]));
1418
+ }
1419
+
1420
+ // Scales ------------
1421
+ // Based on d3 scales: https://github.com/d3/d3-scale
1422
+
1423
+ float scaleIdentity(float value) {
1424
+ return value;
1425
+ }
1426
+
1427
+ float scaleIdentity(uint value) {
1428
+ return float(value);
1429
+ }
1430
+
1431
+ float scaleLinear(float value, vec2 domain, vec2 range) {
1432
+ float domainSpan = domain[1] - domain[0];
1433
+ float rangeSpan = range[1] - range[0];
1434
+ return (value - domain[0]) / domainSpan * rangeSpan + range[0];
1435
+ }
1436
+
1437
+ float scaleLog(float value, vec2 domain, vec2 range, float base) {
1438
+ // y = m log(x) + b
1439
+ // TODO: Perf optimization: precalculate log domain in js.
1440
+ // TODO: Reversed domain, etc
1441
+ return scaleLinear(log(value) / log(base), log(domain) / log(base), range);
1442
+ }
1443
+
1444
+ float symlog(float value, float constant) {
1445
+ // WARNING: emulating log1p with log(x + 1). Small numbers are likely to
1446
+ // have significant precision problems.
1447
+ return sign(value) * log(abs(value / constant) + 1.0);
1448
+ }
1449
+
1450
+ float scaleSymlog(float value, vec2 domain, vec2 range, float constant) {
1451
+ return scaleLinear(
1452
+ symlog(value, constant),
1453
+ vec2(symlog(domain[0], constant), symlog(domain[1], constant)),
1454
+ range
1455
+ );
1456
+ }
1457
+
1458
+ float scalePow(float value, vec2 domain, vec2 range, float exponent) {
1459
+ // y = mx^k + b
1460
+ // TODO: Perf optimization: precalculate pow domain in js.
1461
+ // TODO: Reversed domain, etc
1462
+ return scaleLinear(
1463
+ pow(abs(value), exponent) * sign(value),
1464
+ pow(abs(domain), vec2(exponent)) * sign(domain),
1465
+ range
1466
+ );
1467
+ }
1468
+
1469
+ // TODO: scaleThreshold
1470
+ // TODO: scaleQuantile (special case of threshold scale)
1471
+
1472
+ // TODO: domainExtent should be uint
1473
+ float scaleBand(uint value, vec2 domainExtent, vec2 range,
1474
+ float paddingInner, float paddingOuter,
1475
+ float align, float band) {
1476
+
1477
+ // TODO: reverse
1478
+ float start = range[0];
1479
+ float stop = range[1];
1480
+ float rangeSpan = stop - start;
1481
+
1482
+ float n = domainExtent[1] - domainExtent[0];
1483
+
1484
+ // This fix departs from Vega and d3: https://github.com/vega/vega/issues/3357#issuecomment-1063253596
1485
+ paddingInner = int(n) > 1 ? paddingInner : 0.0;
1486
+
1487
+ // Adapted from: https://github.com/d3/d3-scale/blob/master/src/band.js
1488
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
1489
+ start += (rangeSpan - step * (n - paddingInner)) * align;
1490
+ float bandwidth = step * (1.0 - paddingInner);
1491
+
1492
+ return start + (float(value) - domainExtent[0]) * step + bandwidth * band;
1493
+ }
1494
+
1495
+ const int lowBits = 12;
1496
+ const float lowDivisor = pow(2.0, float(lowBits));
1497
+ const uint lowMask = uint(lowDivisor - 1.0);
1498
+
1499
+ vec2 splitUint(uint value) {
1500
+ uint valueLo = value & lowMask;
1501
+ uint valueHi = value - valueLo;
1502
+ return vec2(float(valueHi), float(valueLo));
1503
+ }
1504
+
1505
+ /**
1506
+ * High precision variant of scaleBand for index/locus scales
1507
+ */
1508
+ float scaleBandHp(uint value, vec3 domainExtent, vec2 range,
1509
+ float paddingInner, float paddingOuter,
1510
+ float align, float band) {
1511
+
1512
+ // TODO: reverse
1513
+ float start = range[0];
1514
+ float stop = range[1];
1515
+ float rangeSpan = stop - start;
1516
+
1517
+ vec2 domainStart = domainExtent.xy;
1518
+ float n = domainExtent[2];
1519
+
1520
+ // The following computation is identical for every vertex. Could be done on the JS side.
1521
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
1522
+ start += (rangeSpan - step * (n - paddingInner)) * align;
1523
+ float bandwidth = step * (1.0 - paddingInner);
1524
+
1525
+ // Split into to values with each having a reduced number of significant digits
1526
+ // to mitigate the lack of precision in float32 calculations.
1527
+ vec2 splitValue = splitUint(value);
1528
+
1529
+ // Using max to prevent the shader compiler from wrecking the precision.
1530
+ // Othwewise the compiler could optimize the sum of the four terms into
1531
+ // some equivalent form that does premature rounding.
1532
+ float inf = 1.0 / uZero;
1533
+ float hi = max(splitValue[0] - domainStart[0], -inf);
1534
+ float lo = max(splitValue[1] - domainStart[1], -inf);
1535
+
1536
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
1537
+ }
1538
+
1539
+ /**
1540
+ * High precision variant of scaleBand for index/locus scales for large
1541
+ * domains where 32bit uints are not sufficient to represent the domain.
1542
+ */
1543
+ float scaleBandHp(uvec2 value, vec3 domainExtent, vec2 range,
1544
+ float paddingInner, float paddingOuter,
1545
+ float align, float band) {
1546
+
1547
+ // TODO: reverse
1548
+ float start = range[0];
1549
+ float stop = range[1];
1550
+ float rangeSpan = stop - start;
1551
+
1552
+ vec2 domainStart = domainExtent.xy;
1553
+ float n = domainExtent[2];
1554
+
1555
+ // The following computation is identical for every vertex. Could be done on the JS side.
1556
+ float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
1557
+ start += (rangeSpan - step * (n - paddingInner)) * align;
1558
+ float bandwidth = step * (1.0 - paddingInner);
1559
+
1560
+ // Split into to values with each having a reduced number of significant digits
1561
+ // to mitigate the lack of precision in float32 calculations.
1562
+ vec2 splitValue = vec2(float(value[0]) * lowDivisor, float(value[1]));
1563
+
1564
+ // Using max to prevent the shader compiler from wrecking the precision.
1565
+ // Othwewise the compiler could optimize the sum of the four terms into
1566
+ // some equivalent form that does premature rounding.
1567
+ float inf = 1.0 / uZero;
1568
+ float hi = max(splitValue[0] - domainStart[0], -inf);
1569
+ float lo = max(splitValue[1] - domainStart[1], -inf);
1570
+
1571
+ return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
1572
+ }
1573
+
1574
+
1575
+ in highp uint attr_uniqueId;
1576
+ in highp float attr_x;
1577
+ in highp float attr_y_y2;
1578
+ in highp float attr_x2;
1579
+
1580
+
1581
+ uint accessor_uniqueId_0() {
1582
+ return attr_uniqueId;
1583
+ }
1584
+
1585
+ uint getScaled_uniqueId() {
1586
+ return accessor_uniqueId_0();
1587
+ }
1588
+
1589
+ #define uniqueId_DEFINED
1590
+
1591
+
1592
+ float accessor_x_0() {
1593
+ return attr_x;
1594
+ }
1595
+
1596
+
1597
+ //////////////////////////////////////////////////////////////////////
1598
+ // Channel: x
1599
+
1600
+ const vec2 range_x = vec2(0.0, 1.0);
1601
+
1602
+ float scale_x(float value) {
1603
+ int slot = 0;
1604
+ vec2 domain = vec2(uDomain_x[slot], uDomain_x[slot + 1]);
1605
+ float transformed = scaleLinear(value, domain, range_x);
1606
+ return transformed;
1607
+
1608
+ }
1609
+
1610
+ float getScaled_x() {
1611
+ return scale_x(accessor_x_0());
1612
+ }
1613
+
1614
+ #define x_DEFINED
1615
+
1616
+
1617
+ float accessor_y_0() {
1618
+ return attr_y_y2;
1619
+ }
1620
+
1621
+
1622
+ //////////////////////////////////////////////////////////////////////
1623
+ // Channel: y
1624
+
1625
+ const vec2 range_y = vec2(0.0, 1.0);
1626
+
1627
+ float scale_y(float value) {
1628
+ int slot = 0;
1629
+ vec2 domain = vec2(uDomain_y[slot], uDomain_y[slot + 1]);
1630
+ float transformed = scaleLinear(value, domain, range_y);
1631
+ return transformed;
1632
+
1633
+ }
1634
+
1635
+ float getScaled_y() {
1636
+ return scale_y(accessor_y_0());
1637
+ }
1638
+
1639
+ #define y_DEFINED
1640
+
1641
+
1642
+ float accessor_opacity_0() {
1643
+ // Constant value
1644
+ return float(1.0);
1645
+ }
1646
+
1647
+ float getScaled_opacity() {
1648
+ return accessor_opacity_0();
1649
+ }
1650
+
1651
+ #define opacity_DEFINED
1652
+
1653
+
1654
+ float accessor_size_0() {
1655
+ // Constant value
1656
+ return float(1.0);
1657
+ }
1658
+
1659
+ float getScaled_size() {
1660
+ return accessor_size_0();
1661
+ }
1662
+
1663
+ #define size_DEFINED
1664
+
1665
+
1666
+ float accessor_x2_0() {
1667
+ return attr_x2;
1668
+ }
1669
+
1670
+
1671
+ //////////////////////////////////////////////////////////////////////
1672
+ // Channel: x2
1673
+
1674
+
1675
+ float scale_x2(float value) {
1676
+ int slot = 0;
1677
+ vec2 domain = vec2(uDomain_x[slot], uDomain_x[slot + 1]);
1678
+ float transformed = scaleLinear(value, domain, range_x);
1679
+ return transformed;
1680
+
1681
+ }
1682
+
1683
+ float getScaled_x2() {
1684
+ return scale_x2(accessor_x2_0());
1685
+ }
1686
+
1687
+ #define x2_DEFINED
1688
+
1689
+
1690
+ float accessor_y2_0() {
1691
+ return attr_y_y2;
1692
+ }
1693
+
1694
+
1695
+ //////////////////////////////////////////////////////////////////////
1696
+ // Channel: y2
1697
+
1698
+
1699
+ float scale_y2(float value) {
1700
+ int slot = 0;
1701
+ vec2 domain = vec2(uDomain_y[slot], uDomain_y[slot + 1]);
1702
+ float transformed = scaleLinear(value, domain, range_y);
1703
+ return transformed;
1704
+
1705
+ }
1706
+
1707
+ float getScaled_y2() {
1708
+ return scale_y2(accessor_y2_0());
1709
+ }
1710
+
1711
+ #define y2_DEFINED
1712
+
1713
+ bool isPointSelected() {
1714
+ return false;
1715
+ }
1716
+
1717
+
1718
+ /**
1719
+ * Describes where a sample facet should be shown. Interpolating between the
1720
+ * current and target positions/heights allows for transitioning between facet
1721
+ * configurations.
1722
+ */
1723
+ struct SampleFacetPosition {
1724
+ float pos;
1725
+ float height;
1726
+ float targetPos;
1727
+ float targetHeight;
1728
+ };
1729
+
1730
+ /**
1731
+ * Trasition fraction [0, 1] between the current and target configurations.
1732
+ */
1733
+ uniform float uTransitionOffset;
1734
+
1735
+
1736
+ // ----------------------------------------------------------------------------
1737
+
1738
+ #if !defined(SAMPLE_FACET_UNIFORM) && !defined(SAMPLE_FACET_TEXTURE)
1739
+
1740
+ SampleFacetPosition getSampleFacetPos() {
1741
+ return SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
1742
+ }
1743
+
1744
+ #elif defined(SAMPLE_FACET_UNIFORM)
1745
+
1746
+ /**
1747
+ * Location and height of the band on the Y axis on a normalized [0, 1] scale.
1748
+ * Elements: curr pos, curr height, target pos, target height
1749
+ */
1750
+ uniform vec4 uSampleFacet;
1751
+
1752
+ SampleFacetPosition getSampleFacetPos() {
1753
+ return SampleFacetPosition(
1754
+ 1.0 - uSampleFacet.x - uSampleFacet.y,
1755
+ uSampleFacet.y,
1756
+ 1.0 - uSampleFacet.z - uSampleFacet.w,
1757
+ uSampleFacet.w
1758
+ );
1759
+ }
1760
+
1761
+ #elif defined(SAMPLE_FACET_TEXTURE)
1762
+
1763
+ uniform sampler2D uSampleFacetTexture;
1764
+
1765
+ SampleFacetPosition getSampleFacetPos() {
1766
+ vec4 texel = texelFetch(uSampleFacetTexture, ivec2(int(attr_facetIndex), 0), 0);
1767
+ return SampleFacetPosition(
1768
+ 1.0 - texel.r - texel.g,
1769
+ texel.g,
1770
+ 1.0 - texel.r - texel.g,
1771
+ texel.g);
1772
+ }
1773
+
1774
+ #endif
1775
+
1776
+ // ----------------------------------------------------------------------------
1777
+
1778
+ bool isFacetedSamples(SampleFacetPosition facetPos) {
1779
+ return facetPos != SampleFacetPosition(0.0, 1.0, 0.0, 1.0);
1780
+ }
1781
+
1782
+ bool isFacetedSamples() {
1783
+ return isFacetedSamples(getSampleFacetPos());
1784
+ }
1785
+
1786
+ bool isInTransit() {
1787
+ return uTransitionOffset > 0.0;
1788
+ }
1789
+
1790
+ float getTransitionFraction(float xPos) {
1791
+ return smoothstep(0.0, 0.7 + uTransitionOffset, (xPos - uTransitionOffset) * 2.0);
1792
+ }
1793
+
1794
+ vec2 applySampleFacet(vec2 pos) {
1795
+ SampleFacetPosition facetPos = getSampleFacetPos();
1796
+
1797
+ if (!isFacetedSamples(facetPos)) {
1798
+ return pos;
1799
+ } else if (isInTransit()) {
1800
+ vec2 interpolated = mix(
1801
+ vec2(facetPos.pos, facetPos.height),
1802
+ vec2(facetPos.targetPos, facetPos.targetHeight),
1803
+ getTransitionFraction(pos.x));
1804
+ return vec2(pos.x, interpolated[0] + pos.y * interpolated[1]);
1805
+ } else {
1806
+ return vec2(pos.x, facetPos.pos + pos.y * facetPos.height);
1807
+ }
1808
+ }
1809
+
1810
+ float getSampleFacetHeight(vec2 pos) {
1811
+ SampleFacetPosition facetPos = getSampleFacetPos();
1812
+
1813
+ if (!isFacetedSamples(facetPos)) {
1814
+ return 1.0;
1815
+ } else if (isInTransit()) {
1816
+ return mix(
1817
+ facetPos.height,
1818
+ facetPos.targetHeight,
1819
+ getTransitionFraction(pos.x));
1820
+ } else {
1821
+ return facetPos.height;
1822
+ }
1823
+ }
1824
+
1825
+
1826
+ /*
1827
+ * Based on concepts presented at:
1828
+ * https://webglfundamentals.org/webgl/lessons/webgl-picking.html
1829
+ * https://deck.gl/docs/developer-guide/custom-layers/picking
1830
+ */
1831
+
1832
+ out highp vec4 vPickingColor;
1833
+
1834
+ /**
1835
+ * Passes the unique id to the fragment shader as a color if picking is enabled.
1836
+ * Returns true if picking is enabled.
1837
+ */
1838
+ bool setupPicking() {
1839
+ if (uPickingEnabled) {
1840
+ #ifdef uniqueId_DEFINED
1841
+ uint id = attr_uniqueId;
1842
+ vPickingColor = vec4(
1843
+ ivec4(id >> 0, id >> 8, id >> 16, id >> 24) & 0xFF
1844
+ ) / float(0xFF);
1845
+ #else
1846
+ vPickingColor = vec4(1.0);
1847
+ #endif
1848
+ return true;
1849
+ }
1850
+ return false;
1851
+ }
1852
+
1853
+
1854
+ flat out vec4 vColor;
1855
+
1856
+ /** Stroke width */
1857
+ flat out float vSize;
1858
+
1859
+ /** The distance from the line center to the direction of normal in pixels */
1860
+ out float vNormalLengthInPixels;
1861
+
1862
+ flat out float vGamma;
1863
+
1864
+ const int SHAPE_ARC = 0;
1865
+ const int SHAPE_DOME = 1;
1866
+ const int SHAPE_DIAGONAL = 2;
1867
+ const int SHAPE_LINE = 3;
1868
+ const int ORIENT_VERTICAL = 0;
1869
+ const int ORIENT_HORIZONTAL = 1;
1870
+
1871
+ float distanceFromLine(vec2 pointOnLine1, vec2 pointOnLine2, vec2 point) {
1872
+ vec2 a = point - pointOnLine1;
1873
+ vec2 b = pointOnLine2 - pointOnLine1;
1874
+ vec2 proj = dot(a, b) / dot(b, b) * b;
1875
+ return length(a - proj);
1876
+ }
1877
+
1878
+ bool isInsideViewport(vec2 point, float marginFactor) {
1879
+ vec2 margin = uViewportSize * vec2(marginFactor);
1880
+ return point.x >= -margin.x
1881
+ && point.x <= uViewportSize.x + margin.x
1882
+ && point.y >= -margin.y
1883
+ && point.y <= uViewportSize.y + margin.y;
1884
+ }
1885
+
1886
+ float inverseSmoothstep(float t) {
1887
+ t = clamp(t, 0.0, 1.0);
1888
+ // The chord-axis coordinate of ARC/DOME follows the smoothstep curve.
1889
+ return 0.5 - sin(asin(1.0 - 2.0 * t) / 3.0);
1890
+ }
1891
+
1892
+ /**
1893
+ * Remaps the parameter t to concentrate vertices to the part that is visible in the viewport.
1894
+ * This keeps the tightly bent endpoints smooth even when zooming in very close.
1895
+ */
1896
+ float remapVisibleChordParameter(
1897
+ float stripT,
1898
+ float chordStart,
1899
+ float chordEnd,
1900
+ float viewportLength
1901
+ ) {
1902
+ // Concentrate samples in the viewport-visible chord interval without dropping the rest.
1903
+ float chordMin = min(chordStart, chordEnd);
1904
+ float chordMax = max(chordStart, chordEnd);
1905
+ float chordSpan = chordMax - chordMin;
1906
+
1907
+ if (chordSpan <= 0.0) {
1908
+ return 0.0;
1909
+ }
1910
+
1911
+ float visibleChordMin = max(chordMin, 0.0);
1912
+ float visibleChordMax = min(chordMax, viewportLength);
1913
+
1914
+ if (visibleChordMax <= visibleChordMin) {
1915
+ return stripT;
1916
+ }
1917
+
1918
+ float visibleTMin = inverseSmoothstep((visibleChordMin - chordMin) / chordSpan);
1919
+ float visibleTMax = inverseSmoothstep((visibleChordMax - chordMin) / chordSpan);
1920
+ float visibleTSpan = visibleTMax - visibleTMin;
1921
+ float offscreenTSpan = visibleTMin + (1.0 - visibleTMax);
1922
+
1923
+ if (offscreenTSpan <= 0.0) {
1924
+ return stripT;
1925
+ }
1926
+
1927
+ float visibleShare = clamp(0.75 + (1.0 - visibleTSpan) * 0.2, 0.75, 0.95);
1928
+ float offscreenShare = 1.0 - visibleShare;
1929
+ float leftShare = offscreenShare * visibleTMin / offscreenTSpan;
1930
+ float rightShare = offscreenShare * (1.0 - visibleTMax) / offscreenTSpan;
1931
+
1932
+ if (stripT <= leftShare) {
1933
+ return leftShare > 0.0 ? mix(0.0, visibleTMin, stripT / leftShare) : visibleTMin;
1934
+ }
1935
+
1936
+ float visibleStart = leftShare;
1937
+ float visibleEnd = visibleStart + visibleShare;
1938
+
1939
+ if (stripT <= visibleEnd) {
1940
+ return visibleShare > 0.0
1941
+ ? mix(visibleTMin, visibleTMax, (stripT - visibleStart) / visibleShare)
1942
+ : visibleTMin;
1943
+ }
1944
+
1945
+ return rightShare > 0.0
1946
+ ? mix(visibleTMax, 1.0, (stripT - visibleEnd) / rightShare)
1947
+ : visibleTMax;
1948
+ }
1949
+
1950
+ void clampChordToViewport(inout vec2 p1, inout vec2 p4, inout float chordLength) {
1951
+ if (chordLength > uMaxChordLength) {
1952
+ vec2 chordVector = p4 - p1;
1953
+ vec2 unitChordVector = normalize(chordVector);
1954
+
1955
+ if (isInsideViewport(p1, 2.0)) {
1956
+ chordLength = uMaxChordLength;
1957
+ p4 = p1 + unitChordVector * uMaxChordLength;
1958
+ } else if (isInsideViewport(p4, 2.0)) {
1959
+ chordLength = uMaxChordLength;
1960
+ p1 = p4 - unitChordVector * uMaxChordLength;
1961
+ }
1962
+ }
1963
+ }
1964
+
1965
+ void clampDomeApex(inout vec2 p1, inout vec2 p4, int orient) {
1966
+ if (orient == ORIENT_VERTICAL) {
1967
+ if (p4.x > 0.0) {
1968
+ p1.x = max(p1.x, -p4.x);
1969
+ }
1970
+ if (p1.x < uViewportSize.x) {
1971
+ p4.x = min(p4.x, 2.0 * uViewportSize.x - p1.x);
1972
+ }
1973
+ } else {
1974
+ if (p4.y > 0.0) {
1975
+ p1.y = max(p1.y, -p4.y);
1976
+ }
1977
+ if (p1.y < uViewportSize.y) {
1978
+ p4.y = min(p4.y, 2.0 * uViewportSize.y - p1.y);
1979
+ }
1980
+ }
1981
+ }
1982
+
1983
+ void main(void) {
1984
+ float pixelSize = 1.0 / uDevicePixelRatio;
1985
+ float opacity = getScaled_opacity() * uViewOpacity;
1986
+
1987
+ // The bezier's control points
1988
+ vec2 p1, p2, p3, p4;
1989
+
1990
+ vec2 a = applySampleFacet(vec2(getScaled_x(), getScaled_y())) * uViewportSize;
1991
+ vec2 b = applySampleFacet(vec2(getScaled_x2(), getScaled_y2())) * uViewportSize;
1992
+
1993
+ if (uShape <= SHAPE_DOME) {
1994
+ if (uShape == SHAPE_DOME) {
1995
+ vec2 height = vec2(0.0);
1996
+ if (uOrient == ORIENT_VERTICAL) {
1997
+ p1 = vec2(min(a.x, b.x), b.y);
1998
+ p4 = vec2(max(a.x, b.x), b.y);
1999
+ height = vec2(0.0, a.y - b.y);
2000
+
2001
+ float chordLength = length(p4 - p1);
2002
+ clampChordToViewport(p1, p4, chordLength);
2003
+ if (uClampApex) {
2004
+ clampDomeApex(p1, p4, ORIENT_VERTICAL);
2005
+ }
2006
+
2007
+ } else {
2008
+ p1 = vec2(b.x, min(a.y, b.y));
2009
+ p4 = vec2(b.x, max(a.y, b.y));
2010
+ height = vec2(a.x - b.x, 0.0);
2011
+
2012
+ float chordLength = length(p4 - p1);
2013
+ clampChordToViewport(p1, p4, chordLength);
2014
+ if (uClampApex) {
2015
+ clampDomeApex(p1, p4, ORIENT_HORIZONTAL);
2016
+ }
2017
+ }
2018
+
2019
+ vec2 controlOffset = height / 0.75;
2020
+ p2 = p1 + controlOffset;
2021
+ p3 = p4 + controlOffset;
2022
+
2023
+ } if (uShape == SHAPE_ARC) {
2024
+ p1 = a;
2025
+ p4 = b;
2026
+
2027
+ vec2 chordVector = p4 - p1;
2028
+ vec2 unitChordVector = normalize(chordVector);
2029
+ vec2 chordNormal = vec2(-unitChordVector.y, unitChordVector.x);
2030
+ float chordLength = length(chordVector);
2031
+ clampChordToViewport(p1, p4, chordLength);
2032
+
2033
+ float height = max(
2034
+ chordLength / 2.0 * uArcHeightFactor,
2035
+ uMinArcHeight
2036
+ );
2037
+
2038
+ // This is a bit poor approximation of a circular arc, but it's probably enough for most purposes.
2039
+ // TODO: Consider a more sophisticated approach: https://stackoverflow.com/a/44829356/1547896
2040
+ vec2 controlOffset = chordNormal * height / 0.75;
2041
+ p2 = p1 + controlOffset;
2042
+ p3 = p4 + controlOffset;
2043
+ }
2044
+
2045
+ } else if (uShape == SHAPE_DIAGONAL) {
2046
+ if (uOrient == ORIENT_VERTICAL) {
2047
+ p1 = a;
2048
+ p2 = vec2(a.x, (a.y + b.y) / 2.0);
2049
+ p3 = vec2(b.x, (a.y + b.y) / 2.0);
2050
+ p4 = b;
2051
+ } else {
2052
+ p1 = a;
2053
+ p2 = vec2((a.x + b.x) / 2.0, a.y);
2054
+ p3 = vec2((a.x + b.x) / 2.0, b.y);
2055
+ p4 = b;
2056
+ }
2057
+ } else if (uShape == SHAPE_LINE) {
2058
+ p1 = a;
2059
+ p2 = (a + b) / 2.0;
2060
+ p3 = p2;
2061
+ p4 = b;
2062
+ }
2063
+
2064
+ vec2 strip = vec2(
2065
+ float(gl_VertexID / 2) / float(uSegmentBreaks),
2066
+ float(gl_VertexID % 2) - 0.5
2067
+ );
2068
+
2069
+ float t = strip.x;
2070
+
2071
+ if (uShape == SHAPE_DOME) {
2072
+ if (uOrient == ORIENT_VERTICAL) {
2073
+ t = remapVisibleChordParameter(strip.x, p1.x, p4.x, uViewportSize.x);
2074
+ } else {
2075
+ t = remapVisibleChordParameter(strip.x, p1.y, p4.y, uViewportSize.y);
2076
+ }
2077
+ } else if (uShape == SHAPE_ARC) {
2078
+ if (a.y == b.y) {
2079
+ t = remapVisibleChordParameter(strip.x, p1.x, p4.x, uViewportSize.x);
2080
+ } else if (a.x == b.x) {
2081
+ t = remapVisibleChordParameter(strip.x, p1.y, p4.y, uViewportSize.y);
2082
+ }
2083
+ }
2084
+
2085
+ vec2 p;
2086
+ vec2 tangent;
2087
+
2088
+ // de Casteljau evaluation keeps the cubic stable for long chords.
2089
+ vec2 q1 = mix(p1, p2, t);
2090
+ vec2 q2 = mix(p2, p3, t);
2091
+ vec2 q3 = mix(p3, p4, t);
2092
+
2093
+ vec2 r1 = mix(q1, q2, t);
2094
+ vec2 r2 = mix(q2, q3, t);
2095
+
2096
+ p = mix(r1, r2, t);
2097
+ tangent = 3.0 * (r2 - r1);
2098
+
2099
+ tangent = normalize(tangent);
2100
+ vec2 normal = vec2(-tangent.y, tangent.x);
2101
+
2102
+ float size = getScaled_size();
2103
+
2104
+ // Avoid artifacts in very thin lines by clamping the size and adjusting opacity respectively
2105
+ if (size < pixelSize) {
2106
+ opacity *= size / pixelSize;
2107
+ size = pixelSize;
2108
+ }
2109
+
2110
+ // Handle minimum picking size or add an extra pixel to the stroke width to accommodate edge antialiasing
2111
+ float paddedSize = uPickingEnabled
2112
+ ? max(size, uMinPickingSize)
2113
+ : size + pixelSize;
2114
+
2115
+ vNormalLengthInPixels = strip.y * paddedSize;
2116
+
2117
+ if (uShape == SHAPE_ARC &&
2118
+ uArcFadingDistance[0] > 0.0 &&
2119
+ uArcFadingDistance[1] > 0.0 &&
2120
+ (!uNoFadingOnPointSelection || !isPointSelected()))
2121
+ {
2122
+ float d = distanceFromLine(p1, p4, p);
2123
+ float distanceOpacity = smoothstep(uArcFadingDistance[1], uArcFadingDistance[0], d);
2124
+
2125
+ // Fade out
2126
+ opacity *= distanceOpacity;
2127
+
2128
+ // Collapse fully transparent triangles to skip fragment processing
2129
+ if (distanceOpacity <= 0.0) {
2130
+ vNormalLengthInPixels = 0.0;
2131
+ }
2132
+ }
2133
+
2134
+ // Extrude
2135
+ p += normal * vNormalLengthInPixels;
2136
+
2137
+ gl_Position = pixelsToNdc(p);
2138
+
2139
+ vec3 color = getScaled_color();
2140
+
2141
+ vColor = vec4(color * opacity, opacity);
2142
+
2143
+ vGamma = getGammaForColor(color);
2144
+
2145
+ vSize = paddedSize;
2146
+
2147
+ setupPicking();
2148
+ }
2149
+ ",
2150
+ }
2151
+ `;
2152
+
1071
2153
  exports[`generated shader snapshots > penguins scatter plot example 1`] = `
1072
2154
  {
1073
2155
  "fragment": "precision highp float;