From d10c05dfe1e31bdb19c2879e65dc938723b4f9dd Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Tue, 20 Jun 2023 16:51:04 +0800 Subject: [PATCH] =?UTF-8?q?:boom:=20Line=20=E6=9B=B4=E5=90=8D=E4=B8=BA=20L?= =?UTF-8?q?ineSegment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/geometry/line.go | 88 +++++++++++++++---------------- utils/geometry/navmesh/navmesh.go | 14 ++--- utils/geometry/navmesh/shape.go | 6 +-- utils/geometry/position.go | 2 +- utils/geometry/shape.go | 12 ++--- 5 files changed, 61 insertions(+), 61 deletions(-) diff --git a/utils/geometry/line.go b/utils/geometry/line.go index 6635874..a00d431 100644 --- a/utils/geometry/line.go +++ b/utils/geometry/line.go @@ -6,112 +6,112 @@ import ( "sort" ) -// NewLine 创建一根线段 -func NewLine[V generic.SignedNumber](start, end Point[V]) Line[V] { +// NewLineSegment 创建一根线段 +func NewLineSegment[V generic.SignedNumber](start, end Point[V]) LineSegment[V] { if start.Equal(end) { panic("two points of the line segment are the same") } - return Line[V]{start, end} + return LineSegment[V]{start, end} } -// NewLineCap 创建一根包含数据的线段 -func NewLineCap[V generic.SignedNumber, Data any](start, end Point[V], data Data) LineCap[V, Data] { - return LineCap[V, Data]{NewLine(start, end), data} +// NewLineSegmentCap 创建一根包含数据的线段 +func NewLineSegmentCap[V generic.SignedNumber, Data any](start, end Point[V], data Data) LineSegmentCap[V, Data] { + return LineSegmentCap[V, Data]{NewLineSegment(start, end), data} } -// NewLineCapWithLine 通过已有线段创建一根包含数据的线段 -func NewLineCapWithLine[V generic.SignedNumber, Data any](line Line[V], data Data) LineCap[V, Data] { - return LineCap[V, Data]{line, data} +// NewLineSegmentCapWithLine 通过已有线段创建一根包含数据的线段 +func NewLineSegmentCapWithLine[V generic.SignedNumber, Data any](line LineSegment[V], data Data) LineSegmentCap[V, Data] { + return LineSegmentCap[V, Data]{line, data} } -// Line 通过两个点表示一根线段 -type Line[V generic.SignedNumber] [2]Point[V] +// LineSegment 通过两个点表示一根线段 +type LineSegment[V generic.SignedNumber] [2]Point[V] -// LineCap 可以包含一份额外数据的线段 -type LineCap[V generic.SignedNumber, Data any] struct { - Line[V] +// LineSegmentCap 可以包含一份额外数据的线段 +type LineSegmentCap[V generic.SignedNumber, Data any] struct { + LineSegment[V] Data Data } -func (slf *LineCap[V, Data]) GetData() Data { +func (slf *LineSegmentCap[V, Data]) GetData() Data { return slf.Data } // GetPoints 获取该线段的两个点 -func (slf Line[V]) GetPoints() [2]Point[V] { +func (slf LineSegment[V]) GetPoints() [2]Point[V] { return slf } // GetStart 获取该线段的开始位置 -func (slf Line[V]) GetStart() Point[V] { +func (slf LineSegment[V]) GetStart() Point[V] { return slf[0] } // GetEnd 获取该线段的结束位置 -func (slf Line[V]) GetEnd() Point[V] { +func (slf LineSegment[V]) GetEnd() Point[V] { return slf[1] } // GetLength 获取该线段的长度 -func (slf Line[V]) GetLength() V { +func (slf LineSegment[V]) GetLength() V { return CalcDistance(DoublePointToCoordinate(slf.GetStart(), slf.GetEnd())) } -// PointOnLineWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 -func PointOnLineWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool { +// PointOnLineSegmentWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +func PointOnLineSegmentWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool { return (x-x1)*(y2-y1) == (x2-x1)*(y-y1) } -// PointOnLineWithPos 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 -func PointOnLineWithPos[V generic.SignedNumber](width, pos1, pos2, pos V) bool { +// PointOnLineSegmentWithPos 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +func PointOnLineSegmentWithPos[V generic.SignedNumber](width, pos1, pos2, pos V) bool { x1, y1 := PosToCoordinate(width, pos1) x2, y2 := PosToCoordinate(width, pos2) x, y := PosToCoordinate(width, pos) - return PointOnLineWithCoordinate(x1, y1, x2, y2, x, y) + return PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y) } -// PointOnLineWithPoint 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 -func PointOnLineWithPoint[V generic.SignedNumber](point1, point2, point Point[V]) bool { +// PointOnLineSegmentWithPoint 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +func PointOnLineSegmentWithPoint[V generic.SignedNumber](point1, point2, point Point[V]) bool { x1, y1 := point1.GetXY() x2, y2 := point2.GetXY() x, y := point.GetXY() - return PointOnLineWithCoordinate(x1, y1, x2, y2, x, y) + return PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y) } -// PointOnSegmentWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 -// - 与 PointOnLineWithCoordinate 不同的是, PointOnSegmentWithCoordinate 中会判断线段及点的位置是否正确 -func PointOnSegmentWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool { - return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineWithCoordinate(x1, y1, x2, y2, x, y) +// PointOnLineSegmentWithCoordinateInBounds 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +// - 与 PointOnLineSegmentWithCoordinate 不同的是, PointOnLineSegmentWithCoordinateInBounds 中会判断线段及点的位置是否正确 +func PointOnLineSegmentWithCoordinateInBounds[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool { + return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y) } -// PointOnSegmentWithPos 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 -// - 与 PointOnLineWithPos 不同的是, PointOnSegmentWithPos 中会判断线段及点的位置是否正确 -func PointOnSegmentWithPos[V generic.SignedNumber](width, pos1, pos2, pos V) bool { +// PointOnLineSegmentWithPosInBounds 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +// - 与 PointOnLineSegmentWithPos 不同的是, PointOnLineSegmentWithPosInBounds 中会判断线段及点的位置是否正确 +func PointOnLineSegmentWithPosInBounds[V generic.SignedNumber](width, pos1, pos2, pos V) bool { x1, y1 := PosToCoordinate(width, pos1) x2, y2 := PosToCoordinate(width, pos2) x, y := PosToCoordinate(width, pos) - return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineWithCoordinate(x1, y1, x2, y2, x, y) + return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y) } -// PointOnSegmentWithPoint 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 -// - 与 PointOnLineWithPoint 不同的是, PointOnSegmentWithPoint 中会判断线段及点的位置是否正确 -func PointOnSegmentWithPoint[V generic.SignedNumber](point1, point2, point Point[V]) bool { +// PointOnLineSegmentWithPointInBounds 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +// - 与 PointOnLineSegmentWithPoint 不同的是, PointOnLineSegmentWithPointInBounds 中会判断线段及点的位置是否正确 +func PointOnLineSegmentWithPointInBounds[V generic.SignedNumber](point1, point2, point Point[V]) bool { x1, y1 := point1.GetXY() x2, y2 := point2.GetXY() x, y := point.GetXY() - return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineWithCoordinate(x1, y1, x2, y2, x, y) + return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y) } -// CalcLineIsCollinear 检查两条线段在一个误差内是否共线 +// CalcLineSegmentIsCollinear 检查两条线段在一个误差内是否共线 // - 共线是指两条线段在同一直线上,即它们的延长线可以重合 -func CalcLineIsCollinear[V generic.SignedNumber](line1, line2 Line[V], tolerance V) bool { +func CalcLineSegmentIsCollinear[V generic.SignedNumber](line1, line2 LineSegment[V], tolerance V) bool { area1 := CalcTriangleTwiceArea(line1.GetStart(), line1.GetEnd(), line2.GetStart()) area2 := CalcTriangleTwiceArea(line1.GetStart(), line1.GetEnd(), line2.GetEnd()) return maths.Tolerance(area1, 0, tolerance) && maths.Tolerance(area2, 0, tolerance) } -// CalcLineIsOverlap 通过对点进行排序来检查两条共线线段是否重叠,返回重叠线段 -func CalcLineIsOverlap[V generic.SignedNumber](line1, line2 Line[V]) (line Line[V], overlap bool) { +// CalcLineSegmentIsOverlap 通过对点进行排序来检查两条共线线段是否重叠,返回重叠线段 +func CalcLineSegmentIsOverlap[V generic.SignedNumber](line1, line2 LineSegment[V]) (line LineSegment[V], overlap bool) { l1ps, l1pe := NewPointCapWithPoint(line1.GetStart(), true), NewPointCapWithPoint(line1.GetEnd(), true) l2ps, l2pe := NewPointCapWithPoint(line2.GetStart(), false), NewPointCapWithPoint(line2.GetEnd(), false) var shapes = [][]PointCap[V, bool]{ @@ -136,5 +136,5 @@ func CalcLineIsOverlap[V generic.SignedNumber](line1, line2 Line[V]) (line Line[ if notOverlap || singlePointOverlap { return line, false } - return NewLine(shapes[1][2].Point, shapes[2][2].Point), true + return NewLineSegment(shapes[1][2].Point, shapes[2][2].Point), true } diff --git a/utils/geometry/navmesh/navmesh.go b/utils/geometry/navmesh/navmesh.go index 8415c92..4c27e13 100644 --- a/utils/geometry/navmesh/navmesh.go +++ b/utils/geometry/navmesh/navmesh.go @@ -185,7 +185,7 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry continue } - var portal geometry.Line[V] + var portal geometry.LineSegment[V] var find bool for i := 0; i < len(current.links); i++ { if current.links[i].id == next.id { @@ -230,11 +230,11 @@ func (slf *NavMesh[V]) generateLink() { for _, shapeEdge := range shapeEdges { for _, targetEdge := range targetShapePkg.Edges() { - if !geometry.CalcLineIsCollinear(shapeEdge, targetEdge, V(maths.GetDefaultTolerance())) { + if !geometry.CalcLineSegmentIsCollinear(shapeEdge, targetEdge, V(maths.GetDefaultTolerance())) { continue } - var overlapLine, overlap = geometry.CalcLineIsOverlap(shapeEdge, targetEdge) + var overlapLine, overlap = geometry.CalcLineSegmentIsOverlap(shapeEdge, targetEdge) if !overlap { continue } @@ -248,9 +248,9 @@ func (slf *NavMesh[V]) generateLink() { a3 := geometry.CalcAngleDifference(edgeAngle, a1) a4 := geometry.CalcAngleDifference(edgeAngle, a2) if a3 < a4 { - shapePkg.portals = append(shapePkg.portals, geometry.NewLine(overlapLine.GetStart(), overlapLine.GetEnd())) + shapePkg.portals = append(shapePkg.portals, geometry.NewLineSegment(overlapLine.GetStart(), overlapLine.GetEnd())) } else { - shapePkg.portals = append(shapePkg.portals, geometry.NewLine(overlapLine.GetEnd(), overlapLine.GetStart())) + shapePkg.portals = append(shapePkg.portals, geometry.NewLineSegment(overlapLine.GetEnd(), overlapLine.GetStart())) } edgeAngle = geometry.CalcAngle(geometry.DoublePointToCoordinate(targetShapeCentroid, targetEdge.GetStart())) @@ -259,9 +259,9 @@ func (slf *NavMesh[V]) generateLink() { a3 = geometry.CalcAngleDifference(edgeAngle, a1) a4 = geometry.CalcAngleDifference(edgeAngle, a2) if a3 < a4 { - targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLine(overlapLine.GetStart(), overlapLine.GetEnd())) + targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLineSegment(overlapLine.GetStart(), overlapLine.GetEnd())) } else { - targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLine(overlapLine.GetEnd(), overlapLine.GetStart())) + targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLineSegment(overlapLine.GetEnd(), overlapLine.GetStart())) } } } diff --git a/utils/geometry/navmesh/shape.go b/utils/geometry/navmesh/shape.go index 9f0ee72..9264a32 100644 --- a/utils/geometry/navmesh/shape.go +++ b/utils/geometry/navmesh/shape.go @@ -19,16 +19,16 @@ type shape[V generic.SignedNumber] struct { id int geometry.Shape[V] links []*shape[V] - portals []geometry.Line[V] + portals []geometry.LineSegment[V] boundingRadius V centroid geometry.Point[V] - edges []geometry.Line[V] + edges []geometry.LineSegment[V] weight V x, y V } -func (slf *shape[V]) Edges() []geometry.Line[V] { +func (slf *shape[V]) Edges() []geometry.LineSegment[V] { return slf.edges } diff --git a/utils/geometry/position.go b/utils/geometry/position.go index 11e2b52..f831be7 100644 --- a/utils/geometry/position.go +++ b/utils/geometry/position.go @@ -174,7 +174,7 @@ func DoublePointToCoordinate[V generic.SignedNumber](point1, point2 Point[V]) (x // CalcProjectionPoint 计算一个点到一条线段的最近点(即投影点)的。这个函数接收一个点和一条线段作为输入,线段由两个端点组成。 // - 该函数的主要用于需要计算一个点到一条线段的最近点的情况下 -func CalcProjectionPoint[V generic.SignedNumber](line Line[V], point Point[V]) Point[V] { +func CalcProjectionPoint[V generic.SignedNumber](line LineSegment[V], point Point[V]) Point[V] { ax, ay, bx, by := DoublePointToCoordinate(line.GetStart(), line.GetEnd()) ds := CalcDistanceSquared(ax, ay, bx, by) px, py := point.GetXY() diff --git a/utils/geometry/shape.go b/utils/geometry/shape.go index 61947ed..a954d9a 100644 --- a/utils/geometry/shape.go +++ b/utils/geometry/shape.go @@ -363,22 +363,22 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result [] // Edges 获取该形状每一条边 // - 该形状需要最少由3个点组成,否则将不会返回任意一边 -func (slf Shape[V]) Edges() (edges []Line[V]) { +func (slf Shape[V]) Edges() (edges []LineSegment[V]) { if len(slf) < 3 { return } for i := 1; i < slf.PointCount(); i++ { before := slf[i-1] - edges = append(edges, NewLine(before, slf[i])) + edges = append(edges, NewLineSegment(before, slf[i])) } - edges = append(edges, NewLine(slf[0], slf[len(slf)-1])) + edges = append(edges, NewLineSegment(slf[0], slf[len(slf)-1])) return edges } // IsPointOnEdge 检查点是否在该形状的一条边上 func (slf Shape[V]) IsPointOnEdge(point Point[V]) bool { for _, edge := range slf.Edges() { - if PointOnSegmentWithPoint(edge.GetStart(), edge.GetEnd(), point) { + if PointOnLineSegmentWithPointInBounds(edge.GetStart(), edge.GetEnd(), point) { return true } } @@ -442,9 +442,9 @@ func CalcTriangleTwiceArea[V generic.SignedNumber](a, b, c Point[V]) V { } // IsPointOnEdge 检查点是否在 edges 的任意一条边上 -func IsPointOnEdge[V generic.SignedNumber](edges []Line[V], point Point[V]) bool { +func IsPointOnEdge[V generic.SignedNumber](edges []LineSegment[V], point Point[V]) bool { for _, edge := range edges { - if PointOnSegmentWithPoint(edge.GetStart(), edge.GetEnd(), point) { + if PointOnLineSegmentWithPointInBounds(edge.GetStart(), edge.GetEnd(), point) { return true } }