2012-02-12 5 views
1

この質問が基本的であれば、私は主にC#の知識を持っていますが、この特定のプロジェクトでJavaを使用することを余儀なくされています。Occupancy GridをJava Swingで効率的にレンダリング

ロボットセンサーデータに基づいてoccupancy gridを表示するGUIを実装しようとしています。占有グリッドはかなり大きく、恐らく最大1500×1500のグリッド・スクエアが、グリッド・セルあたり約10cm2の実世界面積を表します。

単に例えば、可算状態を保存します。各グリッドの正方形:

  • 不明
  • 空い
  • 占領
  • ロボットが

私は単に最良を見つけるしたいと思います異なる色の四角形を使用して異なるグリッドセルの状態を表現する、グリッドとしてレンダリングする方法

私は、正方形とグリッド線を描画するための素朴で基本的なアルゴリズムを実装しましたが、大きな占有グリッドでは非常に悪い結果を出します。クラス内の他のコードは、新しいセンサーデータが収集されるたびに0.5秒ごとにウィンドウを再描画しますが、パフォーマンスが非常に悪い理由は、毎年すべてのセルをレンダリングしているということです。観察可能なクラスで各セルをラップすると、これらのセルを選択的にレンダリングする簡単な方法はありますか?

私の現在の実装:

@Override 
public void paint(Graphics g) { 
    Graphics2D g2 = (Graphics2D) g; 

    int width = getSize().width; 
    int height = getSize().height; 

    int rowHeight = height/(rows); 
    int colWidth = width/(columns); 

    //Draw Squares 
    for (int row = 0; row < rows; row++) { 
     for (int col = 0; col < columns; col++) { 
      switch (this.grid[row][col]) { 
       case Unexplored: 
        g.setColor(Color.LIGHT_GRAY); 
        break; 
       case Empty: 
        g.setColor(Color.WHITE); 
        break; 
       case Occupied: 
        g.setColor(Color.BLACK); 
        break; 
       case Robot: 
        g.setColor(Color.RED); 
        break; 
      } 

      g.drawRect(col * colWidth, height - ((row + 1) * rowHeight), colWidth,  rowHeight); 
      g.fillRect(col * colWidth, height - ((row + 1) * rowHeight), colWidth,  rowHeight); 
     } 
    } 

    int k; 
    if (outline) { 
     g.setColor(Color.black); 
     for (k = 0; k < rows; k++) { 
      g.drawLine(0, k * rowHeight, width, k * rowHeight); 
     } 

     for (k = 0; k < columns; k++) { 
      g.drawLine(k * colWidth, 0, k * colWidth, height); 
     } 
    } 

} 


private void setRefresh() { 
    Action updateUI = new AbstractAction() { 
     boolean shouldDraw = false; 

     public void actionPerformed(ActionEvent e) { 
      repaint(); 
     } 
    }; 

    new Timer(updateRate, updateUI).start(); 
} 

助けてください!前もって感謝します。

+1

それは、この場合に特に有用ではないのですが、「Swingのプログラムは、オーバーライドする必要があります'paint()'をオーバーライドするのではなく 'paintComponent()'を使用します。 " - [AWTとスイングのペイント:ペイントメソッド](http://java.sun.com/products/jfc/tsc/articles/painting/index。 html#コールバック)。 – trashgod

+0

@Andrew Thompsonこの声明はJava開発者を尊敬することを意図したものではなく、私の知識の不足について説明するためのものです。私はそれが特に「不自然な」コメントではないと思います。結局のところ、私はあなたが何か解決策を研究していなかったという印象を得るのが好きではありません。 –

答えて

1

長方形の作成が遅すぎる可能性があります。代わりに、ビットマップイメージを作成しないでください。各ピクセルはグリッドのセルになっていますので、必要なサイズにスケーリングできます。

次のクラスは、整数の行列をとり、それをビットマップファイルに保存します。そのクラスに

import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 

public class BMP { 
    private final static int BMP_CODE = 19778; 

    byte [] bytes; 

    public void saveBMP(String filename, int [][] rgbValues){ 
     try { 
      FileOutputStream fos = new FileOutputStream(new File(filename)); 

      bytes = new byte[54 + 3*rgbValues.length*rgbValues[0].length + getPadding(rgbValues[0].length)*rgbValues.length]; 

      saveFileHeader(); 
      saveInfoHeader(rgbValues.length, rgbValues[0].length); 
      saveRgbQuad(); 
      saveBitmapData(rgbValues); 

      fos.write(bytes); 

      fos.close(); 

     } catch (FileNotFoundException e) { 

     } catch (IOException e) { 

     } 

    } 

    private void saveFileHeader() { 
     byte[]a=intToByteCouple(BMP_CODE); 
     bytes[0]=a[1]; 
     bytes[1]=a[0]; 

     a=intToFourBytes(bytes.length); 
     bytes[5]=a[0]; 
     bytes[4]=a[1]; 
     bytes[3]=a[2]; 
     bytes[2]=a[3]; 

     //data offset 
     bytes[10]=54; 
    } 

    private void saveInfoHeader(int height, int width) { 
     bytes[14]=40; 

     byte[]a=intToFourBytes(width); 
     bytes[22]=a[3]; 
     bytes[23]=a[2]; 
     bytes[24]=a[1]; 
     bytes[25]=a[0]; 

     a=intToFourBytes(height); 
     bytes[18]=a[3]; 
     bytes[19]=a[2]; 
     bytes[20]=a[1]; 
     bytes[21]=a[0]; 

     bytes[26]=1; 

     bytes[28]=24; 
    } 

    private void saveRgbQuad() { 

    } 

    private void saveBitmapData(int[][]rgbValues) { 
     int i; 

     for(i=0;i<rgbValues.length;i++){ 
      writeLine(i, rgbValues); 
     } 

    } 

    private void writeLine(int row, int [][] rgbValues) { 
     final int offset=54; 
     final int rowLength=rgbValues[row].length; 
     final int padding = getPadding(rgbValues[0].length); 
     int i; 

     for(i=0;i<rowLength;i++){ 
      int rgb=rgbValues[row][i]; 
      int temp=offset + 3*(i+rowLength*row) + row*padding; 

      bytes[temp] = (byte) (rgb>>16); 
      bytes[temp +1] = (byte) (rgb>>8); 
      bytes[temp +2] = (byte) rgb; 
     } 
     i--; 
     int temp=offset + 3*(i+rowLength*row) + row*padding+3; 

     for(int j=0;j<padding;j++) 
      bytes[temp +j]=0; 

    } 

    private byte[] intToByteCouple(int x){ 
     byte [] array = new byte[2]; 

     array[1]=(byte) x; 
     array[0]=(byte) (x>>8); 

     return array; 
    } 

    private byte[] intToFourBytes(int x){ 
     byte [] array = new byte[4]; 

     array[3]=(byte) x; 
     array[2]=(byte) (x>>8); 
     array[1]=(byte) (x>>16); 
     array[0]=(byte) (x>>24); 

     return array; 
    } 

    private int getPadding(int rowLength){ 

     int padding = (3*rowLength)%4; 
     if(padding!=0) 
      padding=4-padding; 


     return padding; 
    } 

} 

、あなたは、単に行うことができます:整数の行列(myOccupancyMatrix)を生成

new BMP().saveBMP(fieName, myOccupancyMatrix); 

は簡単です。Switch文を回避するための簡単なトリックは、あなたの稼働列挙にカラー値を代入されます。あなたはそれがディスクを行う保存すると

public enum Occupancy { 
     Unexplored(0x333333), Empty(0xFFFFFF), Occupied(0x000000), Robot(0xFF0000); 
} 

は、BMPは、アプレットに示すことができ、容易にスケール:

public class Form1 extends JApplet { 
    public void paint(Graphics g) { 
     Image i = ImageIO.read(new URL(getCodeBase(), "fileName.bmp")); 
     g.drawImage(i,0,0,WIDTH,HEIGHT,Color.White,null); 
    } 
} 

これが役立つことを願っています!

+1

イメージをディスクに保存して、直後にイメージをロードするのはなぜでしょうか。 BufferedImageとそのラスタデータをusrに置き換え、ディスクの使用を避けます。 – stryba

+0

そうでない場合は、変更のみを描画したい場合はイメージバッファーが必要です – stryba

+0

strybaと同意しますが、BMPを保存する場合でも、自分でエンコードする代わりにImageIOを使用する必要があります。 – jackrabbit

2

ROS - ウィロウガラージからロボットのオペレーティング・システムはJavaで実装occupancygridがありますhttp://code.google.com/p/rosjava/source/browse/android_honeycomb_mr2/src/org/ros/android/views/map/OccupancyGrid.java?spec=svn.android.88c9f4af5d62b5115bfee9e4719472c4f6898665&repo=android&name=88c9f4af5d&r=88c9f4af5d62b5115bfee9e4719472c4f6898665

あなたはそれを使用するか、またはそれからアイデアを得ることができます。

1

2,250,000個のセルのサブセットでさえレンダリングすることは簡単な作業ではありません。あなたが必要とするパターンはModel-View-Controllerhereflyweightです。JTableが役に立ちます。

2

ペイント時にクリップの矩形を尊重する必要があります(グリッドがJScrollPaneにあると仮定して)、JComponent#repaint(Rectangle)を適切に使用します。 (それを遅延セルの値をロードに関連したものの、それはまた、クリップの境界の絵を持っている)

は、このサンプル・プログラムを参照してください:

import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Random; 

import javax.swing.*; 


public class TilePainter extends JPanel implements Scrollable { 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       JFrame frame = new JFrame("Tiles"); 
       frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
       frame.getContentPane().add(new JScrollPane(new TilePainter())); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    private final int TILE_SIZE = 50; 
    private final int TILE_COUNT = 1000; 
    private final int visibleTiles = 10; 
    private final boolean[][] loaded; 
    private final boolean[][] loading; 
    private final Random random; 

    public TilePainter() { 
     setPreferredSize(new Dimension(
       TILE_SIZE * TILE_COUNT, TILE_SIZE * TILE_COUNT)); 
     loaded = new boolean[TILE_COUNT][TILE_COUNT]; 
     loading = new boolean[TILE_COUNT][TILE_COUNT]; 
     random = new Random(); 
    } 

    public boolean getTile(final int x, final int y) { 
     boolean canPaint = loaded[x][y]; 
     if(!canPaint && !loading[x][y]) { 
      loading[x][y] = true; 
      Timer timer = new Timer(random.nextInt(500), 
        new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        loaded[x][y] = true; 
        repaint(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE); 
       } 
      }); 
      timer.setRepeats(false); 
      timer.start(); 
     } 
     return canPaint; 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 

     Rectangle clip = g.getClipBounds(); 
     int startX = clip.x - (clip.x % TILE_SIZE); 
     int startY = clip.y - (clip.y % TILE_SIZE); 
     for(int x = startX; x < clip.x + clip.width; x += TILE_SIZE) { 
      for(int y = startY; y < clip.y + clip.height; y += TILE_SIZE) { 
       if(getTile(x/TILE_SIZE, y/TILE_SIZE)) { 
        g.setColor(Color.GREEN); 
       } 
       else { 
        g.setColor(Color.RED); 
       } 
       g.fillRect(x, y, TILE_SIZE, TILE_SIZE); 
      } 
     } 
    } 

    @Override 
    public Dimension getPreferredScrollableViewportSize() { 
     return new Dimension(visibleTiles * TILE_SIZE, visibleTiles * TILE_SIZE); 
    } 

    @Override 
    public int getScrollableBlockIncrement(
      Rectangle visibleRect, int orientation, int direction) { 
     return TILE_SIZE * Math.max(1, visibleTiles - 1); 
    } 

    @Override 
    public boolean getScrollableTracksViewportHeight() { 
     return false; 
    } 

    @Override 
    public boolean getScrollableTracksViewportWidth() { 
     return false; 
    } 

    @Override 
    public int getScrollableUnitIncrement(
      Rectangle visibleRect, int orientation, int direction) { 
     return TILE_SIZE; 
    } 
}