"""Escape-time fractals, drawn as text.""" import math def escape(z, c, limit): for i in range(limit): if z.real * z.real + z.imag * z.imag > 4.0: return i z = z * z + c return limit def render(preset, width=78, height=28): """The preset's view as lines of text, darker where it escapes later.""" kind = preset.get("kind", "mandelbrot") cx, cy = preset["center"] span = preset["span"] limit = preset.get("iterations", 80) shades = preset["palette"] julia = complex(*preset["c"]) if kind == "julia" else None # Characters are about twice as tall as they are wide. sx = span / width sy = span * 2 / width rows = [] for y in range(height): row = [] for x in range(width): p = complex(cx + (x - width / 2) * sx, cy + (y - height / 2) * sy) n = escape(p, julia, limit) if julia is not None else escape(0j, p, limit) row.append(shades[-1] if n == limit else shade(shades, n, limit)) rows.append("".join(row)) return rows def shade(shades, n, limit): # On a log scale: most points escape in the first few steps. return shades[int(math.log1p(n) / math.log1p(limit) * (len(shades) - 1))]