5 Центральная проекция
Задача: Написать программу, которая строит на экране изображение выпуклого трехмерного тела при центральной проекции. Удалить невидимые грани. Видимые грани закрасить оттенками серого цвета в зависимости от расположения соответствующей грани и источника света. Для закрашивания граней использовать метод сканирующей строки [2].
Решение:
Класс:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Collections;
namespace WindowsFormsApplication1
{
class FillTriangle
{
public void Fill(Graphics g, int[] x, int[] y, int[] rgb)
{
int maxy, miny;
ArrayList mas = new ArrayList();
float a; // Параметр альфа
miny = y[0]; maxy = miny;
for (int i = 1; i < 3; i++)
{
if (y[i] > maxy) maxy = y[i];
if (y[i] < miny) miny = y[i];
}
Pen myPen = new Pen(Color.FromArgb(255, rgb[0], rgb[1], rgb[2]));
for (int yt = miny; yt < maxy; yt++)
{
mas.Clear();
if (y[0] != y[1])
{
a = (float)(yt-y[1]) / (float)(y[0]-y[1]);
if (a>=0&&a<=1)
mas.Insert(0,(int)(a*x[0]+(1-a)*x[1]) );
}
if (y[0] != y[2])
{
a = (float)(yt - y[2]) / (float)(y[0] - y[2]);
if (a >= 0 && a <= 1)
mas.Insert(0, (int)(a * x[0] + (1 - a) * x[2]));
}
if (y[1] != y[2])
{
a = (float)(yt - y[2]) / (float)(y[1] - y[2]);
if (a >= 0 && a <= 1)
mas.Insert(0, (int)(a * x[1] + (1 - a) * x[2]));
}
if (mas.Count == 3)
g.DrawLine(myPen, (int)mas[1], yt, (int)mas[2], yt);
g.DrawLine(myPen, (int)mas[0], yt, (int)mas[1], yt);
}
}
}
}
Форма:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
double[] xv = { 0, 0, 30, 30, 15, 15 };
double[] yv = { 0, -30, -30, 0, -15, -15 };
double[] zv = { 0, 0, 0, 0, 30, -30 };
double[] xpl = new double[6];
double[] ypl = new double[6];
int[] xfrm = new int[6];
int[] yfrm = new int[6];
int[,] gran = new int[8, 3];
double u1x, u1y, u1z, u2x, u2y, u2z, u3x, u3y, u3z;
double rx, ry, rz, ax, ay, az, sx, sy, sz;
double r1, r2, a, b, c, d, fd, rd, gx, gy, gz;
private void Form1_Load(object sender, EventArgs e)
{
ax = 15; ay = -15; az = 0;
fd = 20;
sx = 90; sy = -60; sz = 50;
gx = 100; gy = 30; gz = 20;
r1 = 35;
u3x = ax - gx; u3y = ay - gy; u3z = az - gz;
double m = Math.Sqrt(u3x * u3x + u3y * u3y + u3z * u3z);
u3x = u3x / m; u3y = u3y / m; u3z = u3z / m;
u1x = u3y; u1y = -u3x; u1z = 0;
m = Math.Sqrt(u1x * u1x + u1y * u1y + u1z * u1z);
u1x = u1x / m; u1y = u1y / m; u1z = u1z / m;
u2x = -u3y * u1z + u1y * u3z;
u2y = -u1x * u3z + u3x * u1z;
u2z = -u3x * u1y + u1x * u3y;
m = Math.Sqrt(u2x * u2x + u2y * u2y + u2z * u2z);
u2x = u2x / m; u2y = u2y / m; u2z = u2z / m;
rx = gx + u3x * fd;
ry = gy + u3y * fd;
rz = gz + u3z * fd;
rd=Math.Sqrt(Math.Pow(rx-ax,2)+Math.Pow(ry-ay,2)+Math.Pow(rz-az,2));
r2= r1*fd/(rd+fd);
gran[0, 0] = 1; gran[0, 1] = 4; gran[0, 2] = 0;
gran[1, 0] = 1; gran[1, 1] = 2; gran[1, 2] = 4;
gran[2, 0] = 2; gran[2, 1] = 3; gran[2, 2] = 4;
gran[3, 0] = 3; gran[3, 1] = 0; gran[3, 2] = 4;
gran[4, 0] = 5; gran[4, 1] = 1; gran[4, 2] = 0;
gran[5, 0] = 1; gran[5, 1] = 5; gran[5, 2] = 2;
gran[6, 0] = 5; gran[6, 1] = 3; gran[6, 2] = 2;
gran[7, 0] = 5; gran[7, 1] = 0; gran[7, 2] = 3;
for (int i = 0; i < 6; i++)
{
xpl[i] = px(xv[i], yv[i], zv[i]);
ypl[i] = py(xv[i], yv[i], zv[i]);
}
Ini();
}
private double px(double x, double y, double z)
{
double sc1 = ((x - rx) * u1x + (y - ry) * u1y + (z - rz) * u1z);
double sc2 = ((x - rx) * u3x + (y - ry) * u3y + (z - rz) * u3z);
return fd * sc1 / (sc2 + fd);
}
private double py(double x, double y, double z)
{
double sc1 = ((x - rx) * u2x + (y - ry) * u2y + (z - rz) * u2z);
double sc2 = ((x - rx) * u3x + (y - ry) * u3y + (z - rz) * u3z);
return fd * sc1 / (sc2 + fd);
}
private void Ini()
{
double wp, hp;
if (ClientSize.Width >= ClientSize.Height)
{
wp = 2 * r2 * ClientSize.Width / ClientSize.Height;
hp = 2 * r2;
}
else
{
wp = 2 * r2;
hp = 2 * r2 * ClientSize.Height / ClientSize.Width;
}
a = ClientSize.Width / wp;
b = ClientSize.Width / 2;
c = -ClientSize.Height / hp;
d = ClientSize.Height / 2;
}
private int fx(double x)
{
return (int)(a * x + b);
}
private int fy(double y)
{
return (int)(c * y + d);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.Clear(Color.White);
for (int i = 0; i < xv.Length; i++)
{
xfrm[i] = fx(xpl[i]);
yfrm[i] = fy(ypl[i]);
}
double bx, by, bz, cx, cy, cz, nx, ny, nz;
int[] rgb = new int[3];
double cosfi;
int[] x = new int [3];
int[] y = new int [3];
FillTriangle myFill = new FillTriangle();
for (int i = 0; i < 8; i++)
{
bx = xv[gran[i, 1]] - xv[gran[i, 0]];
by = yv[gran[i, 1]] - yv[gran[i, 0]];
bz = zv[gran[i, 1]] - zv[gran[i, 0]];
cx = xv[gran[i, 2]] - xv[gran[i, 0]];
cy = yv[gran[i, 2]] - yv[gran[i, 0]];
cz = zv[gran[i, 2]] - zv[gran[i, 0]];
nx = by * cz - bz * cy;
ny = bz * cx - bx * cz;
nz = bx * cy - by * cx;
bx = (xv[gran[i, 0]] + xv[gran[i, 1]] + xv[gran[i, 2]]) / 3;
by = (yv[gran[i, 0]] + yv[gran[i, 1]] + yv[gran[i, 2]]) / 3;
bz = (zv[gran[i, 0]] + zv[gran[i, 1]] + zv[gran[i, 2]]) / 3;
cx = bx - gx; cy = by - gy; cz = bz - gz;
if (nx * cx + ny * cy + nz * cz < 0)
{
cx = bx - sx; cy = by - sy; cz = bz - sz;
cosfi = (nx * cx + ny * cy + nz * cz) / (Math.Sqrt(nx * nx + ny * ny + nz * nz) * (Math.Sqrt(cx * cx + cy * cy + cz * cz)));
rgb[0] = (int)(-255 * cosfi / 2 + 255 / 2);//red
rgb[1] = rgb [0]; //green
rgb[2] = rgb[0];//blue
x[0] = xfrm[gran[i, 0]]; y[0] = yfrm[gran[i, 0]];
x[1] = xfrm[gran[i, 1]]; y[1] = yfrm[gran[i, 1]];
x[2] = xfrm[gran[i, 2]]; y[2] = yfrm[gran[i, 2]];
myFill.Fill(g, x, y, rgb);
}
}
}
private void Form1_Resize(object sender, EventArgs e)
{
Ini();
Invalidate();
}
}
}

Рисунок 5.1 Реализация программы, которая строит на экране изображение выпуклого трехмерного тела при центральной проекции
