
- •Аннотация
- •Введение
- •1. Первый раздел
- •Занятие 1
- •Занятие 2
- •Занятие 3
- •Занятие 4
- •Занятие 5
- •Занятие 6
- •1.2 Формирование траектории для движения простого геометрического объекта
- •Выводы:
- •Второй раздел
- •Индивидуальное Задание 1
- •2.1. Первый подраздел второго раздела Формулировка задания:
- •Математическую постановка:
- •Описание используемых элементов для интерфейса и их настройка (свойства):
- •Кнопки управления
- •Ползунки trackBar
- •Метки Lables
- •Ввод координат
- •Выпадающие списки
- •Меню выбора цветов MenuStrip
- •FillColorToolStripMenuItem — Цвет заливки - Выбор цвета заливки шестиугольника borderColorToolStripMenuItem — Цвет контура - Выбор цвета границы шестиугольника
- •Системные компоненты
- •Описание используемых графических примитивов:
- •Кривая траектории яйца Хюгельшеффера
- •Движущийся объект - шестиугольник
- •Примеры работы с учетом изменения параметров:
- •Выводы:
- •3. Третий раздел индивидуальное Задание 2
- •3.1. Первый подраздел третьего раздела
- •Формулировка задания:
- •Математическая постановка:
- •Описание используемых элементов для интерфейса и их настройка:
- •Общие настройки формы
- •Области отрисовки PictureBox
- •Элемент управления уровнем фрактала — NumericUpDown
- •Метки - lLabels
- •5. Цветовая палитра (внутри программы)
- •Описание используемых графических примитивов:
- •1. Окружности
- •Текст программы:
- •Примеры работы программы:
- •Заключение
Ввод координат
|
textBoxCenterX -ввод координаты Х |
textBoxCenterY - ввод координаты Y |
Свойство |
Значение |
Значение |
Text |
400 |
300 |
Size |
60 x 26 |
60 x 26 |
Location |
X = 1117, Y = 126 |
X = 1117, Y = 164 |
Выпадающие списки
|
comboBoxLineWidth - Выбор толщины линии |
comboBoxLineStyle - Выбор стиля траектории |
ComboBoxWidth - выбор ширины траектории |
Свойство |
Значение |
Значение |
Значение |
Items |
1, 2, 3, 4, 5 |
Непрерывная, Пунктирная, Точка-Тире |
1, 2, 3, 4, 5, 6, 8, 10 |
Location |
X = 1015, Y = 329 |
X = 1197, Y = 329 |
X = 1353, Y = 329 |
DropDownStyle |
DropDownList |
DropDownList |
DropDownList |
Меню выбора цветов MenuStrip
Свойство |
Значение |
Name |
menuStrip1 |
Size |
Width: 1012, Height: 33 |
Items |
"Цвета" — содержит три подпункта: |
FillColorToolStripMenuItem — Цвет заливки - Выбор цвета заливки шестиугольника borderColorToolStripMenuItem — Цвет контура - Выбор цвета границы шестиугольника
trajectoryColorToolStripMenuItem - Цвет траектории - Выбор цвета траектории
Системные компоненты
Thread — отдельный поток animationThread для анимации
List<PointF> — коллекции для хранения координат (eggPoints) и длины (cumulativeDistances)
Описание используемых графических примитивов:
Кривая траектории яйца Хюгельшеффера
Примитив: Graphics::DrawClosedCurve(Pen^, array<PointF>^) - для отрисовки кривой по списку точек.
Точки генерируются с шагом 0.01 вдоль оси Х и преобразуются в координаты, которые образуют кривую, автоматически сглаженную с помощью DrawClosedCurve.
Движущийся объект - шестиугольник
Примитивы:
Graphics::FillPolygon(Brush^, array<PointF>^) — заливка
Graphics::DrawPolygon(Pen^, array<PointF>^) — контур
Рисует правильный шестиугольник с центром с текущей позиции на траектории. Цвет заливки: пользовательский (hexagonFillColor)
Цвет контура: пользовательский (hexagonBorderColor)
Фон
Примитив: Graphics::Clear(Color::White)
Для того чтобы избежать наложения старых изображений при новой отрисовке, фон очищается.
Текст программы:
MyForm.h
#pragma once
#include <cmath>
namespace IDZ1 {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms; //визуалка
using namespace System::Drawing; //отрисовка
using namespace System::Threading; //потоки анимация
public ref class MyForm : public System::Windows::Forms::Form
{ public:
MyForm(void)
{ InitializeComponent(); textBoxCenterX->Text = (pictureBox1->Width / 2).ToString();
textBoxCenterY->Text = (pictureBox1->Height / 2).ToString(); InitializeApp(); }
protected:
~MyForm()
{ if (components)delete components; StopAnimation();}
private:
System::Windows::Forms::PictureBox^ pictureBox1; //перем интерфейса
System::Windows::Forms::Button^ buttonStart;
System::Windows::Forms::Button^ buttonStop;
System::Windows::Forms::Label^ labelTitle;
System::Windows::Forms::Label^ labelSpeed;
System::Windows::Forms::TrackBar^ trackBarSpeed;
System::Windows::Forms::Button^ buttonDirection;
System::Windows::Forms::MenuStrip^ menuStrip1;
System::Windows::Forms::ToolStripMenuItem^ colorsToolStripMenuItem; //подменюшки к заданию вар 4
System::Windows::Forms::ToolStripMenuItem^ fillColorToolStripMenuItem;
System::Windows::Forms::ToolStripMenuItem^ borderColorToolStripMenuItem;
System::ComponentModel::Container^ components;
//ввод центра
System::Windows::Forms::TextBox^ textBoxCenterX;
System::Windows::Forms::TextBox^ textBoxCenterY;
System::Windows::Forms::Label^ labelCenterX;
System::Windows::Forms::Label^ labelCenterY;
System::Windows::Forms::Button^ buttonUpdateCenter;
System::Collections::Generic::List<PointF>^ eggPoints;
System::Collections::Generic::List<float>^ cumulativeDistances;
Thread^ animationThread;
bool isMoving; float currentDistance; float speed; float totalLength; bool clockwise;
Color hexagonFillColor;Color hexagonBorderColor;
System::Windows::Forms::Label^ labelSize;
System::Windows::Forms::TrackBar^ trackBarSize; float hexagonSize;
System::Windows::Forms::ComboBox^ comboBoxLineWidth;
System::Windows::Forms::Label^ labelLineWidth; float borderWidth;
System::Windows::Forms::ComboBox^ comboBoxLineStyle;
System::Windows::Forms::Label^ label1;
System::Drawing::Drawing2D::DashStyle currentLineStyle;
System::Windows::Forms::Button^ buttonRotate;
float hexagonRotationAngle; // угол в градусах bool isRotating;
System::Windows::Forms::Label^ labelRotationSpeed;
System::Windows::Forms::TrackBar^ trackBarRotationSpeed;
bool isPulsating = false; bool pulsateIncreasing = true; float minSize = 10.0f; float maxSize = 50.0f;
float pulsateStep = 0.5f;
System::Windows::Forms::Button^ buttonPulse;
System::Windows::Forms::Label^ labelPulsateSpeed;
System::Windows::Forms::TrackBar^ trackBarPulsateSpeed;float pulsateStep2 = 0.5f; int rotationSpeed;
void InitializeApp()
{eggPoints = gcnew System::Collections::Generic::List<PointF>();
cumulativeDistances = gcnew System::Collections::Generic::List<float>();
isMoving = false; currentDistance = 0.0f;speed = 2.0f; clockwise = true;
hexagonFillColor = Color::Blue; hexagonBorderColor = Color::DarkBlue;
trackBarSpeed->Minimum = 1; trackBarSpeed->Maximum = 10; trackBarSpeed->Value = (int)speed;
trackBarSpeed->TickFrequency = 1; labelSpeed->Text = String::Format("Скорость: {0}", speed);
buttonDirection->Text = L"Против часовой";
hexagonSize = 20.0f; trackBarSize->Minimum = 10; trackBarSize->Maximum = 50;
trackBarSize->Value = (int)hexagonSize;trackBarSize->TickFrequency = 5;
labelSize->Text = String::Format("Размер: {0}", hexagonSize);borderWidth = 2.0f;
currentLineStyle = System::Drawing::Drawing2D::DashStyle::Solid;
//кручение
hexagonRotationAngle = 0.0f; isRotating = false; rotationSpeed = 5;DrawHugelschafferEgg(); }
void DrawHugelschafferEgg()
{if (pictureBox1->Width == 0 || pictureBox1->Height == 0) return;
int centerX, centerY;
if (!Int32::TryParse(textBoxCenterX->Text, centerX)) centerX = pictureBox1->Width / 2;
if (!Int32::TryParse(textBoxCenterY->Text, centerY)) centerY = pictureBox1->Height / 2;
Bitmap^ bmp = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
Graphics^ g = Graphics::FromImage(bmp);
g->Clear(Color::White);
g->SmoothingMode = System::Drawing::Drawing2D::SmoothingMode::AntiAlias;
const float scale = 30.0f;const float step = 0.01f;
eggPoints->Clear();cumulativeDistances->Clear();
GenerateEggPoints(scale, step, centerX, centerY);
if (eggPoints->Count > 0) eggPoints->Add(eggPoints[0]); CalculateCumulativeDistances();DrawCurrentPosition();}
void GenerateEggPoints(float scale, float step, int centerX, int centerY)
{ for (float x = -10.0f; x <= 12.0f; x += step) // увеличенный диапазон AddEggPoint(x, scale, centerX, centerY, -1.0f);
for (float x = 12.0f; x >= -10.0f; x -= step) AddEggPoint(x, scale, centerX, centerY, 1.0f);}
void AddEggPoint(float x, float scale, int centerX, int centerY, float yMultiplier)
{float denominator = 25.0f + 2.0f * x; if (denominator <= 0.0f) return;float numerator = 36.0f - (x - 1.0f) * (x - 1.0f);
if (numerator < 0.0f) return; float y = 4.0f * Math::Sqrt(numerator / denominator) * yMultiplier;
float plotX = centerX + x * scale; float plotY = centerY + y * scale; eggPoints->Add(PointF(plotX, plotY));}
void CalculateCumulativeDistances()
{ cumulativeDistances->Clear(); if (eggPoints->Count == 0) return; cumulativeDistances->Add(0.0f); totalLength = 0.0f;
for (int i = 1; i < eggPoints->Count; i++)
{float dx = eggPoints[i].X - eggPoints[i - 1].X; //ро между точками
float dy = eggPoints[i].Y - eggPoints[i - 1].Y;float distance = (float)Math::Sqrt(dx * dx + dy * dy);
totalLength += distance; cumulativeDistances->Add(totalLength);//в список ее}}
void DrawHexagon(Graphics^ g, PointF center)
{ g->TranslateTransform(center.X, center.Y); g->RotateTransform(hexagonRotationAngle);
g->TranslateTransform(-center.X, -center.Y);
array<PointF>^ points = gcnew array<PointF>(6); //массив 6 точек
for (int i = 0; i < 6; i++){float angle = (float)(Math::PI / 3 * i); points[i] = PointF(
center.X + hexagonSize * (float)Math::Cos(angle), center.Y + hexagonSize * (float)Math::Sin(angle) );}
g->FillPolygon(gcnew SolidBrush(hexagonFillColor), points);
g->DrawPolygon(gcnew Pen(hexagonBorderColor, borderWidth), points);}
void DrawEggWithHexagon(Graphics^ g, PointF hexagonPosition)
{g->Clear(Color::White);
if (eggPoints->Count > 1) { Pen^ eggPen = gcnew Pen(Color::Black, 2); eggPen->DashStyle = currentLineStyle;
g->DrawClosedCurve(eggPen, eggPoints->ToArray()); }
//шестиугольник даже если он за границами DrawHexagon(g, hexagonPosition);}
void DrawCurrentPosition()
{ if (eggPoints->Count == 0 || cumulativeDistances->Count == 0) return;
int segment = FindCurrentSegment(currentDistance);
PointF position = CalculateHexagonPosition(segment, currentDistance);
Bitmap^ bmp = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
Graphics^ g = Graphics::FromImage(bmp);DrawEggWithHexagon(g, position);pictureBox1->Image = bmp;}
void AnimateHexagon()
{while (isMoving)
{ this->Invoke(gcnew Action(this, &MyForm::UpdateHexagonPosition)); //обновление позиции на потоке
Thread::Sleep(30); //чтобы плавненько}}
void UpdateHexagonPosition()
{if (eggPoints->Count == 0 || cumulativeDistances->Count == 0) return; currentDistance += clockwise ? speed : -speed;
if (currentDistance > totalLength) currentDistance -= totalLength;
else if (currentDistance < 0) currentDistance += totalLength;
if (isRotating)
{ hexagonRotationAngle += rotationSpeed;if (hexagonRotationAngle >= 360.0f) hexagonRotationAngle -= 360.0f;}
if (isPulsating)
{if (pulsateIncreasing)
{ hexagonSize += pulsateStep2; if (hexagonSize >= maxSize){hexagonSize = maxSize;pulsateIncreasing = false;}}
else
{hexagonSize -= pulsateStep2;if (hexagonSize <= minSize) {hexagonSize = minSize;pulsateIncreasing = true;}}
labelSize->Text = String::Format("Размер: {0}", hexagonSize);}DrawCurrentPosition(); }
int FindCurrentSegment(float distance) //интервал на котором будем строить
{int segment = 0;while (segment < cumulativeDistances->Count - 1 &&
cumulativeDistances[segment + 1] < distance){ segment++;}return segment;}
PointF CalculateHexagonPosition(int segment, float distance) //текущая координата для объ
{float segStart = cumulativeDistances[segment]; float segEnd = (segment < cumulativeDistances->Count - 1) ?
cumulativeDistances[segment + 1] : totalLength;float t = (distance - segStart) / (segEnd - segStart);
if (segment < eggPoints->Count - 1) //линейная интерполяция
{ return PointF(eggPoints[segment].X + t * (eggPoints[segment + 1].X - eggPoints[segment].X),
eggPoints[segment].Y + t * (eggPoints[segment + 1].Y - eggPoints[segment].Y)); }return eggPoints[0];}
void StartAnimation()
{if (!isMoving && eggPoints->Count > 0)
{isMoving = true;
animationThread = gcnew Thread(gcnew ThreadStart(this, &MyForm::AnimateHexagon)); //поток
animationThread->Start();} }
void StopAnimation()
{isMoving = false;
if (animationThread != nullptr){animationThread->Join(); //завершение и сбросanimationThread = nullptr;}}
void UpdateHexagonColors()
{ if (!isMoving && eggPoints->Count > 0){DrawCurrentPosition();}}
void SetFillColor(Color color)
{hexagonFillColor = color;UpdateHexagonColors(); }
void SetBorderColor(Color color)
{ hexagonBorderColor = color;UpdateHexagonColors();}
Void comboBoxLineStyle_SelectedIndexChanged(System::Object^ sender, System::EventArgs^ e) {
switch (comboBoxLineStyle->SelectedIndex) {
case 0: // Непрерывная currentLineStyle = System::Drawing::Drawing2D::DashStyle::Solid;break;
case 1: // Пунктирная currentLineStyle = System::Drawing::Drawing2D::DashStyle::Dash; break;
case 2: // Точка-Тире currentLineStyle = System::Drawing::Drawing2D::DashStyle::DashDotDot;break;
default: currentLineStyle = System::Drawing::Drawing2D::DashStyle::Solid;break;} DrawCurrentPosition(); }
pragma endregion обработчки событий
private:
System::Void buttonStart_Click(System::Object^ sender, System::EventArgs^ e) {
StartAnimation();}
System::Void buttonStop_Click(System::Object^ sender, System::EventArgs^ e) {
StopAnimation(); }
System::Void trackBarSpeed_ValueChanged(System::Object^ sender, System::EventArgs^ e) {
speed = trackBarSpeed->Value;
labelSpeed->Text = String::Format("Скорость: {0}", speed); }
System::Void buttonDirection_Click(System::Object^ sender, System::EventArgs^ e) {
clockwise = !clockwise;
buttonDirection->Text = clockwise ? L"Против часовой" : L"По часовой";
DrawCurrentPosition(); }
System::Void fillColorToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
ColorDialog^ colorDialog = gcnew ColorDialog();
colorDialog->Color = hexagonFillColor;
if (colorDialog->ShowDialog() == System::Windows::Forms::DialogResult::OK)
SetFillColor(colorDialog->Color);}
System::Void borderColorToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
ColorDialog^ colorDialog = gcnew ColorDialog();
colorDialog->Color = hexagonBorderColor;
if (colorDialog->ShowDialog() == System::Windows::Forms::DialogResult::OK)
SetBorderColor(colorDialog->Color); }
System::Void trackBarSize_ValueChanged(System::Object^ sender, System::EventArgs^ e) {
hexagonSize = trackBarSize->Value;
labelSize->Text = String::Format("Размер: {0}", hexagonSize);
DrawCurrentPosition(); }
System::Void buttonUpdateCenter_Click(System::Object^ sender, System::EventArgs^ e) {
StopAnimation();
currentDistance = 0.0f;
DrawHugelschafferEgg();}
System::Void comboBoxLineWidth_SelectedIndexChanged(System::Object^ sender, System::EventArgs^ e) {
int selected = comboBoxLineWidth->SelectedIndex + 1;
borderWidth = (float)selected;
DrawCurrentPosition(); }
System::Void buttonRotate_Click(System::Object^ sender, System::EventArgs^ e) {
isRotating = !isRotating;
buttonRotate->Text = isRotating ? L"Стоп вращение" : L"Вращать"; }
System::Void trackBarRotationSpeed_ValueChanged(System::Object^ sender, System::EventArgs^ e) {
rotationSpeed = trackBarRotationSpeed->Value;
labelRotationSpeed->Text = String::Format("Скорость вращения: {0}", rotationSpeed); }
System::Void buttonPulse_Click(System::Object^ sender, System::EventArgs^ e) {
isPulsating = !isPulsating;
buttonPulse->Text = isPulsating ? L"Без пульсации" : L"Пульсация"; }
System::Void trackBarPulsateSpeed_ValueChanged(System::Object^ sender, System::EventArgs^ e)
{ pulsateStep2 = trackBarPulsateSpeed->Value * 0.1f;
labelPulsateSpeed->Text = String::Format("Скорость пульсации: {0:F1}", pulsateStep2);}};}
Main.cpp
#include "MyForm.h"
using namespace System;
using namespace System::Windows::Forms;
[STAThread]
int main(array<String^>^ args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
IDZ1::MyForm form;
Application::Run(% form);
return 0;
}