CSDN博客

img yako

arx程序说明(一)

发表于2004/10/12 19:54:00  1934人阅读

分类: Research

第一章 

该程序开发的是一个AutoCAD动态连接库,下面是动态连接库的入口函数,在入口函数中用一个case语句对AutoCAD的两个消息进行响应,分别是AcRx::kInitAppMsgkUnloadAppMsg,如下面的程序段所示

extern "C" AcRx::AppRetCode

acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)//动态连接库入口函数。

{

       switch (msg) {

       case AcRx::kInitAppMsg:

              // Comment out the following line if your

              // application should be locked into memory

              acrxDynamicLinker->unlockApplication(pkt);

              acrxDynamicLinker->registerAppMDIAware(pkt);

              InitApplication();//初始化函数

              break;

       case AcRx::kUnloadAppMsg:

              UnloadApplication();//卸载函数

              break;

       }

       return AcRx::kRetOK;

}

1、初始化函数InitApplication()

下面是ARX向导为程序自动添加的代码段

void InitApplication()//初始化函数

{

       //{{AFX_ARX_INIT

       AddCommand("GREEN", "DISPLAYWALL", "DISPLAYWALL", ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET | ACRX_CMD_NOINTERNALLOCK, greendisplaywall);

       AddCommand("GREEN", "SCANROOM", "SCANROOM", ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET | ACRX_CMD_NOINTERNALLOCK, greenscanroom);

       AddCommand("GREEN", "HIGHTLIGHT", "HIGHTLIGHT", ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, greenhightlight);

       AddCommand("GREEN", "HIGHLIGHTLINE", "HIGHLIGHTLINE", ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, greenhighLightLine);

       AddCommand("GREEN", "CREATDOEFILE", "CREATDOEFILE", ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, greencreatdoefile);

       //}}AFX_ARX_INIT

}

在该代码段中,注册用户开发的AutoCAD外部命令,在该代码段中共注册了SCANROOM DISPLAYWALLHIGHTLIGHT以及HIGHLIGHTLINE,分别用于对工程图中的线条信息进行扫描、显示分析得到的墙体、以及进行房间线条的高亮显示和具体单个墙体的高亮显示。下面以SCANROOM为例对代码段中的注册外部命令的函数AddCommand()进行说明。

       AddCommand("GREEN", //组名

"SCANROOM",//注册命令的外部名称

 "SCANROOM", //注册命令的外部名称

ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET | ACRX_CMD_NOINTERNALLOCK, //命令使用方式说明

Greenscanroom//命令执行时调用的函数模块

);

上面的命令SCANROOM执行时调用函数Greenscanroom(),在初始化中的另外几个命令分别调用函数greendisplaywall()、greenhightlight()和greenhighLightLine()。

Greenscanroom()函数用于对AutoCAD图中中的信息采用自顶向下的方法进行分析形成各种墙体信息,然后对墙体信息采用一种综合图论的支撑树算法和节点可见边有向排序算法的综合算法,对设计中的各个房间进行识别,关于自顶向下的方法形成墙体信息以及综合算法识别房间的详细介绍请参考论文,在笔者的论文中有详细的C伪代码,并且在本人的源代码段中都有详细的注释。下面分别对不同命令对应的函数进行介绍

1SCANROOM命令执行时调用的函数Greenscanroom()

函数的代码如下:

void greenscanroom()

{

       scanLines("wall",false);/*得到的信息保存在linehead中,linehead为一个全局变量,用于保存图纸信息。*/

       refineLineSet();/*对获得的信息进行初步处理,对一些由于明显作图错误的问题进行处理*/

       CLineSet *mid=new CLineSet();//*生成一个新的中心线集合,用于保持在分析中形成的中心线*/

       CLineSet *midwall=new CLineSet();//生成一个集合,用于保存中间墙体的中心线。

       make_whole_zone_mid(&linehead,mid,midwall);/*采用自顶向下的方法对墙体信息进行恢复*/

       cut_middle_arc();//为一个空函数,预留用于对圆弧进行处理

       displayMid(mid);//以墙体中心线为参数,在AutoCAD中显示中心线。

       linehead.freeSet();///释放图纸信息

       mid->freeSet();释放中心线占用的内存空间

}

这里要对函数中调用的make_whole_zone_mid()函数进行着重说明,在这个函数中,它的入口参数是lines,它是一个保存有图中线条信息的集合,在程序中对这个集合中的信息进行扫描,区分出墙区和中间墙区,对于墙区中的线条,采用凹陷点的办法识别中间的半墙,形成中心线条;对于中间墙区,生成两个方向的中心线,记录到入口参数midwall集合中

#define chendebug

void make_whole_zone_mid(CLineSet *lines,CLineSet *mid,CLineSet *midwall)

{

       /*※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

       对整个图形中的线条lines先分墙区进行中心化,把墙区的中心线都加入到mid链表中,

       对于中间墙体生成两条中心线,加入到midwall链表中

       ※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※*/

       CLineSet *head=NULL,*givenLine=NULL;//定义头指针和给定线条指针

       int amount=0;

       CLineSet *p=NULL;//定义扫描指针

       CLineSet *p1=NULL;

       while(1)//循环进行墙区和中间墙区的处理,直到所有线条信息处理完毕

       {

              givenLine=lines->pNext;

              lines->pNext=givenLine->pNext;

              givenLine->pNext=NULL;

              head=findLineCircle(lines,givenLine,amount);/*以给定线条为参数,进行墙区识别*/

              if(amount==4)//找到的是一个中间墙区

              {

                     //make the mid line of all direction and add them to the midwall list

                     p=head->pNext;

                     p1=p->pNext;

                     CLineSet *tempmid=new CLineSet(p->theLine->makeMid(p1->pNext->theLine));

                     tempmid->pNext=midwall->pNext;

                     midwall->pNext=tempmid;

                     p=p1;

                     p1=p->pNext;

                     tempmid=new CLineSet(p->theLine->makeMid(p1->pNext->theLine));

                     tempmid->pNext=midwall->pNext;

                     midwall->pNext=tempmid;

                     p=p1=NULL;delete head;

              }

              else/找到的是一个墙区

              {

                     double thick=0;

                     CLineSet *midline=make_zone_midLine(head,thick);/*生成墙区中半墙的中心线*/

                     cut_middle_line(midline,thick);//对形成的中心线进行分割

                     match_zone_mid(midline);//应用极小完整性原则,对一个墙区中的墙体进行匹配

                     p=midline->pNext;

                     while(p->pNext)p=p->pNext;

                     p->pNext=mid->pNext;

                     mid->pNext=midline->pNext;

                     midline->pNext=NULL;

                     delete midline;

              }

              amount=0;

              if(!lines->pNext)break;

       }

       //所以线条扫描完毕

       CLineSet *unmatched=new CLineSet();/*形成一个新的集合,记录不能进行匹配的线条*/

       match_whole_map(mid,unmatched);/*对于所有墙区的中心线,应用极小完整性原则进行匹配,匹配成功的线条加入到mid集合中,不能进行匹配的加入到unmatched集合中*/

       fix_windows(mid,midwall);//安装门窗,其中midwall为中间墙体两条中心线所在的集合。

       delete midwall;//at last free the memory the mid wall occupied

       deal_with_abnormal(mid,unmatched);//对不能匹配的线条进行处理

       p=unmatched;

       while(p->pNext)p=p->pNext;/*如果还有没有匹配好的线条把它直接加入到中心线集合中,在以后进行显示给用户,让用户进行处理。*/

       if(p)

       {

              p->pNext=mid->pNext;

              mid->pNext=unmatched->pNext;

              unmatched->pNext=NULL;

       }

       delete unmatched;

}

该函数中用到的函数如下:

aCLineSet *findLineCircle(CLineSet *wholeList,CLineSet *givenLine,int &amount)

该函数以给定直线givenLine为起始直线,在直线集合中搜索与起始直线能够围合成墙区或者半墙区的其它直线,并记录围合成区域的直线条数,用于区别是半墙区域还墙区,区别的特征是半墙区域围成的直线条数是4条,而墙区的数量大于4条。在make_whole_zone_mid()函数中对中间墙区简单的生成了两个方向的中心线条,而对于墙区调用下面的函数进一步处理。搜索到的直线组织成一个直线链表,进行返回。

BCLineSet *make_zone_midLine(CLineSet *List,double &thickness)

       该函数对给定的围合区域,求出墙体的中心线组成一个链表返回。

       参数List为调用findLineCircle()之后得到的围合成墙区的线条链表,thick为墙体厚度。

       该函数的利用一个墙体和其它墙体的交接的地方总是形成凹陷点的情况对墙区进行分割,并把分割出来的墙体形成它的中心线,组织成链表进行返回,关于该函数的详细描述参考论文和源代码。

Cvoid cut_middle_line(CLineSet *lines,double &thickness)

该函数用于对生成的墙体中心线按照墙体的中心线相交情况进行分割。其中:midline为的中心线链表,分割出来的新的线条仍然加入到该链表中;thick为墙体厚度;

dvoid match_zone_mid(CLineSet *mid)

该函数用于一个墙区的每一个墙体确定它的状态,一个墙体的状态反映的是有没有其它的墙体通过它的端点。

Evoid match_whole_map(CLineSet *mid,CLineSet *unmatched)

该函数用于对所有墙区还没有匹配好的半墙按照极小完整性原则进行匹配,不能进行匹配的半墙线加入到unmached链表中,mid为半墙中心线。

Fvoid fix_windows(CLineSet *whole,CLineSet *winlines)

该函数用于对中间墙区的中心线找到他们所在的墙体,对它们进行分割出门洞位置。其中:mid为完整墙体中心线,midwall为中间墙区的中心线。

g) void deal_with_abnormal(CLineSet *whole,CLineSet *abnormal)

该函数用于对一些异常匹配进行处理,也就是调用函数match_whole_map()之后还不能进行匹配的墙体线条进行特殊处理,关于特殊处理的原则见论文,详细代码参考源代码。

在命令scanroom中调用函数greenscanroom(),该函数按照不同的条件依不同的次序调用上面介绍的不同函数,最后调用函数void displayMid(CLineSet *lines),对分析得到的墙体以及墙体上的门洞线条,在AutoCAD中新开辟一个图层arx_wall如果分析结果不正确,让用户在新图层arx_wall上进行修改。

2displaywall命令对应函数

在执行scanroom命令之后形成了表达墙体的中心线,调用displaywall命令对这些中心线进行分析,形成表达建筑平面空间的房间,该命令最后以对话框的形式让用户设置建筑空间的其它非几何属性。下面是该函数的详细源代码:

void greendisplaywall()

{

       myArcSet *scanedArcSet=new myArcSet();

       scanedArcSet->pNextArc=arcHead.pNextArc;//store the arc set in a new set

       arcHead.pNextArc=NULL;//empty the arc set getted before

       scanLines("arx_wall",true);//重新获得用户修改之后的墙体中心线

       compareArc(&arcHead,scanedArcSet);

       if(scanedArcSet->pNextArc)//the scaned arc set is not empty yet

       {

              //find the lines and make a wall,and use the line to make a win of itself

              find_line_arc_win(scanedArcSet,&linehead);

       }

       //free the not found if any

       myArcSet *p=scanedArcSet->pNextArc,*p1=NULL;

       scanedArcSet->pNextArc=NULL;

       delete     scanedArcSet;

       while(p)

       {

              p1=p->pNextArc;

              delete p;p=p1;

       }

       arc_to_line(&arcHead,&linehead);

       //getHouse();//以前废弃的函数

       Room *temp=findRoom(&linehead);//调用findroom函数,形成表达建筑空间的数据

       RoomSet.nextR=temp->nextR;//把找到的空间组织成链表

#if defined chendebug//测试信息

              {

                     CLineSet *pCurrent=RoomSet.nextR->bases->pNext;

                     AcDbEntity *pEn=NULL;

                     acdbOpenObject(pEn,pCurrent->theLine->lineID,AcDb::kForWrite,false);

                     pEn->close();

              }

#endif

       temp->nextR=NULL;delete temp;

       RoomSet.make_whole_room();//根据房间的相邻性质,设置他们的各种属性

       RoomSet.SetRoomHeight(Floor_Height);//the the rooms' height in the roomSet and set the material struction of all walls

       CAcModuleResourceOverride resOverride;//注册资源,用于对对话框资源申请

       Room *pR=RoomSet.nextR;

       while(pR)

       {

              pR->set_block();//对各个房间的地面进行分块

              pR=pR->nextR;

       }

       pDlg=new LineDisplayer(acedGetAcadFrame());/*分配对话框资源,从而显示用户界面*/

       pDlg->Create(IDD_DISPLAYER);//根据资源的标号,创建对话框资源

       pDlg->ShowWindow(SW_SHOW); //显示对话框,进入用户界面

}

下面对该函数中的重要函数Room *findRoom(CLineSet *wholeLine)进行介绍。

函数findRoom()用于实现整个综合平面面基域的划分,它调用上面的各个函数。其中wholeLine为整个拓扑图中的边的链表,该函数把找到的面基域生成响应的空间并组织成空间链表进行返回。该函数的算法思想是结合图的支撑树和节点可见边旋转有向排序进行的,详细请参考论文,下面是该函数的完整源代码:

Room *findRoom(CLineSet *wholeLine)

{

       CMyPoint *oldLayer=new CMyPoint(),*newLayer=new CMyPoint();/*定义并开辟两个节点层的头节点*/

       CLineSet *p=NULL, *p1=NULL, *p2=NULL, *p3=NULL;//线条遍历指针

CLineSet *upLine=new CLineSet();/*用于记录已经访问过的边,称之为上层线条链表*/

       /*以链表中的第一条线条的两个端点生成两个节点分别加入到上层节点层和新节点层中,并把该线条转移到上层线条链表*/

       p=wholeLine;p1=wholeLine->pNext;

       p->pNext=p1->pNext;

       p1->pNext=upLine->pNext;upLine->pNext=p1;

       newLayer->nextPoint=new CMyPoint(p1->theLine->theE->x,p1->theLine->theE->y,0);/*生成上层节点层的第一个节点*/

       oldLayer->nextPoint=new CMyPoint(p1->theLine->theS->x,p1->theLine->theS->y,0); ;/*生成新节点层的第一个节点*/

       CMyPoint *pp=NULL,*pp1=NULL,*pp2=NULL,*pp3=NULL;/*定义节点层访问指针*/

       CLineSet *foundList=NULL,*baseList=NULL;//定义节点能够访问到的边集

       CLineSet *foundLine=NULL;//定义记录余树边的指针

       Room *roomList=new Room();//定义记录空间的链表

       bool pointfound=false;//定义节点是否访问的标志变量

       CLineSet *failedLine=new CLineSet();/*定义线条链表,记录暂时不能找到面基域的余树边,称之为失败链表*/

CMyPoint *newLayerIterator=newLayer->nextPoint;/*定义指向新节点层最后节点的指针*/

       while(1)//开始对整个拓扑图进行遍历

       {

              pp=oldLayer; //初始化层节点指针

              pp1=pp->nextPoint;

              while(pp1)//对于上层节点中的每一个节点进行广度遍历

              {

                     //wholeLine链表中寻找所有通过该点的直线

                     foundList=findLineThroughPoint(wholeLine,pp1);

                     if(!foundList)//余下的线条中没有通过该点的直线,

                     {

                            pp=pp1;// 改变条件对该层中的下一个节点进行广度遍历

                            pp1=pp1->nextPoint; continue;

                     }

                     p=foundList;p1=p->pNext;

                     while(p1)//对通过当前节点的所有边

                     {

                     //检查该边的另一个端点分别是否已经在上层和下层节点链表中

                            pointfound=checkPoint(p1->theLine->theE,*oldLayer);

if(!pointfound)pointfound=checkPoint(p1->theLine->theE,*newLayer);

                            if(!pointfound)//通过该边访问到的节点是新节点

                            {

                                   //在下层节点链表的最后添加该新节点

                                   newLayerIterator->nextPoint=new CMyPoint(

                                          p1->theLine->theE->x,p1->theLine->theE->y,0);

                                   if(!pointAdd)pointAdd=newLayerIterator;

                                   newLayerIterator=newLayerIterator->nextPoint;

                            }

                            else//遍历发现余树线

                            {

                                   Room *tempRoom=getRoombaseList(upLine,p1);/*搜索面基域*/

                                   if(!tempRoom)//寻找面基域失败

                                   {

                                          //当前边转移到失败链表中

                                          failedLine->pNext=p1; p->pNext=p1->pNext;

                                          p1=p->pNext; continue;

                                   }

                                   tempRoom->nextR=roomList->nextR;//面基域寻找成功

                                   roomList->nextR=tempRoom;tempRoom=NULL;

                            }

                            /*转移当前边到上层边集中,并移动边指针到该节点访问到的下一条边*/

                            p->pNext=p1->pNext; p1->pNext=upLine->pNext;

                            upLine->pNext=p1; p1=p->pNext;

                     }//end while(p1)//结束对当前节点的遍历

                     pp=pp1;pp1=pp1->nextPoint;/*移动节点指针,对下一个节点进行遍历*/

              }//end while(pp1),当前层的所有节点广度遍历完毕

              /*对失败的线条,在对当前层的所有节点访问之后再次试图搜索它对应的面基域*/

              p=failedLine;

              while(p->pNext)//the point that have visited failed at searching for room

              {

                     p1=p->pNext;     

                     Room *tempRoom=getRoombaseList(upLine,p1);

                     if(tempRoom)

                     {

                            tempRoom->nextR=roomList->nextR;

                            roomList->nextR=tempRoom;tempRoom=NULL;

                            p->pNext=p1->pNext; p1->pNext=upLine->pNext;

                            upLine->pNext=p1; p1=p->pNext;

                     }

                     else

                     { p=p1; p1=p1->pNext; }

              }

              /*上层的点都已经搜索到,删除上层上的点,以下层为新的上层,

              重新进行循环,如果没有下层点,表示整个过程已经完成*/

              //delet the point in the unlayer

              pp=oldLayer; pp1=pp->nextPoint;

              while(pp1)

              {

                     pp->nextPoint=pp1->nextPoint;

                     pp1->nextPoint=NULL;delete pp1;

                     pp1=pp->nextPoint;

              }

              if(foundList)

              {

                     foundList->pNext=NULL;

                     delete foundList;foundList=NULL;

              }

              /*如果新节点层中有其它的节点,以新节点层为上层节点层,深入遍历*/

              if(newLayer->nextPoint)

              {

                     oldLayer->nextPoint=newLayer->nextPoint;

                     newLayer->nextPoint=NULL;

                     newLayerIterator=newLayer;

              }

              else break;// 否则结束整个遍历

       }//end while(1)

       delete oldLayer,delete newLayer;//free the layer node at last

       return roomList;//返回空间链表

}

该函数首先调用函数:

CLineSet *findLineThroughPoint(CLineSet *line,CMyPoint *thePoint)

以给定点为广度遍历起点(给定点是line链表中的第一条直线,具体是那一点由前面的程序确定),搜索line链表中所有通过该点的直线,做为一轮广度遍历能够访问到的节点,该函数把搜索到的直线都调整到起点和给定点重合,并把所有直线组织成链表进行返回。

接下来对搜索到的直线终点进行处理,看是否是新的节点还是已经访问过的节点,同时把所有在遍历过程中访问过的边加入到遍历的上层边upLine中,同时记录遍历支撑树的当前层和上一层,用于识别正在访问的节点是新节点还是已经访问过的节点,这调用函数:

bool checkPoint(CMyPoint *givenPoint,CMyPoint PointList)就能进行判断,其中givenPoint是访问到的节点,PointList用于指定是在当前层上还是在上一层进行搜索,搜索的原则是先在上一层上进行搜索,没有搜索到再在当前层上搜索。是新节点加入到当前层,否则调用函数:

Room *getRoombaseList(CLineSet *list,CLineSet *givenLine)

其中:

list传递给图中已经访问过的上层边集,givenLine是发现的余树边,函数返回由余树边确定的面基域。并用面基域上的边生成一个房间进行返回。

函数最后显示用户非几何属性输入界面,关于这个方面留在后面进行介绍。

3)命令hightlight对应的函数green greenhightlight()

void greenhightlight()

{

       if(pHighLight==pHighLighted)//判断要进行高亮显示的房间和已高亮显示的是否是同一房间

       {

              hightLight(pHighLight,false);//是则,把这个房间反高亮显示,表示还原所有高亮显示

              return;

       }

       if(pHighLighted)hightLight(pHighLighted,false);反高亮显示原房间,

       hightLight(pHighLight,true); 高亮显示选定的房间,

       pHighLighted=pHighLight;//记录

       pHighLight=NULL;

}

该函数配合全局遍历Room * pHighLightRoom * pHighLighted对在用户界面中选择的房间在AutoCAD中进行高亮显示,其中pHighLighted用于保存已经高亮显示的房间,pHighLight是要进行高亮显示的房间。这个命令是在用户界面中以命令语句的形式发送的AutoCAD进行执行,具体的,先对pHighLight全局遍历进行赋值,然后发送命令,下面是具体的程序片断:

    pHighLight=curRoom;//对全局变量进行赋值

    acDocManager->sendStringToExecute(acDocManager->curDocument(),

           "_hightLight ",false, true);//发送hightLight命令

4)命令hightlightLine对应的函数greenhightlightLine()

void greenhighLightLine()

{

       if(pHighLighted)hightLight(pHighLighted,false);//unhighLight all the wall first

       if(objectId==highedId)return;//the same line Id

       AcDbEntity *pEn=NULL;

       acdbOpenObject(pEn,highedId,AcDb::kForWrite,true);

       if(pEn)

       {

              pEn->unhighlight();

              pEn->close();//close the pointer;

       }

       acdbOpenObject(pEn,objectId,AcDb::kForWrite,true);//open the new object

       if(pEn)

       {

              pEn->highlight();

              pEn->close();

              highedId=objectId;//save

       }

}

和上面的高亮显示房间基本功能相似,不同的是显示房间要对多条直线进行高亮显示,这个函数只是对代表选定的一个墙体的直线进行高亮显示。同样的该函数用两个全局变量进行,这两个全局遍历为:

AcDbObjectId objectId;//要进行高亮显示的直线Id

AcDbObjectId highedId;//已高亮显示的直线的Id

至此,本人编写的命令入口以及函数都介绍完毕!

阅读全文
0 0

相关文章推荐

img
取 消
img