Dráhy planet

C# .NET Windows Forms Zdrojákoviště Dráhy planet

K čemu aplikace slouží?

Program počítá a následně vykreslí dráhy planet (jejich relativní vzdálenosti vzhledem ke zvolené "středové" planetě). Navolené planety a nastavení lze uložit do souboru a ze souboru zase načíst. Program podporuje exportování obrázku ve formátu .png nebo .bmp.

Proč zrovna dráhy planet?

Při hodinách astronomie v rámci fyziky jsme tyto dráhy museli pracně rýsovat (trvalo to několik hodin). Na popud kamaráda jsem proto začal pracovat na programu, který měl práci zjednodušit. Algoritmus pro zjištění pozice planety v daném čase jsem vymyslel již tehdy při hodině a není nijak složitý. Netušil jsem ale, že se nakonec aplikace se vší omáčkou kolem (návrh aplikace, GUI dle mých představ, správné vykreslení, flexibilita programu, ukládání do souboru atd.) rozroste v můj zatím asi největší projekt a bude trvat desítky hodin ho naprogramovat.

Poznámka: Planetou je pro jednoduchost myšleno jakékoli těleso, které obíhá kolem jiného.

Co jsem se naučil?

Chtěl bych se podělit o několik ze zkušeností, které jsem během vývoje získal. Doufám, že pomohou vyřešit problémy podobné těm, se kterými jsem se během vývoje setkal.

Křivka spojující body

Problém

Jak spojit body značící dráhu planety plynulou křivkou? Nechtěl jsem body spojovat rovnými úsečkami - nevypadalo by to dobře a pro trochu hladší křivku by bylo potřeba vygenerovat velmi mnoho bodů. Co s tím?

Řešení

Zjistil jsem, že napsat vykreslení takovéto křivky pomocí Beziérových křivek vypadá poněkud složitě. Nakonec jsem našel, že knihovny Windows Forms již takovouto funkci obsahují. Konkrétně metodu Graphics.DrawCurve(Pen pen, Point[] points); Rozhodl jsem se tedy aplikaci kompletně předělat z WPF do WinForms.

Přizpůsobení šířky kontrolek scrollbaru

Problém

Máme FlowLayoutPanel s FlowDirection = TopDown. Chceme, aby se při nedostatku místa pro kontrolky objevil ScrollBar, nastavíme tedy AutoScroll na True a WrapContents na False. Zde nastává problém. ScrollBar se sice objeví podle očekávání, ale zabere místo v panelu. Tím pádem se kontrolky nevejdou vodorovně a objeví se i vodorovný ScrollBar. To ovšem není požadované chování. Lepší by bylo, kdyby se kontrolky vodorovně o trochu zmenšily a udělaly tak místo pro ScrollBar. Nepřišel jsem ale na to, jak tohoto chování dosáhnout bez pomoci vlastního kódu na pozadí. Přišel jsem ale na jiné řešení.

Řešení

Řešením je nepoužívat FlowLayoutPanel, ale klasický Panel. Panelu se nastaví AutoScroll na True a kontrolkám uvnitř Dock = Top. Jednoduché, že? Když se nyní kontrolky na panel nevejdou, objeví se svislý ScrollBar a kontrolky se trochu zmenší, aby mu udělaly místo.

Skrytí položek v DropDownu

Problém

Planety v programu mají vlastnost Parent, která udává, kolem jaké planety má daná planeta obíhat. Výběr planety jsem ze začátku řešil ComboBoxem (DropDownStyle = DropDownList), který byl Bindingem napojen na seznam všech planet. Problémem je v tom, že jako rodičovskou planetu nemůžu nastavit planetu samotnou ani žádnou z jejích "dětí". Ty se tedy nesmí objevit ani v ComboBoxu. Otázkou je, jak je v ComboBoxu skrýt. První pokusy vedly přes událost Binding.Format. Nebyly ale úspěšné. Buď by se nevhodné planety odebraly i z původního BindingListu (což samozřejmě nechceme), nebo by nefungoval binding, jelikož jsme položky překopírovali do nové kolekce. Další možností, kterou jsem našel bylo skrýt nežádané položky v ComboBoxu. To má ale hned několik háčků. Za prvé, je třeba přepsat ComboBox tak, aby skryl nechtěné položky. To zahrnuje vytvoření vlastní kontrolky a přepsání logiky vykreslování a pozicování, přičemž se navíc můžou vyskytnout nové problémy. Za druhé, uživatelé vždy najdou způsob, jak něco rozbít. Například, pokud bychom formulář ovládali pomocí klávesnice, můžeme označit i skryté položky (které tam stále jsou, jen nejsou vidět). Za třetí, bylo by třeba nějak upravit program tak, aby bylo možné aktualizovat informaci o tom, zda má být položka skrytá - znamenalo by to zasahovat do již existujícího kódu a velmi ho znepřehlednit.

Dlouho jsem hledal a vymýšlel další řešení, ale na nic funkčního jsem nepřišel. Nakonec jsem se rozhodl, že se pokusím najít jiný způsob výběru rodičovské planety. Vymyslel jsem, že po kliknutí na tlačítko se zobrazí dialog s ComboBoxem na výběr planety, který před zobrazením načte planety, která aktuálně mohou být "rodičem". V tom se mi rozsvítilo. Vždyť vůbec nebylo potřeba vytvářet nový dialog, který stejně obsahoval jen původní ComboBox.

Řešení

Řešením je aktualizovat ComboBox v události Enter, která se spustí, když se daná kontrolka stane aktivní kontrolkou formuláře. Zde jednoduše získáme validní položky (s pomocí LINQ je to otázkou jednoho řádku) a zobrazíme je uživateli. DropDown se tak aktualizuje vždy, když uživatel bude chtít vybrat novou položku, ať už myší nebo klávesnicí.

Dodatek: Stejně jsem na něco zapomněl. Když uživatel najede myší na ComboBox a scrolluje, může změnit označenou položku bez spuštění události Enter. Na řešení se můžete podívat do zdrojových kódů (události MouseEnter a MouseWheel).

Prázdná položka v DropDownu

Problém

Když planeta neobíhá kolem jiné planety (jako například Slunce), je její vlastnost Parent nastavena na hodnotu null. Do ComboBoxu by tedy bylo vhodné přidat prázdnou položku, která by reprezentovala hodnotu null. Do kolekce s planetami ani do DropDownu hodnotu null přidat nemůžeme, jelikož všechny prázdné pozice v seznamu mají hodnotu "null" a nic by se nám nezměnilo.

Řešení

Řešením je v metodě, která filtruje vhodné planety přidat položku s textem například "<None>" (může být i prázdný řetězec). Při přetypování označené položky v události SelectedValueChanged zpět na původní typ (v tomto případě Planet) nepoužít notaci (Planet) ComboBox.SelectedItem, ale ComboBox.SelectedItem as Planet. Tím nám přetypování stringu na planetu nevyhodí výjimku, ale jednoduše bude null - přesně jak jsme chtěli.

Automatické měřítko, vycentrování a zoom

Problém

Vzdálenosti mezi planetami mohou být velmi odlišné a při pořád stejném měřítku můžeme někdy vidět jedinou planetu přes celou obrazovku a jindy zas celou sluneční soustavu pouze jako tečku. Bylo by dobré měřítko automaticky přizpůsobovat.

Řešení

Winforms nám tuto práci značně zjednoduší svými vestavěnými funkcemi. Nejprve přesuneme bod [0; 0] doprostřed: g.TranslateTransform(.VisibleClipBounds.Width / 2, g.VisibleClipBounds.Height / 2). Pro změnu měřítka stačí jednoduše funkci Graphics.ScaleTransform(float sx, float sy) předat měřítko v ose x a y a při vykreslování se na každý bod automaticky aplikuje transformace. Zbývá nám zjistit správné měřítko. To získáme následovně: zoom * velikost plátna / maximální velikost vykreslovaného obrázku.

Obrázkové fonty

Problém

Původně jsem v aplikaci pro ikonky používal obrázkové fonty jako jsou například Wingdings3. Zjistil jsem ale, že na některých zařízeních není font nainstalován a místo ikonek se zobrazují "obdélníčky".

Řešení

Trochu složitější cestou s použitím systémových knihoven lze sice fonty importovat, mohly by ale vzniknout problémy s vlastnickými právy. Udělat (ještě hezčí :P) ikonky v Inkscapu a nastavit je jako obrázek na pozadí kontrolky bylo otázkou několika minut a ušetřil jsem si tak práci i nesmyslné otázky ohledně copyrightu.

Otevírání souborů pomocí aplikace

Problém

Bylo by dobré, kdyby aplikace podporovala otevírání souborů i pomocí "Otevřít v programu" nebo přetáhnutím souboru na ikonku aplikace.

Řešení

Řešení je velmi jednoduché, nepotřeboval jsem internet a bylo to přesně tak, jak jsem si to představoval. V souboru Program.cs přidáte do static void Main() argument string[] args (tak, jak to bývá v konzolové aplikaci). Získáte tak argumenty, se kterými byla aplikace spuštěna (v tomto případě bude pole obsahovat jednotlivé cesty souborů).

Závěrem

Rád bych se vývoji aplikace věnoval ještě dál, ale nevím jestli se k tomu ještě dostanu. Se vším všudy jsem odhadem strávil vývojem něco pod 100 hodin (= cca 10 000 Kč), takže budu rád za vaši podporu (byť i jen milý komentář že aplikace dobře slouží :-) ). V budoucí verzi by se mohl například objevit progressBar, který ukazuje aktuální stav vykreslování.

I když aplikace svůj původní účel ušetření práce vůbec nesplnila, jsem velmi rád, že jsem se do toho pustil. Nejvzácnější na tom jsou totiž zkušenosti, které jsem díky tomu získal.

Jako vždy, v komentářích se budu těšit na připomínky, případné hlášení bugů a diskuse o jiných možných řešeních, která vás napadnou.


Galerie

Program byl vytvořen v roce 2015.

 

Stáhnout

Staženo 29x (458.72 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Program pro vás napsal David Dostal
Avatar
Autor programuje převážně v C#.Net, poslední dobou se zabývá Unity. Dále se zajímá o webové technologie, zejména HTML5, CSS3 a Javascript. Rád se učí novým věcem.

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (16)

Avatar
Michal Žůrek (misaz):

Ale alternativa asi je, jenže na tuto otázku ti lépe odpoví google než já, protože já s alternativou nemám žádné zkušenosti. Ale ano když se tak dožaduješ odpovědi.

Jako alternativa existuje VML (výhodou je třeba i podpora v outlooku), CGM (standart vyvíjí W3C a jeho druhá verze je v trochu pokročilejším stavu než druhá verze standartu SVG, těžko odhadnout jestli je I lepší) a pak je ještě asi 20 dalších vyjmenovaných na https://en.wikipedia.org/…file_formats

Všechno jsem to vygooglil, takže to neber za mé doporučení nebo že by měly být lepší než SVG a tak. Asi mají taky své mouchy, když třeba k CGM už vzniká další verze a tak.

Zajímavé je, že když jsem googlil SVG alternatives, tak mi většinou vyskočilo HTML Canvas (a knihovny využívající Canvas), což je trochu divná alternativa k vektorovému formátu....

Odpovědět 30.8.2015 23:02
Nesnáším {}, proto se jim vyhýbám.
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na Michal Žůrek (misaz)
David Novák:

Tak jsem se na ně mrknul..

VML:
Development of the format ceased in 1998. VML is implemented in Internet Explorer from version 5 to version 9 and in Microsoft Office 2000. VML is no longer available in Internet Explorer 10. Microsoft expects web sites to transition to SVG.

CGM:
Vydání - 1986
Poslední verze - 1999
The initial CGM implementation was effectively a streamed representation of a sequence of Graphical Kernel System primitive operations. It has been adopted to some extent in the areas of technical illustration and professional design, but has largely been superseded by formats such as SVG and DXF.

W3C vyvíjí pouze webovou implementaci CGM, ne formát samotný - tam vývoj neprobíhá.

Obávám se, že oba jsou staré, málo používané "vykopávky" a tvorba v nich bude ještě mnohem náročnější.. ;)

A HTML Canvas jako alternativa je vskutku divný :D

Každopádně myslím, že neexistuje rozumná alternativa k SVG a také že jsi spíše narazil na složitost vektorové grafiky než na nevhodnost a složitost formátu. Ona je skutečně mnohonásobně složitější než rastrová..

Odpovědět 31.8.2015 0:07
Chyba je mezi klávesnicí a židlí.
Avatar
Martin Mandík:

Promiň za mínus, překlep :)

 
Odpovědět 31.8.2015 9:09
Avatar
David Dostal
Redaktor
Avatar
David Dostal:

V zadání, které jsme měli ve škole byly kružnice, takže elipsy byly zbytečné. Několik lidí se mně na to ale už ptalo, takže přidávám do seznamu možných vylepšení (a implementace elips by vlastně byla otázkou přepsání jedné metody). Teď je trochu vidět, jak jsem se cítil během vývoje. Nejprve byla má představa o programu poměrně jednoduchá, pak jsem ale přicházel na další a další věci, které by vlastně byly potřeba :-). Kdybys chtěl být opravdu přesný, tak by ani Pluto neměl střed dráhy ve Slunci, ale o dost vedle.

 
Odpovědět 31.8.2015 10:13
Avatar
David Dostal
Redaktor
Avatar
David Dostal:

Vím, že název je trochu zavádějící a nepřesný, myslím ale, že pro běžného uživatele je "planeta" mnohem pochopitelnější, než "celestial bod".

 
Odpovědět  +1 31.8.2015 10:15
Avatar
David Dostal
Redaktor
Avatar
David Dostal:

Ještě mně také napadlo, že NumericUpDowny by mohly podporovat i jednoduché matematické výrazy - už několikrát jsem použil kalkulačku, abych spočítal například (1/12) * 3 a podobně.

 
Odpovědět 31.8.2015 10:19
Avatar
Odpovídá na David Dostal
Petr Čech (czubehead):

To je sice pravda, ale běžný uživatel nepočítá oběžné dráhy %P

Odpovědět 31.8.2015 11:09
Why so serious? -Joker
Avatar
David Dostal
Redaktor
Avatar
 
Odpovědět  +1 31.8.2015 11:12
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na David Dostal
David Novák:

Mohl bys to pojmenovat: Oběžné dráhy hvězdných těles - to je srozumitelné a přesné, ne? ;)

Odpovědět  +1 31.8.2015 11:30
Chyba je mezi klávesnicí a židlí.
Avatar
David Dostal
Redaktor
Avatar
 
Odpovědět 31.8.2015 12:00
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 26. Zobrazit vše