问题,求上图中线段AB 和线段CD的交点P的坐标

根据《算法艺术与信息学竞赛》,公式如下

原理:

利用叉积求得点P分线段DC的比,然后利用高中学习的定比分点坐标公式求得分点P的坐标

要注意的是

若判断是两条线段,需先判断能否相交,相交的时候才可调用该公式求相交点

判断结果如下图

golang代码如下

package main

import (
   "fmt"
   "math"
)

type Point struct {
   X     int32
   Y     int32
}
func (p Point) GetX() int32 {
   return p.X
}
func (p Point) GetY() int32 {
   return p.Y
}

const  eps = 1e-6    // 最小精度误差

func sgn(x float64) int {
   if x < -eps {
      return -1
   }
   return 1
}

func Cross(p1 *Point, p2 *Point, p3 *Point, p4 *Point) float64 {
   return (float64(p2.GetX())-float64(p1.GetX())) * (float64(p4.GetY())-float64(p3.GetY())) - (float64(p2.GetY())-float64(p1.GetY())) * (float64(p4.GetX())-float64(p3.GetX()))
}

func Area(p1 *Point, p2 *Point, p3 *Point) float64 {
   return Cross(p1, p2, p1, p3)
}

func fArea(p1 *Point, p2 *Point, p3 *Point) float64 {
   return math.Abs(Area(p1, p2, p3))
}

// 判断两条线段能否相交
func Meet(p1, p2, p3, p4 *Point) bool {
   return     math.Max(math.Min(float64(p1.GetX()), float64(p2.GetX())), math.Min(float64(p3.GetX()), float64(p4.GetX()))) <=
         math.Min(math.Max(float64(p1.GetX()), float64(p2.GetX())), math.Max(float64(p3.GetX()), float64(p4.GetX())))      &&
         math.Max(math.Min(float64(p1.GetY()), float64(p2.GetY())), math.Min(float64(p3.GetY()), float64(p4.GetY()))) <=
         math.Min(math.Max(float64(p1.GetY()), float64(p2.GetY())), math.Max(float64(p3.GetY()), float64(p4.GetY())))      &&
         (float64(sgn(Cross(p3, p2, p3, p4))) * Cross(p3, p4, p3, p1) >= 0)                                     &&
         (float64(sgn(Cross(p1, p4, p1, p2))) * Cross(p1, p2, p1, p3) >= 0)
}

// 计算两个直线的交叉点(若是判断两个线段,需先经过上面Meet()函数的检测,结果为true的才可调用本函数)
func Inter(p1 *Point, p2 *Point, p3 *Point, p4 *Point) Point {
   k := fArea(p1, p2, p3) / fArea(p1, p2, p4)
   return Point {
      X: int32( (float64(p3.GetX())+k*float64(p4.GetX())) / (1+k) ),
      Y: int32( (float64(p3.GetY())+k*float64(p4.GetY())) / (1+k) ),
   }
}

func main() {
   // 线段1
   p1 := &Point{X:300,Y:300}
   p2 := &Point{X:500,Y:500}
   // 线段2
   p3 := &Point{X:300,Y:500}
   p4 := &Point{X:500,Y:300}

   if Meet(p1, p2, p3, p4) {
      fmt.Printf("p1p2和p3p4相交 \n")
      p := Inter(p1, p2, p3, p4)
      fmt.Printf("相交点p=%v \n", p)
   } else {
      fmt.Printf("p1p2和p3p4不相交 \n")
   }

   // 线段3
   p5 := &Point{X:100,Y:100}
   p6 := &Point{X:100,Y:500}
   if Meet(p1, p2, p5, p6) {
      fmt.Printf("p1p2和p5p6相交 \n")
      p := Inter(p1, p2, p3, p4)
      fmt.Printf("相交点p=%v \n", p)
   } else {
      fmt.Printf("p1p2和p5p6不相交 \n")
   }
}