diff --git a/frontend/src/Editor.svelte b/frontend/src/Editor.svelte index 4c5632b..790710e 100644 --- a/frontend/src/Editor.svelte +++ b/frontend/src/Editor.svelte @@ -11,6 +11,7 @@ let clicked: string[] = []; let graph: Graph; let currentTask: TaskDescriptor | null = null; + let nodeDraggingEnabled: boolean = false; function clickTask(e: CustomEvent) { // ukladani seznamu poslednich kliknuti @@ -39,6 +40,11 @@ } } + async function saveCurrentStateWithPositions() { + tasks.positions = graph.getNodePositions(); + await saveTasks(tasks); + } + async function saveCurrentState() { await saveTasks(tasks); } @@ -106,22 +112,36 @@ {repulsionForce} on:selectTask={clickTask} on:preSelectTask={startHovering} - bind:this={graph} /> + bind:this={graph} + {nodeDraggingEnabled} />
Toolbox
- + +
+
+
-
Repulsion force:
+
+ +
+
+ +
{#if currentTask != null} diff --git a/frontend/src/Graph.svelte b/frontend/src/Graph.svelte index 38d8f03..56f1955 100644 --- a/frontend/src/Graph.svelte +++ b/frontend/src/Graph.svelte @@ -6,11 +6,12 @@ import type { TasksFile, TaskDescriptor } from "./task-loader"; import { createNodesAndEdges } from "./graph-types"; import { taskForce } from "./task-force"; - import { zoom } from "d3"; export let tasks: TasksFile; - let hoveredTask: null | string = null; export let repulsionForce: number = -1000; + export let nodeDraggingEnabled: boolean = false; + + let hoveredTask: null | string = null; // Svelte automatically fills these with a reference let container: HTMLElement; @@ -23,7 +24,7 @@ let [nodes, edges] = createNodesAndEdges(tasks); function hack() { [nodes, edges] = createNodesAndEdges(tasks, nodes, edges); - runSimulation(); + //runSimulation(); } $: { tasks; @@ -74,12 +75,18 @@ nodes = nodes; } } - const zoomer = d3.zoom().scaleExtent([0.1, 2]) - $: { - // zoomer.extent([[-clientWidth / 2,-clientHeight / 2],[clientWidth,clientHeight]]) + export function getNodePositions(): Map { + let res = new Map(); + for (let n of nodes) { + if (n.x != undefined && n.y != undefined) { + res.set(n.id, [n.x, n.y]); + } + } + return res } + // start simulation and center view on create onMount(() => { // set center of the SVG at (0,0) @@ -89,10 +96,9 @@ function zoomed(e) { svg.attr("transform", e.transform); } + const zoomer = d3.zoom().scaleExtent([0.1, 2]) zoomer.on("zoom", zoomed); d3.select(container).call(zoomer); - - runSimulation(); }); @@ -129,7 +135,9 @@ {task} on:taskClick on:click={nodeClick(task.task)} - on:hoveringChange={nodeHover(task.task)} /> + on:hoveringChange={nodeHover(task.task)} + on:positionChange={() => { tasks = tasks; }} + draggingEnabled={nodeDraggingEnabled} /> {/each} diff --git a/frontend/src/GraphNode.svelte b/frontend/src/GraphNode.svelte index c552b58..da8fa14 100644 --- a/frontend/src/GraphNode.svelte +++ b/frontend/src/GraphNode.svelte @@ -1,9 +1,12 @@ - + { function createNodes(tasks: TasksFile, old?: TaskId[]): TaskId[] { let m = (old == undefined) ? new Map() : toMapById(old); - let res = tasks.tasks.map((t) => { + let res: TaskId[] = tasks.tasks.map((t) => { return { id: t.id, task: t }; }); for (let t of res) { + if (tasks.positions.has(t.id)) { + [t.x, t.y] = tasks.positions.get(t.id)! + } if (m.has(t.id)) { Object.assign(t, m.get(t.id)) } diff --git a/frontend/src/task-loader.ts b/frontend/src/task-loader.ts index e7556fa..303335e 100644 --- a/frontend/src/task-loader.ts +++ b/frontend/src/task-loader.ts @@ -9,20 +9,31 @@ export type TaskDescriptor = { export type TasksFile = { tasks: TaskDescriptor[] clusters: { [name: string]: string[] } + positions: Map } export type TaskMap = Map; export async function loadTasks(): Promise { const r = await fetch("/tasks.json") - return await r.json() + const j = await r.json() + if (j.positions == null) + j.positions = new Map(); + else + j.positions = new Map(Object.entries(j.positions)) + return j } export async function saveTasks(tasks: TasksFile) { + let p: any = {} + for (let [key, val] of tasks.positions.entries()) + p[key] = val; + const data = {...tasks, positions: p} + // request options const options = { method: 'POST', - body: JSON.stringify(tasks, null, 4), + body: JSON.stringify(data, null, 4), headers: { 'Content-Type': 'application/json' } diff --git a/tasks.json b/tasks.json index 81553a4..4d4b60b 100644 --- a/tasks.json +++ b/tasks.json @@ -158,52 +158,62 @@ "requires": [] }, { - "id": "29-Z3-2","type": "open-data", + "id": "29-Z3-2", + "type": "open-data", "comment": "Písemka z angličtiny — voser implementovat, easy dřevorubecký řešení, optimálně trie, což na Z IMHO hard", "requires": [] }, { - "id": "26-Z1-4","type": "open-data", + "id": "26-Z1-4", + "type": "open-data", "comment": "Hroch v jezeře - BFS či jiné prohledávání, počítání velikosti komponent v 2D poli, ", "requires": [] }, { - "id": "26-Z4-4","type": "open-data", + "id": "26-Z4-4", + "type": "open-data", "comment": "Hlídači v labyrintu - policajti hlídající na grafu, konkrétně na stromě, rekurze, technicky asi až DP", "requires": [] }, { - "id": "26-Z3-4","type": "open-data", + "id": "26-Z3-4", + "type": "open-data", "comment": "Tvar labyrintu - nejdelší cesta ve stromě, graf", "requires": [] }, { - "id": "29-Z1-4","type": "open-data", + "id": "29-Z1-4", + "type": "open-data", "comment": "Zuzčin výlet — DFS (topologické pořadí)", "requires": [] }, { - "id": "31-Z1-2","type": "open-data", + "id": "31-Z1-2", + "type": "open-data", "comment": "BFS (šachovnice, custom figurka, nejkratší cesta) ", "requires": [] }, { - "id": "31-Z3-2","type": "open-data", + "id": "31-Z3-2", + "type": "open-data", "comment": "DFS (hledání cesty v grafu po písmenech)", "requires": [] }, { - "id": "31-Z3-3","type": "open-data", + "id": "31-Z3-3", + "type": "open-data", "comment": "barvení bipartitního grafu (hledání partit), na vstupu hrany", "requires": [] }, { - "id": "26-Z4-2","type": "open-data", + "id": "26-Z4-2", + "type": "open-data", "comment": "Sbírání vajíček - hledení mediánu, musí se to ale vymyslet, nejkratší cesta při chození tam a zpět", "requires": [] }, { - "id": "26-Z3-1","type": "open-data", + "id": "26-Z3-1", + "type": "open-data", "comment": "Zámky labyrintu - hromada ifů, vhodné možná na code review, hledání čísla z trojice takového, že je trojice aritmetrická posloupnost", "requires": [] } @@ -259,5 +269,135 @@ "Nápad": [ "26-Z4-2" ] + }, + "positions": { + "start": [ + 60.9366784537015, + 15.282753057729023 + ], + "jak-resit-ulohy": [ + 57.60463651559811, + 85.05400883098152 + ], + "31-Z1-1": [ + 176, + 112 + ], + "26-Z1-1": [ + -86.4374292583939, + 114.1704716787137 + ], + "26-Z2-1": [ + -115, + 184 + ], + "27-Z2-1": [ + -30, + 181 + ], + "26-Z1-2": [ + -324, + 235 + ], + "26-Z4-3": [ + -220.83782675574338, + 190.72741511636147 + ], + "29-Z3-1": [ + -158, + 276 + ], + "31-Z1-4": [ + -245, + 284 + ], + "29-Z1-1": [ + 154, + 199 + ], + "29-Z2-1": [ + 259, + 272 + ], + "29-Z4-3": [ + 164, + 364 + ], + "26-Z2-4": [ + 403.46860248688955, + 23.996420431821353 + ], + "29-Z1-3": [ + -442.93377199520177, + -73.3461550905827 + ], + "26-Z2-2": [ + -64.3269017874483, + -91.7677899309802 + ], + "26-Z3-3": [ + 450.612997715014, + -65.45002256579735 + ], + "26-Z4-1": [ + -551.4441557449455, + -38.58561957493338 + ], + "29-Z3-3": [ + 302.6193712343388, + 13.535655571354772 + ], + "26-Z1-3": [ + 39.68234662392814, + -102.30393169322402 + ], + "26-Z2-3": [ + -320.2744362098852, + -72.1967458848867 + ], + "26-Z3-2": [ + 644.167217052611, + -0.4551191547699971 + ], + "29-Z3-2": [ + -368.51198400620785, + 1.6854832115582556 + ], + "26-Z1-4": [ + 147.37372961796666, + -89.40554252368531 + ], + "26-Z4-4": [ + 335.75328660449225, + -81.50932588628959 + ], + "26-Z3-4": [ + -480.95284944300494, + 29.587091058893556 + ], + "29-Z1-4": [ + 554.2061258687584, + -44.42093615098819 + ], + "31-Z1-2": [ + -236.06692326455527, + -77.65095973572805 + ], + "31-Z3-2": [ + -154.07095173801807, + -65.44506844403092 + ], + "31-Z3-3": [ + 514.5773720938831, + 41.05028681292239 + ], + "26-Z4-2": [ + -633.6757896792913, + 1.4284188628810082 + ], + "26-Z3-1": [ + 234.170971842641, + -59.230241172550606 + ] } -} +} \ No newline at end of file