My answer satisfies the the hypothesis. What Adrian McCarthy said in his answer is true. My algorithm is based on a similar idea and it is valid. Even in my opinion it is not fair not to take into account the overwriting of a pixel. However, I do not represent horizontal lines of N-1 Pixels, otherwise the triangle, if it is a "splinter" it would not be represented.
For example: suppose we have two adjacent triangles:
ABC [A (27.15) -B (32.15) -C (37.15)];
DEF [A (29.15) -B (32.15) -C (35.15)];
the representation will overlap, but the result should be a horizontal segment of the type:
++-------------------------++
So it is not enough to exclude the last pixel to avoid overwriting.
To represent a filled triangle, so that you can use the function created to represent filled polygons (example: a quadrilateral), since they can always be divided into triangles, it is necessary to be able to exclude the representation of a side, otherwise, it would happen that one side of a triangle is overwritten by the side of the adjacent triangle (problems in displaying transparent polygons).
This, which I propose to you, is the C
implementation of my algorithm for the representation of any type of triangles. I advise you to try it, because it is fast, although quite complex, and efficient. it is my variant of the Bresenham
algorithm. The implementation of the routine representing a horizontal segment, I remaind it to you (i've replaced the call to Put
(Shadow)HorizLine ()
, for the representation of the horizontal segment, with the Line ()
instruction, because my implementation of DrawHLine()
can't be inserted in this post; however here I use Line()
instruction only to draw horizontal segment).
This function initially provided for the use of buffers in RAM in my own format, called OBP, which differs from the RASTER format only for 2 reasons: the scan lines are aligned with the 16 Bytes. there is a 16 Byte header, before the data (8-Bit for each Pixel); this header contains the size of the image in the first 2 Words (in the assembly implementation, you can choose, so whether to make the most of the CPU registers, instead of the variables in RAM, since a 32 Bit register can contain both dimensions and, on medium-sized scales of an image, the geometric position of a point can also be contained in a 32 Bit register).
Here the only thing that needs to be done is to rewrite the call to the Line()
function, due to the fact that to specify the color of one side of the triangle, which may be different from the others, transparent or absent, it is necessary to modify an attribute of an object, instead of passing the color directly as a parameter to the line()
function, although once it was possible to call the SetColor()
function, which is only indicative here.
Her is the header (triangle.h
):
#define R_OBP_Al 16 /* 16 Byte alignment; it must always be 2^N */
#define DimOBP_H 16 /* 16 Byte of OBP HEADER; it must always be >=4 */
#define True 1 /* Boolean value for true condition */
#define False 0 /* Boolean value for false condition */
typedef char TShadowTable[256]; /* Array for shadows */
typedef TShadowTable *T_Shadow_Table_Ptr; /* Pointer to an array for shadows */
typedef struct {short int X;
short int Y;} T_Coord_XY;
typedef struct {unsigned short int X;
unsigned short int Y;} T_Pos_Coord_XY;
typedef struct {unsigned short int DimX;
unsigned short int DimY;} T_Dim_XY;
typedef struct {T_Pos_Coord_XY XY; /* Coordinates of the clipping-region */
T_Dim_XY Dim_XY; /* Dimensions of the clipping-region */ } T_Clipp_Rect;
typedef T_Clipp_Rect *T_Clipp_Rect_Ptr; /* Pointer to clipping-region's type */
typedef struct {T_Coord_XY XY; /* Coordinates of the rectangle */
T_Dim_XY Dim_XY; /* Dimensions of the rectangle */ } T_Rect;
typedef T_Rect *T_Rect_Ptr; /* Pointer to a rectangle */
typedef char Boolean; /* Boolean type */
void Triangle_Wind(short int X1,
short int Y1,
short int X2,
short int Y2,
short int X3,
short int Y3,
short int FillColor,
short int BrdColAB,
short int BrdColBC,
short int BrdColCA
/* , T_Shadow_Table_Ptr ShadowTable,
void *OBPVBuff
T_Clipp_Rect_Ptr Clipp */);
Here is the function and the example (triangle.c
):
#include <graphics.h>
#include <conio.h>
#include <string.h>
#include <stdio.h>
#include "triangle.h"
static int *DefColors[16]=
{0FF000000H, /* Black */
0FF7F0000H, /* Blue */
0FF007F00H, /* Green */
0FF7F7F00H, /* Cyan */
0FF00007FH, /* Red */
0FF7F007FH, /* Magenta */
0FF007F7FH, /* Brown */
0FF7F7F7FH, /* LightGray */
0FF3F3F3FH, /* DarkGray */
0FFFF0000H, /* LightBlue */
0FF00FF00H, /* LightGreen */
0FFFFFF00H, /* LightCyan */
0FF0000FFH, /* LightRed */
0FFFF00FFH, /* LightMagenta */
0FF00FFFFH, /* Yellow */
0FFFFFFFFH /* White */ };
int main(void)
{
/* int gd = DETECT;
int gm;
initgraph(&gd, &gm, "C:\\TC\\BGI"); */
Triangle_Wind(80,80,320,200,160,300,
4,1,2,7);
getch();
/* closegraph(); */
return 0;
}
/* Here it is the body of the triangle routine: */
void Triangle_Wind(short int X1,
short int Y1,
short int X2,
short int Y2,
short int X3,
short int Y3,
short int FillColor,
short int BrdColAB,
short int BrdColBC,
short int BrdColCA
/* , T_Shadow_Table_Ptr ShadowTable,
void *OBPVBuff
T_Clipp_Rect_Ptr Clipp */)
{short int A=0;
short int B=1;
short int C=2; /* Identificat. vertici triangoli per ordinam. colori */
short int C1=BrdColAB;
short int C2=BrdColBC;
short int C3=BrdColCA; /* Var. temp. per i colori */
short int XT; /* X1-XT è il segmento orizzontale da disegnare */
short int OY2; /* Valore iniziale coord. Y 2° vertice del triangolo */
short int B1L;
short int B1H; /* Coord. X 1° e ultimo punto 1° bordo (segm. orizz.) */
short int B2L;
short int B2H; /* Coord. X 1° e ultimo punto 2° bordo (segm. orizz.) */
short int D0; /* Dimensione 1° bordo (segm. orizz.) */
short int D1; /* Dimensione parte centrale segm. orizz. */
short int D2; /* Dimensione 2° bordo (segm. orizz.) */
short int Tmp; /* Variabile temporanea x scambio di 2 variabili */
short int Col1; /* Colore 1° bordo segm. orizz. */
short int Col2; /* Colore 2° bordo segm. orizz. */
short int CntX1; /* Contat. per coord. X 1° punto segm. orizz. (Bresenham) */
short int IncX1; /* Increm. contat. per coord. X 1° punto segm. or. (Bresenham) */
short int CntY1; /* Contat. per coord. Y 1° punto segm. orizz. (Bresenham) */
short int Lim1; /* Limite per contat. coord. X e Y 1° punto segm. or. (Bresenham) */
short int DirX1; /* Increm. coord. X 1° punto segm. orizz. */
short int IncY1; /* Increm. contat. per coord. Y 1° punto segm. or. (Bresenham) */
short int FX1; /* Valore iniziale coord. X1 segm. orizz. X1-XT */
short int CntXT; /* Contat. per coord. X 2° punto segm. orizz. (Bresenham) */
short int IncXT; /* Increm. contat. per coord. X 2° punto segm. or. (Bresenham) */
short int CntYT; /* Contat. per coord. Y 2° punto segm. orizz. (Bresenham) */
short int LimT; /* Limite per contat. coord. X e Y 2° punto segm. or. (Bresenham) */
short int DirXT; /* Increm. coord. X 2° punto segm. orizz. */
short int IncYT; /* Increm. contat. per coord. Y 2° punto segm. or. (Bresenham) */
short int FXT; /* Valore iniziale coord. XT segm. orizz. X1-XT */
T_Rect Segm; /* Record per la rappresentazione di un segm. orizz. */
Boolean F1; /* 1° cond. iniz. (eccezione), rappresentaz. triang. */
Boolean F24; /* 2° cond. iniz. (eccezione), rappresentaz. triang. */
Boolean Overflow=False; /* FALSE: Calcola segm. orizz.; TRUE: Ha finito */
Boolean Internal; /* Variabile temp.; salva il val. iniz. di Overflow */
Boolean Finished=True; /* FALSE: 2° semi-triang.; TRUE: 1° semi-triang.} */
/* Ordina i vertici in base alla coordinata Y */
if (Y1>Y2)
{Tmp=X1;
X1=X2;
X2=Tmp;
Tmp=Y1;
Y1=Y2;
Y2=Tmp;
Tmp=A;
A=B;
B=Tmp;}
if (Y2>Y3)
{Tmp=X2;
X2=X3;
X3=Tmp;
Tmp=Y2;
Y2=Y3;
Y3=Tmp;
Tmp=B;
B=C;
C=Tmp;}
if (Y1>Y2)
{Tmp=X1;
X1=X2;
X2=Tmp;
Tmp=Y1;
Y1=Y2;
Y2=Tmp;
Tmp=A;
A=B;
B=Tmp;}
/* Calcola il colore effettivo dei lati A-B, B-C e C-A del triangolo */
switch (27*A+9*B+C)
{case 19:{BrdColAB=C3;
BrdColCA=C1;
break;}
case 29:{BrdColBC=C3;
BrdColCA=C2;
break;}
case 45:{BrdColAB=C2;
BrdColBC=C3;
BrdColCA=C1;
break;}
case 55:{BrdColAB=C3;
BrdColBC=C1;
BrdColCA=C2;
break;}
case 63:{BrdColAB=C2;
BrdColBC=C1;
break;
}}
/* Calc. incr. e limiti, inizial. i cont. lato A-C (Bresenham) */
DirXT=-1;
IncXT=X1-X3;
if (X1<X3)
{DirXT=1;
IncXT=-IncXT;}
IncXT+=1;
CntXT=IncXT>>1;
IncYT=Y3-Y1+1;
CntYT=IncYT>>1;
LimT=IncXT;
if (IncXT<IncYT)
LimT=IncYT;
/* Imposta i valori iniziali delle var. locali */
XT=X1;
OY2=Y2;
F1=(Y1>=Y2) || (Y2!=Y3);
F24=((Y1!=Y2) || (Y2>=Y3)) &&
((Y1>=Y2) || (Y2>=Y3));
/* Disegna il primo vertice del triangolo */
if ((X1=X2) && (X2=X3) &&
(Y1=Y2) && (Y2=Y3))
{/* Segm->XY->X=X1;
Segm->XY->Y=Y1;
Segm->Dim_XY->DimX=1; */
Col1=BrdColAB;
if (Col1<0)
Col1=BrdColCA;
if (Col1<0)
Col1=FillColor;
if (Col1>=0)
{setcolor(DefColors[Col1]);
line(X1,Y1,X1,Y1);}
/* if (Col1<256)
PutHorizLine(&Segm,OBPVBuff,Col1,Clipp)
else
PutShadowHorizLine(&Segm,OBPVBuff,ShadowTable,Clipp); */}
/* Disegna il triangolo */
do
{/* Calc. incr. e limiti, inizial. i cont. lato A-B (Bresenham) */
DirX1=-1;
IncX1=X1-X2;
if (X1<X2)
{DirX1=1;
IncX1=-IncX1;}
IncX1+=1;
CntX1=IncX1>>1;
IncY1=Y2-Y1+1;
CntY1=IncY1>>1;
Lim1=IncX1;
if (IncX1<IncY1)
Lim1=IncY1;
FX1=X1;
FXT=XT;
/* Rappresenta un semi-triangolo */
while ((X1!=X2) || (Y1!=Y2))
{
/* Calcola i 4 estremi del segmento orizzontale da disegnare */
do
{Internal=Overflow;
if (Overflow)
{CntY1-=Lim1;
CntYT-=LimT;
Y1+=1;}
Overflow=True;
Tmp=CntY1+IncY1;
if (Tmp<Lim1)
{CntY1=Tmp;
CntX1+=IncX1;
if (CntX1>=Lim1)
{CntX1-=Lim1;
X1+=DirX1;}
Overflow=False;}
Tmp=CntYT+IncYT;
if (Tmp<LimT)
{CntYT=Tmp;
CntXT+=IncXT;
if (CntXT>=LimT)
{CntXT-=LimT;
XT+=DirXT;}
Overflow=False;}
if (Internal)
{FX1=X1;
FXT=XT;}
} while (!Overflow);
/* Ordina (ord. ascend.) i 4 estremi del segmento orizzontale */
B1L=FX1;
B1H=X1;
if (B1L>B1H)
{Tmp=B1L;
B1L=B1H;
B1H=Tmp;}
B2L=FXT;
B2H=XT;
if (B2L>B2H)
{Tmp=B2L;
B2L=B2H;
B2H=Tmp;}
Col1=BrdColAB;
Col2=BrdColCA;
if ((B2L<B1L) || (B2H<B1H))
{Tmp=B1L;
B1L=B2L;
B2L=Tmp;
Tmp=B1H;
B1H=B2H;
B2H=Tmp;
Tmp=Col1;
Col1=Col2;
Col2=Tmp;}
/* Calcola la posizione e la dimensione dei 2 bordi del segm. orizz. */
D1=B1H-B1L+1;
D0=B2L-B1H-1;
D2=B2H-B2L+1;
/* Ove possibile, unisce bordi con parte centrale del segm. orizz. */
if (D0>0)
{if (FillColor==Col2) /* Parte0 unita a parte2, parte0 esistente */
{D0+=D2;
D2=0;}
if (Col1==FillColor) /* Parte0 unita a parte1, parte0 esistente */
{B1H=B1L-1;
D0+=D1;
D1=0;}}
else
{D0=0;
if (Col1==Col2) /* Parte1 unita a parte2, parte0 inesistente */
{D1=B2H-B1L+1;
D2=0;}}
/* Rappresenta il primo bordo del segm. orizz. */
/* Segm->XY->Y=Y1;
Segm->XY->X=B1L;
Segm->Dim_XY->DimX=D1; */
if ((Col1>=0) && (D1>0))
{setcolor(DefColors[Col1]);
line(B1L,Y1,B1L+D1-1,Y1);}
/* if (Col1<256)
PutHorizLine(&Segm,OBPVBuff,Col1,Clipp)
else
PutShadowHorizLine(&Segm,OBPVBuff,ShadowTable,Clipp); */
/* Rappresenta la parte centrale del segm. orizz. */
if (((Y1!=OY2) ||
(!Finished || F1) && (Finished || F24)) && (D0>0))
{
/* Segm->XY->X=B1H+1;
Segm->Dim_XY->DimX=D0; */
if ((FillColor>=0) && (D0!=0))
{setcolor(DefColors[FillColor]);
line(B1H+1,Y1,B1H+D0,Y1);}
/* if (FillColor<256)
PutHorizLine(&Segm,OBPVBuff,FillColor,Clipp)
else
PutShadowHorizLine(&Segm,OBPVBuff,ShadowTable,Clipp); */
}
/* Rappresenta il secondo bordo del segm. orizz. */
/* Segm->XY->X=B2L;
Segm->Dim_XY->DimX=D2; */
if ((Col2>=0) && (D2>0))
{setcolor(DefColors[Col2]);
line(B2L,Y1,B2L+D2-1,Y1);}
/* if (Col2<256)
PutHorizLine(&Segm,OBPVBuff,Col2,Clipp)
else
PutShadowHorizLine(&Segm,OBPVBuff,ShadowTable,Clipp); */
}
X2=X3;
Y2=Y3;
BrdColAB=BrdColBC;
Finished=!Finished;
} while (!Finished);
}