#include <raylib.h>
#include <cassert>
#include <cmath>
#include <iterator>
#include <vector>
#include "vmath.h"
int const Cw = 600;
int const Ch = 600;
using vec2 = vmath::vec2<float>;
void PutPixel(int x, int y, Color const &color) {
DrawPixel(x + Cw / 2, -y + Ch / 2, color);
}
template <typename Iter>
requires std::output_iterator<Iter, float>
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<int>(std::floor(x0));
auto i1 = static_cast<int>(std::floor(x1));
auto ys = std::vector<float>(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<int>(std::floor(y0));
auto i1 = static_cast<int>(std::floor(y1));
auto xs = std::vector<float>(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<int>(std::floor(P0.y));
auto i1 = static_cast<int>(std::floor(P1.y));
auto i2 = static_cast<int>(std::floor(P2.y));
auto x01 = std::vector<float>(i1 - i0 + 1);
auto x12 = std::vector<float>(i2 - i1 + 1);
auto x02 = std::vector<float>(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<float>(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<int>(std::floor(x_left[y - i0]));
auto x_r = static_cast<int>(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();
}
}