static class BezierSurface
{
private const int n = 3;// размерность массива
private const int m = 4;// mBezierSurface (3 на 4)
private static Point3D[,] mBezierSurface = new Point3D[n, m]{//Второго порядка
// можно сделать 9, ставь m = 3 и подбирай координаты.
{ new Point3D(0, 0, 0), new Point3D(0, 0, 0.5), new Point3D(0, 0.5, 0), new Point3D(0, 0.5, 0.5) },
{ new Point3D(1, 0, 0), new Point3D(1, 0, 1), new Point3D(1, 1, 0), new Point3D(1, 1, 1) },
{ new Point3D(1, 0, 0), new Point3D(1, 0, 1), new Point3D(1, 1, 0), new Point3D(1, 2, 3) },
};
public class BSplineSurface
{
private double size;
private const int m = 4;// ненужный аппендикс, в топку его
private Point3D start = new Point3D(400, 220, 0);
private double psi;// огорчаешь: углы обзора
private double phi;// на поверхность
public BSplineSurface(double size, double psi, double phi)//конструктор класса
{
this.size = size;
this.psi = psi;
this.phi = phi;
}
Point3D[][] mBSplineSurface = new Point3D[][] {
new Point3D[] { new Point3D(0, 0, 0), new Point3D(0, 0, 0.5), new Point3D(0, 0.5, 0), new Point3D(0, 0.5, 0.5) },
new Point3D[] { new Point3D(0.5, 0, 0), new Point3D(0.5, 0, 0.5), new Point3D(0.5, 0.5, 0), new Point3D(0.5, 0.5, 0.5) },
new Point3D[] { new Point3D(2, 0, 0), new Point3D(2, 0, 2), new Point3D(2, 2, 0), new Point3D(2, 2, 2) }
};
private PointF Project(Point3D p)// метод проецирования трехмерной точки на двумерную плоскость
{
Point3D a1 = new Point3D(Math.Cos(phi), Math.Sin(phi), 0);
Point3D a2 = new Point3D(-1 * Math.Sin(phi) * Math.Sin(psi), Math.Cos(phi) * Math.Sin(psi), Math.Cos(psi));
double x = p.X - start.X;//?
double y = p.Y - start.Y;
double z = p.Z - start.Z;
return new PointF((float)(a1.scalyar(new Point3D(x, y, z)) * size), (float)(a2.scalyar(new Point3D(x, y, z)) * size));
}
public void Show(Graphics g, Pen pen)
{
List<Point3D> polygon = new List<Point3D>();// Лист для точек на сетке
for (double u = 0; u <= 1.0; u += 0.01)
{
for (double v = 0; v <= 1.0; v += 0.01)
{
var pList = new List<Point3D>();// Лист, в котором лежат точки сплайна.
for (int i = 0; i < mBSplineSurface.Length; i++)
{
for (int j = 0; j < mBSplineSurface.Length - 2; j++)
{
pList.Add(new Point3D((float)(0.5f * (1f - v) * (1f - v) * mBSplineSurface[i][j].X + (0.75f - (v - 0.5f) * (v - 0.5f)) * mBSplineSurface[i][j + 1].X + 0.5f * v * v * mBSplineSurface[i][j + 2].X),
(float)(0.5f * (1f - v) * (1f - v) * mBSplineSurface[i][j].Y + (0.75f - (v - 0.5f) * (v - 0.5f)) * mBSplineSurface[i][j + 1].Y + 0.5f * v * v * mBSplineSurface[i][j + 2].Y),
(float)(0.5f * (1f - v) * (1f - v) * mBSplineSurface[i][j].Z + (0.75f - (v - 0.5f) * (v - 0.5f)) * mBSplineSurface[i][j + 1].Z + 0.5f * v * v * mBSplineSurface[i][j + 2].Z)));
}
}
for (int j = 0; j < pList.Count - 2; j++)
{
polygon.Add(new Point3D((float)(0.5f * (1f - u) * (1f - u) * pList[j].X + (0.75f - (u - 0.5f) * (u - 0.5f)) * pList[j + 1].X + 0.5f * u * u * pList[j + 2].X),
(float)(0.5f * (1f - u) * (1f - u) * pList[j].Y + (0.75f - (u - 0.5f) * (u - 0.5f)) * pList[j + 1].Y + 0.5f * u * u * pList[j + 2].Y),
(float)(0.5f * (1f - u) * (1f - u) * pList[j].Z + (0.75f - (u - 0.5f) * (u - 0.5f)) * pList[j + 1].Z + 0.5f * u * u * pList[j + 2].Z)));
polygon.Last().X = (float)(polygon.Last().X + start.X);
polygon.Last().Y = (float)(polygon.Last().Y + start.Y);
polygon.Last().Z = (float)(polygon.Last().Z + start.Z);
}
}
var sub = polygon.Select(x => Project(x)).ToList();// создаем новый лист, в него кладем двухмерные точки.
// .Select(x => Project(x)) эта штука берет каждую точку из листа polygon и прогоняет в методе Project
for (int i = 0; i < sub.Count - 1; i++)
{
if (g.ClipBounds.Contains(sub[i]) && g.ClipBounds.Contains(sub[i + 1]))
{
g.DrawLine(pen, sub[i], sub[i + 1]);
}
}
polygon.Clear();
}
for (double v = 0; v <= 1.0; v += 0.01)
{
for (double u = 0; u <= 1.0; u += 0.01)
{
var pList = new List<Point3D>();
for (int i = 0; i < mBSplineSurface.Length; i++)
{
for (int j = 0; j < mBSplineSurface.Length - 2; j++)
{
pList.Add(new Point3D((float)(0.5f * (1f - v) * (1f - v) * mBSplineSurface[i][j].X + (0.75f - (v - 0.5f) * (v - 0.5f)) * mBSplineSurface[i][j + 1].X + 0.5f * v * v * mBSplineSurface[i][j + 2].X),
(float)(0.5f * (1f - v) * (1f - v) * mBSplineSurface[i][j].Y + (0.75f - (v - 0.5f) * (v - 0.5f)) * mBSplineSurface[i][j + 1].Y + 0.5f * v * v * mBSplineSurface[i][j + 2].Y),
(float)(0.5f * (1f - v) * (1f - v) * mBSplineSurface[i][j].Z + (0.75f - (v - 0.5f) * (v - 0.5f)) * mBSplineSurface[i][j + 1].Z + 0.5f * v * v * mBSplineSurface[i][j + 2].Z)));
}
}
for (int j = 0; j < pList.Count - 2; j++)
{
polygon.Add(new Point3D((float)(0.5f * (1f - u) * (1f - u) * pList[j].X + (0.75f - (u - 0.5f) * (u - 0.5f)) * pList[j + 1].X + 0.5f * u * u * pList[j + 2].X),
(float)(0.5f * (1f - u) * (1f - u) * pList[j].Y + (0.75f - (u - 0.5f) * (u - 0.5f)) * pList[j + 1].Y + 0.5f * u * u * pList[j + 2].Y),
(float)(0.5f * (1f - u) * (1f - u) * pList[j].Z + (0.75f - (u - 0.5f) * (u - 0.5f)) * pList[j + 1].Z + 0.5f * u * u * pList[j + 2].Z)));
polygon.Last().X = (float)(polygon.Last().X + start.X);
polygon.Last().Y = (float)(polygon.Last().Y + start.Y);
polygon.Last().Z = (float)(polygon.Last().Z + start.Z);
}
}
var sub = polygon.Select(x => Project(x)).ToList();// то же самое, что было выше
for (int i = 0; i < sub.Count - 1; i++)
{
if (g.ClipBounds.Contains(sub[i]) && g.ClipBounds.Contains(sub[i + 1]))
{
g.DrawLine(pen, sub[i], sub[i + 1]);
}
}
polygon.Clear();
}
}
}
}
class DrawSurface
{
private Graphics e;
private double a, b, c, d, phi, psi, Sx, Sy;// a,b,c,d - прямоугольник, ограничивающий поверхность. phi/psi - угол обзора, Sx, Sy - масштаб
private int Picture_CODE, BEGINX, BEGINY;//Picture_CODE - номер поверхность для отрисовки, BEGINX, BEGINY - точка, с которой будем рисовать
public DrawSurface(Graphics e, double a1, double b1, double c1, double d1, double phi, double psi, double Sx, double Sy, int Picture_CODE, int BEGINX, int BEGINY)
{
this.e = e;
a = a1;
b = b1;
c = c1;
d = d1;
this.phi = phi;
this.psi = psi;
this.Picture_CODE = Picture_CODE;
this.Sx = Sx;
this.Sy = Sy;
this.BEGINX = BEGINX;
this.BEGINY = BEGINY;
}
public void Draw()
{
List<Point3D> list = new List<Point3D>();
Point3D p = new Point3D();
Point3D a1 = new Point3D(Math.Cos(phi), Math.Sin(phi), 0);
Point3D a2 = new Point3D(-1 * Math.Sin(phi) * Math.Sin(psi), Math.Cos(phi) * Math.Sin(psi), Math.Cos(psi));
double u, v, du, dv, Nu = 20, Nv = 20;//u, v - точки на сетке, du,dv - шаг, Nu, Nv - разбиение
du = Math.Abs((b - a) / Nu);
dv = Math.Abs((d - c) / Nv);
Point temp = new Point(0, 0);
Point3D beg = new Point3D(0, 0, 0), cur = new Point3D(0, 0, 0);// точки на сетке
int u0, v0, v1, u1;// спроецированные точки
for (u = a; u <= b; u += du) // рисование по горизонтали
{
v = c;
beg = Function(u, v, Picture_CODE);
u0 = ((int)(Sx * scalyar(beg, a1)));
v0 = ((int)(Sy * scalyar(beg, a2)));
temp.X = BEGINX + u0;
temp.Y = BEGINY + v0;
for (v = c + dv; v <= d; v += dv)// рисование по горизонтали
{
cur = Function(u, v, Picture_CODE);
u1 = ((int)(Sx * scalyar(cur, a1)));
v1 = ((int)(Sy * scalyar(cur, a2)));
e.DrawLine(new Pen(Color.Black), BEGINX + u1, BEGINY + v1, temp.X, temp.Y);
temp.X = BEGINX + u1;
temp.Y = BEGINY + v1;
}
}
for (v = c; v <= d; v += dv)//// рисование по вертикали
{
u = a;
beg = Function(u, v, Picture_CODE);
u0 = ((int)(Sx * scalyar(beg, a1)));
v0 = ((int)(Sy * scalyar(beg, a2)));
temp.X = BEGINX + u0;
temp.Y = BEGINY + v0;
for (u = a + du; u <= b; u += du)
{
cur = Function(u, v, Picture_CODE);
u1 = ((int)(Sx * scalyar(cur, a1)));
v1 = ((int)(Sy * scalyar(cur, a2)));
e.DrawLine(new Pen(Color.Black), BEGINX + u1, BEGINY + v1, temp.X, temp.Y);
temp.X = BEGINX + u1;
temp.Y = BEGINY + v1;
}
}
}
private double scalyar(Point3D a, Point3D b)
{
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
}
public Point3D Function(double u, double v, int code)//Функция, которая возвращает значение для построения поверхности
{ // u, v положение на сетке(точка), code - номер фигуры, которую хочешь вывести. Дашь 0 - будешь поверхность Безье
switch (code)
{
case 0:
return BezierSurface.drawBezierSurface(u, v);
case 1:
return new Point3D(5 * Math.Cos(u) * Math.Atan(v), 5 * Math.Sin(u) * Math.Tanh(v), 5 * Math.Tanh(u * v));
case 2:
return new Point3D((5 + Math.Cos(u)) * Math.Cos(v), (5 + Math.Cos(u)) * Math.Sin(v), 5 * Math.Sin(u));
default:
return new Point3D(0, 0, 0);
}
}
}
}