Алгоритм плавающего горизонта при построении конуса

Horizont.jpg

Изобразить на экране поверхность, заданную уравнением z=f(x,y) в виде сетки координатных линий x=const, y=const. Использовать алгоритм плавающего горизонта для удаления невидимых линий.

Обычно используют параллельное проектирование, при котором вертикальные линии (параллельные оси z в объектном пространстве) переходят в вертикальные линии на экране. Это достигается с помощью матрицы поворота :

  cos(phi)  sin(phi)sin(psi)  0  0
  sin(phi) -cos(phi)sin(psi)  0  0
  0         cos(psi)          0  0
  0         0                 0  1

Основные идеи метода плавающего горизонта можно сформулировать следующим образом:
Трехмерная задача сводится к двумерной путем пересечения исходной поверхности, последовательностью параллельных секущих плоскостей, имеющих постоянные значения координат выбранной одной (y) или двух (x и y) координатных осей. При этом углы phi и psi, определяющие ракурс, подбираются таким образом, что создаются вполне определенные "отношения экранирования" между линиями поверхности из одного семейства. Это означает, например для семейства y=const, что при yi Тогда возможен следующий алгоритм построения поверхности: линии рисуются в порядке удаления и при рисовании очередной линии рисуется только та ее часть, которая не закрывается ранее нарисованными линиями
В терминах координат (X,Y) картинной плоскости алгоритм имеет вид:
Рассматриваем линии в порядке удаления. Если на текущей кривой при некотором заданном значении X соответствующее значение Y больше максимума или меньше минимума по Y для всех предыдущих кривых при этом X, то текущая кривая видима. В противном случае она невидима.
Линии максимума и минимума играют роли плавающих верхнего и нижнего горизонтов.
Пусть проекцией линии z = f(X, Yk) на картинную плоскость является линия Y = Yk(X) , где (X,Y) - координаты на картинной плоскости, причем Y соответствует вертикальной координате. Каждая из линий представляется в виде ломаной. Для рисования сегментов этой ломаной используется модифицированный алгоритм Брезенхейма, которой перед выводом очередного пикселя сравнивает его ординату с верхней и нижней контурными линиями, представляющими из себя в этом случае массивы значений ординат.

#include<conio.h>
#include<graphics.h>
#include<math.h>
#include<process.h>
#include<stdio.h>
#include<stdlib.h>
#include<dos.h>
 
#define NO_VALUE -777
 
#define MAXX 640
#define MAXY 480
 
int YMax[MAXX];
int YMin[MAXX];
 
int Psi, Phi, Dx, Dy;
 
struct Point
{
 int x;
 int y;
};
 
int UpColor = 9;
int DownColor = 15;
 
 
double f(double x, double y) //функция конуса
{
 double r =  x*x/225 + y*y/64;
 return sqrt(r);
}
 
void DrawLine (Point &p1, Point &p2) {/*рисует сегменты ломаной, используя модифицированный алгоритм Брезенхейма*/
 int dx = abs (p2.x - p1.x);
 int dy = abs (p2.y - p1.y);
 int sx = p2.x >= p1.x ? 1 : -1;
 int sy = p2.y >= p1.y ? 1 : -1;
 if (dy <= dx){
  int d = -dx;
  int d1 = dy << 1;
  int d2 = (dy - dx) << 1;
  for (int x=p1.x, y=p1.y, i=0; i<=dx; i++, x+=sx)
  {
   if (YMin[x] == NO_VALUE){
    putpixel(x,y,UpColor);
    YMin[x] = YMax[x] = y;
   }
   else if (y < YMin[x]){
    putpixel(x,y,UpColor);
    YMin[x] = y;
   }
   else if (y > YMax[x]){
    putpixel(x,y,DownColor);
    YMax[x] = y;
   }
   if (d > 0){
    d += d2;
    y += sy;
   }
   else d += d1;
  }
 }
 else
 {
  int d = -dy;
  int d1 = dx << 1;
  int d2 = ( dx - dy ) << 1;
  int m1 = YMin[p1.x];
  int m2 = YMax[p1.x];
  for (int x=p1.x, y=p1.y, i=0; i<=dy; i++, y+=sy)
  {
   if (YMin[x] == NO_VALUE) {
    putpixel(x,y,UpColor);
    YMin[x] = YMax[x] = y;
   }
   else if (y < m1){
    putpixel(x,y,UpColor);
    if (y < YMin[x])
      YMin[x] = y;
   }
   else if (y > m2){
    putpixel(x,y,DownColor);
    if (y > YMax[x])
      YMax[x] = y;
   }
   if (d > 0){
    d+=d2;
    x+=sx;
    m1 = YMin[x]; m2 = YMax [x];
   }
   else d+=d1;
  }
 }
}
 
void PlotSurface( double x1, double y1, double x2, double y2,
    double fmin, double fmax, int n1, int n2)/*n1 - количество точек по Х, которые делят линию на сегменты; n2 - количество линий*/
{
 Point *CurLine = new Point [n1];
 double phi = Phi*3.1415926/180;
 double psi = Psi*3.1415926/180;
 double sphi = sin(phi);
 double spsi = sin(psi);
 double cphi = cos(phi);
 double cpsi = cos(psi);
 double e1 [] = { cphi, sphi, 0 };
 double e2 [] = { spsi * sphi, -spsi * cphi, cpsi };
 double x,y;
 double hx = (x2 - x1)/n1;
 double hy = (y2 - y1)/n2;
 double xmin = (e1[0] >= 0 ? x1 : x2)*e1[0] +
  (e1[1] >= 0 ? y1 : y2)*e1[1];
 double xmax = (e1[0] >= 0 ? x2 : x1)*e1[0] +
  (e1[1] >= 0 ? y2 : y1)*e1[1];
 double ymin = (e2[0] >= 0 ? x1 : x2)*e2[0] +
  (e2[1] >= 0 ? y1 : y2)*e2[1];
 double ymax = (e2[0] >= 0 ? x2 : x1)*e2[0] +
  (e2[1] >= 0 ? y2 : y1)*e2[1];
 int i, j, k;
 if ( e2[2] >= 0 ){
  ymin += fmin*e2[2];
  ymax += fmax*e2[2];
 }
 else {
  ymin += fmax*e2[2];
  ymax += fmin*e2[2];
 }
 double ax = Dx-(MAXX - 40)*xmin/(xmax - xmin);
 double bx = (MAXX - 40)/(xmax - xmin);
 double ay = Dy-(MAXY - 80)*ymin/(ymax - ymin);
 double by = -(MAXY - 80)/(ymax - ymin);
 for ( i=0; i<sizeof(YMax)/sizeof(int); i++)
  YMin[i] = YMax[i] = NO_VALUE;
 for ( i=n2-1; i>-1; i--){
  for (j=0; j<n1; j++){
   x = x1 + j*hx;
   y = y1 + i*hy;
   CurLine[j].x = (int)(ax + bx*(x*e1[0] + y*e1[1]));
   CurLine[j].y = (int)(ay + by*(x*e2[0] + y*e2[1] +
     f(x,y) * e2[2]));
  }
  for (j=0; j<n1-1; j++)
   DrawLine(CurLine[j], CurLine[j+1]);
 }
 delete []CurLine;
}
 
int main()
{
 
 int dr = DETECT, md;
 int res;
 initgraph ( &dr, &md, "");
 
 Psi = 3;    
 Phi = 320;  //угол поворота
 Dx = 20;    //расположение по x
 Dy = 180;   //по y
 PlotSurface(-5,-5,5,5,-0.2,0.3,1000,100 );
 
 getch();
 closegraph();
 return 0;
}

Ключевые слова: 
алгоритм плавающего горизонта, удаление невидимых линий, уравнение конуса