Skip to content
Snippets Groups Projects
graph.ts 4.54 KiB
Newer Older
  • Learn to ignore specific revisions
  • import * as Config from "../config";
    
    
    interface Link {
        source: string;
        target: string;
        type?: string;
    }
    
    
    export interface LinkData {
        source: NodeData;
        target: NodeData;
        type?: string;
    }
    
    export interface NodeData {
        id: string;
        name: string;
        description?: string;
    
        icon?: string;
        banner?: string;
    
        type?: string;
        video?: string;
    
        references?: string[];
    
        neighbors: NodeData[];
        links: LinkData[];
    }
    
    export interface Coordinate {
        x: number;
        y: number;
        z: number;
    }
    
    
    export default class Graph {
    
        public nodes: NodeData[];
        public links: LinkData[];
        private idToNode: Map<string, NodeData>;
    
        public edgeColors: Map<string, string>;
        public nodeColors: Map<string, string>;
    
        constructor(nodes: NodeData[], links: Link[]) {
    
            this.nodes = nodes;
            this.idToNode = new Map<string, NodeData>();
            nodes.forEach((node) => {
                this.idToNode.set(node.id, node);
            });
    
    
            this.links = links.map((link) => {
                return {
                    source: this.idToNode.get(link.source),
                    target: this.idToNode.get(link.target),
                    type: link.type,
                };
            });
    
    
            this.edgeColors = new Map<string, string>();
            this.nodeColors = new Map<string, string>();
    
    
            this.resetNodeData();
    
            this.updateNodeData();
    
            this.mapNodeColors();
            this.mapLinkColors();
    
            for (const node of this.nodes) {
                node.neighbors = [];
                node.links = [];
            }
        }
    
    
        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.resetNodeData();
    
            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);
            });
        }
    
    
        public node(id: string): NodeData {
    
            return this.idToNode.get(id);
        }
    
    
        /**
         * Maps the colors of the color palette to the different edge types
         */
        private mapLinkColors() {
            // TODO: Legacy - is there a use-case for link types?
            const linkClasses = this.getLinkClasses();
            for (let i = 0; i < linkClasses.length; i++) {
                this.edgeColors.set(
                    linkClasses[i],
                    Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length]
                );
            }
        }
    
        /**
         * Maps the colors of the color palette to the different edge types
         */
        private mapNodeColors() {
            const nodeClasses = this.getNodeClasses();
            for (let i = 0; i < nodeClasses.length; i++) {
                this.nodeColors.set(
                    nodeClasses[i],
                    Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length]
                );
            }
        }
    
    
        /**
         * Returns an array containing the different edge types of the graph.
         * @returns {*[]}
         */
    
        public getLinkClasses(): string[] {
    
            const linkClasses: string[] = [];
    
            this.links.forEach((link) => linkClasses.push(link.type));
            return [...new Set(linkClasses)].map((c) => String(c));
        }
    
    
        public getNodeClasses(): string[] {
    
            const nodeClasses: string[] = [];
            this.nodes.forEach((node) => nodeClasses.push(node.type));
            return [...new Set(nodeClasses)].map((c) => String(c));
        }
    
            nodeTypes: Map<string, boolean>,
    
            linkTypes?: Map<string, boolean>
    
            // Filter nodes depending on type
            const nodes = this.nodes.filter((l) => nodeTypes.get(l.type));
    
            // Filter links depending on type
    
            let links;
            if (linkTypes === undefined) {
                links = this.links;
            } else {
                links = this.links.filter((l) => linkTypes.get(l.type));
            }
    
            // Filter links which are connected to an invisible node
            links = links.filter(
                (l) => nodeTypes.get(l.source.type) && nodeTypes.get(l.target.type)
            );
    
    
            return new Graph(
    
                links.map((link) => {
                    return {
                        source: link.source.id,
                        target: link.target.id,
                        type: link.type,
                    };
                })