**
Experimento 1: Compressão de Dados: Método de Douglas Puck
**
1.1 Finalidade experimental
(1) Compressão de dados mestre: os princípios básicos e o processo de implementação do método de Douglas Puck.
(2) Domine a declaração, definição e chamada de funções, bem como métodos de chamada aninhados.
(3) Dominar a leitura e escrita de documentos.
(4) Domine a ideia de programação orientada a objetos.
1.2 Conteúdo do experimento
(1) Ler arquivo de texto
(2) MFC ou C++ implementam o método Douglas Pugh
1.3 Ideias de algoritmo
Conecte uma linha ao primeiro e ao último ponto de cada curva, encontre as distâncias de todos os pontos até a linha e encontre a distância máxima dmax e use dmax para comparar com a tolerância D:
(1) Se dmax < D, neste curve (2) Se dmax≥D
, mantenha o ponto coordenado correspondente a dmax, e tome este ponto como limite, divida a curva em duas partes e use o método repetidamente para essas duas partes.
A partir da ideia básica do algoritmo, pode-se perceber que o algoritmo é recursivo. E o núcleo do algoritmo é encontrar a distância do ponto à linha.
A função principal do algoritmo é Simp() A função desta função é determinar se os pontos da linha são retidos ou removidos de acordo com a tolerância de entrada LimitdDis.
A ideia de realização da função Simp() é conectar os pontos de uma curva e as extremidades da curva para formar um triângulo, da seguinte forma:
Primeiro de acordo com a fórmula da distância ponto a ponto. Encontre o valor de ABC. Em seguida, aplique a fórmula do cosseno
e, em seguida, de acordo com os valores de cosA, cosB, sinA. Calcule a distância. Execute o cálculo acima para cada ponto, encontre a distância de cada ponto até ab, pegue o valor máximo e compare com LimitdDis, se for maior que a tolerância, divida a curva em duas seções do ponto c e repita o primeiro passo , se for menor que, remova todos os pontos entre a e b.
1.4 Fluxograma
O fluxograma desta função é o seguinte,
1.5 Etapas experimentais
(1) Conecte os dois pontos A e B no início e no final da curva para formar uma linha reta AB;
(2) Calcule o ponto C com a maior distância do segmento de linha reta na curva e calcule a distância d entre ele e AB;
(3) Compare esta distância com a predefinição O tamanho do limiar de limiar, se for menor que o limiar, a linha reta é usada como a aproximação da curva e o processamento da curva é concluído.
(4) Se a distância for maior que o limite, use o ponto C para dividir a curva em duas seções AC e BC, e execute os passos [1~3] nas duas seções da curva, respectivamente.
(5) Depois que todas as curvas forem processadas, conecte os pontos de divisão para formar uma polilinha, que é usada como uma aproximação da curva original.
1.6 Código experimental
(1) Construa um arquivo de cabeçalho ptstruct.h, que define a estrutura correspondente:
structPt
{
floatx,y;//x,y坐标
floatflag;//标识符
};
(2) Escreva um
arquivo de cabeçalho de classe (CDglsSF) dglsf.h e adicione o seguinte código:
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) O código correspondente é implementado no arquivo fonte 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 Exibição de resultados experimentais
A linha azul é o ponto da curva original e a linha vermelha é a linha que liga os pontos simplificados, quanto maior o D, maior a simplificação, como mostra a figura a seguir.