Компьютерная графика Основные 2D-примитивы Jordi Linares i Pellicer Escola Politècnica Superior d’Alcoi Dep. de Sistemes Informàtics i Computació jlinares@dsic.upv.es http://www.dsic.upv.es/~jlinares Режимы визуализации • • • • • В processing есть несколько режимов визуализации: JAVA2D, P2D, P3D и OPENGL Есть и другие возможности в специальных библиотеках (например, метод трассировки лучей) Режим визуализации определяется третьим аргументом функции size(). Режим по умолчанию, если ничего не указано, JAVA2D. Режимы JAVA2D и P2D поддерживают двухмерное представление. P2D более производителен (аппаратное ускорение), но в нём пока ещё не реализованы все функции из JAVA2D. Мы будем использовать режим по умолчанию, JAVA2D, так как в нём есть все 2D-функции Система координат в 2D окна определяется в функции • Размер size(), обычно это одно из первых действий, происходящих в функции setup() • Точка(0,0)лежит слева вверху, положительное направление оси x — слева направо, оси y — сверху вниз Основные 2D-примитивы в processing • Точки • Линии • Эллипсы / Окружности / Дуги • Прямоугольники • Треугольники • Четырёхугольники • Кривые (Безье и Кэтмул-Рома) • Объекты (свободные формы) Цвет и настройки В processing многие функции вызывают изменение состояния => они устанавливают параметры, которые будут оставаться активными, пока мы их не поменяем. Пример: stroke() => меняет цвет кисти до тех пор, пока новый цвет не будет задан • Цвет кисти можно менять функцией stroke() stroke(255) => RGB(255, 255, 255), один цвет означает параметр внутри 256-значной серой шкалы (grayscale) stroke(128, 0, 128) => Любой цвет в RGB Толщина кисти указывается функцией strokeWeight() strokeWeight(5) => Толщина в 5 пикселей Цвет заполнения 2D-фигуры указывается функцией fill() fill(128) => RGB(128, 128, 128) fill(200, 120, 90) => RGB(200, 120, 90) • • • • • • • Цвет и настройки background() окно и заполняет указанным цветом • Стирает Примеры: background(0) • background(128, 100, 128) noFill() • 2D-фигуры не будут заполнены noStroke() • 2D-фигуры не будут иметь внешней границы (особенно полезно для замкнутых фигур, но влияет на все виды фигур, даже на линии) Точки point(x, y) Рисует точку в координатах (x, y) Цвет задаётся stroke(), а толщина (размер) - функцией strokeWeight() • • set(x, y, color) Рисует точку в координатах (x, y) с определённым цветом Не учитывает stroke() или strokeWeight() Пример: set(50, 50, color(128, 120, 255)) • • • • Другие применения set (в следующих уроках будет подробнее) Функция set может переместить изображение в (x, y) set может быть применена к изображению • • Линии line(x1, y1, x2, y2) Рисует линию между точками (x1, y1) и (x2, y2) С помощью функции stroke мы можем определить их свойства Пример: • • • size(100, 100); background(0); stroke(255); strokeWeight(5); line(0, 0, 99, 99); size(200, 200); background(0); for (int i=0; i<100; i++) { stroke(random(255), random(255), random(255)); strokeWeight(random(10)); line(0, 0, random(200), random(200)); } Линии • Окончание линии ROUND (скруглённое), PROJECT (линия удлиняется в зависимости от толщины кисти), SQUARE (квадратное) Пример: • • size(100, 100); background(0); stroke(255); strokeWeight(10); strokeCap(ROUND); line(50,50,150,50); strokeCap(PROJECT); line(50,75,150,75); strokeCap(SQUARE); line(50,100,150,100); Эллипсы и окружности ellipse(x, y, width, height) Рисует эллипс в точке с координатами (x, y) с указанными шириной и высотой • ellipseMode() Меняет интерпретацию параметров эллипса ellipseMode(CENTER) => (x, y) - центр эллипса (режим по умолчанию). ellipseMode(RADIUS) => как в предыдущем случае, но ширина и высота — радиусы, а не диаметры ellipseMode(CORNER) => (x, y) определяет верхний левый угол ограничивающего эллипс прямоугольника (bounding box). ellipseMode(CORNERS) => четыре параметра определяют координаты противоположных углов ограничивающего прямоугольника • • • • • Эллипсы и окружности • Пример: size(200, 200); background(0); stroke(255, 0, 0); strokeWeight(5); fill(0, 255, 0); // (x, y) и диаметры ellipse(100, 100, 100, 50); // 2 противоположных угла ellipseMode(CORNERS); ellipse(0, 0, 50, 50); Дуги arc(x, y, width, height, start, end) Рисует дугу как сектор эллипса с координатами (x, y) и с указанными шириной и высотой. Этот фрагмент или сектор определяется углами, заданными как start и end (в радианах по умолчанию) по часовой стрелке Их параметры тоже интерпретируются подобно ellipseMode() В processing заполнение фигур происходит по умолчанию, даже для незамкнутых фигур. Функция noFill() должна быть вызвана явно, если заполнение нежелательно Пример: • • • • size(200, 200); background(0); stroke(255, 0, 0); strokeWeight(5); fill(0, 255, 0); // (x, y) и диаметры arc(100, 100, 100, 100, 0, PI / 2.0); // Не заполнять noFill(); arc(100, 100, 100, 100, PI, 3 * PI / 2.0); Прямоугольники rect(x, y, width, height) Рисует прямоугольник • rectMode() Меняет интерпретацию параметров прямоугольника Параметры те же, что и у эллипса: CENTER , RADIUS, CORNER и CORNERS Режим по умолчанию - CORNER (x и y — координаты левого угла) • • • Треугольники и четырёхугольники triangle(x1, y1, x2, y2, x3, y3) Рисует треугольник по трём вершинам • quad(x1, y1, x2, y2, x3, y3, x4, y4) Рисует четырёхугольник. Первая точка - (x1, y1), остальные 3 вершины определены по часовой или против часовой стрелки (любым из вариантов) • size(200, 400); background(0); stroke(255, 0, 0); strokeWeight(5); fill(0, 255, 0); // (100,25) - (25,175) - (175,175) triangle(100,25,25,175,175,175); // По часовой // (38,231) - (186,220) - (169,363) - (30,376) quad(38, 231, 186, 220, 169, 363, 30, 376); Практика 2-1 Представление тригонометрических функций • • • • • Нарисуйте функции синуса и косинуса, значения от 0 до 2*pi радиан (0,0) в декартовых координатах находится в x=0 и y=height/2 в координатах окна (слева с краю, с середины) Путь от 0 до 2*pi должен занять большую часть окна Запрограммируйте функцию, которая рисует синус, другую для косинуса. Входные параметры — цвет, толщина кисти и ширина и высота окна На заднем плане нарисуйте координатную декартову ось. Кривые Безье bezier(x1, y1, cx1, cy2, cx2, cy2, x2, y2) • • • Кубические кривые (имеют точку перегиба) Называются в честь инженера и математика Пьера Безье Определяются через 4 точки: Первая и последняя — начальная и конечная точка кривой Центральные точки — контрольные точки, и они «притягивают» кривую, изменяя её, но не заставляя проходить через них • • Кривые Безье void setup() { size(400, 400); background(0); // Рисуем две кривые Безье drawBezier(100,140,50,40,350,40,300,140); drawBezier(50,290,150,190,200,390,350,290); } void drawBezier(int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2) { noFill(); stroke(255); // Сама кривая Безье bezier(x1, y1, // Начальная точка cx1, cy1, // Контрольная точка 1 cx2, cy2, // Контрольная точка 2 x2, y2); // Конечная точка // Нарисуем контрольные точки, чтобы // лучше понять их влияние strokeWeight(3); stroke(255,0,0); point(x1, y1); point(x2, y2); point(cx1, cy1); point(cx2, cy2); strokeWeight(1); line(x1, y1, cx1, cy1); line(x2, y2, cx2, cy2); } Кривые Кэтмулл-Рома • • • • curve(cx1, cy1, x1, y1, x2, y2, cx2, cx2) Их ввели Эдвин Катмулл и Рафи Ром Полезно, чтобы сделать кривую, интерполируемую множеством точек и очень полезно для компьютерной графики - например, для анимации с ключевыми кадрами (keyframe animation) curve рисует кривую от (x1, y1) до (x2, y2). Первая контрольная точка определяет кривизну в (x1, y1). Последняя контрольная точка определяет кривизну в (x2, y2). Особенно интересны внутри «фигур» - свободных форм, где могут быть определены коллекции вершин. Кривые Кэтмулл-Рома size(175, 125); background(0); noFill(); stroke(255); curve(25, 25, // Контрольная точка 1 125, 25, // (x1, y1) 125, 75, // (x2, y1) 25, 75); // Контрольная точка 2 // Нарисуем все точки, чтобы лучше понять, как // это всё работает. strokeWeight(3); stroke(255, 0, 0); point(125, 25); point(125, 75); point(25, 25); point(25, 75); strokeWeight(1); line(25, 25, 125, 25); line(125, 75, 25, 75); Фигуры • • • • • В processing можно рисовать свободные и сложные формы простым перечислением их вершин Основной примитив - vertex(x, y) , который определяет одну из вершин фигуры Чтобы начать фигуру, нужно вызвать beginShape(). Потом через vertex() определяются вершины. Фигуру можно завершить вызовом endShape() . Потрясающая гибкость: между вызовами beginShape() и endShape()может быть любой код (вызовы функций, циклы и пр.) для управления заданием вершин. Однако, не любая функция может быть использована между beginShape() и endShape() . Это касается rotate(), scale() и translate(), об этом будет дальше отдельно. Фигуры • • • Когда beginShape() вызывается без аргументов, она позволяет определять полилинии. Если параметр CLOSE определён внутри endShape() , полилиния автоматически замкнётся: последняя вершина соединится с первой Пример: size(200, 200); background(0); // Фигуры также могут быть охарактеризованы // с помощью функций fill и stroke. // По умолчанию фигуры заполнены, и, // чтобы это отключить, используется noFill(). noFill(); stroke(255); // Полилиния beginShape(); // Теперь мы можем определить вершины ... vertex(25, 25); vertex(175, 25); vertex(175, 175); vertex(25, 175); // Конец фигуры endShape(); Фигуры • С параметром CLOSE фигура будет замкнута: // Полилиния beginShape(); // Теперь мы можем задать вершины ... vertex(25, 25); vertex(175, 25); vertex(175, 175); vertex(25, 175); // Конец фигуры endShape(CLOSE); Фигуры • Можно передать аргумент в функцию beginShape() и задать другую интерпретацию вершин: • • • • • • • POINTS. Вершины рисуются как множество точек LINES. Вершины, по парам, задают линии TRIANGLES. Вершины, группами по 3, задают треугольники. TRIANGLE_STRIP. Лента треугольников. TRIANGLE_FAN. Веер треугольников. QUADS. Вершины, группами по четыре, задают четырёхугольники. QUAD_STRIP. Лента четырёхугольников. Фигуры // Точки beginShape(POINTS); // Теперь мы определяем вершины ... vertex(25, 25); vertex(175, 25); vertex(175, 175); vertex(25, 175); // Конец фигуры endShape(); // Линии beginShape(LINES); // Теперь мы определяем вершины ... vertex(25, 25); vertex(175, 25); vertex(175, 175); vertex(25, 175); // Конец фигуры endShape(); Фигуры // Треугольники beginShape(TRIANGLES); // Теперь мы определяем вершины ... vertex(25, 25); vertex(50, 175); vertex(125, 40); vertex(125, 150); vertex(120, 60); vertex(180, 190); // Конец фигуры endShape(); // Четырёхугольники beginShape(QUADS); // Теперь мы определяем вершины ... vertex(25, 25); vertex(50, 175); vertex(125, 150); vertex(115, 40); vertex(130, vertex(190, vertex(195, vertex(140, 60); 70); 150); 190); // Конец фигуры endShape(); Фигуры TRIANGLE_STRIP QUAD_STRIP TRIANGLE_FAN (Последняя точка должна повторяться) Фигуры • • • • Кроме определения вершин с помощью vertex() в processing есть функции curveVertex()и bezierVertex(), рисующие кривые вместо прямых линий Эти функции работают только с непараметрической версией beginShape() С этими функциями можно создать цепочку кубических кривых Безье или Кэтмулл-Рома: bezierVertex() или curveVertex() Их можно комбинировать с функцией vertex() и последовательно создавать сложные функции Фигуры curveVertex(x, y) • • • • Функция curveVertex() задаёт кривую Кэтмул-Рома, которая может интерполировать любое множество точек Первая и последняя точки, заданные curveVertex() , расцениваются как контрольные, и определяют начальную и конечную кривизну цепочки кривых. Внутренние точки будут входить в интерполяцию множеством кубических кривых Должны быть заданы как минимум 4 вершины через функцию curveVertex(), внутри beginShape() и endShape() Фигуры size(400, 400); background(0); noFill(); stroke(255); // Полилиния beginShape(); // Теперь мы определяем вершины ... curveVertex(0, 0); for (int i = 0; i < 20; i++) { int x = (int)random(400), y = (int)random(400); curveVertex(x, y); // Точка кривой rect(x-2, y-2, 4, 4); // Мы рисуем их, чтобы было проще воспринимать } curveVertex(width-1, height-1); // Конец фигуры endShape(); Фигуры bezierVertex(cx1, cy1, cx2, cy2, x, y) • • • • В цепочке кривых Безье первая точка кривой должна быть определена с помощью функции vertex() Каждый вызов bezierVertex() после первой вершины определит две новые контрольные точки и опорную точку Опорная точка будет начальной точкой в случае новых вызовов bezierVertex() Последняя точка кривой Безье будет первой точкой у следующей. Чтобы обеспечить гладкость (непрерывность) в месте соединения, эти точки должны лежать на одной: последняя контрольная точка первой кривой, первая контрольная точка второй кривой и конечная и начальная точки кривых (на самом деле — одна и та же) Фигуры size(300, 200); background(0); noFill(); stroke(255); // Полилиния beginShape(); vertex(10,100); // Первая точка кривой bezierVertex(70, 140, 160, 140, 150, 100); bezierVertex(140, 60, 210, 60, 290, 100); // // Конец фигуры endShape(); // Рисуем точки, чтобы результат // был лучше виден strokeWeight(5); // Точки кривой stroke(255); point(10, 100); point(150, 100); point(290, 100); // Контрольные точки stroke(255, 0, 0); point(70, 140); point(160, 140); point(140, 60); point(210, 60); // Контрольные точки по линии... strokeWeight(1); line(160, 140, 140, 60); Фигуры // Создание сложных фигур // путём комбинирования линейных и // кривых функций. size(400, 300); background(0); stroke(255); noFill(); // Полилиния beginShape(); vertex(10, 10); vertex(150, 10); bezierVertex(175, 200, 225, 200, 250, 10); vertex(390, 10); vertex(390, 290); vertex(10, 290); vertex(10, 10); endShape(); Практика 2-2 код Практики 1-1 с • Перепишите использованием фигур, чтобы нарисовать n-сторонний многоугольник Практика 2-3 • • • • • • • В целях статистики необходимо строить круговую диаграмму значений в процентах Элементы лежат в массиве, и их сумма равна 100% Пример: float[] values = {25.0, 45.0, 5.0, 15.0, 10.0}; Напишите функцию, получающую этот массив на входе и отображающую эту информацию в виде круговой диаграммы Функция на входе также получает центр и радиус диаграммы Функция может узнать число элементов массива из атрибута length Выберите случайный цвет для каждого сектора Практика 2-4 • • • Напишите функцию, аналогичную функции из Практики 2-3, но теперь рисующую двухмерную столбчатую диаграмму. Возьмите максимально большой размер экрана и нарисуйте координатные оси Ширина столбца будет функцией от общего числа величин и ширины окна; высота столбца — функцией значения соответствующей ему величины и максимального значения среди всех величин, учитывая, что высота столбца с максимальной величиной должна быть равна высоте окна.