From 0b44275e6a0f107d538d099c4715f11d2d96130b Mon Sep 17 00:00:00 2001 From: Max <m.giller.dev@gmail.com> Date: Thu, 8 Jul 2021 20:45:16 +0200 Subject: [PATCH] Basic editor --- editor/editor.html | 57 +++++++++ editor/images/tools/collect.png | Bin 0 -> 3591 bytes editor/images/tools/delete.png | Bin 0 -> 3027 bytes editor/images/tools/select.png | Bin 0 -> 3076 bytes editor/js/display.js | 140 ++++++++++++++++++++++ editor/js/editor.js | 56 +++++++++ editor/js/graph.js | 162 ++++++++++++++++++++++++++ editor/js/state.js | 198 ++++++++++++++++++++++++++++++++ editor/js/tools/collecttool.js | 37 ++++++ editor/js/tools/deletetool.js | 13 +++ editor/js/tools/selecttool.js | 19 +++ editor/js/tools/tool.js | 77 +++++++++++++ knowledge-space.php | 15 +++ 13 files changed, 774 insertions(+) create mode 100644 editor/editor.html create mode 100644 editor/images/tools/collect.png create mode 100644 editor/images/tools/delete.png create mode 100644 editor/images/tools/select.png create mode 100644 editor/js/display.js create mode 100644 editor/js/editor.js create mode 100644 editor/js/graph.js create mode 100644 editor/js/state.js create mode 100644 editor/js/tools/collecttool.js create mode 100644 editor/js/tools/deletetool.js create mode 100644 editor/js/tools/selecttool.js create mode 100644 editor/js/tools/tool.js diff --git a/editor/editor.html b/editor/editor.html new file mode 100644 index 0000000..d45958e --- /dev/null +++ b/editor/editor.html @@ -0,0 +1,57 @@ +<div> + <style> + section { + border: 1px lightgrey solid; + border-radius: 1px; + margin: 5px; + padding: 2.5px; + width: auto; + } + + section > * { + margin: 2.5px; + } + + .selected { + background-color: lightblue; + } + </style> + + <script src="https://unpkg.com/force-graph"></script> + <!--<script src="../../dist/force-graph.js"></script>--> + + <script> + const PLUGIN_PATH = "%WWW%"; + </script> + + <script src="%WWW%editor/js/graph.js"></script> + <script src="%WWW%editor/js/tools/tool.js"></script> + <script src="%WWW%editor/js/tools/selecttool.js"></script> + <script src="%WWW%editor/js/tools/collecttool.js"></script> + <script src="%WWW%editor/js/tools/deletetool.js"></script> + <script src="%WWW%editor/js/display.js"></script> + <script src="%WWW%editor/js/state.js"></script> + <script src="%WWW%editor/js/editor.js"></script> + + <h1>Interface</h1> + <div id="2d-graph"></div> + <section id="toolbar"></section> + <section> + <h3 id="selected-item">Nothing selected</h3> + <ul id="selected-params"></ul> + <button onclick="saveCurrentNode()">Save NOT YET IMPLEMENTED</button> + <section> + <h4>Sources</h4> + <ul id="selected-sources"></ul> + </section> + <section> + <h4>Targets</h4> + <ul id="selected-targets"></ul> + </section> + </section> + <section> + <h3>Collected items</h3> + <ul id="selected-items"></ul> + <button onclick="state.clearSelectedItems()">Clear</button> + </section> +</div> diff --git a/editor/images/tools/collect.png b/editor/images/tools/collect.png new file mode 100644 index 0000000000000000000000000000000000000000..623d3a978005133e87994a16e67dbe9ab7259875 GIT binary patch literal 3591 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 N002ovPDHLkV1i=P(jNc- literal 0 HcmV?d00001 diff --git a/editor/images/tools/delete.png b/editor/images/tools/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..007f69a325d89d0d5e4eee3d2b0df7f8844c4d13 GIT binary patch literal 3027 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 Vxfy;?c~JlW002ovPDHLkV1lvOu~q;8 literal 0 HcmV?d00001 diff --git a/editor/images/tools/select.png b/editor/images/tools/select.png new file mode 100644 index 0000000000000000000000000000000000000000..840c4f5df6c058d208879fe7d5bfe906eab43015 GIT binary patch literal 3076 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 S_tpOZ0000<MNUMnLSTXyE5pVB literal 0 HcmV?d00001 diff --git a/editor/js/display.js b/editor/js/display.js new file mode 100644 index 0000000..de6379c --- /dev/null +++ b/editor/js/display.js @@ -0,0 +1,140 @@ +const ID_TOOLBAR = "#toolbar"; +const ID_SELECTEDITEM = "#selected-item"; +const ID_SELECTED_PARAMS = "#selected-params"; +const ID_SELECTED_SOURCES = "#selected-sources"; +const ID_SELECTED_TARGETS = "#selected-targets"; +const ID_SELECTEDITEMS = "#selected-items"; + +const DOM_LIST_ITEM = "li"; + +const TOOL_ICON_SRC = PLUGIN_PATH + "editor/images/tools/"; +const TOOL_ICON_FORMAT = ".png"; +const TOOL_SELECTED_CLASS = "selected"; + +class Display { + constructor(tools) { + this.tools = Object.values(tools); + this.previousTool = undefined; + + this.renderToolbar(this.tools); + } + + setSelectedTool(tool) { + var selectedTool = jQuery(Display.getToolId(tool)); + selectedTool.addClass(TOOL_SELECTED_CLASS); + + if (this.previousTool !== undefined) { + var previousTool = jQuery(Display.getToolId(this.previousTool)); + previousTool.removeClass(TOOL_SELECTED_CLASS); + } + + this.previousTool = tool; + } + + renderToolbar(tools) { + this.fillDomList(ID_TOOLBAR, tools, this.toolRenderer); + } + + static getToolId(tool) { + return ID_TOOLBAR + "-" + tool.getKey(); + } + + toolRenderer(tool) { + return ( + '<button id="' + + Display.getToolId(tool).substr(1) + // Remove # from id + '"onclick="state.setTool(TOOLS.' + + tool.getKey() + + ')" title="' + + tool.getName() + + '"><img src="' + + TOOL_ICON_SRC + + tool.getKey() + + TOOL_ICON_FORMAT + + '"></button>' + ); + } + + setSelectedItem(item) { + jQuery(ID_SELECTEDITEM).html(Display.toStr(item)); + + var paramsDOM = jQuery(ID_SELECTED_PARAMS); + paramsDOM.empty(); + + var params = NODE_PARAMS; + if (item.link) { + params = LINK_PARAMS; + } + + params.forEach((param) => { + paramsDOM.append( + "<" + + DOM_LIST_ITEM + + ">" + + param + + ' <textarea>' + + (item[param] === undefined ? "" : item[param]) + + '</textarea></' + + DOM_LIST_ITEM + + ">" + ); + }); + + // Render Source and Target list + var sources = []; + var targets = []; + if (item.node) { + var nodes = graph.data[GRAPH_NODES]; + for (var i = 0; i < nodes.length; i++) { + if (graph.existsLink(nodes[i][NODE_ID], item[NODE_ID])) { + sources.push(nodes[i]); + } else if (graph.existsLink(item[NODE_ID], nodes[i][NODE_ID])) { + targets.push(nodes[i]); + } + } + } else if (item.link) { + sources.push(item[LINK_SOURCE]); + targets.push(item[LINK_TARGET]); + } + + this.fillDomList(ID_SELECTED_SOURCES, sources, this.graphItemRenderer); + this.fillDomList(ID_SELECTED_TARGETS, targets, this.graphItemRenderer); + } + + setSelectedItems(items, itemsContext) { + this.fillDomList(ID_SELECTEDITEMS, items, this.graphItemRenderer); + } + + graphItemRenderer(item) { + return ( + "<" + + DOM_LIST_ITEM + + ">" + + Display.toStr(item) + + "</" + + DOM_LIST_ITEM + + ">" + ); + } + + fillDomList(listId, items, itemRenderer) { + var listCont = jQuery(listId); + listCont.empty(); + + items.forEach((i) => listCont.append(itemRenderer(i))); + } + + static toStr(item) { + if (item.node) { + return item[NODE_LABEL] + " [" + item[NODE_ID] + "]"; + } else if (item.link) { + return ( + Display.toStr(item[LINK_SOURCE]) + + " <-> " + + Display.toStr(item[LINK_TARGET]) + ); + } else { + return "UNDEFINED"; + } + } +} diff --git a/editor/js/editor.js b/editor/js/editor.js new file mode 100644 index 0000000..88b22de --- /dev/null +++ b/editor/js/editor.js @@ -0,0 +1,56 @@ +var state; +var graphObj; + +window.onload = function () { + fetch(JSON_CONFIG) + .then((r) => { + return r.json(); + }) + .then((graphConfig) => { + state = new State(); + + graph.data = graphConfig; + graph.addIdentifiers(); + load(); + + // Deactivate physics after a short delay + setTimeout(() => { + graph.stopPhysics(); + }, STOP_PHYSICS_DELAY); + }); +}; + +document.onkeydown = (e) => state.onKeyDown(e); +document.onkeyup = (e) => state.onKeyUp(e); + +function downloadJson() { + // TODO: Clean up + // source: https://stackoverflow.com/a/42883108/7376120 + var jsonBlob = new Blob([JSON.stringify(getOnlygraph.data())], { + type: "application/json;charset=utf-8", + }); + var link = window.URL.createObjectURL(jsonBlob); + window.location = link; +} + +function load() { + const graphContainer = document.getElementById("2d-graph"); + const width = graphContainer.offsetWidth; + + graphObj = ForceGraph()(graphContainer) + .height(600) + .width(width) + .graphData(graph.data) + .nodeLabel(NODE_LABEL) + .nodeAutoColorBy(NODE_GROUP) + .onNodeClick((node) => state.onNodeClick(node)) + .autoPauseRedraw(false) // keep redrawing after engine has stopped + .linkWidth((link) => state.linkWidth(link)) + .linkDirectionalParticles(state.linkDirectionalParticles()) + .linkDirectionalParticleWidth((link) => + state.linkDirectionalParticleWidth(link) + ) + .nodeCanvasObjectMode((node) => state.nodeCanvasObjectMode(node)) + .nodeCanvasObject((node, ctx) => state.nodeCanvasObject(node, ctx)) + .onLinkClick((link) => state.onLinkClick(link)); +} diff --git a/editor/js/graph.js b/editor/js/graph.js new file mode 100644 index 0000000..52fffdc --- /dev/null +++ b/editor/js/graph.js @@ -0,0 +1,162 @@ +const NODE_LABEL = "name"; +const NODE_ID = "id"; +const NODE_GROUP = "group"; +const NODE_DESCRIPTION = "description"; +const NODE_IMAGE = "image"; + +const LINK_SOURCE = "source"; +const LINK_TARGET = "target"; +const LINK_TYPE = "type"; +const LINK_PARTICLE_COUNT = 4; + +const GRAPH_NODES = "nodes"; +const GRAPH_LINKS = "links"; + +const IMAGE_SIZE = 12; +const IMAGE_SRC = PLUGIN_PATH + "datasets/images/" + +const LINK_PARAMS = [LINK_TYPE]; +const NODE_PARAMS = [NODE_ID, NODE_LABEL, NODE_IMAGE, NODE_DESCRIPTION]; + +const JSON_CONFIG = PLUGIN_PATH + "datasets/aud1.json"; + +const STOP_PHYSICS_DELAY = 5000; // ms + + + +const graph = { + data: undefined, + + deleteNode(nodeId) { + // Delete node from nodes + graph.data[GRAPH_NODES] = graph.data[GRAPH_NODES].filter( + (n) => n[NODE_ID] !== nodeId + ); + + // Delete links with node + graph.data[GRAPH_LINKS] = graph.data[GRAPH_LINKS].filter( + (l) => + l[LINK_SOURCE][NODE_ID] !== nodeId && + l[LINK_TARGET][NODE_ID] !== nodeId + ); + }, + + stopPhysics() { + graph.data[GRAPH_NODES].forEach((n) => { + n.fx = n.x; + n.fy = n.y; + }); + }, + + addIdentifiers() { + graph.data[GRAPH_NODES].forEach((n) => { + n.node = true; + n.link = false; + }); + graph.data[GRAPH_LINKS].forEach((l) => { + l.node = false; + l.link = true; + }); + }, + + deleteLink(sourceId, targetId) { + // Only keep links, of one of the nodes is different + graph.data[GRAPH_LINKS] = graph.data[GRAPH_LINKS].filter( + (l) => + l[LINK_SOURCE][NODE_ID] !== sourceId || + l[LINK_TARGET][NODE_ID] !== targetId + ); + }, + + isLinkOnNode(link, node) { + if (link === undefined || node === undefined) { + return false; + } + + if (link.link !== true || node.node !== true) { + return false; + } + + return ( + link[LINK_SOURCE][NODE_ID] === node[NODE_ID] || + link[LINK_TARGET][NODE_ID] === node[NODE_ID] + ); + }, + + existsLink(sourceId, targetId) { + const links = graph.data[GRAPH_LINKS]; + + for (var i = 0; i < links.length; i++) { + var link = links[i]; + if ( + link[LINK_SOURCE][NODE_ID] === sourceId && + link[LINK_TARGET][NODE_ID] === targetId + ) { + return true; + } + } + + return false; + }, + + connectNodes(sourceId, targetIds) { + targetIds.forEach((targetId) => { + if ( + graph.existsLink(sourceId, targetId) || + graph.existsLink(targetId, sourceId) + ) { + return; + } + + var link = {}; + + link[LINK_SOURCE] = sourceId; + link[LINK_TARGET] = targetId; + + graph.data[GRAPH_LINKS].push(link); + }); + }, + + getCleanData() { + var cleanData = {}; + cleanData[GRAPH_LINKS] = []; + cleanData[GRAPH_NODES] = []; + + graph.data[GRAPH_LINKS].forEach((link) => + cleanData[GRAPH_LINKS].push(graph.getCleanLink(link)) + ); + + graph.data[GRAPH_NODES].forEach((node) => + cleanData[GRAPH_NODES].push(graph.getCleanNode(node)) + ); + + console.log(cleanData); + return cleanData; + }, + + getCleanNode(node) { + var cleanNode = {}; + + NODE_PARAMS.forEach((param) => { + cleanNode[param] = node[param]; + }); + + return cleanNode; + }, + + getCleanLink(link) { + var cleanLink = {}; + + // Source and target nodes + // Node ids will be converted to complete node objects on running graphs, gotta convert back + cleanLink[LINK_SOURCE] = link[LINK_SOURCE][NODE_ID]; + cleanLink[LINK_TARGET] = link[LINK_TARGET][NODE_ID]; + + // Other parameters + LINK_PARAMS.forEach((param) => { + cleanLink[param] = link[param]; + }); + + return cleanLink; + }, +}; diff --git a/editor/js/state.js b/editor/js/state.js new file mode 100644 index 0000000..dc020c2 --- /dev/null +++ b/editor/js/state.js @@ -0,0 +1,198 @@ +const TOOLS = { + select: new SelectTool("select"), + collect: new CollectTool("collect"), + delete: new DeleteTool("delete"), +}; + +const CONTEXT = { + node: "node", + link: "link", + mixed: "mixed", +}; + +class State extends Tool { + constructor() { + super("State"); + + this.display = new Display(TOOLS); + + this.tool = undefined; + this.setTool(TOOLS.select); + + // Shared variables + this.selectedItem = undefined; + this.selectedItems = new Set(); + this.itemsContext = undefined; + + this.keyStates = {}; + } + + setTool(tool) { + if (this.previousTool === tool) { + return; + } + + this.previousTool = this.tool; + this.tool = tool; + this.display.setSelectedTool(tool); + } + + setSelectedItem(item) { + this.selectedItem = item; + this.display.setSelectedItem(item); + } + + addSelectedItem(item) { + this.selectedItems.add(item); + this.display.setSelectedItems(this.selectedItems, this.itemsContext); + } + + removeSelectedItem(item) { + this.selectedItems.delete(item); + this.display.setSelectedItems(this.selectedItems, this.itemsContext); + } + + clearSelectedItems() { + this.selectedItems.clear(); + this.itemsContext = undefined; + this.display.setSelectedItems(this.selectedItems, this.itemsContext); + } + + onNodeClick(node) { + this.tool.onNodeClick(node); + } + + onLinkClick(link) { + this.tool.onLinkClick(link); + } + + onKeyDown(key) { + var id = this.getKeyId(key); + var previous = this.keyStates[id]; + + this.keyStates[id] = true; + + if (previous !== true) { + this.tool.onKeyDown(key); + } + } + + onKeyUp(key) { + var id = this.getKeyId(key); + var previous = this.keyStates[id]; + + this.keyStates[id] = false; + + if (previous !== false) { + this.tool.onKeyUp(key); + } + } + + getKeyId(key) { + return key.keyCode; + } + + nodeCanvasObject(node, ctx) { + var toolValue = this.tool.nodeCanvasObject(node, ctx); + + 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, 5 * 1.4, 0, 2 * Math.PI, false); + ctx.fillStyle = this.selectedItem === node ? "red" : "green"; + ctx.fill(); + } + + // Draw image + if (node[NODE_IMAGE] !== undefined) { + var path = IMAGE_SRC + node[NODE_IMAGE]; + var img = new Image(); + img.src = path; + + ctx.drawImage( + img, + node.x - IMAGE_SIZE / 2, + node.y - IMAGE_SIZE / 2, + IMAGE_SIZE, + IMAGE_SIZE + ); + } + + // TODO: Render label as always visible + } + + nodePointerAreaPaint(node, color, ctx) { + var toolValue = this.tool.nodePointerAreaPaint(node, color, ctx); + + if (toolValue !== undefined) { + return toolValue; + } + + ctx.fillStyle = color; + ctx.fillRect( + node.x - IMAGE_SIZE / 2, + node.y - IMAGE_SIZE / 2, + IMAGE_SIZE, + IMAGE_SIZE + ); // draw square as pointer trap + } + + nodeCanvasObjectMode(node) { + var toolValue = this.tool.nodeCanvasObjectMode(node); + + if (toolValue !== undefined) { + return toolValue; + } + + return "after"; + } + + linkWidth(link) { + var toolValue = this.tool.linkWidth(link); + + if (toolValue !== undefined) { + return toolValue; + } + + return this.isLinkHighlighted(link) ? 5 : 1; + } + + linkDirectionalParticles() { + var toolValue = this.tool.linkDirectionalParticles(); + + if (toolValue !== undefined) { + return toolValue; + } + + return 4; + } + + linkDirectionalParticleWidth(link) { + var toolValue = this.tool.linkDirectionalParticleWidth(link); + + if (toolValue !== undefined) { + return toolValue; + } + + return this.isLinkHighlighted(link) ? LINK_PARTICLE_COUNT : 0; + } + + redraw() { + this.display.setSelectedTool(this.tool); + this.display.setSelectedItem(this.selectedItem); + this.display.setSelectedItems(this.selectedItems, this.itemsContext); + } + + isLinkHighlighted(link) { + return ( + this.selectedItem === link || + graph.isLinkOnNode(link, state.selectedItem) + ); + } +} diff --git a/editor/js/tools/collecttool.js b/editor/js/tools/collecttool.js new file mode 100644 index 0000000..5554a0f --- /dev/null +++ b/editor/js/tools/collecttool.js @@ -0,0 +1,37 @@ +class CollectTool extends Tool { + constructor(key) { + super("Collect", key); + } + + onNodeClick(node) { + if (state.itemsContext !== CONTEXT.node) { + state.clearSelectedItems(); + state.itemsContext = CONTEXT.node; + } + + if (state.selectedItems.has(node)) { + state.removeSelectedItem(node); + } else { + state.addSelectedItem(node); + } + } + + onLinkClick(link) { + if (state.itemsContext !== CONTEXT.link) { + state.clearSelectedItems(); + state.itemsContext = CONTEXT.link; + } + + if (state.selectedItems.has(link)) { + state.removeSelectedItem(link); + } else { + state.addSelectedItem(link); + } + } + + onKeyUp(key) { + if (key.keyCode === 17) { + state.setTool(state.previousTool); + } + } +} \ No newline at end of file diff --git a/editor/js/tools/deletetool.js b/editor/js/tools/deletetool.js new file mode 100644 index 0000000..6f99000 --- /dev/null +++ b/editor/js/tools/deletetool.js @@ -0,0 +1,13 @@ +class DeleteTool extends Tool { + constructor(key) { + super("Delete", key); + } + + onNodeClick(node) { + graph.deleteNode(node[NODE_ID]); + } + + onLinkClick(link) { + graph.deleteLink(link[LINK_SOURCE][NODE_ID], link[LINK_TARGET][NODE_ID]); + } +} \ No newline at end of file diff --git a/editor/js/tools/selecttool.js b/editor/js/tools/selecttool.js new file mode 100644 index 0000000..4c4b427 --- /dev/null +++ b/editor/js/tools/selecttool.js @@ -0,0 +1,19 @@ +class SelectTool extends Tool { + constructor(key) { + super("Select", key); + } + + onNodeClick(node) { + state.setSelectedItem(node); + } + + onLinkClick(link) { + state.setSelectedItem(link); + } + + onKeyDown(key) { + if (key.keyCode === 17) { + state.setTool(TOOLS.collect); + } + } +} \ No newline at end of file diff --git a/editor/js/tools/tool.js b/editor/js/tools/tool.js new file mode 100644 index 0000000..1b58655 --- /dev/null +++ b/editor/js/tools/tool.js @@ -0,0 +1,77 @@ +class Tool { + constructor(name, key) { + this.name = name; + this.key = key; + this.warnings = false; + } + + getName() { + return this.name; + } + + getKey() { + return this.key; + } + + onNodeClick(node) { + if (this.warnings) { + console.warn('Method "onNodeClick" not implemented.'); + } + } + + onLinkClick(link) { + if (this.warnings) { + console.warn('Method "onLinkClick" not implemented.'); + } + } + + onKeyDown(key) { + if (this.warnings) { + console.warn('Method "onKeyDown" not implemented.'); + } + } + + onKeyUp(key) { + if (this.warnings) { + console.warn('Method "onKeyUp" not implemented.'); + } + } + + nodeCanvasObject(node, ctx) { + if (this.warnings) { + console.warn('Method "nodeCanvasObject" not implemented.'); + } + } + + nodeCanvasObjectMode(node) { + if (this.warnings) { + console.warn('Method "nodeCanvasObjectMode" not implemented.'); + } + } + + nodePointerAreaPaint(node, color, ctx) { + if (this.warnings) { + console.warn('Method "nodePointerAreaPaint" not implemented.'); + } + } + + linkWidth(link) { + if (this.warnings) { + console.warn('Method "linkWidth" not implemented.'); + } + } + + linkDirectionalParticles() { + if (this.warnings) { + console.warn('Method "linkDirectionalParticles" not implemented.'); + } + } + + linkDirectionalParticleWidth(link) { + if (this.warnings) { + console.warn( + 'Method "linkDirectionalParticleWidth" not implemented.' + ); + } + } +} diff --git a/knowledge-space.php b/knowledge-space.php index b03271b..c9f5abd 100644 --- a/knowledge-space.php +++ b/knowledge-space.php @@ -25,6 +25,20 @@ function ks_add_graph(): string return $three . $renderer .$renderer2 . $graph . $div . $variables . $script; } +function ks_add_editor(): string +{ + // Proper, secure script loading in the future + // Reference https://stackoverflow.com/a/16823761/7376120 + wp_enqueue_script('jquery'); + + $plugin_url = plugin_dir_url(__FILE__); + + $raw_html = file_get_contents(__DIR__.DIRECTORY_SEPARATOR."editor".DIRECTORY_SEPARATOR."editor.html"); + $ready_html = str_replace("%WWW%", $plugin_url, $raw_html); + + return $ready_html; +} + function kg_load_css() { $plugin_dir = plugin_dir_url(__FILE__); wp_enqueue_style('kg-style', $plugin_dir.'kg-style.css'); @@ -32,3 +46,4 @@ function kg_load_css() { add_action('wp_enqueue_scripts', 'kg_load_css'); add_shortcode('knowledge-space', 'ks_add_graph'); +add_shortcode('knowledge-space-editor', 'ks_add_editor'); -- GitLab