java - 缩放数据以适应像素空间

标签 java swing graph plot scaling

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

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

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

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

我认为问题出在 hstep 变量中,我在下面的 DataPanel.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();}});
}
}  

数据面板.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);
}
}

最佳答案

@Tedil 发现了这个错误,或者至少是一个很好的猜测。 话虽如此,我会接受我的观点并缩放和抵消它们。 然后在绘制代码中迭代集合。

这种方式的好处是,最小和最大 X 和 Y 为您提供边界矩形,因此最初您只需绘制它,并对缩放/调整大小进行排序,然后绘制点并根据缩放坐标进行装饰。

绘制代码调试起来很痛苦,所以我喜欢尽可能多地委派数学运算,并在用装饰弄乱代码之前对“角落”进行排序。

当有很多点时,您还可以巧妙地优化它,或者如果它非常动态,则考虑构建“屏幕外”。

不是 java 人。 鉴于您可以将您设置的数据集加载到点集合中,例如

Point 是两个名为 X 和 Y 的 float 的结构 Points 是一个有序列表(按 X)

然后计算边界 只需遍历最小和最大 Y 的点,您已经按顺序拥有 X,所以它只是第一个和最后一个 如果需要,将它们合理化,即 Origin 是 0,0 MaxX = 7658,所以称它为 8000,MaxY 是 84,所以称它为 100。

所以现在你的点可以被一个矩形(0,0,8000,100)包围所以 ActutualHeight = 100, actualWidth = 8000(计算这个,处理负轴)

有些地方你已经计算出你的绘图区域是 755 X 384,原点将被绘制在 30, 45。

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

循环遍历实际点并生成另一个缩放列表,添加偏移量,为 Y 反转 然后从ppoint 0 to 1, 1 to 2, n-1 to n)画一条线

一切都是非常简单的事情,只需将整个图形的绘制分解为一些简单的步骤即可。想一想如何用一些方格纸、一把尺子和一支 HB 铅笔来做。

例如,ypou 可以很容易地为您的滴答计算出步长。 然后使用刻度位置和方向计算出刻度标签的位置。 您可以将您的描述、标题等居中。

技巧是相对定位。

X 轴标签是从 X 轴线向下 20 像素,并在 X AxisStart 和 X 轴结束之间居中。

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

PS先搞定,再看变聪明...

关于java - 缩放数据以适应像素空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8734758/

相关文章:

Graphviz/Dot - 如何用独特的颜色标记树中的所有叶子?

java - Tomcat/ApplicationContext 无法找到 Filter 类

java - 如何使用 Java 下载文件?

java - REST 如何传递包含 "/"的值作为 URI 中的路径参数?

java - 使用 BorderLayout 绘制图形并将其放置在面板内

KeyPad 组件的 Java KeyEvents

.net - 合并 .net 对象图

java - 限制 Accumulo 中的查询量

java - 检索从组中选择的 Swing 单选按钮

database - 是否可以在 orientdb 的集群中使用集群?