Improved code readability
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / renderer / raster / shapes / basic / line / Line.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.basic.line;
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.Color;
11 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
12
13 public class Line extends AbstractCoordinateShape {
14
15     private static final double MINIMUM_WIDTH_THRESHOLD = 1;
16
17     private static final double LINE_WIDTH_MULTIPLIER = 0.2d;
18
19     public final double width;
20     final LineInterpolator[] li = new LineInterpolator[4];
21     public Color color;
22
23     public Line(final Line parentLine) {
24         this(parentLine.coordinates[0].coordinate.clone(),
25                 parentLine.coordinates[1].coordinate.clone(),
26                 new Color(parentLine.color), parentLine.width);
27     }
28
29     public Line(final Point3D point1, final Point3D point2, final Color color,
30                 final double width) {
31
32         super(point1, point2);
33
34         this.color = color;
35         this.width = width;
36
37         for (int i = 0; i < li.length; i++)
38             li[i] = new LineInterpolator();
39
40     }
41
42     private void drawHorizontalLine(final LineInterpolator line1,
43                                     final LineInterpolator line2, final int y,
44                                     final RenderingContext renderBuffer) {
45
46         int x1 = line1.getX(y);
47         int x2 = line2.getX(y);
48
49         double d1 = line1.getD();
50         double d2 = line2.getD();
51
52         if (x1 > x2) {
53             final int tmp = x1;
54             x1 = x2;
55             x2 = tmp;
56
57             final double tmp2 = d1;
58             d1 = d2;
59             d2 = tmp2;
60         }
61
62         final int unclippedWidth = x2 - x1;
63         final double dinc = (d2 - d1) / unclippedWidth;
64
65         if (x1 < 0) {
66             d1 += (dinc * (-x1));
67             x1 = 0;
68         }
69
70         if (x2 >= renderBuffer.width)
71             x2 = renderBuffer.width - 1;
72
73         final int drawnWidth = x2 - x1;
74
75         int offset = ((y * renderBuffer.width) + x1) * 4;
76         final byte[] offSreenBufferBytes = renderBuffer.pixels;
77
78         final int lineAlpha = color.a;
79
80         final int colorB = color.b;
81         final int colorG = color.g;
82         final int colorR = color.r;
83
84         for (int i = 0; i < drawnWidth; i++) {
85
86             final double alphaMultiplier = 1d - Math.abs(d1);
87
88             final int realLineAlpha = (int) (lineAlpha * alphaMultiplier);
89             final int backgroundAlpha = 255 - realLineAlpha;
90
91             offSreenBufferBytes[offset] = (byte) 255;
92             offset++;
93             offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorB * realLineAlpha)) / 256);
94             offset++;
95             offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorG * realLineAlpha)) / 256);
96             offset++;
97             offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorR * realLineAlpha)) / 256);
98             offset++;
99
100             d1 += dinc;
101         }
102
103     }
104
105     private void drawSinglePixelHorizontalLine(final RenderingContext buffer,
106                                                final int alpha) {
107
108         final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
109         final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
110
111         int xStart = (int) onScreenPoint1.x;
112         int xEnd = (int) onScreenPoint2.x;
113
114         int lineHeight;
115         int yBase;
116
117         if (xStart > xEnd) {
118             final int tmp = xStart;
119             xStart = xEnd;
120             xEnd = tmp;
121             lineHeight = (int) (onScreenPoint1.y - onScreenPoint2.y);
122             yBase = (int) onScreenPoint2.y;
123         } else {
124             yBase = (int) onScreenPoint1.y;
125             lineHeight = (int) (onScreenPoint2.y - onScreenPoint1.y);
126         }
127
128         final int lineWidth = xEnd - xStart;
129         if (lineWidth == 0)
130             return;
131
132         final byte[] offSreenBufferBytes = buffer.pixels;
133         final int backgroundAlpha = 255 - alpha;
134
135         final int blueWithAlpha = color.b * alpha;
136         final int greenWithAplha = color.g * alpha;
137         final int redWithAlpha = color.r * alpha;
138
139         for (int relativeX = 0; relativeX <= lineWidth; relativeX++) {
140             final int x = xStart + relativeX;
141
142             if ((x >= 0) && (x < buffer.width)) {
143
144                 final int y = yBase + ((relativeX * lineHeight) / lineWidth);
145                 if ((y >= 0) && (y < buffer.height)) {
146                     int ramOffset = ((y * buffer.width) + x) * 4;
147
148                     offSreenBufferBytes[ramOffset] = (byte) 255;
149                     ramOffset++;
150                     offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256);
151                     ramOffset++;
152                     offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAplha) / 256);
153                     ramOffset++;
154                     offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256);
155                 }
156             }
157         }
158
159     }
160
161     private void drawSinglePixelVerticalLine(final RenderingContext buffer,
162                                              final int alpha) {
163
164         final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
165         final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
166
167         int yStart = (int) onScreenPoint1.y;
168         int yEnd = (int) onScreenPoint2.y;
169
170         int lineWidth;
171         int xBase;
172
173         if (yStart > yEnd) {
174             final int tmp = yStart;
175             yStart = yEnd;
176             yEnd = tmp;
177             lineWidth = (int) (onScreenPoint1.x - onScreenPoint2.x);
178             xBase = (int) onScreenPoint2.x;
179         } else {
180             xBase = (int) onScreenPoint1.x;
181             lineWidth = (int) (onScreenPoint2.x - onScreenPoint1.x);
182         }
183
184         final int lineHeight = yEnd - yStart;
185         if (lineHeight == 0)
186             return;
187
188         final byte[] offScreenBufferBytes = buffer.pixels;
189         final int backgroundAlpha = 255 - alpha;
190
191         final int blueWithAlpha = color.b * alpha;
192         final int greenWithAlpha = color.g * alpha;
193         final int redWithAlpha = color.r * alpha;
194
195         for (int relativeY = 0; relativeY <= lineHeight; relativeY++) {
196             final int y = yStart + relativeY;
197
198             if ((y >= 0) && (y < buffer.height)) {
199
200                 final int x = xBase + ((relativeY * lineWidth) / lineHeight);
201                 if ((x >= 0) && (x < buffer.width)) {
202                     int ramOffset = ((y * buffer.width) + x) * 4;
203
204                     offScreenBufferBytes[ramOffset] = (byte) 255;
205                     ramOffset++;
206                     offScreenBufferBytes[ramOffset] = (byte) ((((offScreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256);
207                     ramOffset++;
208                     offScreenBufferBytes[ramOffset] = (byte) ((((offScreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAlpha) / 256);
209                     ramOffset++;
210                     offScreenBufferBytes[ramOffset] = (byte) ((((offScreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256);
211                 }
212             }
213         }
214     }
215
216     private int getLineInterpolator(final int startPointer, final int y) {
217
218         for (int i = startPointer; i < li.length; i++)
219             if (li[i].containsY(y))
220                 return i;
221         return -1;
222     }
223
224     @Override
225     public void paint(final RenderingContext buffer) {
226
227         final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
228         final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
229
230         final double xp = onScreenPoint2.x - onScreenPoint1.x;
231         final double yp = onScreenPoint2.y - onScreenPoint1.y;
232
233         final double point1radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width)
234                 / coordinates[0].transformedCoordinate.z;
235         final double point2radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width)
236                 / coordinates[1].transformedCoordinate.z;
237
238         if ((point1radius < MINIMUM_WIDTH_THRESHOLD)
239                 || (point2radius < MINIMUM_WIDTH_THRESHOLD)) {
240
241             double averageRadius = (point1radius + point2radius) / 2;
242
243             if (averageRadius > 1)
244                 averageRadius = 1;
245
246             final int alpha = (int) (color.a * averageRadius);
247             if (alpha < 2)
248                 return;
249
250             if (Math.abs(xp) > Math.abs(yp))
251                 drawSinglePixelHorizontalLine(buffer, alpha);
252             else
253                 drawSinglePixelVerticalLine(buffer, alpha);
254             return;
255         }
256
257         final double lineLength = Math.sqrt((xp * xp) + (yp * yp));
258
259         final double yinc1 = (point1radius * xp) / lineLength;
260         final double yinc2 = (point2radius * xp) / lineLength;
261
262         final double xdec1 = (point1radius * yp) / lineLength;
263         final double xdec2 = (point2radius * yp) / lineLength;
264
265         final double p1x1 = onScreenPoint1.x - xdec1;
266         final double p1y1 = onScreenPoint1.y + yinc1;
267
268         final double p1x2 = onScreenPoint1.x + xdec1;
269         final double p1y2 = onScreenPoint1.y - yinc1;
270
271         final double p2x1 = onScreenPoint2.x - xdec2;
272         final double p2y1 = onScreenPoint2.y + yinc2;
273
274         final double p2x2 = onScreenPoint2.x + xdec2;
275         final double p2y2 = onScreenPoint2.y - yinc2;
276
277         li[0].setPoints(p1x1, p1y1, 1d, p2x1, p2y1, 1d);
278         li[1].setPoints(p1x2, p1y2, -1d, p2x2, p2y2, -1d);
279
280         li[2].setPoints(p1x1, p1y1, 1d, p1x2, p1y2, -1d);
281         li[3].setPoints(p2x1, p2y1, 1d, p2x2, p2y2, -1d);
282
283         double ymin = p1y1;
284         if (p1y2 < ymin)
285             ymin = p1y2;
286         if (p2y1 < ymin)
287             ymin = p2y1;
288         if (p2y2 < ymin)
289             ymin = p2y2;
290         if (ymin < 0)
291             ymin = 0;
292
293         double ymax = p1y1;
294         if (p1y2 > ymax)
295             ymax = p1y2;
296         if (p2y1 > ymax)
297             ymax = p2y1;
298         if (p2y2 > ymax)
299             ymax = p2y2;
300         if (ymax >= buffer.height)
301             ymax = buffer.height - 1;
302
303         for (int y = (int) ymin; y <= ymax; y++) {
304             final int li1 = getLineInterpolator(0, y);
305             if (li1 != -1) {
306                 final int li2 = getLineInterpolator(li1 + 1, y);
307                 if (li2 != -1)
308                     drawHorizontalLine(li[li1], li[li2], y, buffer);
309             }
310         }
311     }
312
313 }