// Mandelbrot set with progress display // // Renders an 80 x 24 ASCII image. The first row of the ANSI screen is // reserved for a progress indicator. constexpr int ImageWidth = 80; constexpr int ImageHeight = 24; constexpr int ScreenHeight = ImageHeight + 1; constexpr int MaxIterations = 24; constexpr int Scale = 64; constexpr int XMin = -2 * Scale; constexpr int XMax = 1 * Scale; constexpr int YMin = -1 * Scale; constexpr int YMax = 1 * Scale; constexpr int XRange = XMax - XMin; constexpr int YRange = YMax - YMin; constexpr int EscapeRadius = 2 * Scale; constexpr int EscapeRadiusSquared = 4 * Scale; using Screen = ansi::Screen; using Palette = String<10>; auto screen_begin = Screen::begin; auto screen_end = Screen::end; auto screen_clear = Screen::clear.outline(); auto screen_move_to = Screen::move_to.outline(); auto map_x = function_("map_x", "column") | define { auto column = var_("column").cast(); return_(XMin + column * XRange / (ImageWidth - 1)); }; auto map_y = function_("map_y", "row") | define { auto row = var_("row").cast(); return_(YMax - row * YRange / (ImageHeight - 1)); }; auto escape_iterations = function_("escape_iterations", "cx", "cy") | define { auto cx = var_("cx"); auto cy = var_("cy"); auto zx = (let_("zx") = s16{0}); auto zy = (let_("zy") = s16{0}); auto zx_squared = let_("zx_squared"); auto zy_squared = let_("zy_squared"); auto next_zx = let_("next_zx"); auto next_zy = let_("next_zy"); auto iterations = (let_("iterations") = 0); while_(iterations < MaxIterations) { // Keep the next multiplication inside the signed 16-bit range. if_((zx > EscapeRadius) || (zx < -EscapeRadius) || (zy > EscapeRadius) || (zy < -EscapeRadius)) { break_; }; zx_squared = zx * zx / Scale; zy_squared = zy * zy / Scale; if_(zx_squared + zy_squared > EscapeRadiusSquared) { break_; }; next_zx = zx_squared - zy_squared + cx; next_zy = (zx * zy / Scale) * 2 + cy; zx = next_zx; zy = next_zy; ++iterations; }; return_(iterations); }; auto shade = function_("shade", "iterations") | define { auto iterations = var_("iterations"); auto palette = (let_("palette") = " .:-=+*#%@"); return_(palette[iterations * 9 / MaxIterations]); }; auto show_progress = function_("show_progress", "row") | define { auto row = var_("row"); screen_move_to(0, 0); print("Rendering row "); print(row); print("/"); print(ImageHeight); print(" "); return_; }; auto render = function_("render") | define { screen_clear(); auto row = (let_("row") = 0); while_(row < ImageHeight) { show_progress(row + 1); // Move once per row. The pixels can then be written sequentially without // emitting an ANSI cursor-positioning sequence for every character. screen_move_to(0, row + 1); auto cy = (let_("cy") = map_y(row)); auto column = (let_("column") = 0); while_(column < ImageWidth) { auto cx = (let_("cx") = map_x(column)); auto iterations = (let_("iterations") = escape_iterations(cx, cy)); put(shade(iterations)); ++column; }; ++row; }; screen_move_to(0, 0); print("Rendering complete: "); print(ImageHeight); print("/"); print(ImageHeight); print(" "); return_; }; main_() { screen_begin(); render(); screen_end(); return_; };