计算机图形学(一)——数据压缩:道格拉斯普克法

**

实验一:数据压缩:道格拉斯普克法

**

1.1实验目的

(1)掌握数据压缩之:道格拉斯普克法的基本原理以及实现过程。
(2)掌握函数的声明、定义及调用以及嵌套调用方法。
(3)掌握文件的读写。
(4)掌握面向对象程序设计的思想。

1.2实验内容

(1)读取文本文件
(2)MFC或C++实现道格拉斯普格法

1.3算法思路

对每一条曲线的首末端点连一条线,求所有点到该直线的距离,并找出最大距离值dmax,用dmax与限差D相比:
(1)若dmax<D,这条曲线上的中间点全部舍去;
(2)若dmax≥D,保留dmax对应的坐标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法。

由该算法的基本思路可知,该算法是递归的。而且算法的核心是求得点到直线的距离。
该算法的主要功能函数是Simp()该函数的功能是根据输入的限差LimitdDis确定线上的点是保留还是去除。
Simp()函数的实现思想是将一个曲线上的各点与曲线的端点连接组成一个三角形,如下:
在这里插入图片描述

先根据点到点的距离公式。求出ABC的值。然后再应用余弦公式
再根据cosA,cosB,sinA值。算出距离。对每个点进行上述的计算,求出每个点到ab的距离取最大值和LimitdDis较,如果大于限差,那么从c点将曲线分为两段重复第一步,如果小于,则去除ab间的所有点。

1.4流程图

该函数的流程图如下,
在这里插入图片描述

1.5实验步骤

(1)连接曲线首尾两点A、B形成一条直线AB;
(2)计算曲线上离该直线段距离最大的点C,计算其与AB的距离d;
(3)比较该距离与预先给定的阈值threshold的大小,如果小于threshold,则以该直线作为曲线的近似,该段曲线处理完毕。
(4)如果距离大于阈值,则用点C将曲线分为两段AC和BC,并分别对两段曲线进行步骤[1~3]的处理。
(5)当所有曲线都处理完毕后,依次连接各个分割点形成折线,作为原曲线的近似。

1.6实验代码

(1)自建一个头文件ptstruct.h,此文件定义相应结构体:

structPt
{
    
    
	floatx,y;//x,y坐标
	floatflag;//标识符
};

(2)编写一个类(CDglsSF)
头文件dglsf.h中加入如下代码:

public:
	CDgls();
	virtual~CDgls();
	voidReadPtFile();//读点文件
voidDraw(CDC*pdc);//绘图
	intm_LimitDis;//限差
private:
	Ptm_pts[SIZE];//点数组,存入点
	intm_ptsnum;//点个数,用于读文件时获得,最后好操作下标
	voidDrawPts(CDC*pdc);//绘点集
	voidDrawInitLine(CDC*pdc);//绘初始线
	voidDrawSimpLine(CDC*pdc);//绘简化后的线
	doubledist(intptNo1,intptNo2);//计算两点间的距离
	doublesqrdist(intptNo1,intptNo2);//计算两点间的距离的平方,为求cos做准备
	intMaxdist();//返回最大距离的点号
	voidsimple(intptNo1,intptNo2);//简化
	voiddeletept(intptNo1,intptNo2);//删除ptNo1与ptNo2之间的点

(3)源文件dgisf.cpp中实现相应的代码:

#include "stdafx.h"
#include "CDGLS_SF.h"
#include "Dgls.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//
// Construction/Destruction
//
#include"math.h"
CDgls::CDgls()
{
    
    
m_LimitDis=50;//设定阈值
}

CDgls::~CDgls()
{
    
    
}
void CDgls::Draw(CDC *pdc)
{
    
    
	
	ReadPtFile();//读取点坐标文件
    DrawPts(pdc);//画点
	DrawInitLine(pdc);//用线连接点坐标
    simple(0,m_ptsnum);//简化点
	DrawSimpLine(pdc);//画出简化后的线
}
void CDgls::DrawInitLine(CDC *pdc)
{
    
    
	int i;
    CPen pen;//定义画笔
	bool bpen=false;//将画笔属性设定为假
	bpen=pen.CreatePen(PS_SOLID,3,RGB(0,0,255));//创建画笔(蓝色)
	if(bpen==true)//判断笔属性
		pdc->SelectObject(&pen);//指向画笔选择的点
	for(i=0;i<m_ptsnum;i++)
	{
    
    
      pdc->MoveTo(m_pts[i].x,m_pts[i].y);//移动到选择的点
	  pdc->LineTo(m_pts[i+1].x,m_pts[i+1].y);//连接线
	}
	pen.DeleteObject();//关闭
}
void CDgls::DrawSimpLine(CDC *pdc)
{
    
    
   int i,iSum=0,lastx,lasty;
    CPen pen;//定义画笔
	bool bpen=false;//将画笔属性设定为假
	bpen=pen.CreatePen(PS_SOLID,5,RGB(255,0,0));//创建画笔(红色)
	if(bpen==true)
		pdc->SelectObject(&pen);//指向画笔选择的点

	for(i=0;i<=m_ptsnum;i++)
	{
    
    
		if(m_pts[i].flag=='T')
		{
    
     	
			iSum++;
			if(iSum<2)//如果点小于2
			{
    
    
				lastx=m_pts[i].x;//将选择的点赋给最后一点x坐标
				lasty=m_pts[i].y;//将选择的点赋给最后一点y坐标
			}
			else
			{
    
    
				pdc->MoveTo(lastx,lasty);//指针移动到最后一点
				pdc->LineTo(m_pts[i].x,m_pts[i].y);//画向下一点
				lastx=m_pts[i].x;//将选择的点赋给最后一点x坐标
				lasty=m_pts[i].y;//将选择的点赋给最后一点y坐标
			}
		}
	}
	pen.DeleteObject();//删除目标
}
void CDgls::DrawPts(CDC *pdc)
{
    
    
	CBrush brush;
	int i;
	brush.CreateSolidBrush(RGB(255,0,0));//创建红色刷子
	pdc->SelectObject(&brush);//指针指向刷子
	for(i=0;i<=m_ptsnum;i++)
	{
    
    
		pdc->Ellipse(m_pts[i].x-13,m_pts[i].y-13,m_pts[i].x+13,m_pts[i].y+13);//画点,半径为13
	}
	brush.DeleteObject();//删除


}
double CDgls::dist(int ptNo1,int ptNo2)
{
    
    
  double d;
  float delx,dely;
  delx=m_pts[ptNo1].x-m_pts[ptNo2].x;//计算x坐标增量
  dely=m_pts[ptNo1].y-m_pts[ptNo2].y;//计算y坐标增量
  d=sqrt(delx*delx+dely*dely);//计算距离
  return d;//返回距离

}
double CDgls::sqrdist(int ptNo1,int ptNo2)
{
    
    
  double f,d;
  f=dist(ptNo1,ptNo2);//获取相邻点的距离
  d=f*f;//计算距离的平方
  return d;//返回距离
}
int CDgls::Maxdist()
{
    
    
 return 0;
}
void CDgls::ReadPtFile()
{
    
    
	FILE *fp;//定义文件指针
	float x,y;
	int i=0;
	if((fp=fopen("F:\\计算机制图原理与方法\\DGLS_SF_DATA.txt","r"))==NULL)//打开文件
		AfxMessageBox("not open file!");
	while(!feof(fp))
	{
    
    
		fscanf(fp,"%f%f",&x,&y);//读取文件
        m_pts[i].x=x;//x值赋予x坐标
		m_pts[i].y=y;//y值赋予y坐标
		m_pts[i].flag='T';//属性改成真
		i++;
	}
	m_ptsnum=i-2;//点的计数-2
	fclose(fp);//关闭文件
}
void CDgls::simple(int ptNo1,int ptNo2)
{
    
    
	double a,b,c,cosA,cosB,sinA,maxdis,curdis;
	double a1,b1,c1;//求平方
	int i=0,maxNO=0;
	if(ptNo2-ptNo1>=2)//点个数要大于2个
	{
    
    
		maxdis=0.00;
		c1=sqrdist(ptNo1,ptNo2);
		c=dist(ptNo1,ptNo2);
		i=ptNo1+1;
		while(i<ptNo2)
		{
    
    
			curdis=0.00;
			b1=sqrdist(ptNo1,i);//计算两点间的距离的平方,为求cos做准备
			a1=sqrdist(ptNo2,i);//计算两点间的距离的平方,为求cos做准备
			b=dist(ptNo1,i);//计算两点间的距离
			a=dist(ptNo2,i);//计算两点间的距离
			cosA=(b1+c1-a1)/(2*b*c);//计算cos
			cosB=(a1+c1-b1)/(2*a*c);//计算cos
			//cosA=(b*b+c*c-a*a)/(2*b*c);
			//cosB=(a*a+c*c-b*b)/(2*a*c);
			sinA=(float)sqrt(1-cosA*cosA);
			
			if((cosA==0) || (cosB==0))
			{
    
    
				if(cosA==0)
				{
    
    
					curdis=b;
				}
				else
				{
    
    
					curdis=a;
				}
			}
			else
			{
    
    
				curdis=b*sinA;
			}
			if(maxdis<=curdis)
			{
    
    
				maxdis=curdis;
				maxNO=i;
			}
			i++;
		}
		if(maxdis>=m_LimitDis)
		{
    
    
			simple(ptNo1,maxNO);//简化
			simple(maxNO,ptNo2);//简化
		}
		else
		{
    
    
			deletept(ptNo1,ptNo2);//删除点
		}
	}
	
}
void CDgls::deletept(int ptNo1,int ptNo2)
{
    
    
	int c=ptNo1+1;
	while(c<ptNo2)
	{
    
    
		m_pts[c].flag='F';//属性改成假
		c++;
	}
}

1.7实验结果展示

蓝线为原来曲线上的点,红线为简化后的点连接的线,当D越大,简化越大,如下图所示。
m_LimitDis=100m_LimitDis=200m_LimitDis=50

猜你喜欢

转载自blog.csdn.net/chengzilhc/article/details/106728463
今日推荐