2 * Sixth 3D engine. Author: Svjatoslav Agejenko.
3 * This project is released under Creative Commons Zero (CC0) license.
5 package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line;
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.math.Vertex;
11 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
12 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
17 * Line is represented by two points, width and color and width.
19 public class Line extends AbstractCoordinateShape {
21 private static final double MINIMUM_WIDTH_THRESHOLD = 1;
23 private static final double LINE_WIDTH_MULTIPLIER = 0.2d;
28 public final double width;
29 final LineInterpolator[] lineInterpolators = new LineInterpolator[4];
36 public Line(final Line parentLine) {
37 this(parentLine.coordinates[0].coordinate.clone(),
38 parentLine.coordinates[1].coordinate.clone(),
39 new Color(parentLine.color), parentLine.width);
42 public Line(final Point3D point1, final Point3D point2, final Color color,
53 for (int i = 0; i < lineInterpolators.length; i++)
54 lineInterpolators[i] = new LineInterpolator();
58 private void drawHorizontalLine(final LineInterpolator line1,
59 final LineInterpolator line2, final int y,
60 final RenderingContext renderBuffer) {
62 int x1 = line1.getX(y);
63 int x2 = line2.getX(y);
65 double d1 = line1.getD();
66 double d2 = line2.getD();
73 final double tmp2 = d1;
78 final int unclippedWidth = x2 - x1;
79 final double dinc = (d2 - d1) / unclippedWidth;
86 if (x2 >= renderBuffer.width)
87 x2 = renderBuffer.width - 1;
89 final int drawnWidth = x2 - x1;
91 int offset = ((y * renderBuffer.width) + x1) * 4;
92 final byte[] offSreenBufferBytes = renderBuffer.pixels;
94 final int lineAlpha = color.a;
96 final int colorB = color.b;
97 final int colorG = color.g;
98 final int colorR = color.r;
100 for (int i = 0; i < drawnWidth; i++) {
102 final double alphaMultiplier = 1d - Math.abs(d1);
104 final int realLineAlpha = (int) (lineAlpha * alphaMultiplier);
105 final int backgroundAlpha = 255 - realLineAlpha;
107 offSreenBufferBytes[offset] = (byte) 255;
109 offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorB * realLineAlpha)) / 256);
111 offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorG * realLineAlpha)) / 256);
113 offSreenBufferBytes[offset] = (byte) ((((offSreenBufferBytes[offset] & 0xff) * backgroundAlpha) + (colorR * realLineAlpha)) / 256);
121 private void drawSinglePixelHorizontalLine(final RenderingContext buffer,
124 final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
125 final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
127 int xStart = (int) onScreenPoint1.x;
128 int xEnd = (int) onScreenPoint2.x;
134 final int tmp = xStart;
137 lineHeight = (int) (onScreenPoint1.y - onScreenPoint2.y);
138 yBase = (int) onScreenPoint2.y;
140 yBase = (int) onScreenPoint1.y;
141 lineHeight = (int) (onScreenPoint2.y - onScreenPoint1.y);
144 final int lineWidth = xEnd - xStart;
148 final byte[] offSreenBufferBytes = buffer.pixels;
149 final int backgroundAlpha = 255 - alpha;
151 final int blueWithAlpha = color.b * alpha;
152 final int greenWithAplha = color.g * alpha;
153 final int redWithAlpha = color.r * alpha;
155 for (int relativeX = 0; relativeX <= lineWidth; relativeX++) {
156 final int x = xStart + relativeX;
158 if ((x >= 0) && (x < buffer.width)) {
160 final int y = yBase + ((relativeX * lineHeight) / lineWidth);
161 if ((y >= 0) && (y < buffer.height)) {
162 int ramOffset = ((y * buffer.width) + x) * 4;
164 offSreenBufferBytes[ramOffset] = (byte) 255;
166 offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256);
168 offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAplha) / 256);
170 offSreenBufferBytes[ramOffset] = (byte) ((((offSreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256);
177 private void drawSinglePixelVerticalLine(final RenderingContext buffer,
180 final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
181 final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
183 int yStart = (int) onScreenPoint1.y;
184 int yEnd = (int) onScreenPoint2.y;
190 final int tmp = yStart;
193 lineWidth = (int) (onScreenPoint1.x - onScreenPoint2.x);
194 xBase = (int) onScreenPoint2.x;
196 xBase = (int) onScreenPoint1.x;
197 lineWidth = (int) (onScreenPoint2.x - onScreenPoint1.x);
200 final int lineHeight = yEnd - yStart;
204 final byte[] offScreenBufferBytes = buffer.pixels;
205 final int backgroundAlpha = 255 - alpha;
207 final int blueWithAlpha = color.b * alpha;
208 final int greenWithAlpha = color.g * alpha;
209 final int redWithAlpha = color.r * alpha;
211 for (int relativeY = 0; relativeY <= lineHeight; relativeY++) {
212 final int y = yStart + relativeY;
214 if ((y >= 0) && (y < buffer.height)) {
216 final int x = xBase + ((relativeY * lineWidth) / lineHeight);
217 if ((x >= 0) && (x < buffer.width)) {
218 int ramOffset = ((y * buffer.width) + x) * 4;
220 offScreenBufferBytes[ramOffset] = (byte) 255;
222 offScreenBufferBytes[ramOffset] = (byte) ((((offScreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + blueWithAlpha) / 256);
224 offScreenBufferBytes[ramOffset] = (byte) ((((offScreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + greenWithAlpha) / 256);
226 offScreenBufferBytes[ramOffset] = (byte) ((((offScreenBufferBytes[ramOffset] & 0xff) * backgroundAlpha) + redWithAlpha) / 256);
232 private int getLineInterpolator(final int startPointer, final int y) {
234 for (int i = startPointer; i < lineInterpolators.length; i++)
235 if (lineInterpolators[i].containsY(y))
241 public void paint(final RenderingContext buffer) {
243 final Point2D onScreenPoint1 = coordinates[0].onScreenCoordinate;
244 final Point2D onScreenPoint2 = coordinates[1].onScreenCoordinate;
246 final double xp = onScreenPoint2.x - onScreenPoint1.x;
247 final double yp = onScreenPoint2.y - onScreenPoint1.y;
249 final double point1radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width)
250 / coordinates[0].transformedCoordinate.z;
251 final double point2radius = (buffer.width * LINE_WIDTH_MULTIPLIER * width)
252 / coordinates[1].transformedCoordinate.z;
254 if ((point1radius < MINIMUM_WIDTH_THRESHOLD)
255 || (point2radius < MINIMUM_WIDTH_THRESHOLD)) {
257 double averageRadius = (point1radius + point2radius) / 2;
259 if (averageRadius > 1)
262 final int alpha = (int) (color.a * averageRadius);
266 if (Math.abs(xp) > Math.abs(yp))
267 drawSinglePixelHorizontalLine(buffer, alpha);
269 drawSinglePixelVerticalLine(buffer, alpha);
273 final double lineLength = Math.sqrt((xp * xp) + (yp * yp));
275 final double yinc1 = (point1radius * xp) / lineLength;
276 final double yinc2 = (point2radius * xp) / lineLength;
278 final double xdec1 = (point1radius * yp) / lineLength;
279 final double xdec2 = (point2radius * yp) / lineLength;
281 final double p1x1 = onScreenPoint1.x - xdec1;
282 final double p1y1 = onScreenPoint1.y + yinc1;
284 final double p1x2 = onScreenPoint1.x + xdec1;
285 final double p1y2 = onScreenPoint1.y - yinc1;
287 final double p2x1 = onScreenPoint2.x - xdec2;
288 final double p2y1 = onScreenPoint2.y + yinc2;
290 final double p2x2 = onScreenPoint2.x + xdec2;
291 final double p2y2 = onScreenPoint2.y - yinc2;
293 lineInterpolators[0].setPoints(p1x1, p1y1, 1d, p2x1, p2y1, 1d);
294 lineInterpolators[1].setPoints(p1x2, p1y2, -1d, p2x2, p2y2, -1d);
296 lineInterpolators[2].setPoints(p1x1, p1y1, 1d, p1x2, p1y2, -1d);
297 lineInterpolators[3].setPoints(p2x1, p2y1, 1d, p2x2, p2y2, -1d);
316 if (ymax >= buffer.height)
317 ymax = buffer.height - 1;
319 for (int y = (int) ymin; y <= ymax; y++) {
320 final int li1 = getLineInterpolator(0, y);
322 final int li2 = getLineInterpolator(li1 + 1, y);
324 drawHorizontalLine(lineInterpolators[li1], lineInterpolators[li2], y, buffer);