利用dxflib实现dxf文件的读写与可视化

文章介绍了如何使用开源库dxflib进行dxf文件的读写,包括点、线、多段线等数据结构,并展示了在QCAD中下载和使用dxflib的方式。此外,还讨论了dxf文件的数据结构构建,以及如何在MFC框架中进行文件的读取和保存,实现简单的CAD软件的可视化功能。
摘要由CSDN通过智能技术生成

dxflib是开源的读写dxf文件的库,在QCAD上可以直接下载(QCAD - Downloads)。dxf作为一种通用的绘图文件格式,包含的信息量非常大,要去理解文件的内容也比较受苦,详细的解释在论坛里可以找到(dxf 格式详解_dxf是什么文件格式_runing9的博客-CSDN博客)。使用dxflib库的优势在于它很好地简化了各部分的读写,代码可读性很强,同时配库只需要将压缩包下的src文件包含在目录中即可。在给导师做一个简易的CAD软件时用到了包括dxf文件的读写,利用MFC做可视化,还做了一个尺寸修改的交互。

目录

一、数据结构

1.点

2.线

3.多段线

4.圆弧

5.圆

6.文本标注

7.尺寸标注

8.dxf文件数据结构的建立

二、读dxf文件

三、写dxf文件

四、可视化

五、视图变换与交互

一、数据结构

在压缩包内的examples/readwrite文件夹下,有写好的接口类test_creationclass,如下图所示(部分已经修改过),从传入的参数类型来理解各种图元信息的数据结构。目前只使用了包括点、线、圆弧、圆、多段线、文本标注和尺寸标注。

    Test_CreationClass();
	//图元读取
    virtual void addLayer(const DL_LayerData& data);
    virtual void addPoint(const DL_PointData& data);
    virtual void addLine(const DL_LineData& data);
    virtual void addArc(const DL_ArcData& data);
    virtual void addCircle(const DL_CircleData& data);
    virtual void addPolyline(const DL_PolylineData& data);
    virtual void addVertex(const DL_VertexData& data);
    virtual void add3dFace(const DL_3dFaceData& data);
	virtual void addText(const DL_TextData & data);

1.点

struct DXFLIB_EXPORT DL_PointData {
    DL_PointData(double px=0.0, double py=0.0, double pz=0.0) {
        x = px;
        y = py;
        z = pz;
    }
    double x;//3个坐标值
    double y;
    double z;
};

2.线

struct DXFLIB_EXPORT DL_LineData {
    /**
     * Constructor.
     * Parameters: see member variables.
     */
    DL_LineData(double lx1, double ly1, double lz1,
                double lx2, double ly2, double lz2) {
        x1 = lx1;
        y1 = ly1;
        z1 = lz1;

        x2 = lx2;
        y2 = ly2;
        z2 = lz2;
    }
    //2个点坐标
    double x1; double y1;double z1;

    double x2; double y2; double z2;
};

3.多段线

struct DXFLIB_EXPORT DL_PolylineData {
    /**
     * Constructor.
     * Parameters: see member variables.
     */
    DL_PolylineData(int pNumber, int pMVerteces, int pNVerteces, int pFlags, double pElevation = 0.0) {
        number = pNumber;
        m = pMVerteces;
        n = pNVerteces;
        elevation = pElevation;
        flags = pFlags;
    } 
    unsigned int number;//多段线包含的点数,与DL_VertexData配合使用
    unsigned int m;//如果多段线是多边形网格,则m方向上的顶点数
    unsigned int n;//如果多段线是多边形网格,则n方向上的顶点数
    double elevation;//高程
    int flags;
};
struct DXFLIB_EXPORT DL_VertexData {
    /**
     * Constructor.
     * Parameters: see member variables.
     */
    DL_VertexData(double px=0.0, double py=0.0, double pz=0.0,
                  double pBulge=0.0) {
        x = px;
        y = py;
        z = pz;
        bulge = pBulge;
    }

    double x;
    double y;
    double z;
    double bulge;//多段线的顶点凸起部分的定义。使用顶点处的弧角的1/4弧度的切线值,直线为0
};

4.圆弧

struct DXFLIB_EXPORT DL_ArcData {
    DL_ArcData(double acx, double acy, double acz,
               double aRadius,
               double aAngle1, double aAngle2) {

        cx = acx;
        cy = acy;
        cz = acz;
        radius = aRadius;
        angle1 = aAngle1;
        angle2 = aAngle2;
    }
//圆弧圆心点坐标
    double cx;
    double cy;
    double cz;
    double radius;//圆弧半径
    double angle1;//起始角度(以x正半轴逆时针旋转计算角度)
    double angle2;//终止角度
};

5.圆

struct DXFLIB_EXPORT DL_CircleData {
    DL_CircleData(double acx, double acy, double acz,
                  double aRadius) {

        cx = acx;
        cy = acy;
        cz = acz;
        radius = aRadius;
    }
    //圆心坐标及半径
    double cx;
    double cy;
    double cz;
    double radius;
};

6.文本标注

struct DXFLIB_EXPORT DL_TextData {
    DL_TextData(double ipx, double ipy, double ipz,
                double apx, double apy, double apz,
                double height, double xScaleFactor,
                int textGenerationFlags,
                int hJustification,
                int vJustification,
                const std::string& text,
                const std::string& style,
                double angle)
        : ipx(ipx), ipy(ipy), ipz(ipz),
          apx(apx), apy(apy), apz(apz),
          height(height), xScaleFactor(xScaleFactor),
          textGenerationFlags(textGenerationFlags),
          hJustification(hJustification),
          vJustification(vJustification),
          text(text),
          style(style),
          angle(angle) {
    }
    //定义点坐标
    double ipx;
    double ipy;
    double ipz;
    //对齐点坐标
    double apx;
    double apy;
    double apz;

    double height;//字高
    double xScaleFactor;//相对X比例因子
    int textGenerationFlags;//默认为0,向后为2,倒置为4
    int hJustification;//水平对齐
    /* 左侧=0,中心=1,右侧=2,对齐=3,中间=4,适应=5
        对于3、4、5垂直对齐必须为0 */
    int vJustification;//垂直对齐,基线=0,底部=1,中间=2,顶部=3
    std::string text;//文本内容
    std::string style;//字体
    double angle;//旋转角度
};

7.尺寸标注

尺寸标注相对复杂一些,它不止简单的一个文本,还包括直线和箭头,称为标注块,也就是说它需要通过块来添加。具体的内容在文件中查阅,这里不在展示。

	virtual void addBlock(const DL_BlockData&data);//添加块
	virtual void endBlock();//结束块的添加
	virtual void addSolid(const DL_SolidData&data);//箭头
	virtual void addMText(const DL_MTextData & data);//标注文本

8.dxf文件数据结构的建立

在官方下载下来的文件中,有一个Test_CreationClass头文件和源文件,dxf文件的数据结构直接用这个类就可以。这个就相当于接口类,在里面可以重写各类图元读取的函数,原本里面是打印相关信息,我改成了保存数据到成员中。需要注意的是多段线的读取结束时需要调用donpolyline,完成多段线的添加,而标注块也是类似的道理,需要读取完这个块。内容见下面头文件以及源文件。

#ifndef TEST_CREATIONCLASS_H
#define TEST_CREATIONCLASS_H
#include<vector>
#include "../src/dl_creationadapter.h"

class Test_CreationClass : public DL_CreationAdapter {
public:
    Test_CreationClass();
	//图元读取
    virtual void addLayer(const DL_LayerData& data);
    virtual void addPoint(const DL_PointData& data);
    virtual void addLine(const DL_LineData& data);
    virtual void addArc(const DL_ArcData& data);
    virtual void addCircle(const DL_CircleData& data);
    virtual void addPolyline(const DL_PolylineData& data);
    virtual void addVertex(const DL_VertexData& data);
    virtual void add3dFace(const DL_3dFaceData& data);
	virtual void addText(const DL_TextData & data);
	//标注块整体读取
	bool adddim = false;
	virtual void addBlock(const DL_BlockData&data);
	virtual void endBlock();
	virtual void addSolid(const DL_SolidData&data);
	virtual void addMText(const DL_MTextData & data);



    void printAttributes();
	void donepolyline()
	{
		for (int i = 0; i < m_polylines.size(); ++i)
			m_polylines[i].first.number = m_polylines[i].second.size();
	}

	class dimblock{
	public:
		dimblock(){
			dimlines.clear();
			dimtext.clear();
			dimarrow.clear();
		}
		std::vector<DL_LineData>dimlines;
		std::vector<DL_MTextData> dimtext;
		std::vector<DL_SolidData>dimarrow;

	};
public://图元信息
	//DL_LayerData m_layerdata;														//层
	std::vector<DL_PointData>m_points;												//点
	std::vector<DL_LineData>m_lines;												//直线	
	std::vector<std::pair<DL_PolylineData, std::vector<DL_VertexData>>>m_polylines;	//多段线
	std::vector<DL_ArcData> m_arcs;													//圆弧
	std::vector<DL_CircleData >m_circles;											//圆
	std::vector<DL_SolidData>m_solid;												//实体
	std::vector<DL_TextData>m_text;													//普通文本
	std::vector<DL_MTextData>m_mtext;												//标注文本
	std::vector<dimblock>m_dimblock;												//标注块
	
};

#endif
#include "test_creationclass.h"

#include <iostream>
#include <stdio.h>


/**
 * Default constructor.
 */
Test_CreationClass::Test_CreationClass() {
	m_points.clear();
	m_lines.clear();
	m_polylines.clear();
	m_circles.clear();
	m_arcs.clear();
	m_text.clear();
	m_mtext.clear();
	m_dimblock.clear();
}


/**
 * Sample implementation of the method which handles layers.
 */
void Test_CreationClass::addLayer(const DL_LayerData& data) {
//     printf("LAYER: %s flags: %d\n", data.name.c_str(), data.flags);
//     printAttributes();
}

/**
 * Sample implementation of the method which handles point entities.
 */
void Test_CreationClass::addPoint(const DL_PointData& data) {
	m_points.push_back(data);
//    printf("POINT    (%6.3f, %6.3f, %6.3f)\n",data.x, data.y, data.z);
//     printAttributes();
}

/**
 * Sample implementation of the method which handles line entities.
 */
void Test_CreationClass::addLine(const DL_LineData& data) {
	
	if (adddim)
		m_dimblock[m_dimblock.size() - 1].dimlines.push_back(data);
	else
		m_lines.push_back(data);
//     printf("LINE     (%6.3f, %6.3f, %6.3f) (%6.3f, %6.3f, %6.3f)\n",data.x1, data.y1, data.z1, data.x2, data.y2, data.z2);
//     printAttributes();
}

/**
 * Sample implementation of the method which handles arc entities.
 */
void Test_CreationClass::addArc(const DL_ArcData& data) {
	m_arcs.push_back(data);
//     printf("ARC      (%6.3f, %6.3f, %6.3f) %6.3f, %6.3f, %6.3f\n",data.cx, data.cy, data.cz, data.radius, data.angle1, data.angle2);
//     printAttributes();
}

/**
 * Sample implementation of the method which handles circle entities.
 */
void Test_CreationClass::addCircle(const DL_CircleData& data) {
	m_circles.push_back(data);
//     printf("CIRCLE   (%6.3f, %6.3f, %6.3f) %6.3f\n",data.cx, data.cy, data.cz, data.radius);
//     printAttributes();
}


/**
 * Sample implementation of the method which handles polyline entities.
 */
void Test_CreationClass::addPolyline(const DL_PolylineData& data) {
	std::vector<DL_VertexData> temp; temp.clear();
 	std::pair<DL_PolylineData, std::vector<DL_VertexData>> newone(data,temp);
	m_polylines.push_back(newone);
//     printf("POLYLINE \n");
//     printf("flags: %d\n", (int)data.flags);
//     printAttributes();
}


/**
 * Sample implementation of the method which handles vertices.
 */
void Test_CreationClass::addVertex(const DL_VertexData& data) {
	m_polylines[m_polylines.size() - 1].second.push_back(data);
//     printf("VERTEX   (%6.3f, %6.3f, %6.3f) %6.3f\n",data.x, data.y, data.z,data.bulge);
//     printAttributes();
}


void Test_CreationClass::add3dFace(const DL_3dFaceData& data) {
    printf("3DFACE\n");
//     for (int i=0; i<4; i++) {
//         printf("   corner %d: %6.3f %6.3f %6.3f\n",  i, data.x[i], data.y[i], data.z[i]);
//     }
//     printAttributes();
}


void Test_CreationClass::addBlock(const DL_BlockData&data)
{
	adddim = true;
	dimblock newone;
	m_dimblock.push_back(newone);
}


void Test_CreationClass::endBlock()
{
	adddim = false;
	if (m_dimblock[m_dimblock.size() - 1].dimlines.size() < 1 
		&& m_dimblock[m_dimblock.size() - 1].dimtext.size() < 1 
		&& m_dimblock[m_dimblock.size() - 1].dimarrow.size()<1)
		m_dimblock.pop_back();
}


void Test_CreationClass::addSolid(const DL_SolidData&data)
{
	if (adddim)
		m_dimblock[m_dimblock.size() - 1].dimarrow.push_back(data);
	else
		m_solid.push_back(data);
}

void Test_CreationClass::addText(const DL_TextData & data)
{
	m_text.push_back(data);
}


void Test_CreationClass::addMText(const DL_MTextData & data)
{
	if (adddim)
		m_dimblock[m_dimblock.size() - 1].dimtext.push_back(data);
	else
		m_mtext.push_back(data);
}

void Test_CreationClass::printAttributes() {
//     printf("  Attributes: Layer: %s, ", attributes.getLayer().c_str());
//     printf(" Color: ");
//     if (attributes.getColor()==256)	{
//         printf("BYLAYER");
//     } else if (attributes.getColor()==0) {
//         printf("BYBLOCK");
//     } else {
//         printf("%d", attributes.getColor());
//     }
//     printf(" Width: ");
//     if (attributes.getWidth()==-1) {
//         printf("BYLAYER");
//     } else if (attributes.getWidth()==-2) {
//         printf("BYBLOCK");
//     } else if (attributes.getWidth()==-3) {
//         printf("DEFAULT");
//     } else {
//         printf("%d", attributes.getWidth());
//     }
//     printf(" Type: %s\n", attributes.getLinetype().c_str());
}
    
    

// EOF

二、读dxf文件

在外部类中声明上述的类Test_CreationClass为成员变量,定义一个读文件的方法。例如我是外部又建立了一个MyDxf类用来衔接MFC的button事件或其他控件,注意包含相关的头文件。

public:
 	Test_CreationClass		* m_data;		//dxf数据输出接口
void MyDxf::OpenDxf_Button(string filePath)
{
	// Load DXF file into memory:
	Initialdxf();
	std::cout << "Reading file " << filePath << "...\n";
	if (!m_dxf->in(filePath, m_data)) { // if file open failed
		std::cerr << filePath << " could not be opened.\n";
		return;
	}
	m_data->donepolyline();

}

三、写dxf文件

说是写文件,其实是保存文件,写文件的过程就是对m_data修改的过程,例如后面添加交互功能,画线,画圆之类的,只需往相应的m_data中的容器添加即可。

void MyDxf::SaveDxf_Button(string filePath)
{
	if (m_data==NULL)return;
	FILE *pfd = fopen(filePath.c_str(), "w+");
	DL_Codes::version exportVersion = DL_Codes::AC1015;
	DL_WriterA* dw = m_dxf->out(filePath.c_str(), exportVersion);
	if (dw == NULL) { printf("Cannot open file 'myfile.m_dxf' \ for writing."); return; }
	/*--------------------------------------------------------*/
	//1  标题段
	m_dxf->writeHeader(*dw);
	dw->sectionEnd();
	/*--------------------------------------------------------*/
	//2  表段
	dw->sectionTables();
	m_dxf->writeVPort(*dw);
	//2.1  线型表(Linetype)
	dw->tableLinetypes(3);
	m_dxf->writeLinetype(*dw, DL_LinetypeData("BYBLOCK", "BYBLOCK", 0, 0, 0.0));
	m_dxf->writeLinetype(*dw, DL_LinetypeData("BYLAYER", "BYLAYER", 0, 0, 0.0));
	m_dxf->writeLinetype(*dw, DL_LinetypeData("CONTINUOUS", "Continuous", 0, 0, 0.0));
	dw->tableEnd();
	//2.2  图层表(Layer)
	int numberOfLayers = 3;//层数3
	dw->tableLayers(numberOfLayers);
	m_dxf->writeLayer(*dw, DL_LayerData("0", 0), DL_Attributes(std::string(""), DL_Codes::black, 100, "CONTINUOUS", 1.0));
	m_dxf->writeLayer(*dw, DL_LayerData("mainlayer", 0), DL_Attributes(std::string(""), DL_Codes::black, 100, "CONTINUOUS", 1.0));
	m_dxf->writeLayer(*dw, DL_LayerData("anotherlayer", 0), DL_Attributes(std::string(""), DL_Codes::blue, 100, "CONTINUOUS", 1.0));
	dw->tableEnd();
	//2.3  字样表(Style)
	dw->tableStyle(1);
	m_dxf->writeStyle(*dw, DL_StyleData("standard", 0, 2.5, 1.0, 0.0, 0, 2.5, "txt", ""));
	dw->tableEnd();
	//2.4  视图表(View)
 	m_dxf->writeView(*dw);
 	m_dxf->writeUcs(*dw);
	dw->tableAppid(1);
	m_dxf->writeAppid(*dw, "ACAD");
	dw->tableEnd();
	//2.5  DL_VERSION_R13中需要伪造dimstyle和blockrecord两部分,以使AutoCAD能够读取该文件。
 	m_dxf->writeDimStyle(*dw, 1, 1, 1, 1, 1);
 	m_dxf->writeBlockRecord(*dw);
	dw->tableEnd();
	dw->sectionEnd();
	/*--------------------------------------------------------*/
	//3  块段
	DL_Attributes typetemp1("mainlayer", 256, -1, "BYLAYER", 1.0);//属性
	DL_Attributes typetemp2("anotherlayer", 256, -1, "BYLAYER", 1.0);//属性
	dw->sectionBlocks();
 	m_dxf->writeBlock(*dw, DL_BlockData("*Model_Space", 0, 0.0, 0.0, 0.0));
 	m_dxf->writeEndBlock(*dw, "*Model_Space");
 	m_dxf->writeBlock(*dw, DL_BlockData("*Paper_Space", 0, 0.0, 0.0, 0.0));
 	m_dxf->writeEndBlock(*dw, "*Paper_Space");
 	m_dxf->writeBlock(*dw, DL_BlockData("*Paper_Space0", 0, 0.0, 0.0, 0.0));
 	m_dxf->writeEndBlock(*dw, "*Paper_Space0");

// 	m_dxf->writeBlock(*dw, DL_BlockData("myblock1", 0, 0.0, 0.0, 0.0));
// 	// ...
// 	// write block entities e.g. with m_dxf->writeLine(), ..
// 	// ...
// 	m_dxf->writeEndBlock(*dw, "myblock1");
	dw->sectionEnd();

	/*--------------------------------------------------------*/
	//4  实体段
	dw->sectionEntities();
	

	for (int i = 0; i < m_data->m_points.size(); ++i)
	{
		m_dxf->writePoint(*dw, m_data->m_points[i], typetemp1);
	}
	for (int i = 0; i < m_data->m_lines.size(); ++i)
	{
		m_dxf->writeLine(*dw, m_data->m_lines[i], typetemp1);
	}
	for (int i = 0; i < m_data->m_polylines.size(); ++i)
	{
		m_dxf->writePolyline(*dw, m_data->m_polylines[i].first, typetemp1);
		for (int j = 0; j < m_data->m_polylines[i].second.size(); ++j)
		{
			m_dxf->writeVertex(*dw, m_data->m_polylines[i].second[j]);
		}
		m_dxf->writePolylineEnd(*dw);
	}
	for (int i = 0; i < m_data->m_arcs.size(); ++i)
	{
		m_dxf->writeArc(*dw, m_data->m_arcs[i], typetemp1);
	}
	for (int i = 0; i < m_data->m_circles.size(); ++i)
	{
		m_dxf->writeCircle(*dw, m_data->m_circles[i], typetemp1);
	}
  	for (int i = 0; i < m_data->m_text.size();++i)
  	{
  		//m_dxf->writeText(*dw, m_data->m_text[i], typetemp2);//用text写有问题,使用MText代替	
		DL_MTextData temp(m_data->m_text[i].ipx, m_data->m_text[i].ipy, m_data->m_text[i].ipz,
			0, 0, 0, m_data->m_text[i].height, 0, 5, 1, 1, 1, m_data->m_text[i].text, m_data->m_text[i].style, m_data->m_text[i].angle);
		m_dxf->writeMText(*dw, temp, typetemp2);

  	}
 	for (int i = 0; i < m_data->m_mtext.size(); ++i)
 	{
 		//m_data->m_mtext[i].height = 30;
		DL_MTextData temp = m_data->m_mtext[i];
		temp.height = 50;
 		m_dxf->writeMText(*dw, temp, typetemp2);
 	}



	dw->sectionEnd();
	/*--------------------------------------------------------*/
	//5  文件结束段
	m_dxf->writeObjects(*dw);
	m_dxf->writeObjectsEnd(*dw);

	dw->dxfEOF();
	dw->close();
	delete dw;
	delete m_dxf;
	dw = nullptr;
	m_dxf = nullptr;
}

四、可视化

在MFC框架中,有一个Doc类和View类,在Doc类中将我们的外部接口类MyDxf作为成员变量,注意在构造里初始化,在析构里释放。顾名思义,在Doc类中,要做的操作是数据的导入导出,也就是dxf文件的读写,而View类中去显示m_data中的数据。在对数据可视化之前,首先介绍一下文件的读写接口,不然怎么读数据来可视化呢。

public:
	MyDxf *m_mydxf;

首先,在资源视图中,找到菜单栏,接着在打开的标签页中添加事件触发的菜单,这里主要就是打开按钮和另存为按钮。注意在右下角的属性里面设置控件的ID,当然默认也行,记住就行。然后鼠标放在打开按钮上,右键点击添加事件响应程序,然后找到选择Doc类,添加编辑,这里我是已经添加过了,所以显示编辑代码。添加编辑后会自动跳转到目标函数体内。后面是打开,保存dxf文件的代码。

 

void CDXFminiDoc::OnOpendxf()
{
	// TODO:  在此添加命令处理程序代码
	
	CString defaultDir = _T("C:/Users/csy/Desktop");//默认打开的文件路径
	CString filter = _T("文件 (*.dxf)|*.dxf||");//文件过虑的类型
	CFileDialog dlg(TRUE, defaultDir, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, filter, NULL);
	if (dlg.DoModal() == IDOK)
	{
		CString filePath = dlg.GetPathName();
		m_mydxf = new MyDxf();
		string str = CT2A(filePath.GetString());
		m_mydxf->OpenDxf_Button(str);
	}
	UpdateAllViews(NULL);
}


void CDXFminiDoc::OnSavedxf()
{
	// TODO:  在此添加命令处理程序代码
	if (!m_mydxf)return;
	CString defaultDir = _T("C:/Users/lb/Desktop");//默认路径
	CString fileName = _T("newdoor.dxf");//文件名
	CString filter = _T("文件 (*.dxf)|*.dxf||");//文件过滤类型
	CFileDialog dlg(FALSE, defaultDir, fileName, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, filter, NULL);
	if (dlg.DoModal() == IDOK)
	{
		CString filePath = dlg.GetPathName();
		string str = CT2A(filePath.GetString());
		m_mydxf->SaveDxf_Button(str);
	}
	UpdateAllViews(NULL);
}

其次,MFC提供了封装好的绘制函数,调用就行,在view类的OnDraw函数里,放置我们总的绘制函数drawdxf,将所有图元的绘制调用都放到它里面去。这里只展示画多段线的方法

void CDXFminiView::OnDraw(CDC* pDC)
{
	CDXFminiDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)return;
	if (!pDoc->m_mydxf)return;
	else {
		GetClientRect(&rect);
		CDC MemDC; //首先定义一个显示设备对象
		CBitmap MemBitmap;//定义一个位图对象
		MemDC.CreateCompatibleDC(NULL);//建立与屏幕显示兼容的内存显示设备
		MemBitmap.CreateCompatibleBitmap(pDC, rect.right - rect.left, rect.bottom - rect.top );//建立与屏幕显示兼容的位图
		CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);//将位图选入到内存显示设备中,只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
		MemDC.FillSolidRect(0, 0, rect.right - rect.left, rect.bottom - rect.top, RGB(255, 255, 255));//清空位图,以白色填充背景

		pDoc->m_mydxf->drawdxf(&MemDC, rect.right, rect.bottom);//绘制dxf	


		pDC->BitBlt(0, 0, rect.right - rect.left, rect.bottom - rect.top, &MemDC, 0, 0, SRCCOPY);//将内存中的图拷贝到屏幕上进行显示
		MemBitmap.DeleteObject();//绘图完成后的清理
		MemDC.DeleteDC();
	}
}
void MyDxf::drawdxf(CDC *pDC,double dx,double dy)
{
	if (!m_data)return;
	if (firstdraw)
	{
		m_dxfbbox=GetDxfRect(m_data);
		InitialViewMtrx(dx, dy);
		chozenp.clear();
	}
	firstdraw = false;
	drawpoint(pDC);     
	drawlines(pDC);		
	drawpolylines(pDC);	
	drawarc(pDC);			
	drawcircle(pDC);	
 	drawtext(pDC);
 
 	//drawmtext(pDC);
 
 	drawdimblock(pDC);
 
 	drawchoosemtext(pDC, chozenp);
}
void MyDxf::drawpolylines(CDC *pDC)
{
	CPen NewPen, *pOldPen;
	NewPen.CreatePen(PS_DOT, 2, RGB(0, 0, 0));
	pOldPen = pDC->SelectObject(&NewPen);
	for (int i = 0; i < m_data->m_polylines.size(); i++)
	{
		if (m_data->m_polylines[i].second.size() <= 1)break;
		for (int j = 0; j < m_data->m_polylines[i].second.size() - 1; ++j)
		{
			CPoint2D p1(m_data->m_polylines[i].second[j].x, m_data->m_polylines[i].second[j].y);
			CPoint2D p2(m_data->m_polylines[i].second[j + 1].x, m_data->m_polylines[i].second[j + 1].y);
			p1 = p1*m_ViewMtrx; p2 = p2*m_ViewMtrx;
			pDC->MoveTo(p1.x,p1.y);
			pDC->LineTo(p2.x,p2.y);
		}
	}
	pDC->SelectObject(pOldPen);
	NewPen.DeleteObject();
}

五、视图变换与交互

视图变换首先要了解一下二维坐标的基本变换,可以看我的博客【二维坐标基本变换】。交互这里只介绍视图的交互,左键拖动视图,右键重置默认视图,鼠标滚轮实现视图的放大缩小。对于拖动视图,原本应该涉及到3个鼠标响应事件:鼠标点下,移动和抬起,但实际上用后2个就足够了,在鼠标移动事件中添加一个flag,识别鼠标左键是否处于按下的状态MK_LBUTTON。

void CDXFminiView::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	this->SetFocus();
	CDXFminiDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	bool flag =(nFlags &MK_LBUTTON);
	if (pDoc->m_mydxf != NULL&&flag)
	{
		CPoint2D sp(point.x, point.y);
		pDoc->m_mydxf->TransformViewMtrx(sp);
		Invalidate();
	}
	CView::OnMouseMove(nFlags, point);
}

void CDXFminiView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	CDXFminiDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	bool flag = (nFlags &MK_LBUTTON);
	if (pDoc->m_mydxf != NULL&&!flag)
		pDoc->m_mydxf->m_movepoint = pDoc->m_mydxf->m_ScreenCenter;
	CView::OnLButtonUp(nFlags, point);
}

void CDXFminiView::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	CDXFminiDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (pDoc->m_mydxf != NULL&&nFlags)
	{
		pDoc->m_mydxf->InitialViewMtrx(rect.right, rect.bottom);
		Invalidate();
	}
	CView::OnRButtonDown(nFlags, point);
}

BOOL CDXFminiView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	CDXFminiDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	/*bool flag = nFlags;*/
	if (pDoc->m_mydxf != NULL/*&&flag*/)
	{
		float scale0 =1.0+ 0.1*zDelta / 50;
		pDoc->m_mydxf->ZoomViewMtrx(scale0);
		Invalidate();
	}
	
	return CView::OnMouseWheel(nFlags, zDelta, pt);
}

这里以缩放函数为例说明一下如何连接到视图更新的,在该函数里我们修改的是m_ViewMtrx这个矩阵,注意在绘制函数中,待绘制的点都会乘上这一个矩阵,同样的移动和重置函数也是通过更新这个矩阵来连接的。

void MyDxf::ZoomViewMtrx(float &t)
{
	m_zoomtimes *= t;
	CMatrix2D transfMtrx1, transfMtrx2, zoomMtrx;
	VECTOR2D t2, t3;
	t3.dx = m_dxfbbox.cx;
	t3.dy = m_dxfbbox.cy;
	t2.dx = -t3.dx;
	t2.dy = -t3.dy;
	transfMtrx1 = transfMtrx1.CreateTransfMatrix(t2);
	transfMtrx2 = transfMtrx2.CreateTransfMatrix(t3);
	zoomMtrx = zoomMtrx.CreateScaleMatrix(t);
	m_ViewMtrx = transfMtrx1*zoomMtrx*transfMtrx2*m_ViewMtrx;
}

最后实现的效果如下:

当然,这个小应用还有很多不足,比如直径符号,文本的位置等等,尚待研究。

  • 14
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值