mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 15:06:46 +08:00
1 parent
d07972346b
commit
70003486b8
37 files changed
+2386
-2509
No files matched your search
@@ -0,0 +1,129 @@
|
||||
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package geom
|
||||
|
||||
import "math"
|
||||
|
||||
// polygonPoint 允许同结构的后端顶点直接参与裁剪,避免整片坐标转换
|
||||
type polygonPoint interface {
|
||||
~struct{ X, Y float64 }
|
||||
}
|
||||
|
||||
// ClipPolygon 按非退化凸窗口逐边裁剪多边形,保留原顶点绕向,不修改输入
|
||||
// 入参: a 绘制顶点, b 已通过ConvexPolygon检查的裁剪顶点,均不含重复闭合点
|
||||
// 返回: []P 交集顶点,少于三个顶点时不构成区域, bool 是否满足数值精度
|
||||
func ClipPolygon[P polygonPoint](a, b []P) ([]P, bool) {
|
||||
for i, start := range b {
|
||||
origin, end := Point(start), Point(b[(i+1)%len(b)])
|
||||
dx, dy := end.X-origin.X, end.Y-origin.Y
|
||||
length := math.Hypot(dx, dy)
|
||||
if length == 0 {
|
||||
continue
|
||||
}
|
||||
dx, dy = dx/length, dy/length
|
||||
distance := func(p P) float64 { return convexDistance(origin, Point(p), dx, dy) }
|
||||
sign := 1.0
|
||||
for _, p := range b {
|
||||
if d := distance(p); d != 0 {
|
||||
sign = math.Copysign(1, d)
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(a) == 0 {
|
||||
break
|
||||
}
|
||||
var clipped []P
|
||||
appendPoint := func(p P) {
|
||||
if len(clipped) == 0 || p != clipped[len(clipped)-1] {
|
||||
clipped = append(clipped, p)
|
||||
}
|
||||
}
|
||||
previous := a[len(a)-1]
|
||||
previousDistance := sign * distance(previous)
|
||||
if !finite(previousDistance) {
|
||||
return nil, false
|
||||
}
|
||||
for _, p := range a {
|
||||
d := sign * distance(p)
|
||||
if !finite(d) {
|
||||
return nil, false
|
||||
}
|
||||
if (d >= 0) != (previousDistance >= 0) {
|
||||
scale := math.Max(math.Abs(d), math.Abs(previousDistance))
|
||||
fraction := (previousDistance / scale) / (previousDistance/scale - d/scale)
|
||||
intersection := Lerp(Point(previous), Point(p), fraction)
|
||||
if dx == 0 {
|
||||
intersection.X = origin.X
|
||||
}
|
||||
if dy == 0 {
|
||||
intersection.Y = origin.Y
|
||||
}
|
||||
appendPoint(P(intersection))
|
||||
}
|
||||
if d >= 0 {
|
||||
appendPoint(p)
|
||||
}
|
||||
previous, previousDistance = p, d
|
||||
}
|
||||
if len(clipped) > 1 && clipped[0] == clipped[len(clipped)-1] {
|
||||
clipped = clipped[:len(clipped)-1]
|
||||
}
|
||||
a = clipped
|
||||
}
|
||||
return a, true
|
||||
}
|
||||
|
||||
// ConvexPolygon 检查所有顶点位于各边的同一侧,不修改输入
|
||||
// 入参: points 不含重复闭合点的独立顶点
|
||||
// 返回: []P 原顶点,非有限距离或异侧顶点返回nil, bool 是否为非退化凸多边形
|
||||
func ConvexPolygon[P polygonPoint](points []P) ([]P, bool) {
|
||||
positive := false
|
||||
for i, start := range points {
|
||||
origin, end := Point(start), Point(points[(i+1)%len(points)])
|
||||
dx, dy := end.X-origin.X, end.Y-origin.Y
|
||||
length := math.Hypot(dx, dy)
|
||||
if !finite(length) {
|
||||
return nil, false
|
||||
}
|
||||
if length == 0 {
|
||||
continue
|
||||
}
|
||||
dx, dy = dx/length, dy/length
|
||||
sign := 0.0
|
||||
for _, p := range points {
|
||||
d := convexDistance(origin, Point(p), dx, dy)
|
||||
if !finite(d) || sign*d < 0 {
|
||||
return nil, false
|
||||
}
|
||||
if d != 0 {
|
||||
sign, positive = math.Copysign(1, d), true
|
||||
}
|
||||
}
|
||||
}
|
||||
return points, positive
|
||||
}
|
||||
|
||||
// convexDistance 按浮点乘法误差界识别共线点,不使用固定坐标容差
|
||||
// 入参: start 边起点, point 待测顶点, dx 横向单位分量, dy 纵向单位分量
|
||||
// 返回: float64 顶点到边的有向距离
|
||||
func convexDistance(start, point Point, dx, dy float64) float64 {
|
||||
x, y := point.X-start.X, point.Y-start.Y
|
||||
distance := math.FMA(dx, y, -dy*x)
|
||||
errorBound := (math.Abs(dx*y) + math.Abs(dy*x)) * 0x1p-49
|
||||
if finite(errorBound) && math.Abs(distance) <= errorBound {
|
||||
return 0
|
||||
}
|
||||
return distance
|
||||
}
|
||||
Reference in new issue
Block a user