Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Matrix.h
- #pragma once
- class vec2
- {
- public:
- float x, y;
- // конструкторы
- vec2() {}
- vec2(float a, float b) : x(a), y(b) {}
- };
- class vec3
- {
- public:
- float x, y, z;
- // конструкторы
- vec3() {}
- vec3(float a, float b, float c) : x(a), y(b), z(c) {}
- vec3(vec2 v, float z) : vec3(v.x, v.y, z) {}
- // перегрузка операций *= , * и []
- vec3& operator *= (const vec3& v)
- {
- x *= v.x;
- y *= v.y;
- z *= v.z;
- return *this;
- }
- const vec3 operator * (const vec3& v)
- {
- return vec3(*this) *= v; // делаем временную копию текущего объекта, которую
- } // домножаем на данный вектор, и возвращаем ее как результат
- float& operator [] (int i)
- {
- return ((float*)this)[i]; // ссылку на текующий объект рассматриваем как ссылку на 0-ой элемент
- } // массива значений типа float, после чего обращаемся к его i-ому элементу
- };
- // скалярное умножение
- float dot(vec3 v1, vec3 v2)
- {
- vec3 tmp = v1 * v2; // вычисляем произведение соответствующих координат
- return tmp.x + tmp.y + tmp.z; // и возвращаем их сумму
- }
- class mat3
- {
- public:
- vec3 row1, row2, row3; // строки матрицы
- // конструкторы
- mat3() {}
- mat3(vec3 r1, vec3 r2, vec3 r3) : row1(r1), row2(r2), row3(r3) {}
- mat3(float a) // диагональная матрица
- {
- row1 = vec3(a, 0.f, 0.f);
- row2 = vec3(0.f, a, 0.f);
- row3 = vec3(0.f, 0.f, a);
- }
- // перегрузка операции []
- vec3& operator [] (int i)
- {
- return ((vec3*)this)[i]; // массив значений типа vec3
- }
- // транспонирование матрицы
- mat3 transpose()
- {
- mat3 tmp(*this); // временная копия матрицы
- for (int i = 0; i < 3; ++i)
- for (int j = 0; j < 3; ++j)
- (*this)[i][j] = tmp[j][i]; // заменяем элементы текущего объекта
- // из временной копии
- return *this;
- }
- // перегрузка оператора *
- const vec3 operator * (const vec3& v)
- {
- vec3* res = new(vec3); // создаем новый вектор-результат
- for (int i = 0; i < 3; ++i)
- (*res)[i] = dot((*this)[i], v); // i-ый элемент веткора - скалярное произведение
- return *res;
- }
- // перемножение матриц (*= и *)
- // 1. B -> B.transpose()
- // 2. С = A * B.transpose()
- // 3. С -> C.transpose()
- mat3& operator *= (const mat3& m)
- {
- mat3 A(*this), B(m); // копии исходных матриц
- B.transpose();
- for (int i = 0; i < 3; ++i)
- (*this)[i] = A * B[i]; // в i-ую строку текущего объекта записываем результат перемножения
- // 1-ой матрицы с i-ой строкой транспонированной матрицы
- return (*this).transpose(); // транспонируем и получаем ответ
- }
- const mat3 operator * (const mat3& m)
- {
- return mat3(*this) *= m;
- }
- };
- vec2 normalize(vec3 v)
- {
- return vec2(v.x / v.z, v.y / v.z);
- }
- // Transform.h
- #pragma once
- #include "Matrix.h"
- #include <math.h>
- // матрица переноса
- mat3 translate(float Tx, float Ty)
- {
- mat3* res = new mat3(1.f); // матрица-результат
- (*res)[0][2] = Tx; // поменяли значения
- (*res)[1][2] = Ty; // в последнем столбце
- return *res;
- }
- // матрица масштабирования
- mat3 scale(float Sx, float Sy)
- {
- mat3* res = new mat3(1.f); // матрица-результат
- (*res)[0][0] = Sx; // поменяли значения
- (*res)[1][1] = Sy; // на главной диагонали
- return *res;
- }
- // перегрузка: Sx = Sy = S
- mat3 scale(float S) { return scale(S, S); }
- // матрица поворота (относительно начала координат против часовой стрелки)
- mat3 rotate(float theta)
- {
- mat3* res = new mat3(1.f); // матрица-результат
- (*res)[0][0] = (*res)[1][1] = (float)cos(theta); // заполнили главную диагональ
- (*res)[0][1] = (float)sin(theta); // синус в 1-ой строке (с плюсом)
- (*res)[1][0] = -(float)sin(theta); // синус во 2-ой строке (с минусом)
- return *res;
- }
- // матрица зеркального отображения относительно оси Ox
- mat3 mirrorX()
- {
- mat3* res = new mat3(1.f);
- (*res)[1][1] = -1.f;
- return *res;
- }
- // матрица зеркального отображения относительно оси Oy
- mat3 mirrorY()
- {
- mat3* res = new mat3(1.f);
- (*res)[0][0] = -1.f;
- return *res;
- }
- // Figure.h
- #pragma once
- #include "Matrix.h"
- #include <vector>
- using namespace std;
- class path
- {
- public:
- vector <vec2> vertices; // последовательность точек
- vec3 color; // цвет, разбитый на составляющие RGB
- float thickness; // толщина линии
- path(vector <vec2> verts, vec3 col, float thickn)
- {
- vertices = verts;
- color = col;
- thickness = thickn;
- }
- };
- class model
- {
- public:
- vector <path> figure; // составляющие рисунка
- mat3 modelM; // модельная матрица (матрица преобразований)
- model(vector <path> fig, mat3 mat) // конструктор
- {
- figure = fig;
- modelM = mat;
- }
- };
- // MyForm.h
- #pragma once
- namespace Moskvitin {
- using namespace System;
- using namespace System::ComponentModel;
- using namespace System::Collections;
- using namespace System::Windows::Forms;
- using namespace System::Data;
- using namespace System::Drawing;
- using namespace std;
- mat3 T = mat3(1.f); // матрица, в которой накапливаются все преобразования
- mat3 initT; // матрица начального преобразования
- vector<model> models;
- float Vx, Vy; // размеры рисунка по горизонтали и вертикали
- float aspectFig; // соотношение сторон рисунка
- /// <summary>
- /// Сводка для MyForm
- /// </summary>
- public ref class MyForm : public System::Windows::Forms::Form
- {
- public:
- MyForm(void)
- {
- InitializeComponent();
- //
- //TODO: добавьте код конструктора
- //
- }
- protected:
- /// <summary>
- /// Освободить все используемые ресурсы.
- /// </summary>
- ~MyForm()
- {
- if (components)
- {
- delete components;
- }
- }
- private: System::Windows::Forms::OpenFileDialog^ openFileDialog;
- protected:
- private: System::Windows::Forms::Button^ btnOpen;
- private:
- /// <summary>
- /// Обязательная переменная конструктора.
- /// </summary>
- System::ComponentModel::Container ^components;
- #pragma region Windows Form Designer generated code
- /// <summary>
- /// Требуемый метод для поддержки конструктора — не изменяйте
- /// содержимое этого метода с помощью редактора кода.
- /// </summary>
- void InitializeComponent(void)
- {
- this->openFileDialog = (gcnew System::Windows::Forms::OpenFileDialog());
- this->btnOpen = (gcnew System::Windows::Forms::Button());
- this->SuspendLayout();
- //
- // openFileDialog
- //
- this->openFileDialog->DefaultExt = L"txt";
- this->openFileDialog->Filter = L"Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*";
- this->openFileDialog->Title = L"Открыть файл";
- //
- // btnOpen
- //
- this->btnOpen->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Right));
- this->btnOpen->Location = System::Drawing::Point(178, 12);
- this->btnOpen->Name = L"btnOpen";
- this->btnOpen->Size = System::Drawing::Size(88, 31);
- this->btnOpen->TabIndex = 0;
- this->btnOpen->Text = L"Открыть";
- this->btnOpen->UseVisualStyleBackColor = true;
- this->btnOpen->Click += gcnew System::EventHandler(this, &MyForm::btnOpen_Click);
- //
- // MyForm
- //
- this->AutoScaleDimensions = System::Drawing::SizeF(9, 20);
- this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
- this->ClientSize = System::Drawing::Size(278, 244);
- this->Controls->Add(this->btnOpen);
- this->KeyPreview = true;
- this->MinimumSize = System::Drawing::Size(230, 170);
- this->Name = L"MyForm";
- this->Text = L"MyForm";
- this->Load += gcnew System::EventHandler(this, &MyForm::MyForm_Load);
- this->Paint += gcnew System::Windows::Forms::PaintEventHandler(this, &MyForm::MyForm_Paint);
- this->KeyDown += gcnew System::Windows::Forms::KeyEventHandler(this, &MyForm::MyForm_KeyDown);
- this->Resize += gcnew System::EventHandler(this, &MyForm::MyForm_Resize);
- this->ResumeLayout(false);
- }
- #pragma endregion
- private: float left = 30, right = 100, top = 20, bottom = 50; // расстояния до границ окна
- float minX = left, maxX; // диапазон изменения координат x
- float minY = top, maxY; // диапазон изменения координат y
- float Wcx = left, Wcy; // координаты левого нижнего угла прямоугольника
- float Wx, Wy; // ширина и высота прямоугольника
- // инициализация остальных параметров
- private: System::Void rectCalc()
- {
- maxX = ClientRectangle.Width - right; // диапазон изменения координат x
- maxY = ClientRectangle.Height - bottom; // диапазон изменения координат y
- Wcy = maxY; // координаты левого нижнего угла прямоугольника
- Wx = maxX - left; // ширина прямоугольника
- Wy = maxY - top;
- }
- private: System::Void MyForm_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
- // описываем переменную g - область рисования, ссылку на объект типа System::Drawing::Graphics^
- Graphics^ g = e->Graphics;
- // черная ручка
- Pen^ rectPen = gcnew Pen(Color::Black, 2);
- // рисуем область видимости
- g->DrawRectangle(rectPen, left, top, Wx, Wy);
- for (int k = 0; k < models.size(); ++k) // цикл по рисункам
- {
- vector<path> figure = models[k].figure; // список ломаных очередного рисунка
- mat3 TM = T * models[k].modelM; // матрица общего преобразования рисунка
- for (int i = 0; i < figure.size(); ++i)
- {
- path lines = figure[i]; // очередная ломаная линия
- Pen^ pen = gcnew Pen(Color::FromArgb(lines.color.x, lines.color.y, lines.color.z));
- pen->Width = lines.thickness;
- vec2 start = normalize(TM * vec3(lines.vertices[0], 1.0)); // начальная точка первого отрезка
- for (int j = 1; j < lines.vertices.size(); j++) // цикл по конечным точкам (от единицы)
- {
- vec2 end = normalize(TM * vec3(lines.vertices[j], 1.0)); // конечная точка
- vec2 tmpEnd = end; // продублировали координаты точки для будущего использования
- if (clip(start, end, minX, minY, maxX, maxY)) { // если отрезок видим
- g->DrawLine(pen, start.x, start.y, end.x, end.y); // отрисовка видимых частей
- } // после отсечения, start и end - концы видимой части отрезка
- start = tmpEnd; // конечная точка неотсеченного отрезка становится начальной точкой следующего
- }
- }
- }
- }
- private: System::Void MyForm_Resize(System::Object^ sender, System::EventArgs^ e) {
- // Добавляем устойчивость рисунка относительно изменения размера окна
- Refresh();
- // передподсчет сторон прямоугольника
- rectCalc();
- }
- private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) {
- rectCalc();
- }
- private: System::Void MyForm_KeyDown(System::Object^ sender, System::Windows::Forms::KeyEventArgs^ e) {
- switch (e->KeyCode)
- {
- // поворот на 0.01 радиан против часовой стрелки
- case Keys::Q:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T; // перенос начала координат в центр области видимости
- T = rotate(0.01f) * T; // поворот
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T; // перенос начала координат обратно
- break;
- // сдвиг изображения вверх на 1 пиксель
- case Keys::W:
- T = translate(0.f, -1.f) * T;
- break;
- // сброс всех преобразований
- case Keys::Escape:
- T = initT;
- break;
- // поворот на 0.01 радиан по часовой стрелке
- case Keys::E:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T; // перенос начала координат в центр области видимости
- T = rotate(-0.01f) * T; // поворот
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T; // перенос начала координат обратно
- break;
- // сдвиг изображения вниз на 1 пиксель
- case Keys::S:
- T = translate(0.f, 1.f) * T;
- break;
- // сдвиг изображения влево на 1 пиксель
- case Keys::A:
- T = translate(-1.f, 0.f) * T;
- break;
- // сдвиг изображения вправо на 1 пиксель
- case Keys::D:
- T = translate(1.f, 0.f) * T;
- break;
- // поворот на 0.05 радиан по часовой стрелке
- case Keys::R:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T; // перенос начала координат в центр области видимости
- T = rotate(-0.05f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T; // перенос начала координат обратно
- break;
- // поворот на 0.05 радиан против часовой стрелки
- case Keys::Y:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T; // перенос начала координат в центр области видимости
- T = rotate(0.05f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T; // перенос начала координат обратно
- break;
- // сдвиг изображения вверх на 10 пикселей
- case Keys::T:
- T = translate(0.f, -10.f) * T;
- break;
- // сдвиг изображения вниз на 10 пикселей
- case Keys::G:
- T = translate(0.f, 10.f) * T;
- break;
- // сдвиг изображения влево на 10 пикселей
- case Keys::F:
- T = translate(-10.f, 0.f) * T;
- break;
- // сдвиг изображения вправо на 10 пикселей
- case Keys::H:
- T = translate(10.f, 0.f) * T;
- break;
- // увеличение изображения в 1.1 раз
- case Keys::Z:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = scale(1.1f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- // уменьшение изображения в 1.1 раз
- case Keys::X:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = scale(10.f / 11.f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- // зеркальное отражения относительно горизонтальной оси, проходящей через центр окна
- case Keys::U:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = mirrorX() * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- // зеркальное отражение относительно вертикальной оси, проходящей через центр окна
- case Keys::J:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = mirrorY() * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- // растяжение изображения по горизонтали в 1.1 раз
- case Keys::I:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = scale(1.1f, 1.f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- // сжатие изображения по горизонтали в 1.1 раз
- case Keys::K:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = scale(10.f / 11.f, 1.f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- // растяжение изображения по вертикали в 1.1 раз
- case Keys::O:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = scale(1.f, 1.1f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- // сжатие изображения по вертикали в 1.1 раз
- case Keys::L:
- T = translate(-Wcx - Wx / 2, -Wcy + Wy / 2) * T;
- T = scale(1.f, 10.f / 11.f) * T;
- T = translate(Wcx + Wx / 2, Wcy - Wy / 2) * T;
- break;
- default:break;
- }
- Refresh();
- }
- private: System::Void btnOpen_Click(System::Object^ sender, System::EventArgs^ e) {
- if (openFileDialog->ShowDialog() == System::Windows::Forms::DialogResult::OK)
- {
- // в файловом диалоге нажата кнопка OK
- // перезапись имени файла из openFileDialog->FileName в fileName
- wchar_t fileName[1024]; // переменная, в которой посимвольно сохраним имя файла
- for (int i = 0; i < openFileDialog->FileName->Length; i++)
- fileName[i] = openFileDialog->FileName[i];
- fileName[openFileDialog->FileName->Length] = '\0';
- // объявление и открытие файла
- ifstream in;
- in.open(fileName);
- if (in.is_open())
- {
- // файл успешно открыт
- models.clear(); // очищаем имеющийся список рисунков
- mat3 M = mat3(1.f); // матрица для получения модельной матрицы
- mat3 initM; // матрица для начального преобразования каждого рисунка
- vector<mat3> transforms; // стек матриц преобразований
- vector<path> figure; // список ломаных очередного рисунка
- // временные переменные для чтения из файла
- float thickness = 2; // толщина со значением по умолчанию 2
- float r, g, b; // составляющие цвета
- r = g = b = 0; // значение составляющих цвета по умолчанию (черный)
- // непосредственно работа с файлом
- string str; // строка, в которую считываем строки файла
- getline(in, str); // считываем из входного файла первую строку
- while (in) // если очередная строка считана успешно
- {
- // обрабатываем строку
- if ((str.find_first_not_of(" \t\r\n") != string::npos) && (str[0] != '#'))
- {
- // прочитанная строка не пуста и не комментарий
- stringstream s(str); // строковый поток из строки str
- string cmd; // переменная для имени команды
- s >> cmd; // считываем имя команды
- if (cmd == "frame") { // размеры изображения
- s >> Vx >> Vy; // считываем глобальные значение Vx и Vy
- aspectFig = Vx / Vy; // обновление соотношения сторон
- // размеры прямоугольника
- float Wx = ClientRectangle.Width - left - right; // ширина прямоугольника
- float Wy = ClientRectangle.Height - top - bottom; // высота прямоугольника
- // соотношение сторон прямоугольника
- float aspectRect = Wx / Wy;
- // смещение центра рисунка с началом координат
- mat3 T1 = translate(-Vx / 2, -Vy / 2);
- // масштабирование остается прежним, меняется только привязка
- // коэффициент увеличения при сохранении исходного соотношения сторон
- float S = aspectFig < aspectRect ? Wy / Vy : Wx / Vx;
- mat3 S1 = scale(S, -S);
- // сдвиг точки привязки из начала координат в нужную позицию
- mat3 T2 = translate(Wx / 2 + Wcx, Wcy - Wy / 2);
- // В initT совмещаем эти три преобразования (справа налево)
- initT = T2 * (S1 * T1);
- T = initT;
- }
- else if (cmd == "color") { // цвет линии
- s >> r >> g >> b; // считываем три составляющие цвета
- }
- else if (cmd == "thickness") { // толщина линии
- s >> thickness; // считываем значение толщины
- }
- else if (cmd == "path") { // набор точек
- vector<vec2> vertices; // список точек ломаной
- int N; // количество точек
- s >> N;
- string str1; // дополнительная строка для чтения из файла
- while (N > 0) { // пока не все точки считали
- getline(in, str1); // считываем в str1 из входного файла очередную строку
- // так как файл корректный, то на конец файла проверять не нужно
- if ((str1.find_first_not_of(" \t\r\n") != string::npos) && (str1[0] != '#')) {
- // прочитанная строка не пуста и не комментарий
- // значит в ней пара координат
- float x, y; // переменные для считывания
- stringstream s1(str1); // еще один строковый поток из строки str1
- s1 >> x >> y;
- vertices.push_back(vec2(x, y)); // добавляем точку в список
- N--; // уменьшаем счетчик после успешного считывания точки
- }
- }
- // все точки считаны, генерируем ломаную (path) и кладем ее в список figure
- figure.push_back(path(vertices, vec3(r, g, b), thickness));
- }
- else if (cmd == "model") { // начало описания нового рисунка
- float mVcx, mVcy, mVx, mVy; // параметры команды model
- s >> mVcx >> mVcy >> mVx >> mVy; // считываем значения переменных
- float S = mVx / mVy < 1 ? 2.f / mVy : 2.f / mVx;
- // сдвиг точки привязки из начала координат в нужную позицию
- // после которого проводим масштабирование
- initM = scale(S) * translate(-mVcx, -mVcy);
- figure.clear();
- }
- else if (cmd == "figure") { // формирование новой модели
- models.push_back(model(figure, M * initM)); // добавляем рисунок в список
- }
- else if (cmd == "translate") { // перенос
- float Tx, Ty; // параметры преобразования переноса
- s >> Tx >> Ty; // считываем параметры
- M = translate(Tx, Ty) * M; // добавляем перенос к общему преобразованию
- }
- else if (cmd == "scale") { // масштабирование
- float S; // параметр масштабирования
- s >> S; // считываем параметр
- M = scale(S) * M; // добавляем масштабирование к общему преобразованию
- }
- else if (cmd == "rotate") { // поворот
- float theta; // угол поворота в градусах
- s >> theta; // считываем параметр
- M = rotate(-theta / 180.f * Math::PI) * M; // добавляем поворот к общему преобразованию
- }
- else if (cmd == "pushTransform") { // сохранение матрицы в стек
- transforms.push_back(M); // сохраняем матрицу в стек
- }
- else if (cmd == "popTransform") { // откат к матрице из стека
- M = transforms.back(); // получаем верхний элемент стека
- transforms.pop_back(); // выкидываем матрицу из стека
- }
- }
- // считываем очередную строку
- getline(in, str);
- }
- Refresh();
- }
- }
- }
- };
- }
- // MyForm.cpp
- #include <fstream>
- #include <sstream>
- #include <vector>
- #include "Matrix.h"
- #include "Transform.h"
- #include "Figure.h"
- #include "Clip.h"
- #include "MyForm.h"
- using namespace System;
- using namespace System::Windows::Forms;
- [STAThreadAttribute]
- void Main(cli::array<String^>^ args) {
- Application::EnableVisualStyles();
- Application::SetCompatibleTextRenderingDefault(false);
- Moskvitin::MyForm form;
- Application::Run(% form);
- }
Add Comment
Please, Sign In to add comment