Использование мировых координат в AWT-фрейме Java2D

#java #math #awt #coordinates #java-2d

#java #математика #awt #координаты #java-2d

Вопрос:

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

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

     // --- 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 (начало координат на экране в верхнем левом углу, вместо нижнего левого).

Эти тесты работают нормально, прямоугольник и линия нарисованы, чтобы показать это.

Тест1

Однако при переключении на тест 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 ╯
    //
  

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

 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 amp; 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 amp; 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:

Я заглянул в книгу и нахожу использование 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);
  

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


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

     // 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 , поэтому ваш код для создания объекта rectangle во втором примере, вероятно, следует читать Rectangle2D.Double rectangle = new Rectangle2D.Double(0, 1.5, 100, 1); (при высоте 2,5 верхняя граница не будет отображаться на экране).

Комментарии:

1. Я закончил переписывать все аффинное преобразование в соответствии с вашим предложением и также обнаружил проблему с шириной штриха линии.