Skip to content

Commit

Permalink
feat: 增加几何运算辅助函数
Browse files Browse the repository at this point in the history
CalcRayIsIntersect 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞
CalcRadianWithAngle 根据角度 angle 计算弧度
CalcLineSegmentIsIntersect 计算两条线段是否相交
CalcLineSegmentSlope 计算线段的斜率
CalcLineSegmentIntercept 计算线段的截距
ConvertLineSegmentGeneric 转换线段的泛型类型为特定类型
Point.Min 注释为 Max 的问题修复
  • Loading branch information
kercylan98 committed Jun 25, 2023
1 parent 2696624 commit c7a6e09
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 23 deletions.
16 changes: 2 additions & 14 deletions utils/geometry/circle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 与另一个圆是否发生重叠
Expand Down
32 changes: 24 additions & 8 deletions utils/geometry/geometry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 计算两个角度之间的最小角度差
Expand All @@ -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
}
41 changes: 41 additions & 0 deletions utils/geometry/line.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
}
13 changes: 13 additions & 0 deletions utils/geometry/line_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
2 changes: 1 addition & 1 deletion utils/geometry/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit c7a6e09

Please sign in to comment.