Skip to content

Commit e8f6ac1

Browse files
committed
Swap out the poly and visibility packages for float32 variants
1 parent e7dde1a commit e8f6ac1

20 files changed

+681
-977
lines changed

geom/poly/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Warning!
2+
3+
The polygon boolean operation code doesn't function as expected in all cases. It does work for most
4+
cases, but I recommend against using it in production code at the moment.

geom/poly/contour.go

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,14 @@
1010
package poly
1111

1212
import (
13+
"math"
1314
"strings"
1415

1516
"github.com/richardwilkes/toolbox/v2/geom"
1617
)
1718

1819
// Contour is a sequence of vertices connected by line segments, forming a closed shape.
19-
type Contour []Point
20-
21-
// Points converts this Contour into a slice of geom.Point.
22-
func (c Contour) Points() []geom.Point {
23-
points := make([]geom.Point, len(c))
24-
for i, p := range c {
25-
points[i] = p.Point()
26-
}
27-
return points
28-
}
20+
type Contour []geom.Point
2921

3022
// Clone returns a copy of this contour.
3123
func (c Contour) Clone() Contour {
@@ -38,15 +30,15 @@ func (c Contour) Clone() Contour {
3830
}
3931

4032
// Bounds returns the bounding rectangle of the contour.
41-
func (c Contour) Bounds() Rect {
33+
func (c Contour) Bounds() geom.Rect {
4234
if len(c) == 0 {
43-
return Rect{}
35+
return geom.Rect{}
4436
}
45-
minX := c[0].X
46-
minY := c[0].Y
47-
maxX := minX
48-
maxY := minY
49-
for _, p := range c[1:] {
37+
minX := float32(math.MaxFloat32)
38+
minY := minX
39+
maxX := float32(-math.MaxFloat32)
40+
maxY := maxX
41+
for _, p := range c {
5042
if p.X > maxX {
5143
maxX = p.X
5244
}
@@ -60,14 +52,11 @@ func (c Contour) Bounds() Rect {
6052
minY = p.Y
6153
}
6254
}
63-
return NewRect(minX, minY, maxX-minX, maxY-minY)
55+
return geom.NewRect(minX, minY, 1+maxX-minX, 1+maxY-minY)
6456
}
6557

6658
// Contains returns true if the point is contained by the contour.
67-
func (c Contour) Contains(pt Point) bool {
68-
if len(c) < 3 {
69-
return false // A contour needs at least 3 points to contain anything
70-
}
59+
func (c Contour) Contains(pt geom.Point) bool {
7160
var count int
7261
for i := range c {
7362
cur := c[i]
@@ -77,12 +66,8 @@ func (c Contour) Contains(pt Point) bool {
7766
if bottom.Y > top.Y {
7867
bottom, top = top, bottom
7968
}
80-
if pt.Y >= bottom.Y &&
81-
pt.Y < top.Y &&
82-
pt.X < max(cur.X, next.X) &&
83-
next.Y != cur.Y &&
84-
(cur.X == next.X ||
85-
pt.X <= (pt.Y-cur.Y).Mul(next.X-cur.X).Div(next.Y-cur.Y)+cur.X) {
69+
if pt.Y >= bottom.Y && pt.Y < top.Y && pt.X < max(cur.X, next.X) && next.Y != cur.Y &&
70+
(cur.X == next.X || pt.X <= (pt.Y-cur.Y)*(next.X-cur.X)/(next.Y-cur.Y)+cur.X) {
8671
count++
8772
}
8873
}

geom/poly/edge_node.go

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99

1010
package poly
1111

12+
import (
13+
"math"
14+
15+
"github.com/richardwilkes/toolbox/v2/geom"
16+
"github.com/richardwilkes/toolbox/v2/xmath"
17+
)
18+
19+
const epsilon = 0.00001
20+
1221
type horizontalEdgeStates int
1322

1423
const (
@@ -42,12 +51,12 @@ type edgeNode struct {
4251
aboveState bundleState
4352
belowState bundleState
4453
which int
45-
vertex Point
46-
bot Point
47-
top Point
48-
xb Num
49-
xt Num
50-
dx Num
54+
vertex geom.Point
55+
bot geom.Point
56+
top geom.Point
57+
xb float32
58+
xt float32
59+
dx float32
5160
bundleAbove [2]bool
5261
bundleBelow [2]bool
5362
subjectSide bool
@@ -57,9 +66,9 @@ type edgeNode struct {
5766
type sortedEdge struct {
5867
edge *edgeNode
5968
prev *sortedEdge
60-
xb Num
61-
xt Num
62-
dx Num
69+
xb float32
70+
xt float32
71+
dx float32
6372
}
6473

6574
func (e *edgeNode) insertInto(b **edgeNode) {
@@ -91,7 +100,7 @@ func (e *edgeNode) addEdgeToActiveEdgeTable(aet, prev *edgeNode) *edgeNode {
91100
return aet
92101
}
93102

94-
func (e *edgeNode) addLocalMin(p *polygonNode, pt Point) *polygonNode {
103+
func (e *edgeNode) addLocalMin(p *polygonNode, pt geom.Point) *polygonNode {
95104
v := &vertexNode{pt: pt}
96105
result := &polygonNode{
97106
left: v,
@@ -104,7 +113,7 @@ func (e *edgeNode) addLocalMin(p *polygonNode, pt Point) *polygonNode {
104113
return result
105114
}
106115

107-
func (e *edgeNode) buildIntersections(dy Num) *intersection {
116+
func (e *edgeNode) buildIntersections(dy float32) *intersection {
108117
var se *sortedEdge
109118
var it *intersection
110119
for edge := e; edge != nil; edge = edge.next {
@@ -115,7 +124,7 @@ func (e *edgeNode) buildIntersections(dy Num) *intersection {
115124
return it
116125
}
117126

118-
func (e *edgeNode) addToSortedEdgeTable(se **sortedEdge, it **intersection, dy Num) {
127+
func (e *edgeNode) addToSortedEdgeTable(se **sortedEdge, it **intersection, dy float32) {
119128
if *se == nil {
120129
*se = &sortedEdge{
121130
edge: e,
@@ -125,7 +134,7 @@ func (e *edgeNode) addToSortedEdgeTable(se **sortedEdge, it **intersection, dy N
125134
}
126135
} else {
127136
den := ((*se).xt - (*se).xb) - (e.xt - e.xb)
128-
if e.xt >= (*se).xt || e.dx == (*se).dx || den <= 0 {
137+
if e.xt >= (*se).xt || e.dx == (*se).dx || xmath.Abs(den) <= epsilon {
129138
*se = &sortedEdge{
130139
edge: e,
131140
xb: e.xb,
@@ -134,17 +143,17 @@ func (e *edgeNode) addToSortedEdgeTable(se **sortedEdge, it **intersection, dy N
134143
prev: *se,
135144
}
136145
} else {
137-
r := (e.xb - (*se).xb).Div(den)
138-
addIntersection(it, (*se).edge, e, Point{
139-
X: (*se).xb + r.Mul(((*se).xt - (*se).xb)),
140-
Y: r.Mul(dy),
146+
r := (e.xb - (*se).xb) / den
147+
addIntersection(it, (*se).edge, e, geom.Point{
148+
X: (*se).xb + r*((*se).xt-(*se).xb),
149+
Y: r * dy,
141150
})
142151
e.addToSortedEdgeTable(&(*se).prev, it, dy)
143152
}
144153
}
145154
}
146155

147-
func addIntersection(it **intersection, edge0, edge1 *edgeNode, pt Point) {
156+
func addIntersection(it **intersection, edge0, edge1 *edgeNode, pt geom.Point) {
148157
switch {
149158
case *it == nil:
150159
*it = &intersection{
@@ -164,7 +173,7 @@ func addIntersection(it **intersection, edge0, edge1 *edgeNode, pt Point) {
164173
}
165174
}
166175

167-
func (e *edgeNode) bundleFields(pt Point) {
176+
func (e *edgeNode) bundleFields(pt geom.Point) {
168177
updated := e
169178
e.bundleAbove[e.which] = e.top.Y != pt.Y
170179
e.bundleAbove[1-e.which] = false
@@ -174,7 +183,7 @@ func (e *edgeNode) bundleFields(pt Point) {
174183
nextEdge.bundleAbove[1-nextEdge.which] = false
175184
nextEdge.aboveState = unbundled
176185
if nextEdge.bundleAbove[nextEdge.which] {
177-
if updated.xb == nextEdge.xb && updated.dx == nextEdge.dx && updated.top.Y != pt.Y {
186+
if mostlyEqual(updated.xb, nextEdge.xb) && mostlyEqual(updated.dx, nextEdge.dx) && updated.top.Y != pt.Y {
178187
nextEdge.bundleAbove[nextEdge.which] = nextEdge.bundleAbove[nextEdge.which] != updated.bundleAbove[nextEdge.which]
179188
nextEdge.bundleAbove[1-nextEdge.which] = updated.bundleAbove[1-nextEdge.which]
180189
nextEdge.aboveState = bundleHead
@@ -187,7 +196,7 @@ func (e *edgeNode) bundleFields(pt Point) {
187196
}
188197
}
189198

190-
func (e *edgeNode) process(op clipOp, pt Point, inPoly *polygonNode) (bPt Point, outPoly *polygonNode) {
199+
func (e *edgeNode) process(op clipOp, pt geom.Point, inPoly *polygonNode) (bPt geom.Point, outPoly *polygonNode) {
191200
bPt = pt
192201
outPoly = inPoly
193202
var parityClipRight, paritySubjRight bool
@@ -196,7 +205,7 @@ func (e *edgeNode) process(op clipOp, pt Point, inPoly *polygonNode) (bPt Point,
196205
}
197206
var horiz [2]horizontalEdgeStates
198207
var cf *polygonNode
199-
px := Min
208+
px := float32(-math.MaxFloat32)
200209
for edge := e; edge != nil; edge = edge.next {
201210
clipExistsState, clipExists := edge.existsState(clipping)
202211
subjExistsState, subjExists := edge.existsState(subject)
@@ -365,7 +374,7 @@ func (e *edgeNode) process(op clipOp, pt Point, inPoly *polygonNode) (bPt Point,
365374
return
366375
}
367376

368-
func (e *edgeNode) deleteTerminatingEdges(pt Point, yt Num) *edgeNode {
377+
func (e *edgeNode) deleteTerminatingEdges(pt geom.Point, yt float32) *edgeNode {
369378
updated := e
370379
for edge := e; edge != nil; edge = edge.next {
371380
switch edge.top.Y {
@@ -390,13 +399,13 @@ func (e *edgeNode) deleteTerminatingEdges(pt Point, yt Num) *edgeNode {
390399
case yt:
391400
edge.xt = edge.top.X
392401
default:
393-
edge.xt = edge.bot.X + edge.dx.Mul(yt-edge.bot.Y)
402+
edge.xt = edge.bot.X + edge.dx*(yt-edge.bot.Y)
394403
}
395404
}
396405
return updated
397406
}
398407

399-
func (e *edgeNode) prepareForNextScanBeam(yt Num) *edgeNode {
408+
func (e *edgeNode) prepareForNextScanBeam(yt float32) *edgeNode {
400409
updated := e
401410
for edge := e; edge != nil; edge = edge.next {
402411
successorEdge := edge.successor
@@ -522,6 +531,10 @@ func (e *edgeNode) existsState(which int) (int, bool) {
522531
return state, e.bundleAbove[which] || e.bundleBelow[which]
523532
}
524533

534+
func mostlyEqual(a, b float32) bool {
535+
return xmath.Abs(a-b) <= epsilon
536+
}
537+
525538
func calcNextHState(existsState int, current horizontalEdgeStates, parityRight bool) horizontalEdgeStates {
526539
i := (existsState - 1) << 1
527540
if parityRight {

geom/poly/intersection.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99

1010
package poly
1111

12+
import (
13+
"github.com/richardwilkes/toolbox/v2/geom"
14+
)
15+
1216
type intersection struct {
1317
edge0 *edgeNode
1418
edge1 *edgeNode
1519
next *intersection
16-
point Point
20+
point geom.Point
1721
}
1822

19-
func (i *intersection) process(op clipOp, pt Point, outPoly *polygonNode) *polygonNode {
23+
func (i *intersection) process(op clipOp, pt geom.Point, outPoly *polygonNode) *polygonNode {
2024
e0 := i.edge0
2125
e1 := i.edge1
2226

0 commit comments

Comments
 (0)