label rendering and rotation

This commit is contained in:
Vašek Šraier 2020-09-30 19:49:05 +02:00
parent f094973865
commit 39c3b5a264
4 changed files with 370 additions and 270 deletions

View file

@ -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}

View file

@ -7,27 +7,27 @@
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;
$: 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,37 +76,58 @@
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:mouseleave={leave} on:mouseenter={enter}
on:click={click} on:mouseleave={leave}
on:mousedown={dragStart} on:click={click}
on:mouseup={dragStop} on:mousedown={dragStart}
on:mousemove={drag} on:mouseup={dragStop}
on:dblclick={dblclick} on:mousemove={drag}
class={status == null ? "" : on:dblclick={dblclick}
status.solved ? "solved" : class={status == null ? '' : status.solved ? 'solved' : status.submitted ? 'submitted' : ''}>
status.submitted ? "submitted" : ""}> {#if task.task.type == 'label'}
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} /> {#if draggingEnabled }
<text <ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
bind:this={text_element} {/if}
x={cx} <text
y={cy + 5} class="label"
text-anchor="middle" bind:this={text_element}
alignment-baseline="middle"> x={cx}
{task.task.title == null ? task.id : task.task.title} y={cy + 5}
</text> 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> </g>

View file

@ -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;

View file

@ -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",
@ -56,6 +16,80 @@
"26-Z4-3" "26-Z4-3"
] ]
}, },
{
"id": "26-Z1-3",
"type": "open-data",
"comment": "Zamilovaný dopis - triviální, procházení stringů po znacích a kontrola podmínek",
"requires": []
},
{
"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-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": "26-Z2-2",
"type": "open-data",
"comment": "SADO - triviální, procházení celých čísel v intervalu a kontrola podmínky",
"requires": []
},
{
"id": "26-Z2-3",
"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",
"requires": []
},
{
"id": "26-Z2-4",
"type": "open-data",
"comment": "Životně důležitá úloha - dostanu pole čísel, potřebuju v něm najít pravidelně se opakující výskyty hodnot, relativně složitá práce s poli, dvojcemi apod.",
"requires": []
},
{
"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": "26-Z3-2",
"type": "open-data",
"comment": "Čarodějova šifra - šifrování mřížkou, práce se stringy, rotace matice, vhodné na code review?, docela hard",
"requires": []
},
{
"id": "26-Z3-3",
"type": "open-data",
"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",
"comment": "Vražedná čísla- WTF, teorie čísel, hard as fuck, divný",
"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": "26-Z4-3", "id": "26-Z4-3",
"type": "open-data", "type": "open-data",
@ -65,19 +99,17 @@
] ]
}, },
{ {
"id": "29-Z3-1", "id": "26-Z4-4",
"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": "Hlídači v labyrintu - policajti hlídající na grafu, konkrétně na stromě, rekurze, technicky asi až DP",
"requires": [ "requires": []
"26-Z4-3"
]
}, },
{ {
"id": "31-Z1-4", "id": "27-Z2-1",
"type": "open-data", "type": "open-data",
"comment": "2D pole (až na poslední vstup), hešování (piškvorky) - navazuje na 26-Z1-2", "comment": "Závorky z cereálií - 2 průchody pole, závorky",
"requires": [ "requires": [
"26-Z4-3" "26-Z1-1"
] ]
}, },
{ {
@ -88,6 +120,18 @@
"jak-resit-ulohy" "jak-resit-ulohy"
] ]
}, },
{
"id": "29-Z1-3",
"type": "open-data",
"comment": "Petrova statistika — ne úplně triviální, tvorba histogramu z pole",
"requires": []
},
{
"id": "29-Z1-4",
"type": "open-data",
"comment": "Zuzčin výlet — DFS (topologické pořadí)",
"requires": []
},
{ {
"id": "29-Z2-1", "id": "29-Z2-1",
"type": "open-data", "type": "open-data",
@ -96,6 +140,26 @@
"29-Z1-1" "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-Z3-3",
"type": "open-data",
"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", "id": "29-Z4-3",
"type": "open-data", "type": "open-data",
@ -106,88 +170,12 @@
] ]
}, },
{ {
"id": "26-Z2-4", "id": "31-Z1-1",
"type": "open-data", "type": "open-data",
"comment": "Životně důležitá úloha - dostanu pole čísel, potřebuju v něm najít pravidelně se opakující výskyty hodnot, relativně složitá práce s poli, dvojcemi apod.", "requires": [
"requires": [] "jak-resit-ulohy"
}, ],
{ "comment": "úloha s odčítáním času"
"id": "29-Z1-3",
"type": "open-data",
"comment": "Petrova statistika — ne úplně triviální, tvorba histogramu z pole",
"requires": []
},
{
"id": "26-Z2-2",
"type": "open-data",
"comment": "SADO - triviální, procházení celých čísel v intervalu a kontrola podmínky",
"requires": []
},
{
"id": "26-Z3-3",
"type": "open-data",
"comment": "Hádanka - triky s dělitelností devíti a rozkladem čísel, docela hard teorie",
"requires": []
},
{
"id": "26-Z4-1",
"type": "open-data",
"comment": "Vražedná čísla- WTF, teorie čísel, hard as fuck, divný",
"requires": []
},
{
"id": "29-Z3-3",
"type": "open-data",
"comment": "Šestková čísla — mega voser implementovat, spíš matematická úloha, převod dešitkových čísel do hacknutých římských",
"requires": []
},
{
"id": "26-Z1-3",
"type": "open-data",
"comment": "Zamilovaný dopis - triviální, procházení stringů po znacích a kontrola podmínek",
"requires": []
},
{
"id": "26-Z2-3",
"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",
"requires": []
},
{
"id": "26-Z3-2",
"type": "open-data",
"comment": "Čarodějova šifra - šifrování mřížkou, práce se stringy, rotace matice, vhodné na code review?, docela hard",
"requires": []
},
{
"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",
"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",
"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",
"comment": "Tvar labyrintu - nejdelší cesta ve stromě, graf",
"requires": []
},
{
"id": "29-Z1-4",
"type": "open-data",
"comment": "Zuzčin výlet — DFS (topologické pořadí)",
"requires": []
}, },
{ {
"id": "31-Z1-2", "id": "31-Z1-2",
@ -195,6 +183,14 @@
"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": [ "26-Z1-4": [
-158, 400,
276 -203
], ],
"31-Z1-4": [ "26-Z2-1": [
-245, -115,
284 184
],
"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": [ "26-Z2-2": [
-64.3269017874483, -278,
-91.7677899309802 -162
],
"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": [ "26-Z2-3": [
-320.2744362098852, -320.2744362098852,
-72.1967458848867 -72.1967458848867
], ],
"26-Z2-4": [
575,
-111
],
"26-Z3-1": [
446,
-160
],
"26-Z3-2": [ "26-Z3-2": [
644.167217052611, 644.167217052611,
-0.4551191547699971 -0.4551191547699971
], ],
"29-Z3-2": [ "26-Z3-3": [
-368.51198400620785, 485,
1.6854832115582556 -81
],
"26-Z1-4": [
147.37372961796666,
-89.40554252368531
],
"26-Z4-4": [
335.75328660449225,
-81.50932588628959
], ],
"26-Z3-4": [ "26-Z3-4": [
-480.95284944300494, -480.95284944300494,
29.587091058893556 29.587091058893556
], ],
"29-Z1-4": [ "26-Z4-1": [
554.2061258687584, -551.4441557449455,
-44.42093615098819 -38.58561957493338
],
"31-Z1-2": [
-236.06692326455527,
-77.65095973572805
],
"31-Z3-2": [
-154.07095173801807,
-65.44506844403092
],
"31-Z3-3": [
514.5773720938831,
41.05028681292239
], ],
"26-Z4-2": [ "26-Z4-2": [
-633.6757896792913, -633.6757896792913,
1.4284188628810082 1.4284188628810082
], ],
"26-Z3-1": [ "26-Z4-3": [
234.170971842641, -220.83782675574338,
-59.230241172550606 190.72741511636147
],
"26-Z4-4": [
471,
-114
],
"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
],
"29-Z3-3": [
470,
-213
],
"29-Z4-3": [
164,
364
],
"31-Z1-1": [
295,
119
],
"31-Z1-2": [
-360,
-205
],
"31-Z1-4": [
-245,
284
],
"31-Z3-2": [
-398,
-139
],
"31-Z3-3": [
514.5773720938831,
41.05028681292239
],
"jak-resit-ulohy": [
88,
-5
],
"label-1d-pole": [
-115,
80
],
"label-2d-pole": [
-294,
173
],
"label-uvod": [
82,
-128
],
"label-zelvy": [
188,
166
],
"start": [
83,
-87
] ]
} }
} }