Использование мировых координат в Java2D AWT Frame - PullRequest
0 голосов
/ 10 марта 2019

Я застрял при попытке осуществить преобразование мировых координат в координаты устройства.

В основном я хочу нарисовать следующие мировые координаты:

    // --- World Coordinates
    //
    //                  (xmax,ymax)
    //      ┌────────────────┐
    //      │                │
    //      │                │
    //      │                │
    //      │                │
    //      └────────────────┘
    // (xmin,ymin)
    //
    // (xmin,ymin) = (0, 100)
    // (xmax,ymax) = (1.5, 2.5)

После этой книги (стр. 31).), Я пытаюсь реализовать аффинные преобразования, необходимые для перемещения из области просмотра мировых координат в область просмотра координат устройства.

    // Introduction to Computer Graphics Using Java 2D and 3D 
    // Frank Klawonn, Ed. Springer 2008
    // Page 31

Я подготовил тестовый класс, который содержит два теста, первый из которых (только что установленint test = 1) проверяет первые два преобразования, чтобы перевернуть ось Y (начало координат на экране в верхнем левом углу, а не в левом нижнем углу).

Чтобы тесты работали нормально, для отображения отображаются прямоугольник и линияthis.

Test1

Однако при переключении на Тест 2, который включает в себя все ожидаемые преобразования, я получаю пустой экран в результате:

    // --- Affine Transform 1 and 2
    //
    //   T(0, h) ◦ S(1,−1)
    //
    // --- Affine Transform 3, 4 and 5
    //   
    //                     ╭ umax − umin   vmax − vmin ╮
    //   T(umin, vmin) ◦ S | ----------- , ----------- | ◦ T(-xmin, -ymin)  
    //                     ╰ xmax − xmin   ymax − ymin ╯
    //

Весь исходный код для класса Test включен:

package com.example.test2;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

public class Test2 extends Frame {

        Graphics2D          g2d;

        Insets              insFrame;
        Dimension           sizeFrame;

        public Test2() {

            this.setSize(660,540);            
            this.setUndecorated(false);
            this.setVisible(true);

            this.addWindowListener(new WindowAdapter(){  
                public void windowClosing(WindowEvent e) {  
                    dispose();  
                }  
            });

        }

        @Override
        public void paint(Graphics g) {

            g2d = (Graphics2D) g;

            insFrame = this.getInsets();
            sizeFrame = this.getSize();

            //int test = 1;   // Change to test 2 to test the whole transformation
            int test = 2;

            if ( test == 1 ) {

                // AT1 & AT2 Test
                this.setScale(1);
                g2d.setColor(Color.ORANGE);
                Line2D.Double line = new Line2D.Double(0, 0, sizeFrame.width-insFrame.left-insFrame.right-1, sizeFrame.height-insFrame.top-insFrame.bottom);
                g2d.draw(line);
                g2d.setColor(Color.RED);
                g2d.drawRect(0, 0, sizeFrame.width-insFrame.left-insFrame.right-1, sizeFrame.height-insFrame.top-insFrame.bottom-1);

            } else if (test == 2) {

                // AT1, AT2, AT3, AT4 & AT5 Test
                this.setScale(2);
                g2d.setColor(Color.ORANGE);
                Line2D.Double line = new Line2D.Double(0, 1.5, 100, 2.5);
                g2d.draw(line);
                g2d.setColor(Color.RED);
                Rectangle2D.Double rectangle = new Rectangle2D.Double(0, 1.5, 100, 2.5);
                g2d.draw(rectangle);

            }


        };

        // Required affine transforms to move from 
        // World Coordinates Viewport to 
        // Screen Pixel Coordinates Viewport
        //
        // --- Reference textbook:
        //
        // Introduction to Computer Graphics Using Java 2D and 3D 
        // Frank Klawonn, Ed. Springer 2008
        // Page 31
        // 
        // --- Viewports
        //
        // World Coordinates Viewport (xmin,ymin) - (xmax,ymax)
        // Screen Pixel Coordinates Viewport (umin, vmin) - (umax, vmax)
        // 
        // --- World Coordinates
        //
        //                  (xmax,ymax)
        //      ┌────────────────┐
        //      │                │
        //      │                │
        //      │                │
        //      │                │
        //      └────────────────┘
        // (xmin,ymin)
        //
        // (xmin,ymin) = (0, 100)
        // (xmax,ymax) = (1.5, 2.5)
        //
        // --- User coordinates
        //
        //                  (umax,vmax)
        //      ┌────────────────┐
        //      │                │
        //      │                │
        //      │                │
        //      │                │
        //      └────────────────┘
        // (umin,vmin)
        //
        // (umin,vmin) = (inset.left, heightFrame - inset.bottom)
        // (umax,vmax) = (widthFrame - inset.right, inset.top)
        //
        // --- Affine Transform 1 and 2
        //
        //   T(0, h) ◦ S(1,−1)
        //
        // --- Affine Transform 3, 4 and 5
        //   
        //                     ╭ umax − umin   vmax − vmin ╮
        //   T(umin, vmin) ◦ S | ----------- , ----------- | ◦ T(-xmin, -ymin)  
        //                     ╰ xmax − xmin   ymax − ymin ╯
        //
        private void setScale(int test) {

            // World Coordinates
            // (xmin,ymin) = (0, 1.5)
            // (xmax,ymax) = (100, 2.5)
            Double xmin = 0.0;
            Double ymin = 1.5;
            Double xmax = 100.0;
            Double ymax = 2.5;

            // User Coordinates
            // (umin,vmin) = (inset.left, heightFrame - inset.bottom)
            // (umax,vmax) = (widthFrame - inset.right, inset.top)
            int umin = insFrame.left;
            int vmin = (int) (sizeFrame.getHeight() - insFrame.bottom);
            int umax = (int) (sizeFrame.getWidth() - insFrame.right);
            int vmax = insFrame.top;


            if (test == 1) {

                // Affine Transformation 1 and 2
                // T(0, h) ◦ S(1,−1)
                AffineTransform at1 = new AffineTransform();
                at1.setToScale(1,-1);
                AffineTransform at2 = new AffineTransform();
                at2.setToTranslation(insFrame.left, sizeFrame.getHeight() - insFrame.bottom - 1);
                at1.preConcatenate(at2);
                g2d.transform(at1);

            } else if (test == 2) {

                // Affine Transformation 1 and 2
                // T(0, h) ◦ S(1,−1)
                AffineTransform at1 = new AffineTransform();
                at1.setToScale(1,-1);
                AffineTransform at2 = new AffineTransform();
                at2.setToTranslation(insFrame.left, sizeFrame.getHeight() - insFrame.bottom - 1);

                // Affine Transformation 3, 4 and 5
                //                   ╭ umax − umin   vmax − vmin ╮
                // T(umin, vmin) ◦ S | ----------- , ----------- | ◦ T(-xmin, -ymin)  
                //                   ╰ xmax − xmin   ymax − ymin ╯
                AffineTransform at3 = new AffineTransform();
                at3.setToTranslation(umin, vmin);
                AffineTransform at4 = new AffineTransform();
                at4.setToScale(1.0*(umax-umin)/(xmax-xmin), 1.0*(vmax-vmin)/(ymax-ymin));
                AffineTransform at5 = new AffineTransform();
                at5.setToTranslation(-xmin,-ymin);
                at4.preConcatenate(at5);
                at3.preConcatenate(at4);
                at2.preConcatenate(at3);
                at1.preConcatenate(at2);
                g2d.transform(at1);

            }


        }

        public static void main( String[] args ) {
            Test2 window = new Test2();

    }

}

1 Ответ

1 голос
/ 11 марта 2019

Я заглянул в книгу и считаю использование AffineTransform.preConcatenate() запутанным. Я предпочитаю использовать AffineTransform.concatenate(), поскольку для меня это дает более естественный поток.

Давайте рассмотрим первый пример (трансформируем, затем меняем направление y).

Это ваш код:

    AffineTransform at1 = new AffineTransform();
    at1.setToScale(1,-1);
    AffineTransform at2 = new AffineTransform();
    at2.setToTranslation(insFrame.left, sizeFrame.getHeight() - insFrame.bottom - 1);
    at1.preConcatenate(at2);

Используя concatenate, вы можете написать это как:

    AffineTransform at1 = new AffineTransform();
    at1.setToTranslation(insFrame.left, sizeFrame.getHeight() - insFrame.bottom - 1);
    AffineTransform at2 = new AffineTransform();
    at2.setToScale(1,-1);
    at1.concatenate(at2);

или даже короче:

    AffineTransform at = new AffineTransform();
    at.translate(insFrame.left, sizeFrame.getHeight() - insFrame.bottom - 1);
    at.scale(1,-1);

Вы можете увидеть поток «переведите, затем масштабируйте», непосредственно представленный в коде.


Для второго теста вы можете применить тот же подход:

    // Affine Transformation 3, 4 and 5
    //                   ╭ umax − umin   vmax − vmin ╮
    // T(umin, vmin) ◦ S | ----------- , ----------- | ◦ T(-xmin, -ymin)
    //                   ╰ xmax − xmin   ymax − ymin ╯
    AffineTransform at = new AffineTransform();
    at.translate(umin, vmin);
    at.scale((umax-umin)/(xmax-xmin), (vmax-vmin)/(ymax-ymin));
    at.translate(-xmin, -ymin);

Обратите внимание: umin, vmin уже обозначает нижнюю левую экранную координату и umax, vmax верхнюю правую экранную координату, поэтому никакого дополнительного перемещения или переворачивания оси y не требуется!


Некоторые заключительные замечания:

  • ширина строки по умолчанию в Java2D составляет одну масштабированную единицу. При выборе ymin и ymax одна масштабированная единица заполняет всю высоту вашего окна. Чтобы не получить только заполненный прямоугольник, вы должны установить ширину линии на минимально возможное значение с помощью g2d.setStroke(new BasicStroke(0.0f)); перед вызовом g2d.draw().
  • параметры для Rectangle2D.Double() равны x, y, w, h, поэтому ваш код для создания объекта прямоугольника во втором примере, вероятно, должен выглядеть так: Rectangle2D.Double rectangle = new Rectangle2D.Double(0, 1.5, 100, 1); (при высоте 2,5 верхняя граница не будет отображаться на экране).
...