Skip to content

Commit

Permalink
other: v2 geometry 包调整、astar、navmesh 移动到 navigate 包
Browse files Browse the repository at this point in the history
  • Loading branch information
kercylan98 committed May 9, 2024
1 parent 438134f commit bd21158
Show file tree
Hide file tree
Showing 21 changed files with 1,425 additions and 73 deletions.
4 changes: 2 additions & 2 deletions toolkit/collection/clone_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func ExampleCloneSliceN() {
}

// 通过将 map 克隆为 2 个新的 map,将会得到一个新的 map result,而 result 和 map 将不会有任何关联,但是如果 map 中的元素是引用类型,那么 result 中的元素将会和 map 中的元素指向同一个地址
// - result 的结果为 [map[1:1 2:2 3:3] map[1:1 2:2 3:3]] `无序的 Key-Value 对`
// - result 的结果为 [map[1:1 2:2 3:3] map[1:1 2:2 3:3]] `无序的 Key-value 对`
// - 示例中的结果将会输出 2
func ExampleCloneMapN() {
var m = map[int]int{1: 1, 2: 2, 3: 3}
Expand All @@ -59,7 +59,7 @@ func ExampleCloneSlices() {
}

// 通过将多个 map 克隆为 2 个新的 map,将会得到一个新的 map result,而 result 和 map 将不会有任何关联,但是如果 map 中的元素是引用类型,那么 result 中的元素将会和 map 中的元素指向同一个地址
// - result 的结果为 [map[1:1 2:2 3:3] map[1:1 2:2 3:3]] `无序的 Key-Value 对`
// - result 的结果为 [map[1:1 2:2 3:3] map[1:1 2:2 3:3]] `无序的 Key-value 对`
func ExampleCloneMaps() {
var m1 = map[int]int{1: 1, 2: 2, 3: 3}
var m2 = map[int]int{1: 1, 2: 2, 3: 3}
Expand Down
15 changes: 15 additions & 0 deletions toolkit/geometry/angle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package geometry

import "github.com/kercylan98/minotaur/toolkit/constraints"

// CalcAngleDifference 计算两个极角之间的最小角度差。
// 结果在 -180 到 180 度之间,适用于极角、方位角或其他类似场景。
func CalcAngleDifference[T constraints.Number](a, b T) float64 {
diff := float64(a) - float64(b)
if diff > 180 {
diff -= 360
} else if diff < -180 {
diff += 360
}
return diff
}
48 changes: 48 additions & 0 deletions toolkit/geometry/asserts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package geometry

// AssertPolygonValid 断言检查多边形是否有效
func AssertPolygonValid(polygons ...Polygon) {
for _, polygon := range polygons {
AssertPointValid(polygon...)
if len(polygon) < 3 {
panic("polygon must have at least 3 points")
}
}
}

// AssertLineSegmentValid 断言检查线段是否有效
func AssertLineSegmentValid(lineSegments ...LineSegment) {
for _, lineSegment := range lineSegments {
AssertPointValid(lineSegment...)
if len(lineSegment) < 2 {
panic("line segment must have at least 2 points")
}
}
}

// AssertPointValid 断言检查点是否有效
func AssertPointValid(points ...Point) {
for _, point := range points {
if len(point) != 2 {
panic("point must have 2 coordinates")
}
}
}

// AssertVector2Valid 断言检查二维向量是否有效
func AssertVector2Valid(vectors ...Vector2) {
for _, vector := range vectors {
if len(vector) != 2 {
panic("vector must have 2 coordinates")
}
}
}

// AssertVector3Valid 断言检查三维向量是否有效
func AssertVector3Valid(vectors ...Vector3) {
for _, vector := range vectors {
if len(vector) != 3 {
panic("vector must have 3 coordinates")
}
}
}
122 changes: 122 additions & 0 deletions toolkit/geometry/direction_2d.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package geometry

import (
"github.com/kercylan98/minotaur/toolkit/constraints"
"math"
)

// Direction2D 二维方向
type Direction2D = Vector2

var (
direction2DUnknown = NewVector2(0, 0) // 未知
direction2DUp = NewVector2(0, 1) // 上
direction2DDown = NewVector2(0, -1) // 下
direction2DLeft = NewVector2(-1, 0) // 左
direction2DRight = NewVector2(1, 0) // 右
direction2DLeftUp = NewVector2(-1, 1) // 左上
direction2DRightUp = NewVector2(1, 1) // 右上
direction2DLeftDown = NewVector2(-1, -1) // 左下
direction2DRightDown = NewVector2(1, -1) // 右下

direction2D4 = []Direction2D{direction2DUp, direction2DDown, direction2DLeft, direction2DRight} // 上下左右四个方向的数组 // 上下左右四个方向的数组
direction2D8 = []Direction2D{ // 上下左右、左上、右上、左下、右下八个方向的数组
direction2DUp, direction2DDown, direction2DLeft, direction2DRight,
direction2DLeftUp, direction2DRightUp, direction2DLeftDown, direction2DRightDown,
}
)

// Direction2D4 上下左右四个方向
func Direction2D4() []Direction2D {
return direction2D4
}

// Direction2D8 上下左右、左上、右上、左下、右下八个方向
func Direction2D8() []Direction2D {
return direction2D8
}

// Direction2DUnknown 获取未知方向
func Direction2DUnknown() Direction2D {
return direction2DUnknown
}

// Direction2DUp 获取上方向
func Direction2DUp() Direction2D {
return direction2DUp
}

// Direction2DDown 获取下方向
func Direction2DDown() Direction2D {
return direction2DDown
}

// Direction2DLeft 获取左方向
func Direction2DLeft() Direction2D {
return direction2DLeft
}

// Direction2DRight 获取右方向
func Direction2DRight() Direction2D {
return direction2DRight
}

// Direction2DLeftUp 获取左上方向
func Direction2DLeftUp() Direction2D {
return direction2DLeftUp
}

// Direction2DRightUp 获取右上方向
func Direction2DRightUp() Direction2D {
return direction2DRightUp
}

// Direction2DLeftDown 获取左下方向
func Direction2DLeftDown() Direction2D {
return direction2DLeftDown
}

// Direction2DRightDown 获取右下方向
func Direction2DRightDown() Direction2D {
return direction2DRightDown
}

// CalcOppositionDirection2D 计算二维方向的反方向
func CalcOppositionDirection2D(direction Direction2D) Direction2D {
switch {
case direction.Equal(direction2DUp):
return direction2DDown
case direction.Equal(direction2DDown):
return direction2DUp
case direction.Equal(direction2DLeft):
return direction2DRight
case direction.Equal(direction2DRight):
return direction2DLeft
case direction.Equal(direction2DLeftUp):
return direction2DRightDown
case direction.Equal(direction2DRightUp):
return direction2DLeftDown
case direction.Equal(direction2DLeftDown):
return direction2DRightUp
case direction.Equal(direction2DRightDown):
return direction2DLeftUp
default:
return direction2DUnknown
}
}

// CalcOffsetInDirection2D 计算特定方向上按照指定距离偏移后的坐标
func CalcOffsetInDirection2D[T constraints.Number](vector Vector2, direction Direction2D, offset T) Vector2 {
return vector.Add(direction.Mul(float64(offset)))
}

// CalcDirection2DWithAngle 通过角度计算二维方向
func CalcDirection2DWithAngle[T constraints.Number](angle T) Direction2D {
angleFloat := float64(angle)
return NewVector2(math.Cos(angleFloat), math.Sin(angleFloat))
}

// CalcAngleWithDirection2D 计算二维方向的角度
func CalcAngleWithDirection2D(direction Direction2D) float64 {
return math.Atan2(direction.GetY(), direction.GetX())
}
38 changes: 38 additions & 0 deletions toolkit/geometry/floor_plan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package geometry

import (
"strings"
)

// FloorPlan 平面图
type FloorPlan []string

// IsFree 检查位置是否为空格
func (fp FloorPlan) IsFree(position Vector2) bool {
return fp.IsInBounds(position) && fp[int(position.GetY())][int(position.GetX())] == ' '
}

// IsInBounds 检查位置是否在边界内
func (fp FloorPlan) IsInBounds(position Vector2) bool {
x, y := int(position.GetX()), int(position.GetY())
return (0 <= x && x < len(fp[y])) && (0 <= y && y < len(fp))
}

// Put 设置平面图特定位置的字符
func (fp FloorPlan) Put(position Vector2, c rune) {
x, y := int(position.GetX()), int(position.GetY())
fp[y] = fp[y][:x] + string(c) + fp[y][x+1:]
}

// String 获取平面图结果
func (fp FloorPlan) String() string {
var builder strings.Builder
var last = len(fp) - 1
for i, row := range fp {
builder.WriteString(row)
if i != last {
builder.WriteByte('\n')
}
}
return builder.String()
}
101 changes: 37 additions & 64 deletions toolkit/geometry/line.go
Original file line number Diff line number Diff line change
@@ -1,82 +1,55 @@
package geometry

import "math"

type Line [2]Vector2

// NewLine 创建一条直线
func NewLine(start, end Vector2) Line {
if len(start) != 2 || len(end) != 2 {
panic("vector size mismatch")
}
return Line{start, end}
func NewLine(point Point, slope float64) Line {
AssertPointValid(point)
return Line{point, slope}
}

// GetStart 获取起点
func (l Line) GetStart() Vector2 {
return l[0]
// Line 直线是由一个点和斜率定义的
type Line struct {
point Point // 直线上的一个点
slope float64 // 斜率
}

// GetEnd 获取终点
func (l Line) GetEnd() Vector2 {
return l[1]
// IsPointOn 判断点是否在直线上
func (l Line) IsPointOn(point Point) bool {
AssertPointValid(l.point, point)
return l.slope*(point[0]-l.point[0]) == point[1]-l.point[1]
}

// GetLength 获取长度
func (l Line) GetLength() float64 {
return l.GetStart().Sub(l.GetEnd()).Length()
// IsPointOnOrAbove 判断点是否在直线上或者在直线上方
func (l Line) IsPointOnOrAbove(point Point) bool {
AssertPointValid(l.point, point)
return l.slope*(point[0]-l.point[0]) >= point[1]-l.point[1]
}

// Contains 判断点是否在直线上
func (l Line) Contains(point Vector2) bool {
if len(point) != 2 {
panic("vector size mismatch")
}

v1 := l.GetEnd().Sub(l.GetStart()) // 直线的向量
v2 := point.Sub(l.GetStart()) // 直线起点到给定点的向量

// 计算交叉积
crossProduct := v1[0]*v2[1] - v1[1]*v2[0]

// 数值误差
epsilon := 1e-9
return math.Abs(crossProduct) < epsilon
// IsPointOnOrBelow 判断点是否在直线上或者在直线下方
func (l Line) IsPointOnOrBelow(point Point) bool {
AssertPointValid(l.point, point)
return l.slope*(point[0]-l.point[0]) <= point[1]-l.point[1]
}

// Intersect 判断两条直线是否相交
func (l Line) Intersect(l2 Line) bool {
// 两条直线的向量
v1 := l.GetEnd().Sub(l.GetStart())
v2 := l2.GetEnd().Sub(l2.GetStart())

// 计算交叉积
crossProduct1 := v1[0]*v2[1] - v1[1]*v2[0]

// 两条直线的起点到另一条直线起点的向量
v3 := l2.GetStart().Sub(l.GetStart())
v4 := l2.GetEnd().Sub(l.GetStart())

// 计算交叉积
crossProduct2 := v1[0]*v3[1] - v1[1]*v3[0]
crossProduct3 := v1[0]*v4[1] - v1[1]*v4[0]

return crossProduct1 != 0 && crossProduct2*crossProduct3 < 0
// IsPointAbove 判断点是否在直线上方
func (l Line) IsPointAbove(point Point) bool {
AssertPointValid(l.point, point)
return l.slope*(point[0]-l.point[0]) > point[1]-l.point[1]
}

// IntersectCircle 判断直线是否与圆相交
func (l Line) IntersectCircle(c Circle) bool {
// 直线的向量
v1 := l.GetEnd().Sub(l.GetStart())

// 直线起点到圆心的向量
v2 := c.GetCenter().Sub(l.GetStart())

// 计算直线到圆心的投影长度
projection := v1.Dot(v2) / v1.Length()
// IsPointBelow 判断点是否在直线下方
func (l Line) IsPointBelow(point Point) bool {
AssertPointValid(l.point, point)
return l.slope*(point[0]-l.point[0]) < point[1]-l.point[1]
}

// 计算直线到圆心的距离
distance := v2.Length()
// IsPointLeft 判断点是否在直线左侧
func (l Line) IsPointLeft(point Point) bool {
AssertPointValid(l.point, point)
return l.slope*(point[0]-l.point[0]) < point[1]-l.point[1]
}

return distance <= c.GetRadius() && projection >= 0 && projection <= v1.Length()
// IsPointRight 判断点是否在直线右侧
func (l Line) IsPointRight(point Point) bool {
AssertPointValid(l.point, point)
return l.slope*(point[0]-l.point[0]) > point[1]-l.point[1]
}
Loading

0 comments on commit bd21158

Please sign in to comment.