/// <summary> /// Получение промежуточных точек линии. Интерполирование, можно сказать. /// </summary> /// <param name="p1">start point</param> /// <param name="p2">end point</param> /// <returns></returns> public static Point3D[] GetLine3DPoints(Point3D start, Point3D end) { var points = new List <Point3D>(); double diffx = end.X - start.X; double diffy = end.Y - start.Y; if (Math.Abs(diffx) > Math.Abs(diffy)) // abs - убирание знака { // упорядочиваем точки по X if (diffx < 0) { MathHelpers.Swap(ref start, ref end); } // получаем массив координат // их длина = |diffx|+1 (потому что рисуем отрезок, с начальной И конечной точкой.) double[] yCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.X, start.Y, end.X, end.Y); double[] zCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.X, start.Z, end.X, end.Z); // идем по X, получаем Y и Z for (double x = start.X; x <= end.X; ++x) { //(x - start.X) и так будет int. int y = (int)yCoords[(int)(x - start.X)]; double z = zCoords[(int)(x - start.X)]; var point = new Point3D(x, y, z); points.Add(point); } } else { // упорядочиваем точки по y if (diffy < 0) { MathHelpers.Swap(ref start, ref end); } double[] xCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.Y, start.X, end.Y, end.X); double[] zCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.Y, start.Z, end.Y, end.Z); // идем по Y, получаем X и Z for (double y = start.Y; y <= end.Y; y++) { int x = (int)xCoords[(int)(y - start.Y)]; double z = zCoords[(int)(y - start.Y)]; var point = new Point3D(x, y, z); points.Add(point); } } return(points.ToArray()); }
/// <summary> /// Draw line on the bitmap. Using z-buffer and color. /// </summary> private void DrawLine(FastBitmap bitmap, double[,] buffer, Point3D start, Point3D end, Color color) { // dx and dy are coord diffs between two points double diffx = end.X - start.X; double diffy = end.Y - start.Y; if (Math.Abs(diffx) > Math.Abs(diffy)) { // I want to work with point1 is 'greater' than point2 if (diffx < 0) { MathHelpers.Swap(ref start, ref end); } // here we are getting the segment coordinates as an array of Double values double[] yCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.X, start.Y, end.X, end.Y); double[] zCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.X, start.Z, end.X, end.Z); // and here we draw the points for (double x = start.X; x <= end.X; ++x) { int y = (int)yCoords[(int)(x - start.X)]; double z = zCoords[(int)(x - start.X)]; if (IsWithinImage((int)x, y) && buffer[(int)x, y] >= z) { // in fact, there is smth like ray-tracing // using z-buffer: if current point is closer to us than old one, we remember this... buffer[(int)x, y] = z; // and draw the point on the image. At last, the nearest point is drawing, other don't. bitmap.SetPixel((int)x, y, color); } } } else { // I want to work with point1 is 'greater' than point2 if (diffy < 0) { MathHelpers.Swap(ref start, ref end); } double[] xCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.Y, start.X, end.Y, end.X); double[] zCoords = MathHelpers.GetYsByXsBetweenTwoPoints2D(start.Y, start.Z, end.Y, end.Z); for (double y = start.Y; y <= end.Y; y++) { int x = (int)xCoords[(int)(y - start.Y)]; double z = zCoords[(int)(y - start.Y)]; if (IsWithinImage(x, (int)y) && buffer[x, (int)y] >= z) { buffer[x, (int)y] = z; bitmap.SetPixel(x, (int)y, color); } } } }
/// <summary> /// Draws triangle on bitmap using specified point vectors and color (and z-buffer) /// </summary> private void DrawTriangle(Vector3D p0, Vector3D p1, Vector3D p2, Color color, FastBitmap bitmap, double[,] buffer) { if (p0.Y != p1.Y || p0.Y != p2.Y) { // упорядочение if (p0.Y > p1.Y) { MathHelpers.Swap(ref p0, ref p1); } if (p0.Y > p2.Y) { MathHelpers.Swap(ref p0, ref p2); } if (p1.Y > p2.Y) { MathHelpers.Swap(ref p1, ref p2); } // in the next section, I turn the triangle into points, then draw them (using z-buffer): split the triangle into 2 segments (vertically), and rasterize them separately. The algorithm is described here: https://compgraphics.info/2D/triangle_rasterization.php int totalHeight = (int)Math.Round(p2.Y - p0.Y); for (int i = 0; i < totalHeight; ++i) { bool secondHalf = i > p1.Y - p0.Y || p1.Y == p0.Y; int segmentHeight = (int)Math.Round(secondHalf ? p2.Y - p1.Y : p1.Y - p0.Y); double alpha = (double)i / totalHeight; double beta = (i - (secondHalf ? p1.Y - p0.Y : 0.0)) / segmentHeight; Vector3D a = p0 + (p2 - p0) * alpha; Vector3D b = (secondHalf ? p1 + (p2 - p1) * beta : p0 + (p1 - p0) * beta); if (a.X > b.X) { // I want first to be greater than second MathHelpers.Swap(ref a, ref b); } for (int j = (int)Math.Round(a.X); j <= (int)Math.Round(b.X); ++j) { double scale = (a.X == b.X) ? 1 : (j - a.X) / (b.X - a.X); Vector3D p = a + (b - a) * scale; int x = (int)Math.Round(p.X); int y = (int)Math.Round(p.Y); if (IsWithinImage(x, y) && buffer[x, y] >= p.Z) { // z-buffer, we remember this new point buffer[x, y] = p.Z; bitmap.SetPixel(x, y, color); } } } } }