/racytoscal

Primary LanguageJavaScript

rascalscape

rascalscape is an added graphic functionality to rascal which makes use of svg, html, css, cytoscape, chartjs and dagre.

An unit is a directory containing a rsc and a html file. An example is the unit simple which contains the files Graph.rsc and Graph.html.

module demo::simple::Graph
import Prelude;
import Rascalscape;
import util::Math;
     
public list[tuple[str name , Racytoscal::Position pos]] getPositions() = 
    [ <"LT", LT>, <"LC", LC>, <"LB", LB>
    , <"CT", CT>, <"CC", CC>, <"CB", CB>
    , <"RT", RT>, <"RC", RC>, <"RB", RB>
    ];
    
SVG cell(tuple[str name, Racytoscal::Position pos] p) = box(CC
            ,box(p.pos, text(500, 500, p.name), class="kernel" , shrink=0.4, strokeWidth=40)
         ,class="cell",height=1000, width=1000, strokeWidth=60); 
           
public str rows() {
    str output = svg( 2000, 2000
        ,box(LT, [cell(p)|p<-getPositions()]
               , svgLayout=grid(5, width=200, height=200)
               , viewBox=<0,0, 1000, 1000>
             )
         );    
    return output;
    } 
   
 public App def() { 
    str output = rows(); 
    App ap = app( |project://rascalscape/src/demo/simple/Graph.html|, <"attach", output>);
    return ap;
    } 

and

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Simple</title>
<script src="lib/racytoscal.js"></script>
<style>
text {
    font-style:italic;
    text-anchor:middle;
    font-size:160pt;
    dominant-baseline:middle;
    }
.cell{fill:steelblue;stroke:lightblue}
.kernel{fill:yellow;stroke:salmon}
rect{stroke-width:0; fill:none}
.htmlObject {
   display:table;
   height:100%;
   margin:auto;
   }
title {display:block}
</style>
</head>
<body>
<div id='attach'>
</div>
<script src="init"></script>
</body>
</html>

The line <script src="init"></script> is mandatory. This script runs the update defined in the rsc file. The user must call the function app and assign the returned value to a variable ap of type App. The browser will be opened with a connection to the defined html file updated with the generated html string output by entering the command ap.serve(). The html string output will be placed into the <div id='attach'> part standing in the html file. The connection will be closed by entering the command ap.stop(). So in console:

rascal> import demo::simple::Graph;
rascal> ap=def();
rascal> ap.serve();
rascal> ap.stop();

The belonging picture is stored in Simple.png.

The line box(LT, [cell(p)|p<-getPositions()],svgLayout=grid(5, width=200, height=200),viewBox=<0,0, 1000, 1000>) in the rsc file defines a grid in which each row contains at most 5 cells. Each cell has width 200 and height 200. So the whole grid has width 1000 end height 400 because of there are in this case totally 9 cells.

Command app

App app(loc html, Script contents...,loc site = |http://localhost:8081|
   , bool display = true 
    ,React click = <[], nullCallback>
    ,React keypress = <[], nullCallback>
    ,React change = <[], nullCallback>
   , Callback tapstart = nullCallback
    ,Callback tapend =  nullCallback
    ,Callback tap = nullCallback
    ,Callback load =  nullCallback
    ,Callback timer = nullCallback
    )

where html is the location of the belonging html file ,contents is the list of tuples <container, definition>. The field of type str container refers to the position in the html file where the definition must be placed. The field definition can have the type str, CytoScape, or Chart. When display = true, the browser will be opened automatically, otherwise not. With click, keypress, change, load and timer you can define callbacks in html.
With tap, tapend, tap you can define callbacks in cytoscape. Function app returns a data type containing the methods serve and stop for starting and stopping the server.

Interaction

str(str) callback is a function which receives URL path and returns an update command of the browser client. Such an update command will be generated by str update(...). Here follows a simple example of a rascal console defined in the browser.

module demo::eval::Graph
import Prelude;
import Rascalscape;
import util::Eval;
    
str action(str s) {
   list[str] v = split("/", s); 
   str expr = v[-1];
   str result = "<eval(expr+";")>";
   return update(html=[<"result", result>]);
   }
   
 public App def() {  
    App ap = app( |project://rascalscape/src/demo/eval/Graph.html|
      ,change= <["enter-field"], action>);
    return ap;
    }

and

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Eval</title>
<script src="lib/racytoscal.js"></script>
<style>
title {display:block}
</style>
</head>
<body>
<div id="enter">
    <span id = "enter-span">
    <label for="interest-field">Enter: </label>
    <input type="text" id="enter-field" size=40>
    </span>
 </div>
<div id="result"></div>
<script src="init"></script>
</body>
</html>

When the value of the input field has been changed the callback action will be triggered with argument path. The last part of path contains the rascal expression which needs to be evaluated. The result will be send back to the client via update. In app( |project://racytoscal/src/demo/eval/Graph.html|,change= <["enter-field"], action>) will the callback defined. change has type React which is an alias for tuple[list[str] ids, Callback callback] which identifies the widgets on which the change callback must be triggered.

SVG Commands

  • svg(int width , int height, SVG content ..., ViewBox viewBox= <0, 0, width, height>) translates content of type list[SVG] with attributes widthheight, and viewBox. into html code stored in a string. This string can be used into the arguments of app.
   SVG box(Position pos, SVG inner ..., str id = "", str class = "", str style = ""
              ,num vshrink = 1, num hshrink = 1, shrink = 1, num strokeWidth = 2
              ,num width=1000, num height=1000
              ,ViewBox viewBox=<0, 0, 1000, 1000>
              ,Dim padding = pxl(<0,0,0,0>)
              ,SVGLayout svgLayout = overlay())

where pos is the position with respect to the viewBox of the outer SVG figure, width and height are the dimensions with respect to the viewbox of the outer figure inner is the list of inner figures, hshrink the relative size with respect to the width of the outer SVG figure, vshrink the relative size with respect to the height of the outer SVG figure, strokeWidth the width of the stroke with respect to the viewPort of the outer figure, viewBox will be applied to each inner figure, padding applied to each inner figure,
if svgLayout = overlay() then the inner figures will be overlayed,
if svgLayout = grid(ncols, width=-1, height=-1) then the inner figure will be laid next to each other (after ncols a new row will be started), the result is a rect with attributes widthheight, and viewBox on position pos. If width and height are defined in grid then each cell has width width and height height. The total width of the grid ncols*width and ceil(size(inner)/ncols)*height.

  • SVG ellipse has the same parameters as box. There are no parameters rx and ry.
  • SVG text(num x, num y, str txt, str id= "", str class= "", str style= "") places txt in position (x,y) with respect to the viewbox of the parent figure.
   SVG htmlObject(Position pos, str html, str id= "", str class= "", str frameClass = "",
   str style="", int width=1000, int height=1000, num vshrink = 1.0, num hshrink = 1.0, 
   num shrink = 1.0, int strokeWidth=2)

places the html code defined in string html in position pos with respect to the viewbox of the parent.

  • SVG path(str graph, str id= "", str class= "", str style= "") adds the svg path definition (as defined in the format after path -d) in string graph . Coordinates are relative to the viewbox defined in the parent.

Coord

The x- coordinate of a figure has the format:

  • L(x) stands for left x of a box
  • C(x) stands for center x of a box
  • R(x) stands for right x of a box

The y- coordinate of a figure has the format:

  • L(y) stands for top y of a box
  • C(y) stands for center y of a box
  • R(y) stands for bottom y of a box

L1, C1 and, R1 are the relative versions of L, C, and R.

Position

Is a tuple consisting of a x- coordinate and a y- coordinate. The abbreviations:

  • LT stands for <L1(0), L1(0)> (LeftTop)
  • LC stands for <L1(0), C1(0.5)> (LeftCenter)
  • LB stands for <L1(0), R1(1)> (LeftBottom)
  • CT stands for <C1(0.5), L1(0)> (CenterTop)
  • CC stands for <C1(0.5), C1(0.5)> (CenterCenter)
  • CB stands for <C1(0.5), R1(1)> (CenterBottom)
  • RT stands for <R1(1), L1(0)> (RightTop)
  • RC stands for <R1(1), C1(0.5)> (RightCenter)
  • RB stands for <R1(1), R1(1)> (RightBottom)

Dim

Is an abstract data type consisting of constructors pxl en pct.

  • pxl(dim) the size in pixels
  • pct(dim) the size in procents
  • pxl(padding)
  • pct(padding) padding is the tuple <top, right, bottom, left>

Cytoscape

  • The main abstract data type is CytoScape which contains the data type NodeShape, EdgeShape, Layout, and Style.

Here follows an example which generates a tree.

module demo::tree::Graph
import Rascalscape;
import Cytoscape;
import Prelude;

public App def() {
    lrel[str, str] rl  = [
     <"brown","brown_green">,<"brown","brown_grey">,<"brown","brown_black">         
    ,<"brown_grey","brown_grey_blue">,<"brown_grey","brown_grey_red">
    ,<"brown_black","brown_black_orange">
    ];
    println([split("_", i)[-1]|str i<-dup(carrier(rl))]);
    list[Ele] edges = [e_("<a[0]><a[1]>", a[0], a[1])|a<-rl];
    list[Ele]  nodes = 
         [n_(i
              ,style=style(
                 borderColor=split("_", i)[-1]
                 ,color=split("_", i)[-1]
                 ,label=label(split("_", i)[-1] ,vAlign="center")
                ))
            |str i<-dup(carrier(rl))
         ];
    Cytoscape cy = cytoscape(elements= nodes+edges
       ,styles = [<"edge", style(  
                    curveStyle=taxi(taxiDirection=downward()),
                    arrowShape=[
                      ArrowShape::triangle(
                         arrowScale=2, arrowColor="red", pos = target())
                      ]
                      ,lineColor="blue"
                      )
                   >
                  ,<"node", style(
                     width = "15px"
                    ,height= "15px" 
                    ,backgroundColor="antiquewhite"
                    ,shape=NodeShape::ellipse()
                    ,borderWidth = "2" // , borderColor="brown"
                    ,padding = "10" 
                    ,fontSize= "8pt"
                    ,textOpacity=1
                  )>
                  ]
          ,\layout = dagre("")
        ); 
    return app(|project://rascalscape/src/demo/tree/Graph.html|, <"cy", cy>);  
    }

The belonging .html file is:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<title>index</title>
<script src="lib/cytoscape.umd.js"></script>
<script src="lib/dagre.min.js"></script>
<script src="lib/cytoscape-dagre.js"></script>
<script src="lib/racytoscal.js"></script>
<style>
#cy {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 999;
}
title {display:block}
</style>
</head>
<body>
<h2>Tree</h2>
<div id='cy'>
</div>
<script src="init"></script>
</body>
</html>

The result is found in Tree.png

Chart

rascalscape provides an interface to chartjs. An example of a histogram follows here.

module demo::amsterdam::Chart
import Prelude;
import Rascalscape;
import Char;

public App def() { 
    Config conf() = config(\type="bar"
       , \data=\data(
              labels = ["2014","2015","2016","2017","2018"]
              ,datasets= [
                dataSet(
                 \label="alleenstaand"
                  ,backgroundColor= "yellow"
                  ,\data=Points::vec([232762,235152,239179,242253,246378])
                  )
               , dataSet(
                 \label="samenwonend0"
                  ,backgroundColor= "lightblue"
                 ,\data=vec([87701,89826,91984,93748, 95647]) 
                  )
               , dataSet(
                 \label="samenwonend1"
                  ,backgroundColor= "blue"
                 ,\data=vec([71097,71902,72733,73116,73670])
                  )
             , dataSet(
                 \label="eenoudergezin"
                 ,backgroundColor= "beige"
                ,\data=vec([39452,39727,39920,40441,40651]) 
                ) 
              , dataSet(
                 \label="overig"
                  ,backgroundColor= "lightgreen"
                 ,\data=vec([5926,6086,6232,6429,6238]) 
                  )
               ] 
               )                                    
       , options=options(title=title(display=true,text="Amsterdam")
            ,scales = scales(xAxes=[
                axis(
                   stacked = true                 
                    )]
               , yAxes = [
                  axis(
                      stacked = true
                  )
               ]
            )             
            )
       );
    App ap = app(|project://racytoscal/src/demo/amsterdam/Chart.html|
            , <"attach", conf()>
            );
    return ap;
    }

and

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Chart</title>
<script src="lib/cytoscape.umd.js"></script>
<script src="lib/dagre.min.js"></script>
<script src="lib/cytoscape-dagre.js"></script>
<script src="lib/racytoscal.js"></script>
<script src="lib/Chart.bundle.min.js"></script>
<style>
div:{width:400px;height:400px}
</style>
<body>
<div id="attach">
</div>
<script src="init"></script>
</body>
</html>

The result is found in Chart.html

Frame

The command

SVG frame(num hshrink, num vshrink, tuple[str \class, list[str] d] xAxe, tuple[str className, list[str] d] yAxe
    , tuple[str className, lrel[num x, num y] d] graphs... ,ViewBox viewBox =<0, 0, 1000, 1000>,
    num width = 1000, num height = 1000)

displays a set of graphs in a coordinate system. hshrink and vshrink defines the proportion of the relative sizes, xAxe and yAxe are lists of labels, to each axe must be assigned a class name. graphs are a set of relations, where each relation defines a graph. To each graph must be assigned a class name. An example of this:

public void main() {
     num step = 0.1;
     int width =1600, height = 800;
     str output = svg(width, height, 
       box(CC
          ,box(LT, frame(1, 1, <"x-axe", ["<i>"|num i<-[0,0.1..1]]>
                        ,<"y-axe", ["<i>"|num i<-[0,0.1..1]]>
                        ,[<"left" ,[<0, x>, <1-x, 0>]>|num x<-[0,0.01..1]]
                        +[<"right",[<1, x>, <1-x, 1>]>|num x<-[0,0.01..1]]
                        ,viewBox = <0, 0, 1, 1>, width = width, height = height
                       )
          ,ellipse(CC, text(500, 500, "Hallo", class="text"), shrink = 0.3, id="sign", strokeWidth=16)
         ,strokeWidth = 2
        )
        ,
        box(LT, frame(1, 1/PI(),<"x-axe", ["0","\u03C0/2","\u03C0","3\u03C0/2","2\u03C0"]>
                               ,<"y-axe", ["-1","0","1"]>
                               ,<"sin",[<x, sin(x)>|num x<-[0,step..2*PI()+step]]>
                               ,<"cos",[<x, cos(x)>|num x<-[0,step..2*PI()+step]]>
                      ,viewBox = <0, -1, 2*PI(), 2>)
           ,strokeWidth=2)
        ,shrink = 0.8, strokeWidth = 0, svgLayout=grid(2)
        )
     );
     App app = app(|project://rascalscape/src/demo/frame/Graph.html|, <"attach", output>); 
     }

The belonging .html:

<!DOCTYPE html>
<html>
<head>
<title>Frame</title>
<style>
path.x-axe{stroke-width:0.01;stroke:lightgrey}
path.y-axe {stroke-width:0.01;stroke:lightgrey}
rect{fill:none; stroke:black}
rect.x-axe{fill:none; stroke-width:0; stroke:black}
rect.y-axe{fill:none; stroke-width:0; stroke:black}
.frame{fill:antiquewhite; stroke:red}
.inner-tst{fill:lightblue; stroke:orange}
.text {
    fill:darkblue;
    font-size:120pt;
    font-style:italic;
    }
text {
    font-style:italic;
    text-anchor:middle;
    dominant-baseline:middle;
    }
text.x-axe {
    fill:darkblue;
    font-size:12pt;
    }
text.y-axe {
    fill:darkblue;
    font-size:12pt;
    }
 #sign {
    stroke: orange;
    fill:yellow;
    }
 .sin {
      stroke-width:0.01;
      stroke:green;
      fill:none
       }
 .cos {
      stroke-width:0.01;
      stroke:red;
      fill:none
       }
 .left {
      stroke-width:0.001;
      stroke:green;
      fill:none
       }
 .right {
      stroke-width:0.001;
      stroke:red;
      fill:none
       }
 .axe {
      stroke:green;
      fill:none
     }
title {display:block}
</style>
</head>
<body onunload="handleOnClose()">
<div id='attach'>
<script src="init"></script>
</div>
</body>
</html>

The result is found in Frame.png