-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathline.go
117 lines (93 loc) · 3.7 KB
/
line.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
package triangolatte
import "errors"
// Joint is a type of connection happening when lines joining two points meet.
type Joint int
const (
// Normal triangulates with no joint correction.
Normal Joint = 0
// Miter triangulates producing miter joints, i.e. extending the lines until
// they meet at some point.
Miter Joint = 1
)
// Line takes array of points and triangulates them to resemble a line of given
// width. Returns array of two-coordinate CCW triangles one after another.
func Line(joint Joint, points []Point, width float64) ([]float64, error) {
switch joint {
case Normal:
return normal(points, width), nil
case Miter:
return miter(points, width), nil
default:
return nil, errors.New("Unrecognized joint type")
}
}
func normal(points []Point, width float64) []float64 {
width /= 2.0
triangles := make([]float64, 0, len(points)*12)
for i := 0; i <= len(points)-2; i++ {
dx := points[i+1].X - points[i].X
dy := points[i+1].Y - points[i].Y
n1 := Point{dy, -dx}.Scale(width)
n2 := Point{-dy, dx}.Scale(width)
v0, v1 := points[i+1].Add(n2).X, points[i+1].Add(n2).Y
v2, v3 := points[i].Add(n2).X, points[i].Add(n2).Y
v4, v5 := points[i].Add(n1).X, points[i].Add(n1).Y
v6, v7 := points[i].Add(n1).X, points[i].Add(n1).Y
v8, v9 := points[i+1].Add(n1).X, points[i+1].Add(n1).Y
v10, v11 := points[i+1].Add(n2).X, points[i+1].Add(n2).Y
triangles = append(triangles, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
}
return triangles
}
func calculateNormals(x, y float64) [2]Point {
return [2]Point{
Point{y, -x}.Normalize(),
Point{-y, x}.Normalize(),
}
}
func miter(points []Point, width float64) []float64 {
width /= 2.0
triangles := make([]float64, 0, len(points)*12)
var dx, dy float64
var miter1, miter2 Point
var n1, n2 [2]Point
dx = points[1].X - points[0].X
dy = points[1].Y - points[0].Y
n2 = calculateNormals(dx, dy)
miter2 = n2[0].Scale(width)
for i := 1; i < len(points)-1; i++ {
// Shift calculated values.
n1 = n2
miter1 = miter2
dx = points[i+1].X - points[i].X
dy = points[i+1].Y - points[i].Y
n2 = calculateNormals(dx, dy)
// Find tangent vector to both lines in the middle point.
tangent := (points[i+1].Sub(points[i])).Normalize().Add((points[i].Sub(points[i-1])).Normalize()).Normalize()
// Miter vector is perpendicular to the tangent and crosses extensions of
// normal-translated lines in miter join points.
unitMiter := Point{-tangent.Y, tangent.X}
// Length of the miter vector projected onto one of the normals.
// Choice of normal is arbitrary, each of them would work.
miterLength := float64(width) / unitMiter.Dot(n1[0])
miter2 = unitMiter.Scale(miterLength)
v0, v1 := points[i].Sub(miter2).X, points[i].Sub(miter2).Y
v2, v3 := points[i-1].Sub(miter1).X, points[i-1].Sub(miter1).Y
v4, v5 := points[i-1].Add(miter1).X, points[i-1].Add(miter1).Y
v6, v7 := points[i-1].Add(miter1).X, points[i-1].Add(miter1).Y
v8, v9 := points[i].Add(miter2).X, points[i].Add(miter2).Y
v10, v11 := points[i].Sub(miter2).X, points[i].Sub(miter2).Y
triangles = append(triangles, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
}
// Use last normal as another 'neutral element' for miter join.
n := len(points)
lastMiter := n2[0].Scale(width)
v0, v1 := points[n-1].Sub(lastMiter).X, points[n-1].Sub(lastMiter).Y
v2, v3 := points[n-2].Sub(miter1).X, points[n-2].Sub(miter1).Y
v4, v5 := points[n-2].Add(miter1).X, points[n-2].Add(miter1).Y
v6, v7 := points[n-2].Add(miter1).X, points[n-2].Add(miter1).Y
v8, v9 := points[n-1].Add(lastMiter).X, points[n-1].Add(lastMiter).Y
v10, v11 := points[n-1].Sub(lastMiter).X, points[n-1].Sub(lastMiter).Y
triangles = append(triangles, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
return triangles
}