判断两线段是否相交,并求交点

本文介绍了一种通过向量运算判断两条线段是否相交并求出交点的方法。使用参数方程和叉乘原理,提供了两个代码实现版本:基础版和优化版,排除了平行情况,减少了精度误差。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先, 上个示意图.

这里写图片描述

根据图示, 线段a表示为端点a1和a2, 线段b表示为端点b1和b2. 为了利用向量的叉乘关系, 将线段的端点看成四个向量, 下面用粗体表示向量. 根据向量运算可知
a=a2-a1,
b=b2-b1.
将线段表示为参数方程:
a=a1 + t a
b=b1 + u b
其中参数t,u取值 [0,1]

两条线段相交具有如下关系:
a1 + t a=b1 + u b
将上式两边同时叉乘b, 得到:
(a1+t a) x b=(b1+u b) x b
由于b x b=0, 可得
a1 x b + t a x b=b1 x b
解出参数t
t=(b1-a1)x b/(a x b)
同理,解出参数u
u=a x (a1-b1)/(a x b)

当0<=t<=1,且0<=u<=1时,两线段有交点.
代入线段a的参数方程中, 即可得到线段交点坐标:
a1+t a
将上式中的中间变量用原始的线段端点表示, 即可得到根据线段端点表示的交点.

code 1

// Returns 1 if the lines intersect, otherwise 0. In addition, if the lines 
// intersect the intersection point may be stored in the floats i_x and i_y.
char get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s1_x, s1_y, s2_x, s2_y;
    s1_x = p1_x - p0_x;     s1_y = p1_y - p0_y;
    s2_x = p3_x - p2_x;     s2_y = p3_y - p2_y;

    float s, t;
    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
    {
        // Collision detected
        if (i_x != NULL)
            *i_x = p0_x + (t * s1_x);
        if (i_y != NULL)
            *i_y = p0_y + (t * s1_y);
        return 1;
    }

    return 0; // No collision
}

code 2

优化版本,排除线段平行情况,避免精度误差

int get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s02_x, s02_y, s10_x, s10_y, s32_x, s32_y, s_numer, t_numer, denom, t;
    s10_x = p1_x - p0_x;
    s10_y = p1_y - p0_y;
    s32_x = p3_x - p2_x;
    s32_y = p3_y - p2_y;

    denom = s10_x * s32_y - s32_x * s10_y;
    if (denom == 0)//平行或共线
        return 0; // Collinear
    bool denomPositive = denom > 0;

    s02_x = p0_x - p2_x;
    s02_y = p0_y - p2_y;
    s_numer = s10_x * s02_y - s10_y * s02_x;
    if ((s_numer < 0) == denomPositive)//参数是大于等于0且小于等于1的,分子分母必须同号且分子小于等于分母
        return 0; // No collision

    t_numer = s32_x * s02_y - s32_y * s02_x;
    if ((t_numer < 0) == denomPositive)
        return 0; // No collision

    if (fabs(s_numer) > fabs(denom) || fabs(t_numer) > fabs(denom))
        return 0; // No collision
    // Collision detected
    t = t_numer / denom;
    if (i_x != NULL)
        *i_x = p0_x + (t * s10_x);
    if (i_y != NULL)
        *i_y = p0_y + (t * s10_y);

    return 1;
}

方法来源于stack overflow的帖子:geometry - How do you detect where two line segments intersect?

在CAD软件中,判断两条线是否相交返回交点通常涉及几何计算。在C#中,这通常需要使用CAD软件提供的API或者SDK来实现。以AutoCAD为例,你可以使用AutoCAD的.NET API来进行这样的判断和计算。以下是一个简化的代码示例,展示了如何使用AutoCAD的.NET API来判断两条直线是否相交获取交点: ```csharp using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; // 这是一个AutoCAD命令的示例 public class IntersectLineCommands { // 该方法用于判断两条直线是否相交 [CommandMethod("IntersectLines")] public void IntersectTwoLines() { Document acDoc = Application.DocumentManager.MdiActiveDocument; Database acCurDb = acDoc.Database; // 开始一个事务 using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction()) { // 打开块表记录用于读写 BlockTable acBlkTbl; acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable; // 打开块表记录模型空间用于写入 BlockTableRecord acBlkTblRec; acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // 创建两条直线作为示例 Line acLine1 = new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)); Line acLine2 = new Line(new Point3d(0, 10, 0), new Point3d(10, 0, 0)); // 将新对象添加到块表记录和事务 acBlkTblRec.AppendEntity(acLine1); acTrans.AddNewlyCreatedDBObject(acLine1, true); acBlkTblRec.AppendEntity(acLine2); acTrans.AddNewlyCreatedDBObject(acLine2, true); // 判断两条线是否相交 Point3dCollection intersectionPoints = new Point3dCollection(); acLine1.IntersectWith(acLine2, Intersect.OnBothOperands, intersectionPoints, IntPtr.Zero, IntPtr.Zero); if (intersectionPoints.Count > 0) { // 如果有交点,则输出交点坐标 acDoc.Editor.WriteMessage("交点坐标: " + intersectionPoints[0].ToString() + "\n"); } else { // 如果没有交点,则输出提示信息 acDoc.Editor.WriteMessage("两条线相交。\n"); } // 提交事务 acTrans.Commit(); } } } ``` 需要注意的是,上述代码仅作为示例,未考虑所有可能的异常处理和用户交互逻辑。在实际使用中,你需要根据实际CAD软件的具体API文档来编写代码,确保处理好所有可能的异常情况。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值