From 70a2b11271ccb36bee3d800d4ed4cd2c17cb4eaa Mon Sep 17 00:00:00 2001 From: markjreed Date: Mon, 30 Sep 2024 20:00:04 -0400 Subject: [PATCH] New example program: draw a fractal tree (#152) * demo of prog8 recursion to draw a fractal tree * feat: comments * fix: comment formatting * feat: make minimum branch size a tunable parameter --- examples/fractal-tree.p8 | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 examples/fractal-tree.p8 diff --git a/examples/fractal-tree.p8 b/examples/fractal-tree.p8 new file mode 100644 index 000000000..e100d731b --- /dev/null +++ b/examples/fractal-tree.p8 @@ -0,0 +1,79 @@ +; Draw a fractal tree recursively +; This is an example of doing recursion with local per-call state that has to +; be preserved across recursive calls, as opposed to queens.p8 where the +; recursive function only modifies global state. +; +; Note: this program can be compiled for multiple target systems. +%import floats +%import graphics +%zeropage basicsafe + +main { + ; Adjust these parameters to change the appearance of the tree: + ; 1. the angle of each branch away from its parent (left and right) + const float delta_theta = floats.π / 6 + + ; 2. the size of each branch relative to its parent + const float shrink_factor = 2.0 / 3.0 + + ; 3. the size of the smallest branch (in pixels) + const float minimum_branch = 1.0 + + ; These stacks hold copies of the parameters during recursive calls + uword[11] x + ubyte[11] y + float[11] theta + float[11] length + + ; This points to the current level of stack in use + ubyte stack_pointer + + ; draw a branch starting at (x1, y1) at angle th1 with length len1 + ; recurses if length is large enough + sub draw_branch(uword x1, ubyte y1, float th1, float len1) { + + uword x2 = (x1 as float + len1 * floats.cos(th1)) as uword + ubyte y2 = (y1 as float - len1 * floats.sin(th1)) as ubyte + graphics.line(x1, y1, x2, y2) + + if len1 >= minimum_branch { + ; push parameters onto stacks + x[stack_pointer] = x1 + y[stack_pointer] = y1 + theta[stack_pointer] = th1 + length[stack_pointer] = len1 + stack_pointer += 1 + + draw_branch(x2, y2, th1 - delta_theta, len1 * shrink_factor) + + ; recover parameters after recursive call + x1 = x[stack_pointer - 1] + y1 = y[stack_pointer - 1] + th1 = theta[stack_pointer - 1] + len1 = length[stack_pointer - 1] + + x2 = (x1 as float + len1 * floats.cos(th1)) as uword + y2 = (y1 as float - len1 * floats.sin(th1)) as ubyte + + draw_branch(x2, y2, th1 + delta_theta, len1 * shrink_factor) + + ; pop stack level + stack_pointer -= 1 + } + } + + sub start() { + graphics.enable_bitmap_mode() + + draw_branch(graphics.WIDTH / 2, graphics.HEIGHT - 1, + floats.π / 2, + graphics.HEIGHT / 2 * shrink_factor) + + + ; done rendering; wait for a key before exiting + do { + ubyte key = cbm.GETIN2() + } until key != 0 + graphics.disable_bitmap_mode() + } +}