23 | 23 |
invalid: Rect
|
24 | 24 |
|
25 | 25 |
Comp* = ref object
|
26 | |
size*: Size
|
27 | |
blit*: Blit
|
|
26 |
size: Size
|
28 | 27 |
tb*: illwill.TerminalBuffer
|
29 | 28 |
layers: seq[Layer]
|
30 | 29 |
srclayer: RectBuf[int]
|
|
45 | 44 |
p0.x += p1.x
|
46 | 45 |
p0.y += p1.y
|
47 | 46 |
|
|
47 |
## RECTS
|
|
48 |
|
|
49 |
proc width*(r: Rect): int =
|
|
50 |
if r.hi.x < r.lo.x:
|
|
51 |
return 0
|
|
52 |
return r.hi.x - r.lo.x + 1
|
|
53 |
|
|
54 |
proc height*(r: Rect): int =
|
|
55 |
if r.hi.y < r.lo.y:
|
|
56 |
return 0
|
|
57 |
return r.hi.y - r.lo.y + 1
|
|
58 |
|
|
59 |
proc isempty*(r: Rect): bool = r.width() <= 0 or r.height() <= 0
|
|
60 |
|
|
61 |
proc width*[T](b: RectBuf[T]): int = b.stride
|
|
62 |
|
|
63 |
proc height*[T](b: RectBuf[T]): int =
|
|
64 |
int(b.c.len() / int(b.stride))
|
|
65 |
|
|
66 |
proc size*[T](b: RectBuf[T]): Size =
|
|
67 |
Size(w: b.width(), h: b.height())
|
|
68 |
|
|
69 |
proc size*(c: Comp): Size = c.size
|
|
70 |
|
|
71 |
proc contains*(r: Rect, p: Pt): bool =
|
|
72 |
r.lo.x <= p.x and p.x <= r.hi.x and
|
|
73 |
r.lo.y <= p.y and p.y <= r.hi.y
|
|
74 |
|
|
75 |
proc contains*[T](b: RectBuf[T], p: Pt): bool =
|
|
76 |
0 <= p.x and p.x < b.width() and
|
|
77 |
0 <= p.y and p.y < b.height()
|
|
78 |
|
|
79 |
proc contains*(l: Layer, p: Pt): bool =
|
|
80 |
l.dst.contains(p) and l.blit.contains(p - l.src)
|
|
81 |
|
|
82 |
proc `+=`*(r0: var Rect, r1: Rect) =
|
|
83 |
if r1.isempty():
|
|
84 |
return
|
|
85 |
if r0.isempty():
|
|
86 |
r0.lo = r1.lo
|
|
87 |
r0.hi = r1.hi
|
|
88 |
else:
|
|
89 |
r0.lo = Pt(x: min(r0.lo.x, r1.lo.x), y: min(r0.lo.y, r1.lo.y))
|
|
90 |
r0.hi = Pt(x: max(r0.hi.x, r1.hi.x), y: max(r0.hi.y, r1.hi.y))
|
|
91 |
|
48 | 92 |
## BLIT INDEXING
|
49 | 93 |
|
50 | 94 |
proc index(b: RectBuf, p: Pt): int =
|
|
57 | 101 |
proc set*[T](b: RectBuf[T], p: Pt, c: T): void =
|
58 | 102 |
b.c[b.index(p)] = c
|
59 | 103 |
|
60 | |
proc get*[T](b: RectBuf[T], p: Pt): T = b.c[b.index(p)]
|
|
104 |
proc get*[T](b: RectBuf[T], p: Pt): T =
|
|
105 |
b.c[b.index(p)]
|
61 | 106 |
|
62 | 107 |
proc get*(l: Layer, dst: Pt): Char =
|
63 | 108 |
var src = Pt(
|
64 | 109 |
x: l.src.x + (dst.x - l.dst.lo.x),
|
65 | 110 |
y: l.src.y + (dst.y - l.dst.lo.y))
|
66 | |
return l.blit.get(src)
|
67 | |
|
68 | |
proc get*(b: Comp, p: Pt): Char =
|
69 | |
return b.blit.get(p)
|
|
111 |
if l.blit.contains(src):
|
|
112 |
return l.blit.get(src)
|
|
113 |
return NO_CHAR
|
|
114 |
|
|
115 |
proc get*(c: Comp, p: Pt): Char =
|
|
116 |
let layerindex = c.srclayer.get(p)
|
|
117 |
if layerindex == NO_LAYER:
|
|
118 |
return NO_CHAR
|
|
119 |
return c.layers[layerindex].get(p)
|
70 | 120 |
|
71 | 121 |
proc resize*[T](b: var RectBuf[T], size: Size) =
|
72 | 122 |
let s = size.w * size.h
|
73 | 123 |
b.c = newSeq[T](s)
|
74 | 124 |
b.stride = size.w
|
75 | 125 |
|
76 | |
## RECTS
|
77 | |
|
78 | |
proc width*(r: Rect): int =
|
79 | |
if r.hi.x < r.lo.x:
|
80 | |
return 0
|
81 | |
return r.hi.x - r.lo.x + 1
|
82 | |
|
83 | |
proc height*(r: Rect): int =
|
84 | |
if r.hi.y < r.lo.y:
|
85 | |
return 0
|
86 | |
return r.hi.y - r.lo.y + 1
|
87 | |
|
88 | |
proc isempty*(r: Rect): bool = r.width() > 0 and r.height() > 0
|
89 | |
|
90 | |
proc width*[T](b: RectBuf[T]): int = b.stride
|
91 | |
|
92 | |
proc height*[T](b: RectBuf[T]): int =
|
93 | |
int(b.c.len() / int(b.stride))
|
94 | |
|
95 | |
proc size*[T](b: RectBuf[T]): Size =
|
96 | |
Size(w: b.width(), h: b.height())
|
97 | |
|
98 | |
proc contains*(r: Rect, p: Pt): bool =
|
99 | |
r.lo.x <= p.x and p.x <= r.hi.x and
|
100 | |
r.lo.y <= p.y and p.y <= r.hi.y
|
101 | |
|
102 | |
proc contains*[T](b: RectBuf[T], p: Pt): bool =
|
103 | |
0 <= p.x and p.x < b.width() and
|
104 | |
0 <= p.y and p.y < b.height()
|
105 | |
|
106 | |
proc contains*(l: Layer, p: Pt): bool =
|
107 | |
l.dst.contains(p) and l.blit.contains(p - l.src)
|
108 | |
|
109 | |
proc `|`*(r0, r1: Rect): Rect =
|
110 | |
if r0.isempty():
|
111 | |
r1
|
112 | |
elif r1.isempty():
|
113 | |
r0
|
114 | |
else:
|
115 | |
Rect(lo: Pt(x: min(r0.lo.x, r1.lo.x), y: min(r0.lo.y, r1.lo.y)),
|
116 | |
hi: Pt(x: max(r0.hi.x, r1.hi.x), y: max(r0.hi.y, r1.hi.y)))
|
117 | |
|
118 | 126 |
## CONSTRUCTORS
|
119 | 127 |
|
120 | 128 |
proc newPt*(x, y: int): Pt =
|
|
123 | 131 |
proc emptyRect(): Rect =
|
124 | 132 |
Rect(lo: newPt(0, 0), hi: newPt(-1, -1))
|
125 | 133 |
|
126 | |
proc rectFromSize(s: Size, lo: Pt = Pt(x: 0, y: 0)): Rect =
|
|
134 |
proc rectFromSize*(s: Size, lo: Pt = Pt(x: 0, y: 0)): Rect =
|
127 | 135 |
Rect(lo: lo, hi: Pt(x: lo.x + s.w - 1, y: lo.y + s.h - 1))
|
128 | 136 |
|
129 | 137 |
proc newRectBuf[T](size: Size): RectBuf[T] =
|
|
144 | 152 |
|
145 | 153 |
proc newComp*(size: Size): Comp =
|
146 | 154 |
Comp(size: size,
|
147 | |
blit: newBlit(size),
|
148 | 155 |
tb: newTerminalBuffer(size.w, size.h),
|
149 | 156 |
layers: newSeq[Layer](),
|
150 | 157 |
fullrepaint: true,
|
|
155 | 162 |
iterator pointsinside(rects: seq[Rect]): Pt =
|
156 | 163 |
var domain = emptyRect()
|
157 | 164 |
for r in rects:
|
158 | |
domain = domain | r
|
159 | |
for x in domain.lo.x..domain.hi.x:
|
160 | |
for y in domain.lo.y..domain.hi.y:
|
|
165 |
domain += r
|
|
166 |
for y in domain.lo.y..domain.hi.y:
|
|
167 |
for x in domain.lo.x..domain.hi.x:
|
161 | 168 |
let p = newPt(x, y)
|
162 | 169 |
for r in rects:
|
163 | 170 |
if r.contains(p):
|
164 | 171 |
yield p
|
165 | 172 |
break
|
166 | 173 |
|
167 | |
proc layout(c: var Comp) =
|
168 | |
c.srclayer.resize(c.size)
|
169 | |
c.srclayer.fill(NO_LAYER)
|
170 | |
# build srclayer cache
|
171 | |
for l in 0..(len(c.layers) - 1):
|
172 | |
let layer = c.layers[l]
|
173 | |
var full = true
|
174 | |
for i in 0..(c.size.w - 1):
|
175 | |
for j in 0..(c.size.h - 1):
|
176 | |
let d = Pt(x: i, y: j)
|
177 | |
if c.srclayer.get(d) != NO_LAYER:
|
178 | |
continue
|
179 | |
full = false
|
180 | |
if layer.dst.contains(d):
|
181 | |
c.srclayer.set(d, l)
|
182 | |
if full:
|
183 | |
break
|
184 | |
c.fullrepaint = true
|
185 | |
|
186 | |
proc repaint(c: var Comp, invalid: seq[Rect]) =
|
187 | |
echo "repaint:", repr(invalid)
|
|
174 |
proc paint*(c: Comp) =
|
188 | 175 |
c.tb.clear()
|
189 | |
for point in pointsinside(invalid):
|
190 | |
let layerindex = c.srclayer.get(point)
|
191 | |
if layerindex == NO_LAYER:
|
192 | |
continue
|
193 | |
let layer = c.layers[layerindex]
|
194 | |
|
195 | |
# the layer's dst rect determines where it's in control, but
|
196 | |
# it's possible for a pixel in the layer's rect to not have any
|
197 | |
# data in the layer's source blit; this containment query
|
198 | |
# handles that case.
|
199 | |
if layer.contains(point):
|
200 | |
let ch = layer.get(point)
|
201 | |
c.tb.write(point.x, point.y, ch[0].toUTF8)
|
202 | |
|
203 | |
proc paint*(c: var Comp) =
|
204 | |
var invalid = newSeq[Rect]()
|
205 | |
if c.fullrepaint:
|
206 | |
invalid.add(rectFromSize(c.size))
|
207 | |
else:
|
208 | |
for l in c.layers:
|
209 | |
if not l.invalid.isempty():
|
210 | |
invalid.add(l.invalid)
|
211 | |
l.invalid = emptyRect()
|
212 | |
c.fullrepaint = false
|
213 | |
|
214 | |
if invalid.len() > 0:
|
215 | |
c.repaint(invalid)
|
|
176 |
for layer in c.layers:
|
|
177 |
for point in pointsinside(@[layer.dst]):
|
|
178 |
c.tb.write(point.x, point.y, layer.get(point)[0].toUTF8)
|
216 | 179 |
c.tb.display()
|
217 | 180 |
|
218 | 181 |
## LAYER MANAGEMENT
|
219 | 182 |
|
220 | 183 |
proc pushlayer*(c: var Comp, l: Layer) =
|
221 | |
c.layers.insert(l, 0)
|
222 | |
c.layout()
|
|
184 |
c.layers.add(l)
|
223 | 185 |
|
224 | 186 |
proc poplayer*(c: var Comp) =
|
225 | |
c.layers.delete(0)
|
226 | |
c.layout()
|
|
187 |
c.layers.delete(c.layers.len - 1)
|
227 | 188 |
|
228 | 189 |
proc `size=`*(c: var Comp, s: Size) =
|
229 | |
if c.size != s:
|
230 | |
c.tb = newTerminalBuffer(s.w, s.h)
|
231 | |
c.size = s
|
232 | |
c.layout()
|
|
190 |
c.tb = newTerminalBuffer(s.w, s.h)
|
|
191 |
c.size = s
|
233 | 192 |
|
234 | 193 |
proc translate*(l: var Layer, amt: Pt) =
|
235 | 194 |
l.src += amt
|
236 | |
# scrolling invalidates the entire dest rect
|
237 | |
l.invalid = l.dst
|
238 | 195 |
|
239 | 196 |
proc `dst=`*(l: var Layer, r: Rect) =
|
240 | |
l.invalid = l.dst | r
|
|
197 |
l.invalid += l.dst
|
|
198 |
l.invalid += r
|
241 | 199 |
l.dst = r
|
242 | 200 |
|
243 | 201 |
## DEBUG
|
|
258 | 216 |
var c = b.srclayer.get(newPt(i, j))
|
259 | 217 |
stdout.write(if c == NO_LAYER: 'X' else: char(int('1') + c))
|
260 | 218 |
stdout.write('\n')
|
|
219 |
|
|
220 |
proc printdebug*(c: Comp) =
|
|
221 |
echo c.layers[0].src, " -> ", c.layers[0].dst
|