From ec96114111483ee8b1ff6a7fde56366b72abfcfa Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Wed, 31 Jan 2024 18:19:26 +0100 Subject: [PATCH] [FEAT] Add canvas ppm part. --- data/canvas.ppm | 49 ++++++++++++++++++++++++++++++++++ raytracing/src/canvas.cpp | 56 +++++++++++++++++++++++++++++++++++++++ raytracing/src/canvas.h | 9 ++++++- raytracing/src/color.cpp | 48 +++++++++++++++++++++++++++++++-- raytracing/src/color.h | 13 ++++++++- tests/03_canvas.cpp | 50 ++++++++++++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 data/canvas.ppm diff --git a/data/canvas.ppm b/data/canvas.ppm new file mode 100644 index 0000000..2eae473 --- /dev/null +++ b/data/canvas.ppm @@ -0,0 +1,49 @@ +P3 +# Created by GIMP version 2.10.30 PNM plug-in +5 3 +255 +0 +0 +0 +255 +255 +255 +0 +0 +0 +255 +255 +255 +0 +0 +0 +255 +255 +255 +0 +0 +0 +255 +255 +255 +0 +0 +0 +255 +255 +255 +0 +0 +0 +255 +255 +255 +0 +0 +0 +255 +255 +255 +0 +0 +0 diff --git a/raytracing/src/canvas.cpp b/raytracing/src/canvas.cpp index e87955b..eeb5a4b 100644 --- a/raytracing/src/canvas.cpp +++ b/raytracing/src/canvas.cpp @@ -78,3 +78,59 @@ bool Canvas::write_pixel(uint16_t a_pos_x, uint16_t a_pos_y, const Color &a_colo m_pixels[a_pos_x][a_pos_y] = a_color; return true; } + +/* ------------------------------------------------------------------------- */ + +std::string Canvas::to_ppm(void) +{ + std::string the_result; + + // Header + the_result = "P3\n"; + the_result += std::to_string(m_width) + " " + std::to_string(m_height) + "\n"; + the_result += "255\n"; + + for (int j = 0; j < m_height; j++) + { + uint16_t the_col_number = 0; + for (int i = 0; i < m_width; i++) + { + Color the_color; + the_color = m_pixels[i][j]; + + add_color_component(the_result, the_col_number, the_color.red_to_integer()); + add_color_component(the_result, the_col_number, the_color.green_to_integer()); + add_color_component(the_result, the_col_number, the_color.blue_to_integer()); + } + the_result += "\n"; + the_col_number = 0; + } + + return the_result; +} + +/* ------------------------------------------------------------------------- */ + +bool Canvas::add_color_component(std::string &a_str_value, uint16_t &a_col_number, uint8_t a_color) +{ + std::string the_color_value; + + the_color_value = std::to_string(a_color); + + if ((the_color_value.length() + 1 + a_col_number) >= 70) + { + a_str_value += "\n"; + a_col_number = 0; + } + + if (a_col_number != 0) + { + a_str_value += " "; + a_col_number++; + } + + a_str_value += the_color_value; + a_col_number += the_color_value.length(); + + return true; +} diff --git a/raytracing/src/canvas.h b/raytracing/src/canvas.h index 62a0448..ecd0c89 100644 --- a/raytracing/src/canvas.h +++ b/raytracing/src/canvas.h @@ -28,9 +28,11 @@ /* ------------------------------------------------------------------------- */ -#include #include +#include +#include + #include "color.h" /* ------------------------------------------------------------------------- */ @@ -49,6 +51,11 @@ namespace Raytracer const Color &pixel_at(uint16_t a_pos_x, uint16_t a_pos_y) const; bool write_pixel(uint16_t a_pos_x, uint16_t a_pos_y, const Color &a_color); + std::string to_ppm(void); + + private: + bool add_color_component(std::string &a_str_value, uint16_t &a_col_number, uint8_t a_color); + private: uint16_t m_width; uint16_t m_height; diff --git a/raytracing/src/color.cpp b/raytracing/src/color.cpp index e28186c..427b049 100644 --- a/raytracing/src/color.cpp +++ b/raytracing/src/color.cpp @@ -28,13 +28,14 @@ /* ------------------------------------------------------------------------- */ +#include +#include + #include "common.h" #include "color.h" using namespace Raytracer; -#include - /* ------------------------------------------------------------------------- */ Color::Color(void) : m_red(0), m_green(0), m_blue(0) @@ -174,3 +175,46 @@ double Color::blue(void) const { return m_blue; } + +/* ------------------------------------------------------------------------- */ + +uint8_t Color::red_to_integer(void) const +{ + return color_to_integer(m_red); +} + +/* ------------------------------------------------------------------------- */ + +uint8_t Color::green_to_integer(void) const +{ + return color_to_integer(m_green); +} + +/* ------------------------------------------------------------------------- */ + +uint8_t Color::blue_to_integer(void) const +{ + return color_to_integer(m_blue); +} + +/* ------------------------------------------------------------------------- */ + +uint8_t Color::color_to_integer(double a_color) const +{ + uint8_t the_value; + + if (a_color < 0) + { + the_value = 0; + } + else if (a_color > 1.0) + { + the_value = 255; + } + else + { + the_value = round(a_color * 255); + } + + return the_value; +} diff --git a/raytracing/src/color.h b/raytracing/src/color.h index 0ecaff2..308c752 100644 --- a/raytracing/src/color.h +++ b/raytracing/src/color.h @@ -28,6 +28,10 @@ /* ------------------------------------------------------------------------- */ +#include + +/* ------------------------------------------------------------------------- */ + namespace Raytracer { class Color @@ -40,7 +44,7 @@ namespace Raytracer bool operator==(const Color &a_color) const; const Color &operator=(const Color &an_other); - + const Color operator+(const Color &a_color) const; const Color operator-(const Color &a_color) const; const Color operator*(double a_scalar) const; @@ -55,6 +59,13 @@ namespace Raytracer double green(void) const; double blue(void) const; + uint8_t red_to_integer(void) const; + uint8_t green_to_integer(void) const; + uint8_t blue_to_integer(void) const; + + private: + uint8_t color_to_integer(double a_color) const; + private: double m_red; double m_green; diff --git a/tests/03_canvas.cpp b/tests/03_canvas.cpp index f1c4ae0..8286b32 100644 --- a/tests/03_canvas.cpp +++ b/tests/03_canvas.cpp @@ -56,3 +56,53 @@ TEST_CASE("[Canvas] Writing pixels to a canvas", "[Canvas]") REQUIRE(c.pixel_at(2, 3) == red); } + +/* ------------------------------------------------------------------------- */ + +TEST_CASE("[Canvas] Constructing the PPM pixel data", "[Canvas]") +{ + std::string ppm, the_ref_ppm; + Canvas c(5, 3); + Color c1(1.5, 0, 0); + Color c2(0, 0.5, 0); + Color c3(-0.5, 0, 1); + + c.write_pixel(0, 0, c1); + c.write_pixel(2, 1, c2); + c.write_pixel(4, 2, c3); + + ppm = c.to_ppm(); + + the_ref_ppm = "P3\n5 3\n255\n"; + the_ref_ppm += "255 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; + the_ref_ppm += "0 0 0 0 0 0 0 128 0 0 0 0 0 0 0\n"; + the_ref_ppm += "0 0 0 0 0 0 0 0 0 0 0 0 0 0 255\n"; + + REQUIRE(ppm == the_ref_ppm); +} + +/* ------------------------------------------------------------------------- */ + +TEST_CASE("[Canvas] Split long lines in PPM files", "[Canvas]") +{ + std::string ppm, the_ref_ppm; + Canvas c(10, 2); + + for (int j = 0; j < 2; ++j) + { + for (int i = 0; i < 10; ++i) + { + c.write_pixel(i, j, Color(1, 0.8, 0.6)); + } + } + + ppm = c.to_ppm(); + + the_ref_ppm = "P3\n10 2\n255\n"; + the_ref_ppm += "255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n"; + the_ref_ppm += "153 255 204 153 255 204 153 255 204 153 255 204 153\n"; + the_ref_ppm += "255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n"; + the_ref_ppm += "153 255 204 153 255 204 153 255 204 153 255 204 153\n"; + + REQUIRE(ppm == the_ref_ppm); +}