[JAVA][TABLICE] Generowanie planszy

0

Próbuje wygenerować planszę z dużą ilością pól(80x80), tak, aby była możliwość zmian koloru poszczególnych pół planszy.

Zrealizowałem generowanie takiej planszy za pomocą tablicy dwuwymiarowej oraz dwóch pętli - niestety jest to bardzo powolne - istnieje jakiś alternatywny sposób na generowanie takich rzeczy? Na forum znalazłem jedynie generowanie szachownic, również w ten sam sposób. Załączam część kodu odpowiedzialną za generowanie:


 package msi;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
public class PanelRysunku extends JPanel
{
    static public void setWymiary(int x,int y, int rozm)
    {
      POZIOM=x;
      PION=y;
      ROZMIAR=rozm;
      kolorWypelnienia = new Color(0x00,0xFF,0x00);

    }
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;

        double lewyX = 10;
        double gornyY = 10;
        double szerokosc = 10;
        double wysokosc = 10;

        for(int i=0;i<POZIOM;i++)
        {
            for(int j=0;j<PION;j++)
            {
                wypelnieniePola = new Rectangle2D[POZIOM][PION];
                wypelnieniePola[i][j] = new Rectangle2D.Double(ROZMIAR*i, ROZMIAR*j+12, ROZMIAR, ROZMIAR);
                g2.setPaint(kolorWypelnienia);
                g2.fill(wypelnieniePola[i][j]);
                g2.draw(wypelnieniePola[i][j]);

                ramkiPola = new Rectangle2D[POZIOM][PION];
                ramkiPola[i][j] = new Rectangle2D.Double(ROZMIAR*i, ROZMIAR*j+12, ROZMIAR, ROZMIAR);
                g2.setPaint(new Color(0xFF,0xFF,0xFF));
                g2.draw(ramkiPola[i][j]);
            }
        }

        
    }
    static public void zmienkolor(int r, int g, int b)
    {
        kolorWypelnienia = new Color(r,g,b);
    }

    public static Color kolorWypelnienia;
    Rectangle2D[][] ramkiPola;
    Rectangle2D[][] wypelnieniePola;
    static int POZIOM;
    static int PION;
    static int ROZMIAR;
}
0

Tworzysz klasę Pole;

public class Pole implements Comparable<Pole>{
   
   Integer x;
   Integer y;
   Color color;
 
   public int compareTo(Pole p){
       int xx = x.compareTo(p.x);
       if(xx ==0){ // w tym samym rzędzie porównujemy y
           return y.compareTo(p.y)
       }
       return xx;
   }

}

Tworzysz sobie listę pól (w pętli), sortujesz i już można rysować w ramach jednej pętli, bo parametry będziesz pobierał z obiektu, a nie z podwójnej pętli.

0

jest wolne, bo tworzysz masę obiektów w paintComponent, które może być wywoływane dość często. Nie możesz utworzyć tych obiektów raz potem je tylko odmalowywać?
W każdej iteracji tworzysz od nowa cała tablicę, a korzystasz tylko i wyłącznie z jednego elementu. Którego zresztą potem już nie potrzebujesz.

Ostatecznie masz: 80x80 alokacji dwóch tablic o rozmiarach 80x80, 28080 alokacji pojedynczego obiektu.

0

Zrobiłem dużo szybszy kod dzięki tylko jednokrotnemu utworzeniu obiektów, Również wrzuciłem rysowanie w jedną pętle - niestety nie odczułem zwiększenia wydajności - nadal jest ok 80-90ms na repaint();


package msi;

/**
 * @author Madierfakier
 */
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;
public class PanelRysunku extends JPanel
{
    public PanelRysunku(int x, int y, int rozm)
    {
      addMouseListener(new UchwytMyszki());
      POZIOM=x;
      PION=y;
      ROZMIAR=rozm;
      ramkiPola = new Rectangle2D[POZIOM][PION];
      wypelnieniePola = new Rectangle2D[POZIOM][PION];
      tablicaPol = new pole[POZIOM][PION];
      listaPol = new pole[POZIOM*PION];
      {
          ind=0;         
         for(int i=0;i<POZIOM;i++)
        {
            for(int j=0;j<PION;j++)
            {
                tablicaPol[i][j] = new pole(ROZMIAR,i,j);
                listaPol[ind]= tablicaPol[i][j];
                ind++;
            }
         }
      }
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        long start=System.currentTimeMillis();

         for(int j=0;j<listaPol.length;j++)
         {
            g2.setPaint(listaPol[j].getColor());
            g2.fill(listaPol[j].getWypelnienie());
            g2.draw(listaPol[j].getWypelnienie());
            g2.setPaint(new Color(0xFF,0xFF,0xFF));
            g2.draw(listaPol[j].getRamki());
         }

        System.out.print("\n\nCzas wykonania zadania [ms] = " + (System.currentTimeMillis()-start));
   }

    public void ustawSciane(int x, int y)
    {
        tablicaPol[x][y].setColor(new Color(255,0,0));
        repaint();
    }
    public void ustawXY(int x, int y)
    {
        System.out.println(" x="+x+"y= "+y);
    }

    private ArrayList listakwadratow;
    static private pole[] listaPol;
    static private pole[][] tablicaPol;
    public static Color[][] kolorWypelnienia;
    public Rectangle2D[][] ramkiPola;
    public Rectangle2D[][] wypelnieniePola;
    static int ind;
    static int POZIOM;
    static int PION;
    static int ROZMIAR;


    private class UchwytMyszki extends MouseAdapter
    {
      public void mouseClicked(MouseEvent zdarzenie)
      {
          int x = zdarzenie.getX();
          int y = zdarzenie.getY();
          System.out.println("x="+x+" y="+y);
          ustawSciane(x/5,(y-12)/5);
      }
    }
}


niestety po dodaniu obsługi myszki, okazuje się, ze to niewystarczające czasy, i teraz pytanie: Czy mogę rozważać zrobienie każdego pola w osobnym Jpanelu, odświeżanie pojedyńczych elementów będzie na pewno szybsze, ale czy będzie działało np mouseDragged pomiędzy poszczególnymi panelami?

0

Hm... a jakby tak potraktować planszę jako stałą? Stwórz sobie w jeden BufferedImage gdy po raz pierwszy potrzebujesz planszy i zapisz w pamięci rezultat. Następnie jeżeli będziesz chciał odrysować planszę to skorzystasz ze starego już wygenerowanego obrazu planszy. Chyba brzmi sensownie?

if(plansza ==null){
   plansza = new BufferedImage();
   Graphics2D g2 = (Graphics2D)plansza.getGraphics();
   for(int j=0;j<listaPol.length;j++)
         {
            g2.setPaint(listaPol[j].getColor());
            g2.fill(listaPol[j].getWypelnienie());
            g2.draw(listaPol[j].getWypelnienie());
            g2.setPaint(new Color(0xFF,0xFF,0xFF));
            g2.draw(listaPol[j].getRamki());
    }
}
g.drawImage(plansza);

Czy coś w ten deseń bo nie mam IDE pod ręką.

0

Jeżeli chcesz mieć szybko, to zapomnij o używaniu do odmalowywania liczb double, a już tym bardziej Double. Tak samo zapominasz o operacjach dzielenia. Zastępujesz je przesunięciami bitowymi. Na przykład x / 100, to to samo co x / (64 + 32 + 4), a to { x >> 6; x >> 5; x >> 2; }

0

Po podmianie metod Rectangle2d.double() na Rectangle(), która przyjmuje inty nie odczułem poprawy - prawdopodobnie dlatego, że do rysowania przekazywany jest obiekt a nie liczba double. A co do dzielenia, to ten wzór co podałeś to jest raczej dzielenie x/8192 czyli x/(64324)

EDIT:

Po użyciu BufferedImage śmiga jak ta lala (0-2ms na repainta)- zamieszczam kod jakby co.


for(int i=0;i<POZIOM;i++)
        {
            for(int j=0;j<PION;j++)
            {
                tablicaPol[i][j] = new pole(ROZMIAR,i,j);
                listaPol[ind]= tablicaPol[i][j];             
                Graphics2D g2d = plansza.createGraphics();
                g2d.setColor(tablicaPol[i][j].getColor());
                g2d.fill(tablicaPol[i][j].getWypelnienie());
                //g2d.dispose();
                ind++;
            }
         }                
    }

 
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);       
        long start=System.currentTimeMillis();
        g.drawImage(plansza,0,16,null);
        System.out.print("\n\nCzas wykonania zadania [ms] = " + (System.currentTimeMillis()-start));
   }
0
madierfakier napisał(a)

A co do dzielenia, to ten wzór co podałeś to jest raczej dzielenie x/8192 czyli x/(64324)

Zgadza się. Tam w klamrze jest błąd. Chodzi o użycie wzorów eliminujących dzielenie na korzyść mnożenia oraz zamiany mnożeń na przesunięcia bitowe. Obliczenia są nieco bardziej skomplikowane i mniej jasne, ale korzyścią duże przyspieszenie. Szczególnie jeżeli są to obliczenia w zagnieżdżonych pętlach.
Rzuć okiem na to jako przykłady: http://panoramix.ift.uni.wroc.pl/~bosy/cpp_opt.htm

0

mechanika planszy jest mniej więcej ukończona - zabrałem się za algorytmy SI i tu napotkałem na problem: dla przeszukiwania wgłąb przy małej planszy (50x50)jest ok, niestety przy planszy 100x200 wyrzuca mi błąd

 Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError

at sun.java2d.pipe.LoopPipe.fillParallelogram(LoopPipe.java:357)
        at sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(PixelToParallelogramConverter.java:298)
        at sun.java2d.pipe.PixelToParallelogramConverter.fill(PixelToParallelogramConverter.java:141)

        at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:142)
        at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2364)
        at msi.PanelRysunku.ustawOdwiedzone(PanelRysunku.java:133)
        at msi.PanelRysunku.wdol(PanelRysunku.java:242)
        at msi.algorytmy.wglab(algorytmy.java:37)

Jak siętego pozbyć? Zamieszczam fragment kodu

private void wglab()
    {
        if(owner.sprawdzkoniec())
        {
            System.out.print("znalezione");
            
        }
        try
        {
        //Thread.sleep(5);
        }
        catch(Exception e)
        {
            System.out.print(e);
        }
        if(owner.wdol())
            wglab();
        if(owner.wlewo())
            wglab();
        if(owner.wgore())
            wglab();
        if(owner.wprawo())
            wglab();
        
            owner.cofnijruch();
    }

1 użytkowników online, w tym zalogowanych: 0, gości: 1