Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added colors and display coordinate abstraction #3

Merged
merged 1 commit into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_library(
fractal-generator_lib OBJECT
source/lib.cpp
source/graphics/basic_display.cpp
source/graphics/color_conversions.cpp
)

target_include_directories(
Expand Down
8 changes: 5 additions & 3 deletions source/config.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#pragma once

#include <cstddef>

namespace fractal {
constexpr auto WINDOW_WIDTH = 800UZ;
constexpr auto WINDOW_HEIGHT = 600UZ;
constexpr auto FRAME_RATE{60UZ};
constexpr std::size_t WINDOW_WIDTH = 800UZ;
constexpr std::size_t WINDOW_HEIGHT = 600UZ;
constexpr std::size_t FRAME_RATE = 60UZ;
} // namespace fractal
65 changes: 65 additions & 0 deletions source/coordinates.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#pragma once

#include <complex>

namespace fractal {
using display_coordinate = std::pair<uint16_t, uint16_t>;
using complex_coordinate = std::complex<float>;

struct display_domain {
display_coordinate start_coordinate;
display_coordinate end_coordinate;

class DisplayCoordinateIterator {
display_coordinate current_coordinate_;
display_coordinate end_coordinate_;

public:
explicit DisplayCoordinateIterator(const display_domain& domain) :
current_coordinate_{domain.start_coordinate},
end_coordinate_{domain.end_coordinate}
{}

const display_coordinate& operator*() const { return current_coordinate_; }

DisplayCoordinateIterator& operator++()
{
if (current_coordinate_.first == end_coordinate_.first) [[unlikely]] {
current_coordinate_.first = 0;
current_coordinate_.second++;
}
else {
current_coordinate_.first++;
}
return *this;
}

bool operator==(const DisplayCoordinateIterator&) const = default;
};

DisplayCoordinateIterator begin() const { return DisplayCoordinateIterator{*this}; }

DisplayCoordinateIterator end() const
{
return DisplayCoordinateIterator{
display_domain{end_coordinate, end_coordinate}
};
}
};

struct complex_domain {
complex_coordinate start_coordinate;
complex_coordinate end_coordinate;
};

inline float real_domain_size(const complex_domain& domain)
{
return domain.end_coordinate.real() - domain.start_coordinate.real();
}

inline float imaginary_domain_size(const complex_domain& domain)
{
return domain.end_coordinate.imag() - domain.start_coordinate.imag();
}

} // namespace fractal
24 changes: 20 additions & 4 deletions source/graphics/basic_display.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
#include "basic_display.hpp"

#include "graphics/color_conversions.hpp"

#include <fmt/format.h>
#include <SFML/Graphics.hpp>
#include <SFML/Graphics/RectangleShape.hpp>

#include <cmath>

namespace fractal {
void BasicDisplay::set_pixel(std::size_t x_pos, std::size_t y_pos, uint8_t value)
void BasicDisplay::set_pixel(display_coordinate coordinate, uint16_t value)
{
pixels_.at(coordinate.first).at(coordinate.second) = value;
}

std::tuple<int, int, int> interpolateColor(
float t, std::tuple<int, int, int> color1, std::tuple<int, int, int> color2
)
{
pixels_.at(x_pos).at(y_pos) = value;
int r = std::get<0>(color1) + t * (std::get<0>(color2) - std::get<0>(color1));
int g = std::get<1>(color1) + t * (std::get<1>(color2) - std::get<1>(color1));
int b = std::get<2>(color1) + t * (std::get<2>(color2) - std::get<2>(color1));
return {r, g, b};
}

void BasicDisplay::display_window()
Expand All @@ -20,10 +34,12 @@ void BasicDisplay::display_window()
image.create(WINDOW_WIDTH, WINDOW_HEIGHT);
for (std::size_t x_pos = 0; x_pos < WINDOW_WIDTH; ++x_pos) {
for (std::size_t y_pos = 0; y_pos < WINDOW_HEIGHT; ++y_pos) {
std::uint8_t pixel_value = pixels_.at(x_pos).at(y_pos);
std::uint16_t pixel_value = pixels_.at(x_pos).at(y_pos);
auto tuple = number_to_rgb(pixel_value);

image.setPixel(
static_cast<unsigned int>(x_pos), static_cast<unsigned int>(y_pos),
sf::Color(pixel_value, pixel_value, pixel_value)
sf::Color(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple))
);
}
}
Expand Down
5 changes: 3 additions & 2 deletions source/graphics/basic_display.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "config.hpp"
#include "coordinates.hpp"

#include <SFML/System/Vector2.hpp>

Expand All @@ -12,7 +13,7 @@

namespace fractal {
class BasicDisplay {
std::array<std::array<uint8_t, WINDOW_HEIGHT>, WINDOW_WIDTH> pixels_{};
std::array<std::array<uint16_t, WINDOW_HEIGHT>, WINDOW_WIDTH> pixels_{};
std::function<void(sf::Vector2f, sf::Vector2f)> on_resize_;

public:
Expand All @@ -22,7 +23,7 @@ class BasicDisplay {
) : on_resize_(std::move(on_resize))
{}

void set_pixel(std::size_t x_pos, std::size_t y_pos, std::uint8_t value);
void set_pixel(display_coordinate coordinate, uint16_t value);
void display_window();
};
} // namespace fractal
68 changes: 68 additions & 0 deletions source/graphics/color_conversions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "color_conversions.hpp"

#include <cmath>

namespace fractal {
std::tuple<uint16_t, uint16_t, uint16_t>
hsv_to_rgb(float hue, float saturation, float value)
{
float chroma = value * saturation;
float hue_prime = hue / 60.0f;
float uint16_termediate =
chroma * (1 - static_cast<float>(std::fabs(std::fmod(hue_prime, 2) - 1)));
float red_temp = 0.0f;
float green_temp = 0.0f;
float blue_temp = 0.0f;

if (0 <= hue_prime && hue_prime < 1) {
red_temp = chroma;
green_temp = uint16_termediate;
blue_temp = 0;
}
else if (1 <= hue_prime && hue_prime < 2) {
red_temp = uint16_termediate;
green_temp = chroma;
blue_temp = 0;
}
else if (2 <= hue_prime && hue_prime < 3) {
red_temp = 0;
green_temp = chroma;
blue_temp = uint16_termediate;
}
else if (3 <= hue_prime && hue_prime < 4) {
red_temp = 0;
green_temp = uint16_termediate;
blue_temp = chroma;
}
else if (4 <= hue_prime && hue_prime < 5) {
red_temp = uint16_termediate;
green_temp = 0;
blue_temp = chroma;
}
else if (5 <= hue_prime && hue_prime < 6) {
red_temp = chroma;
green_temp = 0;
blue_temp = uint16_termediate;
}

float match_value = value - chroma;
float red = red_temp + match_value;
float green = green_temp + match_value;
float blue = blue_temp + match_value;

auto red_int = static_cast<uint16_t>(red * 255);
auto green_int = static_cast<uint16_t>(green * 255);
auto blue_int = static_cast<uint16_t>(blue * 255);

return {red_int, green_int, blue_int};
}

std::tuple<uint16_t, uint16_t, uint16_t> number_to_rgb(uint16_t number)
{
float hue = (static_cast<float>(number) / 65535.0f) * 360.0f;
float saturation = 1.0f;
float value = 1.0f;

return hsv_to_rgb(hue, saturation, value);
}
} // namespace fractal
12 changes: 12 additions & 0 deletions source/graphics/color_conversions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <cstdint>

#include <tuple>

namespace fractal {
std::tuple<uint16_t, uint16_t, uint16_t>
hsv_to_rgb(float hue, float saturation, float value);

std::tuple<uint16_t, uint16_t, uint16_t> number_to_rgb(uint16_t number);
} // namespace fractal
59 changes: 59 additions & 0 deletions source/graphics/display_to_complex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include "coordinates.hpp"

#include <complex>

namespace fractal {
class DisplayToComplexCoordinates {
float real_scaling_factor_;
float imaginary_scaling_factor_;
complex_coordinate complex_domain_start_;

static float real_scaling_factor(
const display_coordinate& display_top_right,
const complex_domain& complex_domain
)
{
float real_d_size = real_domain_size(complex_domain);
return real_d_size / static_cast<float>(display_top_right.first);
}

static float imaginary_scaling_factor(
const display_coordinate& display_top_right,
const complex_domain& complex_domain
)
{
float imaginary_d_size = imaginary_domain_size(complex_domain);
return imaginary_d_size / static_cast<float>(display_top_right.second);
}

static complex_coordinate to_complex(display_coordinate coordinate)
{
return {
static_cast<float>(coordinate.first), static_cast<float>(coordinate.second)
};
}

public:
DisplayToComplexCoordinates(
display_coordinate display_top_right, const complex_domain& complex_domain
) :
real_scaling_factor_(real_scaling_factor(display_top_right, complex_domain)),
imaginary_scaling_factor_(
imaginary_scaling_factor(display_top_right, complex_domain)
),
complex_domain_start_(complex_domain.start_coordinate)
{}

complex_coordinate to_complex_projection(display_coordinate display_coord)
{
std::complex<float> raw_complex_coord = to_complex(display_coord);
std::complex offset = {
real_scaling_factor_ * raw_complex_coord.real(),
imaginary_scaling_factor_ * raw_complex_coord.imag()
};
return complex_domain_start_ + offset;
}
};
} // namespace fractal
Loading
Loading