public static void PlotLineSmooth(this IScreenBuffer buffer, int x0, int y0, int x1, int y1, Color baseColor) { void Swap(ref int a, ref int b) { var temp = a; a = b; b = temp; } float FractionalPart(float f) => f - (int)f; float ReciprocalOfFractionalPart(float f) => 1 - FractionalPart(f); float Gamma(float x, float exp) => (float)Pow(x, 1.0f / exp); void DrawPixel(int x, int y) => buffer.DrawPixel(x, y, baseColor); void DrawPixelScale(int x, int y, float intensity) => buffer.AddPixel(x, y, baseColor * Gamma(intensity, GammaExponent)); // Make sure the line runs top to bottom if (y0 > y1) { Swap(ref x0, ref x1); Swap(ref y0, ref y1); } // Draw the initial pixel, which is always exactly intersected by the line and so needs no weighting DrawPixel(x0, y0); var deltaX = x1 - x0; var xDir = 1; if (deltaX < 0) { xDir = -1; deltaX = -deltaX; // make DeltaX positive } var deltaY = y1 - y0; // Guaranteed to be positive since we made sure it goes from top to bottom // Special cases for horizontal, vertical, and diagonal lines if (deltaY == 0) { while (deltaX-- != 0) { x0 += xDir; DrawPixel(x0, y0); } return; } if (deltaX == 0) { do { y0++; DrawPixel(x0, y0); } while (--deltaY != 0); return; } if (deltaX == deltaY) { do { x0 += xDir; y0++; DrawPixel(x0, y0); } while (--deltaY != 0); return; } // line is not horizontal, diagonal, or vertical float gradient = 0; float accumulatedError = 0; bool isYMajorLine = deltaY > deltaX; if (isYMajorLine) { gradient = (float)deltaX / deltaY; while (--deltaY != 0) { accumulatedError += gradient; y0++; DrawPixelScale(x0 + xDir * (int)accumulatedError, y0, ReciprocalOfFractionalPart(accumulatedError)); DrawPixelScale(x0 + xDir * (int)accumulatedError + xDir, y0, FractionalPart(accumulatedError)); } } else { gradient = (float)deltaY / deltaX; while (--deltaX != 0) { accumulatedError += gradient; x0 += xDir; DrawPixelScale(x0, y0 + (int)accumulatedError, ReciprocalOfFractionalPart(accumulatedError)); DrawPixelScale(x0, y0 + (int)accumulatedError + 1, FractionalPart(accumulatedError)); } } // Draw the final pixel, which is always exactly intersected by the line // and so needs no weighting DrawPixel(x1, y1); }
public static void AddPixel(this IScreenBuffer buffer, Point p, Color c) => buffer.AddPixel(p.X, p.Y, c);