免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123
最近访问板块 发新帖
楼主: jianjiandandan
打印 上一主题 下一主题

如何求四条直线所围成的四边形的顶点 [复制链接]

论坛徽章:
0
21 [报告]
发表于 2007-09-30 09:37 |只看该作者
正在编写,请大家不要着急啊,本人编码的能力比较弱,
只好慢工出细活了,等做完了,请大家帮我参考参考啊!

论坛徽章:
0
22 [报告]
发表于 2007-10-09 12:54 |只看该作者

国庆节没事,写了一个函数,似乎可以解决这个问题

原帖由 jianjiandandan 于 2007-9-28 15:57 发表
小弟近来遇到一个问题如下:
已知四条直线的方程,判断出这四条直线是否围成了一个四边形,
如果围成了一个四边形,那么请求出这四个顶点。
程序倒是编出来,但是感觉很丑陋,代码也很长。
现在向各位大侠请 ...


请大家斧正

问题描述
=======================================================================
平面中有四条直线,其位置任意,现需要判断这四条是否围成四边形。如果
围成四边形,则求出所有的四边形(可以是凸,也可以是凹)的顶点。
原问题的描述见:http://bbs.chinaunix.net/thread-997555-1-4.html
=======================================================================
问题分析
=======================================================================
其基本的解决思路是:
1).构造一个张4*4的表,来表示四条直线之间的相互关系,是否相交?如果
不相交,则求出其交点。
2).从第一条直线出发,找到与之相交的另外一条直线,并记录它们交点到
交点集中去;
3).从找到的这条直线上,找到到另外一条直线,及其交点,且这个焦点不在
已记录的交点集中,如果满足上述条件的点找不到,则回溯前一步的结果(即回
到前一步,删除加入到交点集中的点,然后找下一个点),如果满足上诉条件的
点找到,则继续重复步骤3;
4).上述步骤3种,如果已经找到满足条件的四个点,且从最后一个点出发,
可以到达第一个出发的点,那么则构成我们需要找的四边形,这时就顺序打印四
边形的四个顶点。如果不能到达,则向上回溯;
5).如果从第一条直线的所有的与之相交的直线,均查找完毕,则搜索结束。

=======================================================================

所有源代码

  1. /*
  2. *
  3. *
  4. **qrangle.h**
  5. *
  6. *
  7. */
  8. #ifndef _QRANGLE_H_
  9. #define _QRANGLE_H_
  10. struct point {
  11.         float x;
  12.         float y;
  13. };   // 交点
  14. struct line {
  15.         float a;
  16.         float b;
  17.         float c;
  18. };   // 直线    利用形如 ax+by+c=0 的形式表示直线
  19. struct linerela {
  20.         int bIntersect;
  21.         struct point scon;
  22.         struct linerela *pnext;
  23. };   // 直线关系结构,也用来存放结果
  24. // bIntersect = 0   两条直线平行
  25. //      = 1   直线相交
  26. // struct point scon;   直线交点,只在bIntersect=1 是表示交点
  27. // 表示结果是,bIntersect没有意义,scon存放定点
  28. // 所有的定点通过 pnext连接
  29. #define LINENUM   4 //我们找四边形,有四条直线
  30. #endif

  31. /*
  32. *
  33. *
  34. **qrutils.h**
  35. *
  36. *qrutils.h定义了库函数的API,该文件与qrangle.h一起,都需要export给用户
  37. */
  38. #ifndef _QRUTILS_H_
  39. #define _QRUTILS_H_
  40. #include "qrangle.h"
  41. int   find_rangle(struct line *psLine, struct linerela **psRes);
  42. void show_result(struct linerela *pRes);
  43. void free_mem(struct linerela *pRes);
  44. #endif

  45. /*
  46. *
  47. *
  48. **qrangle.c**
  49. *
  50. * 利用一个接口API写的一个测试用例
  51. */

  52. #include <stdio.h>
  53. #include "qrangle.h"
  54. int main(void)
  55. {
  56.         struct line asLine[LINENUM] = {
  57.                 {1, -1, 1},
  58.                 {1, 1, -1},
  59.                 {3, -2, -2},
  60.                 {3, 2, 2}
  61.                 //{1, 1, 1},
  62.                 //{1, -1, 1},
  63.                 //{1, 1, -1},
  64.                 //{1, -1, -1}
  65.         };
  66.         struct linerela *psRes = NULL;
  67.         int iFind;
  68.         iFind = find_rangle(asLine, &psRes);

  69.         if ( NULL != psRes ) {
  70.                 show_result(psRes);
  71.                 free_mem(psRes);
  72.         }
  73.         return 0;
  74. }

  75. /*
  76. *
  77. *
  78. **qrutils.c**
  79. *
  80. * 库函数文件,提供具体的实现
  81. */

  82. #include <stdio.h>
  83. #include "qrutils.h"
  84. #include "qrangle.h"
  85. #define DET(a1,b1,a2,b2) ((a1)*(b2)-(a2)*(b1)) //计算行列式 ((a1,b1),(a2,b2))
  86. #define VERY_ZERO    0.00001      //表示一个很小的数,用来浮点数和零比较
  87. #define GET_VALUE(x1,y1,x2,y2,x,y) \     //计算点(x,y)在由(x1,y1),(x2,y2)点构成的直线的计算值
  88.         ( ((y2)-(y1))*(x) + ((x1)-(x2))*(y) + ((x2)-(x1))*(y1) + ((y1)-(y2))*(x1) )

  89. void init_table(struct line *psLine, struct linerela (*psTable)[LINENUM])
  90. { //初始化直线关系表
  91.         int i, j;
  92.         float a1, a2;
  93.         float b1, b2;
  94.         float c1, c2;
  95.         for ( i = 0 ; i < LINENUM ; i ++ ) {
  96.                 for ( j = i ; j < LINENUM ; j ++ ) {
  97.                         a1 = (psLine + i)->a;
  98.                         b1 = (psLine + i)->b;
  99.                         c1 = (psLine + i)->c;
  100.                         a2 = (psLine + j)->a;
  101.                         b2 = (psLine + j)->b;
  102.                         c2 = (psLine + j)->c;
  103.                         if ( 0 == DET(a1, b1, a2, b2) ) {
  104.                                 (*(psTable+j)+i)->bIntersect = (*(psTable+i)+j)->bIntersect = 0; //平行
  105.                         }
  106.                         else {
  107.                                 (*(psTable+j)+i)->bIntersect = (*(psTable+i)+j)->bIntersect = 1; //相交
  108.                                 (*(psTable+j)+i)->scon.x = (*(psTable+i)+j)->scon.x = \
  109.                                                            DET(b1, c1, b2, c2)/DET(a1, b1, a2, b2);   //求交点x坐标
  110.                                 (*(psTable+j)+i)->scon.y = (*(psTable+i)+j)->scon.y = \
  111.                                                            -1 * DET(a1, c1, a2, c2)/DET(a1, b1, a2, b2); //求交点y坐标
  112.                         }
  113.                 }
  114.         }
  115. }

  116. void AppendPoint(struct linerela **ppsRes, struct point sp)
  117. { //把点sp加到链表中
  118.         struct linerela *tmp;
  119.         struct linerela *node;

  120.         node = (struct linerela *) malloc(sizeof(struct linerela));
  121.         node->scon.x = sp.x;
  122.         node->scon.y = sp.y;
  123.         node->pnext = NULL;
  124.         if ( NULL == *ppsRes ) {
  125.                 *ppsRes = node;   //初始链表为空
  126.         }
  127.         else {
  128.                 tmp = *ppsRes;
  129.                 while ( NULL != tmp->pnext ) {
  130.                         tmp = tmp -> pnext;
  131.                 }

  132.                 tmp->pnext = node; //加入链表尾部,其实加到头部也可以,还相对简单些
  133.         }
  134. }

  135. int Exist_In_Result(struct linerela **ppsRes, struct point sp)
  136. { //判断一个点sp,是否已经在链表中
  137.         struct linerela *tmp = *ppsRes ;

  138.         if ( NULL == tmp ) {
  139.                 return 0;
  140.         }

  141.         while ( NULL != tmp ) {
  142.                 if ( fabs(tmp->scon.x - sp.x) < VERY_ZERO && \
  143.                                 fabs(tmp->scon.y - sp.y) < VERY_ZERO ) {
  144.                         return 1;
  145.                 }

  146.                 tmp = tmp->pnext;
  147.         }
  148.         return 0;
  149. }

  150. void Remove_Last_Point(struct linerela **ppsRes)
  151. { //从链表中删除最近加入的节点
  152.         struct linerela *tmp = *ppsRes;
  153.         struct linerela *p = NULL;
  154.         while ( NULL != tmp->pnext ) {
  155.                 p = tmp;
  156.                 tmp = tmp->pnext;
  157.         }
  158.         if ( tmp == *ppsRes ) { //only one point is in the result
  159.                 free(*ppsRes);
  160.                 *ppsRes = NULL;
  161.         }
  162.         else {
  163.                 free(tmp);
  164.                 p->pnext = NULL;
  165.         }
  166. }

  167. int judge_quard(struct linerela *psRes)
  168. { //判断找到的四个点,能否构成一个四边形
  169.         struct point *asTmp[LINENUM];
  170.         int i = 0;

  171.         //先把四个点从链表中拷到数组中,方便操作
  172.         while ( NULL != psRes && i < LINENUM ) {
  173.                 asTmp[i] = &psRes->scon;
  174.                 psRes = psRes->pnext;
  175.                 i ++;
  176.         }
  177.         if ( i < LINENUM ) {
  178.                 return 0;
  179.         }

  180.         //排除有3点共线的情况
  181.         if ( fabs(DET(asTmp[0]->x-asTmp[1]->x, asTmp[0]->y-asTmp[1]->y, asTmp[1]->x-asTmp[2]->x,   asTmp[1]->y-asTmp[2]->y)) > VERY_ZERO &&
  182.                         fabs(DET(asTmp[0]->x-asTmp[1]->x, asTmp[0]->y-asTmp[1]->y, asTmp[1]->x-asTmp[3]->x,   asTmp[1]->y-asTmp[3]->y)) > VERY_ZERO &&
  183.                         fabs(DET(asTmp[0]->x-asTmp[2]->x, asTmp[0]->y-asTmp[2]->y, asTmp[3]->x-asTmp[2]->x,   asTmp[3]->y-asTmp[2]->y)) > VERY_ZERO &&
  184.                         fabs(DET(asTmp[3]->x-asTmp[1]->x, asTmp[3]->y-asTmp[1]->y, asTmp[1]->x-asTmp[2]->x,   asTmp[1]->y-asTmp[2]->y)) > VERY_ZERO ) {
  185.                 float fa = 0.0, fc = 0.0;
  186.                 float fb = 0.0, fd = 0.0;
  187.                 //四个点中,任何3个都不共线
  188.                 //假设四个点 a-->b-->c-->d-->a
  189.                 //判断是否构成四边的依据是,点a,c 在直线bd两侧 或者 点b,d在ac直线两侧
  190.                 //感谢ceh提供了判断思路
  191.                 fa = GET_VALUE(asTmp[1]->x, asTmp[1]->y, asTmp[3]->x, asTmp[3]->y, asTmp[0]->x, asTmp[0]->y);
  192.                 fc = GET_VALUE(asTmp[1]->x, asTmp[1]->y, asTmp[3]->x, asTmp[3]->y, asTmp[2]->x, asTmp[2]->y);
  193.                 fb = GET_VALUE(asTmp[0]->x, asTmp[0]->y, asTmp[2]->x, asTmp[2]->y, asTmp[1]->x, asTmp[1]->y);
  194.                 fd = GET_VALUE(asTmp[0]->x, asTmp[0]->y, asTmp[2]->x, asTmp[2]->y, asTmp[3]->x, asTmp[3]->y);

  195.                 if ( ( fa*fc < (-1 * VERY_ZERO) ) || ( fb*fd < (-1 * VERY_ZERO) ) ) {
  196.                         return 1;
  197.                 }
  198.         }

  199.         return 0;
  200. }

  201. void do_search(struct linerela (*pLineRelaTable)[LINENUM], struct linerela **ppsRes, int iPointNum, int iLineNum, \
  202.                 struct linerela **pResultSet)
  203. { //递归搜索,直到符合条件的四个点
  204.         int i = 0;
  205.         if ( LINENUM ==   iPointNum ) { //已找到了四个点
  206.                 //for循环判断,最后一个点是否能到达第一个点
  207.                 for ( i = 0 ; i < LINENUM ; i ++ ) {
  208.                         if ( 1 == (*(pLineRelaTable + iLineNum) + i)->bIntersect && \
  209.                                         fabs((*(pLineRelaTable + iLineNum) + i)->scon.x - (*ppsRes)->scon.x) < VERY_ZERO && \
  210.                                         fabs((*(pLineRelaTable + iLineNum) + i)->scon.y - (*ppsRes)->scon.y) < VERY_ZERO ) {
  211.                                 if ( judge_quard(*ppsRes) ) { //是否构成四边形
  212.                                         struct linerela *ptmp = *ppsRes;
  213.                                         while ( NULL != ptmp ) { //把这四个点记录到结果集中
  214.                                                 AppendPoint(pResultSet, ptmp->scon);
  215.                                                 ptmp = ptmp->pnext;
  216.                                         }
  217.                                 }
  218.                         }
  219.                 }
  220.                 return;   //回溯……
  221.         }
  222.         for ( i = 0 ; i < LINENUM ; i ++ ) { //从当前直线查找
  223.                 if ( 0 == (*(pLineRelaTable + iLineNum) + i)->bIntersect ) {
  224.                         continue; //平行
  225.                 }
  226.                 if ( Exist_In_Result(ppsRes, (*(pLineRelaTable + iLineNum) + i)->scon) ) {
  227.                         continue; //交点已经加入
  228.                 }
  229.                 AppendPoint(ppsRes, (*(pLineRelaTable + iLineNum) + i)->scon);
  230.                 //加入点,递归查找
  231.                 do_search(pLineRelaTable, ppsRes, iPointNum + 1, i, pResultSet);
  232.                 //go back
  233.                 Remove_Last_Point(ppsRes); //删除加入的点,找其他分支
  234.         }
  235. }

  236. static void remove_duplicate(struct linerela **ppResSet)
  237. { //把结果集中,找到的重复的四边去掉,这个算法,写太复杂了,应该可以改进
  238.         int k = 0, m = 0;
  239.         int notinflag = 0;
  240.         struct linerela *pStart, *pEnd, *pCheckB, *pSave, *pCheckP, *pRemovePre;

  241.         pStart = pEnd = pCheckB = *ppResSet;
  242.         while ( k < LINENUM -1 ) { //设置找到初始查找的位置
  243.                 pCheckB = pCheckB->pnext;
  244.                 k ++;
  245.         }
  246.         pRemovePre = pCheckB;
  247.         pCheckB = pCheckB->pnext;
  248.         while ( NULL != pCheckB ) {
  249.                 notinflag = 0;    //没有重复标志
  250.                 pEnd = pStart;
  251.                 k = 0;
  252.                 while ( k < LINENUM - 1 ) { //去顶比较结束的地方
  253.                         pEnd = pEnd->pnext;
  254.                         k ++;
  255.                 }
  256.                 pSave = pEnd->pnext;
  257.                 pEnd->pnext = NULL;

  258.                 pCheckP = pCheckB;
  259.                 for ( k = 0 ; k < LINENUM ; k ++ ) { //判断是否重复
  260.                         if ( Exist_In_Result (&pStart, pCheckP->scon ) ) {
  261.                                 pCheckP = pCheckP->pnext;
  262.                         }
  263.                         else { //只要有一个定点不同,就不可能是重复的四边形
  264.                                 notinflag   = 1;
  265.                                 break;
  266.                         }
  267.                 }

  268.                 if ( notinflag ) { //没找到,重复下一轮的比较
  269.                         pEnd->pnext = pSave;
  270.                         pStart = pSave;
  271.                         if ( pStart == pCheckB ) { //前面所有的都找过了,没有重复,搜索下一个四边形
  272.                                 k = 0;
  273.                                 while ( k < LINENUM - 1) {
  274.                                         pCheckB = pCheckB->pnext;
  275.                                         k ++;
  276.                                 }
  277.                                 pRemovePre = pCheckB;
  278.                                 pCheckB = pCheckB->pnext;
  279.                                 pStart = pEnd = *ppResSet;
  280.                         }
  281.                 }
  282.                else { //四边形重复了,删除那四个顶点,并继续搜索
  283.                         pEnd->pnext = pSave;
  284.                         pRemovePre->pnext = pCheckP;
  285.                         while ( pCheckB != pCheckP ) {
  286.                                 pEnd = pCheckB->pnext;
  287.                                 free(pCheckB);
  288.                                 pCheckB = pEnd;
  289.                         }
  290.                         pStart = *ppResSet;

  291.                 }
  292.         }

  293. }

  294. int find_rangle(struct line *psLine, struct linerela **ppResSet)
  295. { //从四条直线中,找四边形
  296.         struct linerela **ppsRes;
  297.         struct linerela asLineRelaTable[LINENUM][LINENUM];

  298.         //如果传入的NULL
  299.         if ( NULL == psLine ) {
  300.                 return 1;
  301.         }

  302.         memset(asLineRelaTable, 0, sizeof(struct linerela)*LINENUM*LINENUM);
  303.         //初始化直线关系表
  304.         init_table(psLine, asLineRelaTable);

  305.         *ppsRes = NULL;
  306.         *ppResSet = NULL;
  307.         do_search(asLineRelaTable, ppsRes, 0, 0, ppResSet);

  308.         if ( NULL != ppResSet) { //如果找到了四边形,则去除重复的
  309.                 remove_duplicate(ppResSet);
  310.         }
  311.         return 0;
  312. }

  313. void show_result(struct linerela *pRes)
  314. { //打印结果
  315.         int i = 0;
  316.         printf("The found rangle is : \n");
  317.         while ( NULL != pRes ) {
  318.                 printf("\tpoint %d: %f\t%f\n", i+1, pRes->scon.x, pRes->scon.y);
  319.                 pRes = pRes->pnext;
  320.                 i ++;
  321.                 if ( LINENUM == i ) { //每四个点构成一个四边形
  322.                         printf("\n");
  323.                         i = 0;
  324.                 }
  325. }

  326. void free_mem(struct linerela *pRes)
  327. { //释放开配的内存
  328.         struct linerela *tmp = pRes ;

  329.         while ( NULL != pRes ) {
  330.                 tmp = pRes->pnext ;
  331.                 free(pRes);
  332.                 pRes = tmp;
  333.         }
  334. }

  335. /*
  336. *
  337. *
  338. **Makefile**
  339. *
  340. *
  341. */

  342. CC = gcc
  343. CFLAGS = -g
  344. DFLAGS = $(CFLAGS)
  345. Target = qrangle
  346. OBJECT = qrangle.o qrutils.o
  347. .PHONY : all clean
  348. all : $(OBJECT)
  349.         $(CC) $(DFLAGS) -o $(Target) $^
  350. $(OBJECT) : %.o : %.c
  351.         $(CC) $(CFLAGS) -c $<
  352. clean:
  353.         rm -rf $(OBJECT)
  354.         rm -rf $(Target)
复制代码

[ 本帖最后由 drowsyboy 于 2007-10-9 12:58 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP