Масштабирование данных по размеру пикселя - PullRequest
1 голос
/ 05 января 2012

У меня есть набор данных, который я строю в прямоугольном пространстве, определенном в коде ниже. Длина набора данных почти всегда будет отличаться от ширины в пикселях экранного пространства, в котором необходимо построить набор данных.

Моя проблема в том, что мне нужен эффективный способ расширения графика набора данных по всей ширине пространства печати. ​​

Как видно из кода, который я публикую ниже, данные (неправильно) отображаются в подмножестве ширины прямоугольника. Если вы скомпилируете код и вручную измените размер фрейма несколько раз, вы увидите, что ширина графа данных относительно ширины синего внутреннего прямоугольника изменяется с изменением ширины фрейма. Чтобы проиллюстрировать это наиболее четко, приведенный ниже код также отображает две зеленые вертикальные линии, одну в начале, а другую в конце графика набора данных. Если код работал правильно, вертикальные зеленые линии должны располагаться только в начале и в конце синего внутреннего прямоугольника, а пики между ними должны находиться на соответствующем процентном расстоянии от начала и конца внутреннего синего прямоугольника (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 ]

1 голос
/ 05 января 2012

Ваша проблема в том, что blueWidth и len оба являются целыми числами, и их результатом будет целое число (которое затем конвертируется в double).

Вместо этого попробуйте

double hstep = ((double)blueWidth)/len;
1 голос
/ 05 января 2012

@ Тедил заметил ошибку или, по крайней мере, очень хорошее предположение. Сказав это, я бы взял свои очки, масштабировал и компенсировал их. Затем просто перебираем коллекцию в коде краски.

Хорошая особенность этого способа в том, что min и max X и Y дают вам ограничивающий прямоугольник, поэтому сначала вы просто рисуете его, сортируете масштабирование / изменение размера, затем рисуете точки и декорируете на основе масштабированных координат.

Код рисования болезнен для отладки, поэтому я предпочитаю делегировать как можно больше математических вычислений и сортировать «углы», прежде чем загромождать код украшением.

Вы также можете получить умную оптимизацию, когда есть много точек, или посмотреть на построение «за кадром», если оно очень динамичное.

Не парень по Java. Даже если вы можете загрузить набор данных из своего набора в набор точек, например,

Точка - это структура из двух чисел с плавающей точкой, называемых X и Y And Points - упорядоченный список (по X)

Тогда отработайте границы Просто прокрутите точки для min и max Y, у вас уже есть X, поскольку они являются порядком, так что это только первый и последний Рационализируйте их, если требуется, то есть Origin равен 0,0 MaxX = 7658, поэтому назовите его 8000, MaxY равен 84, поэтому назовите его 100.

Так что теперь ваши точки могут быть ограничены прямоугольником (0,0,8000,100), поэтому ActtualHeight = 100, actualWidth = 8000 (рассчитайте это, чтобы справиться с отрицательной осью)

В некоторых случаях, когда вы определили, что ваша область черчения составляет 755 х 384, а начало координат будет нанесено на 30, 45.

То есть XRatio = (Double) actualWidth / 755, а YRatio = ...

переберите действительные точки и создайте другой список масштабированных точек, добавьте смещение, инвертируйте для Y Затем проведите линию от точки 0 до 1, от 1 до 2, от n-1 до n)

Это все очень простые вещи, просто разбейте рисунок всего графика на несколько простых простых шагов. Подумайте, как бы вы сделали это с помощью миллиметровки, линейки и карандаша HB. * ​​1023 *

Например, вы можете легко определить размер шага для своих тиков. Затем используйте положение галочки и ориентацию, чтобы определить, где находится метка галочки. Вы можете центрировать свое описание, заголовок и т. Д.

Трюк относительное расположение.

Метка оси X, скажем, на 20 пикселей ниже линии оси X и центрирована между X AxisStart и end.

Не пытайтесь делать все это в одном цикле, используя абсолютные координаты.

PS сначала заставьте его работать, а затем посмотрите на умное ...

1 голос
/ 05 января 2012

Это в основном виновник: double hstep = blueWidth/len; Вы делите два int здесь, что снова приведет к целому числу.Чтобы получить double, который вы хотите, вам нужно явно привести, например, len к удвоению, как это: double hstep = blueWidth/(double)len;.(Также относится к линии, где вы делите на numYticks.)

...