Skip to content
Snippets Groups Projects
graph.ts 4.71 KiB
Newer Older
import * as Config from "../config";

    source: string;
    target: string;
    type?: string;
}

export interface Link {
    source: Node;
    target: Node;
    type?: GraphObjectType;
    name: string;
    description?: string;
    icon?: string;
    banner?: string;
    video?: string;
    references?: string[];
}

interface NodeData extends NodeContent {
    id: string;
    type?: string;
}

export interface Node extends NodeContent {
    id: string;
    type: GraphObjectType;

    neighbors: Node[];
    links: Link[];
}

export interface GraphObjectType {
    name: string;
    color?: string;
}

export interface Coordinate {
    x: number;
    y: number;
    z: number;
}

/**
 * Basic graph data structure.
 */
export default class Graph {
    public nodes: Node[];
    public links: Link[];
    public objectGroups: GraphObjectType[];
    public nameToObjectGroup: Map<string, GraphObjectType>;
    private idToNode: Map<string, Node>;

    constructor(
        nodes: NodeData[],
        links: LinkData[],
        objectGroups?: GraphObjectType[]
    ) {
        this.objectGroups = objectGroups ?? this.createObjectGroups(nodes);

        this.nameToObjectGroup = new Map<string, GraphObjectType>();
        this.objectGroups.forEach((group) =>
            this.nameToObjectGroup.set(group.name, group)
        );

        this.createNodes(nodes);

        this.links = links.map((link) => {
            return {
                source: this.idToNode.get(link.source),
                target: this.idToNode.get(link.target),
            };
        });

        this.updateNodeData();
    private createNodes(nodes: NodeData[]) {
        this.nodes = [];
        for (const nodeData of nodes) {
            const { type, ...nodeVars } = nodeData;
            const node = { ...nodeVars } as Node;
            node.type = this.nameToObjectGroup.get(type);
            node.neighbors = [];
            node.links = [];

        this.idToNode = new Map<string, Node>();
        this.nodes.forEach((node) => {
            this.idToNode.set(node.id, node);
        });
    private removeFloatingNodes() {
        this.nodes = this.nodes.filter((node) => node.neighbors.length > 0);
    }

    /**
     * Updates the graph data structure to contain additional values.
     * Creates a 'neighbors' and 'links' array for each node object.
     */
        this.links.forEach((link) => {
            const a = link.source;
            const b = link.target;
            a.neighbors.push(b);
            b.neighbors.push(a);
            a.links.push(link);
            b.links.push(link);
        });
    }

        return this.idToNode.get(id);
    }

    private createObjectGroups(nodes: NodeData[]): GraphObjectType[] {
        const objectGroups: GraphObjectType[] = [];
        const nodeClasses: string[] = [];
        nodes.forEach((node) => nodeClasses.push(node.type));
        const nodeTypes = [...new Set(nodeClasses)].map((c) => String(c));

        for (let i = 0; i < nodeTypes.length; i++) {
            objectGroups.push({
                name: nodeTypes[i],
                color: Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length],
            });
        nodeTypes: Map<string, boolean>,
        linkTypes?: Map<string, boolean>
        const nodes = this.nodes.filter((l) => nodeTypes.get(l.type.name));
        let links;
        if (linkTypes === undefined) {
            links = this.links;
        } else {
            links = this.links.filter((l) => linkTypes.get(l.type.name));
        // Filter links which are connected to an invisible node
        links = links.filter(
            (l) =>
                nodeTypes.get(l.source.type.name) &&
                nodeTypes.get(l.target.type.name)
        // Convert to data objects and create new graph.
        // Using spread syntax to simplify object copying.
        return new Graph(
            nodes.map((node) => {
                // eslint-disable-next-line no-unused-vars
                const { type, neighbors, links, ...nodeVars } = node;
                const nodeData = { ...nodeVars } as NodeData;
                nodeData.type = type.name;
                return nodeData;
            }),
            links.map((link) => {
                return {
                    source: link.source.id,
                    target: link.target.id,
                };