Skip to content

Commit

Permalink
CurvedPath edge picking fix and add StraightPath and ClusterBundle ed…
Browse files Browse the repository at this point in the history
…ge picking (#101)

* fix curved path picking

* Add picking for StraightPath edge type

* fix ClusterBundle edge type and add picking
  • Loading branch information
Manfred Cheung authored Aug 22, 2023
1 parent f4d858a commit 2a58eb9
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 20 deletions.
13 changes: 12 additions & 1 deletion src/graph/edges/bundle/ClusterBundle.data.vs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ layout(location=4) in uint aSourceColor;
layout(location=5) in uint aTargetColor;
layout(location=6) in uvec2 aHyperEdgeStats;
layout(location=7) in uint aIndex;
layout(location=8) in uvec4 aPickingColor;

uniform sampler2D uGraphPoints;

Expand All @@ -17,8 +18,16 @@ out vec3 vControl;
flat out uint vSourceColor;
flat out uint vTargetColor;
out vec2 vColorMix;
flat out uvec4 vPickingColor;

#pragma glslify: valueForIndex = require(../../../renderer/shaders/valueForIndex.glsl)
// manual import from ../../../renderer/shaders/valueForIndex.glsl
// to avoid uvec4 pragma error
vec4 valueForIndex(sampler2D tex, int index) {
int texWidth = textureSize(tex, 0).x;
int col = index % texWidth;
int row = index / texWidth;
return texelFetch(tex, ivec2(col, row), 0);
}

void main() {
vec4 source = valueForIndex(uGraphPoints, int(aSourceIndex));
Expand Down Expand Up @@ -94,4 +103,6 @@ void main() {
vTargetColor = aTargetColor;

vColorMix = vec2(float(aIndex) * 0.25, float(aIndex + 1u) * 0.25);

vPickingColor = aPickingColor;
}
1 change: 1 addition & 0 deletions src/graph/edges/bundle/ClusterBundle.picking.fs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#pragma glslify: import(../path/CurvedPath.picking.fs.glsl)
55 changes: 51 additions & 4 deletions src/graph/edges/bundle/ClusterBundle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import edgeVS from './ClusterBundle.vs.glsl';
import edgeFS from './ClusterBundle.fs.glsl';
import pickingFS from './ClusterBundle.picking.fs.glsl';
import dataVS from './ClusterBundle.data.vs.glsl';

import {App, DrawCall, PicoGL, Program, VertexArray, VertexBuffer} from 'picogl';
Expand All @@ -14,7 +15,8 @@ import {
} from '../../../renderer/Renderable';
import {Edges} from '../Edges';
import {GraphPoints} from '../../../data/GraphPoints';
import {PickingManager} from '../../../UX/picking/PickingManager';
import {PickingColors, PickingEvent, PickingManager} from '../../../UX/picking/PickingManager';
import { MouseCallback } from 'src/UX/mouse/MouseHandler';
import {GraferContext} from '../../../renderer/GraferContext';

export interface ClusterBundleEdgeData {
Expand All @@ -26,8 +28,10 @@ export interface ClusterBundleEdgeData {
sourceColor?: GraferInputColor,
targetColor?: GraferInputColor,
hyperEdgeStats?: [number, number],
pickingColor?: number | [number, number, number, number];
}

const pickingColorNoOpMapping = (): null => null;
const kClusterBundleEdgeNoOpMapping = (): null => null;
export const kClusterBundleEdgeMappings: DataMappings<ClusterBundleEdgeData & { index: number[] }> = {
id: (entry: ClusterBundleEdgeData, i) => 'id' in entry ? entry.id : i,
Expand All @@ -39,6 +43,7 @@ export const kClusterBundleEdgeMappings: DataMappings<ClusterBundleEdgeData & {
targetColor: (entry: ClusterBundleEdgeData) => 'targetColor' in entry ? entry.targetColor : 0, // first registered color
hyperEdgeStats: kClusterBundleEdgeNoOpMapping, // this will be replaced in `computeMappings`
index: () => [0, 1, 2],
pickingColor: pickingColorNoOpMapping,
};

export const kClusterBundleEdgeDataTypes: GLDataTypes<ClusterBundleEdgeData & { index: number[] }> = {
Expand All @@ -50,6 +55,7 @@ export const kClusterBundleEdgeDataTypes: GLDataTypes<ClusterBundleEdgeData & {
targetColor: PicoGL.UNSIGNED_INT,
hyperEdgeStats: [PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT],
index: PicoGL.UNSIGNED_INT,
pickingColor: [PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT],
};

export const kGLClusterBundleEdgeTypes = {
Expand All @@ -59,6 +65,7 @@ export const kGLClusterBundleEdgeTypes = {
sourceColor: PicoGL.UNSIGNED_INT,
targetColor: PicoGL.UNSIGNED_INT,
colorMix: [PicoGL.FLOAT, PicoGL.FLOAT],
pickingColor: [PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT],
} as const;
export type GLClusterBundleEdgeTypes = typeof kGLClusterBundleEdgeTypes;

Expand All @@ -68,6 +75,11 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE
protected program: Program;
protected drawCall: DrawCall;

protected pickingProgram: Program;
protected pickingDrawCall: DrawCall;
protected pickingColors: PickingColors;
protected pickingHandler: MouseCallback;

protected verticesVBO: VertexBuffer;
protected edgesVAO: VertexArray;

Expand Down Expand Up @@ -100,6 +112,8 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE
);
}

this.pickingHandler = this.handlePickingEvent.bind(this);

this.verticesVBO = context.createVertexBuffer(PicoGL.FLOAT, 2, new Float32Array(segmentVertices));
this.edgesVAO = context.createVertexArray().vertexAttributeBuffer(0, this.verticesVBO);
this.configureTargetVAO(this.edgesVAO);
Expand All @@ -108,19 +122,32 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE
this.program = context.createProgram(shaders.vs, shaders.fs);
this.drawCall = context.createDrawCall(this.program, this.edgesVAO).primitive(PicoGL.TRIANGLE_STRIP);

const pickingShaders = this.getPickingShaders();
this.pickingProgram = context.createProgram(pickingShaders.vs, pickingShaders.fs);
this.pickingDrawCall = context.createDrawCall(this.pickingProgram, this.edgesVAO).primitive(PicoGL.TRIANGLE_STRIP);

this.compute(context, {
uGraphPoints: this.dataTexture,
});

// printDataGL(context, this.targetVBO, data.length, kGLClusterBundleEdgeTypes);

this.pickingManager.on(PickingManager.events.hoverOn, this.pickingHandler);
this.pickingManager.on(PickingManager.events.hoverOff, this.pickingHandler);
this.pickingManager.on(PickingManager.events.click, this.pickingHandler);

this.localUniforms.uSegments = segments;
}

public destroy(): void {
// TODO: Implement destroy method
}

protected ingestData(context: App, data: unknown[], mappings: Partial<DataMappings<ClusterBundleEdgeData>>): void {
this.pickingColors = this.pickingManager.allocatePickingColors(data.length);
super.ingestData(context, data, mappings);
}

public render(context:App, mode: RenderMode, uniforms: RenderUniforms): void {
setDrawCallUniforms(this.drawCall, uniforms);
setDrawCallUniforms(this.drawCall, this.localUniforms);
Expand All @@ -129,7 +156,10 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE

switch (mode) {
case RenderMode.PICKING:
// this.pickingDrawCall.draw();
setDrawCallUniforms(this.pickingDrawCall, uniforms);
setDrawCallUniforms(this.pickingDrawCall, this.localUniforms);
this.pickingDrawCall.uniform('uPicking', true);
this.pickingDrawCall.draw();
break;

default:
Expand All @@ -148,7 +178,7 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE
protected getPickingShaders(): RenderableShaders {
return {
vs: edgeVS,
fs: null, // pickingFS,
fs: pickingFS,
};
}

Expand All @@ -163,7 +193,7 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE
protected getDataShader(): DataShader {
return {
vs: dataVS,
varyings: [ 'vSource', 'vTarget', 'vControl', 'vSourceColor', 'vTargetColor', 'vColorMix' ],
varyings: [ 'vSource', 'vTarget', 'vControl', 'vSourceColor', 'vTargetColor', 'vColorMix', 'vPickingColor' ],
};
}

Expand Down Expand Up @@ -207,6 +237,16 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE
}
this.hyperEdgeStats.set(key, count + 1);
entry.hyperEdgeStats[0] = count;

this.idArray.push(entry.id);

const indexStart = 4 * i;
entry.pickingColor = [
this.pickingColors.colors[indexStart],
this.pickingColors.colors[indexStart + 1],
this.pickingColors.colors[indexStart + 2],
this.pickingColors.colors[indexStart + 3],
];
};

const cb2 = (i: number, entry: ClusterBundleEdgeData): void => {
Expand All @@ -219,4 +259,11 @@ export class ClusterBundle extends Edges<ClusterBundleEdgeData, GLClusterBundleE
cb2,
];
}

protected handlePickingEvent(event: PickingEvent, colorID: number): void {
if (this.picking && this.pickingColors.map.has(colorID)) {
const id = this.idArray[this.pickingColors.map.get(colorID)];
this.emit(event, id);
}
}
}
15 changes: 13 additions & 2 deletions src/graph/edges/path/CurvedPath.data.vs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ layout(location=1) in uint aTargetIndex;
layout(location=2) in uvec3 aControl;
layout(location=3) in uint aSourceColor;
layout(location=4) in uint aTargetColor;
layout(location=5) in uvec4 aPickingColor;

uniform sampler2D uGraphPoints;

Expand All @@ -14,8 +15,16 @@ out vec3 vControl;
flat out uint vSourceColor;
flat out uint vTargetColor;
out vec2 vColorMix;

#pragma glslify: valueForIndex = require(../../../renderer/shaders/valueForIndex.glsl)
flat out uvec4 vPickingColor;

// manual import from ../../../renderer/shaders/valueForIndex.glsl
// to avoid uvec4 pragma error
vec4 valueForIndex(sampler2D tex, int index) {
int texWidth = textureSize(tex, 0).x;
int col = index % texWidth;
int row = index / texWidth;
return texelFetch(tex, ivec2(col, row), 0);
}

void main() {
vec4 source = valueForIndex(uGraphPoints, int(aSourceIndex));
Expand All @@ -41,4 +50,6 @@ void main() {
vTargetColor = aTargetColor;

vColorMix = vec2(float(aControl[1]) / float(aControl[2]), float(aControl[1] + 1u) / float(aControl[2]));

vPickingColor = aPickingColor;
}
36 changes: 29 additions & 7 deletions src/graph/edges/path/CurvedPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,18 @@ export interface CurvedPathEdgeData {
control: number | number[];
sourceColor?: GraferInputColor,
targetColor?: GraferInputColor,
pickingColor?: number | [number, number, number, number];
}

const pickingColorNoOpMapping = (): null => null;
export const kCurvedPathEdgeMappings: DataMappings<CurvedPathEdgeData> = {
id: (entry: CurvedPathEdgeData, i) => 'id' in entry ? entry.id : i,
source: (entry: CurvedPathEdgeData) => entry.source,
target: (entry: CurvedPathEdgeData) => entry.target,
control: (entry: CurvedPathEdgeData) => entry.control,
sourceColor: (entry: CurvedPathEdgeData) => 'sourceColor' in entry ? entry.sourceColor : 0, // first registered color
targetColor: (entry: CurvedPathEdgeData) => 'targetColor' in entry ? entry.targetColor : 0, // first registered color
pickingColor: pickingColorNoOpMapping,
};

export const kCurvedPathEdgeDataTypes: GLDataTypes<CurvedPathEdgeData> = {
Expand All @@ -43,6 +46,7 @@ export const kCurvedPathEdgeDataTypes: GLDataTypes<CurvedPathEdgeData> = {
control: [PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT],
sourceColor: PicoGL.UNSIGNED_INT,
targetColor: PicoGL.UNSIGNED_INT,
pickingColor: [PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT],
};

export const kGLCurvedPathEdgeTypes = {
Expand All @@ -52,6 +56,7 @@ export const kGLCurvedPathEdgeTypes = {
sourceColor: PicoGL.UNSIGNED_INT,
targetColor: PicoGL.UNSIGNED_INT,
colorMix: [PicoGL.FLOAT, PicoGL.FLOAT],
pickingColor: [PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT, PicoGL.UNSIGNED_INT],
} as const;
export type GLCurvedPathEdgeTypes = typeof kGLCurvedPathEdgeTypes;

Expand All @@ -62,7 +67,6 @@ export class CurvedPath extends Edges<CurvedPathEdgeData, GLCurvedPathEdgeTypes>
protected pickingProgram: Program;
protected pickingDrawCall: DrawCall;
protected pickingColors: PickingColors;
protected pickingVBO: VertexBuffer;
protected pickingHandler: MouseCallback;

protected verticesVBO: VertexBuffer;
Expand Down Expand Up @@ -97,15 +101,11 @@ export class CurvedPath extends Edges<CurvedPathEdgeData, GLCurvedPathEdgeTypes>
);
}

this.verticesVBO = context.createVertexBuffer(PicoGL.FLOAT, 2, new Float32Array(segmentVertices));

this.pickingHandler = this.handlePickingEvent.bind(this);
this.pickingColors = this.pickingManager.allocatePickingColors(data.length);
this.pickingVBO = context.createVertexBuffer(PicoGL.UNSIGNED_BYTE, 4, this.pickingColors.colors);

this.verticesVBO = context.createVertexBuffer(PicoGL.FLOAT, 2, new Float32Array(segmentVertices));
this.edgesVAO = context.createVertexArray().vertexAttributeBuffer(0, this.verticesVBO);
this.configureTargetVAO(this.edgesVAO);
this.edgesVAO.instanceAttributeBuffer(7, this.pickingVBO);

const shaders = this.getDrawShaders();
this.program = context.createProgram(shaders.vs, shaders.fs);
Expand All @@ -120,6 +120,9 @@ export class CurvedPath extends Edges<CurvedPathEdgeData, GLCurvedPathEdgeTypes>
});

// printDataGL(context, this.targetVBO, data.length, kGLCurvedPathEdgeTypes);
this.pickingManager.on(PickingManager.events.hoverOn, this.pickingHandler);
this.pickingManager.on(PickingManager.events.hoverOff, this.pickingHandler);
this.pickingManager.on(PickingManager.events.click, this.pickingHandler);

this.localUniforms.uSegments = segments;
}
Expand All @@ -128,6 +131,25 @@ export class CurvedPath extends Edges<CurvedPathEdgeData, GLCurvedPathEdgeTypes>
// TODO: Implement destroy method
}

protected ingestData(context: App, data: unknown[], mappings: Partial<DataMappings<CurvedPathEdgeData>>): void {
this.pickingColors = this.pickingManager.allocatePickingColors(data.length);
super.ingestData(context, data, mappings);
}

protected packDataCB(): any {
return (index, entry): void => {
this.idArray.push(entry.id);

const indexStart = 4 * index;
entry.pickingColor = [
this.pickingColors.colors[indexStart],
this.pickingColors.colors[indexStart + 1],
this.pickingColors.colors[indexStart + 2],
this.pickingColors.colors[indexStart + 3],
];
};
}

public render(context:App, mode: RenderMode, uniforms: RenderUniforms): void {
setDrawCallUniforms(this.drawCall, uniforms);
setDrawCallUniforms(this.drawCall, this.localUniforms);
Expand Down Expand Up @@ -173,7 +195,7 @@ export class CurvedPath extends Edges<CurvedPathEdgeData, GLCurvedPathEdgeTypes>
protected getDataShader(): DataShader {
return {
vs: dataVS,
varyings: [ 'vSource', 'vTarget', 'vControl', 'vSourceColor', 'vTargetColor', 'vColorMix' ],
varyings: [ 'vSource', 'vTarget', 'vControl', 'vSourceColor', 'vTargetColor', 'vColorMix', 'vPickingColor' ],
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/graph/edges/path/StraightPath.data.vs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ layout(location=1) in uint aTargetIndex;
layout(location=2) in uvec2 aControl;
layout(location=3) in uint aSourceColor;
layout(location=4) in uint aTargetColor;
layout(location=5) in uvec4 aPickingColor;

uniform sampler2D uGraphPoints;

Expand All @@ -13,6 +14,7 @@ flat out uint fTarget;
flat out uint fSourceColor;
flat out uint fTargetColor;
flat out vec2 fColorMix;
flat out uvec4 fPickingColor;

void main() {
fSource = aSourceIndex;
Expand All @@ -22,4 +24,6 @@ void main() {
fTargetColor = aTargetColor;

fColorMix = vec2(float(aControl[0]) / float(aControl[1]), float(aControl[0] + 1u) / float(aControl[1]));

fPickingColor = aPickingColor;
}
10 changes: 10 additions & 0 deletions src/graph/edges/path/StraightPath.picking.fs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#version 300 es
precision highp float;

flat in vec4 fPickingColor;

out vec4 fragColor;

void main() {
fragColor = fPickingColor;
}
Loading

0 comments on commit 2a58eb9

Please sign in to comment.