Java - vykreslování (ideálně Swing)

Java - vykreslování (ideálně Swing)
« kdy: 01. 12. 2014, 16:56:34 »
Zdravim,
řešim následující problém: mám po mapě na mapě rozmístěné uzly, které sou různě propojeny cestami. Mezi těmito uzly cestují objekty. Uzlů je řádově tisíce a pohybujících se objektů řádově stovky. Uzly a cesty jsou neměnné (resp. jejich polohy). Přemýšlím nad nejlepším způsobem vykreslení, prozatím mám impementovanou takovou brute-force metodu, překreslovací cyklus vypadá takhle:

for(Cesta c: Cesty)
   c.vykresli

for(Uzel u: Uzly)
   u.vykresli

for(Pohyb p: PohybObjekty)
   p.vykresli


Přijde mi ale zbytečný překreslovat znova celou mapu, když se mění jen těch několik pohybujích se objektů. Napadlo mě si mapu uzlů vykreslit do BufferedImage a výslednej překreslovací cyklus by pak vypadal asi takhle:

vykresli BufferedImage na JPanel
for(Pohyb p: PohybObjekty)
   p.vykresli


Chtěl bych se zeptat na vaše návrhy řešený, případně alespoň směr, kterym bych se měl vydat. Napadla mě spritová grafika, ale netušim jestli má Java nějakou implementaci.


dsfasdfasdf

Re:Java - vykreslování (ideálně Swing)
« Odpověď #1 kdy: 01. 12. 2014, 17:08:37 »
no swing uz jsem zase zapomel.

neda se nastavit nejaky clip rectangle ve kterem se bude modifigovat vetsi obrazek a pri prekreslovani
modifikovat jen male obdelnicky.

tdvorak

Re:Java - vykreslování (ideálně Swing)
« Odpověď #2 kdy: 02. 12. 2014, 06:51:11 »
A opravdu to překreslování zdržuje? Proč ti to přijde zbytečný? Máš to podložený měřením, nebo se ti trhá a viditelně překresluje obrázek? Abys zbytečně neřešil optimalizaci překreslování, když je možná výkonový problém jinde.

Běžná optimalizace vykreslování je použít double buffering, do jednoho obrázku kreslím a jeden zobrazuji. Když mám vykresleno, jen obrázky přehodím. Pozor na to, že většina věcí ze swingu není thread-safe.

Pár tipů: https://stackoverflow.com/questions/4430356/java-how-to-do-double-buffering-in-swing

jiri.tusla

Re:Java - vykreslování (ideálně Swing)
« Odpověď #3 kdy: 02. 12. 2014, 11:46:48 »
To si říká o použití JavaFX, který je docela HW akcelerovaný
https://docs.oracle.com/javafx/2/get_started/animation.htm

Když budeš chtít zůstat u Swingu, tak si skutečně nakresli mapu uzlů a cest do off-screen image a hlavně jej udělej kompatibilní
s grafikou obrazovky.
http://www.java2s.com/Code/Java/2D-Graphics-GUI/Createbufferedimagesthatarecompatiblewiththescreen.htm

Potom si úplně stejně a také kompatibilně připrav všechny pohybující se objekty.
A nakonec to pomocí Graphics.drawImage narazítkuj třeba na ten JPanel.
Jestli je ta mapa opravdu veliká a máš jej v JScrollPane, tak při kreslení kontroluj viditelnou část toho JPanelu.
Tím ušetříš také spoustu času. A to je tak nějak spritová grafika...

A ještě se pokusím rozvinout myšlenku tdvoraka. Kromě tipů s double-bufferingem obrázků
se zamysli i nad double bufferingem dat.
Tím myslím, že si připravíš sadu dat, jako immutable ji pošleš do vykreslování a dalšího "datového pole" si počítáš následující iteraci.

bezprízorný

Re:Java - vykreslování (ideálně Swing)
« Odpověď #4 kdy: 02. 12. 2014, 13:11:41 »
A opravdu to překreslování zdržuje? Proč ti to přijde zbytečný? Máš to podložený měřením, nebo se ti trhá a viditelně překresluje obrázek? Abys zbytečně neřešil optimalizaci překreslování, když je možná výkonový problém jinde.

Pravdu má kolega. Aby to nebolo Javou ;)


vyvojar

Re:Java - vykreslování (ideálně Swing)
« Odpověď #5 kdy: 02. 12. 2014, 17:41:24 »
To nemůžeš vykreslovat všechno no.
Musíš vykreslit jenom to, co má být zrovna vidět.
Třeba řekněme, že máš mapu, která reprezentuje 100km čtverečních.
Určíš si, že 1km = 100px.
Máš třeba nějaký ten JPanel, který má rozměry 100x100px, tzn. vždycky zobrazuje 1km čtvereční té mapy.
Aby si se v té mapě mohl pohybovat, tak musíš mít taky scrollbary nebo něco podobného.
Budeš mít třeba vertikální scrollbar o délce 100px.
Vezmeš si celkovou délku té mapy (1km = 100px, čili 10x100px, protože mapa má 100km čtverečních, což je 10kmx10km, že.).
Lze teda scrollovat vertikálně i horizontálně o 1000px.
Protože vždycky zobrazuješ 100px, tak si uděláš 100/1000, což je 0.1. Tzn. i ten ovladač scrollbaru musí zabrat 0.1 jeho maximální délky, aby to vizuálně reflektovalo, kolik z celkového prostoru se zobrazuje.
Akorát potom ve chvíli, kdy máš tu mapu třeba obrovskou a bude ti vycházet, že má ovladač scrollbaru
zabrat třeba 0.1px, tak to samozřejmě nejde a ten ovladač bude mít vždycky nějakou minimální velikost.
Proto jsou tam i ty šipečky dole, aby si se mohl pohybovat i jakoby s menším rozlišením, než ti umožňuje
ten ovladač scrollbaru. Třeba když posun scrollbaru o 1 pixel by měl znamenat posun mapy o 1000px, ale
tvůj viewport je jen 100px, tak uživatel použije ty šipečky. Kliknutí na šipečku pak posune mapu, ale nezmění pozici scrollbaru.
Vždycky si to prostě převádíš jakoby do intervalu 0-1. Zobrazuju 50% mapy, tak ovladač scrollbaru
zabere 50% jeho celkové velikosti.
Pak si při každým překreslení zeptáš scrollbaru na jaký je pozici. Třeba 0.2-0.3, tzn. by jsi měl zobrazovat 0.2-0.3 té mapy. Celková velikost mapy je 1000px, tzn. 0.2x1000 až 0.3x1000.
No a to tam vykreslíš do toho okna 100x100.
Musíš si ty data kde je jaký uzel ukládat nějak chytře, aby si rychle zjistil, co je v jaké oblasti.
A taky musíš vykreslovat věci, co jsou viditelné jen částečně. To se většinou dělá tak, že pak kreslíš jakoby
do záporných souřadnich toho grafického kontextu, čímž se to ořízně přesně tam kde má.
Žádné jednoduché řešení na tyhle věci není, prostě musíš si to naprogramovat celé fakt sám, pokud už musíš
optimalizovat vykreslování na téhle úrovni.
Dělají to třeba textové editory, aby nevykreslovali celý text, tak vždycky podle výšky řádku zjistí, co je vidět
a vykreslují jen to. 

Sorry jsem ti to vysvětlil asi moc složitě, ale tu myšlenku pochytíš. Je to vlastně hrozně jednoduchý.