Аффинные отображения и составные фигуры в Java
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Аффинные отображения
и составные фигуры в Java
Лекция 8
25 октября 2021 г.
1 / 23
Содержание
1
Аффинное отображение как отдельный класс
2
Аффинное отображение в графическом контексте
3
Применение аффинного отображения к фигурам
4
Составные фигуры
2 / 23
Содержание
1
Аффинное отображение как отдельный класс
2
Аффинное отображение в графическом контексте
3
Применение аффинного отображения к фигурам
4
Составные фигуры
3 / 23
Определение
Класс java.awt.geom.AffineTransform
Конструкторы
AffineTransform() // Тождественное отображение
AffineTransform(double[] flatmatrix)
AffineTransform(
double m00, double m10, double m01,
double m11, double m02, double m12)
Представляет матрицу
m00 m01 m02
m10 m11 m12
1
Важно: в конструкторе значения указываются по столбцам.
То же относится к массиву flatmatrix. Длина массива: 4 или 6.
4 / 23
Задание аффинных отображений
Некоторые методы класса AffineTransform
void
void
void
void
void
concatenate(AffineTransform B)
rotate(double theta)
scale(double sx, double sy)
translate(double tx, double ty)
shear(double shx, double shy)
Метод
A.concatenate(B)
A.rotate(theta)
A.scale(sx, sy)
A.translate(tx, ty)
Эффект
A 7→ AB
A 7→ AR(θ)
A 7→ AS(sx , sy )
A 7→ AT (tx , ty )
//
//
//
//
//
композиция
поворот (в радианах)
масштабирование
параллельный перенос
сдвиг
Новая матрица
находится справа
Напоминание:
R(θ) — поворот
S(sx , sy ) — масштабирование
T (tx , ty ) — параллельный перенос
5 / 23
Образы фигур при аффинных отображениях
Метод класса AffineTransform:
Shape createTransformedShape(Shape src)
Возвращает фигуру, полученную применением отображения к src.
Методы класса Graphics2D:
void draw(Shape s)
void fill(Shape s)
(Shape — это интерфейс, реализуемый всеми классами фигур.)
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Shape s = new
Rectangle2D.Double(50, 50, 200, 100);
AffineTransform at = new AffineTransform();
at.rotate(Math.PI / 6);
g2.draw(at.createTransformedShape(s));
}
50
π
6
250
50
150
6 / 23
Содержание
1
Аффинное отображение как отдельный класс
2
Аффинное отображение в графическом контексте
3
Применение аффинного отображения к фигурам
4
Составные фигуры
7 / 23
Методы для изменения
Графический контекст Graphics2D также включает в себя аффинное
отображение.
Некоторые методы класса Graphics2D:
void
void
void
void
rotate(double theta)
scale(double sx, double sy)
translate(double tx, double ty)
transform(AffineTransform B)
Композиция называется transform, а не concatenate, как в
AffineTransform; остальные методы называются так же.
g2.transform(B) заменяет отображение A в контексте g2 на AB.
Аналогично для других методов.
8 / 23
Аффинное отображение в графическом контексте
Пусть в графическом контексте g2 содержится матрица A
аффинного отображения.
Когда рисуется фигура:
g2.draw(shape);
матрица A применяется к координатам каждой точки shape.
Точки с полученными координатами изображаются в экранной
системе координат S.
Матрицу A можно рассматривать как матрицу перехода от S к
некоторому реперу W , задающему пользовательскую, или мировую,
систему координат.
9 / 23
Пример аффинного отображения в контексте
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.translate(getWidth()/2, getHeight()/2);
g2.scale(50, -50);
g2.rotate(Math.PI / 6);
g2.setStroke(new BasicStroke(1/50.0f));
g2.draw(new Ellipse2D.Double(-2, -1, 4, 2));
}
Векторы репера S увеличены.
S
W
Нужно установить ширину линии
1/50, чтобы компенсировать
масштабирование (только если
коэффициенты равны по модулю).
Конструктор BasicStroke принимает
float.
10 / 23
Аффинное отображение как стек
S .
Пусть g2 содержит матрицу перехода A от S к W : A = CW
Тогда g2.transform(A1) порождает новую систему координат W 0 ,
W =A .
такую что CW
1
Методы rotate, translate, transform и т.п. класса Graphics2D
умножают новую матрицу на существующую только справа.
Поэтому новая матрица будет первой применяться к мировым
координатам.
Таким образом запись в Java последовательности отображений
сверху вниз соответствует умножению матриц слева направо, как в
матрице композиции переходов.
См. также:
Макаров Е.М. Элементы двумерной графики в Java. ННГУ, 2017.
11 / 23
Сравнение AffineTransform и отображения в Graphics2D
Чтобы нарисовать фигуру в нужном положении, можно создать
объект, описывающий эту фигуру (см. далее) и затем
1. создать объект AffineTransform с нужным преобразованием,
применить его к фигуре и нарисовать результат в исходном
графическом контексте, или
2. изменить преобразование в графическом контексте,
компенсировать изменение линий и нарисовать исходную фигуру.
Возможна комбинация этих методов.
• Если x 6= y в S(x, y), то нужно использовать метод 1.
• Если нужно повернуть текст, то проще использовать метод 2.
12 / 23
Иерархическое моделирование
Изображение состоит из нескольких объектов.
Каждый из них рисуется в естественной системе координат.
Перед рисованием объекта к графическому контексту применяется
аффинное преобразование, которое задает требуемое положение и
размер объекта.
13 / 23
Содержание
1
Аффинное отображение как отдельный класс
2
Аффинное отображение в графическом контексте
3
Применение аффинного отображения к фигурам
4
Составные фигуры
14 / 23
Применение к опорным точкам
Аффинное отображение f нельзя применить к каждой точке
фигуры.
Оно применяется только к ключевым точкам: концам отрезков,
опорным точкам кривых Безье.
Чтобы это работало, метод построения фигуры по опорным точкам
должен коммутировать с аффинным отображением.
Пусть B(p1 , . . . , pn )(t), t ∈ [0, 1] — параметрическое задание фигуры,
использующее опорные точки (параметры) p1 , . . . , pn .
p1 , . . . , pn
f
B(·)(t)
p
f
f
p01 , . . . , p0n
B(·)(t)
p0
15 / 23
Применение аффинного отображения к фигурам
Коммутирование f и B имеет место, потому что Java использует
фигуры, где B есть аффинная комбинация.
B(p1 , . . . , pn )(t) =
n
X
k=1
λi (t)pi ,
n
X
λi (t) = 1
k=1
Аффинные отображения сохраняют аффинные комбинации.
16 / 23
draw() или fill()?
Почему аффинное отображение, являющееся частью графического
контекста, влияет на ширину линии?
Связанный вопрос: какой метод первичный: draw() или fill()?
17 / 23
draw() или fill()?
Почему аффинное отображение, являющееся частью графического
контекста, влияет на ширину линии?
Связанный вопрос: какой метод первичный: draw() или fill()?
Порядок рисования линии:
1. создать фигуру, описывающую контур линии;
2. закрасить эту фигуру, используя метод g2.fill(...).
Аффинное отображение влияет на фигуру, описывающую контур
линии, как и на любую другую фигуру.
Увеличенное изображение угла, образованного двумя отрезками.
17 / 23
Содержание
1
Аффинное отображение как отдельный класс
2
Аффинное отображение в графическом контексте
3
Применение аффинного отображения к фигурам
4
Составные фигуры
18 / 23
Классы фигур
Пакет java.awt.geom содержит классы
• Point2D
• Line2D
• Rectangle2D
• Ellipse2D
• Arc2D
• QuadCurve2D
• CubicCurve2D
Все они абстрактные.
Все, кроме Point2D, реализуют интерфейс java.awt.Shape.
Каждый из этих классов имеет «конкретные» статические
вложенные классы Float и Double.
Кроме того, эти вложенные классы являются подклассами
содержащих классов.
19 / 23
Классы фигур
Создавать нужно объекты Figure2D.Float или Figure2D.Double,
где Figure — это Point, Line и т.д.
При написании методов, принимающих эти классы, следует по
возможности использовать Figure2D или Shape (полиморфизм).
Примеры
void drawInBlack(Graphics2D g, Shape s) {
g.setColor(Color.BLACK);
g.draw(shape);
}
Line2D line = new Line2D.Double(0, 0, 100, 200);
drawInBlack(line);
20 / 23
Составные фигуры
Абстрактный класс Path2D также имеет вложенные подклассы Float
и Double и реализует интерфейс Shape.
Описывает составную, не обязательно непрерывную фигуру.
Фигура имеет текущую точку, по отношении к которой можно
перемещаться и рисовать отрезки прямых и кривых.
Можно также добавлять любую другую фигуру, реализующую
Shape.
После создания пустой фигуры с помощью конструктора
Path2D.Double() нужно вызвать
void moveTo(double x, double y),
чтобы переместиться в начальную точку фигуры.
21 / 23
Методы Path2D
Path2D.Double()
Создает пустую фигуру
(конструктор)
void moveTo
(double x, double y)
void lineTo
(double x, double y)
void quadTo
(double x1, double y1,
double x2, double y2)
void curveTo
(double x1, double y1,
double x2, double y2,
double x3, double y3)
void append
(Shape s, boolean connect)
void closePath()
boolean contains
(double x, double y)
Перемещает текущую точку в (x, y)
Соединяет текущую точку с (x, y) отрезком
Соединяет текущую точку с (x2 , y2 )
квадратичной кривой Безье с контрольной точкой (x1 , y1 )
Соединяет текущую точку с (x3 , y3 ) кубической кривой Безье с контрольными точками (x1 , y1 ) и (x2 , y2 )
Добавляет фигуру s. Если connect
есть true, соединяет текущую точку и
s отрезком
Соединяет отрезком текущую точку с
точкой из последнего moveTo
Возвращает true, если точка (x, y) ле22 / 23
жит внутри фигуры
Сглаживание (antialiasing)
Типичный метод paintComponent()
import java.awt.RenderingHints;
...
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// Следующий вызов устанавливает режим сглаживания
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
...
}
23 / 23