求两线段交点

2024-01-01 06:49
文章标签 交点 两线

本文主要是介绍求两线段交点,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

来源于力扣上的一道题目:面试题 16.03. 交点
给定两条线段(表示为起点start = ( x 1 , y 1 ) (x_1,y_1) (x1,y1)和终点end = ( x 2 , y 2 ) (x_2,y_2) (x2,y2),如果它们有交点,请计算其交点,没有交点则返回空值。
要求浮点型误差不超过10^-6。若有多个交点(线段重叠)则返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。

解析

现在假设两条线段的四个点分别为 ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x 3 , y 3 ) , ( x 4 , y 4 ) (x_1,y_1),(x_2,y_2),(x_3,y_3),(x_4,y_4) (x1,y1)(x2,y2)(x3,y3)(x4,y4),交点为 ( x i n t e r , y i n t e r ) (x_{inter},y_{inter}) (xinter,yinter)
可能的情况无非以下几种,相交、平行不共线、共线不相交、共线相交

平行判定

y 2 − y 1 x 2 − x 1 = y 4 − y 3 x 4 − x 3 \frac{y_2-y_1}{x_2-x_1} = \frac{y_4-y_3}{x_4-x_3} x2x1y2y1=x4x3y4y3
为了避免出现斜率为0的情况,使用乘法。
( y 2 − y 1 ) ∗ ( x 4 − x 3 ) = ( x 2 − x 1 ) ∗ ( y 4 − y 3 ) (y_2-y_1)*(x_4-x_3)=(x_2-x_1)*(y_4-y_3) (y2y1)(x4x3)=(x2x1)(y4y3)

相交(不平行)

用高中学过的参数方程来表示两条直线
{ x = x 1 + t 1 ∗ ( x 2 − x 1 ) y = y 1 + t 1 ∗ ( y 2 − y 1 ) \left\{ \begin{aligned} x & = x_1+t_1*(x_2-x_1) \\ y & = y_1+t_1*(y_2-y_1) \\ \end{aligned} \right. {xy=x1+t1(x2x1)=y1+t1(y2y1)
{ x = x 3 + t 2 ∗ ( x 4 − x 3 ) y = y 3 + t 2 ∗ ( y 4 − y 3 ) \left\{ \begin{aligned} x & = x_3+t_2*(x_4-x_3) \\ y & = y_3+t_2*(y_4-y_3) \\ \end{aligned} \right. {xy=x3+t2(x4x3)=y3+t2(y4y3)

如果求交点,则直接令其联立,求解出 t 1 , t 2 t_1,t_2 t1,t2,解得:
t 1 = ( x 3 − x 1 ) ( y 4 − y 3 ) − ( y 3 − y 1 ) ( x 4 − x 3 ) ( x 2 − x 1 ) ( y 4 − y 3 ) − ( x 4 − x 3 ) ( y 2 − y 1 ) t 1 ∈ [ 0 , 1 ] t_1 = \frac {(x_3-x_1)(y_4-y_3)-(y_3-y_1)(x_4-x_3)}{(x_2-x_1)(y_4-y_3)-(x_4-x_3)(y_2-y_1)} \quad t_1 \in[0,1] t1=(x2x1)(y4y3)(x4x3)(y2y1)(x3x1)(y4y3)(y3y1)(x4x3)t1[0,1]
t 2 = ( x 1 − x 3 ) ( y 2 − y 1 ) + ( y 3 − y 1 ) ( x 2 − x 1 ) ( x 4 − x 3 ) ( y 2 − y 1 ) − ( x 2 − x 1 ) ( y 4 − y 3 ) t 2 ∈ [ 0 , 1 ] t_2 = \frac {(x_1-x_3)(y_2-y_1)+(y_3-y_1)(x_2-x_1)}{(x_4-x_3)(y_2-y_1)-(x_2-x_1)(y_4-y_3)} \quad t_2 \in[0,1] t2=(x4x3)(y2y1)(x2x1)(y4y3)(x1x3)(y2y1)+(y3y1)(x2x1)t2[0,1]
假如最后求出来的 t 1 , t 2 t_1,t_2 t1,t2在范围内的话,那交点为:
{ x i n t e r = x 1 + t 1 ∗ ( x 2 − x 1 ) y i n t e r = y 1 + t 1 ∗ ( y 2 − y 1 ) \left\{ \begin{aligned} x_{inter} & = x_1+t_1*(x_2-x_1) \\ y_{inter} & = y_1+t_1*(y_2-y_1) \\ \end{aligned} \right. {xinteryinter=x1+t1(x2x1)=y1+t1(y2y1)

平行

下面讨论平行的情况:

共线判定

( x 3 , y 3 ) (x_3,y_3) (x3,y3)是否在 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1,y1)(x2,y2)的直线上
y k − y 1 x k − x 1 = y 2 − y 1 x 2 − x 1 \frac{y_k-y_1}{x_k-x_1} = \frac{y_2-y_1}{x_2-x_1} xkx1yky1=x2x1y2y1
为了避免出现斜率为0的情况,使用乘法。
( y k − y 1 ) ∗ ( x 2 − x 1 ) = ( y 2 − y 1 ) ∗ ( x k − x 1 ) (y_k-y_1)*(x_2-x_1)=(y_2-y_1)*(x_k-x_1) (yky1)(x2x1)=(y2y1)(xkx1)

// 判断一点(xk,yk)在直线(x1,y1)(x2,y2)上bool commonLine(int x1,int y1,int x2,int y2,int xk,int yk){return (yk-y1)*(x2-x1)==(y2-y1)*(xk-x1);}
平行且共线

判断是否有交点,如果有交点,且最优的交点一定是端点中的一个,下面就来判断已知平行且共线的情况下,判断两线段是否相交

// 已知(x1,y1)(x2,y2)(xk,yk)共线的情况下,判断(xk,yk)是否在(x1,y1)(x2,y2)线段上
bool inside(int x1,int y1,int x2,int y2,int xk,int yk){return (xk>=min(x1,x2)&&xk<=max(x1,x2)&&yk>=min(y1,y2)&&yk<=max(y1,y2));
}

依次去判断

  • (x3,y3)是否在(x1,y1)(x2,y2)线段中?
  • (x4,y4)是否在(x1,y1)(x2,y2)线段中?
  • (x1,y1)是否在(x3,y3)(x4,y4)线段中?
  • (x2,y2)是否在(x3,y3)(x4,y4)线段中?
    如果在的话,端点便是当前的最优交点,然后去更新结果。
平行不共线

无交点

实现

class Solution {
public:// 判断一点(xk,yk)在直线(x1,y1)(x2,y2)上bool commonLine(int x1,int y1,int x2,int y2,int xk,int yk){return (yk-y1)*(x2-x1)==(y2-y1)*(xk-x1);}// 已知(x1,y1)(x2,y2)(xk,yk)共线的情况下,判断(xk,yk)是否在(x1,y1)(x2,y2)线段上bool inside(int x1,int y1,int x2,int y2,int xk,int yk){return (xk>=min(x1,x2)&&xk<=max(x1,x2)&&yk>=min(y1,y2)&&yk<=max(y1,y2));}// 更新交点,使其最优void update(vector<double>&ans,double xk,double yk){if(ans.size()==0){ans.resize(2);ans[0] = xk; ans[1] = yk;}else if(ans[0]>xk||(xk==ans[0]&&yk<ans[1])){ans[0] = xk; ans[1] = yk;}}vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {vector<double> ans;// if(start1.empty()||start2.empty()||end1.empty()||end2.empty()) return ans;int x1 = start1[0],y1 = start1[1], x2 = end1[0], y2 = end1[1];int x3 = start2[0],y3 = start2[1], x4 = end2[0], y4 = end2[1];// 判断两直线是否平行if((y2-y1)*(x4-x3)==(x2-x1)*(y4-y3)){// 两直线共线if(commonLine(x1,y1,x2,y2,x3,y3)){if(inside(x1,y1,x2,y2,x3,y3))update(ans,(double)x3,(double)y3);if(inside(x1,y1,x2,y2,x4,y4))update(ans,(double)x4,(double)y4);if(inside(x3,y3,x4,y4,x1,y1))update(ans,(double)x1,(double)y1);if(inside(x3,y3,x4,y4,x2,y2))update(ans,(double)x2,(double)y2);}}else {double t1 = (double)((x3-x1)*(y4-y3)-(y3-y1)*(x4-x3))/((x2-x1)*(y4-y3)-(x4-x3)*(y2-y1));double t2 = (double)((x1-x3)*(y2-y1)+(y3-y1)*(x2-x1))/((x4-x3)*(y2-y1)-(x2-x1)*(y4-y3));if(t1>=0&&t1<=1.0&&t2>=0&&t2<=1.0){if(ans.size()==0) ans.resize(2);ans[0] = (double)(x1+t1*(x2-x1));ans[1] = double(y1+t1*(y2-y1));}}return ans;}
};

Ref:思路和代码大部分来源于方法一:参数方程

这篇关于求两线段交点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/558445

相关文章

圆与线段的交点

poj 3819  给出一条线段的两个端点,再给出n个圆,求出这条线段被所有圆覆盖的部分占了整条线段的百分比。 圆与线段的交点 : 向量AB 的参数方程  P = A + t * (B - A)      0<=t<=1 ; 将点带入圆的方程即可。  注意: 有交点 0 <= t <= 1 ; 此题求覆盖的部分。 则 若求得 t  满足 ; double ask(d

求空间直线与平面的交点

若直线不与平面平行,将存在交点。如下图所示,已知直线L过点m(m1,m2,m3),且方向向量为VL(v1,v2,v3),平面P过点n(n1,n2,n3),且法线方向向量为VP(vp1,vp2,vp3),求得直线与平面的交点O的坐标(x,y,z): 将直线方程写成参数方程形式,即有: x = m1+ v1 * t y = m2+ v2 * t

Codeforces Round #329 (Div. 2) B. Anton and Lines ([好题] 计算直线在区间是否有交点)

题目链接 题意:给出n个条直线,然后在指定的区间(x1,x2)是否有直线的交点存在。 解法:一:闭区间,首先把区间略微调小。 二:计算直线在x1,x2上的交点y坐标,以及直线的id,然后按照y值,id值排序,最后判断第x1,x2左右两边的第i个点是不是同一直线的,如果不是,就存在交点。 #include<bits/stdc++.h>using namespace std;const i

计算射线与平面的交点

#include "stdafx.h"#include<iostream>using namespace std;struct Point3{float x;float y;float z;};struct Vector{float x;float y;float z;};struct Ray{ //一点,和一个方向向量(两点求差)确定一条射线,Point3 p0;Vect

计算直线的交点数(dp+枚举)

计算直线的交点数 Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submission(s) : 10   Accepted Submission(s) : 7 Font: Times New Roman | Verdana | Georgia Font Siz

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

首先, 上个示意图. 根据图示, 线段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

计算两直线的交点 poj 1269 我的第一道几何题

设读入的四个点分别为A, B, C, D。 首先,算出矢量AB和CD,如果它们的叉积为0,则说明它们的方向是相同或相反的,也就是这两条直线的斜率相等。 若斜率相等,则要进一步判断它们是否为同一条直线,那只需要判断点C是否在直线AB上,如果矢量CA和矢量AB的叉积为0,则说明它们的方向是相同或相反的,那么点C就在直线AB上。 若斜率不相等,则要求交点。如图,C’D’平行CD,v、w、u是

两线段相交的判断(跨立实验法)

精度的控制 第一种方法:int dblcmp(double x){if(fabs(x)<eps)return 0;return x>0?1:-1;}第二种方法:int dblcmp(double x){if(x>eps)return 1;else if(x<-eps)return -1;else return 0;} 叉积的求解 double det(double x1,do

求两直线的交点

转自    http://blog.csdn.net/abcjennifer/article/details/7584628 一般方程法: 直线的一般方程为F(x) = ax + by + c = 0。既然我们已经知道直线的两个点,假设为(x0,y0), (x1, y1),那么可以得到a = y0 – y1, b = x1 – x0, c = x0y1 – x1y0。 因此我们可以将两条

C# 有一条垂直线,怎么判断一点坐标点是在左侧还是右侧,以及该坐标与垂直线的交点?

在C#中,要判断一个点相对于垂直线的位置(左侧还是右侧),以及计算该点与垂直线的交点,你需要先定义垂直线的位置和属性。垂直线通常可以用它的一个点(比如线段的起点或终点)和它的方向(垂直,即垂直于X轴)来定义。 以下是一个简单的C#方法,它接受垂直线的一个点(假设为(lineX, lineY))和一个待检查的点(pointX, pointY),然后判断该点是在垂直线的左侧还是右侧,并计算交点(如果