Browse Source

label rendering and rotation

mj-deploy
Vašek Šraier 4 years ago
parent
commit
39c3b5a264
  1. 25
      frontend/src/Editor.svelte
  2. 61
      frontend/src/GraphNode.svelte
  3. 9
      frontend/src/task-loader.ts
  4. 425
      tasks.json

25
frontend/src/Editor.svelte

@ -15,6 +15,7 @@
let graph: Graph; let graph: Graph;
let currentTask: TaskDescriptor | null = null; let currentTask: TaskDescriptor | null = null;
let nodeDraggingEnabled: boolean = false; let nodeDraggingEnabled: boolean = false;
let angle: number;
const { open } = getContext("simple-modal"); const { open } = getContext("simple-modal");
function clickTask(e: CustomEvent<TaskDescriptor>) { function clickTask(e: CustomEvent<TaskDescriptor>) {
@ -73,6 +74,12 @@
return; return;
} }
let existing = tasks.tasks.find((t) => t.id == id);
if (existing != undefined) {
alert("úloha s tímto ID již existuje!");
return;
}
let novaUloha: TaskDescriptor = { let novaUloha: TaskDescriptor = {
id: id, id: id,
type: "label", type: "label",
@ -118,6 +125,18 @@
// je to bezpečné, mažeme // je to bezpečné, mažeme
tasks.tasks = tasks.tasks.filter((t) => t.id != id); tasks.tasks = tasks.tasks.filter((t) => t.id != id);
} }
function getTask(id: string): TaskDescriptor | undefined {
return tasks.tasks.find((t) => t.id == id);
}
function setAngleToTheCurrentLabel() {
let t = getTask(clicked[clicked.length - 1]);
if (clicked.length > 0 && t != undefined && t.type == "label") {
t.rotationAngle = angle;
tasks = tasks;
}
}
</script> </script>
<style> <style>
@ -219,6 +238,12 @@
vrcholů vrcholů
</label> </label>
</div> </div>
{#if clicked.length > 0 && getTask(clicked[clicked.length - 1]).type == "label"}
<div>
Úhel rotace:
<input bind:value={angle} type="range" max="360" min="0" on:change={setAngleToTheCurrentLabel}/>
</div>
{/if}
</div> </div>
<div class="taskDetails"> <div class="taskDetails">
{#if currentTask != null} {#if currentTask != null}

61
frontend/src/GraphNode.svelte

@ -7,7 +7,7 @@
export let task: TaskId; export let task: TaskId;
export let draggingEnabled: boolean = false; export let draggingEnabled: boolean = false;
export let status: TaskStatus | undefined = undefined export let status: TaskStatus | undefined = undefined;
let hovering: boolean = false; let hovering: boolean = false;
let text_element: SVGTextElement; let text_element: SVGTextElement;
@ -15,19 +15,19 @@
$: cx = task === undefined || task.x === undefined ? 0 : task.x; $: cx = task === undefined || task.x === undefined ? 0 : task.x;
$: cy = task === undefined || task.y === undefined ? 0 : task.y; $: cy = task === undefined || task.y === undefined ? 0 : task.y;
const eventDispatcher = createEventDispatcher() const eventDispatcher = createEventDispatcher();
function enter() { function enter() {
hovering = true; hovering = true;
eventDispatcher("hoveringChange", hovering) eventDispatcher("hoveringChange", hovering);
} }
function leave() { function leave() {
hovering = false; hovering = false;
eventDispatcher("hoveringChange", hovering) eventDispatcher("hoveringChange", hovering);
} }
function click(e: MouseEvent) { function click(e: MouseEvent) {
eventDispatcher("click", e) eventDispatcher("click", e);
} }
// automatically size the bubbles to fit the text // automatically size the bubbles to fit the text
@ -43,7 +43,7 @@
if (!draggingEnabled) return; if (!draggingEnabled) return;
dragging = true; dragging = true;
e.preventDefault() e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }
function drag(e: MouseEvent) { function drag(e: MouseEvent) {
@ -54,8 +54,8 @@
task.x = x; task.x = x;
task.y = y; task.y = y;
eventDispatcher("positionChange"); eventDispatcher("positionChange");
e.preventDefault() e.preventDefault();
e.stopPropagation() e.stopPropagation();
} }
function dragStop(e: MouseEvent) { function dragStop(e: MouseEvent) {
if (!draggingEnabled) return; if (!draggingEnabled) return;
@ -67,8 +67,8 @@
function dblclick(e: MouseEvent) { function dblclick(e: MouseEvent) {
eventDispatcher("dblclick", e); eventDispatcher("dblclick", e);
e.stopPropagation() e.stopPropagation();
e.preventDefault() e.preventDefault();
} }
</script> </script>
@ -76,31 +76,51 @@
g { g {
cursor: pointer; cursor: pointer;
} }
g:hover > ellipse { g:hover > .taskNode {
fill: #ffb3a2 fill: #ffb3a2;
} }
ellipse { .taskNode {
fill: #69b3a2 fill: #69b3a2;
} }
.submitted ellipse { .submitted .taskNode {
fill: red; /* TODO */ fill: red; /* TODO */
} }
.solved ellipse { .solved .taskNode {
fill: green; /* TODO */ fill: green; /* TODO */
} }
.label {
font-size: 1.5em;
fill: gainsboro;
stroke: gainsboro;
}
</style> </style>
<g on:mouseenter={enter} <g
on:mouseenter={enter}
on:mouseleave={leave} on:mouseleave={leave}
on:click={click} on:click={click}
on:mousedown={dragStart} on:mousedown={dragStart}
on:mouseup={dragStop} on:mouseup={dragStop}
on:mousemove={drag} on:mousemove={drag}
on:dblclick={dblclick} on:dblclick={dblclick}
class={status == null ? "" : class={status == null ? '' : status.solved ? 'solved' : status.submitted ? 'submitted' : ''}>
status.solved ? "solved" : {#if task.task.type == 'label'}
status.submitted ? "submitted" : ""}> {#if draggingEnabled }
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} /> <ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
{/if}
<text
class="label"
bind:this={text_element}
x={cx}
y={cy + 5}
text-anchor="middle"
alignment-baseline="middle"
transform="translate({cx}, {cy}) rotate({task.task.rotationAngle ?? 0}) translate({-cx}, {-cy})">
{task.task.title == null ? task.id : task.task.title}
</text>
{:else}
<ellipse class="taskNode" rx={ellipse_rx} ry={20} {cx} {cy} />
<text <text
bind:this={text_element} bind:this={text_element}
x={cx} x={cx}
@ -109,4 +129,5 @@
alignment-baseline="middle"> alignment-baseline="middle">
{task.task.title == null ? task.id : task.task.title} {task.task.title == null ? task.id : task.task.title}
</text> </text>
{/if}
</g> </g>

9
frontend/src/task-loader.ts

@ -17,7 +17,8 @@ export type TaskDescriptor = {
} }
| |
{ {
type: "label" type: "label",
rotationAngle?: number
} }
); );
@ -39,7 +40,13 @@ export async function loadTasks(): Promise<TasksFile> {
return j return j
} }
function normalizeTasks(tasks: TasksFile) {
tasks.tasks.sort((t1, t2) => t1.id.localeCompare(t2.id))
}
export async function saveTasks(tasks: TasksFile) { export async function saveTasks(tasks: TasksFile) {
normalizeTasks(tasks);
let p: any = {} let p: any = {}
for (let [key, val] of tasks.positions.entries()) for (let [key, val] of tasks.positions.entries())
p[key] = val; p[key] = val;

425
tasks.json

@ -1,29 +1,5 @@
{ {
"tasks": [ "tasks": [
{
"id": "start",
"type": "text",
"requires": [],
"comment": "úvodní kecy o tom, jak to celé funguje"
},
{
"id": "jak-resit-ulohy",
"type": "text",
"requires": [
"start"
],
"comment": "kecy o tom, jak se může řešit taková úloha",
"title": "Jak řešit úlohy?",
"htmlContent": ""
},
{
"id": "31-Z1-1",
"type": "open-data",
"requires": [
"jak-resit-ulohy"
],
"comment": "úloha s odčítáním času"
},
{ {
"id": "26-Z1-1", "id": "26-Z1-1",
"type": "open-data", "type": "open-data",
@ -32,22 +8,6 @@
"jak-resit-ulohy" "jak-resit-ulohy"
] ]
}, },
{
"id": "26-Z2-1",
"type": "open-data",
"comment": "Had z domina - triviální, linerání průchod pole a počítání, na kolika místech platí podmínka, vyžaduje práci se dvojicemi",
"requires": [
"26-Z1-1"
]
},
{
"id": "27-Z2-1",
"type": "open-data",
"comment": "Závorky z cereálií - 2 průchody pole, závorky",
"requires": [
"26-Z1-1"
]
},
{ {
"id": "26-Z1-2", "id": "26-Z1-2",
"type": "open-data", "type": "open-data",
@ -57,53 +17,36 @@
] ]
}, },
{ {
"id": "26-Z4-3", "id": "26-Z1-3",
"type": "open-data",
"comment": "Hra Othello - link na 26-Z1-2, procházení 2D pole po různých směrech",
"requires": [
"26-Z1-1"
]
},
{
"id": "29-Z3-1",
"type": "open-data", "type": "open-data",
"comment": "Želva na dvorku — spíš triviální, netriviální udělat efektivně (ale asi to není třeba pro úspěch), volně navazuje na 29-Z2-1 a 29-Z1-1, průchod po 2D souřadnicích, invalidní příkazy (směrem do překážky) ignorujeme", "comment": "Zamilovaný dopis - triviální, procházení stringů po znacích a kontrola podmínek",
"requires": [ "requires": []
"26-Z4-3"
]
}, },
{ {
"id": "31-Z1-4", "id": "26-Z1-4",
"type": "open-data", "type": "open-data",
"comment": "2D pole (až na poslední vstup), hešování (piškvorky) - navazuje na 26-Z1-2", "comment": "Hroch v jezeře - BFS či jiné prohledávání, počítání velikosti komponent v 2D poli, ",
"requires": [ "requires": []
"26-Z4-3"
]
}, },
{ {
"id": "29-Z1-1", "id": "26-Z2-1",
"type": "open-data", "type": "open-data",
"comment": "Kevinova želva — triviální, průchod po 2D souřadnicích", "comment": "Had z domina - triviální, linerání průchod pole a počítání, na kolika místech platí podmínka, vyžaduje práci se dvojicemi",
"requires": [ "requires": [
"jak-resit-ulohy" "26-Z1-1"
] ]
}, },
{ {
"id": "29-Z2-1", "id": "26-Z2-2",
"type": "open-data", "type": "open-data",
"comment": "Krocení zlé želvy — triviální, průchod po 2D souřadnicích, přímo navazuje na 29-Z1-1", "comment": "SADO - triviální, procházení celých čísel v intervalu a kontrola podmínky",
"requires": [ "requires": []
"29-Z1-1"
]
}, },
{ {
"id": "29-Z4-3", "id": "26-Z2-3",
"type": "open-data", "type": "open-data",
"comment": "Želva v akváriu — malej voser, triviální, přímo navazuje na 29-Z2-1 a 29-Z1-1, průchod po 3D souřadnicích", "comment": "Šifrovaná zpráva - práce s řetězci, hledání mapování mezi písmenky (substitučka) a validace, že jinde substitučka funguje",
"requires": [ "requires": []
"29-Z1-1",
"29-Z2-1"
]
}, },
{ {
"id": "26-Z2-4", "id": "26-Z2-4",
@ -112,15 +55,15 @@
"requires": [] "requires": []
}, },
{ {
"id": "29-Z1-3", "id": "26-Z3-1",
"type": "open-data", "type": "open-data",
"comment": "Petrova statistika — ne úplně triviální, tvorba histogramu z pole", "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": [] "requires": []
}, },
{ {
"id": "26-Z2-2", "id": "26-Z3-2",
"type": "open-data", "type": "open-data",
"comment": "SADO - triviální, procházení celých čísel v intervalu a kontrola podmínky", "comment": "Čarodějova šifra - šifrování mřížkou, práce se stringy, rotace matice, vhodné na code review?, docela hard",
"requires": [] "requires": []
}, },
{ {
@ -129,6 +72,12 @@
"comment": "Hádanka - triky s dělitelností devíti a rozkladem čísel, docela hard teorie", "comment": "Hádanka - triky s dělitelností devíti a rozkladem čísel, docela hard teorie",
"requires": [] "requires": []
}, },
{
"id": "26-Z3-4",
"type": "open-data",
"comment": "Tvar labyrintu - nejdelší cesta ve stromě, graf",
"requires": []
},
{ {
"id": "26-Z4-1", "id": "26-Z4-1",
"type": "open-data", "type": "open-data",
@ -136,65 +85,112 @@
"requires": [] "requires": []
}, },
{ {
"id": "29-Z3-3", "id": "26-Z4-2",
"type": "open-data", "type": "open-data",
"comment": "Šestková čísla — mega voser implementovat, spíš matematická úloha, převod dešitkových čísel do hacknutých římských", "comment": "Sbírání vajíček - hledení mediánu, musí se to ale vymyslet, nejkratší cesta při chození tam a zpět",
"requires": [] "requires": []
}, },
{ {
"id": "26-Z1-3", "id": "26-Z4-3",
"type": "open-data", "type": "open-data",
"comment": "Zamilovaný dopis - triviální, procházení stringů po znacích a kontrola podmínek", "comment": "Hra Othello - link na 26-Z1-2, procházení 2D pole po různých směrech",
"requires": [] "requires": [
"26-Z1-1"
]
}, },
{ {
"id": "26-Z2-3", "id": "26-Z4-4",
"type": "open-data", "type": "open-data",
"comment": "Šifrovaná zpráva - práce s řetězci, hledání mapování mezi písmenky (substitučka) a validace, že jinde substitučka funguje", "comment": "Hlídači v labyrintu - policajti hlídající na grafu, konkrétně na stromě, rekurze, technicky asi až DP",
"requires": [] "requires": []
}, },
{ {
"id": "26-Z3-2", "id": "27-Z2-1",
"type": "open-data", "type": "open-data",
"comment": "Čarodějova šifra - šifrování mřížkou, práce se stringy, rotace matice, vhodné na code review?, docela hard", "comment": "Závorky z cereálií - 2 průchody pole, závorky",
"requires": [] "requires": [
"26-Z1-1"
]
}, },
{ {
"id": "29-Z3-2", "id": "29-Z1-1",
"type": "open-data", "type": "open-data",
"comment": "Písemka z angličtiny — voser implementovat, easy dřevorubecký řešení, optimálně trie, což na Z IMHO hard", "comment": "Kevinova želva — triviální, průchod po 2D souřadnicích",
"requires": [] "requires": [
"jak-resit-ulohy"
]
}, },
{ {
"id": "26-Z1-4", "id": "29-Z1-3",
"type": "open-data", "type": "open-data",
"comment": "Hroch v jezeře - BFS či jiné prohledávání, počítání velikosti komponent v 2D poli, ", "comment": "Petrova statistika — ne úplně triviální, tvorba histogramu z pole",
"requires": [] "requires": []
}, },
{ {
"id": "26-Z4-4", "id": "29-Z1-4",
"type": "open-data", "type": "open-data",
"comment": "Hlídači v labyrintu - policajti hlídající na grafu, konkrétně na stromě, rekurze, technicky asi až DP", "comment": "Zuzčin výlet — DFS (topologické pořadí)",
"requires": [] "requires": []
}, },
{ {
"id": "26-Z3-4", "id": "29-Z2-1",
"type": "open-data", "type": "open-data",
"comment": "Tvar labyrintu - nejdelší cesta ve stromě, graf", "comment": "Krocení zlé želvy — triviální, průchod po 2D souřadnicích, přímo navazuje na 29-Z1-1",
"requires": [
"29-Z1-1"
]
},
{
"id": "29-Z3-1",
"type": "open-data",
"comment": "Želva na dvorku — spíš triviální, netriviální udělat efektivně (ale asi to není třeba pro úspěch), volně navazuje na 29-Z2-1 a 29-Z1-1, průchod po 2D souřadnicích, invalidní příkazy (směrem do překážky) ignorujeme",
"requires": [
"26-Z4-3"
]
},
{
"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": [] "requires": []
}, },
{ {
"id": "29-Z1-4", "id": "29-Z3-3",
"type": "open-data", "type": "open-data",
"comment": "Zuzčin výlet — DFS (topologické pořadí)", "comment": "Šestková čísla — mega voser implementovat, spíš matematická úloha, převod dešitkových čísel do hacknutých římských",
"requires": [] "requires": []
}, },
{
"id": "29-Z4-3",
"type": "open-data",
"comment": "Želva v akváriu — malej voser, triviální, přímo navazuje na 29-Z2-1 a 29-Z1-1, průchod po 3D souřadnicích",
"requires": [
"29-Z1-1",
"29-Z2-1"
]
},
{
"id": "31-Z1-1",
"type": "open-data",
"requires": [
"jak-resit-ulohy"
],
"comment": "úloha s odčítáním času"
},
{ {
"id": "31-Z1-2", "id": "31-Z1-2",
"type": "open-data", "type": "open-data",
"comment": "BFS (šachovnice, custom figurka, nejkratší cesta) ", "comment": "BFS (šachovnice, custom figurka, nejkratší cesta) ",
"requires": [] "requires": []
}, },
{
"id": "31-Z1-4",
"type": "open-data",
"comment": "2D pole (až na poslední vstup), hešování (piškvorky) - navazuje na 26-Z1-2",
"requires": [
"26-Z4-3"
]
},
{ {
"id": "31-Z3-2", "id": "31-Z3-2",
"type": "open-data", "type": "open-data",
@ -208,16 +204,51 @@
"requires": [] "requires": []
}, },
{ {
"id": "26-Z4-2", "id": "jak-resit-ulohy",
"type": "open-data", "type": "text",
"comment": "Sbírání vajíček - hledení mediánu, musí se to ale vymyslet, nejkratší cesta při chození tam a zpět", "requires": [
"requires": [] "start"
],
"comment": "kecy o tom, jak se může řešit taková úloha",
"title": "Jak řešit úlohy?",
"htmlContent": ""
}, },
{ {
"id": "26-Z3-1", "id": "label-1d-pole",
"type": "open-data", "type": "label",
"comment": "Zámky labyrintu - hromada ifů, vhodné možná na code review, hledání čísla z trojice takového, že je trojice aritmetrická posloupnost", "comment": "...",
"requires": [] "requires": [],
"title": "1D pole",
"rotationAngle": 337
},
{
"id": "label-2d-pole",
"type": "label",
"comment": "...",
"requires": [],
"title": "2D pole",
"rotationAngle": 340
},
{
"id": "label-uvod",
"type": "label",
"comment": "...",
"requires": [],
"title": "Úvod"
},
{
"id": "label-zelvy",
"type": "label",
"comment": "...",
"requires": [],
"title": "Želvy",
"rotationAngle": 43
},
{
"id": "start",
"type": "text",
"requires": [],
"comment": "úvodní kecy o tom, jak to celé funguje"
} }
], ],
"clusters": { "clusters": {
@ -273,133 +304,149 @@
] ]
}, },
"positions": { "positions": {
"start": [
60.9366784537015,
15.282753057729023
],
"jak-resit-ulohy": [
57.60463651559811,
85.05400883098152
],
"31-Z1-1": [
176,
112
],
"26-Z1-1": [ "26-Z1-1": [
-86.4374292583939, -86.4374292583939,
114.1704716787137 114.1704716787137
], ],
"26-Z2-1": [
-115,
184
],
"27-Z2-1": [
-30,
181
],
"26-Z1-2": [ "26-Z1-2": [
-324, -324,
235 235
], ],
"26-Z4-3": [ "26-Z1-3": [
-220.83782675574338, -464,
190.72741511636147 -196
],
"29-Z3-1": [
-158,
276
], ],
"31-Z1-4": [ "26-Z1-4": [
-245, 400,
284 -203
], ],
"29-Z1-1": [ "26-Z2-1": [
154, -115,
199 184
], ],
"29-Z2-1": [ "26-Z2-2": [
259, -278,
272 -162
], ],
"29-Z4-3": [ "26-Z2-3": [
164, -320.2744362098852,
364 -72.1967458848867
], ],
"26-Z2-4": [ "26-Z2-4": [
403.46860248688955, 575,
23.996420431821353 -111
], ],
"29-Z1-3": [ "26-Z3-1": [
-442.93377199520177, 446,
-73.3461550905827 -160
], ],
"26-Z2-2": [ "26-Z3-2": [
-64.3269017874483, 644.167217052611,
-91.7677899309802 -0.4551191547699971
], ],
"26-Z3-3": [ "26-Z3-3": [
450.612997715014, 485,
-65.45002256579735 -81
],
"26-Z3-4": [
-480.95284944300494,
29.587091058893556
], ],
"26-Z4-1": [ "26-Z4-1": [
-551.4441557449455, -551.4441557449455,
-38.58561957493338 -38.58561957493338
], ],
"29-Z3-3": [ "26-Z4-2": [
302.6193712343388, -633.6757896792913,
13.535655571354772 1.4284188628810082
], ],
"26-Z1-3": [ "26-Z4-3": [
39.68234662392814, -220.83782675574338,
-102.30393169322402 190.72741511636147
], ],
"26-Z2-3": [ "26-Z4-4": [
-320.2744362098852, 471,
-72.1967458848867 -114
], ],
"26-Z3-2": [ "27-Z2-1": [
644.167217052611, -30,
-0.4551191547699971 181
],
"29-Z1-1": [
154,
199
],
"29-Z1-3": [
-442.93377199520177,
-73.3461550905827
],
"29-Z1-4": [
554.2061258687584,
-44.42093615098819
],
"29-Z2-1": [
259,
272
],
"29-Z3-1": [
-158,
276
], ],
"29-Z3-2": [ "29-Z3-2": [
-368.51198400620785, -368.51198400620785,
1.6854832115582556 1.6854832115582556
], ],
"26-Z1-4": [ "29-Z3-3": [
147.37372961796666, 470,
-89.40554252368531 -213
],
"26-Z4-4": [
335.75328660449225,
-81.50932588628959
], ],
"26-Z3-4": [ "29-Z4-3": [
-480.95284944300494, 164,
29.587091058893556 364
], ],
"29-Z1-4": [ "31-Z1-1": [
554.2061258687584, 295,
-44.42093615098819 119
], ],
"31-Z1-2": [ "31-Z1-2": [
-236.06692326455527, -360,
-77.65095973572805 -205
],
"31-Z1-4": [
-245,
284
], ],
"31-Z3-2": [ "31-Z3-2": [
-154.07095173801807, -398,
-65.44506844403092 -139
], ],
"31-Z3-3": [ "31-Z3-3": [
514.5773720938831, 514.5773720938831,
41.05028681292239 41.05028681292239
], ],
"26-Z4-2": [ "jak-resit-ulohy": [
-633.6757896792913, 88,
1.4284188628810082 -5
], ],
"26-Z3-1": [ "label-1d-pole": [
234.170971842641, -115,
-59.230241172550606 80
],
"label-2d-pole": [
-294,
173
],
"label-uvod": [
82,
-128
],
"label-zelvy": [
188,
166
],
"start": [
83,
-87
] ]
} }
} }