缩放数据以适应像素空间

时间:2012-01-04 22:05:48

标签: java swing graph plot scaling

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

我的问题是我需要一种有效的方法来在绘图空间的整个宽度上展开数据集的图形。

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

有人能告诉我如何修复下面的代码,以便在内部蓝色矩形的宽度上均匀分布数据图的宽度吗?

我认为问题出现在hstep变量中,我在下面的DataPanel.java代码中标记了这个位置,我认为问题代码在注释中包含ALL 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);
}
}

3 个答案:

答案 0 :(得分:1)

这基本上是罪魁祸首:double hstep = blueWidth/len; 你在这里划分了两个int,这将再次产生一个整数。为了获得您想要的double,您必须明确投出,例如len加倍:double hstep = blueWidth/(double)len;。 (也适用于除以numYticks的行。)

答案 1 :(得分:1)

您的问题是 blueWidth len 都是整数,其结果将是一个int(然后转换为double)。

而是尝试

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

答案 2 :(得分:1)

@Tedil发现了这个bug,或者至少是一个非常好的猜测。 这样说,我已经拿走了我的积分并缩放并抵消了它们。 然后只是在绘画代码中遍历集合。

关于这种方式的好处是min和max X和Y为你提供了边界矩形,所以首先你只需绘制它,然后对你的缩放/调整大小进行排序,然后绘制点并根据缩放的坐标进行装饰。

绘制代码很难调试,所以我喜欢委托尽可能多的数学运算,并在我用装饰混乱代码之前对'角点'进行排序。

如果有很多分数,你也可以巧妙地优化它,或者如果它非常动态,可以看看建立“屏幕外”。

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

Point是两个浮点数的结构,称为X和Y. 而Points是一个有序列表(由X表示)

然后计算出边界 只需循环显示最小和最大Y点,你已经有了X,因为它们是订单,所以它只是第一个也是最后一个 如果需要合理化它们,即Origin为0,0 MaxX = 7658,所以称之为8000,MaxY为84,所以称之为100。

所以现在你的点可以被一个矩形(0,0,8000,100)所限制 ActtualHeight = 100,actualWidth = 8000(计算此值,处理负轴)

你已经计算出你的绘图区域是755 X 384并且原点将被绘制为30,45。

所以XRatio =(Double)actualWidth / 755,YRatio = ...

循环通过实际点并生成另一个缩放的列表,添加teh偏移,反转为Y. 然后从ppoint 0到1,1,2到n-1到n)绘制一条线

这些都是简单的东西,只需将整个图形的绘图分解为一些简单易行的步骤。想想你是如何用一些方格纸,一把尺子和一支HB铅笔做的。

例如,ypou可以计算出一个步长,让你的蜱容易死掉。 然后使用刻度线位置和方向来确定刻度标签的位置。 您可以将描述,标题等居中。

诡计是相对定位。

X轴标签表示距离X轴线20个像素,并且在X轴启动和结束之间居中。

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

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