viewof classifyCoordsFragShader = {
const coordDefines = Object.entries(coordEncodings.values)
.map(([key, value]) => `const float f${_.startCase(key)} = ${(value / 255).toFixed(32)};`)
.join("\n");
const dirKeys4 = ["N", "E", "S", "W"];
const dirKeys = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
const cAndDirKeys = ["C", ...dirKeys];
const compassPointsN = [[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]];
const compassPoints = {
N: compassPointsN,
E: [...compassPointsN.slice(2), ...compassPointsN.slice(0, 2)],
S: [...compassPointsN.slice(4), ...compassPointsN.slice(0, 4)],
W: [...compassPointsN.slice(6), ...compassPointsN.slice(0, 6)]
}
const compassCenters = {
N: [ 0, 0],
E: [-1, 0],
S: [-1, -1],
W: [ 0, -1]
}
return highlightGlsl.fragment(`#version 300 es
precision mediump float;
// classifyCoordsFragShader
${coordDefines.trim()}
uniform sampler2D uSampler;
${dirKeys4.map(d => `uniform int mapEdge${d}px;`).join("\n").trim()}
struct directions {
${cAndDirKeys.map(k => ` ivec2 ${k};`).join("\n").trim() }
};
struct directionColors {
${cAndDirKeys.map(k => ` vec4 ${k};`).join("\n").trim() /* vec4 C, vec4 N...NW */}
};
${Object.entries(compassPoints) /* const directions directions[N..W] = ... */
.map(([key, values]) => {
const params = [compassCenters[key], ...values].map(([x,y]) => `ivec2(${x}, ${y})`).join(", ");
return `const directions directions${key} = directions(${params});`
})
.join("\n")}
directionColors readColors(ivec2 xyC, directions dirs) {
ivec2 newXyC = xyC + dirs.C;
vec4 cC = texelFetch(uSampler, newXyC, 0);
return directionColors(cC, ${dirKeys.map(dir => `texelFetch(uSampler, newXyC + dirs.${dir}, 0)`).join(", ") /* texelFetch(uSampler, newXyC + dirs.[N..NW], 0) */});
}
bool isJunction(directionColors dirColors) {
// Count the number of unique colors in the 4px range [C, W, NW, N]
int colorCount = 1;
vec4 uniqueColor1 = dirColors.C;
vec4 uniqueColor2 = vec4(-1.0);
vec4 uniqueColor3 = vec4(-1.0);
if (dirColors.W != uniqueColor1) {
colorCount++;
uniqueColor2 = dirColors.W;
}
if (dirColors.NW != uniqueColor1 && dirColors.NW != uniqueColor2) {
colorCount++;
uniqueColor3 = dirColors.NW;
}
if (dirColors.N != uniqueColor1 && dirColors.N != uniqueColor2 && dirColors.N != uniqueColor3) {
colorCount++;
}
if (colorCount >= 3) {
// Standard junction: 3 distinct colors touch at this coord
return true;
}
if (colorCount == 2 && dirColors.C == dirColors.NW && dirColors.N == dirColors.W) {
// Special case junction: 2 distinct colors in an hourglass shape 4-pixel diagonal hourglass
return true;
}
return false;
}
bool isMaybeCorner(directionColors dirColors) {
return (dirColors.C != dirColors.W && dirColors.C != dirColors.NW && dirColors.C != dirColors.N);
}
bool isIntrusion(directionColors dirColors, ivec2 xyC, directions dirs) {
// Assumption: isMaybeCorner test already passed
// Horizontal check
for (int i = 1; i < 5; i++) {
ivec2 xyE = xyC + i * dirs.E;
vec4 cE = texelFetch(uSampler, xyE, 0);
vec4 cNE = texelFetch(uSampler, xyE + dirs.N, 0);
if (cNE == dirColors.C) {
// It advanced north again. This isn't an intrusion.
break;
}
if (cE != dirColors.C) {
// It retreated south without advancing north
return true;
}
}
// Vertical check
for (int i = 1; i < 5; i++) {
ivec2 xyS = xyC + i * dirs.S;
vec4 cS = texelFetch(uSampler, xyS, 0);
vec4 cSW = texelFetch(uSampler, xyS + dirs.W, 0);
if (cSW == dirColors.C) {
// It advanced west again. Not an intrusion.
break;
}
if (cS != dirColors.C) {
// It retreated east without advancing west.
return true;
}
}
return false;
}
bool isCorner(directionColors dirColors) {
// Assumption: isMaybeCorner test already passed
if (dirColors.C == dirColors.NE || dirColors.C == dirColors.SW) return false;
return (dirColors.C == dirColors.E && dirColors.C == dirColors.SE && dirColors.C == dirColors.S);
}
bool isEdge(ivec2 xyC, directionColors dirColors) {
bool result = false;
if (xyC.y == mapEdgeNpx) result = result || (dirColors.C.a + dirColors.W.a) > 0.0;
if (xyC.x > 0 && xyC.y == mapEdgeEpx+1) result = result || (dirColors.W.a + dirColors.NW.a) > 0.0;
if (xyC.y > 0 && xyC.y == mapEdgeSpx+1) result = result || (dirColors.N.a + dirColors.NW.a) > 0.0;
if (xyC.x == mapEdgeWpx) result = result || (dirColors.C.a + dirColors.N.a) > 0.0;
return result;
}
out vec4 fragColor;
void main () {
fragColor = vec4(0.0);
ivec2 xyC = ivec2(round(gl_FragCoord.xy - 0.5));
directions[4] allDirs = directions[4](${dirKeys4.map(k => `directions${k}`).join(", ")});
directionColors dirColorsN = readColors(xyC, directionsN);
// Edge test
if (isEdge(xyC, dirColorsN)) {
fragColor = vec4(fJunction, 0.0, 0.0, 1.0);
return;
}
// Junction test
if (isJunction(dirColorsN)) {
fragColor = vec4(fJunction, 0.0, 0.0, 1.0);
return;
}
directionColors[4] allDirColors = directionColors[4](dirColorsN, ${d3.range(1,4).map(i => `readColors(xyC, allDirs[${i}])`).join(", ")});
bool[4] allDirMaybes = bool[4](${d3.range(0,4).map(i => `isMaybeCorner(allDirColors[${i}])`).join(", ")});
// Intrusion test
for (int i = 0; i < 4; i++) {
if (!allDirMaybes[i]) continue;
directionColors dirColors = allDirColors[i];
directions dirs = allDirs[i];
ivec2 xyCOffset = xyC + dirs.C;
if (isIntrusion(dirColors, xyCOffset, dirs)) {
fragColor = vec4(fIntrusion, 0.0, 0.0, 1.0);
return;
}
}
// Corner test
for (int i = 0; i < 4; i++) {
if (!allDirMaybes[i]) continue;
directionColors dirColors = allDirColors[i];
if (isCorner(dirColors)) {
fragColor = vec4(fCorner, 0.0, 0.0, 1.0);
return;
}
}
}`);
}