✨ 支持更多的计算函数
This commit is contained in:
parent
261b14e789
commit
caa1512e1c
|
@ -139,3 +139,14 @@ func CalculateNewCoordinate[V generic.SignedNumber](x, y, angle, distance V) (ne
|
|||
|
||||
return newX, newY
|
||||
}
|
||||
|
||||
// CalcAngleDifference 计算两个角度之间的最小角度差
|
||||
func CalcAngleDifference[V generic.Number](angleA, angleB V) V {
|
||||
pi := math.Pi
|
||||
t := angleA - angleB
|
||||
a := t + V(pi)
|
||||
b := V(pi) * 2
|
||||
t = V(math.Floor(float64(a/b))) * b
|
||||
t -= pi
|
||||
return t
|
||||
}
|
||||
|
|
|
@ -2,8 +2,41 @@ package geometry
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/maths"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// NewLine 创建一根线段
|
||||
func NewLine[V generic.SignedNumber](start, end Point[V]) Line[V] {
|
||||
if start.Equal(end) {
|
||||
panic("two points of the line segment are the same")
|
||||
}
|
||||
return Line[V]{start, end}
|
||||
}
|
||||
|
||||
// Line 通过两个点表示一根线段
|
||||
type Line[V generic.SignedNumber] [2]Point[V]
|
||||
|
||||
// GetPoints 获取该线段的两个点
|
||||
func (slf Line[V]) GetPoints() [2]Point[V] {
|
||||
return slf
|
||||
}
|
||||
|
||||
// GetStart 获取该线段的开始位置
|
||||
func (slf Line[V]) GetStart() Point[V] {
|
||||
return slf[0]
|
||||
}
|
||||
|
||||
// GetEnd 获取该线段的结束位置
|
||||
func (slf Line[V]) GetEnd() Point[V] {
|
||||
return slf[1]
|
||||
}
|
||||
|
||||
// GetLength 获取该线段的长度
|
||||
func (slf Line[V]) GetLength() V {
|
||||
return CalcDistance(DoublePointToCoordinate(slf.GetStart(), slf.GetEnd()))
|
||||
}
|
||||
|
||||
// PointOnLineWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||
func PointOnLineWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool {
|
||||
return (x-x1)*(y2-y1) == (x2-x1)*(y-y1)
|
||||
|
@ -48,3 +81,38 @@ func PointOnSegmentWithCoordinateArray[V generic.SignedNumber](point1, point2, p
|
|||
x, y := point.GetXY()
|
||||
return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLineWithCoordinate(x1, y1, x2, y2, x, y)
|
||||
}
|
||||
|
||||
// CalcLineIsCollinear 检查两条线段在一个误差内是否共线
|
||||
// - 共线是指两条线段在同一直线上,即它们的延长线可以重合
|
||||
func CalcLineIsCollinear[V generic.SignedNumber](line1, line2 Line[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) {
|
||||
var shapes = []Shape[V]{
|
||||
{line1.GetStart(), line1.GetEnd(), line1.GetStart()},
|
||||
{line1.GetStart(), line1.GetEnd(), line1.GetEnd()},
|
||||
{line2.GetStart(), line2.GetEnd(), line2.GetStart()},
|
||||
{line2.GetStart(), line2.GetEnd(), line2.GetEnd()},
|
||||
}
|
||||
sort.Slice(shapes, func(i, j int) bool {
|
||||
a, b := shapes[i], shapes[j]
|
||||
if a[2].GetX() < b[2].GetX() {
|
||||
return true
|
||||
} else if a[2].GetX() > b[2].GetX() {
|
||||
return false
|
||||
} else {
|
||||
return a[2].GetY() < b[2].GetY()
|
||||
}
|
||||
})
|
||||
|
||||
notOverlap := shapes[0][0].Equal(shapes[1][0]) && shapes[0][1].Equal(shapes[1][1])
|
||||
singlePointOverlap := shapes[1][2].Equal(shapes[2][2])
|
||||
if notOverlap || singlePointOverlap {
|
||||
return line, false
|
||||
}
|
||||
return NewLine(shapes[1][2], shapes[2][2]), true
|
||||
}
|
||||
|
|
|
@ -322,3 +322,14 @@ func GetRectangleFullPos[V generic.SignedNumber](width, height V) (result []V) {
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CalcRectangleCentroid 计算矩形质心
|
||||
// - 非多边形质心计算,仅为顶点的平均值 - 该区域中多边形因子的适当质心
|
||||
func CalcRectangleCentroid[V generic.SignedNumber](shape Shape[V]) Point[V] {
|
||||
x, y := V(0), V(0)
|
||||
length := V(shape.PointCount())
|
||||
|
||||
x /= length
|
||||
y /= length
|
||||
return NewPoint(x, y)
|
||||
}
|
||||
|
|
|
@ -11,9 +11,19 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ShapeStringHasBorder = false // 控制 Shape.String 是否拥有边界
|
||||
shapeStringHasBorder = false // 控制 Shape.String 是否拥有边界
|
||||
)
|
||||
|
||||
// SetShapeStringHasBorder 设置 Shape.String 是拥有边界的
|
||||
func SetShapeStringHasBorder() {
|
||||
shapeStringHasBorder = true
|
||||
}
|
||||
|
||||
// SetShapeStringNotHasBorder 设置 Shape.String 是没有边界的
|
||||
func SetShapeStringNotHasBorder() {
|
||||
shapeStringHasBorder = false
|
||||
}
|
||||
|
||||
// NewShape 通过多个点生成一个形状进行返回
|
||||
func NewShape[V generic.SignedNumber](points ...Point[V]) Shape[V] {
|
||||
return points
|
||||
|
@ -63,7 +73,7 @@ func (slf Shape[V]) String() string {
|
|||
left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
|
||||
width := right - left + 1
|
||||
height := bottom - top + 1
|
||||
if !ShapeStringHasBorder {
|
||||
if !shapeStringHasBorder {
|
||||
for y := top; y < top+height; y++ {
|
||||
for x := left; x < left+width; x++ {
|
||||
exist := false
|
||||
|
@ -334,6 +344,20 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
|
|||
return result
|
||||
}
|
||||
|
||||
// Edges 获取该形状每一条边
|
||||
// - 该形状需要最少由3个点组成,否则将不会返回任意一边
|
||||
func (slf Shape[V]) Edges() (edges []Line[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, NewLine(slf[0], slf[len(slf)-1]))
|
||||
return edges
|
||||
}
|
||||
|
||||
// getAllGraphicCompositionWithAsc 通过升序的方式获取该形状中包含的所有图形组合及其位置
|
||||
// - 升序指标为图形包含的点数量
|
||||
// - 其余内容可参考 getAllGraphicComposition
|
||||
|
@ -355,3 +379,37 @@ func (slf Shape[V]) getAllGraphicCompositionWithDesc(opt *shapeSearchOptions) (r
|
|||
})
|
||||
return
|
||||
}
|
||||
|
||||
// CalcBoundingRadius 计算多边形转换为圆的半径
|
||||
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))
|
||||
if distance > boundingRadius {
|
||||
boundingRadius = distance
|
||||
}
|
||||
}
|
||||
return boundingRadius
|
||||
}
|
||||
|
||||
// CalcBoundingRadiusWithCentroid 计算多边形在特定质心下圆的半径
|
||||
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))
|
||||
if distance > boundingRadius {
|
||||
boundingRadius = distance
|
||||
}
|
||||
}
|
||||
return boundingRadius
|
||||
}
|
||||
|
||||
// CalcTriangleTwiceArea 计算由 a、b、c 三个点组成的三角形的面积的两倍
|
||||
func CalcTriangleTwiceArea[V generic.SignedNumber](a, b, c Point[V]) V {
|
||||
ax := b.GetX() - a.GetX()
|
||||
ay := b.GetY() - a.GetY()
|
||||
bx := c.GetX() - a.GetX()
|
||||
by := c.GetY() - a.GetY()
|
||||
return bx*ay - ax*by
|
||||
}
|
||||
|
|
|
@ -2,8 +2,18 @@ package maths
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTolerance = 0.0001 // 默认误差范围
|
||||
)
|
||||
|
||||
// GetDefaultTolerance 获取默认误差范围
|
||||
func GetDefaultTolerance() float64 {
|
||||
return DefaultTolerance
|
||||
}
|
||||
|
||||
// Pow 整数幂运算
|
||||
func Pow(a, n int) int {
|
||||
if a == 0 {
|
||||
|
@ -94,3 +104,8 @@ func Clamp[V generic.Number](value, min, max V) V {
|
|||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Tolerance 检查两个值是否在一个误差范围内
|
||||
func Tolerance[V generic.Number](value1, value2, tolerance V) bool {
|
||||
return V(math.Abs(float64(value1-value2))) <= tolerance
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue