4 Параллельная проекция
Задача: Написать программу, которая строит на экране изображение выпуклого трехмерного тела при параллельной проекции. Удалить невидимые грани. Видимые грани закрасить оттенками серого цвета в зависимости от расположения соответствующей грани и источника света. Для закрашивания граней использовать метод сканирующей строки [2].
Решение:
Класс:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Collections;
namespace Triangle
{
class FillTriangle
{
public void Fill(Graphics g, int[] x, int[] y, int[] rgb)
{
int maxy, miny;
ArrayList mas = new ArrayList();
float a;
Pen myPen = new Pen(Color.FromArgb(255, rgb[0], rgb[1], rgb[2]));
if (Math.Abs((x[1] - x[0]) * (y[2] - y[0]) - (x[2] - x[0]) * (y[1] - y[0])) <= 0.001)
{
g.DrawLine(myPen, x[0], y[0], x[1], y[1]);
g.DrawLine(myPen, x[1], y[1], x[2], y[2]);
return;
}
maxy = y[0];
miny = maxy;
for (int i = 1; i <= 2; i++)
{
if (y[i] > maxy) maxy = y[i];
if (y[i] < miny) miny = y[i];
}
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[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 (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(mas.Count == 3)
{
g.DrawLine(myPen,(int)mas[0],yt,(int)mas[1],yt);
g.DrawLine(myPen,(int)mas[1],yt,(int)mas[2],yt);
}
else
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;
using System.Drawing.Drawing2D;
namespace Lab_4
{
public partial class Form1 : Form
{
private double[] xv = { 0, 2, 0, 0, 2, 0};
private double[] yv = { 0, 0, 2, 0, 0, 2};
private double[] zv = { 0, 0, 0, 2, 2, 2};
private double[] xpl = new double[6];
private double[] ypl = new double[6];
private int[] xfrm = new int[6];
private int[] yfrm = new int[6];
private int[,] gran1 = new int[3, 4];
private int[,] gran2 = new int[2, 3];
private double u1x, u1y, u1z, u2x, u2y, u2z, u3x, u3y, u3z;
private double rx, ry, rz;
private double ax, ay, az;
private double sx, sy, sz;
private double r;
private double a, b, c, d;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
rx = 10; ry = 5; rz = 5;
ax = 1; ay = 1; az = 1;
sx = -5; sy = 1; sz = 5;
r = Math.Sqrt(3);
u3x = ax - rx; u3y = ay - ry; u3z = az - rz;
double t = Math.Sqrt(u3x * u3x + u3y * u3y + u3z * u3z);
u3x = u3x / t; u3y = u3y / t; u3z = u3z / t;
u1x = u3y; u1y = -u3x; u1z = 0;
t = Math.Sqrt(u1x * u1x + u1y * u1y + u1z * u1z);
u1x = u1x / t; u1y = u1y / t; u1z = u1z / t;
u2x = u1y * u3z - u3y * u1z; u2y = u3x * u1z - u1x * u3z; u2z = u1x * u3y - u3x * u1y;
t = Math.Sqrt(u2x * u2x + u2y * u2y + u2z * u2z);
u2x = u2x / t; u2y = u2y / t; u2z = u2z / t;
gran1[1, 0] = 0; gran1[1, 1] = 1; gran1[1, 2] = 4; gran1[1, 3] = 3;
gran1[0, 0] = 0; gran1[0, 1] = 3; gran1[0, 2] = 5; gran1[0, 3] = 2;
gran1[2, 0] = 1; gran1[2, 1] = 2; gran1[2, 2] = 5; gran1[2, 3] = 4;
gran2[0, 0] = 0; gran2[0, 1] = 2; gran2[0, 2] = 1;
gran2[1, 0] = 3; gran2[1, 1] = 4; gran2[1, 2] = 5;
for (int i = 0; i < xv.Length; 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)
{
return ((x - rx) * u1x + (y - ry) * u1y + (z - rz) * u1z);
}
private double py(double x,double y,double z)
{
return ((x - rx) * u2x + (y - ry) * u2y + (z - rz) * u2z);
}
private void Ini()
{
double wp, hp;
if (ClientSize.Width >= ClientSize.Height)
{
wp = 2 * r * ClientSize.Width / ClientSize.Height; hp = 2 * r;
}
else
{
wp = 2 * r; hp = 2 * r * ClientSize.Height / ClientSize.Width;
}
a = ClientSize.Width / wp;
b = ClientSize.Width / 2;
c = -ClientSize.Height / hp;
d = ClientSize.Height / 2;
}
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];
int[]x=new int[3]; int[]y=new int[3];
double cosfi;
Triangle.FillTriangle myFill=new Triangle.FillTriangle();
for(int i=0;i<=2;i++)
{
bx=xv[gran1[i,1]]-xv[gran1[i,0]];
by=yv[gran1[i,1]]-yv[gran1[i,0]];
bz=zv[gran1[i,1]]-zv[gran1[i,0]];
cx=xv[gran1[i,2]]-xv[gran1[i,0]];
cy=yv[gran1[i,2]]-yv[gran1[i,0]];
cz=zv[gran1[i,2]]-zv[gran1[i,0]];
nx=by*cz-bz*cy;
ny=bz*cx-bx*cz;
nz=bx*cy-by*cx;
if(nx*u3x+ny*u3y+nz*u3z>0)
{
bx=xv[gran1[i,0]]; by=yv[gran1[i,0]];bz=zv[gran1[i,0]];
for(int j=1;j<=3;j++)
{
bx+=xv[gran1[i,j]];by+=yv[gran1[i,j]];bz+=zv[gran1[i,j]];
}
bx=bx/4;by=by/4;bz=bz/4;
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);
rgb[1]=rgb[0]; rgb[2]=rgb[0];
x[0]=xfrm[gran1[i,0]];y[0]=yfrm[gran1[i,0]];
x[1]=xfrm[gran1[i,1]];y[1]=yfrm[gran1[i,1]];
x[2]=xfrm[gran1[i,2]];y[2]=yfrm[gran1[i,2]];
myFill.Fill(g,x,y,rgb);
x[0]=xfrm[gran1[i,0]];y[0]=yfrm[gran1[i,0]];
x[1]=xfrm[gran1[i,2]];y[1]=yfrm[gran1[i,2]];
x[2]=xfrm[gran1[i,3]];y[2]=yfrm[gran1[i,3]];
myFill.Fill(g, x, y, rgb);
}
}
for (int i = 0; i <= 1; i++)
{
bx = xv[gran2[i, 1]] - xv[gran2[i, 0]];
by = yv[gran2[i, 1]] - yv[gran2[i, 0]];
bz = zv[gran2[i, 1]] - zv[gran2[i, 0]];
cx = xv[gran2[i, 2]] - xv[gran2[i, 0]];
cy = yv[gran2[i, 2]] - yv[gran2[i, 0]];
cz = zv[gran2[i, 2]] - zv[gran2[i, 0]];
nx = by * cz - bz * cy;
ny = bz * cx - bx * cz;
nz = bx * cy - by * cx;
if (nx * u3x + ny * u3y + nz * u3z > 0)
{
bx = xv[gran2[i, 0]]; by = yv[gran2[i, 0]]; bz = zv[gran2[i, 0]];
for (int j = 1; j <= 2; j++)
{
bx += xv[gran2[i, j]]; by += yv[gran2[i, j]]; bz += zv[gran2[i, j]];
}
bx = bx / 3; by = by / 3; bz = bz / 3;
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);
rgb[1] = rgb[0]; rgb[2] = rgb[0];
x[0] = xfrm[gran2[i, 0]]; y[0] = yfrm[gran2[i, 0]];
x[1] = xfrm[gran2[i, 1]]; y[1] = yfrm[gran2[i, 1]];
x[2] = xfrm[gran2[i, 2]]; y[2] = yfrm[gran2[i, 2]];
myFill.Fill(g, x, y, rgb);
x[0] = xfrm[gran2[i, 0]]; y[0] = yfrm[gran2[i, 0]];
x[1] = xfrm[gran2[i, 1]]; y[1] = yfrm[gran2[i, 1]];
x[2] = xfrm[gran2[i, 2]]; y[2] = yfrm[gran2[i, 2]];
myFill.Fill(g, x, y, rgb);
}
}
}
private int fx(double x)
{
return(int)(a*x+b);
}
private int fy(double y)
{
return(int)(c*y+d);
}
private void Form1_Resize(object sender, EventArgs e)
{
Ini();
Invalidate();
}
}
}
Рисунок 4.1 Реализация программы, которая строит на экране изображение выпуклого трехмерного тела при параллельной проекции
