关于如何判断在平面上的两条线段是否相交

来源:互联网 发布:unity3d 仿真 编辑:程序博客网 时间:2024/05/04 02:58

今天面试某公司时,被问道这道题目:

解决方法:

1.传统判断(这是我当时想到的答案):

*求出两条线段所对应的直线的交点(若直线不相交,那么线段肯定不想交)

*若有交点,则判断该交点是否分别在两条线段上,若符合条件,则两条线段相交,否则不想交

这是一个很简单也最容易向导的思路,于是我试着编程实现,结果确实写代码时有点复杂(主要是判断的情况太多就容易混乱)

2.算法2:

*分别求出两条线段所在的直线方程(斜率不存在的情况下直线方程为x-b=0)

*分别将一条线段的两个端点坐标代入另外一条线段直线方程,比如L1:ax+by+c=0,则有代入后的方程为result=ax+by+c,若是两个端点的结果的符号相反,则肯定该线段在另外一条直线的两侧。一次将两条线段都判断完。

*如果结果的乘积都为负或者为0(为0代表至少有一个端点在另外一条直线上),则代表两条线段相交。(这里得排除一种两条直线重合,而线段不相交的情况)

*根据原理:若是点在直线的上方(斜率存在),或者直线左方(斜率不存在)则代入ax+by+c=0.时值为负,反之为正

这种可能刚开始比较难想到,但其实在编程实现时相对简单。(最主要是感觉编写时头脑会更加清晰)

***主要算法代码:(我将这些功能代码封装进入了一个JudgeUtils的类中)

方法1:(判断两条线段是否相交的函数入口为

<strong>IsLineIntersection(LineSegment l1,LineSegment l2)//LineSegment是一个自定义的线段类</strong>
)

/**该类里面是一些判断的函数 * 所有的判断函数全在这个类里面 * static型函数供main方法使用 * 工具类 * */public class JudgeUtils {/**判断两个点的起始与结束位置的函数,且进行校正 * 两个点如果x2>x1,则P1为起点,如果x2=x1,判断y2>y1,则P1为起点 * 传进去的形参是对象引用,所以可以直接改变值 * */public static void exchangePoint(Point startP,Point endP){//记得进行处理,如果x2>x1,则P1为起点,如果x2=x1,判断y2>y1,则P1为起点//如果P1的起点比终点的x大,交换位置//在x相等时,判断y,如果P1的起点y比终点Y大,交换值if((startP.getX()>endP.getX())||((startP.getX()==endP.getX())&&(startP.getY()>endP.getY()))){double temp;//交换x值temp=startP.getX();startP.setX(endP.getX());endP.setX(temp);//交换Y的值temp=startP.getY();startP.setY(endP.getY());endP.setY(temp);System.out.println("起点与终点的值已经交换");}}/**判断两条线段是否相交的函数*/public static boolean IsLineIntersection(LineSegment l1,LineSegment l2){//默认为不想交,满足一定条件才相交boolean isIntersection=false;//取得l1,l2的交点Point intersectionPoint=IntersectionOfLines(l1,l2);/**返回值为空,代表直线没有交点*/if(intersectionPoint==null){System.out.println("直线不是相交状态!");}/**存在返回值,代表直线存在交点,所以进行下一步判断,判断交点是否在两条线段上*/else{System.out.println("直线存在交点,");System.out.println("交点为:x="+intersectionPoint.getX()+",y="+intersectionPoint.getY());System.out.println("下一步判断是否交点在线段!");//如果交点存在与两条线段之间,则代表两条线段相交if(isPointInLine(l1, intersectionPoint)&&isPointInLine(l2, intersectionPoint)){isIntersection=true;System.out.println("交点在线段上!");}}return isIntersection;}/**取得直线的交点坐标的函数 * 传入的是线段,但是里面会将其计算为直线 * 斜率不存在的情况单独判断 * Y=k1X+b1与Y=k2X+b2进行判断(k2!k1的情况,且斜率存在的情况) * 解方程式x=(b1-b2)/(k2-k1) * y=b1+k1*(b1-b2)/(k2-k1) * */public static Point IntersectionOfLines(LineSegment l1,LineSegment l2){Point intersection=null;//斜率不存在的情况//如果l1斜率不存在或l2斜率不存在if((l1.getEndP().getX()==l1.getStartP().getX())||(l2.getEndP().getX()==l2.getStartP().getX())){//如果两者的斜率都不存在,肯定不会相交,所以,只有两者存在一个有斜率才有可能相交if(!((l1.getEndP().getX()==l1.getStartP().getX())&&(l2.getEndP().getX()==l2.getStartP().getX()))){//某条斜率不存在,则另外一条就很好求出交点if(l1.getEndP().getX()==l1.getStartP().getX())//l1斜率不存在{//通过一个函数得到点//x=b1与y=k2x+b2的求交点的函数intersection=IntersectionOfLines(l1.getEndP().getX(),l2);}else if(l2.getEndP().getX()==l2.getStartP().getX())//l2的斜率不存在{//通过一个函数得到点//x=b2与y=k1x+b1的求交点的函数intersection=IntersectionOfLines(l2.getEndP().getX(),l1);}}else//如果平行,判断是否重合{if(l1.getEndP().getX()==l2.getEndP().getX())//如果重合{System.out.println("直线重合!取一个特殊交点");intersection=new Point(l2.getStartP().getX(),l2.getStartP().getY());}else{System.out.println("直线平行!");}}}//斜率不存在处理完毕else//斜率存在时的处理方式{//数学公式辅助值,double k1,k2,b1,b2;//计算斜率,k=(y2-y1)/(x2-x1)k1=(l1.getEndP().getY()-l1.getStartP().getY())/(l1.getEndP().getX()-l1.getStartP().getX());k2=(l2.getEndP().getY()-l2.getStartP().getY())/(l2.getEndP().getX()-l2.getStartP().getX());//计算b,b=y-kxb1=l1.getStartP().getY()-k1*l1.getStartP().getX();b2=l2.getStartP().getY()-k2*l2.getStartP().getX();//只有两条直线不平行才有可能相交if(k2!=k1){//计算交点//解方程式x=(b1-b2)/(k2-k1),y=b1+k1*(b1-b2)/(k2-k1)double tempx=(b1-b2)/(k2-k1);double tempy=b1+k1*(b1-b2)/(k2-k1);//得到具体交点intersection=new Point(tempx,tempy);}else//考虑重合线段{if(b1==b2){System.out.println("直线重合!");//如果两条重合线段有交点//只有可能是l2的起点大于l1的起点小于l1的终点//或者l2的终点大于l1的起点小于l1的终点if((l2.getStartP().getX()>=l1.getStartP().getX())&&(l2.getStartP().getX()<=l1.getEndP().getX())){System.out.println("重合线段取一个特殊点!");//返回一个l2的起点intersection=new Point(l2.getStartP().getX(),l2.getStartP().getY());}else if((l2.getEndP().getX()>=l1.getStartP().getX())&&(l2.getEndP().getX()<=l1.getEndP().getX())){System.out.println("重合线段取一个特殊交点!");//返回一个l2的终点intersection=new Point(l2.getStartP().getX(),l2.getStartP().getY());}}else//直线平行{System.out.println("直线平行!");}}}return intersection;}/**提高代码复用,将求x=b1和y=k2x+b2的交点的代码封装到一个函数里面 * 通过改变参数进行重载 * 返回值是一个Point类型 * */public static Point IntersectionOfLines(double b1,LineSegment l2){//算交点//现求出l2那条直线的斜率和bdouble k2,b2,tempx,tempy;k2=(l2.getEndP().getY()-l2.getStartP().getY())/(l2.getEndP().getX()-l2.getStartP().getX());b2=l2.getStartP().getY()-k2*l2.getStartP().getX();//根据y=kx+b,已知的是x=l1.getStartP().xtempx=b1;tempy=tempx*k2+b2;//得到具体交点return new Point(tempx,tempy);}/**判断点是否在线段上的函数 * 只需要分别判点p的x值>=l的最小的x而且p的x值<=l的最大的x * 因为可以确定起始点X的值肯定是<=终止点X的值 * 注意,别判断Y的值,因为有可能起始点的Y比结束点的Y小 * 所以,只有当直线斜率不存在时才判断Y值 * */public static boolean isPointInLine(LineSegment l,Point p){boolean isPointInLine=false;//直线斜率存在时,只判断Xif((p.getX()<=l.getEndP().getX()&&p.getX()>=l.getStartP().getX())&&(l.getStartP().getX()!=l.getEndP().getX())){isPointInLine=true;}//斜率不存在,判断Yelse if((l.getStartP().getX()!=l.getEndP().getX())  &&(p.getY()<=l.getEndP().getY()&&p.getY()>=l.getStartP().getY())){isPointInLine=true;}return isPointInLine;}}
方法2:

/**第二种算法 * 分别求出两条线段所在的直线方程,然后分别将一条线段的端点坐标带入另外一条线 *段直线方程,如果符号不同即为该线段在另外一条直线的两侧。两条线段都判断完后, *即相交。 *根据:若是点在直线的上方。则代入ax+by+c=0.时值为负,在下方则为正 *若是斜率不存在,如x-b=0,点在直线右侧时为证,左侧时为负 * */public class JudgeUtils2 {/**判断两个点的起始与结束位置的函数,且进行校正 * 两个点如果x2>x1,则P1为起点,如果x2=x1,判断y2>y1,则P1为起点 * 传进去的形参是对象引用,所以可以直接改变值 * */public static void exchangePoint(Point startP,Point endP){//记得进行处理,如果x2>x1,则P1为起点,如果x2=x1,判断y2>y1,则P1为起点//如果P1的起点比终点的x大,交换位置//在x相等时,判断y,如果P1的起点y比终点Y大,交换值if((startP.getX()>endP.getX())||((startP.getX()==endP.getX())&&(startP.getY()>endP.getY()))){double temp;//交换x值temp=startP.getX();startP.setX(endP.getX());endP.setX(temp);//交换Y的值temp=startP.getY();startP.setY(endP.getY());endP.setY(temp);System.out.println("起点与终点的值已经交换");}}/**判断两条线段是否相交的函数*/public static boolean IsLineIntersection(LineSegment l1,LineSegment l2){boolean isIntersection=false;//分别判断l1线段是否在直线l2两侧//还有l2线段是否在直线l1两侧//如果有一个线段的结果乘起来是0,则代表这个线段与另条直线有交点//但是要排除直线重合但是线段不重合的情况int l2Values=PointBelowLine(l1, l2.getStartP())*PointBelowLine(l1, l2.getEndP());int l1Values=PointBelowLine(l2, l1.getStartP())*PointBelowLine(l2, l1.getEndP());if(((l2Values==-1)||l2Values==0)&&((l1Values==-1)||l1Values==0)){//如果两者都为0,代表至少有一个端点在另外一条线段上,所以这时候要判断为重合的情况if(l2Values==0&&l2Values==0){//判断出直线重合但是线段不重合的情况//l1的终点小于l2的起点,或者是l1的起点大于l2的终点if(l1.getEndP().getX()<l2.getStartP().getX()||l1.getStartP().getX()>l2.getEndP().getY()){isIntersection=false;System.out.println("直线重合,但是线段不相交");}else{isIntersection=true;System.out.println("线段部分重合或相交!");}}else{//两条线段相交isIntersection=true;System.out.println("最终结果为:两条线段相交!");}}else{System.out.println("最终结果为:两条线段不是相交关系!");}return isIntersection;}/**判断点是否在直线的下方和(斜率不存在时在直线的右方的函数)返回正 * 若是点在直线的上方,或者(斜率不存在时在直线的左方)则返回-1 * 斜率存在时y=kx+b.k=(y2-y1)/(x2-x1).b=y2-k*x2. * 斜率不存在时。x=b * */public static int PointBelowLine(LineSegment l,Point p){int rValue=1;//默认值为0//先求出线段对应的直线//斜率不存在时if(l.getStartP().getX()==l.getEndP().getX()){System.out.println("直线斜率不存在!");//直线为x-b=0.double b;b=l.getStartP().getX();if(p.getX()-b>0){rValue=1;}else if(p.getX()-b<0){rValue=-1;}else{rValue=0;}}else //斜率存在时{System.out.println("直线斜率存在!");double k,b;k=(l.getEndP().getY()-l.getStartP().getY())/(l.getEndP().getX()-l.getStartP().getX());b=l.getEndP().getY()-l.getEndP().getX()*k;//直线kx-y+b=0.代入kx1-y1+b来测试点和直线的关系,if(k*p.getX()-p.getY()+b>0){rValue=1;}else if(k*p.getX()-p.getY()+b<0){rValue=-1;}else {rValue=0;}}return rValue;}}

完整工程路径:http://download.csdn.net/detail/u010979495/8097901

0 0
原创粉丝点击