Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_tikz_frame_stream_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 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28*/
29
35#include <atomic>
36#include <chrono>
37#include <filesystem>
38#include <fstream>
39#include <sstream>
40#include <stdexcept>
41#include <string>
42#if defined(_WIN32)
43# include <process.h>
44#else
45# include <unistd.h>
46#endif
47
48#include <gtest/gtest.h>
49
50#include <ca-frame-stream.H>
51#include <ca-tikz.H>
52#include <tpl_ca_hex_lattice.H>
53#include <tpl_ca_lattice.H>
54#include <tpl_ca_storage.H>
56
57using namespace Aleph::CA;
58
59namespace
60{
64
65 Grid make_frame()
66 {
67 Grid g({2, 3}, 0);
68 g.set({0, 1}, 1);
69 g.set({1, 2}, 1);
70 return g;
71 }
72
74 long long process_id() noexcept
75 {
76#if defined(_WIN32)
77 return static_cast<long long>(_getpid());
78#else
79 return static_cast<long long>(getpid());
80#endif
81 }
82
83 std::filesystem::path unique_tmp_dir(const std::string &name)
84 {
85 // A steady_clock tick alone is unique *within* a process, but not
86 // across the several processes that actually run this suite (each
87 // TEST() is its own ctest process, and CI runs ctest with
88 // --parallel) -- two processes' calls landing in the same clock
89 // tick produce the identical name and race on the same directory.
90 // Mixing in the process id and a per-process counter closes that
91 // gap regardless of clock resolution or call count.
92 static std::atomic<unsigned long long> counter{0};
93 const auto tick = std::chrono::steady_clock::now().time_since_epoch().count();
94 return std::filesystem::temp_directory_path()
95 / (name + "_" + std::to_string(static_cast<long long>(tick)) +
96 "_" + std::to_string(process_id()) +
97 "_" + std::to_string(counter++));
98 }
99
100 std::string read_file(const std::filesystem::path &path, bool binary = false)
101 {
102 std::ifstream in(path, binary ? std::ios::binary : std::ios::in);
103 std::ostringstream out;
104 out << in.rdbuf();
105 return out.str();
106 }
107}
108
110{
111 const Grid frame = make_frame();
113 opts.cell_size = 0.5;
114 opts.include_legend = true;
115 opts.grid_colour = "blue!20";
116
117 const std::string tikz = render_rect_lattice_tikz(
118 frame,
119 [](int v) { return v == 0 ? std::string("green!10") : std::string("red!80"); },
120 opts);
121
122 EXPECT_NE(tikz.find("% Aleph::CA::render_rect_lattice_tikz"), std::string::npos);
123 EXPECT_NE(tikz.find("\\begin{tikzpicture}"), std::string::npos);
124 EXPECT_NE(tikz.find("fill=green!10, draw=blue!20"), std::string::npos);
125 EXPECT_NE(tikz.find("fill=red!80, draw=blue!20"), std::string::npos);
126 EXPECT_NE(tikz.find("rectangle (1, 0.5)"), std::string::npos);
127 EXPECT_NE(tikz.find("{alive}"), std::string::npos);
128}
129
139
141{
142 HexGrid hex({2, 2}, 0);
143 hex.set_axial({1, 0}, 1);
144 const std::string hex_tikz = render_lattice_tikz(
145 hex, Binary_Tikz_Palette<int>{0, "white", "orange"});
146 EXPECT_NE(hex_tikz.find("% Aleph::CA::render_hex_lattice_tikz"), std::string::npos);
147 EXPECT_NE(hex_tikz.find("regular polygon sides=6"), std::string::npos);
148 EXPECT_NE(hex_tikz.find("fill=orange"), std::string::npos);
149
150 TriGrid tri({2, 2}, 0);
151 tri.set_tri({1, 1}, 1);
152 const std::string tri_tikz = render_lattice_tikz(
153 tri, Binary_Tikz_Palette<int>{0, "white", "cyan"});
154 EXPECT_NE(tri_tikz.find("% Aleph::CA::render_triangular_lattice_tikz"), std::string::npos);
155 EXPECT_NE(tri_tikz.find("regular polygon sides=3"), std::string::npos);
156 EXPECT_NE(tri_tikz.find("fill=cyan"), std::string::npos);
157}
158
160{
161 const auto dir = unique_tmp_dir("aleph_ca_stream_pgm");
162 std::filesystem::remove_all(dir);
163
165 opts.directory = dir;
166 opts.prefix = "gol";
167 opts.format = Frame_Output_Format::PGM;
168 opts.stride = 2;
169 opts.zero_pad = 3;
170
172 const Grid frame = make_frame();
173 sink.accept(0, frame);
174 sink.accept(1, frame);
175 sink.accept(2, frame);
176 sink.flush();
177
178 ASSERT_EQ(sink.written_frames().size(), 2u);
179 const auto p0 = dir / "gol_000.pgm";
180 const auto p2 = dir / "gol_002.pgm";
181 EXPECT_TRUE(std::filesystem::exists(p0));
182 EXPECT_FALSE(std::filesystem::exists(dir / "gol_001.pgm"));
183 EXPECT_TRUE(std::filesystem::exists(p2));
184 EXPECT_EQ(read_file(p0, true).rfind("P5\n3 2\n255\n", 0), 0u);
185
186 const std::string manifest = read_file(dir / "manifest.csv");
187 EXPECT_NE(manifest.find("step,format,path"), std::string::npos);
188 EXPECT_NE(manifest.find("0,pgm,"), std::string::npos);
189 EXPECT_NE(manifest.find("2,pgm,"), std::string::npos);
190
191 std::filesystem::remove_all(dir);
192}
193
195{
196 const auto dir = unique_tmp_dir("aleph_ca_stream_text");
197 std::filesystem::remove_all(dir);
198 const Grid frame = make_frame();
199
200 struct Case
201 {
202 Frame_Output_Format format;
203 const char *prefix;
204 const char *filename;
205 const char *needle;
206 };
207
208 const Case cases[] = {
209 {Frame_Output_Format::RLE, "rle", "rle_000001.rle", "x = 3, y = 2"},
210 {Frame_Output_Format::CSV, "csv", "csv_000001.csv", "0,1,0"},
211 {Frame_Output_Format::JSON, "json", "json_000001.json", "\"cells\":[[0,1,0],[0,0,1]]"},
212 {Frame_Output_Format::TikZ, "tikz", "tikz_000001.tex", "\\begin{tikzpicture}"}
213 };
214
215 for (const auto &tc : cases)
216 {
218 opts.directory = dir;
219 opts.prefix = tc.prefix;
220 opts.format = tc.format;
222 sink.accept(1, frame);
223 sink.flush();
224
225 const auto path = dir / tc.filename;
226 ASSERT_TRUE(std::filesystem::exists(path)) << path;
227 EXPECT_NE(read_file(path).find(tc.needle), std::string::npos) << path;
228 }
229
230 std::filesystem::remove_all(dir);
231}
232
234{
235 const auto dir = unique_tmp_dir("aleph_ca_stream_invalid");
237 bad.directory = dir;
238 bad.stride = 0;
239 EXPECT_THROW({ Directory_Frame_Sink bad_sink(bad); }, std::domain_error);
240
242 opts.directory = dir;
243 opts.prefix = "frame";
244 opts.format = Frame_Output_Format::PPM;
245 opts.zero_pad = 4;
247 const std::string cmd = sink.montage_command_hint("movie.mp4", 12);
248 EXPECT_NE(cmd.find("ffmpeg -y -framerate 12"), std::string::npos);
249 EXPECT_NE(cmd.find("frame_%04d.ppm"), std::string::npos);
250 EXPECT_NE(cmd.find("movie.mp4"), std::string::npos);
251
252 std::filesystem::remove_all(dir);
253}
size_t size_t int32_t * out
Definition ca-c-api.h:120
Directory-backed frame sinks for CA trajectories.
TikZ renderers for cellular-automata frames.
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
Directory-backed sink for CA frames.
std::string montage_command_hint(const std::string &output_file="ca.mp4", unsigned fps=24) const
Return an ffmpeg command hint for the current frame sequence.
void accept(std::size_t step, const Lattice &frame)
Accept a frame and write it if it passes the stride filter.
const Array< std::pair< std::size_t, std::filesystem::path > > & written_frames() const noexcept
Return the frame files written so far.
void flush() const
Write the optional manifest.
Hexagonal lattice with axial accessors over a 2D storage.
Lattice that adds boundary-aware access on top of a storage.
Triangular lattice with parity helpers over a 2D storage.
#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
Frame_Output_Format
File format used by Directory_Frame_Sink.
void render_rect_lattice_tikz(std::ostream &out, const Lattice &frame, Palette &&palette, const Rect_Tikz_Options &opts={})
Render a rectangular 2D frame as a TikZ picture.
Definition ca-tikz.H:139
void render_lattice_tikz(std::ostream &out, const Lattice &frame, Palette &&palette, const Rect_Tikz_Options &opts={})
Render any supported 2D CA lattice as TikZ.
Definition ca-tikz.H:225
static void prefix(Node *root, DynList< Node * > &acc)
Itor find(const Itor &beg, const Itor &end, const T &value)
Find the first element equal to a value.
Definition ahAlgo.H:230
STL namespace.
Default binary TikZ palette.
Definition ca-tikz.H:92
Options for directory-backed frame streams.
std::filesystem::path directory
destination directory
Out-of-range neighbours behave as if the lattice ended.
Definition ca-traits.H:119
TikZ rendering options for rectangular frames.
Definition ca-tikz.H:74
double cell_size
cell side length in centimetres
Definition ca-tikz.H:75
static long counter
Definition test-splice.C:40
CmdLine cmd
Definition testHash.C:48
Hexagonal lattice with axial / offset / cube coordinate conversions and a TikZ-compatible pixel mappi...
Cellular automata lattice with pluggable boundary policies.
Dense, contiguous storage for cellular automata cells (1D/2D/3D).
Triangular lattice with up/down parity helpers and pixel mapping suitable for visualisation.