gamnit: support partial line forward and backward
[nit.git] / lib / gamnit / tileset.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Support for `TileSet`, `TileSetFont` and drawing text with `TextSprites`
16 module tileset
17
18 import flat
19
20 # Efficiently retrieve tiles in a big texture
21 class TileSet
22 # Texture containing the tileset
23 var texture: Texture
24
25 # Width of a tile
26 var width: Numeric
27
28 # Height of a tile
29 var height: Numeric
30
31 # Number of columns of tiles in the texture
32 var nb_cols: Int = (texture.width / width.to_f).to_i is lazy
33
34 # Number of rows of tiles in the texture
35 var nb_rows: Int = (texture.height / height.to_f).to_i is lazy
36
37 # Cache of the subtextures of tiles
38 var subtextures: Array[Texture] is lazy do
39 var subtextures = new Array[Texture]
40 for j in [0..nb_rows[ do
41 for i in [0..nb_cols[ do
42 subtextures.add texture.subtexture(
43 i.to_f*width.to_f, j.to_f*height.to_f, width.to_f, height.to_f)
44 end
45 end
46 return subtextures
47 end
48
49 # Subtexture for the tile at `x, y`
50 #
51 # Require: `x < nb_cols and y < nb_rows`
52 fun [](x,y: Int): Texture
53 do
54 assert x >= 0 and x < nb_cols and y >= 0 and y <= nb_rows else print "{x}x{y}<?{nb_cols}x{nb_rows}"
55 var idx = x + y * nb_cols
56 return subtextures[idx]
57 end
58 end
59
60 # A monospace bitmap font where glyphs are stored in a tileset
61 class TileSetFont
62 super TileSet
63
64 # Set the characters present in `texture`
65 #
66 # Last all characters from left to right, then top to bottom.
67 # Line skip `\n`, are ignored and space ' ' skips holes in the tileset.
68 fun chars=(chars: Text)
69 is autoinit do
70 chars_cleaned = chars.replace("\n", "")
71 end
72
73 # Character present in the texture, set by `chars=`
74 private var chars_cleaned: Text is noautoinit
75
76 # Additional space to insert horizontally between characters
77 #
78 # A negative value may display overlapped tiles.
79 var hspace: Numeric = 0.0 is writable
80
81 # Additional space to insert vertically between characters
82 #
83 # A negative value may display overlapped tiles.
84 var vspace: Numeric = 0.0 is writable
85
86 # Line spacing modifier for `pld` and `plu`
87 #
88 # This value acts as multiplier to `height + vspace`.
89 # Defaults to 0.4, so a `pld` moves chars down by about half a line.
90 var partial_line_mod: Numeric = 0.4 is writable
91
92 # The glyph/tile/texture associated to `char`
93 #
94 # Returns null if `char` is not in `chars`.
95 fun char(char: Char): nullable Texture
96 do
97 var i = chars_cleaned.index_of(char)
98 if i == -1 then return null
99 return subtextures[i]
100 end
101
102 # Distance between the beginning of a letter tile and the beginning of the next letter tile
103 fun advance: Float do return width.to_f + hspace.to_f
104
105 # Distance between the beginning and the end of the longest line of `text`
106 fun text_width(text: Text): Numeric
107 do
108 var lines = text.split('\n')
109 if lines.is_empty then return 0
110
111 var longest = 0
112 for line in lines do longest = longest.max(line.length)
113
114 return longest.mul(advance)
115 end
116
117 # Distance between the top of the first line to the bottom of the last line in `text`
118 fun text_height(text: Text): Numeric
119 do
120 if text.is_empty then return 0
121
122 var n_lines = text.chars.count('\n')
123 return (n_lines+1).mul(height.add(vspace)).sub(vspace)
124 end
125 end
126
127 # Manage a set of sprites to display some text
128 class TextSprites
129
130 # Font used to draw text
131 var font: TileSetFont
132
133 # Top left of the first character in UI coordinates
134 var anchor: Point3d[Float]
135
136 # Last set of sprites generated to display `text=`
137 var sprites = new Array[Sprite]
138
139 # Sprite set where to put created sprites
140 #
141 # Defaults to `app::ui_sprites`, but it could also be set to a
142 # `app::sprites` or a custom collection.
143 var target_sprite_set: Set[Sprite] = app.ui_sprites is lazy, writable
144
145 private var cached_text: nullable Text = ""
146
147 # Last text drawn
148 fun text: nullable Text do return cached_text
149
150 # Update the text displayed by inserting new sprites into `app.ui_sprites`
151 #
152 # Does not redraw if `text` has not changed.
153 fun text=(text: nullable Text)
154 is autoinit do
155 # Don't redraw if text hasn't changed
156 if text == cached_text then return
157 cached_text = text
158
159 # Clean up last used sprites
160 for s in sprites do if target_sprite_set.has(s) then target_sprite_set.remove s
161 sprites.clear
162
163 if text == null then return
164
165 # Build new sprites
166 var dx = font.advance/2.0
167 var dy = font.hspace.to_f/2.0
168 for c in text do
169 if c == '\n' then
170 dy -= font.height.to_f + font.vspace.to_f
171 dx = font.advance/2.0
172 continue
173 else if c == pld then
174 dy -= (font.height.to_f + font.vspace.to_f) * font.partial_line_mod.to_f
175 continue
176 else if c == plu then
177 dy += (font.height.to_f + font.vspace.to_f) * font.partial_line_mod.to_f
178 continue
179 else if c.is_whitespace then
180 dx += font.advance
181 continue
182 end
183
184 var tex = font.char(c)
185 if tex == null then
186 # Try to fallback to '?'
187 tex = font.char('?')
188 if tex == null then continue
189 end
190
191 sprites.add new Sprite(tex, anchor.offset(dx, dy, 0))
192 dx += font.advance
193 end
194
195 # Register sprites to be drawn by `app.ui_camera`
196 target_sprite_set.add_all sprites
197 end
198 end
199
200 # Partial line forward (U+008B)
201 fun pld: Char do return '\8b'
202
203 # Partial line backward (U+008C)
204 fun plu: Char do return '\8c'