Skip to content

Commit

Permalink
[sigma] propagate graph types into sigma code #1359
Browse files Browse the repository at this point in the history
  • Loading branch information
sim51 committed Feb 8, 2024
1 parent c89cdce commit 7783ffa
Show file tree
Hide file tree
Showing 21 changed files with 271 additions and 133 deletions.
22 changes: 14 additions & 8 deletions packages/node-image/src/factory.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Attributes } from "graphology-types";
import Sigma from "sigma";
import { floatColor } from "sigma/utils";
import { NodeProgram } from "sigma/rendering/node";
Expand All @@ -11,21 +12,22 @@ import { Atlas, DEFAULT_TEXTURE_MANAGER_OPTIONS, TextureManager, TextureManagerO

const { UNSIGNED_BYTE, FLOAT } = WebGLRenderingContext;

interface CreateNodeImageProgramOptions extends TextureManagerOptions {
interface CreateNodeImageProgramOptions<N extends Attributes, E extends Attributes, G extends Attributes>
extends TextureManagerOptions {
// - If "background", color will be used to color full node behind the image.
// - If "color", color will be used to color image pixels (for pictograms)
drawingMode: "background" | "color";
// If true, the images are always cropped to the circle
keepWithinCircle: boolean;
// Allows overriding drawLabel and drawHover returned class static methods.
drawLabel: NodeLabelDrawingFunction | undefined;
drawHover: NodeLabelDrawingFunction | undefined;
drawLabel: NodeLabelDrawingFunction<N, E, G> | undefined;
drawHover: NodeLabelDrawingFunction<N, E, G> | undefined;
// The padding should be expressed as a [0, 1] percentage.
// A padding of 0.05 will always be 5% of the diameter of a node.
padding: number;
}

const DEFAULT_CREATE_NODE_IMAGE_OPTIONS: CreateNodeImageProgramOptions = {
const DEFAULT_CREATE_NODE_IMAGE_OPTIONS: CreateNodeImageProgramOptions<Attributes, Attributes, Attributes> = {
...DEFAULT_TEXTURE_MANAGER_OPTIONS,
drawingMode: "background",
keepWithinCircle: true,
Expand All @@ -50,17 +52,21 @@ const UNIFORMS = [
* hovered nodes (to prevent some flickering, mostly), this program must be
* "built" for each sigma instance:
*/
export default function getNodeImageProgram(options?: Partial<CreateNodeImageProgramOptions>): NodeProgramType {
export default function getNodeImageProgram<N extends Attributes, E extends Attributes, G extends Attributes>(
options?: Partial<CreateNodeImageProgramOptions<N, E, G>>,
): NodeProgramType<N, E, G> {
const {
drawHover,
drawLabel,
drawingMode,
keepWithinCircle,
padding,
...textureManagerOptions
}: CreateNodeImageProgramOptions = {
}: CreateNodeImageProgramOptions<N, E, G> = {
...DEFAULT_CREATE_NODE_IMAGE_OPTIONS,
...(options || {}),
drawLabel: undefined,
drawHover: undefined,
};

/**
Expand All @@ -70,7 +76,7 @@ export default function getNodeImageProgram(options?: Partial<CreateNodeImagePro
*/
const textureManager = new TextureManager(textureManagerOptions);

return class NodeImageProgram extends NodeProgram<(typeof UNIFORMS)[number]> {
return class NodeImageProgram extends NodeProgram<N, E, G, (typeof UNIFORMS)[number]> {
static readonly ANGLE_1 = 0;
static readonly ANGLE_2 = (2 * Math.PI) / 3;
static readonly ANGLE_3 = (4 * Math.PI) / 3;
Expand Down Expand Up @@ -103,7 +109,7 @@ export default function getNodeImageProgram(options?: Partial<CreateNodeImagePro
latestRenderParams?: RenderParams;
textureManagerCallback: () => void;

constructor(gl: WebGLRenderingContext, pickingBuffer: WebGLFramebuffer | null, renderer: Sigma) {
constructor(gl: WebGLRenderingContext, pickingBuffer: WebGLFramebuffer | null, renderer: Sigma<N, E, G>) {
super(gl, pickingBuffer, renderer);

this.textureManagerCallback = () => {
Expand Down
14 changes: 10 additions & 4 deletions packages/sigma/src/core/captors/captor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* ======================
* @module
*/
import { Attributes } from "graphology-types";

import { Coordinates, MouseCoords, TouchCoords, WheelCoords, TypedEventEmitter, EventsMapping } from "../../types";
import Sigma from "../../sigma";

Expand Down Expand Up @@ -103,13 +105,17 @@ export function getWheelDelta(e: WheelEvent): number {
/**
* Abstract class representing a captor like the user's mouse or touch controls.
*/
export default abstract class Captor<Events extends EventsMapping> extends TypedEventEmitter<Events> {
export default abstract class Captor<
Events extends EventsMapping,
N extends Attributes,
E extends Attributes,
G extends Attributes,
> extends TypedEventEmitter<Events> {
container: HTMLElement;
renderer: Sigma;
renderer: Sigma<N, E, G>;

constructor(container: HTMLElement, renderer: Sigma) {
constructor(container: HTMLElement, renderer: Sigma<N, E, G>) {
super();

// Properties
this.container = container;
this.renderer = renderer;
Expand Down
11 changes: 9 additions & 2 deletions packages/sigma/src/core/captors/mouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Sigma's captor dealing with the user's mouse.
* @module
*/
import { Attributes } from "graphology-types";

import { CameraState, MouseCoords, WheelCoords } from "../../types";
import Sigma from "../../sigma";
import Captor, { getWheelDelta, getMouseCoords, getPosition, getWheelCoords } from "./captor";
Expand Down Expand Up @@ -43,7 +45,12 @@ export type MouseCaptorEvents = {
*
* @constructor
*/
export default class MouseCaptor extends Captor<MouseCaptorEvents> {
export default class MouseCaptor<N extends Attributes, E extends Attributes, G extends Attributes> extends Captor<
MouseCaptorEvents,
N,
E,
G
> {
// State
enabled = true;
draggedEvents = 0;
Expand All @@ -60,7 +67,7 @@ export default class MouseCaptor extends Captor<MouseCaptorEvents> {
currentWheelDirection: -1 | 0 | 1 = 0;
lastWheelTriggerTime?: number;

constructor(container: HTMLElement, renderer: Sigma) {
constructor(container: HTMLElement, renderer: Sigma<N, E, G>) {
super(container, renderer);

// Binding methods
Expand Down
11 changes: 9 additions & 2 deletions packages/sigma/src/core/captors/touch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Sigma's captor dealing with touch.
* @module
*/
import { Attributes } from "graphology-types";

import { CameraState, Coordinates, Dimensions, TouchCoords } from "../../types";
import Captor, { getPosition, getTouchCoords, getTouchesArray } from "./captor";
import Sigma from "../../sigma";
Expand All @@ -29,7 +31,12 @@ export type TouchCaptorEvents = {
*
* @constructor
*/
export default class TouchCaptor extends Captor<TouchCaptorEvents> {
export default class TouchCaptor<N extends Attributes, E extends Attributes, G extends Attributes> extends Captor<
TouchCaptorEvents,
N,
E,
G
> {
enabled = true;
isMoving = false;
hasMoved = false;
Expand All @@ -43,7 +50,7 @@ export default class TouchCaptor extends Captor<TouchCaptorEvents> {
lastTouchesPositions?: Coordinates[];
lastTouches?: Touch[];

constructor(container: HTMLElement, renderer: Sigma) {
constructor(container: HTMLElement, renderer: Sigma<N, E, G>) {
super(container, renderer);

// Binding methods:
Expand Down
10 changes: 6 additions & 4 deletions packages/sigma/src/rendering/edge-labels.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { Attributes } from "graphology-types";

import { EdgeDisplayData, NodeDisplayData, PartialButFor } from "../types";
import { Settings } from "../settings";

export type EdgeLabelDrawingFunction = (
export type EdgeLabelDrawingFunction<N extends Attributes, E extends Attributes, G extends Attributes> = (
context: CanvasRenderingContext2D,
edgeData: PartialButFor<EdgeDisplayData, "label" | "color" | "size">,
sourceData: PartialButFor<NodeDisplayData, "x" | "y" | "size">,
targetData: PartialButFor<NodeDisplayData, "x" | "y" | "size">,
settings: Settings,
settings: Settings<N, E, G>,
) => void;

export function drawStraightEdgeLabel(
export function drawStraightEdgeLabel<N extends Attributes, E extends Attributes, G extends Attributes>(
context: CanvasRenderingContext2D,
edgeData: PartialButFor<EdgeDisplayData, "label" | "color" | "size">,
sourceData: PartialButFor<NodeDisplayData, "x" | "y" | "size">,
targetData: PartialButFor<NodeDisplayData, "x" | "y" | "size">,
settings: Settings,
settings: Settings<N, E, G>,
): void {
const size = settings.edgeLabelSize,
font = settings.edgeLabelFont,
Expand Down
62 changes: 41 additions & 21 deletions packages/sigma/src/rendering/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
* @module
*/
import Sigma from "../sigma";
import { Attributes } from "graphology-types";
import { AbstractProgram, Program } from "./program";
import { NodeDisplayData, EdgeDisplayData, RenderParams } from "../types";
import { EdgeLabelDrawingFunction } from "./edge-labels";
import { indexToColor } from "../utils";

export abstract class AbstractEdgeProgram extends AbstractProgram {
static drawLabel: EdgeLabelDrawingFunction | undefined;
export abstract class AbstractEdgeProgram<
N extends Attributes,
E extends Attributes,
G extends Attributes,
> extends AbstractProgram<N, E, G> {
abstract drawLabel: EdgeLabelDrawingFunction<N, E, G> | undefined;

abstract process(
edgeIndex: number,
Expand All @@ -22,11 +27,16 @@ export abstract class AbstractEdgeProgram extends AbstractProgram {
): void;
}

export abstract class EdgeProgram<Uniform extends string = string>
extends Program<Uniform>
implements AbstractEdgeProgram
export abstract class EdgeProgram<
N extends Attributes,
E extends Attributes,
G extends Attributes,
Uniform extends string = string,
>
extends Program<N, E, G, Uniform>
implements AbstractEdgeProgram<N, E, G>
{
static drawLabel: EdgeLabelDrawingFunction | undefined = undefined;
drawLabel: EdgeLabelDrawingFunction<N, E, G> | undefined = undefined;

kill(): void {
return undefined;
Expand Down Expand Up @@ -60,12 +70,18 @@ export abstract class EdgeProgram<Uniform extends string = string>
): void;
}

class EdgeImageClass implements AbstractEdgeProgram {
static drawLabel: EdgeLabelDrawingFunction | undefined = undefined;

constructor(_gl: WebGLRenderingContext, _pickingBuffer: WebGLFramebuffer | null, _renderer: Sigma) {
class EdgeImageClass<
N extends Attributes = Attributes,
E extends Attributes = Attributes,
G extends Attributes = Attributes,
> implements AbstractEdgeProgram<N, E, G>
{
constructor(_gl: WebGLRenderingContext, _pickingBuffer: WebGLFramebuffer | null, _renderer: Sigma<N, E, G>) {
return this;
}

drawLabel: EdgeLabelDrawingFunction<N, E, G> | undefined = undefined;

kill(): void {
return undefined;
}
Expand All @@ -85,7 +101,11 @@ class EdgeImageClass implements AbstractEdgeProgram {
return undefined;
}
}
export type EdgeProgramType = typeof EdgeImageClass;
export type EdgeProgramType<
N extends Attributes = Attributes,
E extends Attributes = Attributes,
G extends Attributes = Attributes,
> = typeof EdgeImageClass<N, E, G>;

/**
* Helper function combining two or more programs into a single compound one.
Expand All @@ -96,21 +116,21 @@ export type EdgeProgramType = typeof EdgeImageClass;
* @param {function} drawLabel - An optional edge "draw label" function.
* @return {function}
*/
export function createEdgeCompoundProgram(
programClasses: Array<EdgeProgramType>,
drawLabel?: EdgeLabelDrawingFunction,
): EdgeProgramType {
return class EdgeCompoundProgram implements AbstractEdgeProgram {
static drawLabel = drawLabel;

programs: Array<AbstractEdgeProgram>;

constructor(gl: WebGLRenderingContext, pickingBuffer: WebGLFramebuffer | null, renderer: Sigma) {
export function createEdgeCompoundProgram<N extends Attributes, E extends Attributes, G extends Attributes>(
programClasses: Array<EdgeProgramType<N, E, G>>,
drawLabel?: EdgeLabelDrawingFunction<N, E, G>,
): EdgeProgramType<N, E, G> {
return class EdgeCompoundProgram implements AbstractEdgeProgram<N, E, G> {
programs: Array<AbstractEdgeProgram<N, E, G>>;

constructor(gl: WebGLRenderingContext, pickingBuffer: WebGLFramebuffer | null, renderer: Sigma<N, E, G>) {
this.programs = programClasses.map((Program) => {
return new Program(gl, pickingBuffer, renderer);
});
}

drawLabel = drawLabel;

reallocate(capacity: number): void {
this.programs.forEach((program) => program.reallocate(capacity));
}
Expand Down
9 changes: 5 additions & 4 deletions packages/sigma/src/rendering/node-hover.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Attributes } from "graphology-types";
import { Settings } from "../settings";
import { NodeDisplayData, PartialButFor } from "../types";
import { drawDiscNodeLabel } from "./node-labels";

export type NodeHoverDrawingFunction = (
export type NodeHoverDrawingFunction<N extends Attributes, E extends Attributes, G extends Attributes> = (
context: CanvasRenderingContext2D,
data: PartialButFor<NodeDisplayData, "x" | "y" | "size" | "label" | "color">,
settings: Settings,
settings: Settings<N, E, G>,
) => void;

/**
Expand All @@ -14,10 +15,10 @@ export type NodeHoverDrawingFunction = (
* - if the label box is bigger than node size => display a label box that contains the node with a shadow
* - else node with shadow and the label box
*/
export function drawDiscNodeHover(
export function drawDiscNodeHover<N extends Attributes, E extends Attributes, G extends Attributes>(
context: CanvasRenderingContext2D,
data: PartialButFor<NodeDisplayData, "x" | "y" | "size" | "label" | "color">,
settings: Settings,
settings: Settings<N, E, G>,
): void {
const size = settings.labelSize,
font = settings.labelFont,
Expand Down
9 changes: 5 additions & 4 deletions packages/sigma/src/rendering/node-labels.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Attributes } from "graphology-types";
import { NodeDisplayData, PartialButFor } from "../types";
import { Settings } from "../settings";

export type NodeLabelDrawingFunction = (
export type NodeLabelDrawingFunction<N extends Attributes, E extends Attributes, G extends Attributes> = (
context: CanvasRenderingContext2D,
data: PartialButFor<NodeDisplayData, "x" | "y" | "size" | "label" | "color">,
settings: Settings,
settings: Settings<N, E, G>,
) => void;

export function drawDiscNodeLabel(
export function drawDiscNodeLabel<N extends Attributes, E extends Attributes, G extends Attributes>(
context: CanvasRenderingContext2D,
data: PartialButFor<NodeDisplayData, "x" | "y" | "size" | "label" | "color">,
settings: Settings,
settings: Settings<N, E, G>,
): void {
if (!data.label) return;

Expand Down
Loading

0 comments on commit 7783ffa

Please sign in to comment.