Po zadání modelu uživatel použije tlačítko „Plot!“. To spustí funkci, která zajišťuje celý výpočet a vykreslení samotného grafu.
V následujících částech kapitoly se budu postupně zabývat podrobným popisem jednotlivých částí tohoto výpočtu i samotným vykreslením grafu.
2.3.1 Výpočet základních přípustných řešení
První částí výpočtu je získání všech základních přípustných řešení. Dále bude popsán průběh tohoto výpočtu. Pro snazší pochopení algoritmu je na obrázku 14 vytvořen diagram, který ho popisuje.
Nejprve se vytvoří pole FeasiblesArr, do něhož je potřeba vložit všechny názvy omezení, aby z nich později byly vytvořeny kombinace. Poté jsou pomocí „for“ cyklu, procházena omezení uložená v místním úložišti prohlížeče a postupně přidávána do pole. Jakmile je dokončen tento cyklus, jsou v poli FeasiblesArr uloženy všechny názvy omezení.
Následuje spuštění funkce combine, ta postupně v cyklu vytváří z pole FeasiblesArr unikátní kombinace trojic (trojice proto, že se jedná o trojrozměrný graf) omezení.
Pro takto vzniklou kombinaci jsou vytvořeny tři pomocné proměnné constraint1, constraint2, constraint3 a do nich je přiřazen objekt, který představuje zápis rovnice či nerovnice omezení v jednom z následujících tvarů:
1 2 3
Tento objekt je získán z místního úložiště, kde jsou uložena všechna omezení, ovšem v již serializované podobě (v datovém typu String). Proto je nutné převést tuto podobu zpět na objekt. Převedení nám umožňuje využití funkce parse (viz následující výpis).
constraint1 = JSON.parse(localStorage.getItem(combiResFeas[0]));
constraint2 = JSON.parse(localStorage.getItem(combiResFeas[1]));
constraint3 = JSON.parse(localStorage.getItem(combiResFeas[2]));
Následně je vytvořena proměnná (viz Výpis 3) matrixDelta4 (typu pole), která představuje zápis matice ve formátu: podle (Kannan, Dinakaran a Lavanya, 2004, s. 8) spočítá determinant matice ∆4:
1 1 1
Výpis 3 Výpočet determinantu matice (zdroj: autor) //spočítá delta4 pro danou kombinaci
matrixDelta4 = [
[constraint1[0], constraint1[1], constraint1[2]], [constraint2[0], constraint2[1], constraint2[2]], [constraint3[0], constraint3[1], constraint3[2]]
];
delta4 = math.det(matrixDelta4);
Pokud platí ∆4 = 0, znamená to, že daná kombinace třech omezení nemá průsečík a pokračuje se další kombinací.
Pokud platí ∆4≠ 0, je nutné dopočítat podle (Kannan, Dinakaran a Lavanya, 2004, s.8)
∆1, ∆2, ∆3 následovně:
Souřadnice jsou definovány soustavou rovnic (1.3.1), ze kterých vyplývá, že platí:
1
Výpis 4 Výpočet průsečíků poloprostorů omezení (zdroj: autor) if(delta4 !== 0) {
//spočítá ostatní delty matrixDelta1 = [
[constraint1[1], constraint1[2], -constraint1[4]], [constraint2[1], constraint2[2], -constraint2[4]], [constraint3[1], constraint3[2], -constraint3[4]]
];
matrixDelta2 = [
[constraint1[2], -constraint1[4], constraint1[0]], [constraint2[2], -constraint2[4], constraint2[0]], [constraint3[2], -constraint3[4], constraint3[0]]
];
matrixDelta3 = [
[-constraint1[4], constraint1[0], constraint1[1]], [-constraint2[4], constraint2[0], constraint2[1]], [-constraint3[4], constraint3[0], constraint3[1]]
];
Jakmile máme určeny souřadnice průsečíku, je nutné ověřit, zda průsečík splňuje podmínky všech omezení. Ne všechny tyto získané průsečíky náleží do množiny přípustných řešení.
Proto je potřeba je postupně dosadit do všech zadaných omezení. V případě, že splní podmínky všech omezení, jedná se o základní přípustné řešení a souřadnice tohoto řešení jsou přidány do polí FeasiblePointsX1, FeasiblePointsX2, FeasiblePointsX3.
Pokud alespoň jedné z rovnic nevyhovuje, nejedná se o základní přípustné řešení, dále už není potřebné, proto se „zahodí“ a pokračuje se v cyklu dalším výpočtem (viz výpis 5).
Výpis 5 Validace průsečíků (zdroj: autor)
//pokud se jedná o účelovou funkci v localstorage, přeskočí v cyklu 4
//jinak načte potřebné parametry nerovnice do proměnné 5
if(localStorage.key(j) === "/>>$ObjFunction|") { 6
continue;
7
} else{
8
loaded = JSON.parse(localStorage.getItem(localStorage.key(j)));
9
} 10
//levá a pravá strana nerovnice 11
//vytváří nerovnici či rovnici k následnému vyhodnocení z levé a pravé strany nerovnice či 15
//pokud platí všechny nerovnice či rovnice pro dané x1, x2, x3 --> push do pole základních 24
2.3.2 Výpočet hraničních přípustných řešení
Dalším krokem výpočtu je výpočet tzv. hraničních přípustných řešení. Pomocí nich se zjistí, zda má úloha alternativní optimální řešení. V případě, že úloha má neomezenou množinu přípustných řešení (pokračuje do nekonečna), jsou tyto body použity k vykreslení takové neomezené množiny přípustných řešení. Tu lze však zobrazit pouze částečně a uživateli nastínit, jakým směrem pokračuje do nekonečna. Na obrázku 15 je ukázka neomezené množiny, která pokračuje do nekonečna ve směru oranžových šipek (oranžové šipky jsou doplněné ručně). Žluté body na obrázku jsou právě hraniční přípustná řešení.
Obrázek 15 Ukázka neomezené množiny přípustných řešení (zdroj: autor)
Při výpočtu hraničních přípustných řešení, je nejprve nutné získat maximální hodnoty všech os grafu. Ty poté vynásobíme konstantou multiplier (v současné chvíli nastaveno na 1,8).
Tím je docíleno, že v případě vykreslování neomezené množiny přípustných řešení, bude zobrazena v určitém poměru ke zbytku grafu. Poté přidáme do soustavy omezení tři další omezení (hraniční) v následujícím tvaru:
1 2 3
, , . x graphMaxX1 multiplier x graphMaxX2 multiplier x graphMaxX3 multiplier
(2.2.6)
kde proměnné graphMaxX1, graphMaxX2, graphMaxX3 jsou získaná maxima jednotlivých os grafu a multiplier přednastavená konstanta.
Následně je třeba vytvořit pole, které bude obsahovat všechna omezení, včetně třech nově vzniklých, aby mohly být vypočítány všechny průsečíky hraničních rovin (omezení). Proto je vytvořeno nové pole BorderPsArr. Toto pole vznikne sloučením pole FeasiblesArr a třech nových omezení. Obsahuje tedy všechna omezení zadaná uživatelem a tři nová hraniční omezení (2.2.6). S daným polem provede práci funkce combineBorderP. Tato funkce pracuje téměř stejně jako funkce combine. Vypočítá všechny průsečíky omezení a uloží je jako pole do proměnných BorderPointsX1, BorderPointsX2, BorderPointsX3.
Jelikož tato pole obsahují i některé souřadnice průsečíků z množiny základních přípustných řešení, je nutné odfiltrovat tyto shodné průsečíky. To zajišťuje následující část kódu, která vytvoří tři nová pole filteredX1, filteredX2, filteredX3 s průsečíky s hraničními omezeními, která nahrazují pole BorderPointsX1, BorderPointsX2, BorderPointsX3. V těchto polích už nejsou souřadnice duplicitních průsečíků (viz výpis 6).
Výpis 6 Filtrace shodných bodů (zdroj: autor)
let filteredX1 = [];
let filteredX2 = [];
let filteredX3 = [];
//vyfiltruje stejné body z polí BorderPoints a FeasiblePoints, uloží do nového pole ty, které jsou jedinečné
for(i=0; i<BorderPointsX1.length;i++){
let c = true;
let t = [BorderPointsX1[i], BorderPointsX2[i], BorderPointsX3[i]];
for(j=0;j<FeasiblePointsX1.length;j++){
let f = [FeasiblePointsX1[j], FeasiblePointsX2[j], FeasiblePointsX3[j]];
if(JSON.stringify(t) === JSON.stringify(f)){
c = false;
se pouze odlišnými názvy některých proměnných a některé funkce sloužící k porovnávání hodnot by byly nahrazeny funkcemi inverzními. Diagram lze nalézt v příloze B.
V současné fázi výpočtu jsou k dispozici základní přípustná řešení a hraniční přípustná řešení. Jejich souřadnice jsou uloženy v polích FeasiblePointsX1, FeasiblePointsX2, FeasiblePointsX3 a v polích filteredX1, filteredX2, filteredX3. Nyní je potřeba všechny tyto průsečíky postupně dosazovat do uživatelem zadané účelové funkce a hledat bod, v němž účelová funkce dosahuje maxima nebo minima. (viz výpis 7)
Výsledek úlohy může mít mnoho podob. Zde se pokusím popsat jednotlivé situace.
Pokud hodnota účelové funkce dosahuje lepšího maxima nebo minima v průsečíku hraničních omezení, boolean hodnota proměnné valueBetter je nastavena na true. To znamená, že úloha nemá optimální řešení, jelikož má neomezenou hodnotu účelové funkce.
Pokud maximum nebo minimum pro danou účelovou funkci dosahuje stejné hodnoty (maxima nebo minima) v průsečíku hraničních omezení jako v bodě, který je součástí základních přípustných řešení, pak se boolean hodnota proměnné valueEqual nastaví na true. To znamená, že výsledkem je polopřímka (případně polorovina) a tento průsečík hraničních omezení (bod) udává její směr. Úloha má alternativní optimální řešení.
Pokud hodnoty v průsečících hraničních omezení jsou horší než v bodech základních přípustných řešení, její optimální řešení spočívá v množině základních přípustných řešení.
Výpis 7 Hledání optima účelové funkce (maxima) (zdroj: autor) //maximalizace účelové funkce
1
if(objectiveFunction[3] === "max") { 2
//maxFeas je hodnota pro první bod 3
//pokud máme nějaké hraniční průsečíky 8
if(filteredX1.length>0){
9
maxBorder = firstBorder;
10
SolutionCoordX1border[0] = filteredX1[0];
11
SolutionCoordX2border[0] = filteredX2[0];
12
SolutionCoordX3border[0] = filteredX3[0];
13 14 } 15
for(i = 1; i < FeasiblePointsX1.length; i++) { 16
//a je výpočet hodnoty pro další průsečík 17
a = math.bignumber(math.add(math.round(math.multiply(objectiveFunction[0], 18
//pokud body mají shodnou hodnotu 22
SolutionCoordX1feas[0] = FeasiblePointsX1[i];
31
SolutionCoordX2feas[0] = FeasiblePointsX2[i];
32
SolutionCoordX3feas[0] = FeasiblePointsX3[i];
33 34 } 35 }
//porovnávání pro hraniční body 36
a = math.bignumber(math.add(math.round(math.multiply(objectiveFunction[0], 40
SolutionCoordX1border.push(filteredX1[i]);
45
SolutionCoordX2border.push(filteredX2[i]);
46
SolutionCoordX3border.push(filteredX3[i]);
47
SolutionCoordX1border[0] = filteredX1[i];
52
SolutionCoordX2border[0] = filteredX2[i];
53
SolutionCoordX3border[0] = filteredX3[i];
54
//pokud je výsledná hodnota účelové funkce u hraničních průsečíků stejná jako u průsečíků 63
//pokud je výsledná hodnota účelové funkce u hraničních průsečíků lepší než u průsečíků 68
2.3.4 Grafické zobrazení všech typů výsledků
Vyobrazení výsledků je poměrně složité, jelikož může nastat několik případů. V první fázi je nutné odlišit tři typy výstupů, které mohou nastat.
První možností je, že proměnné valueEqual a valueBetter zároveň nabývají hodnoty false.
V tomto případě se optimální řešení nachází v množině základních přípustných řešení.
V takovém případě je nejprve vykreslován objekt, jehož vrcholy jsou všechny body z množiny základních přípustných řešení. Tyto vrcholy jsou pak v grafu vyznačeny jako modré body. Poté je třeba vykreslit optimální řešení. To může mít trojí podobu. Řešením může být bod, úsečka nebo rovinná výseč. O jaké řešení se jedná, poznáme z délky pole, v němž jsou uloženy souřadnice výsledných bodů. Pokud je délka pole rovna jedné, výsledkem je bod. Pokud je délka pole rovna dvěma, výsledkem je úsečka mezi dvěma body, které máme v poli. Jestliže je délka pole rovna třem, výsledkem je rovinná výseč.
Druhou možností je, že hodnota proměnné valueEqual je rovna true. To nastane, pokud hledaný extrém účelové funkce, má stejnou hodnotu v bodě ze základních přípustných řešení jako v bodě z hraničních přípustných řešeních. Optimálním řešením je v tomto případě polopřímka, případně polorovina či rovinná výseč. Počáteční bod této polopřímky je ten bod ze základních přípustných řešení, se kterým hledaná účelová funkce nabývá extrému. Směr této polopřímky je určen pomocí bodu z hraničních přípustných řešení, v němž hodnota účelové funkce taktéž nabývá extrému. Při vykreslování roviny nebo rovinné výseče jsou sloučeny pole výsledných bodů ze základních přípustných řešení a pole výsledných bodů z hraničních přípustných řešení.
Třetí možností je, že hledaný extrém účelové funkce má „lepší“ hodnoty v nějakém bodě z hraničních přípustných řešení než v bodě z množiny základních přípustných řešení.
V takovém případě je hodnota proměnné valueBetter rovna true. To znamená, že neexistuje optimální řešení, jelikož hodnota účelové funkce je neomezená. V tomto případě je v grafu vykreslena pouze množina přípustných řešení, která pokračuje do nekonečna ve směru žlutých bodů (tyto body pouze pomáhají vyznačit směr, nemají jiný účel). Uživateli je zobrazena hláška, že optimální řešení neexistuje (viz obrázek 28).
Pokud nastane situace, kdy uživatel zadá úlohu, která má prázdnou množinu přípustných řešení, jelikož poloprostory omezení se „rozcházejí“, je upozorněn hláškou, algoritmus je zastaven a dále nepokračuje. Taková situace se pozná, pokud pole FeasiblePointsX1, FeasiblePointsX2, FeasiblePointsX3 jsou prázdná, tedy nemáme žádná základní přípustná řešení. V tomto případě je zobrazen pouze graf se zadanými omezeními.
2.3.5 Použití knihovny Plotly k vykreslení grafu Implementace knihovny
Knihovna je do webové aplikace implementována pomocí odkazu ke stažení ze CDN
v rodičovském elementu body. To zajišťuje, že se při každém načtení webové stránky stahuje nejaktuálnější verze této knihovny.
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
Příprava knihovny
V počáteční fázi je na stránce vytvořen prázdný div s unikátním identifikátorem, do něhož je poté vykreslován graf. Následuje vytvoření konstanty v Javascriptu, do které je přiřazen objekt daného „divu“. Dalším potřebným krokem je vytvoření dvou konstant layout a config. Tyto dvě konstanty jsou objekty, ze kterých si později knihovna shromažďuje nastavení pro graf, více o těchto proměnných dále (Vykreslování).
Vykreslování
K vykreslování slouží především dvě funkce. Funkce newPlot a funkce addTrace.
V popisované aplikaci je třeba do grafu postupně přidávat velké množství objektů různých typů, proto po stisknutí tlačítka „Plot!“ je nejprve pomocí funkce newPlot vykreslen prázdný graf a do něj se v průběhu výpočtu přidávají objekty.
Plotly.newPlot(myDiv, [], layout, config);
Prvním parametrem funkce newPlot je myDiv. To je objekt, ve kterém je uložen odkaz na samotný předpřipravený HTML element (typu div), do něhož se graf vykresluje.
Do prázdných hranatých závorek by se většinou vkládal objekt skládající se z dat pro graf, typu objektu a dalších popisných parametrů. Zde jsou pouze prázdné závorky, jelikož zatím je vytvářen prázdný graf.
V objektu layout je potřeba nastavit některé parametry, které určují rozložení grafu.
Například se zde určuje velikost písma nebo názvy os grafu (viz výpis 8).
Výpis 8 Konstanta layout (zdroj: autor)
title: 'x<sub>1</sub>', },
yaxis: {
title: 'x<sub>2</sub>', },
zaxis: {
title: 'x<sub>3</sub>', }
} };
V objektu config se nastavují další parametry pro graf. V případě této aplikace tak, aby byl graf responzivní, tedy zabíral vždy maximální plochu v přiřazeném HTML elementu, a aby nebylo zobrazeno logo vykreslovací knihovny.
const config = {responsive: true, displaylogo: false,};
V průběhu algoritmu se přidávají do „prázdného“ grafu různé typy objektů, a to pomocí funkce addTraces. Na následujícím výpisu (výpis 9) lze například vidět, jak jsou vykreslovány poloprostory rovnoběžné s osami grafu.
Výpis 9 Ukázka funkce addTraces (zdroj: autor) Plotly.addTraces(myDiv, {
Za parametry x, y, z je vždy dosazováno pole, jenž reprezentuje souřadnice vrcholů nebo bodů nutných pro vykreslení grafu. Pomocí parametru type se určuje typ vykreslovaného
parametr mesh3D. Parametr opacity určuje průhlednost daného objektu.
V ukázce (výpis 9) je dosazena proměnná constraintsOpacity, do níž je na počátku funkce dopočítána a přiřazena hodnota odpovídající posuvníku průhlednosti na stránce (více o tomto posuvníku v části Průhlednost omezení).
Do parametru hovertext je vložen textový řetězec, který se zobrazí ve vyskakovací „bublině“
v případě, že uživatel „najede“ myší na daný objekt.
2.3.6 Další funkcionalita aplikace
Základním kritériem této aplikace bylo, aby výsledný graf byl pro uživatele co nejpřehlednější. Pokud nastane situace, kdy je zadáno mnoho omezení, stává se graf méně přehledný. Viz obrázek 16, kde je vykreslen graf s „pouze“ šesti omezeními a už tak ztrácí na přehlednosti.
Zobrazení hraničních rovin značně snižuje přehlednost zobrazení množiny přípustných řešení a optimálního řešení. Proto bylo potřeba implementovat řešení, které uživateli usnadní orientaci v grafu.
Obrázek 16 Nepřehlednost grafu úlohy se 6 omezeními (zdroj: autor)
K usnadnění orientace v grafu, slouží v aplikaci 2 prvky, úplné skrytí hraničních rovin nebo změna jejich průhlednosti.
Skrytí poloprostorů omezení
Prvním prvkem je zaškrtávací políčko s popisem „Display constraints in graph“. Pokud není zaškrtnuto, poloprostory omezení nebudou v grafu vykresleny a bude vykreslena pouze množina přípustných řešení, optimální řešení nebo alternativní optimální řešení (dle situace). Pokud se jedná o množinu přípustných řešení s tvarem mnohostěnu, oproti
standardnímu zobrazení bylo pro další zvýšení přehlednosti nastaveno, aby mnohostěn měl různé barvy stran jeho povrchu. To uživateli značně usnadňuje zobrazení pouhého řešení.
Viz obrázek 17, který zobrazuje stejnou úlohu jako obrázek 16, tentokrát bez vykreslení omezení.
Obrázek 17 Graf úlohy se 6 omezeními, bez vykreslení omezení, s barevným odlišením stěn (zdroj: autor)
Pokud není použito barevné odlišení stěn mnohostěnu, je složitější odlišit některé stěny od jiných, jelikož splývají do sebe (viz obrázek 18).
Obrázek 18 Graf úlohy s 6 omezeními, bez vykreslení omezení, bez barevného odlišení stěn (zdroj: autor)
Průhlednost omezení
Druhým prvkem k usnadnění přehlednosti grafu je posuvník s popisem „Constraints opacity“, pomocí něhož lze nastavit průhlednost omezení. Průhlednost se nastavuje v procentech, přičemž minimální hodnota je 10 % a maximální 100 %, přírůstky jsou nastaveny po 10 %. Výchozí hodnota je nastavena na 100 %.
<input type="range" min="10" max="100" step="10" value="100" id="opacity" class="slider">
Zobrazení účelové funkce
V případě, že má úloha jediné optimální řešení, lze pomocí zaškrtávacího políčka s popisem
„Display objective function in graph“ zobrazit účelovou funkci. Při zaškrtnutí tohoto políčka se účelová funkce zobrazí v grafu (viz obrázek 19)
Obrázek 19 Zobrazení účelové funkce (zdroj: autor)
Nahlásit bug
V aplikaci je možné přejít na stránku „Report bug“, kde je možné pomocí předpřipraveného formuláře nahlásit jakýkoliv problém s aplikací. Tento formulář je poté zpracován a odeslán jako e-mail na speciální e-mailovou adresu, kde budou problémy shromažďovány a následně řešeny. Kód pro zpracování tohoto formuláře byl napsán pomocí jazyka PHP (viz obrázek 20).
Obrázek 20 Formulář pro nahlášení problémů s aplikací (zdroj: autor)
2.4 Problémy při vývoji aplikace
Při tvorbě aplikace jsem narazil na celou řadu problémů. Většina těchto obtíží souvisela s technickými omezeními vykreslovací knihovny nebo s problémy při práci s velkými čísly v Javascriptu. Většinu komplikací se mi podařilo odstranit. Část z nich však zůstává nevyřešena. Jejich řešení totiž klade větší nároky na čas. Další jsou limitovány vlastnostmi vykreslovací knihovny.
2.4.1 Zaokrouhlovací problém
Tento problém vzniká při násobení necelých čísel v Javascriptu.
V části aplikace se ověřují průsečíky poloprostorů omezení dosazením do všech rovnic (nerovnic) omezení. Nejprve se vytvoří nerovnice či rovnice, která se skládá z levé a pravé strany a operátoru (kterým je ≤ nebo ≥ nebo =). Následně je třeba vyhodnotit, zda rovnost (nerovnost) platí či ne. To se standardně provádí funkcí eval, viz následující kód.
formula= LHS + operator + RHS;
Pokud je použita tato funkce, nejsou započítány odchylky vzniklé násobením necelých čísel a některé rovnice (nerovnice) jsou vyhodnoceny nesprávně (viz obrázek 21). To způsobovalo, že některé body nebyly přidány do množiny přípustných řešení.
Obrázek 21 Vyhodnocení nerovnice při použití funkce eval (zdroj: autor)
Tento problém byl vyřešen použitím funkce evaluate knihovny Math.js, která s drobnými odchylkami umí pracovat a vyhodnocuje vše správně. Relační funkce knihovny Math.js zkontrolují, zda je relativní rozdíl mezi porovnávanými hodnotami menší než předem definovaná konstanta. Ve výchozím nastavení knihovny je tato konstanta 1-12, tuto hodnotu lze upravit. V aplikaci byla ponechána výchozí hodnota.
formula= LHS + operator + RHS;
if(!(math.evaluate(formula))) {checkFeas = false;}
Viz obrázek 22, kde je již vidět správné vyhodnocení nerovnic.
Obrázek 22 Správné vyhodnocení nerovnic s použitím knihovny Math.js (zdroj: autor)
2.4.2 Směr polorovin omezení
Vykreslené poloprostory reprezentující omezení soustavy mají směr, který je udán znaménkem rovnice. Ve většině případů není nutné tento směr nijak vyznačovat, jelikož výsledná množina přípustných řešení dostatečně naznačuje, jakým směrem poloprostory směřují a vyznačení směru omezení šipkou by pouze graf udělalo více nepřehledným.
V některých případech by však toto vyznačení mohlo být vhodné. Nastává zde však otázka, jak takové případy pomocí algoritmu poznat? Teoreticky by ho šlo vyřešit způsobem, který je uveden autorkou BP z roku 2017:
„Teoreticky by to mohlo fungovat tak, že se nalezne střed přímky (myšleno ale střed na viditelné části grafu), zjistí se směr, jakým by šipka měla směřovat (například dosazením libovolného bodu z jedné z polorovin) a vytyčí se nová datová řada se zakončením šipky v daném směru.“ (Nebesová, 2017, s. 2)
Tento způsob řešení je dle mého názoru velice komplikovaný a jeho realizace by byla velmi časově náročná a nepřinesla by mnoho užitku. Jelikož se jednotlivé objekty v grafu vykreslují zvlášť a nezávisle na sobě, přidáním dalších výpočtů a vykreslováním dalších objektů, by se již tak zatížený algoritmus ještě více zpomalil. Navíc je možné, že by se složité grafy staly více nepřehledné. Proto tento problém zůstává nevyřešen.
2.4.3 Šipky polopřímek
Jak lze vidět z obrázku 27, kde výsledkem úlohy je polopřímka, pro její grafické zobrazení by bylo vhodné, aby její směr byl udán „šipkou“. Toto bohužel vykreslovací knihovna zatím nepodporuje, a proto je ve výsledku vyznačen pouze začátek této polopřímky.
2.4.4 Mizení částí grafu
Pokud je vykreslen graf a dojde ke změně velikosti okna prohlížeče (např.: zmenšení okna prohlížeče z celé obrazovky na menší, přesunutí okna prohlížeče, zapnutí vývojářské konzole, atd.), občas se stane, že některé části grafu zmizí. Tento problém je bohužel způsoben vlastnostmi vykreslovací knihovny, tudíž ho v rámci této práce není možné řešit.
3 Testování aplikace
K otestování správné funkčnosti aplikace bylo nutné vytvořit sérii úloh se všemi typy výsledků. Výsledky těchto úloh byly pro kontrolu ověřeny pomocí softwaru LINGO. Většina
K otestování správné funkčnosti aplikace bylo nutné vytvořit sérii úloh se všemi typy výsledků. Výsledky těchto úloh byly pro kontrolu ověřeny pomocí softwaru LINGO. Většina