-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
contour.go
91 lines (83 loc) · 2.09 KB
/
contour.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Copyright ©2016-2023 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.
package poly
import (
"strings"
"github.com/richardwilkes/toolbox/xmath"
"github.com/richardwilkes/toolbox/xmath/geom"
"golang.org/x/exp/constraints"
)
// Contour is a sequence of vertices connected by line segments, forming a closed shape.
type Contour[T constraints.Float] []geom.Point[T]
// Clone returns a copy of this contour.
func (c Contour[T]) Clone() Contour[T] {
if len(c) == 0 {
return nil
}
clone := make(Contour[T], len(c))
copy(clone, c)
return clone
}
// Bounds returns the bounding rectangle of the contour.
func (c Contour[T]) Bounds() geom.Rect[T] {
if len(c) == 0 {
return geom.Rect[T]{}
}
minX := xmath.MaxValue[T]()
minY := minX
maxX := xmath.MinValue[T]()
maxY := maxX
for _, p := range c {
if p.X > maxX {
maxX = p.X
}
if p.X < minX {
minX = p.X
}
if p.Y > maxY {
maxY = p.Y
}
if p.Y < minY {
minY = p.Y
}
}
return geom.NewRect(minX, minY, 1+maxX-minX, 1+maxY-minY)
}
// Contains returns true if the point is contained by the contour.
func (c Contour[T]) Contains(pt geom.Point[T]) bool {
var count int
for i := range c {
cur := c[i]
bottom := cur
next := c[(i+1)%len(c)]
top := next
if bottom.Y > top.Y {
bottom, top = top, bottom
}
if pt.Y >= bottom.Y && pt.Y < top.Y && pt.X < max(cur.X, next.X) && next.Y != cur.Y &&
(cur.X == next.X || pt.X <= (pt.Y-cur.Y)*(next.X-cur.X)/(next.Y-cur.Y)+cur.X) {
count++
}
}
return count%2 == 1
}
func (c Contour[T]) String() string {
var buffer strings.Builder
buffer.WriteByte('{')
for j, pt := range c {
if j != 0 {
buffer.WriteByte(',')
}
buffer.WriteByte('{')
buffer.WriteString(pt.String())
buffer.WriteByte('}')
}
buffer.WriteByte('}')
return buffer.String()
}