-
Notifications
You must be signed in to change notification settings - Fork 83
/
bezier.go
60 lines (49 loc) · 1.61 KB
/
bezier.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
package render
import (
"image"
"image/color"
"github.com/oakmound/oak/alg"
"github.com/oakmound/oak/alg/intgeom"
"github.com/oakmound/oak/shape"
)
// BezierLine converts a bezier into a line sprite.
func BezierLine(b shape.Bezier, c color.Color) *Sprite {
return BezierThickLine(b, c, 0)
}
// BezierThickLine draws a BezierLine wrapping each colored pixel in
// a square of width and height = thickness
func BezierThickLine(b shape.Bezier, c color.Color, thickness int) *Sprite {
low := 0.0
high := 1.0
pts := make([]intgeom.Point2, 2)
pts[0] = roundToIntPoint(b.Pos(low))
pts[1] = roundToIntPoint(b.Pos(high))
bezierDraw(b, &pts, low, high, pts[0], pts[1])
min := pts[0].LesserOf(pts...)
max := pts[0].GreaterOf(pts...)
rgba := image.NewRGBA(image.Rect(0, 0, 1+(max.X()-min.X()), 1+(max.Y()-min.Y())))
for _, p := range pts {
x := p.X() - min.X()
y := p.Y() - min.Y()
for i := x - thickness; i <= x+thickness; i++ {
for j := y - thickness; j <= y+thickness; j++ {
rgba.Set(i, j, c)
}
}
}
return NewSprite(float64(min.X()), float64(min.Y()), rgba)
}
func roundToIntPoint(x, y float64) intgeom.Point2 {
return intgeom.Point2{alg.RoundF64(x), alg.RoundF64(y)}
}
func bezierDraw(b shape.Bezier, pts *[]intgeom.Point2, low, high float64, lowPt, highPt intgeom.Point2) {
mid := (low + high) / 2
p := roundToIntPoint(b.Pos(mid))
// If we haven't yet added this point at this low or high value
// I.E. if the low and high values are sufficiently close together
if p != lowPt && p != highPt {
*pts = append(*pts, p)
bezierDraw(b, pts, low, mid, lowPt, p)
bezierDraw(b, pts, mid, high, p, highPt)
}
}