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