Dráhy planét
K čomu aplikácia slúži?
Program počíta a následne vykreslí dráhy planét (ich relatívna vzdialenosti vzhľadom ku zvolenej "stredovej" planéte). Navolené planéty a nastavenia je možné uložiť do súboru a zo súboru zase načítať. Program podporuje exportovanie obrázku vo formáte .png alebo .bmp.
Prečo práve dráhy planét?
Pri hodinách astronómie v rámci fyziky sme tieto dráhy museli pracne rysovať (trvalo to niekoľko hodín). Na popud kamaráta som preto začal pracovať na programe, ktorý mal prácu zjednodušiť. Algoritmus pre zistenie pozície planéty v danom čase som vymyslel už vtedy pri hodine a nie je nijako zložitý. Netušil som však, že sa nakoniec aplikácia so všetkou omáčkou okolo (návrh aplikácie, GUI podľa mojich predstáv, správne vykreslenie, flexibilita programu, ukladanie do súboru atď.) Rozrastie v môj zatiaľ asi najväčší projekt a bude trvať desiatky hodín ho naprogramovať.
Poznámka: Planéta je pre jednoduchosť myslené akékoľvek teleso, ktoré obieha okolo iného.
Čo som sa naučil?
Chcel by som sa podeliť o niekoľko zo skúseností, ktoré som počas vývoja získal. Dúfam, že pomôžu vyriešiť problémy podobné tým, s ktorými som sa počas vývoja stretol.
Krivka spájajúca body
Problém
Ako spojiť body značiace dráhu planéty plynulou krivkou? Nechcel som body spájať rovnými úsečkami - nevyzeralo by to dobre a pre trochu hladšie krivku by bolo potrebné vygenerovať veľmi veľa bodov. Čo s tým?
Riešenie
Zistil som, že napísať vykreslenie takéto krivky pomocou Beziérových
kriviek vyzerá trochu zložito. Nakoniec som našiel, že knižnice Windows
Forms už takúto funkciu obsahujú. Konkrétne metódu
Graphics.DrawCurve(Pen pen, Point[] points);
Rozhodol som sa teda
aplikáciu kompletne prerobiť z WPF do WinForms.
Prispôsobenie šírky kontroliek scrollbaru
Problém
Máme FlowLayoutPanel
s FlowDirection = TopDown
.
Chceme, aby sa pri nedostatku miesta pre kontrolky objavil scrollbar, nastavíme
teda AutoScroll
na True
a WrapContents
na
False
. Tu nastáva problém. Scrollbar sa síce objaví podľa
očakávaní, ale zaberie miesto v paneli. Tým pádom sa kontrolky nezmestia
vodorovne a objaví sa aj vodorovný scrollbar. To však nie je požadované
správanie. Lepšie by bolo, keby sa kontrolky vodorovne o trochu zmenšili a
urobili tak miesto pre scrollbar. Neprišiel som ale na to, ako tohto správania
dosiahnuť bez pomoci vlastného kódu na pozadí. Prišiel som ale na iné
riešenie.
Riešenie
Riešením je nepoužívať FlowLayoutPanel, ale klasický Panel. Panelu sa
nastaví AutoScroll
na True
a kontrolkám vnútri
Dock = Top
. Jednoduché, že? Keď sa teraz kontrolky na panel
nezmestia, objaví sa zvislý scrollbar a kontrolky sa trochu zmenší, aby mu
urobili miesto.
Skrytie položiek v dropdown
Problém
Planéty v programe majú vlastnosť Parent
, ktorá udáva,
okolo aké planéty má daná planéta obiehať. Výber planéty som zo
začiatku riešil ComboBox (DropDownStyle = DropDownList
), ktorý
bol binding napojený na zoznam všetkých planét. Problémom je v tom, že ako
rodičovskú planétu nemôžem nastaviť planétu samotnú ani žiadnu z jej
"detí". Tie sa teda nesmie objaviť ani v ComboBox. Otázkou je, ako je v
combobox skryť. Prvé pokusy viedli cez udalosť Binding.Format
.
Neboli ale úspešné. Buď by sa nevhodné planéty odobrali aj z pôvodného
BindingListu (čo samozrejme nechceme), alebo by nefungoval binding, pretože
sme položky prekopírovali do novej kolekcie. Ďalšou možnosťou, ktorú som
našiel bolo skryť nežiadané položky v ComboBox. To má ale hneď niekoľko
háčikov. Po prvé, je potrebné prepísať ComboBox tak, aby skryl nechcené
položky. To zahŕňa vytvorenie vlastnej kontrolky a prepísanie logiky
vykresľovanie a pozicovanie, pričom sa navyše môžu vyskytnúť nové
problémy. Po druhé, užívatelia vždy nájdu spôsob, ako niečo rozbiť.
Napríklad, ak by sme formulár ovládali pomocou klávesnice, môžeme
označiť aj skryté položky (ktoré tam stále sú, len nie sú vidieť). Po
tretie, bolo by treba nejako upraviť program tak, aby bolo možné
aktualizovať informáciu o tom, či má byť položka skrytá - znamenalo by to
zasahovať do už existujúceho kódu a veľmi ho znepřehlednit.
Dlho som hľadal a vymýšľal ďalšie riešenia, ale na nič funkčného som neprišiel. Nakoniec som sa rozhodol, že sa pokúsim nájsť iný spôsob výberu rodičovskej planéty. Vymyslel som, že po kliknutí na tlačidlo sa zobrazí dialóg s ComboBox na výber planéty, ktorý pred zobrazením načíta planéty, ktorá aktuálne môžu byť "rodičom". V tom sa mi rozsvietilo. Veď vôbec nebolo potrebné vytvárať nový dialóg, ktorý rovnako obsahoval len pôvodné ComboBox.
Riešenie
Riešením je aktualizovať ComboBox v udalosti Enter
, ktorá sa
spustí, keď sa daná kontrolka stane aktívnou kontrolkou formulára. Tu
jednoducho získame validný položky (s pomocou LINQ je to otázkou jedného
riadku) a zobrazíme je užívateľovi. Dropdown sa tak aktualizuje vždy, keď
užívateľ bude chcieť vybrať novú položku, či už myšou alebo
klávesnicou.
Dodatok: Rovnako som na niečo zabudol. Keď používateľ nabehne myšou na
ComboBox a scrolluje, môže zmeniť označenú položku bez spustenia udalosti
Enter. Na riešenie sa môžete pozrieť do zdrojových kódov (udalosti
MouseEnter
a MouseWheel
).
Prázdna položka v dropdown
Problém
Keď planéta neobieha okolo inej planéty (ako napríklad Slnko), je jej
vlastnosť Parent
nastavená na hodnotu null
. Do
combobox by teda bolo vhodné pridať prázdnu položku, ktorá by
reprezentovala hodnotu null
. Do kolekcie s planétami ani do
dropdown hodnotu null
pridať nemôžeme, keďže všetky prázdne
pozície v zozname majú hodnotu "null" a nič by sa nám nezmenilo.
Riešenie
Riešením je v metóde, ktorá filtruje vhodné planéty pridať položku s
textom napríklad "<None>" (môže byť aj prázdny reťazec). Pri
pretypovanie označené položky v udalosti SelectedValueChanged
späť na pôvodnú typ (v tomto prípade Planet
) nepoužiť
notáciu (Planet) ComboBox.SelectedItem
, ale
ComboBox.SelectedItem as Planet
. Tým nám pretypovanie stringu na
planétu nevyhodí výnimku, ale jednoducho bude null
- presne ako
sme chceli.
Automatické mierka, vycentrovanie a zoom
Problém
Vzdialenosti medzi planétami môžu byť veľmi odlišné a pri stále rovnakej mierke môžeme niekedy vidieť jedinú planétu cez celú obrazovku a inokedy zas celú slnečnú sústavu iba ako bodku. Bolo by dobré meradlo automaticky prispôsobovať.
Riešenie
WinForms nám túto prácu značne zjednoduší svojimi vstavanými
funkciami. Najprv presunieme bod [0; 0] doprostred:
g.TranslateTransform(.VisibleClipBounds.Width / 2, g.VisibleClipBounds.Height / 2)
.
Pre zmenu mierky stačí jednoducho funkciu
Graphics.ScaleTransform(float sx, float sy)
odovzdať mierka v osi
xaya pri vykresľovaní sa na každý bod automaticky aplikuje transformácie.
Zostáva nám zistiť správne meradlo. To získame nasledovne:
zoom * velikost plátna / maximální velikost vykreslovaného obrázku
.
Obrázkové fonty
Problém
Pôvodne som v aplikácii pre ikonky používal obrázkové fonty ako sú napríklad Wingdings3. Zistil som ale, že na niektorých zariadeniach nie je font nainštalovaný a miesto ikoniek sa zobrazujú "obdĺžničky".
Riešenie
Trochu zložitejšie cestou s použitím systémových knižníc možno síce fonty importovať, mohli by ale vzniknúť problémy s vlastníckymi právami. Urobiť (ešte krajšie : P ) Ikonky v Inkscape a nastaviť ich ako obrázok na pozadí kontrolky bolo otázkou niekoľkých minút a ušetril som si tak prácu aj nezmyselné otázky ohľadom copyrightu.
Otváranie súborov pomocou aplikácie
Problém
Bolo by dobré, keby aplikácia podporovala otváranie súborov i pomocou "Otvoriť v programe" alebo pretiahnutím súboru na ikonku aplikácie.
Riešenie
Riešenie je veľmi jednoduché, nepotreboval som internet a bolo to presne
tak, ako som si to predstavoval. V súbore Program.cs
pridáte do
static void Main()
argument string[] args
(tak, ako to
býva v konzolovej aplikácii). Získate tak argumenty, s ktorými bola
aplikácia spustená (v tomto prípade bude pole obsahovať jednotlivé cesty
súborov).
Záverom
Rád by som sa vývoji aplikácie venoval ešte ďalej, ale neviem či sa k tomu ešte dostanem. So všetkým som odhadom strávil vývojom niečo pod 100 hodín (= cca 10 000 Sk), takže budem rád za vašu podporu (čo i len milý komentár že aplikácia dobre slúži ). V budúcej verzii by sa mohol napríklad objaviť progressbar, ktorý ukazuje aktuálny stav vykresľovanie.
Aj keď aplikácia svoj pôvodný účel ušetrenie práce vôbec nesplnila, som veľmi rád, že som sa do toho pustil. Najvzácnejšie na tom sú totiž skúsenosti, ktoré som vďaka tomu získal.
Ako vždy, v komentároch sa budem tešiť na pripomienky, prípadné hlásenia bugov a diskusia o iných možných riešeniach, ktorá vás napadnú.
Galéria
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 66x (458.72 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#