【计算几何】确定两条连续线段向左转还是向右转

2024-02-11 13:44

本文主要是介绍【计算几何】确定两条连续线段向左转还是向右转,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

确定两条连续线段向左转还是向右转

目录

  • 一、说明
  • 二、算法
    • 2.1 两点的叉积
    • 2.2 两个段的叉积
  • 三、旋转方向判别
    • 3.1 左转
    • 3.2、右转
    • 3.3 共线判别

一、说明

   如果是作图,或者是判别小车轨迹。为了直观地了解,从当前点到下一个点过程中,什么是左转、什么是右转,或者方向不变,这在现场实时运算中是很重要的。本篇描述其快速算法。

   请考虑下面所示的示例。
在这里插入图片描述

   两图中,共有三点p1,p2和p3和两条线段 p 1 p 2 ‾ \overline{p_1p_2} p1p2 p 2 p 3 ‾ \overline{p_2p_3} p2p3。中途点 p 2 p_2 p2是两条线段共有的。在图(a)中,线段 p 2 p 3 ‾ \overline{p_2p_3} p2p3 p 2 p_2 p2该点右转而在图(b)中,段 p 2 p 3 ‾ \overline{p_2p_3} p2p3在公共点 p 2 p_2 p2左转。我们的眼睛更容易通过观察图形来快速识别路段是左转还是右转,因为您的眼睛天生具有快速识别事物的能力。在这篇文章中,我将讨论计算机如何使用几何算法来识别这一点。

二、算法

2.1 两点的叉积

   给定两点 p 1 ( x 1 , y 1 ) p_1(x_1,y_1) p1(x1,y1 p 2 ( x 2 , y 2 ) p_2(x_2,y_2) p2(x2,y2),我们首先需要判断点是否 p 1 p_1 p1从点开始是顺时针还是逆时针 p 2 p_2 p2就起源而言。解决这个问题的一种方法是计算两者所成的角度 p 1 ‾ \overline{p_1} p1 p 2 ‾ \overline{p_2} p2
和X轴和角度的差异可以判断一个点是顺时针还是逆时针。有一个比查找计算向量叉积的角度更简单有效的解决方案 p 1 ‾ \overline{p_1} p1 p 2 ‾ \overline{p_2} p2。数学上两个向量的叉积 p 1 ‾ \overline{p_1} p1 p 2 ‾ \overline{p_2} p2定义成:
p 1 × p 2 = x 1 y 2 − x 2 y 1 p_1×p_2=x_1y_2-x_2y_1 p1×p2=x1y2x2y1

   如果值p1×p2则为正,p1是沿逆时针到达p2,关于原点如下图所示。
在这里插入图片描述

   同样,如果p1×p2是负数,从p1到p2是顺时针过度。相对于原点,如果值为 0,则点p1,p2和原点共线。以下 python 代码实现了叉积。

class Point:def __init__(self, x, y):self.x = xself.y = ydef subtract(self, p):return Point(self.x - p.x, self.y - p.y)def __str__(self):return '(' + str(self.x) + ', ' + str(self.y) + ')'# calculates the cross product of vector p1 and p2
# if p1 is clockwise from p2 wrt origin then it returns +ve value
# if p2 is anti-clockwise from p2 wrt origin then it returns -ve value
# if p1 p2 and origin are collinear then it returs 0
def cross_product(p1, p2):return p1.x * p2.y - p2.x * p1.y

2.2 两个段的叉积

   考虑两个线段,其端点是p1(X1,y1),p2(X2,y2)和p1(X1,y1),p3(X3,y3)分别。为了计算两个线段的叉积,我们需要将它们转换为向量。这可以通过以下方式完成。

p 1 p 2 ‾ = ( x 2 − x 1 , y 2 − y 1 ) , p 1 p 3 ‾ = ( x 3 − x 1 , y 3 − y 1 ) \overline{p_1p_2} = (x_2 - x_1, y_2 - y_1), \overline{p_1p_3} = (x_3 - x_1, y_3 - y_1) p1p2=(x2x1,y2y1),p1p3=(x3x1,y3y1)

   一旦我们有了两个两个向量,我们就可以调用cross_product来计算叉积,如下面的代码所示。

# returns the cross product of vector p1p3 and p1p2
# if p1p3 is clockwise from p1p2 it returns +ve value
# if p1p3 is anti-clockwise from p1p2 it returns -ve value
# if p1 p2 and p3 are collinear it returns 0
def direction(p1, p2, p3):return  cross_product(p3.subtract(p1), p2.subtract(p1))

三、旋转方向判别

3.1 左转

   判断一个段是否 p 2 p 3 p_2p_3 p2p3从路段左转 p 1 p 2 p_1p_2 p1p2在点 p 2 p_2 p2,我们画下一个向量 p 1 p 3 ‾ \overline{p_1p_3} p1p3并检查新向量是否与向量逆时针方向 p 2 p 3 ‾ \overline{p_2p_3} p2p3。如下图所示。右图显示向量
在这里插入代码片在这里插入图片描述

   p 1 p 3 ‾ \overline{p_1p_3} p1p3从向量逆时针方向 p 1 p 2 ‾ \overline{p_1p_2} p1p2因此我们可以说该段p2p3
从路段左转p1p2在点p2。下面给出了 python 实现。

# checks if p3 makes left turn at p2
def left(p1, p2, p3):return direction(p1, p2, p3) < 0

3.2、右转

   这与左转相同,唯一的区别是向量 p 1 p 3 ‾ \overline{p_1p_3} p1p3从向量顺时针方向 p 2 p 3 ‾ \overline{p_2p_3} p2p3。下面给出了 python 实现。

# checks if p3 makes right turn at p2
def right(p1, p2, p3):return direction(p1, p2, p3) > 0

3.3 共线判别

   当该方法direction既不返回正值也不返回负值而是返回零时,就会出现一种特殊情况。在这种情况下,所有的点 p 1 p_1 p1, p 2 p_2 p2 p 3 p_3 p3位于同一条线上。在本例中段 p 2 p 3 p_2p_3 p2p3不转向任何方向。

# checks if p1, p2 and p3 are collinear
def collinear(p1, p2, p3):return direction(p1, p2, p3) == 0

参考
Cormen, TH、Leiserson, CE、Rivest, RL 和 Stein, C.(nd)。算法简介(第三版)。麻省理工学院出版社。

这篇关于【计算几何】确定两条连续线段向左转还是向右转的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

poj2406(连续重复子串)

题意:判断串s是不是str^n,求str的最大长度。 解题思路:kmp可解,后缀数组的倍增算法超时。next[i]表示在第i位匹配失败后,自动跳转到next[i],所以1到next[n]这个串 等于 n-next[n]+1到n这个串。 代码如下; #include<iostream>#include<algorithm>#include<stdio.h>#include<math.

uva 10387 Billiard(简单几何)

题意是一个球从矩形的中点出发,告诉你小球与矩形两条边的碰撞次数与小球回到原点的时间,求小球出发时的角度和小球的速度。 简单的几何问题,小球每与竖边碰撞一次,向右扩展一个相同的矩形;每与横边碰撞一次,向上扩展一个相同的矩形。 可以发现,扩展矩形的路径和在当前矩形中的每一段路径相同,当小球回到出发点时,一条直线的路径刚好经过最后一个扩展矩形的中心点。 最后扩展的路径和横边竖边恰好组成一个直

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 1113 凸包+简单几何计算

题意: 给N个平面上的点,现在要在离点外L米处建城墙,使得城墙把所有点都包含进去且城墙的长度最短。 解析: 韬哥出的某次训练赛上A出的第一道计算几何,算是大水题吧。 用convexhull算法把凸包求出来,然后加加减减就A了。 计算见下图: 好久没玩画图了啊好开心。 代码: #include <iostream>#include <cstdio>#inclu

uva 1342 欧拉定理(计算几何模板)

题意: 给几个点,把这几个点用直线连起来,求这些直线把平面分成了几个。 解析: 欧拉定理: 顶点数 + 面数 - 边数= 2。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

uva 11178 计算集合模板题

题意: 求三角形行三个角三等分点射线交出的内三角形坐标。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <