-
Notifications
You must be signed in to change notification settings - Fork 6
/
grid.go
180 lines (159 loc) · 4.59 KB
/
grid.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package ganim8
import (
"bytes"
"fmt"
"image"
_ "image/png"
"log"
"regexp"
"strconv"
)
func assertPositiveInteger(value int, name string) {
if value < 1 {
log.Fatal(fmt.Sprintf("%s should be a positive number, was %d", name, value))
}
}
func assertSize(size, limit int, name string) {
if size > limit {
log.Fatal(fmt.Sprintf("%s should be <= %d, was %d", name, limit, size))
}
}
type frameCache map[string]map[int]map[int]*image.Rectangle
var _frames frameCache
var intervalMatcher regexp.Regexp
func init() {
_frames = make(map[string]map[int]map[int]*image.Rectangle)
intervalMatcher = *regexp.MustCompile("^([0-9]+)-([0-9]+)$")
}
// Grid represents a grid
type Grid struct {
frameWidth, frameHeight int
imageWidth, imageHeight int
left, top int
width, height int
border int
key string
}
// NewGrid returns a new grid with specified frame size, image size, and
// offsets (left, top).
//
// Grids have only one purpose: To build groups of quads of the same size as
// easily as possible.
//
// They need to know only 2 things: the size of each frame and the size of
// the image they will be applied to.
//
// Grids are just a convenient way of getting frames from a sprite.
// Frames are assumed to be distributed in rows and columns.
// Frame 1,1 is the one in the first row, first column.
func NewGrid(frameWidth, frameHeight, imageWidth, imageHeight int, args ...int) *Grid {
assertPositiveInteger(frameWidth, "frameWidth")
assertPositiveInteger(frameHeight, "frameHeight")
assertPositiveInteger(imageWidth, "imageWidth")
assertPositiveInteger(imageHeight, "imageHeight")
assertSize(frameWidth, imageWidth, "frameWidth")
assertSize(frameHeight, imageHeight, "frameHeight")
left, top, border := 0, 0, 0
switch len(args) {
case 3:
border = args[2]
fallthrough
case 2:
top = args[1]
fallthrough
case 1:
left = args[0]
}
g := &Grid{
frameWidth: frameWidth,
frameHeight: frameHeight,
imageWidth: imageWidth,
imageHeight: imageHeight,
left: left,
top: top,
width: imageWidth / frameWidth,
height: imageHeight / frameHeight,
border: border,
}
g.key = getGridKey(g.frameWidth, g.frameHeight, g.imageWidth,
g.imageHeight, g.left, g.top)
return g
}
func getGridKey(args ...int) string {
var b bytes.Buffer
s := ""
for _, a := range args {
b.Write([]byte(s))
b.Write([]byte(strconv.Itoa(a)))
s = "-"
}
return b.String()
}
func (g *Grid) createFrame(x, y int) *image.Rectangle {
fw, fh := g.frameWidth, g.frameHeight
x0, y0 := g.left+(x-1)*fw+x*g.border, g.top+(y-1)*fh+y*g.border
r := image.Rect(x0, y0, x0+fw, y0+fh)
return &r
}
func (g *Grid) getOrCreateFrame(x, y int) *image.Rectangle {
if x < 1 || x > g.width || y < 1 || y > g.height {
log.Fatal(fmt.Sprintf("There is no frame for x=%d, y=%d", x, y))
}
key := g.key
if _, ok := _frames[key]; !ok {
_frames[key] = map[int]map[int]*image.Rectangle{}
}
if _, ok := _frames[key][x]; !ok {
_frames[key][x] = map[int]*image.Rectangle{}
}
if _, ok := _frames[key][x][y]; !ok {
_frames[key][x][y] = g.createFrame(x, y)
}
return _frames[key][x][y]
}
// GetFrames accepts an arbitrary number of parameters.
// They can be either numbers or strings.
//
// Each two numbers are interpreted as quad coordinates in the
// format (column, row). This way, grid:getFrames(3,4) will return
// the frame in column 3, row 4 of the grid.
//
// There can be more than just two: grid:getFrames(1,1, 1,2, 1,3)
// will return the frames in {1,1}, {1,2} and {1,3} respectively.
func (g *Grid) GetFrames(args ...interface{}) []*image.Rectangle {
result := []*image.Rectangle{}
if len(args) == 0 {
for y := 1; y <= g.height; y++ {
for x := 1; x <= g.width; x++ {
result = append(result, g.getOrCreateFrame(x, y))
}
}
return result
}
for i := 0; i < len(args); i += 2 {
minx, maxx, stepx := parseInterval(args[i])
miny, maxy, stepy := parseInterval(args[i+1])
for y := miny; stepy > 0 && y <= maxy || stepy < 0 && y >= maxy; y += stepy {
for x := minx; stepx > 0 && x <= maxx || stepx < 0 && x >= maxx; x += stepx {
result = append(result, g.getOrCreateFrame(x, y))
}
}
}
return result
}
// Width returns the width of the grid
func (g *Grid) Width() int {
return g.width
}
// Height returns the height of the grid
func (g *Grid) Height() int {
return g.height
}
// Frames is a shorter name of GetFrames
func (g *Grid) Frames(args ...interface{}) []*image.Rectangle {
return g.GetFrames(args...)
}
// G is a shorter name of GetFrames
func (g *Grid) G(args ...interface{}) []*image.Rectangle {
return g.GetFrames(args...)
}