Skip to content

Commit 0969f09

Browse files
committed
Added colors and display coordinate abstraction
1 parent eaf431b commit 0969f09

9 files changed

+275
-75
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_library(
1919
fractal-generator_lib OBJECT
2020
source/lib.cpp
2121
source/graphics/basic_display.cpp
22+
source/graphics/color_conversions.cpp
2223
)
2324

2425
target_include_directories(

source/config.hpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#pragma once
22

3+
#include <cstddef>
4+
35
namespace fractal {
4-
constexpr auto WINDOW_WIDTH = 800UZ;
5-
constexpr auto WINDOW_HEIGHT = 600UZ;
6-
constexpr auto FRAME_RATE{60UZ};
6+
constexpr std::size_t WINDOW_WIDTH = 800UZ;
7+
constexpr std::size_t WINDOW_HEIGHT = 600UZ;
8+
constexpr std::size_t FRAME_RATE = 60UZ;
79
} // namespace fractal

source/coordinates.hpp

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#pragma once
2+
3+
#include <complex>
4+
5+
namespace fractal {
6+
using display_coordinate = std::pair<uint16_t, uint16_t>;
7+
using complex_coordinate = std::complex<float>;
8+
9+
struct display_domain {
10+
display_coordinate start_coordinate;
11+
display_coordinate end_coordinate;
12+
13+
class DisplayCoordinateIterator {
14+
display_coordinate current_coordinate_;
15+
display_coordinate end_coordinate_;
16+
17+
public:
18+
explicit DisplayCoordinateIterator(const display_domain& domain) :
19+
current_coordinate_{domain.start_coordinate},
20+
end_coordinate_{domain.end_coordinate}
21+
{}
22+
23+
const display_coordinate& operator*() const { return current_coordinate_; }
24+
25+
DisplayCoordinateIterator& operator++()
26+
{
27+
if (current_coordinate_.first == end_coordinate_.first) [[unlikely]] {
28+
current_coordinate_.first = 0;
29+
current_coordinate_.second++;
30+
}
31+
else {
32+
current_coordinate_.first++;
33+
}
34+
return *this;
35+
}
36+
37+
bool operator==(const DisplayCoordinateIterator&) const = default;
38+
};
39+
40+
DisplayCoordinateIterator begin() const { return DisplayCoordinateIterator{*this}; }
41+
42+
DisplayCoordinateIterator end() const
43+
{
44+
return DisplayCoordinateIterator{
45+
display_domain{end_coordinate, end_coordinate}
46+
};
47+
}
48+
};
49+
50+
struct complex_domain {
51+
complex_coordinate start_coordinate;
52+
complex_coordinate end_coordinate;
53+
};
54+
55+
inline float real_domain_size(const complex_domain& domain)
56+
{
57+
return domain.end_coordinate.real() - domain.start_coordinate.real();
58+
}
59+
60+
inline float imaginary_domain_size(const complex_domain& domain)
61+
{
62+
return domain.end_coordinate.imag() - domain.start_coordinate.imag();
63+
}
64+
65+
} // namespace fractal

source/graphics/basic_display.cpp

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
#include "basic_display.hpp"
22

3+
#include "graphics/color_conversions.hpp"
4+
35
#include <SFML/Graphics.hpp>
46

7+
#include <cmath>
8+
59
namespace fractal {
6-
void BasicDisplay::set_pixel(std::size_t x_pos, std::size_t y_pos, uint8_t value)
10+
void BasicDisplay::set_pixel(display_coordinate coordinate, uint16_t value)
11+
{
12+
pixels_.at(coordinate.first).at(coordinate.second) = value;
13+
}
14+
15+
std::tuple<int, int, int> interpolateColor(
16+
float t, std::tuple<int, int, int> color1, std::tuple<int, int, int> color2
17+
)
718
{
8-
pixels_.at(x_pos).at(y_pos) = value;
19+
int r = std::get<0>(color1) + t * (std::get<0>(color2) - std::get<0>(color1));
20+
int g = std::get<1>(color1) + t * (std::get<1>(color2) - std::get<1>(color1));
21+
int b = std::get<2>(color1) + t * (std::get<2>(color2) - std::get<2>(color1));
22+
return {r, g, b};
923
}
1024

1125
void BasicDisplay::display_window()
@@ -18,10 +32,12 @@ void BasicDisplay::display_window()
1832
image.create(WINDOW_WIDTH, WINDOW_HEIGHT);
1933
for (std::size_t x_pos = 0; x_pos < WINDOW_WIDTH; ++x_pos) {
2034
for (std::size_t y_pos = 0; y_pos < WINDOW_HEIGHT; ++y_pos) {
21-
std::uint8_t pixel_value = pixels_.at(x_pos).at(y_pos);
35+
std::uint16_t pixel_value = pixels_.at(x_pos).at(y_pos);
36+
auto tuple = number_to_rgb(pixel_value);
37+
2238
image.setPixel(
2339
static_cast<unsigned int>(x_pos), static_cast<unsigned int>(y_pos),
24-
sf::Color(pixel_value, pixel_value, pixel_value)
40+
sf::Color(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple))
2541
);
2642
}
2743
}

source/graphics/basic_display.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
#pragma once
22

33
#include "config.hpp"
4+
#include "coordinates.hpp"
45

56
#include <cstdint>
67

78
#include <array>
89

910
namespace fractal {
1011
class BasicDisplay {
11-
std::array<std::array<uint8_t, WINDOW_HEIGHT>, WINDOW_WIDTH> pixels_{};
12+
std::array<std::array<uint16_t, WINDOW_HEIGHT>, WINDOW_WIDTH> pixels_{};
1213

1314
public:
14-
void set_pixel(std::size_t x_pos, std::size_t y_pos, std::uint8_t value);
15+
void set_pixel(display_coordinate coordinate, uint16_t value);
1516
void display_window();
1617
};
1718
} // namespace fractal

source/graphics/color_conversions.cpp

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "color_conversions.hpp"
2+
3+
#include <cmath>
4+
5+
namespace fractal {
6+
std::tuple<uint16_t, uint16_t, uint16_t>
7+
hsv_to_rgb(float hue, float saturation, float value)
8+
{
9+
float chroma = value * saturation;
10+
float hue_prime = hue / 60.0f;
11+
float uint16_termediate =
12+
chroma * (1 - static_cast<float>(std::fabs(std::fmod(hue_prime, 2) - 1)));
13+
float red_temp = 0.0f;
14+
float green_temp = 0.0f;
15+
float blue_temp = 0.0f;
16+
17+
if (0 <= hue_prime && hue_prime < 1) {
18+
red_temp = chroma;
19+
green_temp = uint16_termediate;
20+
blue_temp = 0;
21+
}
22+
else if (1 <= hue_prime && hue_prime < 2) {
23+
red_temp = uint16_termediate;
24+
green_temp = chroma;
25+
blue_temp = 0;
26+
}
27+
else if (2 <= hue_prime && hue_prime < 3) {
28+
red_temp = 0;
29+
green_temp = chroma;
30+
blue_temp = uint16_termediate;
31+
}
32+
else if (3 <= hue_prime && hue_prime < 4) {
33+
red_temp = 0;
34+
green_temp = uint16_termediate;
35+
blue_temp = chroma;
36+
}
37+
else if (4 <= hue_prime && hue_prime < 5) {
38+
red_temp = uint16_termediate;
39+
green_temp = 0;
40+
blue_temp = chroma;
41+
}
42+
else if (5 <= hue_prime && hue_prime < 6) {
43+
red_temp = chroma;
44+
green_temp = 0;
45+
blue_temp = uint16_termediate;
46+
}
47+
48+
float match_value = value - chroma;
49+
float red = red_temp + match_value;
50+
float green = green_temp + match_value;
51+
float blue = blue_temp + match_value;
52+
53+
auto red_int = static_cast<uint16_t>(red * 255);
54+
auto green_int = static_cast<uint16_t>(green * 255);
55+
auto blue_int = static_cast<uint16_t>(blue * 255);
56+
57+
return {red_int, green_int, blue_int};
58+
}
59+
60+
std::tuple<uint16_t, uint16_t, uint16_t> number_to_rgb(uint16_t number)
61+
{
62+
float hue = (static_cast<float>(number) / 65535.0f) * 360.0f;
63+
float saturation = 1.0f;
64+
float value = 1.0f;
65+
66+
return hsv_to_rgb(hue, saturation, value);
67+
}
68+
} // namespace fractal

source/graphics/color_conversions.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
5+
#include <tuple>
6+
7+
namespace fractal {
8+
std::tuple<uint16_t, uint16_t, uint16_t>
9+
hsv_to_rgb(float hue, float saturation, float value);
10+
11+
std::tuple<uint16_t, uint16_t, uint16_t> number_to_rgb(uint16_t number);
12+
} // namespace fractal
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#pragma once
2+
3+
#include "coordinates.hpp"
4+
5+
#include <complex>
6+
7+
namespace fractal {
8+
class DisplayToComplexCoordinates {
9+
float real_scaling_factor_;
10+
float imaginary_scaling_factor_;
11+
complex_coordinate complex_domain_start_;
12+
13+
static float real_scaling_factor(
14+
const display_coordinate& display_top_right,
15+
const complex_domain& complex_domain
16+
)
17+
{
18+
float real_d_size = real_domain_size(complex_domain);
19+
return real_d_size / static_cast<float>(display_top_right.first);
20+
}
21+
22+
static float imaginary_scaling_factor(
23+
const display_coordinate& display_top_right,
24+
const complex_domain& complex_domain
25+
)
26+
{
27+
float imaginary_d_size = imaginary_domain_size(complex_domain);
28+
return imaginary_d_size / static_cast<float>(display_top_right.second);
29+
}
30+
31+
static complex_coordinate to_complex(display_coordinate coordinate)
32+
{
33+
return {
34+
static_cast<float>(coordinate.first), static_cast<float>(coordinate.second)
35+
};
36+
}
37+
38+
public:
39+
DisplayToComplexCoordinates(
40+
display_coordinate display_top_right, const complex_domain& complex_domain
41+
) :
42+
real_scaling_factor_(real_scaling_factor(display_top_right, complex_domain)),
43+
imaginary_scaling_factor_(
44+
imaginary_scaling_factor(display_top_right, complex_domain)
45+
),
46+
complex_domain_start_(complex_domain.start_coordinate)
47+
{}
48+
49+
complex_coordinate to_complex_projection(display_coordinate display_coord)
50+
{
51+
std::complex<float> raw_complex_coord = to_complex(display_coord);
52+
std::complex offset = {
53+
real_scaling_factor_ * raw_complex_coord.real(),
54+
imaginary_scaling_factor_ * raw_complex_coord.imag()
55+
};
56+
return complex_domain_start_ + offset;
57+
}
58+
};
59+
} // namespace fractal

0 commit comments

Comments
 (0)