支持更多的计算函数

This commit is contained in:
kercylan98 2023-06-19 14:15:08 +08:00
parent 261b14e789
commit caa1512e1c
5 changed files with 165 additions and 2 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}