综合实验二简易图形编辑器实验报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
综合实验二“简易图形编辑器”实验报告
张良钿20048002129 一.总体展示:
下图是我用自己写的“简易图形编辑器”编辑的图形的全屏截屏图片。图中的“indows”文本,“W”是用四条直线拼合而成。所有图形都是在画出后用鼠标选中并调整位置和形状的,右侧的是非模态窗口的“工具栏”。图形源文件为此压缩包中的sky.sky文件中,可用本编辑软件打开。
二.开发过程:
1. 预期目标(暨需求分析):
A.完成基本功能:交互式绘制图形(至少包括矩形和文本),绘制完成的图形能够正确地重现,绘制结果可以保存到文件并读出;
B.完成高级要求:矩形、文本的颜色、文本字体可调,能够通过鼠标点击选中图形,并进行属性的修改
C.拥有“工具栏”、“状态栏”等,使程序具有更好的人机交互性;
D.支持“剪贴”、“复制”、“粘帖”、“删除”等操作,能够直接定位图元并编辑。
2. 数据结构选择(开发中最重要的环节):
由于对C++还不熟悉,因此考虑的都还是C中的方法。用结构封装图元和文本,图元结构MYGRAP和文本结构MYTEXT,详见源程序中的MyDoc.h文件。
使用动态申请内存的数据存储方法,具体的方案考虑过三种,具体如下:
A.动态数组:像梁老师上课时介绍的那样,当存储空间不足时,new出更大的一块内存,然后memcpy,再delete原空间。优点:简单,易于理解和操作。缺点:运行效率不高,
尤其是当数据量较大时,每次都要向内存池申请一块很大的连续的内存,然后memcpy 大量数据,浪费了大量内存和CPU运行时间,虽然现在的内存和CPU性能很好,但它们是给用户用的,而不是给程序员使用的。因此在不影响开发效率的情况下,应尽量提高运行效率。
B.双向链表:链表的运行效率明显高于动态数组,且易于构建图层的优先级结构(即图元重叠是的上下顺序)。故在刚开始的时候,我采用了双向链表的结构,并完成了双向链表相关函数的编写(在MyDoc.cpp文件末尾被注释的部分)。但在开发过程中,由于需要能根据图层序号直接定位图层,这样就需要从链头或链尾搜索。于是萌生了对链表进行改造的想法。
C.映射表:我不是是否有这种数据结构,也不知道它的正确名称,因为这是我自己构思出的,姑且称其为“映射表”。它由一系列动态的离散的节点和一个动态的数组表构成。
详细结构如下图所示:
映射表中的每一个元素都是一个指针,指向不同的离散的MYGRAP或MYTEXT的结构便量。表的管理策略同动态数组一样,先申请一个一定长度的数组,当表不够长时,new出一个更长的数组,然后memcpy,再删除原数组。由于数组的每个元素存储的只是一个指针,占用的内存很小,只需很少的资源即可动态整理。每个节点的管理策略同链表的节点相同,都是new和delete的。映射表的优点:运行和存储效率基本接近链表,但可以根据层序号直接定位图元节点,所有的添加、删除、查找、插入、交换层顺序等操作都只需要对映射表进行整理即可,操作方便快速。缺点:增加了一个结构层次,不易理解。
经比较,我选择了映射表的结构,由于MYGRAP和MYTEXT两种结构不同,尤其是MYTEXT中还有一个字符串指针,因此虽然从存储原理上两种结构的指针没有区别,但仍需一个PMYGRAP和一个PMYTEXT分别指向才能通过编译,连void *也不行。于是构造了一个新结构LAYERMAP作为映射表数组的元素,如下:
typedef struct _LAYERMAP_
{
int iFlg; // GRAPDATA or TEXTDA TA,
PMYGRAP pGData;
PMYTEXT pTData;
//void *pData; // pointer to MYGRAP or MYTEXT
} LAYERMAP, *PLAYERMAP;
每次在访问图元节点时都要先判断iFlg,再决定使用pGData还是pTData,增加了程序的复杂度,不得以而为之,应该还有更好的解决策略,我还没想到。
数据结构及其相关函数在源文件MyDoc.cpp中,函数的设置有点C++的味道,可以方便地做成一个类。
3. 具体开发:
选定数据结构,写好MyDoc.cpp后,工作以及完成了一半。很快就完成了基本功能的实现和调试。开始完成扩展功能。
A.实现鼠标选中图元和编辑图元。
希望能实现类似MS-Word中的图形编辑那样的效果。
a.鼠标点击选中最外层的图元,编写函数bool SearchLayer(POINT *a_pPT, int
*pCurLayerCnt),其中调用了判断点在直线上的函数bool PointInLine(POINT *a_pt, POINT *a_pStart, POINT *a_pEnd, int a_LineWidth),判断点在矩形内的函数bool PointInRect(POINT *a_pt, POINT *a_pStart, POINT *a_pEnd),判断点在椭圆内的函数bool PointInEllipse(POINT *a_pt, POINT *a_pStart, POINT *a_pEnd)。编写加调试共约2小时,对数学公式做了化简和近似,确保了运算量的最小化,调试结果良好,矩形、椭圆、直线、文本框都能准确辨认。
b.绘制图元被选中时的标志(外框上的四个小圆和四个小矩形)以及鼠标在图元附件的不
同Cursor。编写了函数bool DrawSltFlg(HDC hdc, PMYGRAP a_pFlgGrap, PLAYERMAP a_pLayer, int a_DrawOrClear),和bool DrawSltFlg(HDC hdc, PMYGRAP a_pFlgGrap, PLAYERMAP a_pLayer, int a_DrawOrClear),并大量增补了主消息处理函数WinProc(),编写加调试用了2~3小时,实现效果良好。但整个程序结构开始混乱,尤其是WinProc()过于冗长,程序流程在七种操作模式间跳转,结构不清晰(自己转晕了)。CurrOpMode;
#define RYDRAW 1 // 预备绘图
#define DRAWING 2 // 正在绘图
#define RYTEXT 3 // 准备输入文本
#define TEXTRECTING 4 // 正在绘制文本矩形框
#define TEXTINPUTING 5 // 正在输入文本
#define RYSELECTAGRAP 6 // 预备选择图元
#define SELECTEDAGRAP 7 // 已经选择中了一个图元
#define MOVEINGAGRAP 8 // 移动一个图元
#define MODIFYINGAGRAP 9 // 改变一个图元的形状
c.绘制图元移动和修改过程中的虚线框,编写完代码后发现程序运行混乱了,一时间也查
不出原因,就将其删除或注释了,此功能先搁置。
d.移动或修改选中的图元,编写了函数bool MouseAroundPoint(POINT *a_ptMoser, POINT
*a_ptCenter),其余的都在WinProc()中增添,加剧了WinProc()的混乱度。耗时约3小时,完成编写与调试。移动和修改功能都已实现,实现效果还不错,但由于没有了虚线框,移动过程的可视化与交互性不够。
B.“工具栏”的开发,用一个dialog绘制工具栏,将其创为非模态对话窗。原想使用TAB Contrl,但由于从没用过,估计要花不少时间学习,而离交作业的最后期限很近了,就用了两个BUTTON代替TAB来切换“图层和图元”界面,编写了ToolBar.cpp文件,编写是特别注意了其可扩展性。“图元”界面用于选择图元操作,“图层”界面用于显示和修改当前选中的图元的参数信息,提供了修改图元的快捷方式。由于时间紧迫以及程序的结构已经混乱,代码尚未编写完成,“图层”功能搁置。
C.“剪切”、“复制”、“粘帖”的功能不难写,由于时间原因未编写。“撤销”功能,我目前还没想出好的策略,似乎只能开辟空间暂存操作记录和数据。“删除”功能是在绘制实验报告的那幅图时,发现没有“删除”很是不便,才临时增加的。由于MyDoc.cpp中相关函数