#include #include #include #include #include "prelude.hpp" enum class Error { file_error }; struct RGB { U8 Red; U8 Green; U8 Blue; RGB(U8 r, U8 g, U8 b); }; struct Color { F32 hue; F32 saturation; F32 value; Color(); Color(U32 val); Color(F32 hue, F32 saturation, F32 value); fn to_rgb() const noexcept -> RGB; fn lerp(ref, F64) const noexcept -> Color; }; struct Point { F64 x; F64 y; Point(F64 x, F64 y); }; template class Canvas { F64 x_max; F64 y_max; F64 x_min; F64 y_min; std::array pixels; public: Canvas(F64 min_x, F64 min_y, F64 max_x, F64 max_y) : x_max(max_x) , y_max(max_y) , x_min(min_x) , y_min(min_y) { } Canvas(Point p, Size size) { let delta = size / 2.0; x_max = p.x + delta; x_min = p.x - delta; y_max = p.y + delta; y_min = p.y - delta; } fn save_to_ppm(const char* file_path) const noexcept -> std::expected { var f = std::fstream(file_path, std::ios::binary | std::ios::out); if (not f.is_open()) return std::unexpected(Error::file_error); f << "P6\n" << width << " " << height << " 255\n"; for (let &color : pixels) { var c = color.to_rgb(); let rgb = std::make_tuple(c.Red, c.Green, c.Blue); f << fst(rgb) << snd(rgb) << thr(rgb) ; } f.close(); return Unit(); } fn coordinate(Size index) const noexcept -> Point { return Point( std::lerp(x_min, x_max, (index % width) / (F64)width), std::lerp(y_min, y_max, ((F64)index / height) / height)); } fn construct(auto op) noexcept -> Unit { #pragma omp parallel for for (Size idx = 0; idx < width * height; idx++) pixels[idx] = op(coordinate(idx)); return Unit(); } fn operator[](Size index) noexcept -> ref { return pixels[index]; } fn operator()(Size row, Size col) noexcept -> ref { return pixels[row * width + col]; } };