⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ptriangle.cpp

📁 这是我做的一个VC++小程序
💻 CPP
字号:
/////////////////////////////////////////////////////////////////////////////////
//	
// PTriangle.cpp: implementation of the CPTriangle class.
//
////////////////////////////////////////////////////////////////////////////////
// 版权所有(2002)
// Copyright(2002)
// 编写者: 向世明
// Author: Xiang Shiming

#include "stdafx.h"
#include "PTriangle.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPTriangle::CPTriangle(){}

CPTriangle::~CPTriangle(){}

//用指定的笔和画刷绘制并填充三角形.
void CPTriangle::Fill(CDC* pDC,  POINT pt1,  POINT pt2,  POINT pt3,  CPen& pen, CBrush& brush)
{
	POINT* pPts = new POINT[3];
	pPts[0] = pt1;
	pPts[1] = pt2;
	pPts[2] = pt3;
	CPen* pOldPen = pDC->SelectObject(&pen);
	CBrush* pOldBrush = pDC->SelectObject(&brush);
	pDC->Polygon(pPts, 3);
	pDC->SelectObject(pOldPen);
	pDC->SelectObject(pOldBrush);
	delete[] pPts;
}

//采用平面明暗着色方法填充三角形, 
//一般地, 采用三角形的第一个顶点作为填充颜色
//整个三角形被填充成单一颜色.
void CPTriangle::Flat(CDC* pDC,  POINT pt1,  POINT pt2,  POINT pt3, COLORREF clr)
{

	POINT* pPts = new POINT[3];
	pPts[0] = pt1;
	pPts[1] = pt2;
	pPts[2] = pt3;
	CPen pen(PS_SOLID, 1, clr);
	CBrush brush(clr);
	CPen* pOldPen = pDC->SelectObject(&pen);
	CBrush* pOldBrush = pDC->SelectObject(&brush);
	pDC->Polygon(pPts, 3);
	pDC->SelectObject(pOldPen);
	pDC->SelectObject(pOldBrush);
	delete[] pPts;
}

//采用Gouraud明暗着色方法填充三角形
void CPTriangle::Gouraud(CDC* pDC,  POINT pt1,  POINT pt2,  POINT pt3,  FLOATCOLORRGB clr1, FLOATCOLORRGB clr2, FLOATCOLORRGB clr3)
{
	
	//第一部分代码:处理退化情况中的一种情况

	//处理退化情况(三点在一条水平直线上)
	if((pt1.y == pt2.y) && (pt2.y == pt3.y))
	{
		//pt1位于中间(采用矢量点积进行判断)
		if((pt2.x - pt1.x) * (pt3.x - pt1.x) <= 0)
		{
			Scan(pDC, pt1.x, pt2.x, pt1.y, clr1, clr2);
			Scan(pDC, pt1.x, pt3.x, pt1.y, clr1, clr3);
		}
		
		//pt2位于中间
		else if((pt1.x - pt2.x) * (pt3.x - pt2.x) <= 0)
		{
			Scan(pDC, pt2.x, pt1.x, pt1.y, clr2, clr1);
			Scan(pDC, pt2.x, pt3.x, pt1.y, clr2, clr3);
			
		}
		//pt3位于中间
		else 
		{
			Scan(pDC, pt3.x, pt1.x, pt1.y, clr3, clr1);
			Scan(pDC, pt3.x, pt2.x, pt1.y, clr3, clr2);
		}
		//三个端点也应该绘制
		pDC->SetPixelV(pt1, RGB((BYTE)(clr1.red * 255.0f), (BYTE)(clr1.green * 255.0f), (BYTE)(clr1.blue * 255.0f)));
		pDC->SetPixelV(pt2, RGB((BYTE)(clr2.red * 255.0f), (BYTE)(clr2.green * 255.0f), (BYTE)(clr2.blue * 255.0f)));
		pDC->SetPixelV(pt3, RGB((BYTE)(clr3.red * 255.0f), (BYTE)(clr3.green * 255.0f), (BYTE)(clr3.blue * 255.0f)));
		return;
	}

	/////////////////////////////////////////////////////////////////////////////////////////////////
	
	//第二部分代码:处理一般情况

	//第一步, 排序:
	//排序规则:第一个点的y坐标最小, 第二个点的y坐标最大, 第三个点则位于它们的中间
	//顶点和颜色备份

	POINT point1 = pt1,  point2 = pt2,  point3 = pt3;
	FLOATCOLORRGB color1 = clr1,  color2 = clr2,  color3 = clr3;

	//point1记录y坐标最小者
	if(point1.y > point2.y)
	{
		point1 = pt2;
		point2 = pt1;
		color1 = clr2;
		color2 = clr1;
	}
	if(point1.y > point3.y) 
	{
		POINT ptTemp = point1;
		FLOATCOLORRGB clrTemp = color1;
		point1 = point3;
		color1 = color3;
		point3 = ptTemp;
		color3 = clrTemp;
	}
	//point2记录y坐标最大者
	if(point2.y < point3.y)
	{
		POINT ptTemp = point2;
		FLOATCOLORRGB clrTemp = color2;
		point2 = point3;
		color2 = color3;
		point3 = ptTemp;
		color3 = clrTemp;
	}


	//第二步, 获取边界点的 x 坐标, 计算边界颜色
	//12 --- 1 to 2,  13 ---- 1 to 3,   32 ---- 3 to 2
	//y坐标之间的距离
	int nDy12 = (point2.y - point1.y) + 1;
	int nDy13 = (point3.y - point1.y) + 1;
	int nDy32 = (point2.y - point3.y) + 1;
	//获取扫描线的 x 坐标
	int* pnEdge12 = new int[nDy12];
	int* pnEdge13 = new int[nDy13];
	int* pnEdge32 = new int[nDy32];
		
	//获取边界颜色
	//注意, 这里不支持透明计算, 因而颜色用三个分量
	FLOATCOLORRGB* pClrEdge12 = new FLOATCOLORRGB[nDy12];
	FLOATCOLORRGB* pClrEdge13 = new FLOATCOLORRGB[nDy13];
	FLOATCOLORRGB* pClrEdge32 = new FLOATCOLORRGB[nDy32];

	//获取边界及其颜色

	//计算矢量叉积(只需要计算Z分量), 以判断三角形的位置关系
	int nDelta = ((point3.x - point1.x) * (point2.y - point3.y)
				 - (point2.x - point3.x) * (point3.y - point1.y));

	if(nDelta > 0)
	{
		DealEdge(pDC,  point1.x,  point1.y,  point2.x,  point2.y, color1,  color2,  pnEdge12, pClrEdge12); 
		DealEdge(pDC,  point1.x,  point1.y,  point3.x,  point3.y, color1,  color3,  pnEdge13, pClrEdge13,  G3D_TRIANGLE_ON_LINE_LEFT); 
		DealEdge(pDC,  point3.x,  point3.y,  point2.x,  point2.y, color3,  color2,  pnEdge32, pClrEdge32,  G3D_TRIANGLE_ON_LINE_LEFT); 
	}
	else
	{
		DealEdge(pDC,  point1.x,  point1.y,  point2.x,  point2.y, color1,  color2,  pnEdge12,  pClrEdge12,  G3D_TRIANGLE_ON_LINE_LEFT); 
		DealEdge(pDC,  point1.x,  point1.y,  point3.x,  point3.y, color1,  color3,  pnEdge13,  pClrEdge13);
		DealEdge(pDC,  point3.x,  point3.y,  point2.x,  point2.y, color3,  color2,  pnEdge32,  pClrEdge32);
	}

	//保证最后一个点也能绘制
	//这是因为, CPLine基元与Windows绘制直线的方式是一致的, 即不绘制最后一个点
	pDC->SetPixelV(point2, RGB((BYTE)(color2.red * 255.0f),  (BYTE)(color2.green * 255.0f),  (BYTE)(color2.blue * 255.0f)));
	if(point3.y == point2.y)
		pDC->SetPixelV(point3, RGB((BYTE)(color3.red * 255.0f),  (BYTE)(color3.green * 255.0f),  (BYTE)(color3.blue * 255.0f)));

	
	//第三步, 进行扫描填充
	//扫描分为两个阶段:
	//第一, 扫描第一点至第三点之间的区域
	//第二, 扫描第三点至第二点之间的区域
	//当前扫描线的y坐标及扫描计数器
	int y,  nPlumbScan = 0;
	if(point1.y < point3.y)
	{
		for(y = point1.y; y < point3.y; y++)
		{
			//一条扫描线的x坐标
			int	x12 = pnEdge12[nPlumbScan];
			int	x13 = pnEdge13[nPlumbScan];
			//颜色
			FLOATCOLORRGB clrEdge12 = pClrEdge12[nPlumbScan];
			FLOATCOLORRGB clrEdge13 = pClrEdge13[nPlumbScan];
			//填充
			//
			Scan(pDC, x12, x13, y, clrEdge12, clrEdge13);
			nPlumbScan++;
		}//end for y
	
	}//end (point1.y < point3.y)
	
	//第二阶段扫描
	//准备数据
	//沿边3-2的计数器
	int nPlumbScan32 = 0;
	for(y = point3.y; y <= point2.y; y++)
	{
		//一条扫描线的x坐标
		int	x12 = pnEdge12[nPlumbScan];
		int	x32 = pnEdge32[nPlumbScan32];
		//颜色
		FLOATCOLORRGB clrEdge12 = pClrEdge12[nPlumbScan];
		FLOATCOLORRGB clrEdge32 = pClrEdge32[nPlumbScan32];

		//填充
		Scan(pDC, x12, x32, y, clrEdge12, clrEdge32);

		nPlumbScan++;
		nPlumbScan32++;

	}//end for y

	delete[] pnEdge12;
	delete[] pnEdge13;
	delete[] pnEdge32;
	delete[] pClrEdge12;
	delete[] pClrEdge13;
	delete[] pClrEdge32;

}

//着色一条扫描线, 扫描坐标的x坐标为x1和x2,  y坐标为nScanLine, 
//clr1和clr2分别为起点和终点的颜色.
void CPTriangle::Scan(CDC* pDC, int x1, int x2, int nScanLine, FLOATCOLORRGB clr1, FLOATCOLORRGB clr2)
{
	//填充像素个数 
	int nFillPixels = ABS(x2 - x1);
	//如果为同一个点或距离相差为0, 则返回;
	if(nFillPixels < 2) return;

	//填充方向
	int nDirection = (x1 < x2) ? 1 : -1;

	
	//颜色增量(颜色差值 / 份数)
	float fIncR = (clr2.red - clr1.red) / nFillPixels;
	float fIncG = (clr2.green - clr1.green) / nFillPixels;
	float fIncB = (clr2.blue - clr1.blue) / nFillPixels;

	//颜色
	float fRed = clr1.red + fIncR;
	float fGreen = clr1.green + fIncG;
	float fBlue = clr1.blue + fIncB;
	
	//当前x坐标
	int x = x1 + nDirection;
	nFillPixels--;
	//开始填充
	for(int i = 0; i < nFillPixels ; i++)
	{
		pDC->SetPixelV(x, nScanLine, RGB((BYTE)(fRed * 255.0f), (BYTE)(fGreen * 255.0f), (BYTE)(fBlue * 255.0f)));
		x += nDirection;
		fRed += fIncR;
		fGreen += fIncG;
		fBlue += fIncB;
	}
} 

//获取直线边界上的点的x坐标
//pnCoord--------记录扫描线与边的交点的x坐标,
//pnCoord--------pnCoord数组的长度应该是y坐标差的绝对值加1
//pClrEdge-------记录各交点的颜色。
//pClrEdge-------其长度与pnCoord数组的长度相同
//这两个指针分别指向int型和FLOATCOLORRGB数组。

//x1, y1, x2, y2----直线两端点坐标
//clr1, clr2------直线起点和终点颜色
//dwFeature------是一个边界记录特征量。当其为TRIANGLE_AREA_ON_LINE_RIGHT时,表示区域在直线右侧;当其为TRIANGLE_AREA_ON_LINE_LEFT时, 表示区域在其左侧。
//程序缺省设置为区域在直线的右侧。
//注意, 该函数同时绘制边界, 但仍然不包含最后一个点
void CPTriangle::DealEdge(CDC* pDC,  int x1, int y1, int x2, int y2, FLOATCOLORRGB clr1, FLOATCOLORRGB clr2, int* pnCoord, FLOATCOLORRGB* pClrEdge, DWORD dwFeature)
{
	//pnCoord数组的长度应该是y坐标差的绝对值加1, 
	ASSERT(pnCoord);
	ASSERT(pClrEdge);

	//对于一条水平直线, 则简单地返回第一个点的坐标和颜色
	//对于一条水平直线, 我们不区分区域相对它的左右关系.
	//该边界本身已经是一条扫描线, 只记录不绘制
	if(y1 == y2)
	{
		pnCoord[0] = x1;
		pClrEdge[0] = clr1;
		return;
	}

	//下面考查直线斜率不为0的情况:

	//两端点间的水平偏移量和垂直偏移量
	int nDx = x2 - x1;	
	int nDy = y2 - y1;

	//两端点间的水平距离和垂直距离
	int nIx = ABS(nDx);
	int nIy = ABS(nDy);

	//描点步数(增量总值)
	int nInc = MAX(nIx, nIy);
	
	//用于判断是否在nJudgeX, nJudgeY方向上向前进
	int nJudgeX = -nIy,  nJudgeY = -nIx;

	//通过增量计算得到的当前点
	int x = x1,  y = y1;

	//Bresenham算法
	int nTwoIx = 2 * nIx,  nTwoIy = 2 * nIy;

	//直线当前点的颜色
	float fPlotR = clr1.red,  fPlotG = clr1.green,  fPlotB = clr1.blue; 
	
	//颜色增量
	float fIncR = (clr2.red - fPlotR) / nInc;
	float fIncG = (clr2.green - fPlotG) / nInc;
	float fIncB = (clr2.blue - fPlotB) / nInc;

	//数组下标,水平边界段起点和终点的 x 坐标
	int nIndex = 0,  nStartX = x1,  nEndX = x1;

	//水平边界段起点和终点
	FLOATCOLORRGB clrStart = {fPlotR, fPlotG, fPlotB};
	FLOATCOLORRGB clrEnd = clrStart;

	//////////////////////////////////////////////////////
	//绘制第一个点
	pDC->SetPixelV(x1, y1, RGB((BYTE)(fPlotR * 255.0f), (BYTE)(fPlotG * 255.0f), (BYTE)(fPlotB * 255.0f)));
	//////////////////////////////////////////////////////
	
	//开始增量计算
	for(int i = 0;i <= nInc; i++)
	{
		nJudgeX += nTwoIx;
		nJudgeY += nTwoIy;

		//通过增量法计算的当前点是否属于直线上的点
		BOOL bRecord = FALSE;
		
		//当前点是否为直线上的点
		BOOL bPlot = FALSE;

		//先检测 y 方向
		if(nJudgeY >= 0)
		{
			bPlot = TRUE;
			bRecord = TRUE;
			nJudgeY -= nTwoIx;

			//将任意走向的直线统一起来
			if(nDy > 0)
			{
				nEndX = x;
				y++;
				//水平边界段终点的颜色
				clrEnd.red = fPlotR;
				clrEnd.green = fPlotG;
				clrEnd.blue = fPlotB;

			}
			else if(nDy < 0)
			{
				nEndX = x;
				y--;
				//水平边界段终点的颜色
				clrEnd.red = fPlotR;
				clrEnd.green = fPlotG;
				clrEnd.blue = fPlotB;
			}
		}//end if 

		//后检测 x 方向
		if(nJudgeX >= 0)
		{
			bPlot = TRUE;
			nJudgeX -= nTwoIy;

			//将任意走向的直线统一起来
			if(nDx > 0)x++;
			else if(nDx < 0)x--;
		}

		if(bPlot)
		{
			fPlotR += fIncR;
			fPlotG += fIncG;
			fPlotB += fIncB;
			
			//绘制该点
			//保证最后的两个点不绘制, 
			//这两个点的其中一个为直线的终点, 另外一个点(循环最后的一个点(x, y))完全不是直线上的点
			if(i < (nInc - 1))
				pDC->SetPixelV(x, y, RGB((BYTE)(fPlotR * 255.0f), (BYTE)(fPlotG * 255.0f), (BYTE)(fPlotB * 255.0f)));
		}

		//如果当前点在直线上, 则记录当前点
		if(bRecord)
		{
			//取最左边点, 区域在左侧
			if(dwFeature == G3D_TRIANGLE_ON_LINE_LEFT)
			{
				//取x坐标最小者
				if(nStartX < nEndX)
				{
					pnCoord[nIndex] = nStartX;	
					pClrEdge[nIndex] = clrStart;
				}
				else
				{
					pnCoord[nIndex] = nEndX;	
					pClrEdge[nIndex] = clrEnd;
				}
			}//end if
				
			//取最右边点, 区域在右侧
			else
			{
				//取x坐标最大者
				if(nStartX < nEndX)
				{
					pnCoord[nIndex] = nEndX;	
					pClrEdge[nIndex] = clrEnd;
				}
				else
				{
					pnCoord[nIndex] = nStartX;	
					pClrEdge[nIndex] = clrStart;
				}
			}//end else
			
			nIndex++;

			//下一个起点
			nStartX = x;

			//下一个起点的颜色
			clrStart.red = fPlotR;
			clrStart.green = fPlotG;
			clrStart.blue = fPlotB;
		}//end if(bRecord)

	}//end for

	//当斜率的很小时, y坐标增长缓慢, 这时需要特别处理
	if(nIndex < (ABS(y2 - y1) + 1))
	{
		//取最左边点, 区域在左侧
		if(dwFeature == G3D_TRIANGLE_ON_LINE_LEFT)
		{
			//取x坐标最小者
			if(nStartX < x2)
			{
				pnCoord[nIndex] = nStartX;	
				pClrEdge[nIndex] = clrStart;
			}
			else
			{
				pnCoord[nIndex] = x2;
				pClrEdge[nIndex] = clr2;
			}

		}//end if
		//取最右边点, 区域在右侧
		else
		{
			//取x坐标最大者
			if(nStartX < x2)
			{
				pnCoord[nIndex] = x2;	
				pClrEdge[nIndex] = clr2;
			}
			else
			{
				pnCoord[nIndex] = nStartX;	
				pClrEdge[nIndex] = clrStart;
			}
		}//end else
	}//end if(nIndex < (ABS(y2 - y1) + 1))
}


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -