Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca-html-player.H
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 ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
43#ifndef CA_HTML_PLAYER_H
44#define CA_HTML_PLAYER_H
45
46#include <concepts>
47#include <cstddef>
48#include <cstdint>
49#include <filesystem>
50#include <fstream>
51#include <ostream>
52#include <sstream>
53#include <string>
54#include <utility>
55
56#include <ah-errors.H>
57#include <tpl_array.H>
58
59#include <ca-frame-stream.H>
60#include <ca-io.H>
61#include <tpl_ca_concepts.H>
62
63namespace Aleph {
64namespace CA {
65
68{
69 std::string title = "Aleph::CA trajectory";
70 unsigned fps = 12;
71 unsigned cell_size = 8;
72 bool show_grid = false;
73};
74
75namespace ca_html_detail {
76
77[[nodiscard]] inline std::string html_escape(const std::string &s)
78{
79 std::string out;
80 for (const char c : s)
81 switch (c)
82 {
83 case '&': out += "&amp;"; break;
84 case '<': out += "&lt;"; break;
85 case '>': out += "&gt;"; break;
86 case '"': out += "&quot;"; break;
87 default: out.push_back(c); break;
88 }
89 return out;
90}
91
92[[nodiscard]] inline std::string rgb_hex(const RGB8 c)
93{
94 const char digits[] = "0123456789abcdef";
95 std::string out = "#000000";
96 out[1] = digits[c.r >> 4];
97 out[2] = digits[c.r & 0x0f];
98 out[3] = digits[c.g >> 4];
99 out[4] = digits[c.g & 0x0f];
100 out[5] = digits[c.b >> 4];
101 out[6] = digits[c.b & 0x0f];
102 return out;
103}
104
105struct Frame
106{
107 std::size_t step = 0;
111};
112
113inline void write_frame_json(std::ostream &out, const Frame &f)
114{
115 out << "{\"step\":" << f.step << ",\"width\":" << f.width
116 << ",\"height\":" << f.height << ",\"pixels\":[";
117 for (std::size_t i = 0; i < f.pixels.size(); ++i)
118 {
119 if (i != 0)
120 out << ',';
121 out << '"' << rgb_hex(f.pixels(i)) << '"';
122 }
123 out << "]}";
124}
125
126} // namespace ca_html_detail
127
132template <typename Mapper>
134{
135 std::filesystem::path path_;
139
140public:
146 HTML_Player_Frame_Sink(std::filesystem::path path,
149 : path_(std::move(path)), mapper_(std::move(mapper)), opts_(std::move(opts))
150 {}
151
157 template <typename Lattice>
158 void accept(const std::size_t step, const Lattice &frame)
159 {
160 static_assert(LatticeLike<Lattice>, "HTML_Player_Frame_Sink requires LatticeLike frames");
161 static_assert(Lattice::rank == 2, "HTML_Player_Frame_Sink requires rank-2 frames");
162 using coord_t = typename Lattice::coord_type;
163
165 f.step = step;
166 f.width = frame.size(1);
167 f.height = frame.size(0);
168 f.pixels.reserve(f.width * f.height);
169 for (ca_size_t r = 0; r < frame.size(0); ++r)
170 for (ca_size_t c = 0; c < frame.size(1); ++c)
171 f.pixels.append(mapper_(frame.at(coord_t{static_cast<ca_index_t>(r),
172 static_cast<ca_index_t>(c)})));
173 frames_.append(std::move(f));
174 }
175
179 void flush() const
180 {
181 if (const auto parent = path_.parent_path(); not parent.empty())
182 std::filesystem::create_directories(parent);
183 std::ofstream out(path_);
185 << "HTML_Player_Frame_Sink::flush: cannot open '" << path_.string() << "'";
186
187 const unsigned interval = opts_.fps == 0 ? 1000u : 1000u / opts_.fps;
188 out << "<!doctype html>\n<html lang=\"en\">\n<head>\n";
189 out << "<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n";
190 out << "<title>" << ca_html_detail::html_escape(opts_.title) << "</title>\n";
191 out << "<style>body{margin:0;font:14px system-ui;background:#111;color:#eee;display:grid;"
192 "place-items:center;min-height:100vh}main{width:min(96vw,980px)}canvas{image-rendering:pixelated;"
193 "background:#fff;max-width:100%;height:auto;border:1px solid #444}button{margin:.35rem .2rem;"
194 "padding:.35rem .6rem;background:#222;color:#eee;border:1px solid #555}output{margin-left:.7rem}</style>\n";
195 out << "</head>\n<body><main>\n<h1>"
196 << ca_html_detail::html_escape(opts_.title) << "</h1>\n";
197 out << "<canvas id=\"view\"></canvas><div><button id=\"play\">Play</button>"
198 "<button id=\"prev\">Prev</button><button id=\"next\">Next</button><output id=\"status\"></output></div>\n";
199 out << "<script>\nconst frames = [";
200 for (std::size_t i = 0; i < frames_.size(); ++i)
201 {
202 if (i != 0)
203 out << ',';
205 }
206 out << "];\n";
207 out << "const cellSize=" << opts_.cell_size << ", showGrid="
208 << (opts_.show_grid ? "true" : "false") << ", delay=" << interval << ";\n";
209 out << R"JS(
210const canvas=document.getElementById('view'),ctx=canvas.getContext('2d');
211const status=document.getElementById('status'),play=document.getElementById('play');
212let idx=0,timer=null;
213function draw(){
214 if(frames.length===0){status.value='no frames';return;}
215 const f=frames[idx]; canvas.width=f.width*cellSize; canvas.height=f.height*cellSize;
216 for(let r=0;r<f.height;r++) for(let c=0;c<f.width;c++){
217 ctx.fillStyle=f.pixels[r*f.width+c];
218 ctx.fillRect(c*cellSize,r*cellSize,cellSize,cellSize);
219 }
220 if(showGrid){ctx.strokeStyle='rgba(0,0,0,.18)';ctx.lineWidth=1;
221 for(let x=0;x<=f.width;x++){ctx.beginPath();ctx.moveTo(x*cellSize,0);ctx.lineTo(x*cellSize,canvas.height);ctx.stroke();}
222 for(let y=0;y<=f.height;y++){ctx.beginPath();ctx.moveTo(0,y*cellSize);ctx.lineTo(canvas.width,y*cellSize);ctx.stroke();}
223 }
224 status.value=`frame ${idx+1}/${frames.length} ยท step ${f.step}`;
225}
226function stop(){if(timer!==null){clearInterval(timer);timer=null;play.textContent='Play';}}
227play.onclick=()=>{if(timer===null){timer=setInterval(()=>{idx=(idx+1)%frames.length;draw();},delay);play.textContent='Pause';}else stop();};
228document.getElementById('prev').onclick=()=>{stop();idx=(idx+frames.length-1)%frames.length;draw();};
229document.getElementById('next').onclick=()=>{stop();idx=(idx+1)%frames.length;draw();};
230draw();
231)JS";
232 out << "</script>\n</main></body>\n</html>\n";
233 ah_runtime_error_if(not out) << "HTML_Player_Frame_Sink::flush: output stream failed";
234 }
235
239 [[nodiscard]] std::size_t size() const noexcept
240 {
241 return frames_.size();
242 }
243};
244
245template <typename Mapper>
248
249} // namespace CA
250} // namespace Aleph
251
252#endif // CA_HTML_PLAYER_H
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
Definition ah-errors.H:271
size_t size_t int32_t * out
Definition ca-c-api.h:120
Directory-backed frame sinks for CA trajectories.
File-format readers and writers for cellular-automata frames.
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
Sink that writes a self-contained HTML playback file.
void flush() const
Write the final self-contained HTML file.
void accept(const std::size_t step, const Lattice &frame)
Append one frame to the embedded trajectory.
HTML_Player_Frame_Sink(std::filesystem::path path, Mapper mapper, HTML_Player_Options opts={})
Build an HTML player sink.
std::size_t size() const noexcept
Return collected frame count.
Array< ca_html_detail::Frame > frames_
Lattice that adds boundary-aware access on top of a storage.
typename Storage::coord_type coord_type
static constexpr std::size_t rank
ca_size_t size() const noexcept
state_type at(const coord_type &c) const
Strict access: throws if c is out of range.
Storage + topology that carries the cell values.
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
std::string rgb_hex(const RGB8 c)
std::string html_escape(const std::string &s)
void write_frame_json(std::ostream &out, const Frame &f)
std::size_t ca_size_t
Unsigned size component used for extents and counts.
Definition ca-traits.H:63
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
STL namespace.
Options for generated HTML players.
bool show_grid
draw faint cell grid
unsigned cell_size
canvas pixel size per CA cell
unsigned fps
playback frames per second
std::string title
document title
RGB byte triplet used by PPM exporters.
Definition ca-io.H:85
std::uint8_t g
green channel
Definition ca-io.H:87
std::uint8_t b
blue channel
Definition ca-io.H:88
std::uint8_t r
red channel
Definition ca-io.H:86
gsl_rng * r
Dynamic array container with automatic resizing.
C++20 concepts for the Cellular Automata module.