Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_animation_sinks_test.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11*/
12
18#include <atomic>
19#include <chrono>
20#include <cstdint>
21#include <filesystem>
22#include <fstream>
23#include <sstream>
24#include <string>
25#if defined(_WIN32)
26# include <process.h>
27#else
28# include <unistd.h>
29#endif
30
31#include <gtest/gtest.h>
32
33#include <ca-ffmpeg-sink.H>
34#include <ca-frame-stream.H>
35#include <ca-gif.H>
36#include <ca-html-player.H>
37#include <ca-npy.H>
38#include <ca-png.H>
39#include <ca-x11-viewer.H>
40#include <tpl_ca_lattice.H>
41#include <tpl_ca_storage.H>
42
43using namespace Aleph::CA;
44
45namespace
46{
48
49 Grid make_frame(const int phase = 0)
50 {
51 Grid g({3, 3}, 0);
52 if (phase == 0)
53 {
54 g.set({1, 0}, 1);
55 g.set({1, 1}, 1);
56 g.set({1, 2}, 1);
57 }
58 else
59 {
60 g.set({0, 1}, 1);
61 g.set({1, 1}, 1);
62 g.set({2, 1}, 1);
63 }
64 return g;
65 }
66
68 long long process_id() noexcept
69 {
70#if defined(_WIN32)
71 return static_cast<long long>(_getpid());
72#else
73 return static_cast<long long>(getpid());
74#endif
75 }
76
77 std::filesystem::path unique_tmp_dir(const std::string &name)
78 {
79 // A steady_clock tick alone is unique *within* a process, but not
80 // across the several processes that actually run this suite (each
81 // TEST() is its own ctest process, and CI runs ctest with
82 // --parallel) -- two processes' calls landing in the same clock
83 // tick produce the identical name and race on the same directory.
84 // Mixing in the process id and a per-process counter closes that
85 // gap regardless of clock resolution or call count.
86 static std::atomic<unsigned long long> counter{0};
87 const auto tick = std::chrono::steady_clock::now().time_since_epoch().count();
88 return std::filesystem::temp_directory_path()
89 / (name + "_" + std::to_string(static_cast<long long>(tick)) +
90 "_" + std::to_string(process_id()) +
91 "_" + std::to_string(counter++));
92 }
93
94 std::string read_file(const std::filesystem::path &path, const bool binary = false)
95 {
96 std::ifstream in(path, binary ? std::ios::binary : std::ios::in);
97 EXPECT_TRUE(in.is_open()) << "read_file: cannot open '" << path.string() << "'";
98 std::ostringstream out;
99 out << in.rdbuf();
100 return out.str();
101 }
102
103 std::size_t count_byte(const std::string &s, const unsigned char byte)
104 {
105 std::size_t count = 0;
106 for (const unsigned char c : s)
107 if (c == byte)
108 ++count;
109 return count;
110 }
111}
112
114{
115 const auto dir = unique_tmp_dir("aleph_ca_gif");
116 std::filesystem::remove_all(dir);
117 const auto path = dir / "blinker.gif";
118
120 opts.delay_cs = 4;
121 Gif_Frame_Sink gif(path,
122 Binary_RGB_Mapper<int>{0, {250, 250, 250}, {20, 20, 20}},
123 opts);
124 gif.accept(0, make_frame(0));
125 gif.accept(1, make_frame(1));
126 gif.flush();
127
128 const std::string bytes = read_file(path, true);
129 ASSERT_GE(bytes.size(), 16u);
130 EXPECT_EQ(bytes.substr(0, 6), "GIF89a");
131 EXPECT_EQ(count_byte(bytes, 0x2c), 2u);
132 EXPECT_EQ(bytes.back(), ';');
133
134 std::filesystem::remove_all(dir);
135}
136
138{
139 const auto dir = unique_tmp_dir("aleph_ca_html");
140 std::filesystem::remove_all(dir);
141 const auto path = dir / "player.html";
142
144 opts.title = "Blinker demo";
145 opts.cell_size = 5;
147 Binary_RGB_Mapper<int>{0, {255, 255, 255}, {0, 0, 0}},
148 opts);
149 html.accept(0, make_frame(0));
150 html.accept(1, make_frame(1));
151 html.flush();
152
153 const std::string text = read_file(path);
154 EXPECT_NE(text.find("<canvas id=\"view\""), std::string::npos);
155 EXPECT_NE(text.find("const frames = ["), std::string::npos);
156 EXPECT_NE(text.find("\"step\":0"), std::string::npos);
157 EXPECT_EQ(text.find("http://"), std::string::npos);
158 EXPECT_EQ(text.find("https://"), std::string::npos);
159
160 std::filesystem::remove_all(dir);
161}
162
164{
166 opts.auto_start = false;
167 Ffmpeg_Frame_Sink sink("movie.mp4",
168 Binary_RGB_Mapper<int>{0, {255, 255, 255}, {0, 0, 0}},
169 opts);
170 EXPECT_FALSE(sink.active());
171 EXPECT_NE(sink.command().find("ffmpeg -y"), std::string::npos);
172 EXPECT_NE(sink.command().find("movie.mp4"), std::string::npos);
173 sink.accept(0, make_frame());
174 sink.flush();
175}
176
178{
180 opts.enabled = false;
181 X11_Frame_Sink viewer(Binary_RGB_Mapper<int>{0, {255, 255, 255}, {0, 0, 0}}, opts);
182 EXPECT_FALSE(viewer.active());
183 viewer.accept(0, make_frame());
184 viewer.flush();
185 EXPECT_FALSE(viewer.active());
186}
187
189{
190 const auto dir = unique_tmp_dir("aleph_ca_composite");
191 std::filesystem::remove_all(dir);
192
194 Png_Frame_Sink(dir / "frame_{step}.png",
195 Binary_RGB_Mapper<int>{0, {255, 255, 255}, {0, 0, 0}},
196 2),
197 Npy_Frame_Sink(dir / "frame_{step}.npy", 2),
198 HTML_Player_Frame_Sink(dir / "trajectory.html",
199 Binary_RGB_Mapper<int>{0, {255, 255, 255}, {0, 0, 0}}));
200
201 composite.accept(0, make_frame(0));
202 composite.accept(1, make_frame(1));
203 composite.flush();
204
205 EXPECT_TRUE(std::filesystem::exists(dir / "frame_00.png"));
206 EXPECT_TRUE(std::filesystem::exists(dir / "frame_00.npy"));
207 EXPECT_TRUE(std::filesystem::exists(dir / "trajectory.html"));
208 EXPECT_NE(read_file(dir / "trajectory.html").find("\"step\":1"), std::string::npos);
209
210 std::filesystem::remove_all(dir);
211}
size_t size_t int32_t * out
Definition ca-c-api.h:120
Runtime ffmpeg sink for publication-quality CA video.
Directory-backed frame sinks for CA trajectories.
Animated GIF sink for cellular-automata trajectories.
Self-contained HTML trajectory player for CA frames.
NumPy .npy writer for CA frames.
Dependency-free PNG frame sink for cellular automata.
Optional live X11 viewer sink for CA simulations.
Fan-out sink that forwards every frame to several sinks.
Frame sink that streams PPM frames into ffmpeg.
Sink that buffers frames and writes an animated GIF on flush.
Definition ca-gif.H:337
void accept(const std::size_t step, const Lattice &frame)
Collect one frame.
Definition ca-gif.H:359
Sink that writes a self-contained HTML playback file.
void accept(const std::size_t step, const Lattice &frame)
Append one frame to the embedded trajectory.
Lattice that adds boundary-aware access on top of a storage.
Path-pattern sink that writes one .npy file per frame.
Definition ca-npy.H:184
Path-pattern sink that writes one PNG file per accepted frame.
Definition ca-png.H:241
Live X11 frame sink.
#define TEST(name)
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Itor find(const Itor &beg, const Itor &end, const T &value)
Find the first element equal to a value.
Definition ahAlgo.H:230
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
STL namespace.
Default binary RGB mapper.
Definition ca-io.H:245
Options for Ffmpeg_Frame_Sink.
bool auto_start
start ffmpeg in the constructor
Options for animated GIF output.
Definition ca-gif.H:66
unsigned delay_cs
frame delay in centiseconds
Definition ca-gif.H:67
Options for generated HTML players.
std::string title
document title
Out-of-range neighbours behave as if the lattice ended.
Definition ca-traits.H:119
Options for the live X11 viewer.
bool enabled
disable to force no-op behaviour
static long counter
Definition test-splice.C:40
Cellular automata lattice with pluggable boundary policies.
Dense, contiguous storage for cellular automata cells (1D/2D/3D).