From c0570392bf1409b7dfdb404571fd7cea445336cd Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Tue, 20 Jun 2023 17:39:35 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E5=A2=9E=E5=8A=A0=E5=9C=86?= =?UTF-8?q?=E5=BD=A2=E7=9B=B8=E5=85=B3=E5=A4=84=E7=90=86=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- component/components/moving2d.go | 2 +- game/builtin/aoi2d.go | 6 +-- utils/geometry/astar/astar_example_test.go | 4 +- utils/geometry/circle.go | 49 +++++++++++++++++++++- utils/geometry/geometry.go | 9 +++- utils/geometry/line.go | 2 +- utils/geometry/navmesh/navmesh.go | 16 +++---- utils/geometry/navmesh/shape.go | 2 +- utils/geometry/position.go | 20 +++++++++ utils/geometry/shape.go | 12 ++++-- 10 files changed, 99 insertions(+), 23 deletions(-) diff --git a/component/components/moving2d.go b/component/components/moving2d.go index 3f78555..d59af9e 100644 --- a/component/components/moving2d.go +++ b/component/components/moving2d.go @@ -123,7 +123,7 @@ func (slf *Moving2D) handle() { if interval == 0 { continue } - distance := geometry.CalcDistance(x, y, entity.x, entity.y) + distance := geometry.CalcDistanceWithCoordinate(x, y, entity.x, entity.y) moveDistance := interval * (entity.GetSpeed() / (slf.timeUnit / 1000 / 1000)) if moveDistance >= distance || (x == entity.x && y == entity.y) { entity.SetPosition(entity.x, entity.y) diff --git a/game/builtin/aoi2d.go b/game/builtin/aoi2d.go index a1fce9d..0a4c6d9 100644 --- a/game/builtin/aoi2d.go +++ b/game/builtin/aoi2d.go @@ -177,7 +177,7 @@ func (slf *AOI2D) refresh(entity game.AOIEntity2D) { focus := slf.focus[guid] for eg, e := range focus { ex, ey := e.GetPosition() - if geometry.CalcDistance(x, y, ex, ey) > vision { + if geometry.CalcDistanceWithCoordinate(x, y, ex, ey) > vision { delete(focus, eg) delete(slf.focus[eg], guid) } @@ -243,13 +243,13 @@ func (slf *AOI2D) rangeVisionAreaEntities(entity game.AOIEntity2D, handle func(g } else { areaY = y } - areaDistance := geometry.CalcDistance(x, y, areaX, areaY) + areaDistance := geometry.CalcDistanceWithCoordinate(x, y, areaX, areaY) if areaDistance <= vision { for eg, e := range slf.areas[w][h] { if eg == guid { continue } - if ex, ey := e.GetPosition(); geometry.CalcDistance(x, y, ex, ey) > vision { + if ex, ey := e.GetPosition(); geometry.CalcDistanceWithCoordinate(x, y, ex, ey) > vision { continue } handle(eg, e) diff --git a/utils/geometry/astar/astar_example_test.go b/utils/geometry/astar/astar_example_test.go index 0948f5b..892ed95 100644 --- a/utils/geometry/astar/astar_example_test.go +++ b/utils/geometry/astar/astar_example_test.go @@ -37,9 +37,9 @@ func ExampleFind() { } paths := astar.Find[geometry.Point[int], int](graph, geometry.NewPoint(1, 1), geometry.NewPoint(8, 6), func(a, b geometry.Point[int]) int { - return geometry.CalcDistance(geometry.DoublePointToCoordinate(a, b)) + return geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(a, b)) }, func(a, b geometry.Point[int]) int { - return geometry.CalcDistance(geometry.DoublePointToCoordinate(a, b)) + return geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(a, b)) }) for _, path := range paths { diff --git a/utils/geometry/circle.go b/utils/geometry/circle.go index 8da0aa8..d121e01 100644 --- a/utils/geometry/circle.go +++ b/utils/geometry/circle.go @@ -5,8 +5,53 @@ import ( "math" ) +// 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 + } + 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 +} + +// Overlap 与另一个圆是否发生重叠 +func (slf Circle[V]) Overlap(circle Circle[V]) bool { + return CalcDistanceWithPoint(slf.Centroid(), circle.Centroid())/2 > slf.radius +} + +// Area 获取圆形面积 +func (slf Circle[V]) Area() V { + return V(math.Pi * math.Pow(float64(slf.Radius()), 2)) +} + +// Length 获取圆的周长 +func (slf Circle[V]) Length() V { + return V(2 * math.Pi * float64(slf.Radius())) +} + // GenerateCircle 通过传入圆的半径和需要的点数量,生成一个圆 -func GenerateCircle[V generic.SignedNumber](radius V, points int) Shape[V] { +func GenerateCircle[V generic.SignedNumber](radius V, points int) Circle[V] { angle := 2.0 * math.Pi / float64(points) var shape = make(Shape[V], points) for i := 0; i < points; i++ { @@ -15,5 +60,5 @@ func GenerateCircle[V generic.SignedNumber](radius V, points int) Shape[V] { y := radius * V(math.Sin(curAngle)) shape = append(shape, NewPoint(x, y)) } - return shape + return shape.ToCircle() } diff --git a/utils/geometry/geometry.go b/utils/geometry/geometry.go index bd3f1f1..38d010e 100644 --- a/utils/geometry/geometry.go +++ b/utils/geometry/geometry.go @@ -109,11 +109,16 @@ func CalcDirection[V generic.SignedNumber](x1, y1, x2, y2 V) Direction { return DirectionUnknown } -// CalcDistance 计算两点之间的距离 -func CalcDistance[V generic.SignedNumber](x1, y1, x2, y2 V) V { +// CalcDistanceWithCoordinate 计算两点之间的距离 +func CalcDistanceWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2 V) V { return V(math.Sqrt(math.Pow(float64(x2-x1), 2) + math.Pow(float64(y2-y1), 2))) } +// CalcDistanceWithPoint 计算两点之间的距离 +func CalcDistanceWithPoint[V generic.SignedNumber](point1, point2 Point[V]) V { + return CalcDistanceWithCoordinate(DoublePointToCoordinate(point1, point2)) +} + // CalcDistanceSquared 计算两点之间的平方距离 // - 这个函数的主要用途是在需要计算两点之间距离的情况下,但不需要得到实际的距离值,而只需要比较距离大小。因为平方根运算相对较为耗时,所以在只需要比较大小的情况下,通常会使用平方距离。 func CalcDistanceSquared[V generic.SignedNumber](x1, y1, x2, y2 V) V { diff --git a/utils/geometry/line.go b/utils/geometry/line.go index a00d431..81867bc 100644 --- a/utils/geometry/line.go +++ b/utils/geometry/line.go @@ -54,7 +54,7 @@ func (slf LineSegment[V]) GetEnd() Point[V] { // GetLength 获取该线段的长度 func (slf LineSegment[V]) GetLength() V { - return CalcDistance(DoublePointToCoordinate(slf.GetStart(), slf.GetEnd())) + return CalcDistanceWithCoordinate(DoublePointToCoordinate(slf.GetStart(), slf.GetEnd())) } // PointOnLineSegmentWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 diff --git a/utils/geometry/navmesh/navmesh.go b/utils/geometry/navmesh/navmesh.go index 4c27e13..e2d9abd 100644 --- a/utils/geometry/navmesh/navmesh.go +++ b/utils/geometry/navmesh/navmesh.go @@ -74,7 +74,7 @@ func (slf *NavMesh[V]) Find(point geometry.Point[V], maxDistance V) (distance V, break } br := geometry.CalcBoundingRadius(meshShape.Shape) - distance := geometry.CalcDistance(geometry.DoublePointToCoordinate( + distance := geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate( geometry.CalcRectangleCentroid(meshShape.Shape), point, )) @@ -111,13 +111,13 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry for _, meshShape := range slf.meshShapes { br := meshShape.BoundingRadius() - distance := geometry.CalcDistance(geometry.DoublePointToCoordinate(meshShape.Centroid(), start)) + distance := geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(meshShape.Centroid(), start)) if (distance <= startDistance || startDistance == V(-1)) && distance <= br && meshShape.Contains(start) { startShape = meshShape startDistance = distance } - distance = geometry.CalcDistance(geometry.DoublePointToCoordinate(meshShape.Centroid(), end)) + distance = geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(meshShape.Centroid(), end)) if (distance <= endDistance || endDistance == V(-1)) && distance <= br && meshShape.Contains(end) { endShape = meshShape endDistance = distance @@ -127,7 +127,7 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry if endShape == nil && slf.meshShrinkAmount > V(0) { for _, meshShape := range slf.meshShapes { br := meshShape.BoundingRadius() + slf.meshShrinkAmount - distance := geometry.CalcDistance(geometry.DoublePointToCoordinate(meshShape.Centroid(), end)) + distance := geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(meshShape.Centroid(), end)) if distance <= br { _, projectionDistance := geometry.ProjectionPointToShape(end, meshShape.Shape) if projectionDistance <= slf.meshShrinkAmount && projectionDistance < endDistance { @@ -145,7 +145,7 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry if startShape == nil && slf.meshShrinkAmount > 0 { for _, meshShape := range slf.meshShapes { br := meshShape.BoundingRadius() + slf.meshShrinkAmount - distance := geometry.CalcDistance(geometry.DoublePointToCoordinate(meshShape.Centroid(), start)) + distance := geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(meshShape.Centroid(), start)) if distance <= br { _, projectionDistance := geometry.ProjectionPointToShape(start, meshShape.Shape) if projectionDistance <= slf.meshShrinkAmount && projectionDistance < startDistance { @@ -165,9 +165,9 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry } path := astar.Find[*shape[V], V](slf, startShape, endShape, func(a, b *shape[V]) V { - return geometry.CalcDistance(geometry.DoublePointToCoordinate(a.centroid, b.centroid)) + return geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(a.centroid, b.centroid)) }, func(a, b *shape[V]) V { - return geometry.CalcDistance(geometry.DoublePointToCoordinate(a.centroid, b.centroid)) + return geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(a.centroid, b.centroid)) }) if len(path) == 0 { @@ -223,7 +223,7 @@ func (slf *NavMesh[V]) generateLink() { targetShapePkg := slf.meshShapes[t] targetShapeCentroid := targetShapePkg.Centroid() targetShapeBoundingRadius := targetShapePkg.BoundingRadius() - centroidDistance := geometry.CalcDistance(geometry.DoublePointToCoordinate(shapeCentroid, targetShapeCentroid)) + centroidDistance := geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(shapeCentroid, targetShapeCentroid)) if centroidDistance > shapeBoundingRadius+targetShapeBoundingRadius { continue } diff --git a/utils/geometry/navmesh/shape.go b/utils/geometry/navmesh/shape.go index 9264a32..dc0682e 100644 --- a/utils/geometry/navmesh/shape.go +++ b/utils/geometry/navmesh/shape.go @@ -45,5 +45,5 @@ func (slf *shape[V]) IsWall() bool { } func (slf *shape[V]) GetCost(point geometry.Point[V]) V { - return geometry.CalcDistance(geometry.DoublePointToCoordinate(slf.Centroid(), point)) + return geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(slf.Centroid(), point)) } diff --git a/utils/geometry/position.go b/utils/geometry/position.go index f831be7..19a4ce2 100644 --- a/utils/geometry/position.go +++ b/utils/geometry/position.go @@ -59,6 +59,26 @@ func (slf Point[V]) Copy() Point[V] { return PointCopy(slf) } +// Add 得到加上 point 后的点 +func (slf Point[V]) Add(point Point[V]) Point[V] { + return slf.GetOffset(point.GetXY()) +} + +// Sub 得到减去 point 后的点 +func (slf Point[V]) Sub(point Point[V]) Point[V] { + return NewPoint(slf.GetX()-point.GetX(), slf.GetY()-point.GetY()) +} + +// Mul 得到乘以 point 后的点 +func (slf Point[V]) Mul(point Point[V]) Point[V] { + return NewPoint(slf.GetX()*point.GetX(), slf.GetY()*point.GetY()) +} + +// Div 得到除以 point 后的点 +func (slf Point[V]) Div(point Point[V]) Point[V] { + return NewPoint(slf.GetX()/point.GetX(), slf.GetY()/point.GetY()) +} + // NewPointCap 创建一个由 x、y 坐标组成的点,这个点具有一个数据容量 func NewPointCap[V generic.SignedNumber, D any](x, y V) PointCap[V, D] { return PointCap[V, D]{ diff --git a/utils/geometry/shape.go b/utils/geometry/shape.go index a954d9a..7be4971 100644 --- a/utils/geometry/shape.go +++ b/utils/geometry/shape.go @@ -84,6 +84,12 @@ func (slf Shape[V]) Contains(point Point[V]) bool { return inside } +// ToCircle 将形状转换为圆形进行处理 +// - 当形状非圆形时将会产生意外情况 +func (slf Shape[V]) ToCircle() Circle[V] { + return Circle[V]{slf} +} + // String 将该形状转换为可视化的字符串进行返回 func (slf Shape[V]) String() string { var result string @@ -412,7 +418,7 @@ func CalcBoundingRadius[V generic.SignedNumber](shape Shape[V]) V { var boundingRadius V var centroid = CalcRectangleCentroid(shape) for _, point := range shape.Points() { - distance := CalcDistance(DoublePointToCoordinate(centroid, point)) + distance := CalcDistanceWithCoordinate(DoublePointToCoordinate(centroid, point)) if distance > boundingRadius { boundingRadius = distance } @@ -424,7 +430,7 @@ func CalcBoundingRadius[V generic.SignedNumber](shape Shape[V]) V { func CalcBoundingRadiusWithCentroid[V generic.SignedNumber](shape Shape[V], centroid Point[V]) V { var boundingRadius V for _, point := range shape.Points() { - distance := CalcDistance(DoublePointToCoordinate(centroid, point)) + distance := CalcDistanceWithCoordinate(DoublePointToCoordinate(centroid, point)) if distance > boundingRadius { boundingRadius = distance } @@ -459,7 +465,7 @@ func ProjectionPointToShape[V generic.SignedNumber](point Point[V], shape Shape[ for _, edge := range shape.Edges() { projectedPoint := CalcProjectionPoint(edge, point) - distance := CalcDistance(DoublePointToCoordinate(point, projectedPoint)) + distance := CalcDistanceWithCoordinate(DoublePointToCoordinate(point, projectedPoint)) if !hasClosestProjection || distance < closestDistance { closestDistance = distance closestProjection = projectedPoint