-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathturtle.go
123 lines (109 loc) · 2.35 KB
/
turtle.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
/*
A little fun with Turtle graphics!
Copyright 2017 Ahmet Inan <inan@aicodix.de>
*/
package main
import (
"os"
"fmt"
"image"
"image/png"
"image/color"
"strings"
)
func die(err interface{}) {
fmt.Println(err)
os.Exit(1)
}
func rot90(p image.Point) image.Point {
return image.Point{-p.Y, p.X}
}
func rot270(p image.Point) image.Point {
return image.Point{p.Y, -p.X}
}
func draw(input string) []image.Point {
position := image.Point{0, 0}
delta := image.Point{1, 0}
var path []image.Point
path = append(path, position)
for _, c := range input {
switch c {
case 'F': // draw forward
position = position.Add(delta)
path = append(path, position)
case '-': // turn left 90°
delta = rot90(delta)
case '+': // turn right 90°
delta = rot270(delta)
}
}
return path
}
func bounds(path []image.Point) image.Rectangle {
var rect image.Rectangle
for _, pos := range path {
if pos.X < rect.Min.X { rect.Min.X = pos.X }
if pos.Y < rect.Min.Y { rect.Min.Y = pos.Y }
if pos.X > rect.Max.X { rect.Max.X = pos.X }
if pos.Y > rect.Max.Y { rect.Max.Y = pos.Y }
}
return rect
}
func line(img *image.NRGBA, a, b image.Point) {
if a.X == b.X {
if a.Y > b.Y { a, b = b, a }
for y := a.Y; y <= b.Y; y++ {
img.Set(a.X, y, color.Black)
}
} else if a.Y == b.Y {
if a.X > b.X { a, b = b, a }
for x := a.X; x <= b.X; x++ {
img.Set(x, a.Y, color.Black)
}
} else {
die("can only do horizontal and vertical lines")
}
}
func main() {
scale := 10
margin := 10
/*
// Dragon curve
rules := strings.NewReplacer(
"X", "X+YF+",
"Y", "-FX-Y")
axiom := "FX"
level := 12
// Koch curve
rules := strings.NewReplacer(
"F", "F+F-F-F+F")
axiom := "F"
level := 4
*/
// Hilbert curve
rules := strings.NewReplacer(
"A", "-BF+AFA+FB-",
"B", "+AF-BFB-FA+")
axiom := "A"
level := 5
tmp := axiom
for i := 0; i < level; i++ {
tmp = rules.Replace(tmp)
}
// fmt.Println(tmp)
path := draw(tmp)
rect := bounds(path)
rect.Min = rect.Min.Mul(scale).Sub(image.Point{margin, margin})
rect.Max = rect.Max.Mul(scale).Add(image.Point{margin, margin})
img := image.NewNRGBA(rect)
prev := path[0].Mul(scale)
for _, pos := range path[1:] {
scaled := pos.Mul(scale)
line(img, scaled, prev)
prev = scaled
}
name := "output.png"
file, err := os.Create(name)
if err != nil { die(err) }
if err := png.Encode(file, img); err != nil { die(err) }
}