{{:cgfs:filled_triangles-000.png?400|}} #include #include #include #include #include #include "vmath.h" int const Cw = 600; int const Ch = 600; using vec2 = vmath::vec2; void PutPixel(int x, int y, Color const &color) { DrawPixel(x + Cw / 2, -y + Ch / 2, color); } template requires std::output_iterator void Interpolate(int i0, float d0, int i1, float d1, Iter out) { if (i0 == i1) { *out = d0; return; } auto a = (d1 - d0) / (i1 - i0); auto d = d0; for (int i = i0; i <= i1; ++i) { *out++ = d; d += a; } } void DrawLine(vec2 const &p0, vec2 const &p1, Color const &color) { auto x0 = p0.x; auto y0 = p0.y; auto x1 = p1.x; auto y1 = p1.y; if (std::abs(x1 - x0) > std::abs(y1 - y0)) { if (x0 > x1) { std::swap(x0, x1); std::swap(y0, y1); } auto i0 = static_cast(std::floor(x0)); auto i1 = static_cast(std::floor(x1)); auto ys = std::vector(i1 - i0 + 1); Interpolate(i0, y0, i1, y1, ys.begin()); for (int i = i0; i <= i1; ++i) { PutPixel(i, ys[i - i0], color); } } else { if (y0 > y1) { std::swap(y0, y1); std::swap(x0, x1); } auto i0 = static_cast(std::floor(y0)); auto i1 = static_cast(std::floor(y1)); auto xs = std::vector(i1 - i0 + 1); Interpolate(i0, x0, i1, x1, xs.begin()); for (int i = i0; i <= i1; ++i) { PutPixel(xs[i - i0], i, color); } } } void DrawWireframeTriangle(vec2 const &p0, vec2 const &p1, vec2 const &p2, Color const &color) { DrawLine(p0, p1, color); DrawLine(p1, p2, color); DrawLine(p2, p0, color); } void DrawFilledTriangle(vec2 const &p0, vec2 const &p1, vec2 const &p2, Color const &color) { vec2 P0{p0}, P1{p1}, P2{p2}; if (P1.y < P0.y) { std::swap(P1, P0); } if (P2.y < P0.y) { std::swap(P2, P0); } if (P2.y < P1.y) { std::swap(P2, P1); } auto i0 = static_cast(std::floor(P0.y)); auto i1 = static_cast(std::floor(P1.y)); auto i2 = static_cast(std::floor(P2.y)); auto x01 = std::vector(i1 - i0 + 1); auto x12 = std::vector(i2 - i1 + 1); auto x02 = std::vector(i2 - i0 + 1); Interpolate(i0, P0.x, i1, P1.x, x01.begin()); Interpolate(i1, P1.x, i2, P2.x, x12.begin()); Interpolate(i0, P0.x, i2, P2.x, x02.begin()); x01.pop_back(); auto x012 = std::vector(x01.size() + x12.size()); assert(x012.size() == x02.size()); auto corner = std::copy(x01.begin(), x01.end(), x012.begin()); std::copy(x12.begin(), x12.end(), corner); auto &x_left = x012; auto &x_right = x02; auto m = x012.size() / 2; if (x02[m] < x012[m]) { std::swap(x_left, x_right); } for (int y = i0; y < i2; ++y) { auto x_l = static_cast(std::floor(x_left[y - i0])); auto x_r = static_cast(std::floor(x_right[y - i0])); for (int x = x_l; x <= x_r; ++x) { PutPixel(x, y, color); } } } int main() { InitWindow(Cw, Ch, "Filled Triangles"); vec2 p0{-100, 200}; vec2 p1{100, 100}; vec2 p2{-200, -200}; while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(RAYWHITE); PutPixel(p0.x, p0.y, RED); PutPixel(p1.x, p1.y, RED); PutPixel(p2.x, p2.y, RED); DrawFilledTriangle(p0, p1, p2, BLUE); DrawWireframeTriangle(p0, p1, p2, PURPLE); EndDrawing(); } }