首先,请接受我的道歉,如果这个问题是基本的,我主要了解C#,但我被迫在这个特定项目中使用Java!
我正在尝试使用GUI来显示基于机器人传感器数据的occupancy grid。占用网格将非常大,可能高达1500x1500网格方块,代表每网格单元约10平方厘米的真实世界区域。
每个网格方块只会存储一个Enumerable状态,例如:
我只想找到将其渲染为网格的最佳方法,使用不同的颜色方块来描绘不同的网格单元状态。
我已经实现了一个简单的基本算法来绘制正方形和网格线,但是它在较大的占用网格上执行非常糟糕。当收集新的传感器数据时,类中的其他代码每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();
}
请帮忙!提前谢谢。
答案 0 :(得分:2)
ROS - 来自willowgarage的机器人操作系统在java中有一个占用网格实现: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)
绘制时需要尊重剪辑矩形(假设您的网格位于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;
}
}
答案 2 :(得分:1)
甚至渲染2,250,000个细胞的子集也不是一项微不足道的工作。您需要的两种模式是Model-View-Controller,讨论here和flyweight,JTable
可能会有用。
答案 3 :(得分: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;
}
}
通过该课程,你可以做到:
new BMP().saveBMP(fieName, myOccupancyMatrix);
生成整数矩阵(myOccupancyMatrix
)很简单。避免Switch
语句的一个简单技巧是将颜色值分配给占用枚举:
public enum Occupancy {
Unexplored(0x333333), Empty(0xFFFFFF), Occupied(0x000000), Robot(0xFF0000);
}
一旦你保存了磁盘,BMP就可以显示在applet中并轻松扩展:
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);
}
}
希望这有帮助!