💥 Line 更名为 LineSegment
This commit is contained in:
parent
68e456fe8f
commit
d10c05dfe1
|
@ -6,112 +6,112 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewLine 创建一根线段
|
// NewLineSegment 创建一根线段
|
||||||
func NewLine[V generic.SignedNumber](start, end Point[V]) Line[V] {
|
func NewLineSegment[V generic.SignedNumber](start, end Point[V]) LineSegment[V] {
|
||||||
if start.Equal(end) {
|
if start.Equal(end) {
|
||||||
panic("two points of the line segment are the same")
|
panic("two points of the line segment are the same")
|
||||||
}
|
}
|
||||||
return Line[V]{start, end}
|
return LineSegment[V]{start, end}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLineCap 创建一根包含数据的线段
|
// NewLineSegmentCap 创建一根包含数据的线段
|
||||||
func NewLineCap[V generic.SignedNumber, Data any](start, end Point[V], data Data) LineCap[V, Data] {
|
func NewLineSegmentCap[V generic.SignedNumber, Data any](start, end Point[V], data Data) LineSegmentCap[V, Data] {
|
||||||
return LineCap[V, Data]{NewLine(start, end), data}
|
return LineSegmentCap[V, Data]{NewLineSegment(start, end), data}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLineCapWithLine 通过已有线段创建一根包含数据的线段
|
// NewLineSegmentCapWithLine 通过已有线段创建一根包含数据的线段
|
||||||
func NewLineCapWithLine[V generic.SignedNumber, Data any](line Line[V], data Data) LineCap[V, Data] {
|
func NewLineSegmentCapWithLine[V generic.SignedNumber, Data any](line LineSegment[V], data Data) LineSegmentCap[V, Data] {
|
||||||
return LineCap[V, Data]{line, data}
|
return LineSegmentCap[V, Data]{line, data}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Line 通过两个点表示一根线段
|
// LineSegment 通过两个点表示一根线段
|
||||||
type Line[V generic.SignedNumber] [2]Point[V]
|
type LineSegment[V generic.SignedNumber] [2]Point[V]
|
||||||
|
|
||||||
// LineCap 可以包含一份额外数据的线段
|
// LineSegmentCap 可以包含一份额外数据的线段
|
||||||
type LineCap[V generic.SignedNumber, Data any] struct {
|
type LineSegmentCap[V generic.SignedNumber, Data any] struct {
|
||||||
Line[V]
|
LineSegment[V]
|
||||||
Data Data
|
Data Data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *LineCap[V, Data]) GetData() Data {
|
func (slf *LineSegmentCap[V, Data]) GetData() Data {
|
||||||
return slf.Data
|
return slf.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPoints 获取该线段的两个点
|
// GetPoints 获取该线段的两个点
|
||||||
func (slf Line[V]) GetPoints() [2]Point[V] {
|
func (slf LineSegment[V]) GetPoints() [2]Point[V] {
|
||||||
return slf
|
return slf
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStart 获取该线段的开始位置
|
// GetStart 获取该线段的开始位置
|
||||||
func (slf Line[V]) GetStart() Point[V] {
|
func (slf LineSegment[V]) GetStart() Point[V] {
|
||||||
return slf[0]
|
return slf[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnd 获取该线段的结束位置
|
// GetEnd 获取该线段的结束位置
|
||||||
func (slf Line[V]) GetEnd() Point[V] {
|
func (slf LineSegment[V]) GetEnd() Point[V] {
|
||||||
return slf[1]
|
return slf[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLength 获取该线段的长度
|
// GetLength 获取该线段的长度
|
||||||
func (slf Line[V]) GetLength() V {
|
func (slf LineSegment[V]) GetLength() V {
|
||||||
return CalcDistance(DoublePointToCoordinate(slf.GetStart(), slf.GetEnd()))
|
return CalcDistance(DoublePointToCoordinate(slf.GetStart(), slf.GetEnd()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PointOnLineWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
// PointOnLineSegmentWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||||
func PointOnLineWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool {
|
func PointOnLineSegmentWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool {
|
||||||
return (x-x1)*(y2-y1) == (x2-x1)*(y-y1)
|
return (x-x1)*(y2-y1) == (x2-x1)*(y-y1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PointOnLineWithPos 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
// PointOnLineSegmentWithPos 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||||
func PointOnLineWithPos[V generic.SignedNumber](width, pos1, pos2, pos V) bool {
|
func PointOnLineSegmentWithPos[V generic.SignedNumber](width, pos1, pos2, pos V) bool {
|
||||||
x1, y1 := PosToCoordinate(width, pos1)
|
x1, y1 := PosToCoordinate(width, pos1)
|
||||||
x2, y2 := PosToCoordinate(width, pos2)
|
x2, y2 := PosToCoordinate(width, pos2)
|
||||||
x, y := PosToCoordinate(width, pos)
|
x, y := PosToCoordinate(width, pos)
|
||||||
return PointOnLineWithCoordinate(x1, y1, x2, y2, x, y)
|
return PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PointOnLineWithPoint 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
// PointOnLineSegmentWithPoint 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||||
func PointOnLineWithPoint[V generic.SignedNumber](point1, point2, point Point[V]) bool {
|
func PointOnLineSegmentWithPoint[V generic.SignedNumber](point1, point2, point Point[V]) bool {
|
||||||
x1, y1 := point1.GetXY()
|
x1, y1 := point1.GetXY()
|
||||||
x2, y2 := point2.GetXY()
|
x2, y2 := point2.GetXY()
|
||||||
x, y := point.GetXY()
|
x, y := point.GetXY()
|
||||||
return PointOnLineWithCoordinate(x1, y1, x2, y2, x, y)
|
return PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PointOnSegmentWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
// PointOnLineSegmentWithCoordinateInBounds 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||||
// - 与 PointOnLineWithCoordinate 不同的是, PointOnSegmentWithCoordinate 中会判断线段及点的位置是否正确
|
// - 与 PointOnLineSegmentWithCoordinate 不同的是, PointOnLineSegmentWithCoordinateInBounds 中会判断线段及点的位置是否正确
|
||||||
func PointOnSegmentWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool {
|
func PointOnLineSegmentWithCoordinateInBounds[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)
|
return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineSegmentWithCoordinate(x1, y1, x2, y2, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PointOnSegmentWithPos 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
// PointOnLineSegmentWithPosInBounds 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||||
// - 与 PointOnLineWithPos 不同的是, PointOnSegmentWithPos 中会判断线段及点的位置是否正确
|
// - 与 PointOnLineSegmentWithPos 不同的是, PointOnLineSegmentWithPosInBounds 中会判断线段及点的位置是否正确
|
||||||
func PointOnSegmentWithPos[V generic.SignedNumber](width, pos1, pos2, pos V) bool {
|
func PointOnLineSegmentWithPosInBounds[V generic.SignedNumber](width, pos1, pos2, pos V) bool {
|
||||||
x1, y1 := PosToCoordinate(width, pos1)
|
x1, y1 := PosToCoordinate(width, pos1)
|
||||||
x2, y2 := PosToCoordinate(width, pos2)
|
x2, y2 := PosToCoordinate(width, pos2)
|
||||||
x, y := PosToCoordinate(width, pos)
|
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 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
// PointOnLineSegmentWithPointInBounds 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||||
// - 与 PointOnLineWithPoint 不同的是, PointOnSegmentWithPoint 中会判断线段及点的位置是否正确
|
// - 与 PointOnLineSegmentWithPoint 不同的是, PointOnLineSegmentWithPointInBounds 中会判断线段及点的位置是否正确
|
||||||
func PointOnSegmentWithPoint[V generic.SignedNumber](point1, point2, point Point[V]) bool {
|
func PointOnLineSegmentWithPointInBounds[V generic.SignedNumber](point1, point2, point Point[V]) bool {
|
||||||
x1, y1 := point1.GetXY()
|
x1, y1 := point1.GetXY()
|
||||||
x2, y2 := point2.GetXY()
|
x2, y2 := point2.GetXY()
|
||||||
x, y := point.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())
|
area1 := CalcTriangleTwiceArea(line1.GetStart(), line1.GetEnd(), line2.GetStart())
|
||||||
area2 := CalcTriangleTwiceArea(line1.GetStart(), line1.GetEnd(), line2.GetEnd())
|
area2 := CalcTriangleTwiceArea(line1.GetStart(), line1.GetEnd(), line2.GetEnd())
|
||||||
return maths.Tolerance(area1, 0, tolerance) && maths.Tolerance(area2, 0, tolerance)
|
return maths.Tolerance(area1, 0, tolerance) && maths.Tolerance(area2, 0, tolerance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalcLineIsOverlap 通过对点进行排序来检查两条共线线段是否重叠,返回重叠线段
|
// CalcLineSegmentIsOverlap 通过对点进行排序来检查两条共线线段是否重叠,返回重叠线段
|
||||||
func CalcLineIsOverlap[V generic.SignedNumber](line1, line2 Line[V]) (line Line[V], overlap bool) {
|
func CalcLineSegmentIsOverlap[V generic.SignedNumber](line1, line2 LineSegment[V]) (line LineSegment[V], overlap bool) {
|
||||||
l1ps, l1pe := NewPointCapWithPoint(line1.GetStart(), true), NewPointCapWithPoint(line1.GetEnd(), true)
|
l1ps, l1pe := NewPointCapWithPoint(line1.GetStart(), true), NewPointCapWithPoint(line1.GetEnd(), true)
|
||||||
l2ps, l2pe := NewPointCapWithPoint(line2.GetStart(), false), NewPointCapWithPoint(line2.GetEnd(), false)
|
l2ps, l2pe := NewPointCapWithPoint(line2.GetStart(), false), NewPointCapWithPoint(line2.GetEnd(), false)
|
||||||
var shapes = [][]PointCap[V, bool]{
|
var shapes = [][]PointCap[V, bool]{
|
||||||
|
@ -136,5 +136,5 @@ func CalcLineIsOverlap[V generic.SignedNumber](line1, line2 Line[V]) (line Line[
|
||||||
if notOverlap || singlePointOverlap {
|
if notOverlap || singlePointOverlap {
|
||||||
return line, false
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,7 +185,7 @@ func (slf *NavMesh[V]) FindPath(start, end geometry.Point[V]) (result []geometry
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var portal geometry.Line[V]
|
var portal geometry.LineSegment[V]
|
||||||
var find bool
|
var find bool
|
||||||
for i := 0; i < len(current.links); i++ {
|
for i := 0; i < len(current.links); i++ {
|
||||||
if current.links[i].id == next.id {
|
if current.links[i].id == next.id {
|
||||||
|
@ -230,11 +230,11 @@ func (slf *NavMesh[V]) generateLink() {
|
||||||
|
|
||||||
for _, shapeEdge := range shapeEdges {
|
for _, shapeEdge := range shapeEdges {
|
||||||
for _, targetEdge := range targetShapePkg.Edges() {
|
for _, targetEdge := range targetShapePkg.Edges() {
|
||||||
if !geometry.CalcLineIsCollinear(shapeEdge, targetEdge, V(maths.GetDefaultTolerance())) {
|
if !geometry.CalcLineSegmentIsCollinear(shapeEdge, targetEdge, V(maths.GetDefaultTolerance())) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var overlapLine, overlap = geometry.CalcLineIsOverlap(shapeEdge, targetEdge)
|
var overlapLine, overlap = geometry.CalcLineSegmentIsOverlap(shapeEdge, targetEdge)
|
||||||
if !overlap {
|
if !overlap {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -248,9 +248,9 @@ func (slf *NavMesh[V]) generateLink() {
|
||||||
a3 := geometry.CalcAngleDifference(edgeAngle, a1)
|
a3 := geometry.CalcAngleDifference(edgeAngle, a1)
|
||||||
a4 := geometry.CalcAngleDifference(edgeAngle, a2)
|
a4 := geometry.CalcAngleDifference(edgeAngle, a2)
|
||||||
if a3 < a4 {
|
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 {
|
} 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()))
|
edgeAngle = geometry.CalcAngle(geometry.DoublePointToCoordinate(targetShapeCentroid, targetEdge.GetStart()))
|
||||||
|
@ -259,9 +259,9 @@ func (slf *NavMesh[V]) generateLink() {
|
||||||
a3 = geometry.CalcAngleDifference(edgeAngle, a1)
|
a3 = geometry.CalcAngleDifference(edgeAngle, a1)
|
||||||
a4 = geometry.CalcAngleDifference(edgeAngle, a2)
|
a4 = geometry.CalcAngleDifference(edgeAngle, a2)
|
||||||
if a3 < a4 {
|
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 {
|
} else {
|
||||||
targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLine(overlapLine.GetEnd(), overlapLine.GetStart()))
|
targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLineSegment(overlapLine.GetEnd(), overlapLine.GetStart()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,16 +19,16 @@ type shape[V generic.SignedNumber] struct {
|
||||||
id int
|
id int
|
||||||
geometry.Shape[V]
|
geometry.Shape[V]
|
||||||
links []*shape[V]
|
links []*shape[V]
|
||||||
portals []geometry.Line[V]
|
portals []geometry.LineSegment[V]
|
||||||
boundingRadius V
|
boundingRadius V
|
||||||
centroid geometry.Point[V]
|
centroid geometry.Point[V]
|
||||||
edges []geometry.Line[V]
|
edges []geometry.LineSegment[V]
|
||||||
|
|
||||||
weight V
|
weight V
|
||||||
x, y V
|
x, y V
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *shape[V]) Edges() []geometry.Line[V] {
|
func (slf *shape[V]) Edges() []geometry.LineSegment[V] {
|
||||||
return slf.edges
|
return slf.edges
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,7 @@ func DoublePointToCoordinate[V generic.SignedNumber](point1, point2 Point[V]) (x
|
||||||
|
|
||||||
// CalcProjectionPoint 计算一个点到一条线段的最近点(即投影点)的。这个函数接收一个点和一条线段作为输入,线段由两个端点组成。
|
// 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())
|
ax, ay, bx, by := DoublePointToCoordinate(line.GetStart(), line.GetEnd())
|
||||||
ds := CalcDistanceSquared(ax, ay, bx, by)
|
ds := CalcDistanceSquared(ax, ay, bx, by)
|
||||||
px, py := point.GetXY()
|
px, py := point.GetXY()
|
||||||
|
|
|
@ -363,22 +363,22 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
|
||||||
|
|
||||||
// Edges 获取该形状每一条边
|
// Edges 获取该形状每一条边
|
||||||
// - 该形状需要最少由3个点组成,否则将不会返回任意一边
|
// - 该形状需要最少由3个点组成,否则将不会返回任意一边
|
||||||
func (slf Shape[V]) Edges() (edges []Line[V]) {
|
func (slf Shape[V]) Edges() (edges []LineSegment[V]) {
|
||||||
if len(slf) < 3 {
|
if len(slf) < 3 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i := 1; i < slf.PointCount(); i++ {
|
for i := 1; i < slf.PointCount(); i++ {
|
||||||
before := slf[i-1]
|
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
|
return edges
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPointOnEdge 检查点是否在该形状的一条边上
|
// IsPointOnEdge 检查点是否在该形状的一条边上
|
||||||
func (slf Shape[V]) IsPointOnEdge(point Point[V]) bool {
|
func (slf Shape[V]) IsPointOnEdge(point Point[V]) bool {
|
||||||
for _, edge := range slf.Edges() {
|
for _, edge := range slf.Edges() {
|
||||||
if PointOnSegmentWithPoint(edge.GetStart(), edge.GetEnd(), point) {
|
if PointOnLineSegmentWithPointInBounds(edge.GetStart(), edge.GetEnd(), point) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,9 +442,9 @@ func CalcTriangleTwiceArea[V generic.SignedNumber](a, b, c Point[V]) V {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPointOnEdge 检查点是否在 edges 的任意一条边上
|
// 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 {
|
for _, edge := range edges {
|
||||||
if PointOnSegmentWithPoint(edge.GetStart(), edge.GetEnd(), point) {
|
if PointOnLineSegmentWithPointInBounds(edge.GetStart(), edge.GetEnd(), point) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue