-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpointproc.go
130 lines (118 loc) · 3.68 KB
/
pointproc.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package graphics2d
import (
"math"
"math/rand"
"github.com/jphsd/graphics2d/util"
)
// PointRot specifies the type of shape rotation
type PointRot int
const (
// RotFixed no rotation
RotFixed PointRot = iota
// RotRelative rotation relative to the tangent of the path step
RotRelative
// RotRandom rotation is randomized
RotRandom
)
// PointsProc contains a slice of shapes, one of which will be placed at the start of each step in the
// path and at the path end, if not closed. If any shape is nil, then it is skipped. The rotation flag
// indicates if the shapes should be rotated relative to the path's tangent at that point.
type PointsProc struct {
Points []*Shape
Rotate PointRot
}
// NewPointsProc creates a new points path processor with the supplied shapes and rotation flag.
func NewPointsProc(shapes []*Shape, rot PointRot) *PointsProc {
return &PointsProc{shapes, rot}
}
// Process implements the PathProcessor interface.
func (pp *PointsProc) Process(p *Path) []*Path {
parts := p.Parts()
n := len(parts)
if !p.Closed() {
n++
}
res := make([]*Path, 0, n)
ns := len(pp.Points)
cp := 0
for _, part := range parts {
if partLen(part) < 0.0001 {
// Skip 0 length parts which cause tangent issues
continue
}
if pp.Points[cp] != nil {
var xfm *Aff3
switch pp.Rotate {
default:
fallthrough
case RotFixed:
xfm = CreateTransform(part[0][0], part[0][1], 1, 0)
case RotRelative:
t0 := util.DeCasteljau(part, 0)
ang := math.Atan2(t0[3], t0[2])
xfm = CreateTransform(t0[0], t0[1], 1, ang)
case RotRandom:
t0 := util.DeCasteljau(part, 0)
xfm = CreateTransform(t0[0], t0[1], 1, rand.Float64()*TwoPi)
}
res = append(res, pp.Points[cp].Transform(xfm).Paths()...)
}
if cp++; cp == ns {
cp = 0
}
}
// Only apply end shape to open paths
if pp.Points[cp] != nil && !p.Closed() {
var xfm *Aff3
part := parts[len(parts)-1]
switch pp.Rotate {
case RotFixed:
xfm = CreateTransform(part[0][0], part[0][1], 1, 0)
case RotRelative:
t0 := util.DeCasteljau(part, 1)
xfm = CreateTransform(t0[0], t0[1], 1, math.Atan2(t0[3], t0[2]))
case RotRandom:
t0 := util.DeCasteljau(part, 1)
xfm = CreateTransform(t0[0], t0[1], 1, rand.Float64()*TwoPi)
}
res = append(res, pp.Points[cp].Transform(xfm).Paths()...)
}
return res
}
// ShapesProc contains a slice of shapes, which will be placed sequentially along the path,
// starting at the beginning and spaced there after by the spacing value, and at the path end,
// if not closed. If any shape is nil, then it is skipped. The rotation flag indicates if the
// shapes should be rotated relative to the path's tangent at that point.
type ShapesProc struct {
Comp *CompoundProc
Shapes *PointsProc
}
// NewShapesProc creates a new shapes path processor with the supplied shapes, spacing and rotation flag.
func NewShapesProc(shapes []*Shape, spacing float64, rot PointRot) *ShapesProc {
pattern := []float64{spacing, spacing}
spaces := NewSnipProc(2, pattern, 0)
n := len(shapes)
d := int(math.Floor(spacing + 0.5))
nn := d * n
nshapes := make([]*Shape, nn)
for i := 0; i < n; i++ {
nshapes[i*d] = shapes[i]
// remaining d-1 slots are left nil
}
// Assumption - this is taking place in image space so pixel level sampling should
// be sufficient.
comp := NewCompoundProc(NewMunchProc(1), spaces)
comp.Concatenate = true
return &ShapesProc{comp, NewPointsProc(nshapes, rot)}
}
// Process implements the PathProcessor interface.
func (sp *ShapesProc) Process(p *Path) []*Path {
path := p.Process(sp.Comp)[0]
return path.Process(sp.Shapes)
}
func partLen(part [][]float64) float64 {
p1 := part[0]
p2 := part[len(part)-1]
dx, dy := p2[0]-p1[0], p2[1]-p1[1]
return math.Hypot(dx, dy)
}