using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace MMCG_lab3
{
public partial class Form1 : Form
{
List<Point> pointList = new List<Point>(); //основной список точек, в нем лежат все превсе точки
List<Point> tmpList = new List<Point>(); // временный список точек, в нем лежат только точки одной кривой
List<Point> lastList = new List<Point>(); //список точек для составной кривой
Graphics g;
Pen blackBrush;
Pen redBrush;
Pen blueBrush;
int j = 0;
public Form1()
{
InitializeComponent();
g = Graphics.FromHwnd(this.Handle);
blackBrush = new Pen(new SolidBrush(Color.Black), 1f);
redBrush = new Pen(new SolidBrush(Color.Red), 2f);
blueBrush = new Pen(new SolidBrush(Color.Blue), 2f);
flowLayoutPanel1.AutoScroll = true; // рамка в которой отображаются веса, конкретно эта строчка разрешает скролл
}
private void mouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
tmpList.Add(new Point(e.X, e.Y)); // заполняем по клику лист
g.DrawRectangle(blackBrush, e.X, e.Y, 1, 1);
}
if (e.Button == MouseButtons.Right)
{
flowLayoutPanel1.Refresh(); // обновление рамки
if (tmpList.Count > 0)
for (int i = 0; i < tmpList.Count - 1; i++)
{
g.DrawLine(blackBrush, tmpList[i], tmpList[i + 1]);
}
for (int i = 0; i < tmpList.Count; i++)
{
flowLayoutPanel1.Controls.Add(new Label() { Text = "Point № " + (i + 1)}); //заполняет нашу рамку
flowLayoutPanel1.Controls.Add(new TextBox() { Name = i.ToString(), Text = "1" }); // текстБоксами
// количество текстБоксов равно количеству точек
}
}
}
private void castelio(List<Point> P, Pen pen) // Кастельо, ну ты его знаешь
{
int n = P.Count;
Double[] RX = new Double[n];
Double[] RY = new Double[n];
Double[] QX = new Double[n];
Double[] QY = new Double[n];
int c = n;
int oldX = P[0].X;
int oldY = P[0].Y;
int newX, newY;
for (Double t = 0; t < 1; t += 0.001)
{
n = c;
for (int i = 0; i < c; i++)
{
RX[i] = P[i].X;
RY[i] = P[i].Y;
}
while (n > 0)
{
for (int j = 0; j < n - 1; j++)
{
QX[j] = RX[j] + t * (RX[j + 1] - RX[j]);
QY[j] = RY[j] + t * (RY[j + 1] - RY[j]);
}
n--;
for (int j = 0; j < n; j++)
{
RX[j] = QX[j];
RY[j] = QY[j];
}
}
newX = (int)RX[0];
newY = (int)RY[0];
g.DrawLine(pen, oldX, oldY, newX, newY);
oldX = newX;
oldY = newY;
}
}
private void button1_Click(object sender, EventArgs e) // кнопка чистки
{
g.Clear(Color.White);
pointList.Clear();
lastList.Clear();
tmpList.Clear();
flowLayoutPanel1.Controls.Clear();
}
private void button2_Click(object sender, EventArgs e) // составная кривая
{//принцип следующий: из последних двух точек первой кривой и первых двух точек второй кривой мы делаем 2 прямые. Ищем их, прямых,
// точку пересечения, запоминаем ее. Затем по 3 точкам рисуем новую кривую Безье. Эти точки: последняя точка первой кривой, найденная точка пересечения, первая точка второй кривой.
if (tmpList.Count > 1)
{
if (j % 2 == 0) //если это первая кривая, то сохраняем последние ее 2 точки
{
lastList.Add(tmpList[tmpList.Count - 1]);
lastList.Add(tmpList[tmpList.Count - 2]);
g.DrawRectangle(redBrush, lastList[0].X, lastList[0].Y, 1, 1);
g.DrawRectangle(redBrush, lastList[1].X, lastList[1].Y, 1, 1);
}
else // если вторая кривая, то сохраняем первые 2 точки
{
lastList.Add(tmpList[0]);
lastList.Add(tmpList[1]);
g.DrawRectangle(redBrush, getPoint(lastList[0], lastList[1], lastList[2], lastList[3]).X, getPoint(lastList[0], lastList[1], lastList[2], lastList[3]).Y, 1, 1);
List<Point> l = new List<Point>(); // лист для рисования составной кривой
l.Add(lastList[0]);
l.Add(getPoint(lastList[0], lastList[1], lastList[2], lastList[3])); // считаем точку пересечения наших прямых и добавляем в лист
l.Add(lastList[2]);
g.DrawRectangle(redBrush, lastList[2].X, lastList[2].Y, 1, 1);
g.DrawRectangle(redBrush, lastList[3].X, lastList[3].Y, 1, 1);
castelio(l, redBrush); // рисует кастелио
l.Clear();
}
castelio(tmpList, blueBrush);
pointList.AddRange(tmpList);
tmpList.Clear();
j++;
}
}
private void button3_Click(object sender, EventArgs e) // рациональная кривая
{
if (tmpList.Count > 1 && flowLayoutPanel1.Controls.Count > 1) // проверка
{
double[] w = new double[tmpList.Count]; // массив весов
for (int i = 0; i < tmpList.Count; i++) // парсим веса с текстБоксов
{
string value = ((TextBox)flowLayoutPanel1.Controls[i.ToString()]).Text;
w[i] = Double.Parse(value);
}
drawRational(tmpList, w); // запускаем функцию
pointList.AddRange(tmpList);
tmpList.Clear();
}
}
//тут начинается самый сок
private double[] Bernshtein(int n, double u) // тут создается многочлен Бернштейна для нашей кривой
{
double[] B = new double[n];
B[0] = 1;
double u1 = 1 - u, tmp, save;
for (int i = 1; i < n; i++)
{
save = 0;
for (int k = 0; k < i ; k++)
{
tmp = B[k];
B[k] = save + u1 * tmp;
save = u * tmp;
}
B[i] = save;
}
return B;
}
private double[] BasicFunc(int n, double u, double[] weight) // Эта функция домножает многочлен Бернштейна на веса
{
double[] B = new double[n];
double[] result = new double[n];
B = Bernshtein(n, u);
double tmp = 0;
for (int i = 0; i < n; i++)
{
tmp += B[i] * weight[i];
}
for (int i = 0; i < n; i++)
{
result[i] = B[i] * weight[i] / tmp;
}
return result;
}
private void drawRational(List<Point> P, double[] w) // эта рисует кривую
{
int n = P.Count;
List<PointF> result = new List<PointF>();
double[] B = new double[n];
double CX = 0, CY = 0;
for (double i = 0; i <= 1; i+=0.01) // дали точность
{
B = BasicFunc(n, i, w); // получили многочлен
CX = 0;
CY = 0;
for (int j = 0; j < n; j++) // посчитали точку
{
CX = CX + B[j] * P[j].X;
CY = CY + B[j] * P[j].Y;
}
result.Add(new PointF((float)CX, (float)CY)); // запомнили
}
result.Add(P[P.Count - 1]); // еще раз запомнили, чтобы кривая соединилась с последней вершиной.
for (int i = 0; i < result.Count - 1; i++) // отрисовали
{
g.DrawLine(redBrush, result[i], result[i + 1]);
}
}
private Point getPoint(Point ps1, Point pe1, Point ps2, Point pe2) // эта функция тупо считает точку пересечения двух кривых
{ // вот ссылка на литературу http://e-maxx.ru/algo/lines_intersection
float A1 = pe1.Y - ps1.Y;
float B1 = ps1.X - pe1.X;
float C1 = A1 * ps1.X + B1 * ps1.Y;
float A2 = pe2.Y - ps2.Y;
float B2 = ps2.X - pe2.X;
float C2 = A2 * ps2.X + B2 * ps2.Y;
float delta = A1 * B2 - A2 * B1;
if (delta == 0)
throw new System.Exception("Lines are parallel");
return new Point(
(int)((B2 * C1 - B1 * C2) / delta),
(int)((A1 * C2 - A2 * C1) / delta)
);
}
}
}