-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsplit.go
More file actions
111 lines (100 loc) · 2.59 KB
/
split.go
File metadata and controls
111 lines (100 loc) · 2.59 KB
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package curve
import (
"iter"
"math"
"honnef.co/go/stuff/container/maybe"
)
// SplitArclen subdivides each subpath into segments of arc length l. Each group
// of segments is delimited by a zero value path segment.
func SplitArclen(inner iter.Seq[PathSegment], l float64) iter.Seq[PathSegment] {
return splitArclenMaxGroups(inner, l, -1)
}
// SplitN subdivides each subpath into n segments of identical arc length. Each
// group of segments is delimited by a zero value path segment.
//
// inner must not be a single-use iterator.
func SplitN(inner iter.Seq[PathSegment], n int) iter.Seq[PathSegment] {
if n <= 1 {
return inner
}
const splitAccuracy = 1e-6
totalLength := 0.0
for seg := range inner {
totalLength += seg.Arclen(splitAccuracy)
}
splitLength := totalLength / float64(n)
if math.IsInf(splitLength, 0) || math.IsNaN(splitLength) {
return inner
}
return splitArclenMaxGroups(inner, splitLength, n)
}
func splitArclenMaxGroups(inner iter.Seq[PathSegment], l float64, n int) iter.Seq[PathSegment] {
// TODO(dh): should we support splitting the remainder across the start and
// end of the curve? Currently any remainder is at the end.
if l == 0 {
return inner
}
const splitAccuracy = 1e-6
splitLength := l
if math.IsInf(splitLength, 0) || math.IsNaN(splitLength) {
return inner
}
// We cannot use SplitArcLength(inner, splitLength) because due to rounding
// errors we might end up with n+1 groups.
return func(yield func(PathSegment) bool) {
remainingLength := splitLength
remainingSegs := n
var prevEnd maybe.Option[Point]
var out []PathSegment
for seg := range inner {
if v, ok := prevEnd.Get(); ok && seg.P0 != v {
// New subpath
remainingLength = splitLength
remainingSegs = n
if !yield(PathSegment{}) {
return
}
}
switch seg.Kind {
case LineKind:
prevEnd = maybe.Some(seg.P1)
case QuadKind:
prevEnd = maybe.Some(seg.P2)
case CubicKind:
prevEnd = maybe.Some(seg.P3)
}
for {
if a := seg.Arclen(splitAccuracy); a < remainingLength {
remainingLength -= a
if !yield(seg) {
return
}
break
} else {
var t float64
if remainingSegs == 1 {
t = 1
} else {
t = SolveForArclen(seg, remainingLength, splitAccuracy)
}
out = append(out, seg.Subsegment(0, t))
if !yield(seg.Subsegment(0, t)) {
return
}
if remainingSegs > 0 {
remainingSegs--
}
if !yield(PathSegment{}) {
return
}
remainingLength = splitLength
if t >= 1 {
break
} else {
seg = seg.Subsegment(t, 1)
}
}
}
}
}
}