@genome-spy/core 0.75.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.
- package/dist/bundle/{esm-CgfVIRJ-.js → esm-BimDEpBb.js} +1 -1
- package/dist/bundle/{esm-DtE8VqAv.js → esm-Bvlm1uVk.js} +1 -1
- package/dist/bundle/{esm-sIoQYZ21.js → esm-CngqBe45.js} +17 -17
- package/dist/bundle/{esm-DQiq2Zhd.js → esm-D_euN86T.js} +43 -43
- package/dist/bundle/index.es.js +3253 -3137
- package/dist/bundle/index.js +97 -96
- package/dist/schema.json +352 -0
- package/dist/src/config/defaults/markDefaults.d.ts.map +1 -1
- package/dist/src/config/defaults/markDefaults.js +1 -12
- package/dist/src/config/defaults/scaleDefaults.d.ts.map +1 -1
- package/dist/src/config/defaults/scaleDefaults.js +1 -0
- package/dist/src/config/markConfig.d.ts.map +1 -1
- package/dist/src/config/markConfig.js +16 -8
- package/dist/src/config/themes.d.ts.map +1 -1
- package/dist/src/config/themes.js +15 -2
- package/dist/src/data/sources/lazy/registerBuiltInLazySources.js +2 -2
- package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts +2 -0
- package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts.map +1 -0
- package/dist/src/data/sources/lazy/registerCoreLazySources.js +2 -0
- package/dist/src/data/sources/lazy/tabixSource.d.ts +7 -0
- package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/tabixSource.js +18 -0
- package/dist/src/data/sources/lazy/tabixTsvSource.d.ts +37 -0
- package/dist/src/data/sources/lazy/tabixTsvSource.d.ts.map +1 -0
- package/dist/src/data/sources/lazy/tabixTsvSource.js +163 -0
- package/dist/src/genomeSpyBase.d.ts.map +1 -1
- package/dist/src/genomeSpyBase.js +4 -1
- package/dist/src/gl/webGLHelper.d.ts +5 -2
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +20 -3
- package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +1082 -0
- package/dist/src/marks/link.vertex.glsl.js +1 -1
- package/dist/src/minimal.d.ts.map +1 -1
- package/dist/src/minimal.js +5 -4
- package/dist/src/scale/scale.js +10 -2
- package/dist/src/scales/domainPlanner.js +1 -1
- package/dist/src/scales/scaleResolution.d.ts.map +1 -1
- package/dist/src/scales/scaleResolution.js +9 -3
- package/dist/src/scales/selectionDomainUtils.d.ts +10 -0
- package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -1
- package/dist/src/scales/selectionDomainUtils.js +32 -3
- package/dist/src/spec/channel.d.ts +30 -0
- package/dist/src/spec/data.d.ts +40 -0
- package/dist/src/spec/parameter.d.ts +6 -0
- package/dist/src/spec/transform.d.ts +6 -0
- package/dist/src/view/axisGridView.d.ts.map +1 -1
- package/dist/src/view/axisGridView.js +2 -1
- package/dist/src/view/axisView.d.ts.map +1 -1
- package/dist/src/view/axisView.js +2 -1
- package/dist/src/view/facetView.d.ts.map +1 -1
- package/dist/src/view/facetView.js +2 -1
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +9 -1
- package/dist/src/view/gridView/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView/gridView.js +198 -32
- package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
- package/dist/src/view/gridView/scrollbar.js +5 -1
- package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
- package/dist/src/view/gridView/selectionRect.js +5 -1
- package/dist/src/view/gridView/separatorView.d.ts.map +1 -1
- package/dist/src/view/gridView/separatorView.js +5 -1
- package/dist/src/view/testUtils.d.ts +30 -3
- package/dist/src/view/testUtils.d.ts.map +1 -1
- package/dist/src/view/testUtils.js +51 -2
- package/dist/src/view/viewSelectors.d.ts +38 -10
- package/dist/src/view/viewSelectors.d.ts.map +1 -1
- package/dist/src/view/viewSelectors.js +67 -2
- package/dist/src/view/viewUtilTypes.d.ts +15 -0
- package/dist/src/view/viewUtils.d.ts.map +1 -1
- package/dist/src/view/viewUtils.js +10 -0
- package/package.json +2 -2
- package/LICENSE +0 -21
- /package/dist/bundle/{esm-BDFRLEuD.js → esm-C49STiCR.js} +0 -0
- /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;
|