问题,求上图中线段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") } }
执行结果如下:
E:\go_test\两条线段相交2>go run main.go
p1p2和p3p4相交
相交点p={400 400}
p1p2和p5p6不相交