Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/300.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何确定一个点位于线段上的两个其他点之间?_Python_Math_Geometry - Fatal编程技术网

Python 如何确定一个点位于线段上的两个其他点之间?

Python 如何确定一个点位于线段上的两个其他点之间?,python,math,geometry,Python,Math,Geometry,假设有一个二维平面,平面上有两个点(称为a和b),每个点由一个x整数和一个y整数表示 如何确定另一个点c是否位于由a和b定义的线段上 我大部分使用python,但任何语言中的示例都会有帮助。检查b-a和c-a的叉积是否为0:这意味着所有点都是共线的。如果是,检查c的坐标是否在a和b之间。使用x或y坐标,只要a和b在该轴上是分开的(或者两者都相同) def开启(a、b、c): “返回真iff点c与从a到b的线段相交。” #(或所有3个点重合的退化情况) 返回(共线(a、b、c) 和(在(a.x,c

假设有一个二维平面,平面上有两个点(称为a和b),每个点由一个x整数和一个y整数表示

如何确定另一个点c是否位于由a和b定义的线段上


我大部分使用python,但任何语言中的示例都会有帮助。

检查
b-a
c-a
的叉积是否为
0
:这意味着所有点都是共线的。如果是,检查
c
的坐标是否在
a
b
之间。使用x或y坐标,只要
a
b
在该轴上是分开的(或者两者都相同)

def开启(a、b、c):
“返回真iff点c与从a到b的线段相交。”
#(或所有3个点重合的退化情况)
返回(共线(a、b、c)
和(在(a.x,c.x,b.x)内,如果a.x!=b.x else
在(a.y,c.y,b.y))范围内
def共线(a、b、c):
如果a、b和c都位于同一条线上,则返回true
返回(b.x-a.x)*(c.y-a.y)=(c.x-a.x)*(b.y-a.y)
(p、q、r)内的def:
“如果q在p和r之间(包括p和r),则返回真值。”

返回p检查(b-a)和(c-a)的叉积是否为0,正如Darius Bacon告诉您的那样,点a、b和c是否对齐

但是,当您想知道c是否在a和b之间时,您还必须检查(b-a)和(c-a)的点积是否为正,并且小于a和b之间距离的平方

在非优化伪代码中:

def isBetween(a, b, c):
    crossproduct = (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y)

    # compare versus epsilon for floating point values, or != 0 if using integers
    if abs(crossproduct) > epsilon:
        return False

    dotproduct = (c.x - a.x) * (b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0:
        return False

    squaredlengthba = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if dotproduct > squaredlengthba:
        return False

    return True
# epsilon = small constant

def isBetween(a, b, c):
    lengthca2  = (c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y)
    lengthba2  = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if lengthca2 > lengthba2: return False
    dotproduct = (c.x - a.x)*(b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0.0: return False
    if abs(dotproduct*dotproduct - lengthca2*lengthba2) > epsilon: return False 
    return True
def介于(a、b、c)之间:
叉积=(c.y-a.y)*(b.x-a.x)-(c.x-a.x)*(b.y-a.y)
#比较浮点值与ε,或!=如果使用整数,则为0
如果abs(叉积)>ε:
返回错误
点积=(c.x-a.x)*(b.x-a.x)+(c.y-a.y)*(b.y-a.y)
如果dotproduct<0:
返回错误
平方长度ba=(b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y)
如果dotproduct>squaredlengthba:
返回错误
返回真值

在(c-a)和(b-a)之间的标量积必须等于它们长度的乘积(这意味着向量(c-a)和(b-a)对齐且方向相同)。此外,(c-a)的长度必须小于或等于(b-a)。伪代码:

def isBetween(a, b, c):
    crossproduct = (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y)

    # compare versus epsilon for floating point values, or != 0 if using integers
    if abs(crossproduct) > epsilon:
        return False

    dotproduct = (c.x - a.x) * (b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0:
        return False

    squaredlengthba = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if dotproduct > squaredlengthba:
        return False

    return True
# epsilon = small constant

def isBetween(a, b, c):
    lengthca2  = (c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y)
    lengthba2  = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if lengthca2 > lengthba2: return False
    dotproduct = (c.x - a.x)*(b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0.0: return False
    if abs(dotproduct*dotproduct - lengthca2*lengthba2) > epsilon: return False 
    return True
#ε=小常数
def介于(a、b、c)之间:
长度ca2=(c.x-a.x)*(c.x-a.x)+(c.y-a.y)*(c.y-a.y)
长度ba2=(b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y)
如果lengthca2>lengthba2:返回False
点积=(c.x-a.x)*(b.x-a.x)+(c.y-a.y)*(b.y-a.y)
如果dotproduct<0.0:返回False
如果abs(dotproduct*dotproduct-lengthca2*lengthba2)>ε:返回False
返回真值

还有另一种方法:

  • 假设两点是A(x1,y1)和B(x2,y2)
  • 通过这些点的直线方程为(x-x1)/(y-y1)=(x2-x1)/(y2-y1)。。(只是使坡度相等)
点C(x3,y3)将位于A和B之间,如果:

  • x3,y3满足上述方程
  • x3位于x1和x2之间,y3位于y1和y2之间(普通检查)

使用更几何的方法,计算以下距离:

ab = sqrt((a.x-b.x)**2 + (a.y-b.y)**2)
ac = sqrt((a.x-c.x)**2 + (a.y-c.y)**2)
bc = sqrt((b.x-c.x)**2 + (b.y-c.y)**2)
并测试ac+bc是否等于ab

is_on_segment = abs(ac + bc - ab) < EPSILON
c = ma + (1 - m)b, where 0 <= m <= 1
是否在\u段=abs(ac+bc-ab)
这是因为有三种可能性:

  • 这三个点形成一个三角形=>ac+bc>ab
  • 它们是共线的,cab段=>ac+bc>ab
  • 它们是共线的,c位于ab段=>ac+bc=ab
    • 以下是我的做法:

      def distance(a,b):
          return sqrt((a.x - b.x)**2 + (a.y - b.y)**2)
      
      def is_between(a,c,b):
          return distance(a,c) + distance(c,b) == distance(a,b)
      

      这是我在学校的表现。我忘了为什么这不是个好主意

      #!/usr/bin/env python
      from __future__ import division
      
      epsilon = 1e-6
      
      class Point:
          def __init__(self, x, y):
              self.x, self.y = x, y
      
      class LineSegment:
          """
          >>> ls = LineSegment(Point(0,0), Point(2,4))
          >>> Point(1, 2) in ls
          True
          >>> Point(.5, 1) in ls
          True
          >>> Point(.5, 1.1) in ls
          False
          >>> Point(-1, -2) in ls
          False
          >>> Point(.1, 0.20000001) in ls
          True
          >>> Point(.1, 0.2001) in ls
          False
          >>> ls = LineSegment(Point(1, 1), Point(3, 5))
          >>> Point(2, 3) in ls
          True
          >>> Point(1.5, 2) in ls
          True
          >>> Point(0, -1) in ls
          False
          >>> ls = LineSegment(Point(1, 2), Point(1, 10))
          >>> Point(1, 6) in ls
          True
          >>> Point(1, 1) in ls
          False
          >>> Point(2, 6) in ls 
          False
          >>> ls = LineSegment(Point(-1, 10), Point(5, 10))
          >>> Point(3, 10) in ls
          True
          >>> Point(6, 10) in ls
          False
          >>> Point(5, 10) in ls
          True
          >>> Point(3, 11) in ls
          False
          """
          def __init__(self, a, b):
              if a.x > b.x:
                  a, b = b, a
              (self.x0, self.y0, self.x1, self.y1) = (a.x, a.y, b.x, b.y)
              self.slope = (self.y1 - self.y0) / (self.x1 - self.x0) if self.x1 != self.x0 else None
      
          def __contains__(self, c):
              return (self.x0 <= c.x <= self.x1 and
                      min(self.y0, self.y1) <= c.y <= max(self.y0, self.y1) and
                      (not self.slope or -epsilon < (c.y - self.y(c.x)) < epsilon))
      
          def y(self, x):        
              return self.slope * (x - self.x0) + self.y0
      
      if __name__ == '__main__':
          import  doctest
          doctest.testmod()
      
      编辑:

      @Darius Bacon:这包含了为什么下面的代码不是一个好主意的解释

      #!/usr/bin/env python
      from __future__ import division
      
      epsilon = 1e-6
      
      class Point:
          def __init__(self, x, y):
              self.x, self.y = x, y
      
      class LineSegment:
          """
          >>> ls = LineSegment(Point(0,0), Point(2,4))
          >>> Point(1, 2) in ls
          True
          >>> Point(.5, 1) in ls
          True
          >>> Point(.5, 1.1) in ls
          False
          >>> Point(-1, -2) in ls
          False
          >>> Point(.1, 0.20000001) in ls
          True
          >>> Point(.1, 0.2001) in ls
          False
          >>> ls = LineSegment(Point(1, 1), Point(3, 5))
          >>> Point(2, 3) in ls
          True
          >>> Point(1.5, 2) in ls
          True
          >>> Point(0, -1) in ls
          False
          >>> ls = LineSegment(Point(1, 2), Point(1, 10))
          >>> Point(1, 6) in ls
          True
          >>> Point(1, 1) in ls
          False
          >>> Point(2, 6) in ls 
          False
          >>> ls = LineSegment(Point(-1, 10), Point(5, 10))
          >>> Point(3, 10) in ls
          True
          >>> Point(6, 10) in ls
          False
          >>> Point(5, 10) in ls
          True
          >>> Point(3, 11) in ls
          False
          """
          def __init__(self, a, b):
              if a.x > b.x:
                  a, b = b, a
              (self.x0, self.y0, self.x1, self.y1) = (a.x, a.y, b.x, b.y)
              self.slope = (self.y1 - self.y0) / (self.x1 - self.x0) if self.x1 != self.x0 else None
      
          def __contains__(self, c):
              return (self.x0 <= c.x <= self.x1 and
                      min(self.y0, self.y1) <= c.y <= max(self.y0, self.y1) and
                      (not self.slope or -epsilon < (c.y - self.y(c.x)) < epsilon))
      
          def y(self, x):        
              return self.slope * (x - self.x0) + self.y0
      
      if __name__ == '__main__':
          import  doctest
          doctest.testmod()
      
      #/usr/bin/env python
      来自未来进口部
      ε=1e-6
      课程点:
      定义初始化(self,x,y):
      self.x,self.y=x,y
      类别线段:
      """
      >>>ls=线段(点(0,0),点(2,4))
      >>>ls中的点(1,2)
      真的
      >>>ls中的点(.5,1)
      真的
      >>>ls中的点(.5,1.1)
      假的
      >>>ls中的点(-1,-2)
      假的
      >>>ls中的点(.1,0.2000001)
      真的
      >>>ls中的点(.1,0.2001)
      假的
      >>>ls=线段(点(1,1),点(3,5))
      >>>ls中的点(2,3)
      真的
      >>>ls中的点(1.5,2)
      真的
      >>>ls中的点(0,-1)
      假的
      >>>ls=线段(点(1,2),点(1,10))
      >>>ls中的点(1,6)
      真的
      >>>ls中的点(1,1)
      假的
      >>>ls中的点(2,6)
      假的
      >>>ls=线段(点(-1,10),点(5,10))
      >>>ls中的点(3,10)
      真的
      >>>ls中的点(6,10)
      假的
      >>>ls中的点(5,10)
      真的
      >>>ls中的点(3,11)
      假的
      """
      定义初始化(self,a,b):
      如果a.x>b.x:
      a、 b=b,a
      (self.x0,self.y0,self.x1,self.y1)=(a.x,a.y,b.x,b.y)
      self.slope=(self.y1-self.y0)/(self.x1-self.x0)如果self.x1!=self.x0其他无
      def___;包含____;(self,c):
      
      返回(self.x0段的长度不重要,因此不需要使用平方根,应避免使用平方根,因为我们可能会失去一些精度

      class Point:
          def __init__(self, x, y):
              self.x = x
              self.y = y
      
      class Segment:
          def __init__(self, a, b):
              self.a = a
              self.b = b
      
          def is_between(self, c):
              # Check if slope of a to c is the same as a to b ;
              # that is, when moving from a.x to c.x, c.y must be proportionally
              # increased than it takes to get from a.x to b.x .
      
              # Then, c.x must be between a.x and b.x, and c.y must be between a.y and b.y.
              # => c is after a and before b, or the opposite
              # that is, the absolute value of cmp(a, b) + cmp(b, c) is either 0 ( 1 + -1 )
              #    or 1 ( c == a or c == b)
      
              a, b = self.a, self.b             
      
              return ((b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y) and 
                      abs(cmp(a.x, c.x) + cmp(b.x, c.x)) <= 1 and
                      abs(cmp(a.y, c.y) + cmp(b.y, c.y)) <= 1)
      

      好的,很多人提到线性代数(向量的叉积),这在实(即连续或浮点)空间中有效,但问题明确指出两点表示为整数,因此叉积不是正确的解,尽管它可以给出近似解

      正确的解决方案是在两点之间使用,并查看第三点是否为其中一点
      def is_on(a, b, c):
          """Is c on the line segment ab?"""
      
          def _is_zero( val ):
              return -epsilon < val < epsilon
      
          x1 = a.x - b.x
          x2 = c.x - b.x
          y1 = a.y - b.y
          y2 = c.y - b.y
      
          if _is_zero(x1) and _is_zero(y1):
              # a and b are the same point:
              # so check that c is the same as a and b
              return _is_zero(x2) and _is_zero(y2)
      
          if _is_zero(x1):
              # a and b are on same vertical line
              m2 = y2 * 1.0 / y1
              return _is_zero(x2) and 0 <= m2 <= 1
          elif _is_zero(y1):
              # a and b are on same horizontal line
              m1 = x2 * 1.0 / x1
              return _is_zero(y2) and 0 <= m1 <= 1
          else:
              m1 = x2 * 1.0 / x1
              if m1 < 0 or m1 > 1:
                  return False
              m2 = y2 * 1.0 / y1
              return _is_zero(m2 - m1)
      
      Boolean Contains(PointF from, PointF to, PointF pt, double epsilon)
              {
      
                  double segmentLengthSqr = (to.X - from.X) * (to.X - from.X) + (to.Y - from.Y) * (to.Y - from.Y);
                  double r = ((pt.X - from.X) * (to.X - from.X) + (pt.Y - from.Y) * (to.Y - from.Y)) / segmentLengthSqr;
                  if(r<0 || r>1) return false;
                  double sl = ((from.Y - pt.Y) * (to.X - from.X) - (from.X - pt.X) * (to.Y - from.Y)) / System.Math.Sqrt(segmentLengthSqr);
                  return -epsilon <= sl && sl <= epsilon;
              }
      
      is_on = (a,b,c) ->
          # "Return true if point c intersects the line segment from a to b."
          # (or the degenerate case that all 3 points are coincident)
          return (collinear(a,b,c) and withincheck(a,b,c))
      
      withincheck = (a,b,c) ->
          if a[0] != b[0]
              within(a[0],c[0],b[0]) 
          else 
              within(a[1],c[1],b[1])
      
      collinear = (a,b,c) ->
          # "Return true if a, b, and c all lie on the same line."
          ((b[0]-a[0])*(c[1]-a[1]) < (c[0]-a[0])*(b[1]-a[1]) + 1000) and ((b[0]-a[0])*(c[1]-a[1]) > (c[0]-a[0])*(b[1]-a[1]) - 1000)
      
      within = (p,q,r) ->
          # "Return true if q is between p and r (inclusive)."
          p <= q <= r or r <= q <= p
      
      l1 + A(l2 - l1)
      
      x = l1.x + A(l2.x - l1.x)
      y = l1.y + A(l2.y - l1.y)
      
      // Vec2 is a simple x/y struct - it could very well be named Point for this use
      
      bool isBetween(double a, double b, double c) {
          // return if c is between a and b
          double larger = (a >= b) ? a : b;
          double smaller = (a != larger) ? a : b;
      
          return c <= larger && c >= smaller;
      }
      
      bool pointOnLine(Vec2<double> p, Vec2<double> l1, Vec2<double> l2) {
          if(l2.x - l1.x == 0) return isBetween(l1.y, l2.y, p.y); // vertical line
          if(l2.y - l1.y == 0) return isBetween(l1.x, l2.x, p.x); // horizontal line
      
          double Ax = (p.x - l1.x) / (l2.x - l1.x);
          double Ay = (p.y - l1.y) / (l2.y - l1.y);
      
          // We want Ax == Ay, so check if the difference is very small (floating
          // point comparison is fun!)
      
          return fabs(Ax - Ay) < 0.000001 && Ax >= 0.0 && Ax <= 1.0;
      }
      
      boolean liesOnSegment(Coordinate a, Coordinate b, Coordinate c) {
              
          double dotProduct = (c.x - a.x) * (c.x - b.x) + (c.y - a.y) * (c.y - b.y);
          return (dotProduct < 0);
      }
      
      public static bool IsOnSegment(this Segment2D @this, Point2D c, double tolerance)
      {
           var distanceSquared = tolerance*tolerance;
           // Start of segment to test point vector
           var v = new Vector2D( @this.P0, c ).To3D();
           // Segment vector
           var s = new Vector2D( @this.P0, @this.P1 ).To3D();
           // Dot product of s
           var ss = s*s;
           // k is the scalar we multiply s by to get the projection of c onto s
           // where we assume s is an infinte line
           var k = v*s/ss;
           // Convert our tolerance to the units of the scalar quanity k
           var kd = tolerance / Math.Sqrt( ss );
           // Check that the projection is within the bounds
           if (k <= -kd || k >= (1+kd))
           {
              return false;
           }
           // Find the projection point
           var p = k*s;
           // Find the vector between test point and it's projection
           var vp = (v - p);
           // Check the distance is within tolerance.
           return vp * vp < distanceSquared;
      }
      
      s * s
      
      def dot(v,w): return v.x*w.x + v.y*w.y
      def wedge(v,w): return v.x*w.y - v.y*w.x
      
      def is_between(a,b,c):
         v = a - b
         w = b - c
         return wedge(v,w) == 0 and dot(v,w) > 0
      
      private bool _isPointOnLine( Vector2 ptLineStart, Vector2 ptLineEnd, Vector2 ptPoint )
      {
          bool bRes = false;
          if((Mathf.Approximately(ptPoint.x, ptLineStart.x) || Mathf.Approximately(ptPoint.x, ptLineEnd.x)))
          {
              if(ptPoint.y > ptLineStart.y && ptPoint.y < ptLineEnd.y)
              {
                  bRes = true;
              }
          }
          else if((Mathf.Approximately(ptPoint.y, ptLineStart.y) || Mathf.Approximately(ptPoint.y, ptLineEnd.y)))
          {
              if(ptPoint.x > ptLineStart.x && ptPoint.x < ptLineEnd.x)
              {
                  bRes = true;
              }
          }
          return bRes;
      }
      
      public static double CalcDistanceBetween2Points(double x1, double y1, double x2, double y2)
      {
          return Math.Sqrt(Math.Pow (x1-x2, 2) + Math.Pow (y1-y2, 2));
      }
      
      public static bool PointLinesOnLine (double x, double y, double x1, double y1, double x2, double y2, double allowedDistanceDifference)
      {
          double dist1 = CalcDistanceBetween2Points(x, y, x1, y1);
          double dist2 = CalcDistanceBetween2Points(x, y, x2, y2);
          double dist3 = CalcDistanceBetween2Points(x1, y1, x2, y2);
          return Math.Abs(dist3 - (dist1 + dist2)) <= allowedDistanceDifference;
      }
      
      function getLineDefinition($p1=array(0,0), $p2=array(0,0)){
          
          $k = ($p1[1]-$p2[1])/($p1[0]-$p2[0]);
          $q = $p1[1]-$k*$p1[0];
          
          return array($k, $q);
          
      }
      
      function isPointOnLineSegment($line=array(array(0,0),array(0,0)), $pt=array(0,0)){
          
          // GET THE LINE DEFINITION y = k.x + q AS array(k, q) 
          $def = getLineDefinition($line[0], $line[1]);
          
          // use the line definition to find y for the x of your point
          $y = $def[0]*$pt[0]+$def[1];
      
          $yMin = min($line[0][1], $line[1][1]);
          $yMax = max($line[0][1], $line[1][1]);
      
          // exclude y values that are outside this segments bounds
          if($y>$yMax || $y<$yMin) return false;
          
          // calculate the difference of your points y value from the reference value calculated from lines definition 
          // in ideal cases this would equal 0 but we are dealing with floating point values so we need some threshold value not to lose results
          // this is up to you to fine tune
          $diff = abs($pt[1]-$y);
          
          $thr = 0.000001;
          
          return $diff<=$thr;
          
      }