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