Line graphics in a HTML client

Can someone give me some hint, how to implement line graphics through a HTML client? Maybe some example code? (I am not at all a Web and HTML guru…)
yours: Imre

What kind of line graphics? Do you want to be able to draw lines with your HTML client?

For graphics in the browser the two naive methods are either using a canvas or SVG.

Thanx, inbetween I have dug it out… Unfortunately html//1 doesn’t interpret svg tags, I needed to define html_write:layout/3 facts for them… An example code could have been a serious help… or should I send my program in a distilled form for Annie to extend her tutorial?

I have not seen Annie active with anything related to Prolog online for over a year now. She might be enjoying her retirement.

The reason I did not answer this earlier is that Line graphics covers so much that it is hard to give a reasonable answer, e.g. you could be trying to create lines for forms for users to enter data.

As my main needs for graphics with Prolog were for graphs (nodes and edges), years ago I use to use GraphViz but the images were static, a user could not interact with the image. A step up from that is PlantUML but still the images are static because they are generated using GraphViz. Today I prefer Cytoscape.js . (ref)

There are some topics on this site related to these but I don’t know if these are what you need so can’t say much more.

It worked for me out of the box. With the examples from the SVG link above and the HOWTO I got the following:

$ cat draw_circle.pl 
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/html_write)).

:- http_handler(root(draw_circle), draw_circle, []).

server(Port) :-
    http_server(http_dispatch, [port(Port)]).

draw_circle(_Request) :-
    reply_html_page(title('SVG circle'),
            [ svg([viewBox([0,0,800,600])],
                [ circle(
                    [ cx(400),
                      cy(200),
                      r(180),
                      stroke(green),
                      'stroke-width'(4),
                      fill(yellow)
                    ],
                    [ ]),
                  line(
                    [ x1(100),
                      y1(400),
                      x2(700),
                      y2(550),
                      stroke(blue),
                      'stroke-width'(4)
                    ],
                    [ ]),
                  line(
                    [ x1(100),
                      y1(550),
                      x2(700),
                      y2(400),
                      stroke(blue),
                      'stroke-width'(4)
                    ],
                    [ ])
                ])
            ]).

I then load it and start the server locally:

$ swipl -q draw_circle.pl
?- server(8000).
true.

… and finally I load the page in a browser at http://localhost:8000/draw_circle.

1 Like

Yes but this was not the question, at least not obviously so. If we need interactive graphics in the browser we will be using Javascript in some form?

SWI-Prolog’s HTML framework generates HTML using a DCG in “reverse” (serializing) mode. That can, as @Boris showed, also generate SVG. Indeed, there are no “layout” statements for the SVG tags and thus the result is compact but hard to read. All tricks are allowed, and thus generating a grid of circles shouldn’t be too hard for a Prolog programmer. Nice recursion should do :slight_smile: It is quite common to generate HTML from backtrackable Prolog predicates though (e.g., generate a table with a row for each answer). That is why I added the DCG high order library. No need to use it, but it does work nicely with the HTML framework. The implementation is simple plain Prolog, so any system can copy the DCG high order library :slight_smile:

The foreach//2 and foreach//3 implementation in library(dcg/high_order) uses findall/3.

1 Like

I guess two nested foreach//2 constructs will do the trick. Possibly more elegant would be a DCG variant of the ECLiPSe logical loops? The real Prolog way is of course simply to recurse over the rows and inside that recurse of the columns. It is a bit verbose though :frowning:

So, when I’m lazy, I’d go for this. Not blindly fast, but easy to read and fast enough considering what needs to happen elsewhere for generating and rendering.

foreach(between(1,Rows,Y), 
        foreach(between(1,Cols,X),
                circle(X,Y))).

I continue to be confused about this topic (nested logical loops). I used to think that there is no need for nesting, like:

?- phrase(
       foreach(( between(1,2,X), between(1,3,Y) ),
           html(p([span(X), -, span(Y)]))), HTML), print_html(HTML).


<p>
<span>1</span>-<span>1</span></p>

<p>
<span>1</span>-<span>2</span></p>

<p>
<span>1</span>-<span>3</span></p>

<p>
<span>2</span>-<span>1</span></p>

<p>
<span>2</span>-<span>2</span></p>

<p>
<span>2</span>-<span>3</span></p>
HTML = [nl(2), <, p, >, nl(1), <, span, >, 1|...].

So this is now a very rudimentary solution for @j4n_bur53 :

$ cat draw_circles.pl 
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/html_write)).
:- use_module(library(http/http_parameters)).
:- use_module(library(dcg/high_order)).

:- http_handler(root(draw_circles), draw_circles, []).

server(Port) :-
    http_server(http_dispatch, [port(Port)]).

centers(Rows, Cols, X, Y) :-
    between(1, Rows, Y),
    between(1, Cols, X).

svg_circles(Rows, Cols) -->
    foreach(( centers(Rows, Cols, X0, Y0), X is 100*X0, Y is 100*Y0 ),
        html(circle([cx(X), cy(Y), r(40), stroke(green), fill(yellow)], []))).

draw_circles(Request) :-
    http_parameters(Request, [rows(Rows,[nonneg]), cols(Cols, [nonneg])]),
    reply_html_page(title('SVG circles'),
        [ svg([viewBox([0,0,800,600])],
            [\svg_circles(Rows, Cols)])
        ]
    ).

and then again

$ swipl -q draw_circles.pl 
?- server(8000).
true.

and go to, say, http://localhost:8000/draw_circles?rows=3&cols=5. I kept it too simple so it only works for some small numbers. But I guess the points are:

  • just use library(dcg/high_order)
  • I still don’t understand nested loops
1 Like

Nested loops were not so much discussed in detail. It was only about loops.
Its enough to demonstrate “each block” by showing how to do one loop.

Since it is a structured construct, if you can do one loop, you can also
do two loops etc… Means you can put them into each other like a Matryoshka dolls:

"each block"
    [...]
    "each block"
    [...]

In Prolog if the [...] before and after the inner “each block” is empty,
and if the “each block” is driven by a generator, you can indeed

use only a single “each block”, where two generators are joined by
Prolog conjunction. Thats kind of a special case of a more general scheme.

Edit 01.08.2023
For example if you would make a “table” grid of svg pictures,
you would possibly use two “each block” because you have to generate
row tags “tr” and cell tags “td”. If you have time at your hand, you could try that.

Two “each blocks” gives kind of preview view of the upcoming result:

<table>
"each block"
    <tr>
    "each block"
         <td>..</td>
    </tr>
</table>

The template code corresponds to the output case rows=1 and cols=1.

1 Like

I don’t recall all details. I think there is an xhtml mode, but luckily xhtml seems dead :slight_smile: I think that in xhtml mode it always used <tag .../>. Otherwise it uses <tag></tag> unless tag is know to not allow for content. This surely satisfies classic html and html5. I’m not sure out SVG. Isn’t that an XML application? Anyway, XML allows for <tag></tag>, so worst case it is a little verbose.

I read something different. Quoting the document:

In HTML, a void element must not have an end tag. For example, <input type="text"></input> is invalid HTML. In contrast, SVG or MathML elements that cannot have any child nodes may use an end tag instead of XML self-closing-tag syntax in their start tag.

So, both <circle .../> and <circle ...></circle> are valid. That is what one would expect in XML that can be parsed without a DOCTYPE declaration and thus the parser may not know which elements are void and which may contain content. Next

Self-closing tags (<tag />) do not exist in HTML.

So, I think we are ok, be it more verbose than needed on SVG. It is an HTML generating framework :slight_smile: Updated the list of official void elements (<source>, <track> and <wbr> were missing).