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