From 20d305f31f2cdddb38d1e51509f2afbbeee2f711 Mon Sep 17 00:00:00 2001 From: Mika Pi Date: Sun, 22 Aug 2021 13:56:07 -0700 Subject: [PATCH 1/4] C++ sample code for The Barnsley Fern --- contents/barnsley/barnsley.md | 2 + contents/barnsley/code/c++/barnsley.cpp | 118 ++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 contents/barnsley/code/c++/barnsley.cpp diff --git a/contents/barnsley/barnsley.md b/contents/barnsley/barnsley.md index 4464e4523..1c471f26a 100644 --- a/contents/barnsley/barnsley.md +++ b/contents/barnsley/barnsley.md @@ -125,6 +125,8 @@ The biggest differences between the two code implementations is that the Barnsle {% method %} {% sample lang="jl" %} [import, lang:"julia"](code/julia/barnsley.jl) +{% sample lang="cpp" %} +[import, lang:"cpp"](code/c++/barnsley.cpp) {% endmethod %} ### Bibliography diff --git a/contents/barnsley/code/c++/barnsley.cpp b/contents/barnsley/code/c++/barnsley.cpp new file mode 100644 index 000000000..4e5fc0513 --- /dev/null +++ b/contents/barnsley/code/c++/barnsley.cpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +using Vec2 = std::array; +using Vec3 = std::array; +using Row = std::array; +using Op = std::array; + +constexpr auto OpN = 4; + +template +auto operator+(std::array x, std::array y) { + for (auto i = 0U; i < N; ++i) + x[i] += y[i]; + return x; +} + +template +auto operator*(double k, std::array v) { + for (auto i = 0U; i < N; ++i) + v[i] *= k; + return v; +} + +template +auto operator*(std::array v, double k) { + return k * v; +} + +auto operator*(const Op& x, const Vec3& y) { + auto ret = Vec3{}; + for (auto i = 0; i < 3; ++i) { + ret[i] = 0; + for (auto j = 0; j < 3; ++j) + ret[i] += y[j] * x[i][j]; + } + return ret; +} + +// Returns a pseudo-random number generator +std::default_random_engine& rng() { + // Initialize static pseudo-random engine with non-deterministic random seed + static std::default_random_engine randEngine(std::random_device{}()); + return randEngine; +} + +// Returns a random double in [0, 1) +double drand() { + return std::uniform_real_distribution(0.0, 1.0)(rng()); +} + +// This is a function that reads in the Hutchinson operator and +// corresponding +// probabilities and outputs a randomly selected transform +// This works by choosing a random number and then iterating through all +// probabilities until it finds an appropriate bin +auto select_array( + const std::array& hutchinson_op, + const std::array& probabilities) { + + // random number to be binned + auto rnd = drand(); + + // This checks to see if a random number is in a bin, if not, that + // probability is subtracted from the random number and we check the + // next bin in the list + for (auto i = 0U; i < probabilities.size(); ++i) { + if (rnd < probabilities[i]) + return hutchinson_op[i]; + rnd -= probabilities[i]; + } + assert(!"check if probabilities adding up to 1"); +} + +// This is a general function to simulate a chaos game +// n is the number of iterations +// initial_location is the the starting point of the chaos game +// hutchinson_op is the set of functions to iterate through +// probabilities is the set of probabilities corresponding to the likelihood +// of choosing their corresponding function in hutchinson_op +auto chaos_game( + size_t n, + Vec2 initial_location, + const std::array& hutchinson_op, + const std::array& probabilities) { + + // Initializing the output array and the initial point + auto output_points = std::vector{}; + + // extending point to 3D for affine transform + auto point = Vec3{initial_location[0], initial_location[1], 1}; + + for (auto i = 0U; i < n; ++i) { + output_points.push_back(Vec2{point[0], point[1]}); + point = select_array(hutchinson_op, probabilities) * point; + } + + return output_points; +} + +int main() { + + const std::array barnsley_hutchinson = { + Op{Row{0.0, 0.0, 0.0}, Row{0.0, 0.16, 0.0}, Row{0.0, 0.0, 1.0}}, + Op{Row{0.85, 0.04, 0.0}, Row{-0.04, 0.85, 1.60}, Row{0.0, 0.0, 1.0}}, + Op{Row{0.20, -0.26, 0.0}, Row{0.23, 0.22, 1.60}, Row{0.0, 0.0, 1.0}}, + Op{Row{-0.15, 0.28, 0.0}, Row{0.26, 0.24, 0.44}, Row{0.0, 0.0, 1.0}}}; + + const std::array barnsley_probabilities = {0.01, 0.85, 0.07, 0.07}; + auto output_points = chaos_game( + 10'000, Vec2{0, 0}, barnsley_hutchinson, barnsley_probabilities); + + std::ofstream ofs("out.dat"); + for (auto pt : output_points) + ofs << pt[0] << '\t' << pt[1] << '\n'; +} From 599daa5eebc70f1f96b8cfcc0a42c2b578c1385d Mon Sep 17 00:00:00 2001 From: Mika Pi Date: Mon, 23 Aug 2021 10:42:40 -0700 Subject: [PATCH 2/4] Fix compilation warnings with strict flags --- contents/barnsley/code/c++/barnsley.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contents/barnsley/code/c++/barnsley.cpp b/contents/barnsley/code/c++/barnsley.cpp index 4e5fc0513..e32c1e490 100644 --- a/contents/barnsley/code/c++/barnsley.cpp +++ b/contents/barnsley/code/c++/barnsley.cpp @@ -10,30 +10,30 @@ using Op = std::array; constexpr auto OpN = 4; -template +template auto operator+(std::array x, std::array y) { for (auto i = 0U; i < N; ++i) x[i] += y[i]; return x; } -template +template auto operator*(double k, std::array v) { for (auto i = 0U; i < N; ++i) v[i] *= k; return v; } -template +template auto operator*(std::array v, double k) { return k * v; } auto operator*(const Op& x, const Vec3& y) { auto ret = Vec3{}; - for (auto i = 0; i < 3; ++i) { + for (auto i = 0U; i < 3U; ++i) { ret[i] = 0; - for (auto j = 0; j < 3; ++j) + for (auto j = 0U; j < 3U; ++j) ret[i] += y[j] * x[i][j]; } return ret; @@ -71,7 +71,7 @@ auto select_array( return hutchinson_op[i]; rnd -= probabilities[i]; } - assert(!"check if probabilities adding up to 1"); + assert(!static_cast("check if probabilities adding up to 1")); } // This is a general function to simulate a chaos game From 666cda07b42d40075a8f275740b36c42277ca1e1 Mon Sep 17 00:00:00 2001 From: Mika Pi Date: Mon, 23 Aug 2021 10:49:24 -0700 Subject: [PATCH 3/4] Mention C++ 17 --- contents/barnsley/code/c++/barnsley.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contents/barnsley/code/c++/barnsley.cpp b/contents/barnsley/code/c++/barnsley.cpp index e32c1e490..833085afd 100644 --- a/contents/barnsley/code/c++/barnsley.cpp +++ b/contents/barnsley/code/c++/barnsley.cpp @@ -1,3 +1,6 @@ +// The code bellow uses C++-17 features, compile it with C++-17 flags, e.g.: +// clang++ -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wpedantic -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -gdwarf-3 -D_GLIBCXX_DEBUG -std=c++17 -O3 -c ./barnsley.cpp barnsley + #include #include #include From 259cb1b4bd50402fb9356a79a6f5555cf9dd017e Mon Sep 17 00:00:00 2001 From: Mika Pi Date: Mon, 23 Aug 2021 10:53:01 -0700 Subject: [PATCH 4/4] Missed one U --- contents/barnsley/code/c++/barnsley.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contents/barnsley/code/c++/barnsley.cpp b/contents/barnsley/code/c++/barnsley.cpp index 833085afd..88fb24766 100644 --- a/contents/barnsley/code/c++/barnsley.cpp +++ b/contents/barnsley/code/c++/barnsley.cpp @@ -11,7 +11,7 @@ using Vec3 = std::array; using Row = std::array; using Op = std::array; -constexpr auto OpN = 4; +constexpr auto OpN = 4U; template auto operator+(std::array x, std::array y) {