graf: přidána funkce pro manuální pozicování vrcholů a perzistence pozice
This commit is contained in:
parent
bd5e5a6a48
commit
a264dbead9
6 changed files with 241 additions and 28 deletions
|
@ -11,6 +11,7 @@
|
|||
let clicked: string[] = [];
|
||||
let graph: Graph;
|
||||
let currentTask: TaskDescriptor | null = null;
|
||||
let nodeDraggingEnabled: boolean = false;
|
||||
|
||||
function clickTask(e: CustomEvent<TaskDescriptor>) {
|
||||
// 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} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="toolbox">
|
||||
<div>Toolbox</div>
|
||||
<div>
|
||||
<button disabled={clicked.length <= 1} on:click={addEdge}>Přidat hranu {clicked[clicked.length - 2]} -> {clicked[clicked.length - 1]}</button>
|
||||
<button disabled={clicked.length <= 1} on:click={addEdge}>Přidat hranu {clicked[clicked.length - 2]}
|
||||
-> {clicked[clicked.length - 1]}</button>
|
||||
</div>
|
||||
<div>
|
||||
<button on:click={graph.runSimulation}>Spustit simulaci</button>
|
||||
</div>
|
||||
<div><button on:click={graph.runSimulation}>Spustit simulaci</button></div>
|
||||
<div>
|
||||
Repulsion force: <input type="number" bind:value={repulsionForce} name="repulsionForceInput" max="1000" min="-10000" />
|
||||
</div>
|
||||
<div>
|
||||
<button on:click={saveCurrentState}>Uložit aktuální stav</button>
|
||||
</div>
|
||||
<div>
|
||||
<button on:click={saveCurrentStateWithPositions}>Uložit aktuální stav
|
||||
včetně pozic nodů</button>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={nodeDraggingEnabled} /> Povolit přesouvání
|
||||
vrcholů
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taskDetails">
|
||||
{#if currentTask != null}
|
||||
|
|
|
@ -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<string, [number, number]> {
|
||||
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();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -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}
|
||||
</g>
|
||||
</g>
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<script lang="ts">
|
||||
import * as d3 from "d3";
|
||||
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import type { TaskId } from "./graph-types";
|
||||
|
||||
|
||||
export let task: TaskId;
|
||||
export let draggingEnabled: boolean = false;
|
||||
|
||||
let hovering: boolean = false;
|
||||
let text_element: SVGTextElement;
|
||||
|
||||
|
@ -31,6 +34,34 @@
|
|||
const bbox = text_element.getBBox();
|
||||
ellipse_rx = bbox.width / 2 + 8;
|
||||
});
|
||||
|
||||
// dragging
|
||||
let dragging: boolean = false;
|
||||
function dragStart(e: MouseEvent) {
|
||||
if (!draggingEnabled) return;
|
||||
|
||||
dragging = true;
|
||||
e.preventDefault()
|
||||
e.stopPropagation();
|
||||
}
|
||||
function drag(e: MouseEvent) {
|
||||
if (!draggingEnabled) return;
|
||||
if (!dragging) return;
|
||||
|
||||
let [x, y] = d3.pointer(e);
|
||||
task.x = x;
|
||||
task.y = y;
|
||||
eventDispatcher("positionChange");
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
function dragStop(e: MouseEvent) {
|
||||
if (!draggingEnabled) return;
|
||||
|
||||
dragging = false;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -45,7 +76,7 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<g on:mouseenter={enter} on:mouseleave={leave} on:click={click}>
|
||||
<g on:mouseenter={enter} on:mouseleave={leave} on:click={click} on:mousedown={dragStart} on:mouseup={dragStop} on:mousemove={drag}>
|
||||
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
|
||||
<text
|
||||
bind:this={text_element}
|
||||
|
|
|
@ -21,11 +21,14 @@ function toMapById(nodes: TaskId[]): Map<string, TaskId> {
|
|||
function createNodes(tasks: TasksFile, old?: TaskId[]): TaskId[] {
|
||||
let m = (old == undefined) ? new Map<string, TaskId>() : 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))
|
||||
}
|
||||
|
|
|
@ -9,20 +9,31 @@ export type TaskDescriptor = {
|
|||
export type TasksFile = {
|
||||
tasks: TaskDescriptor[]
|
||||
clusters: { [name: string]: string[] }
|
||||
positions: Map<string, [number, number]>
|
||||
}
|
||||
|
||||
export type TaskMap = Map<string, TaskDescriptor>;
|
||||
|
||||
export async function loadTasks(): Promise<TasksFile> {
|
||||
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'
|
||||
}
|
||||
|
|
162
tasks.json
162
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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue