feat: 增加几何运算辅助函数

CalcRayIsIntersect 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞
CalcRadianWithAngle 根据角度 angle 计算弧度
CalcLineSegmentIsIntersect 计算两条线段是否相交
CalcLineSegmentSlope 计算线段的斜率
CalcLineSegmentIntercept 计算线段的截距
ConvertLineSegmentGeneric 转换线段的泛型类型为特定类型
Point.Min 注释为 Max 的问题修复
This commit is contained in:
kercylan98 2023-06-25 15:14:56 +08:00
parent 269662486b
commit c7a6e09926
5 changed files with 81 additions and 23 deletions

View File

@ -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 与另一个圆是否发生重叠

View File

@ -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
}

View File

@ -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()
}

View File

@ -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))
}

View File

@ -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()