Skip to content
Snippets Groups Projects
state.ts 8.85 KiB
Newer Older
  • Learn to ignore specific revisions
  • import Tool from "./tools/tool";
    import UndoTool from "./tools/undotool";
    import RedoTool from "./tools/redotool";
    import SelectTool from "./tools/selecttool";
    import DeleteTool from "./tools/deletetool";
    import AddNodeTool from "./tools/addnodetool";
    import ConnectTool from "./tools/connecttool";
    
    import SettingsTool from "./tools/settingstool";
    
    import SaveTool from "./tools/savetool";
    
    import Toolbar from "./toolbar";
    
    import { DRAG_THRESHOLD_2D } from "../../config";
    
    import { GraphElement } from "./structures/graph/graphelement";
    import { Node } from "./structures/graph/node";
    
    
    export const TOOLS = {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        undo: new UndoTool("undo"),
        redo: new RedoTool("redo"),
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        select: new SelectTool("select"),
        delete: new DeleteTool("delete"),
    
        addnode: new AddNodeTool("addnode"),
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        connect: new ConnectTool("connect"),
    
        settings: new SettingsTool("settings"),
    
        save: new SaveTool("save"),
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    };
    
    
    export const CONTEXT = {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        node: "node",
        link: "link",
        mixed: "mixed",
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    };
    
    
    export class State extends Tool {
    
        display: Toolbar;
        tool: Tool;
        previousTool: Tool;
        selectedItem: GraphElement;
        selectedItems: Set<GraphElement>;
        itemsContext: any;
        labelVisible: boolean;
        keyStates: any;
    
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        constructor() {
            super("State");
    
    
            this.display = new Toolbar(TOOLS);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            this.tool = undefined;
            this.setTool(TOOLS.select);
    
            // Shared variables
            this.selectedItem = undefined;
            this.selectedItems = new Set();
    
            this.itemsContext = CONTEXT.nothing;
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.labelVisible = true;
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            this.keyStates = {};
        }
    
    
        setTool(tool: Tool) {
    
            if (this.tool === tool) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
                return;
            }
    
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            if (this.tool !== undefined) {
    
                this.tool.deactivateTool(tool);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.previousTool = this.tool;
            this.tool = tool;
            this.display.setSelectedTool(tool);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            if (this.tool !== undefined) {
    
                this.tool.activateTool();
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        }
    
    
        setSelectedItem(item: GraphElement) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.selectedItem = item;
        }
    
    
        addSelectedItem(item: GraphElement) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.selectedItems.add(item);
        }
    
    
        addSelectedItems(items: GraphElement) {
    
            Object.values(items).forEach((item) => {
                this.selectedItems.add(item);
            });
        }
    
    
        removeSelectedItem(item: GraphElement) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.selectedItems.delete(item);
        }
    
        clearSelectedItems() {
            this.selectedItems.clear();
    
            this.itemsContext = CONTEXT.nothing;
    
        onNodeClick(node: GraphElement) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.tool.onNodeClick(node);
        }
    
    
        onNodeDragEnd(node: any, translate: any) {
    
            // Handle as click event, if drag distance under a certain threshold
    
            const distanceDragged = Math.sqrt(
    
                Math.pow(translate.x, 2) + Math.pow(translate.y, 2)
            );
    
            if (distanceDragged < DRAG_THRESHOLD_2D) {
                this.onNodeClick(node);
                return;
            } else {
                this.tool.onNodeDragEnd(node, translate);
            }
        }
    
    
        onLinkClick(link: any) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.tool.onLinkClick(link);
        }
    
    
        linkColor(link: any) {
    
        nodeColor(node: any) {
    
        onKeyDown(key: any) {
            const id = this.getKeyId(key);
            const previous = this.keyStates[id];
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            this.keyStates[id] = true;
    
            if (previous !== true) {
                this.tool.onKeyDown(key);
            }
        }
    
    
        onKeyUp(key: any) {
            const id = this.getKeyId(key);
            const previous = this.keyStates[id];
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            this.keyStates[id] = false;
    
            if (previous !== false) {
                this.tool.onKeyUp(key);
            }
        }
    
    
        getKeyId(key: any) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            return key.keyCode;
        }
    
    
        nodeCanvasObject(node: any, ctx: any, globalScale: any) {
            const toolValue = this.tool.nodeCanvasObject(node, ctx);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
            // TODO: Clean up function
    
            // add ring just for highlighted nodes
            if (this.selectedItem === node || this.selectedItems.has(node)) {
                ctx.beginPath();
    
                ctx.arc(node.x, node.y, 4 * 0.6, 0, 2 * Math.PI, false);
                ctx.fillStyle = this.selectedItem === node ? "red" : "white";
    
    Maximilian Giller's avatar
    Maximilian Giller committed
                ctx.fill();
            }
    
            // Draw image
    
            const imageSize = 12;
            if (node.icon !== undefined) {
                const img = new Image();
                img.src = node.icon.link;
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
                ctx.drawImage(
                    img,
    
                    node.x - imageSize / 2,
                    node.y - imageSize / 2,
                    imageSize,
                    imageSize
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            if (this.labelVisible) {
    
                const label = node.label;
    
                const fontSize = 11 / globalScale;
    
    Maximilian Giller's avatar
    Maximilian Giller committed
                ctx.font = `${fontSize}px Sans-Serif`;
                const textWidth = ctx.measureText(label).width;
    
                const bckgDimensions = [textWidth, fontSize].map(
                    (n) => n + fontSize * 0.2
                ); // some padding
    
    
                const nodeHeightOffset = imageSize / 3 + bckgDimensions[1];
    
                ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
                ctx.fillRect(
                    node.x - bckgDimensions[0] / 2,
                    node.y - bckgDimensions[1] / 2 + nodeHeightOffset,
                    ...bckgDimensions
                );
    
                ctx.textAlign = "center";
                ctx.textBaseline = "middle";
                ctx.fillStyle = "white";
    
    Maximilian Giller's avatar
    Maximilian Giller committed
                ctx.fillText(label, node.x, node.y + nodeHeightOffset);
            }
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            // TODO: Render label as always visible
        }
    
    
        nodePointerAreaPaint(node: any, color: any, ctx: any) {
            const toolValue = this.tool.nodePointerAreaPaint(node, color, ctx);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
    
            const imageSize = 12;
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            ctx.fillStyle = color;
            ctx.fillRect(
    
                node.x - imageSize / 2,
                node.y - imageSize / 2,
                imageSize,
                imageSize
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            ); // draw square as pointer trap
        }
    
    
        nodeCanvasObjectMode(node: any) {
            const toolValue = this.tool.nodeCanvasObjectMode(node);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
            return "after";
        }
    
    
        linkCanvasObjectMode(link: any) {
            const toolValue = this.tool.linkCanvasObjectMode(link);
    
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
    
        linkCanvasObject(link: any, ctx: any, globalScale: any) {
            const toolValue = this.tool.linkCanvasObject(link, ctx);
    
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
    
            // Links already initialized?
            if (link.source.x === undefined) {
                return undefined;
            }
    
            // Draw gradient link
    
            const gradient = ctx.createLinearGradient(
    
                link.source.x,
                link.source.y,
                link.target.x,
                link.target.y
            );
    
            // Have reversed colors
            // Color at source node referencing the target node and vice versa
    
            gradient.addColorStop("0", link.target.type.color);
            gradient.addColorStop("1", link.source.type.color);
    
    
            ctx.beginPath();
            ctx.moveTo(link.source.x, link.source.y);
            ctx.lineTo(link.target.x, link.target.y);
            ctx.strokeStyle = gradient;
            ctx.stroke();
    
            // Only render strokes on last link
            // var lastLink = graph.data[Graph.GRAPH_LINKS][graph.data[Graph.GRAPH_LINKS].length - 1];
            // if (link === lastLink) {
            //     ctx.stroke();
            // }
    
            return undefined;
        }
    
    
        linkWidth(link: any) {
            const toolValue = this.tool.linkWidth(link);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
    
            return this.isLinkHighlighted(link) ? 2 : 1;
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        }
    
        linkDirectionalParticles() {
    
            const toolValue = this.tool.linkDirectionalParticles();
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
            return 4;
        }
    
    
        linkDirectionalParticleWidth(link: any) {
            const toolValue = this.tool.linkDirectionalParticleWidth(link);
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
            if (toolValue !== undefined) {
                return toolValue;
            }
    
    
            return this.isLinkHighlighted(link) ? 4 : 0;
    
        onBackgroundClick(event: any, positions: any) {
    
            this.tool.onBackgroundClick(event, positions);
        }
    
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        redraw() {
            this.display.setSelectedTool(this.tool);
        }
    
    
        isLinkHighlighted(link: any) {
    
            return false;   // Code after that is causing issues
    
    
            if (this.selectedItem === link) {
                return true;
            }
    
            if (this.selectedItem.node) {
                return (this.selectedItem as Node).links.includes(link);
            }
    
            return false;
    
    Maximilian Giller's avatar
    Maximilian Giller committed
        }
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    
    
        setLabelVisibility(visibility: boolean) {
    
    Maximilian Giller's avatar
    Maximilian Giller committed
            this.labelVisible = visibility;
        }
    
    Maximilian Giller's avatar
    Maximilian Giller committed
    }