gamnit: add customization settings to `TextSprites`
[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 font
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 #
62 # This `Font` does not implement all settings of `TextSprites`.
63 class TileSetFont
64 super TileSet
65 super Font
66
67 # Set the characters present in `texture`
68 #
69 # Last all characters from left to right, then top to bottom.
70 # Line skip `\n`, are ignored and space ' ' skips holes in the tileset.
71 fun chars=(chars: Text)
72 is autoinit do
73 chars_cleaned = chars.replace("\n", "")
74 end
75
76 # Character present in the texture, set by `chars=`
77 private var chars_cleaned: Text is noautoinit
78
79 # Additional space to insert horizontally between characters
80 #
81 # A negative value may display overlapped tiles.
82 var hspace: Numeric = 0.0 is writable
83
84 # Additional space to insert vertically between characters
85 #
86 # A negative value may display overlapped tiles.
87 var vspace: Numeric = 0.0 is writable
88
89 # The glyph/tile/texture associated to `char`
90 #
91 # Returns null if `char` is not in `chars`.
92 fun char(char: Char): nullable Texture
93 do
94 var i = chars_cleaned.index_of(char)
95 if i == -1 then return null
96 return subtextures[i]
97 end
98
99 # Distance between the beginning of a letter tile and the beginning of the next letter tile
100 fun advance: Float do return width.to_f + hspace.to_f
101
102 # Distance between the beginning and the end of the longest line of `text`
103 fun text_width(text: Text): Numeric
104 do
105 var lines = text.split('\n')
106 if lines.is_empty then return 0
107
108 var longest = 0
109 for line in lines do longest = longest.max(line.length)
110
111 return longest.mul(advance)
112 end
113
114 # Distance between the top of the first line to the bottom of the last line in `text`
115 fun text_height(text: Text): Numeric
116 do
117 if text.is_empty then return 0
118
119 var n_lines = text.chars.count('\n')
120 return (n_lines+1).mul(height.add(vspace)).sub(vspace)
121 end
122
123 redef fun write_into(text_sprites, text)
124 do
125 # Build new sprites
126 var dx = advance/2.0
127 var dy = hspace.to_f/2.0
128 for c in text do
129 if c == '\n' then
130 dy -= height.to_f + vspace.to_f
131 dx = advance/2.0
132 continue
133 else if c == pld then
134 dy -= (height.to_f + vspace.to_f) * partial_line_mod.to_f
135 continue
136 else if c == plu then
137 dy += (height.to_f + vspace.to_f) * partial_line_mod.to_f
138 continue
139 else if c.is_whitespace then
140 dx += advance
141 continue
142 end
143
144 var tex = char(c)
145 if tex == null then
146 # Try to fallback to '?'
147 tex = char('?')
148 if tex == null then continue
149 end
150
151 text_sprites.sprites.add new Sprite(tex, text_sprites.anchor.offset(dx, dy, 0))
152 dx += advance
153 end
154 end
155 end