How to use AffineTransform with very little coordinates?

ⅰ亾dé卋堺 提交于 2020-01-24 12:58:06

问题


I have a set of two dimensions points. Their X and Y are greater than -2 and lesser than 2. Such point could be : (-0.00012 ; 1.2334 ).

I would want to display these points on a graph, using rectangles (a rectangle illustrates a point, and has its coordinates set to its point's ones - moreover, it has a size of 10*10).

Rectangles like (... ; Y) should be displayed above any rectangles like (... ; Y-1) (positive Y direction is up). Thus, I must set the graph's origin not at the top-left hand-corner, but somewhere else.

I'm trying to use Graphics2D's AffineTransform to do that.

  1. I get the minimal value for all the X coordinates
  2. I get the minimal value for all the Y coordinates
  3. I get the maximal value for all the X coordinates
  4. I get the maximal value for all the Y coordinates
  5. I get the distance xmax-xmin and ymax-ymin
  6. Then, I wrote the code I give you below.

Screenshots

Some days ago, using my own method to scale, I had this graph:
(so as I explained, Y are inverted and that's not a good thing)

For the moment, i.e., with the code I give you below, I have only one point that takes all the graph's place! Not good at all.

I would want to have:

(without lines, and without graph's axis. The important here is that points are correctly displayed, according to their coordinates).


Code

To get min and max coordinates value:

x_min = Double.parseDouble((String) list_all_points.get(0).get(0));
x_max = Double.parseDouble((String) list_all_points.get(0).get(0));
y_min = Double.parseDouble((String) list_all_points.get(0).get(1));
y_max = Double.parseDouble((String) list_all_points.get(0).get(1));
for(StorableData s : list_all_points) {
    if(Double.parseDouble((String) s.get(0)) < x_min) {
        x_min = Double.parseDouble((String) s.get(0));
    }
    if(Double.parseDouble((String) s.get(0)) > x_max) {
        x_max = Double.parseDouble((String) s.get(0));
    }

    if(Double.parseDouble((String) s.get(1)) < y_min) {
        y_min = Double.parseDouble((String) s.get(1));
    }
    if(Double.parseDouble((String) s.get(1)) > y_max) {
        y_max = Double.parseDouble((String) s.get(1));
    }
}

To draw a point:

int x, y;
private void drawPoint(Cupple storable_data) {
    //x = (int) (storable_data.getNumber(0) * scaling_coef + move_x);
    //y = (int) (storable_data.getNumber(1) * scaling_coef + move_y);
    x = storable_data.getNumber(0).intValue();
    y = storable_data.getNumber(1).intValue();
    graphics.fillRect(x, y, 10, 10);
    graphics.drawString(storable_data.toString(), x - 5, y - 5);
}

To paint the graph:

@Override
public void paint(Graphics graphics) {
    this.graphics = graphics;
    Graphics2D graphics_2d = ((Graphics2D) this.graphics);

    AffineTransform affine_transform = graphics_2d.getTransform();
    affine_transform.scale(getWidth()/(x_max - x_min), getHeight()/(y_max - y_min));
    affine_transform.translate(x_min, y_min);
    graphics_2d.transform(affine_transform);

    for(StorableData storable_data : list_all_points) {
        graphics_2d.setColor(Color.WHITE);
        this.drawPoint((Cupple) storable_data);
    }

回答1:


I suggest you map each data point to a point on the screen, thus avoiding the following coordinate system pitfalls. Take your list of points and create from them a list of points to draw. Take into account that:

  • The drawing is pixel-based, so you will want to scale your points (or you would have rectangles 1 to 4 pixels wide...).
  • You will need to translate all your points because negative values will be outside the boundaries of the component on which you draw.
  • The direction of the y axis is reversed in the drawing coordinates.

Once that is done, use the new list of points for the drawing and the initial one for calculations. Here is an example:

public class Graph extends JPanel {

    private static int gridSize = 6;
    private static int scale = 100;
    private static int size = gridSize * scale;
    private static int translate = size / 2;
    private static int pointSize = 10;

    List<Point> dataPoints, scaledPoints;

    Graph() {
        setBackground(Color.WHITE);

        // points taken from your example
        Point p1 = new Point(-1, -2);
        Point p2 = new Point(-1, 0);
        Point p3 = new Point(1, 0);
        Point p4 = new Point(1, -2);
        dataPoints = Arrays.asList(p1, p2, p3, p4);

        scaledPoints = dataPoints.stream()
                .map(p -> new Point(p.x * scale + translate, -p.y * scale + translate))
                .collect(Collectors.toList());
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(size, size);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        // draw a grid
        for (int i = 0; i < gridSize; i++) {
            g2d.drawLine(i * scale, 0, i * scale, size);
            g2d.drawLine(0, i * scale, size, i * scale);
        }

        // draw the rectangle
        g2d.setPaint(Color.RED);
        g2d.drawPolygon(scaledPoints.stream().mapToInt(p -> p.x).toArray(),
                        scaledPoints.stream().mapToInt(p -> p.y).toArray(),
                        scaledPoints.size());

        // draw the points
        g2d.setPaint(Color.BLUE);
        // origin
        g2d.fillRect(translate, translate, pointSize, pointSize);
        g2d.drawString("(0, 0)", translate, translate);
        // data
        for (int i = 0; i < dataPoints.size(); i++) {
            Point sp = scaledPoints.get(i);
            Point dp = dataPoints.get(i);
            g2d.fillRect(sp.x, sp.y, pointSize, pointSize);
            g2d.drawString("(" + dp.x + ", " + dp.y + ")", sp.x, sp.y);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setContentPane(new Graph());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

And another:

You might want to have the points aligned on the grid intersections and not below and to the right of them. I trust you will figure this one out.

Also, I ordered the points so that drawPolygon will paint the lines in the correct order. If your points are arbitrarily arranged, look for ways to find the outline. If you want lines between all points like in your example, iterate over all combinations of them with drawLine.



来源:https://stackoverflow.com/questions/43293980/how-to-use-affinetransform-with-very-little-coordinates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!