Computação Gráfica (1) - Compressão de Dados: Método de Douglas Puck

**

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:
insira a descrição da imagem aqui

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,
insira a descrição da imagem aqui

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.
m_LimitDis=100m_LimitDis=200m_LimitDis=50

Acho que você gosta

Origin blog.csdn.net/chengzilhc/article/details/106728463
Recomendado
Clasificación