a103d7c5564b2cb1ee687155ddd4e394d4131dea
[sixth-3d.git] /
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.Point2D;
8 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
9 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
10 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
11 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
12
13 import java.awt.*;
14
15 import static eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon.drawPolygon;
16 import static eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas.FONT_CHAR_HEIGHT;
17 import static eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas.FONT_CHAR_WIDTH;
18 import static java.lang.String.valueOf;
19
20 /**
21  * Represents a single character on the text canvas.
22  */
23 public class CanvasCharacter extends AbstractCoordinateShape {
24
25     private static final int MAX_FONT_SIZE = 500;
26
27     /**
28      * Cached fonts.
29      */
30     private static final Font[] fonts = new Font[MAX_FONT_SIZE];
31
32     /**
33      * The character to be rendered.
34      */
35     private char value;
36
37     /**
38      * The foreground color of the character.
39      */
40     private eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor;
41
42     /**
43      * The background color of the character.
44      */
45     private eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor;
46
47     /**
48      * Creates a canvas character at the specified location with given colors.
49      *
50      * @param centerLocation   the center position in 3D space
51      * @param character        the character to render
52      * @param foregroundColor  the foreground (text) color
53      * @param backgroundColor  the background color
54      */
55     public CanvasCharacter(final Point3D centerLocation, final char character,
56                            final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor,
57                            final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) {
58
59         // There are 5 coordinates: center, upper left, upper right, lower right, lower left
60         super(5);
61
62         value = character;
63         this.foregroundColor = foregroundColor;
64         this.backgroundColor = backgroundColor;
65
66
67         coordinates[0].coordinate = centerLocation;
68
69         final double halfWidth = FONT_CHAR_WIDTH / 2d;
70         final double halfHeight = FONT_CHAR_HEIGHT / 2d;
71
72         // upper left
73         coordinates[1].coordinate = centerLocation.clone().translateX(-halfWidth)
74                 .translateY(-halfHeight);
75
76         // upper right
77         coordinates[2].coordinate = centerLocation.clone().translateX(halfWidth)
78                 .translateY(-halfHeight);
79
80         // lower right
81         coordinates[3].coordinate = centerLocation.clone().translateX(halfWidth)
82                 .translateY(halfHeight);
83
84         // lower left
85         coordinates[4].coordinate = centerLocation.clone().translateX(-halfWidth)
86                 .translateY(halfHeight);
87     }
88
89     /**
90      * Returns a font of the specified size.
91      * <p>
92      * If the font of the specified size is already cached, it will be
93      * returned. Otherwise, a new font will be created, cached and returned.
94      *
95      * @param size the size of the font
96      * @return the font
97      */
98     public static Font getFont(final int size) {
99         if (fonts[size] != null)
100             return fonts[size];
101
102         final Font font = new Font("Courier", Font.BOLD, size);
103         fonts[size] = font;
104         return font;
105     }
106
107     /**
108      * Returns the background color of the character.
109      *
110      * @return the background color
111      */
112     public eu.svjatoslav.sixth.e3d.renderer.raster.Color getBackgroundColor() {
113         return backgroundColor;
114     }
115
116     /**
117      * Sets the background color of the character.
118      *
119      * @param backgroundColor the new background color
120      */
121     public void setBackgroundColor(
122             final eu.svjatoslav.sixth.e3d.renderer.raster.Color backgroundColor) {
123         this.backgroundColor = backgroundColor;
124     }
125
126     /**
127      * Returns color of the foreground.
128      *
129      * @return the color
130      */
131     public eu.svjatoslav.sixth.e3d.renderer.raster.Color getForegroundColor() {
132         return foregroundColor;
133     }
134
135     /**
136      * Sets color of the foreground.
137      *
138      * @param foregroundColor the color
139      */
140     public void setForegroundColor(
141             final eu.svjatoslav.sixth.e3d.renderer.raster.Color foregroundColor) {
142         this.foregroundColor = foregroundColor;
143     }
144
145     /**
146      * Paints the character on the screen.
147      * @param renderingContext the rendering context
148      */
149     @Override
150     public void paint(final RenderingContext renderingContext) {
151
152         // Draw background rectangle first. It is composed of two triangles.
153         drawPolygon(renderingContext,
154                 coordinates[1].onScreenCoordinate,
155                 coordinates[2].onScreenCoordinate,
156                 coordinates[3].onScreenCoordinate,
157                 mouseInteractionController,
158                 backgroundColor);
159
160         drawPolygon(renderingContext,
161                 coordinates[1].onScreenCoordinate,
162                 coordinates[3].onScreenCoordinate,
163                 coordinates[4].onScreenCoordinate,
164                 mouseInteractionController,
165                 backgroundColor);
166
167         final int desiredFontSize = (int) ((renderingContext.width * 4.5) / onScreenZ);
168
169         // do not render too large characters
170         if (desiredFontSize >= MAX_FONT_SIZE)
171             return;
172
173         final Point2D onScreenLocation = coordinates[0].onScreenCoordinate;
174
175         // screen borders check
176         if (onScreenLocation.x < 0)
177             return;
178         if (onScreenLocation.y < 0)
179             return;
180
181         if (onScreenLocation.x > renderingContext.width)
182             return;
183         if (onScreenLocation.y > renderingContext.height)
184             return;
185
186         // check render Y bounds
187         if (onScreenLocation.y + desiredFontSize < renderingContext.renderMinY)
188             return;
189         if (onScreenLocation.y - desiredFontSize >= renderingContext.renderMaxY)
190             return;
191
192         // draw the character
193         final int fontSize = desiredFontSize;
194         final int drawX = (int) onScreenLocation.x - (int) (fontSize / 3.2);
195         final int drawY = (int) onScreenLocation.y + (int) (fontSize / 2.5);
196
197         renderingContext.executeWithGraphics(g -> {
198             g.setFont(getFont(fontSize));
199             g.setColor(foregroundColor.toAwtColor());
200             g.drawString(valueOf(value), drawX, drawY);
201         });
202
203     }
204
205     /**
206      * Sets the character value to render.
207      *
208      * @param value the new character value
209      */
210     public void setValue(final char value) {
211         this.value = value;
212     }
213
214 }