✨ 支持更多的计算函数
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
|
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 (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"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 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
// PointOnLineWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
|
||||||
func PointOnLineWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2, x, y V) bool {
|
func PointOnLineWithCoordinate[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)
|
||||||
|
@ -48,3 +81,38 @@ func PointOnSegmentWithCoordinateArray[V generic.SignedNumber](point1, point2, p
|
||||||
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 && 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
|
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 (
|
var (
|
||||||
ShapeStringHasBorder = false // 控制 Shape.String 是否拥有边界
|
shapeStringHasBorder = false // 控制 Shape.String 是否拥有边界
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SetShapeStringHasBorder 设置 Shape.String 是拥有边界的
|
||||||
|
func SetShapeStringHasBorder() {
|
||||||
|
shapeStringHasBorder = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetShapeStringNotHasBorder 设置 Shape.String 是没有边界的
|
||||||
|
func SetShapeStringNotHasBorder() {
|
||||||
|
shapeStringHasBorder = false
|
||||||
|
}
|
||||||
|
|
||||||
// NewShape 通过多个点生成一个形状进行返回
|
// NewShape 通过多个点生成一个形状进行返回
|
||||||
func NewShape[V generic.SignedNumber](points ...Point[V]) Shape[V] {
|
func NewShape[V generic.SignedNumber](points ...Point[V]) Shape[V] {
|
||||||
return points
|
return points
|
||||||
|
@ -63,7 +73,7 @@ func (slf Shape[V]) String() string {
|
||||||
left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
|
left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
|
||||||
width := right - left + 1
|
width := right - left + 1
|
||||||
height := bottom - top + 1
|
height := bottom - top + 1
|
||||||
if !ShapeStringHasBorder {
|
if !shapeStringHasBorder {
|
||||||
for y := top; y < top+height; y++ {
|
for y := top; y < top+height; y++ {
|
||||||
for x := left; x < left+width; x++ {
|
for x := left; x < left+width; x++ {
|
||||||
exist := false
|
exist := false
|
||||||
|
@ -334,6 +344,20 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
|
||||||
return 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 通过升序的方式获取该形状中包含的所有图形组合及其位置
|
// getAllGraphicCompositionWithAsc 通过升序的方式获取该形状中包含的所有图形组合及其位置
|
||||||
// - 升序指标为图形包含的点数量
|
// - 升序指标为图形包含的点数量
|
||||||
// - 其余内容可参考 getAllGraphicComposition
|
// - 其余内容可参考 getAllGraphicComposition
|
||||||
|
@ -355,3 +379,37 @@ func (slf Shape[V]) getAllGraphicCompositionWithDesc(opt *shapeSearchOptions) (r
|
||||||
})
|
})
|
||||||
return
|
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 (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultTolerance = 0.0001 // 默认误差范围
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetDefaultTolerance 获取默认误差范围
|
||||||
|
func GetDefaultTolerance() float64 {
|
||||||
|
return DefaultTolerance
|
||||||
|
}
|
||||||
|
|
||||||
// Pow 整数幂运算
|
// Pow 整数幂运算
|
||||||
func Pow(a, n int) int {
|
func Pow(a, n int) int {
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
|
@ -94,3 +104,8 @@ func Clamp[V generic.Number](value, min, max V) V {
|
||||||
}
|
}
|
||||||
return value
|
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