Browse Source

label rendering and rotation

mj-deploy
Vašek Šraier 4 years ago
parent
commit
39c3b5a264
  1. 25
      frontend/src/Editor.svelte
  2. 93
      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 currentTask: TaskDescriptor | null = null;
let nodeDraggingEnabled: boolean = false;
let angle: number;
const { open } = getContext("simple-modal");
function clickTask(e: CustomEvent<TaskDescriptor>) {
@ -73,6 +74,12 @@
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 = {
id: id,
type: "label",
@ -118,6 +125,18 @@
// je to bezpečné, mažeme
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>
<style>
@ -219,6 +238,12 @@
vrcholů
</label>
</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 class="taskDetails">
{#if currentTask != null}

93
frontend/src/GraphNode.svelte

@ -7,27 +7,27 @@
export let task: TaskId;
export let draggingEnabled: boolean = false;
export let status: TaskStatus | undefined = undefined
export let status: TaskStatus | undefined = undefined;
let hovering: boolean = false;
let text_element: SVGTextElement;
$: cx = task === undefined || task.x === undefined ? 0 : task.x;
$: cy = task === undefined || task.y === undefined ? 0 : task.y;
const eventDispatcher = createEventDispatcher()
const eventDispatcher = createEventDispatcher();
function enter() {
hovering = true;
eventDispatcher("hoveringChange", hovering)
eventDispatcher("hoveringChange", hovering);
}
function leave() {
hovering = false;
eventDispatcher("hoveringChange", hovering)
eventDispatcher("hoveringChange", hovering);
}
function click(e: MouseEvent) {
eventDispatcher("click", e)
eventDispatcher("click", e);
}
// automatically size the bubbles to fit the text
@ -43,7 +43,7 @@
if (!draggingEnabled) return;
dragging = true;
e.preventDefault()
e.preventDefault();
e.stopPropagation();
}
function drag(e: MouseEvent) {
@ -54,8 +54,8 @@
task.x = x;
task.y = y;
eventDispatcher("positionChange");
e.preventDefault()
e.stopPropagation()
e.preventDefault();
e.stopPropagation();
}
function dragStop(e: MouseEvent) {
if (!draggingEnabled) return;
@ -67,8 +67,8 @@
function dblclick(e: MouseEvent) {
eventDispatcher("dblclick", e);
e.stopPropagation()
e.preventDefault()
e.stopPropagation();
e.preventDefault();
}
</script>
@ -76,37 +76,58 @@
g {
cursor: pointer;
}
g:hover > ellipse {
fill: #ffb3a2
g:hover > .taskNode {
fill: #ffb3a2;
}
ellipse {
fill: #69b3a2
.taskNode {
fill: #69b3a2;
}
.submitted ellipse {
.submitted .taskNode {
fill: red; /* TODO */
}
.solved ellipse {
.solved .taskNode {
fill: green; /* TODO */
}
.label {
font-size: 1.5em;
fill: gainsboro;
stroke: gainsboro;
}
</style>
<g on:mouseenter={enter}
on:mouseleave={leave}
on:click={click}
on:mousedown={dragStart}
on:mouseup={dragStop}
on:mousemove={drag}
on:dblclick={dblclick}
class={status == null ? "" :
status.solved ? "solved" :
status.submitted ? "submitted" : ""}>
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
<text
bind:this={text_element}
x={cx}
y={cy + 5}
text-anchor="middle"
alignment-baseline="middle">
{task.task.title == null ? task.id : task.task.title}
</text>
<g
on:mouseenter={enter}
on:mouseleave={leave}
on:click={click}
on:mousedown={dragStart}
on:mouseup={dragStop}
on:mousemove={drag}
on:dblclick={dblclick}
class={status == null ? '' : status.solved ? 'solved' : status.submitted ? 'submitted' : ''}>
{#if task.task.type == 'label'}
{#if draggingEnabled }
<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
bind:this={text_element}
x={cx}
y={cy + 5}
text-anchor="middle"
alignment-baseline="middle">
{task.task.title == null ? task.id : task.task.title}
</text>
{/if}
</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
}
function normalizeTasks(tasks: TasksFile) {
tasks.tasks.sort((t1, t2) => t1.id.localeCompare(t2.id))
}
export async function saveTasks(tasks: TasksFile) {
normalizeTasks(tasks);
let p: any = {}
for (let [key, val] of tasks.positions.entries())
p[key] = val;

425
tasks.json

@ -1,29 +1,5 @@
{
"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",
"type": "open-data",
@ -32,22 +8,6 @@
"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",
"type": "open-data",
@ -57,53 +17,36 @@
]
},
{
"id": "26-Z4-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",
"id": "26-Z1-3",
"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"
]
"comment": "Zamilovaný dopis - triviální, procházení stringů po znacích a kontrola podmínek",
"requires": []
},
{
"id": "31-Z1-4",
"id": "26-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"
]
"comment": "Hroch v jezeře - BFS či jiné prohledávání, počítání velikosti komponent v 2D poli, ",
"requires": []
},
{
"id": "29-Z1-1",
"id": "26-Z2-1",
"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": [
"jak-resit-ulohy"
"26-Z1-1"
]
},
{
"id": "29-Z2-1",
"id": "26-Z2-2",
"type": "open-data",
"comment": "Krocení zlé želvy — triviální, průchod po 2D souřadnicích, přímo navazuje na 29-Z1-1",
"requires": [
"29-Z1-1"
]
"comment": "SADO - triviální, procházení celých čísel v intervalu a kontrola podmínky",
"requires": []
},
{
"id": "29-Z4-3",
"id": "26-Z2-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"
]
"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": []
},
{
"id": "26-Z2-4",
@ -112,15 +55,15 @@
"requires": []
},
{
"id": "29-Z1-3",
"id": "26-Z3-1",
"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": []
},
{
"id": "26-Z2-2",
"id": "26-Z3-2",
"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": []
},
{
@ -129,6 +72,12 @@
"comment": "Hádanka - triky s dělitelností devíti a rozkladem čísel, docela hard teorie",
"requires": []
},
{
"id": "26-Z3-4",
"type": "open-data",
"comment": "Tvar labyrintu - nejdelší cesta ve stromě, graf",
"requires": []
},
{
"id": "26-Z4-1",
"type": "open-data",
@ -136,65 +85,112 @@
"requires": []
},
{
"id": "29-Z3-3",
"id": "26-Z4-2",
"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": []
},
{
"id": "26-Z1-3",
"id": "26-Z4-3",
"type": "open-data",
"comment": "Zamilovaný dopis - triviální, procházení stringů po znacích a kontrola podmínek",
"requires": []
"comment": "Hra Othello - link na 26-Z1-2, procházení 2D pole po různých směrech",
"requires": [
"26-Z1-1"
]
},
{
"id": "26-Z2-3",
"id": "26-Z4-4",
"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": []
},
{
"id": "26-Z3-2",
"id": "27-Z2-1",
"type": "open-data",
"comment": "Čarodějova šifra - šifrování mřížkou, práce se stringy, rotace matice, vhodné na code review?, docela hard",
"requires": []
"comment": "Závorky z cereálií - 2 průchody pole, závorky",
"requires": [
"26-Z1-1"
]
},
{
"id": "29-Z3-2",
"id": "29-Z1-1",
"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": []
"comment": "Kevinova želva — triviální, průchod po 2D souřadnicích",
"requires": [
"jak-resit-ulohy"
]
},
{
"id": "26-Z1-4",
"id": "29-Z1-3",
"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": []
},
{
"id": "26-Z4-4",
"id": "29-Z1-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",
"comment": "Zuzčin výlet — DFS (topologické pořadí)",
"requires": []
},
{
"id": "26-Z3-4",
"id": "29-Z2-1",
"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": []
},
{
"id": "29-Z1-4",
"id": "29-Z3-3",
"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": []
},
{
"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",
"type": "open-data",
"comment": "BFS (šachovnice, custom figurka, nejkratší cesta) ",
"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",
"type": "open-data",
@ -208,16 +204,51 @@
"requires": []
},
{
"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": "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": "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": []
"id": "label-1d-pole",
"type": "label",
"comment": "...",
"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": {
@ -273,133 +304,149 @@
]
},
"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
"26-Z1-3": [
-464,
-196
],
"31-Z1-4": [
-245,
284
"26-Z1-4": [
400,
-203
],
"29-Z1-1": [
154,
199
"26-Z2-1": [
-115,
184
],
"29-Z2-1": [
259,
272
"26-Z2-2": [
-278,
-162
],
"29-Z4-3": [
164,
364
"26-Z2-3": [
-320.2744362098852,
-72.1967458848867
],
"26-Z2-4": [
403.46860248688955,
23.996420431821353
575,
-111
],
"29-Z1-3": [
-442.93377199520177,
-73.3461550905827
"26-Z3-1": [
446,
-160
],
"26-Z2-2": [
-64.3269017874483,
-91.7677899309802
"26-Z3-2": [
644.167217052611,
-0.4551191547699971
],
"26-Z3-3": [
450.612997715014,
-65.45002256579735
485,
-81
],
"26-Z3-4": [
-480.95284944300494,
29.587091058893556
],
"26-Z4-1": [
-551.4441557449455,
-38.58561957493338
],
"29-Z3-3": [
302.6193712343388,
13.535655571354772
"26-Z4-2": [
-633.6757896792913,
1.4284188628810082
],
"26-Z1-3": [
39.68234662392814,
-102.30393169322402
"26-Z4-3": [
-220.83782675574338,
190.72741511636147
],
"26-Z2-3": [
-320.2744362098852,
-72.1967458848867
"26-Z4-4": [
471,
-114
],
"26-Z3-2": [
644.167217052611,
-0.4551191547699971
"27-Z2-1": [
-30,
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": [
-368.51198400620785,
1.6854832115582556
],
"26-Z1-4": [
147.37372961796666,
-89.40554252368531
],
"26-Z4-4": [
335.75328660449225,
-81.50932588628959
"29-Z3-3": [
470,
-213
],
"26-Z3-4": [
-480.95284944300494,
29.587091058893556
"29-Z4-3": [
164,
364
],
"29-Z1-4": [
554.2061258687584,
-44.42093615098819
"31-Z1-1": [
295,
119
],
"31-Z1-2": [
-236.06692326455527,
-77.65095973572805
-360,
-205
],
"31-Z1-4": [
-245,
284
],
"31-Z3-2": [
-154.07095173801807,
-65.44506844403092
-398,
-139
],
"31-Z3-3": [
514.5773720938831,
41.05028681292239
],
"26-Z4-2": [
-633.6757896792913,
1.4284188628810082
"jak-resit-ulohy": [
88,
-5
],
"26-Z3-1": [
234.170971842641,
-59.230241172550606
"label-1d-pole": [
-115,
80
],
"label-2d-pole": [
-294,
173
],
"label-uvod": [
82,
-128
],
"label-zelvy": [
188,
166
],
"start": [
83,
-87
]
}
}