43#ifndef CA_HTML_PLAYER_H
44#define CA_HTML_PLAYER_H
69 std::string
title =
"Aleph::CA trajectory";
75namespace ca_html_detail {
80 for (
const char c : s)
83 case '&':
out +=
"&";
break;
84 case '<':
out +=
"<";
break;
85 case '>':
out +=
">";
break;
86 case '"':
out +=
""";
break;
87 default:
out.push_back(c);
break;
94 const char digits[] =
"0123456789abcdef";
95 std::string
out =
"#000000";
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)
132template <
typename Mapper>
157 template <
typename Lattice>
161 static_assert(
Lattice::rank == 2,
"HTML_Player_Frame_Sink requires rank-2 frames");
172 static_cast<ca_index_t>(c)})));
181 if (
const auto parent =
path_.parent_path();
not parent.empty())
182 std::filesystem::create_directories(parent);
185 <<
"HTML_Player_Frame_Sink::flush: cannot open '" <<
path_.string() <<
"'";
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";
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>"
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)
210const canvas=document.getElementById('view'),ctx=canvas.getContext('2d');
211const status=document.getElementById('status'),play=document.getElementById('play');
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);
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();}
224 status.value=`frame ${idx+1}/${frames.length} ยท step ${f.step}`;
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();};
232 out << "</script>\n</main></body>\n</html>\n";
245template <
typename Mapper>
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
size_t size_t int32_t * out
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.
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.
HTML_Player_Options opts_
Array< ca_html_detail::Frame > frames_
std::filesystem::path path_
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().
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.
Main namespace for Aleph-w library functions.
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.
std::uint8_t g
green channel
std::uint8_t b
blue channel
std::uint8_t r
red channel
Dynamic array container with automatic resizing.
C++20 concepts for the Cellular Automata module.