数学篇 求两条直线的交点,说明过程 您所在的位置:网站首页 origin两条直线交点 数学篇 求两条直线的交点,说明过程

数学篇 求两条直线的交点,说明过程

2024-07-03 05:29| 来源: 网络整理| 查看: 265

CAD调用说明

cad上面调用不用这么复杂,可以见 cad.net 投影三维图元到某个平面上+求图元交点

某些情况数学方法处理更佳.

简述

首先要说明,看懂本篇您并不需要高中文化水平... 为了求两条线的交点,首先要知道什么能求,而目前来说,我只知道高中数学的直线方程,那么我就要引入直线方程的概念...

然后为什么直线方程能求交点呢?因为同时满足两条联立的直线方程,它的共同解也就只能是交点...(这里没看懂没关系,跟着代码走的时候你就知道了.) 而直线方程实际上是描述一条两端无限延长的线,cad术语就是参照线,构成的两点只是过两点,而不是端点. 是参照线的话,这就有一个非常好的条件: 除非平行,否则必然有交点

那么在编程上,我拿到的数据一般是两个点..(x1,y1)(x2,y2)

通过这一条线坐标计算出斜率,这个斜率实际上就是直角三角形(高/底)

img

//斜率=高/底 var a = (y2 - y1) / (x2 - x1); //需考虑分母不能为0 var b = (y4 - y3) / (x4 - x3); //需考虑分母不能为0

这样就有个问题了,会存在分母可能为0的情况,需要先判断一下:

//因为求斜率需要用除法,分母可能为0,所以求斜率之前, //需要判断两条线是否x轴平行或者y轴平行 if (Eq(x2, x1) && Eq(x4, x3)) throw new Exception("与y轴平行,两直线垂直,斜率不存在,无交点"); if (Eq(y2, y1) && Eq(y4, y3)) throw new Exception("与x轴平行,两直线水平,斜率为零,无交点"); //求斜率,分母为0并不报错,而是赋值成 Infinity double a = (y2 - y1) / (x2 - x1); //需考虑分母不能为0 即x2=x1 l1垂直于x轴 double b = (y4 - y3) / (x4 - x3); //需考虑分母不能为0 即x4=x3 l2垂直于x轴 if (Eq(a, b)) throw new Exception("斜率一致,两直线斜着平行,无交点");

有了斜率和有两个点,就可以求直线方程,可以利用"点斜式"来求. 如果你想知道其他的方式,可以看直线方程的五种形式可看乐乐课堂, 用我的话来说,其他形式最后都会成为点斜式,因为它足够简单.

直线方程点斜式 y-y1=k(x-x1)

这里k是斜率(因两条直线:我的斜率是a和b),可以理解公式的x=x2,y=y2,为了使得x2,y2是个可变的点,所以用x,y代替(我的是_x,_y),成为未知数....这里x1,y1就是套入的点.

如下图,形象理解一下y-y1是竖,x-x1是横,竖=斜率*横

变换成:第一种 y=k(x-x1)+y1 这种比较重要! 因为众所周知的黎曼可积都是竖着切

变换成:第二种 x=(y-y1-k*x1)/k

然后由于未知数有两个还没法求,但是现在知道了一个条件: {知道x就可以推出y,知道y就可以推出x}

垂线情况

通过垂线得到_x,求出_y

我之前的代码否决了两条线都平行或者都垂直

还有一种情况未否决,这就是其中一条是垂直,它导致了一条线的分母是0,在c#中使用了分母为0的并不报错,而是double的值成为一个Infinity(正无穷)

如果使用了这个斜率就会报错,所以我需要避免使用这个它..

又由于这个斜率为0肯定是一条垂线,它的x1==x2是确定的,代表了两条直线的交点的_x肯定是这个x1.

通过条件{知道x就可以推出y,知道y就可以推出x}套入公式即可求_y

double _x, _y = 0;//未知数初始化 //L1或L2两直线可能其中一个有Y轴平行(垂直X轴)的 if (Eq(x2, x1)) //L1垂直于x轴 则x=x1=x2,(x2 - x1)是0==斜率a的分母,a=Infinity正无穷 { _x = x1; _y = b * x1 - b * x3 + y3;//公式变换第一种 return new double[] { _x, _y }; } else if (Eq(x4, x3)) //L2垂直于x轴 则x=x3=x4,(x4 - x3)是0==斜率b的分母,b=Infinity正无穷 { _x = x3; _y = a * _x - a * x1 + y1;//公式变换第一种 return new double[] { _x, _y }; } 联立方程

现在剩下一种情况,就是两条都是斜的.这个时候需要联立方程.再重复提及一下: 因为直线方程描述是一条参照线,两端无限延长,除非平行,否则必有交点.

又因为交点是两条线的共同解,所以点斜式:line1和line2相减必然是0.

{第一条线的直线方程} - {第二条线的直线方程} = 0; //桥接你的思路: 因为交点.Y-交点.Y=0,交点.X-交点.X=0啊!

这样做的目的,就是算式剩下未知数是_x(其实反过来用X也可以)

套到公式就是这样:

[y=a(_x-x1)+y1] - [y=b(_x-x3)+y3] =0; [a*(_x-x1)+y1] - [b*(_x-x3)+y3] =0; [a*_x-a*x1+y1] - [b*_x-b*x3+y3] =0; a*_x-a*x1+y1 - b*_x+b*x3-y3 =0; 去括号,括号前是-号,故此+-互变 a*_x-b*_x-a*x1+y1+b*x3-y3=0; 未知数的放一块 (a - b)* _x = 0 + a*x1 - y1 - b*x3 + y3; 移项,+-互变 _x = (a * x1 - y1 - b * x3 + y3) / (a - b); //上面程序代码已经算了_x了,直接套入点斜式方程,通过条件{知道x就可以推出y,知道y就可以推出x} _y = a * _x - a * x1 + y1;

就这样,大功告成....

直线方程一般式

在点斜式上面使用了逻辑避让开垂直和水平的情况,那么一般式里面本身就容纳这个逻辑. 可以看看数学家们是怎么利用各种直线方程归纳出一般式的,就明白了为什么"容纳"了,这个视频有.

代码 // https://blog.csdn.net/yangtrees/article/details/7965983 /// /// 求交点,直线方程一般式,平行无解 /// /// 交点 public static PointV GetCrossPoint(PointV p1, PointV p2, PointV p3, PointV p4) { /* 直线方程一般式: Ax+By+C=0,推导: * 当x1!=x2,则斜率[(y2-y1)/(x2-x1)],点斜式方程: y-y1=k(x-x1) * y-y1 = [(y2-y1)/(x2-x1)]*(x-x1) ;高=斜率*底 * y = [(y2-y1)/(x2-x1)]*(x-x1)+y1 ;加法交换律,y=斜率*底+y1. * (x2-x1)y = {[(y2-y1)/(x2-x1)]*(x-x1)+y1}*(x2-x1) ;两边同乘(x2-x1) * = (y2-y1)/(x2-x1)*(x-x1)*(x2-x1) + y1(x2-x1) ;拆括号 * = (y2-y1)*(x-x1) + y1(x2-x1) ;约去两个相同(x2-x1)项 * = (y2-y1)x -(y2-y1)x1 + y1(x2-x1) ;这一步开始根据一般式的格式,将系数划分出来 * | |-(x1y2-x1y1)+ x2y1-x1y1 ;简单运算 * | |x2y1-x1y1-(x1y2-x1y1) ;简单运算 * | |x2y1-x1y1-x1y2+x1y1 ;简单运算 * (x2-x1)y | (y2-y1)x |x2y1-x1y2 ;简单运算 * -B | A |C ;格式 * ------------------------------------------------------;保留系数 * x1-x2 |y2-y1 |x2y1-x1y2 ;写入到代码中 * B | A |C ;直线方程一般式 */ var a1 = p2.Y - p1.Y; var b1 = p1.X - p2.X; var c1 = p2.X * p1.Y - p1.X * p2.Y; var a2 = p4.Y - p3.Y; var b2 = p3.X - p4.X; var c2 = p4.X * p3.Y - p3.X * p4.Y; /* 求交点就是联立方程: * (A1x+B1y+C1)-(A2x+B2y+C2)=0,二者实际上就是联立方程组的叉积应用 * 叉乘:依次用手指盖住每列,交叉相乘再相减,注意主副顺序 * x y z * a1 b1 c1 * a2 b2 c2 */ var x = b1 * c2 - b2 * c1;//主-副(左上到右下是主,左下到右上是副) var y = a2 * c1 - a1 * c2;//副-主 var z = a1 * b2 - a2 * b1;//主-副,为0表示两直线重合 var cp = new PointV(); if (Math.Abs(z) > 1e-8) { cp.X = x / z; cp.Y = y / z; //cp.Z = z / z; } //唉唉唉!!!这样Z不都是1了?直线方程是平面坐标系,因此Z抹去, //那么明明叉乘是可以满足XYZ的推导,恰恰这个时候告诉你Z抹去了, //那是不是代表说,直线方程 上面少了参数?然后就可以描述成 空间直线方程? //所以就延伸出 空间直线方程一般式: Ax+By+Cz+D=0. return cp; } 编程特有

如果你把这个提示报错代码去掉,会出现有意思的东西. img

测试代码

本篇代码参考自

先下载一个PointV类

控制台测试 using JoinBox.BasalMath; using System.Runtime.InteropServices; using static JoinBox.BasalMath.Geometrist; namespace 求交点 { public class test { public static void Print(string str) { System.Console.WriteLine(str); } public static void Main(string[] args) { PointV pt; //水平平行 pt = IntersectWith(PointV.Origin, new PointV(10, 0), new PointV(0, 5), new PointV(10, 5)); Print(pt.ToString()); //垂直平行 pt = IntersectWith(PointV.Origin, new PointV(0, 10), new PointV(5, 0), new PointV(5, 10)); Print(pt.ToString()); //一斜一水平 pt = IntersectWith(PointV.Origin, new PointV(10, 10), new PointV(0, 5), new PointV(10, 5)); Print(pt.ToString()); //一斜一垂直 pt = IntersectWith(PointV.Origin, new PointV(10, 10), new PointV(5, 0), new PointV(5, 10)); Print(pt.ToString()); //L1线垂直 pt = IntersectWith(new PointV(5, 0), new PointV(5, 10), PointV.Origin, new PointV(10, 10)); Print(pt.ToString()); //L2线垂直 pt = IntersectWith(PointV.Origin, new PointV(10, 10), new PointV(5, 0), new PointV(5, 10)); Print(pt.ToString()); //两条都是斜的 交点 pt = IntersectWith(PointV.Origin, new PointV(10, 10), new PointV(10, 0), new PointV(0, 10)); Print(pt.ToString()); //两条都是斜着 平行 pt = IntersectWith(PointV.Origin, new PointV(1, 1), new PointV(0.70710678118655, -0.70710678118655), new PointV(1.70710678118655, 0.29289321881345)); Print(pt.ToString()); } } } 封装 using System; namespace JoinBox.BasalMath { public partial class Geometrist { /// /// 直线方程求交点 /// public static PointV IntersectWith(PointV p1, PointV p2, PointV p3, PointV p4) { var obj = IntersectWith(p1.X, p1.Y, p2.X, p2.Y, p3.X, p3.Y, p4.X, p4.Y); return new PointV(obj); } static bool Eq(double a, double b, double tolerance = 1e-6) { return Math.Abs(a - b) < tolerance; } /// /// 直线方程求交点 /// static double[] IntersectWith( double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { //因为求斜率需要用除法,分母可能为0,所以求斜率之前, //需要两条线是否x轴平行或者y轴平行 if (Eq(x2, x1) && Eq(x4, x3)) throw new Exception("与y轴平行,两直线垂直,斜率不存在,无交点"); if (Eq(y2, y1) && Eq(y4, y3)) throw new Exception("与x轴平行,两直线水平,斜率为零,无交点"); //求斜率,分母为0并不报错,而是赋值成 Infinity double a = (y2 - y1) / (x2 - x1); //需考虑分母不能为0 即x2=x1 l1垂直于x轴 double b = (y4 - y3) / (x4 - x3); //需考虑分母不能为0 即x4=x3 l2垂直于x轴 if (Eq(a, b)) throw new Exception("斜率一致,两直线斜着平行,无交点"); double _x, _y; //L1或L2两直线可能其中一个有Y轴平行(垂直X轴)的 if (Eq(x2, x1)) //L1垂直于x轴 则x=x1=x2,(x2 - x1)是0==a分母,a=Infinity正无穷 { _x = x1; _y = b * x1 - b * x3 + y3;//公式变换第一种 return new double[] { _x, _y }; } else if (Eq(x4, x3)) //L2垂直于x轴 则x=x3=x4,(x4 - x3)是0==b分母,b=Infinity正无穷 { _x = x3; _y = a * _x - a * x1 + y1;//公式变换第一种 return new double[] { _x, _y }; } //两条直线都是非垂直状态 /* 知道了点和斜率,那么两条点斜式方程联立. 因为直线方程是一条参照线,两端无限延长,除非平行,否则必有交点. 又因为交点是两条线的共同解,所以点斜式:line1和line2的y相减是0,y=k(_x-x1)+y1 所以未知数y就相减去掉,剩下x,来求y. 反之,也可以相减去掉x,来求y. [y=a(_x-x1)+y1] - [y=b(_x-x3)+y3] =0; [a*(_x-x1)+y1] - [b*(_x-x3)+y3] =0; [a*_x-a*x1+y1] - [b*_x-b*x3+y3] =0; a*_x-a*x1+y1 - b*_x+b*x3-y3 =0; 去括号,+-互变 (a - b)* _x = 0 + a*x1 - y1 - b*x3 + y3; //移项,+-互变 _x = (a * x1 - y1 - b * x3 + y3) / (a - b); */ _x = (a * x1 - y1 - b * x3 + y3) / (a - b); //但是上面程序代码已经算了_x了,直接套入点斜式方程,偷懒...也可以通过公式计算 /* y-y1=k*x-k*x1 y-y1+k*x1=k*x (y-y1+k*x1)/k=x [(y-y1-a*x1)/a] - [(y-y3-b*x3)/b] =0; //这是按照公式的方法 */ _y = a * _x - a * x1 + y1; // 点斜式方程 y-y1=k(x-x1) return new double[] { _x, _y }; } } } CAD测试 namespace JoinBox { public class CmdTest { [CommandMethod("CmdTest_IntersectWith")] public void CmdTest_IntersectWith() { var doc = Acap.DocumentManager.MdiActiveDocument; var ed = doc.Editor; var db = doc.Database; ed.WriteMessage(Environment.NewLine + "惊惊net测试区:"); var pts = new List(); var ppo = new PromptPointOptions("") { AllowArbitraryInput = true,//任意输入 AllowNone = true //允许回车 }; for (int i = 0; i < 4; i++) { ppo.Message = $"{Environment.NewLine}测试点{i + 1}:"; var ppr = ed.GetPoint(ppo);//用户点选 if (ppr.Status != PromptStatus.OK) return; pts.Add(ppr.Value); } var pt1 = Geometrist.IntersectWith(pts[0], pts[1], pts[2], pts[3]); ed.WriteMessage("\n点斜式交点1是:" + pt1.ToString()); var pt2 = Geometrist.GetCrossPoint(pts[0], pts[1], pts[2], pts[3]); ed.WriteMessage("\n一般式交点2是:" + pt2.ToString()); } } }

(完)



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有