diff --git a/utils/geometry/circle.go b/utils/geometry/circle.go index 7eda549..c2d725b 100644 --- a/utils/geometry/circle.go +++ b/utils/geometry/circle.go @@ -8,31 +8,19 @@ import ( // Circle 圆形 type Circle[V generic.SignedNumber] struct { Shape[V] - radius V - centroid Point[V] - initCentroid bool } // Radius 获取圆形半径 func (slf Circle[V]) Radius() V { - if slf.radius > V(-0) { - return slf.radius - } for _, point := range slf.Points() { - slf.radius = CalcDistanceWithPoint(slf.Centroid(), point) - return slf.radius + return CalcDistanceWithPoint(slf.Centroid(), point) } panic("circle without any points") } // Centroid 获取圆形质心位置 func (slf Circle[V]) Centroid() Point[V] { - if slf.initCentroid { - return slf.centroid - } - slf.centroid = CalcRectangleCentroid(slf.Shape) - slf.initCentroid = true - return slf.centroid + return CalcRectangleCentroid(slf.Shape) } // Overlap 与另一个圆是否发生重叠 diff --git a/utils/geometry/geometry.go b/utils/geometry/geometry.go index 8a125fd..8ff85d1 100644 --- a/utils/geometry/geometry.go +++ b/utils/geometry/geometry.go @@ -133,16 +133,17 @@ func CalcAngle[V generic.SignedNumber](x1, y1, x2, y2 V) V { // CalcNewCoordinate 根据给定的x、y坐标、角度和距离计算新的坐标 func CalcNewCoordinate[V generic.SignedNumber](x, y, angle, distance V) (newX, newY V) { - // 将角度转换为弧度 + radian := CalcRadianWithAngle(angle) + newX = x + distance*V(math.Cos(float64(radian))) + newY = y + distance*V(math.Sin(float64(radian))) + return newX, newY +} + +// CalcRadianWithAngle 根据角度 angle 计算弧度 +func CalcRadianWithAngle[V generic.SignedNumber](angle V) V { var pi = math.Pi var dividend = 180.0 - radians := angle * V(pi) / V(dividend) - - // 计算新的坐标 - newX = x + distance*V(math.Cos(float64(radians))) - newY = y + distance*V(math.Sin(float64(radians))) - - return newX, newY + return angle * V(pi) / V(dividend) } // CalcAngleDifference 计算两个角度之间的最小角度差 @@ -155,3 +156,18 @@ func CalcAngleDifference[V generic.SignedNumber](angleA, angleB V) V { t -= V(pi) return t } + +// CalcRayIsIntersect 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞 +func CalcRayIsIntersect[V generic.SignedNumber](x, y, angle V, shape Shape[V]) bool { + fx, fy := float64(x), float64(y) + radian := CalcRadianWithAngle(float64(angle)) + end := NewPoint(fx+math.Cos(radian), fy+math.Sin(radian)) + + edges := shape.Edges() + for i := 0; i < len(edges); i++ { + if CalcLineSegmentIsIntersect(NewLineSegment(NewPoint(fx, fy), end), ConvertLineSegmentGeneric[V, float64](edges[i])) { + return true + } + } + return false +} diff --git a/utils/geometry/line.go b/utils/geometry/line.go index 81867bc..e1283a6 100644 --- a/utils/geometry/line.go +++ b/utils/geometry/line.go @@ -57,6 +57,13 @@ func (slf LineSegment[V]) GetLength() V { return CalcDistanceWithCoordinate(DoublePointToCoordinate(slf.GetStart(), slf.GetEnd())) } +// ConvertLineSegmentGeneric 转换线段的泛型类型为特定类型 +func ConvertLineSegmentGeneric[V generic.SignedNumber, TO generic.SignedNumber](line LineSegment[V]) LineSegment[TO] { + x1, y1 := line.GetStart().GetXY() + x2, y2 := line.GetEnd().GetXY() + return NewLineSegment(NewPoint(TO(x1), TO(y1)), NewPoint(TO(x2), TO(y2))) +} + // PointOnLineSegmentWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 func PointOnLineSegmentWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool { return (x-x1)*(y2-y1) == (x2-x1)*(y-y1) @@ -138,3 +145,37 @@ func CalcLineSegmentIsOverlap[V generic.SignedNumber](line1, line2 LineSegment[V } return NewLineSegment(shapes[1][2].Point, shapes[2][2].Point), true } + +// CalcLineSegmentIsIntersect 计算两条线段是否相交 +func CalcLineSegmentIsIntersect[V generic.SignedNumber](line1, line2 LineSegment[V]) bool { + fl1 := ConvertLineSegmentGeneric[V, float64](line1) + fl2 := ConvertLineSegmentGeneric[V, float64](line2) + slope1 := CalcLineSegmentSlope(fl1) + slope2 := CalcLineSegmentSlope(fl2) + + if slope1 == slope2 { + return false + } + + intercept1 := CalcLineSegmentIntercept(fl1) + intercept2 := CalcLineSegmentIntercept(fl2) + + intersectX := (intercept2 - intercept1) / (slope1 - slope2) + + if intersectX >= maths.Min(fl1.GetStart().GetX(), fl1.GetEnd().GetX()) && intersectX <= maths.Max(fl1.GetStart().GetX(), fl1.GetEnd().GetX()) && + intersectX >= maths.Min(fl2.GetStart().GetX(), fl2.GetEnd().GetX()) && intersectX <= maths.Max(fl2.GetStart().GetX(), fl2.GetEnd().GetX()) { + return true + } + + return false +} + +// CalcLineSegmentSlope 计算线段的斜率 +func CalcLineSegmentSlope[V generic.SignedNumber](line LineSegment[V]) V { + return (line.GetEnd().GetY() - line.GetStart().GetY()) / (line.GetEnd().GetX() - line.GetStart().GetX()) +} + +// CalcLineSegmentIntercept 计算线段的截距 +func CalcLineSegmentIntercept[V generic.SignedNumber](line LineSegment[V]) V { + return line.GetStart().GetY() - CalcLineSegmentSlope(line)*line.GetStart().GetX() +} diff --git a/utils/geometry/line_test.go b/utils/geometry/line_test.go new file mode 100644 index 0000000..e2616b6 --- /dev/null +++ b/utils/geometry/line_test.go @@ -0,0 +1,13 @@ +package geometry_test + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/geometry" + "testing" +) + +func TestCalcLineSegmentIsIntersect(t *testing.T) { + line1 := geometry.NewLineSegment(geometry.NewPoint(1, 1), geometry.NewPoint(3, 5)) + line2 := geometry.NewLineSegment(geometry.NewPoint(0, 5), geometry.NewPoint(3, 6)) + fmt.Println(geometry.CalcLineSegmentIsIntersect(line1, line2)) +} diff --git a/utils/geometry/position.go b/utils/geometry/position.go index 2ea8a64..a82ea64 100644 --- a/utils/geometry/position.go +++ b/utils/geometry/position.go @@ -97,7 +97,7 @@ func (slf Point[V]) Max(point Point[V]) Point[V] { return NewPoint(x, y) } -// Max 返回两个位置中每个维度的最小值组成的新的位置 +// Min 返回两个位置中每个维度的最小值组成的新的位置 func (slf Point[V]) Min(point Point[V]) Point[V] { x, y := slf.GetXY() px, py := point.GetXY()