2012-01-04 54 views
1

我有一个数据集,我在下面的代码中定义的矩形空间内绘图。数据集的长度几乎总是不同于需要绘制数据集的屏幕空间的像素宽度。缩放数据以适合像素空间

我的问题是,我需要一种有效的方式将数据集的图形分布在整个绘图空间的宽度上。

正如您从下面发布的代码中看到的,数据正在(不正确)绘制在矩形宽度的子集中。如果编译代码并手动重新调整框架大小几次,则会看到数据图的宽度相对于蓝色内部矩形的宽度随着框架宽度的变化而变化。为了更清楚地说明这一点,下面的代码还绘制了两条绿色垂直线,一条在数据集图形的开头和另一端。如果代码正常工作,垂直绿线应该只位于蓝色内矩形的开始和结尾,并且它们之间的尖峰应该与内部蓝色矩形的起点和终点之间的适当的百分比距离(30 %和70%,分别对应于指数120/400和280/400)。但是,正如您所看到的那样,第二条绿色垂直线总是不合适,而第一条绿色线似乎始终处于其正确位置,并且数据卡的位置与(错位)绿色垂直标记相对,而不是相对于内部蓝色矩形的左右边缘正确放置,它应与(正确放置的)绿色垂直线重叠。

任何人都可以告诉我如何解决下面的代码,以便它平均分配宽度的内部蓝色矩形的数据图的宽度?

我认为问题出在hstep变量中,并且我在下面的DataPanel.java代码中标记了我认为问题代码与评论中的所有CAPS一致的地方。

的代码在以下两个文件:

DataGUI.java

import java.awt.*; 
import javax.swing.*; 

class DataGUI{ 
DataGUI() { 
    JFrame jfrm = new JFrame("X-Y Plot");// Create a new JFrame container. 
    jfrm.getContentPane().setLayout(new GridLayout(1,1));// Specify FlowLayout for the layout manager. 
    int frameHeight = 500; 
    int frameWidth = 767; 
    jfrm.setSize(frameWidth, frameHeight);// Give the frame an initial size. 
    jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// Terminate program when user closes application. 
    int len = 400; 
    double[] data = new double[len]; 
    for(int d=0;d<data.length;d++){ 
     if(d==120||d==280){data[d]=45;} 
     else{data[0]=0;} 
    } 
    DataPanel myDataPanel = new DataPanel(data); 
    jfrm.add(myDataPanel); 
    jfrm.setVisible(true);// Display the frame. 
} 

public static void main(String args[]) { 
    // Create frame on event dispatching thread. 
    SwingUtilities.invokeLater(new Runnable() {public void run(){new DataGUI();}}); 
} 
} 

DataPanel.java

import java.awt.*; 
import javax.swing.*; 
import java.text.NumberFormat; 

class DataPanel extends JPanel { 
Insets ins; // holds the panel's insets 
int len = 400;// for testing only. will throw error if you do not link this to an array length. 
double[] plotData; 
double xScale; 

DataPanel(double[] myData) { 
    setOpaque(true);// Ensure that panel is opaque. 
    plotData = myData; 
} 

protected void paintComponent(Graphics g){// Override paintComponent() method. 
    super.paintComponent(g);// Always call superclass method first. 
    int height = getHeight();// Get height of component. 
    int width = getWidth();// Get width of component. 
    ins = getInsets();// Get the insets. 
    // Get dimensions of text 
    Graphics2D g2d = (Graphics2D)g; 
    FontMetrics fontMetrics = g2d.getFontMetrics(); 
    String xString = ("x-axis label"); 
    int xStrWidth = fontMetrics.stringWidth(xString); 
    int xStrHeight = fontMetrics.getHeight(); 
    String yString = "y-axis label"; 
    int yStrWidth = fontMetrics.stringWidth(yString); 
    int yStrHeight = fontMetrics.getHeight(); 
    String titleString ="Title of Graphic"; 
    int titleStrWidth = fontMetrics.stringWidth(titleString); 
    int titleStrHeight = fontMetrics.getHeight(); 
    //get margins 
    int leftMargin = ins.left; 
    //set parameters for inner rectangle 
    int hPad=10; 
    int vPad = 6; 
    int numYticks = 10; 
    int testLeftStartPlotWindow = ins.left+5+(3*yStrHeight); 
    int testInnerWidth = width-testLeftStartPlotWindow-ins.right-hPad; 
    int remainder = testInnerWidth%numYticks; 
    int leftStartPlotWindow = testLeftStartPlotWindow-remainder; 
    System.out.println("remainder is: "+remainder); 
    int blueWidth = testInnerWidth-remainder; 
    int endIndex = leftStartPlotWindow+blueWidth; 
    int blueTop = ins.bottom+(vPad/2)+titleStrHeight; 
    int bottomPad = (3*xStrHeight)-vPad; 
    int blueHeight = height-bottomPad-blueTop; 
    int blueBottom = blueHeight+blueTop; 

    g.setColor(Color.red); 
    int redWidth = width-leftMargin-1; 
    //plot outer rectangle 
    g.drawRect(leftMargin, ins.bottom, redWidth, height-ins.bottom-1); 
    System.out.println("blueWidth is: "+blueWidth); 
    // fill inner rectangle 
    g.setColor(Color.white); 
    g.fillRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight); 

    //write top label 
    g.setColor(Color.black); 
    g.drawString(titleString, (width/2)-(titleStrWidth/2), titleStrHeight); 

    //write x-axis label 
    g.setColor(Color.red); 
    g.drawString(xString, (width/2)-(xStrWidth/2), height-ins.bottom-vPad); 
    g2d.rotate(Math.toRadians(-90), 0, 0);//rotate text 90 degrees counter-clockwise 
    //write y-axis label 
    g.drawString(yString, -(height/2)-(yStrWidth/2), yStrHeight); 
    g2d.rotate(Math.toRadians(+90), 0, 0);//rotate text 90 degrees clockwise 
    // draw tick marks and data rectangles on x-axis 
    NumberFormat formatter = NumberFormat.getPercentInstance(); 
    double k = blueWidth/numYticks; 
    double iteration = 0; 
    for(int h=0;h<=numYticks;h++){ 
     int xval = (int)(h*k); 
     //draw tick marks 
     g.setColor(Color.red); 
     g.drawLine(leftStartPlotWindow+xval, blueBottom+2, leftStartPlotWindow+xval, blueBottom+(xStrHeight/2)); 
     g.drawString(formatter.format(iteration/10),leftStartPlotWindow+xval-(fontMetrics.stringWidth(Double.toString(iteration/10))/2),blueBottom+(xStrHeight/2)+13); 
     iteration+=1; 
    } 
    //plot data 
// I THINK THE PROBLEM IS IN THE NEXT LINE OF CODE, BECAUSE hstep EVALUATES TO EITHER 1 OR 2. 
// HOW DO I ALTER THIS SO THAT THE PLOT IS ABLE TO STRETCH ON A SCALAR THAT IS NOT AS COARSE? 
    double hstep = blueWidth/len; 
    System.out.println("hstep, len are: "+hstep+", "+len); 
    for(int h = 1;h<len;h++){ 
     int x2 = leftStartPlotWindow+(int)(h * hstep); 
     int x1 = leftStartPlotWindow+(int) ((h - 1) * hstep); 
     int y1 = (blueBottom-(int)plotData[h - 1]); 
     int y2 = (blueBottom-(int)plotData[h]); 
     g.drawLine(x1, y1, x2, y2); 
     g.setColor(Color.green); 
     if(h==1){ 
      g.drawLine(leftStartPlotWindow+(int)(h*hstep), blueBottom, leftStartPlotWindow+(int)(h*hstep), blueBottom-100); 
     } 
     if(h==len-1){ 
      g.drawLine(leftStartPlotWindow+(int)(h*hstep), blueBottom, leftStartPlotWindow+(int)(h*hstep), blueBottom-100); 
     } 
     g.setColor(Color.red); 
    } 
    // plot inner rectangle 
    g.setColor(Color.blue); 
    g.drawRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight); 
} 
} 

回答

1

@Tedil发现的bug,或者至少是非常好的猜测。说这个,我已经拿到了我的分数并缩小了他们。然后在代码中迭代遍历集合。

这种方式的好处是最小和最大X和Y给你的边界矩形,所以initialiiy你只需要绘制,然后得到你的缩放/调整大小排序,然后绘制点和装饰基于缩放的坐标。

调试代码很痛苦,所以我喜欢尽可能多地委托数学,并且在我用装饰来混淆代码之前先对“角点”进行排序。

您还可以在有很多分数的时候巧妙地对其进行优化,或者如果它非常动态的话,可以看看'屏幕外'的建筑。

不是java的家伙。您可以将您所设置的数据集加载到点集合中,例如

点是两个浮体的结构称为X和Y 而积分是一个有序列表(按X)

然后 只是遍历最低和最高的Y点,制定出界,你已经有X因为它们是顺序的,所以它只是第一个也是最后一个 如果需要合理化它们,即Origin为0,0 MaxX = 7658所以称它为8000,MaxY为84,因此称它为100.

所以现在你的观点可以是(0,0,8000,100)所以 ActtualHeight = 100,actualWidth = 8000(计算此值,以处理负轴)

一些,你已经计算出你的绘图区是755×384和原点将在30绘制,45

所以XRatio =(双人间)ActualWidth的/ 755,和YRatio = ...

loopp通过实际点和生产规模的人的另一份名单,加德偏移,反转Y的 然后从ppoint 0再接1,1〜2,N-1到n)

这一切都死了简单的东西,只需将整个图形的绘图分解为一些简单的步骤即可。想想你会怎样用一些方格纸,一把尺子和一支HB铅笔做这件事。

例如ypou可以为你的蜱虫制定一个简单的步长。 然后使用teh滴答位置和方向来计算滴答标签的位置。 您可以居中描述,标题等。

特技是相对定位。

X轴标签是从X轴线向下20个像素,并且位于X AxisStart和end之间的中心。

不要尝试使用绝对坐标在一个循环内完成所有操作。

PS首先得到它的工作,然后看看变得聪明...

+0

+1谢谢您的建议。有没有简单的方法让你提供一些示例代码来说明你所建议的技术? – CodeMed 2012-01-04 22:40:57

+0

谢谢。我将此标记为答案,因为您比其他人更重视这一点。但是,任何读者都可以看到所有三个答案都是正确的。 – CodeMed 2012-01-05 21:56:23

1

这基本上是罪魁祸首:double hstep = blueWidth/len; 你将2 int在这里,这将导致一个整数。为了得到你想要的double,你必须明确地转换例如len加倍像这样:double hstep = blueWidth/(double)len;。 (也适用于你被numYticks分割线。)

+0

谢谢。 +1帮助我解决这个问题。 – CodeMed 2012-01-04 22:44:29

1

您的问题是blueWidthlen个均为整数且其结果将是一个int(然后将其转化成一个双) 。

而是尝试

double hstep = ((double)blueWidth)/len; 
+0

Lindsjo,谢谢。 +1帮助我解决这个问题。 – CodeMed 2012-01-04 22:42:05