Improved code readability
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / renderer / raster / shapes / composite / textcanvas / TextCanvas.java
1 /*
2  * Sixth 3D engine. Author: Svjatoslav Agejenko.
3  * This project is released under Creative Commons Zero (CC0) license.
4  */
5 package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas;
6
7 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
8 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
9 import eu.svjatoslav.sixth.e3d.gui.TextPointer;
10 import eu.svjatoslav.sixth.e3d.math.Transform;
11 import eu.svjatoslav.sixth.e3d.math.TransformsStack;
12 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
13 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.TexturedRectangle;
14
15 import java.awt.*;
16 import java.io.BufferedReader;
17 import java.io.IOException;
18 import java.io.StringReader;
19
20 import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.BLACK;
21 import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.WHITE;
22
23 public class TextCanvas extends TexturedRectangle {
24
25     /**
26      * Font character width in world coordinates.
27      */
28     public static final int FONT_CHAR_WIDTH = 8;
29
30     /**
31      * Font character height in world coordinates.
32      */
33     public static final int FONT_CHAR_HEIGHT = 16;
34
35     /**
36       * Font character width in texture pixels.
37       */
38     public static final int FONT_CHAR_WIDTH_TEXTURE_PIXELS = 16;
39
40     /**
41      * Font character height in texture pixels.
42      */
43     public static final int FONT_CHAR_HEIGHT_TEXTURE_PIXELS = 32;
44
45
46     public static final Font FONT = CanvasCharacter.getFont((int) (FONT_CHAR_HEIGHT_TEXTURE_PIXELS / 1.066));
47     private static final String GROUP_TEXTURE = "texture";
48     private static final String GROUP_CHARACTERS = "characters";
49     private final TextPointer size;
50     private final TextPointer cursorLocation = new TextPointer();
51     CanvasCharacter[][] lines;
52     private RenderMode renderMode = null;
53     private Color backgroundColor = BLACK;
54     private Color foregroundColor = WHITE;
55
56     public TextCanvas(final Transform location, final String text,
57                       final Color foregroundColor, final Color backgroundColor) {
58         this(location, getTextDimensions(text), foregroundColor,
59                 backgroundColor);
60         setText(text);
61     }
62
63     public TextCanvas(final Transform location, final TextPointer dimensions,
64                       final Color foregroundColor, final Color backgroundColor) {
65         super(location);
66         getRelativityTracker().enableOrientationTracking();
67
68         size = dimensions;
69         final int columns = dimensions.column;
70         final int rows = dimensions.row;
71
72         this.backgroundColor = backgroundColor;
73         this.foregroundColor = foregroundColor;
74
75         // initialize underlying textured rectangle
76         initialize(
77                 columns * FONT_CHAR_WIDTH,
78                 rows * FONT_CHAR_HEIGHT,
79                 columns * FONT_CHAR_WIDTH_TEXTURE_PIXELS,
80                 rows * FONT_CHAR_HEIGHT_TEXTURE_PIXELS,
81                 0);
82
83         getTexture().primaryBitmap.fillColor(backgroundColor);
84         getTexture().resetResampledBitmapCache();
85
86         setGroupForUngrouped(GROUP_TEXTURE);
87
88         lines = new CanvasCharacter[rows][];
89         for (int row = 0; row < rows; row++) {
90             lines[row] = new CanvasCharacter[columns];
91
92             for (int column = 0; column < columns; column++) {
93                 final Point3D characterCoordinate = getCharLocation(row, column);
94
95                 final CanvasCharacter character = new CanvasCharacter(
96                         characterCoordinate, ' ', foregroundColor,
97                         backgroundColor);
98                 addShape(character);
99                 lines[row][column] = character;
100             }
101
102         }
103
104         setGroupForUngrouped(GROUP_CHARACTERS);
105
106         setRenderMode(RenderMode.TEXTURE);
107     }
108
109     public static TextPointer getTextDimensions(final String text) {
110
111         final BufferedReader reader = new BufferedReader(new StringReader(text));
112
113         int rows = 0;
114         int columns = 0;
115
116         while (true) {
117             final String line;
118             try {
119                 line = reader.readLine();
120             } catch (IOException e) {
121                 throw new RuntimeException(e);
122             }
123
124             if (line == null)
125                 return new TextPointer(rows, columns);
126
127             rows++;
128             columns = Math.max(columns, line.length());
129         }
130     }
131
132     @Override
133     public void beforeTransformHook(final TransformsStack transformPipe,
134                                     final RenderingContext context) {
135
136         final double textRelativeSize = context.width
137                 / getRelativityTracker().getDistanceToUser();
138
139         if (textRelativeSize < 2d) {
140             if (renderMode == RenderMode.CHARACTERS)
141                 setRenderMode(RenderMode.TEXTURE);
142             return;
143         }
144
145         final double piHalf = Math.PI / 2;
146
147         final double deviation = Math.abs(getRelativityTracker().getAngleXZ()
148                 + piHalf)
149                 + Math.abs(getRelativityTracker().getAngleYZ() + piHalf);
150
151         final double maxDeviation = 0.5;
152
153         if (deviation > maxDeviation) {
154             if (renderMode == RenderMode.CHARACTERS)
155                 setRenderMode(RenderMode.TEXTURE);
156         } else if (renderMode == RenderMode.TEXTURE)
157             setRenderMode(RenderMode.CHARACTERS);
158     }
159
160     public void clear() {
161         for (final CanvasCharacter[] line : lines)
162             for (final CanvasCharacter character : line) {
163                 character.setValue(' ');
164                 character.setBackgroundColor(backgroundColor);
165                 character.setForegroundColor(foregroundColor);
166             }
167
168         // set background color
169         getTexture().primaryBitmap.fillColor(backgroundColor);
170         getTexture().resetResampledBitmapCache();
171     }
172
173     private void drawCharToTexture(final int row, final int column,
174                                    final char character, final Color foreground) {
175         final Graphics2D graphics = getTexture().graphics;
176
177         getTexture().primaryBitmap.drawRectangle(
178                 column * FONT_CHAR_WIDTH_TEXTURE_PIXELS,
179                 row * FONT_CHAR_HEIGHT_TEXTURE_PIXELS,
180                 (column * FONT_CHAR_WIDTH_TEXTURE_PIXELS) + FONT_CHAR_WIDTH_TEXTURE_PIXELS,
181                 (row * FONT_CHAR_HEIGHT_TEXTURE_PIXELS) + FONT_CHAR_HEIGHT_TEXTURE_PIXELS,
182                 backgroundColor);
183
184         graphics.setFont(FONT);
185         graphics.setColor(foreground.toAwtColor());
186         graphics.drawChars(
187                 new char[]{character,}, 0, 1,
188                 (column * FONT_CHAR_WIDTH_TEXTURE_PIXELS),
189                 (row * FONT_CHAR_HEIGHT_TEXTURE_PIXELS) + (int) (FONT_CHAR_HEIGHT_TEXTURE_PIXELS / 1.23f));
190
191         getTexture().resetResampledBitmapCache();
192     }
193
194     public Point3D getCharLocation(final int row, final int column) {
195         final Point3D coordinate = topLeft.clone();
196
197         coordinate.translateY((row * FONT_CHAR_HEIGHT)
198                 + (FONT_CHAR_HEIGHT / 3.2));
199
200         coordinate.translateX((column * FONT_CHAR_WIDTH)
201                 + (FONT_CHAR_WIDTH / 2));
202
203         return coordinate;
204     }
205
206     public TextPointer getSize() {
207         return size;
208     }
209
210     public void locate(final int row, final int column) {
211         cursorLocation.row = row;
212         cursorLocation.column = column;
213     }
214
215     public void print(final String text) {
216         for (final char c : text.toCharArray())
217             putChar(c);
218     }
219
220     public void putChar(final char character) {
221         putChar(cursorLocation, character);
222
223         cursorLocation.column++;
224         if (cursorLocation.column >= size.column) {
225             cursorLocation.column = 0;
226             cursorLocation.row++;
227         }
228     }
229
230     public void putChar(final int row, final int column, final char character) {
231         if ((row >= lines.length) || (row < 0))
232             return;
233
234         final CanvasCharacter[] line = lines[row];
235
236         if ((column >= line.length) || (column < 0))
237             return;
238
239         final CanvasCharacter canvasCharacter = line[column];
240         canvasCharacter.setValue(character);
241         canvasCharacter.setBackgroundColor(backgroundColor);
242         canvasCharacter.setForegroundColor(foregroundColor);
243         drawCharToTexture(row, column, character,
244                 foregroundColor);
245     }
246
247     public void putChar(final TextPointer location, final char character) {
248         putChar(location.row, location.column, character);
249     }
250
251     public void setBackgroundColor(
252             final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) {
253         this.backgroundColor = backgroundColor;
254     }
255
256     public void setForegroundColor(
257             final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor) {
258         this.foregroundColor = foregroundColor;
259     }
260
261     private void setRenderMode(final RenderMode mode) {
262         if (mode == renderMode)
263             return;
264
265         switch (mode) {
266             case CHARACTERS:
267                 hideGroup(GROUP_TEXTURE);
268                 showGroup(GROUP_CHARACTERS);
269                 break;
270             case TEXTURE:
271                 hideGroup(GROUP_CHARACTERS);
272                 showGroup(GROUP_TEXTURE);
273                 break;
274         }
275
276         renderMode = mode;
277     }
278
279     public void setText(final String text) {
280         final BufferedReader reader = new BufferedReader(new StringReader(text));
281
282         int row = 0;
283
284         while (true) {
285             final String line;
286             try {
287                 line = reader.readLine();
288             } catch (IOException e) {
289                 throw new RuntimeException(e);
290             }
291
292             if (line == null)
293                 return;
294
295             int column = 0;
296             for (final char c : line.toCharArray()) {
297                 putChar(row, column, c);
298                 column++;
299             }
300             row++;
301         }
302     }
303
304     public void setTextColor(final Color color) {
305         for (final CanvasCharacter[] line : lines)
306             for (final CanvasCharacter character : line)
307                 character.setForegroundColor(color);
308     }
309
310 }