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.
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.
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(int width , int height, SVG content ..., ViewBox viewBox= <0, 0, width, height>)
translates content of type list[SVG] with attributes width, height, and viewBox. intohtml
code stored in a string. This string can be used into the arguments ofapp
.
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 width, height, 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 asbox
. 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 afterpath -d
) in string graph . Coordinates are relative to the viewbox defined in the parent.
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.
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)
Is an abstract data type consisting of constructors pxl en pct.
pxl(dim)
the size in pixelspct(dim)
the size in procentspxl(padding)
pct(padding)
padding is the tuple <top, right, bottom, left>
- The main abstract data type is
CytoScape
which contains the data typeNodeShape
,EdgeShape
,Layout
, andStyle
.
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
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
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