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