In this example we cover various aspects of the Dogelog Player integration with the browser, such as library(markup) and library(vector). At the same time the example delivers an exercise in dynamic database and recursion.
A Prolog system maintains a state through their input, output and error streams. In the case of a browser environment the corresponding output and error streams point to DOM elements, which can be simultaneously set via set_cursor().
:- current_output(S), write(S, 'Hello World!'), nl(S).
The error stream will also do some text coloring:
:- current_error(S), write(S, 'Hello World!'), nl(S).
There are also factory predicates in library(markup) to create more such markup streams. Markup streams act on their cursor elem as defined in library(markup):
Prolog Action write('foo < bar') elem.insertAdjacentText("beforeend", "foo < bar") tag('&') elem.insertAdjacentHTML("beforeend", "&") tag('<foo>') elem.insertAdjacentHTML("beforeend", "<foo></foo>"); elem = elem.lastElementChild tag('</foo>') elem = elem.parentElement tag('<foo/>') elem.insertAdjacentHTML("beforeend", "</foo>")
This allows for complex dynamic HTML output:
:- ensure_loaded(library(misc/markup)). bullets(L, H) :- tag('<ol>'), (between(L, H, I), tag('<li>'), write('List item '), write(I), tag('</li>'), fail; true), tag('</ol>'). :- bullets(1, 3).
This file provides convenience predicates to generate the markup for scalable vector graphics (SVG). The predicates require a markup writer as found in library(markup), either explicit as a parameter or implicit as current output. We can define a turtle drawing area:
:- ensure_loaded(library(misc/vector)). begin :- retractall(current_angle(_)), assertz(current_angle(0)), retractall(current_position(_,_)), assertz(current_position(50,350)), svg_begin([style('stroke: peru; fill: peachpuff')]). end :- svg_end. test :- begin, end. :- test.
The SVG output area will have 500x400 SVG coordinate dimension and a twelfth of it in relative font size as browser dimension. We can draw a little arrow to indicate the location and orientation of the turtle:
show :- current_position(X, Y), current_angle(A), svg_group_begin(X, Y, A), svg_path(['M', 5, 0, 'L', -2.5, -5, 'Q', -5, 0, -2.5, 5, 'Z'], []), svg_group_end. test2 :- begin, show, end. :- test2.
We can realize turtle commands by modifying the dynamic database. There is a command turn/1, which affects the orientation of the turtle, and there is a command move/1 which affects the location of the turtle, by traveling a distance along the current orientation:
turn(D) :- retract(current_angle(A)), !, A2 is A+D, assertz(current_angle(A2)). move(D) :- retract(current_position(X1,Y1)), !, current_angle(A), X2 is X1+D*cos(A), Y2 is Y1+D*sin(A), assertz(current_position(X2,Y2)). test3 :- begin, move(200), turn(-pi/2), move(100), show, end. :- test3.
Besides modifying the dynamic database we can also affect the browser DOM. Here the line/1 command works like the move/1 command, except that the turtle will leave a trace in the drawing area:
line(D) :- retract(current_position(X1,Y1)), !, current_angle(A), X2 is X1+D*cos(A), Y2 is Y1+D*sin(A), assertz(current_position(X2,Y2)), svg_line(X1, Y1, X2, Y2, []). test4 :- begin, line(200), turn(-pi/2), line(100), show, end. :- test4.
A popular visualization of recursion are fractal curves. The basic element of the Sierpinski courve is a triangle. It can be arranged that the turtel returns to its original location and orientation:
sierpinski(0, D) :- !, line(D), turn(-pi*2/3), line(D), turn(-pi*2/3), line(D), turn(pi*4/3). test5 :- begin, sierpinski(0, 400), show, end. :- test5.
We can repeat the process recursively, putting a triangle into the top of the triangle, the left bottom of the triangle and the right bottom of the triangle. Amazingly it was again arrange that the turtle returns to its original location and orientation:
:- discontiguous sierpinski/2. sierpinski(L, D) :- D2 is D/2, L2 is L-1, sierpinski(L2, D2), move(D2), sierpinski(L2, D2), turn(-pi*2/3), move(D2), turn(pi*2/3), sierpinski(L2, D2), turn(pi*2/3), move(D2), turn(-pi*2/3). test6 :- begin, sierpinski(1, 400), show, end. :- test6.
If we repeat the process more than once, we start seeing the Sierpinski fractal. Since markup streams immediately update the DOM model and the underlying Prolog system automatically yields, the browser will reflect the SVG changes and we can watch how the fractal is drawn:
test7 :- begin, sierpinski(7, 400), end. :- test7.