I would like to produce a diagram with DiagramR.
This is my code
require(DiagrammeR)
grViz("
digraph boxes_and_circles {
node [shape = box,
fontname = Helvetica]
A; B; C; D
B->A C->A D->B
}
")
But I get this result
And I need like this one
How can I set flow direction to up?
You could to set the parameter rankdir and add the color to edge and node:
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
rankdir = BT
node [shape = box,
fontname = Helvetica,
color = gray
]
A; B; C; D
edge [color = gray]
B->A C->A D->B
}
")
Related
I'm trying out DiagrammeR to build some flowcharts and I'm interested in knowing whether I can include values from a secondary data frame (data pulled from a SQL database).
I'm using an example from the documentation and I want to add a random value from the Iris dataset.
I wish to add iris[2,4] as a subtext to the ELEPHANT node in my code below, is this possible?
library(DiagrammeR)
grViz("
digraph {
# graph attributes
graph [overlap = true]
# node attributes
node [shape = box,
fontname = Helvetica,
color = blue]
# edge attributes
edge [color = gray]
# node statements
A; B; C; D; ELEPHANT
F [color = black]
# node attributes
node [shape = circle,
fixedsize = true,
width = 0.9]
# node statements
1; 2; 3; 4; 5; 6; 7; 8
# edge statements
A->1; B->2 // gray
B->3 [color = red] // red
B->4 // gray
C->A [color = green] // green
1->D; ELEPHANT->A; 2->4; 1->5; 1->F // gray
ELEPHANT->6; 4->6; 5->7; 6->7 // gray
3->8 [color = blue] // blue
}
")
I believe what I'm looking for is a parameterized diagram which is detailed here https://mikeyharper.uk/flowcharts-in-r-using-diagrammer/
You can achieve this by adding 2 simple steps, please see the example code and comments below:
library(DiagrammeR)
grViz("
digraph {
# graph attributes
graph [overlap = true]
# node attributes
node [shape = box,
fontname = Helvetica,
color = blue]
# edge attributes
edge [color = gray]
# node statements
A; B; C; D; ELEPHANT[label = '##1'] # Here calls label from 1 below
F [color = black]
# node attributes
node [shape = circle,
fixedsize = true,
width = 0.9]
# node statements
1; 2; 3; 4; 5; 6; 7; 8
# edge statements
A->1; B->2 // gray
B->3 [color = red] // red
B->4 // gray
C->A [color = green] // green
1->D; ELEPHANT->A; 2->4; 1->5; 1->F // gray
ELEPHANT->6; 4->6; 5->7; 6->7 // gray
3->8 [color = blue] // blue
}
[1]: paste0('iris[2, 4] is ', iris[2, 4]) # here is the label and value
")
You should be able to see the same result like this
I constructed a simple graph:
digraph{
rankdir = LR;
{
rank="source";
Sa;
Sb;
Sc;
St;
}
St -> {t_1[label="t",shape=plaintext];}
Na;
{t_a[label="t",shape=plaintext];}->Na
Sa->Na;
Sb->Na;
Sc->Na;
subgraph cluster_b {
fillcolor = "#ddDDdd";
style=filled;
label="";
Nb1;
Nb;
Nb1->Nb;
}
{t_2[label="t",shape=plaintext];}->Nb1
Sa->Nb;
Nc;
{t_c[label="t",shape=plaintext];}->Nc
Nd;
{t_d[label="t",shape=plaintext];}->Nd
Na->Nd;
Nb->Nc;
Nd->O1;
Nc->Nd;
{
rank="sink";
O1;
}
}
view online here
It seems that dot is ignoring the rank="source".
According to the documentation
If rank="min", all nodes are placed on the minimum rank. If
rank="source", all nodes are placed on the minimum rank, and the only
nodes on the minimum rank belong to some subgraph whose rank attribute
is "source" or "min".
The Sx nodes should be the only ones on the lowest ranks.
(as if there would be an additional St->t_2[style=invis]; edge).
Is this a bug? do i misunderstand the documentation?
I vote "bug" (probably in software, maybe in documentation).
Here is a work-around, using the minlen attribute to "force" the Nb node (and the cluster) "down" the ranks.
digraph{
rankdir = LR;
{
rank="source";
Sa;
Sb;
Sc;
St;
}
St -> {t_1[label="t",shape=plaintext];}
Na;
{t_a[label="t",shape=plaintext];}->Na
Sa->Na;
Sb->Na;
Sc->Na;
subgraph cluster_b {
fillcolor = "#ddDDdd";
style=filled;
label="";
Nb1;
Nb;
Nb1->Nb;
}
{t_2[label="t",shape=plaintext];}->Nb1
Sa->Nb [minlen=3] // will this move the cluster??
Nc;
{t_c[label="t",shape=plaintext];}->Nc
Nd;
{t_d[label="t",shape=plaintext];}->Nd
Na->Nd;
Nb->Nc;
Nd->O1;
Nc->Nd;
{
rank="sink";
O1;
}
}
Giving:
The following code creates a flowchart in R with the DiagrammeR package. How can I add "Yes" or "No" (or any other text) to the flowlines connecting the shapes? When creating charts, my nodes will often be decisions, and I'd like to label the flowline answer as Yes/No.
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 B->2 B->3 B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
Try to define the label for each flowline, like this:
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 [label='YES']
B->2 [label='NO']
B->3 [label='...']
B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
Hope it helps! :)
I am trying to use a GraphViz graph in DiagrammeR. How can I do this?
myGraph = grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 B->2 B->3 B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
and then I want to use it in DiagrammeR, but it will not allow it.
render_graph(myGraph)
Gives:
Error: class(graph) == "dgr_graph" are not all TRUE
Is there a way I need to convert or input the GraphViz graph into the DiagrammeR environment?
grViz takes a string describing the graph (vis.js style) : it is the interpreted by vis.js. Its return value is then an htmlwidget object.
render_graph takes a dgr_graph object, created using the create_graph function.
you can see in the DiagrammeR doc
library(DiagrammeR)
# Create a simple NDF
nodes <-
create_nodes(
nodes = 1:4,
type = "number")
# Create a simple EDF
edges <-
create_edges(
from = c(1, 1, 3, 1),
to = c(2, 3, 4, 4),
rel = "related")
# Create the graph object,
# incorporating the NDF and
# the EDF, and, providing
# some global attributes
graph <-
create_graph(
nodes_df = nodes,
edges_df = edges,
graph_attrs = "layout = neato",
node_attrs = "fontname = Helvetica",
edge_attrs = "color = gray20")
# View the graph
render_graph(graph)
DiagrammeR can produce Graphviz code : From the doc mentioned below : "If you'd like to return the Graphviz DOT code (to, perhaps, share it or use it directly with the Graphviz command-line utility), just use output = "DOT" in the render_graph()"
So
you can use create_graph to produce graphviz dot code
you can use graphviz dot code directly with grViz in DiagrammeR
Here the problem is with render_graph(myGraph), using just myGraph works like charm.
library(DiagrammeR)
myGraph = grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 B->2 B->3 B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
myGraph
render_graph(myGraph) Does not work in R.
Just myGraph works fine.
I have this Processing sketch in which I'm trying to load a model (.stl or .obj) and build parametric socket junctions for every edge intersection. These will be 3D printed and rods of the appropriate gauge will slot in their holes.
I've been able to draw Spheres at the vertices (in red,on the model), but I can't generate the green sockets or even the rods in pink:
I can get the coordinates of the model's edges:
id: 0 0 {-25.805634,-23.170607,13.6315975} -> 1 {-16.868328,-10.148323,6.785455} f: 2
id: 1 1 {-16.868328,-10.148323,6.785455} -> 2 {-52.833824,-10.148322,16.799314} f: 2
....
and with these I could draw a Line3D(Vec3D a, Vec3D b) (toxi.geom.Line3D)
But I don't want a Line3D, I need to draw a cylinder between them, thereby getting the rods. To sort of extrude or inflate the line to a 3D volume, if you will...
WETriangleMesh whale, redmesh;
[bla bla bla]
Then inside void setup():
for(WingedEdge e : whale.edges.values()) {
edges.add(e);
drawSocket(e);
}
void drawSocket(WingedEdge e) {
// draw size 2 balls at model vertices
Sphere ball = new Sphere(e.a, 2);
// convert to mesh at resolution 6 and add to redmesh
ball.toMesh(redmesh, 6);
}
Then inside void draw():
// draw the mesh with the whale
gfx.mesh(whale);
// color the next mesh red
fill(255,0,0);
// draw the mesh with the spheres
gfx.mesh(redmesh);
I have found no 3D shape class that can be constructed in place from the Vec3D a, Vec3D b arguments. Perhaps creating a class to make use of those two coordinates?
If building a shape in place is too difficult, maybe a transform then? pushMatrix() translate() and popMatrix()?
EDIT: Solved, see below. Thanks!
The trick is to translate the sockets to one vertex in the edge and have them face the opposite vertex. Here's a Processing function that'll take a WETriangleMesh like your whale and return back meshes for the points, sockets and rods. I've written a more complete example here.
float socketLength = 30;
float socketRadius = 6;
float rodRadius = 5;
float pointRadius = 10.0;
int resolution = 10;
WETriangleMesh[] convertMeshToRodSockets(WETriangleMesh inMesh) {
WETriangleMesh[] meshes = new WETriangleMesh[3];
meshes[0] = new WETriangleMesh();
meshes[1] = new WETriangleMesh();
meshes[2] = new WETriangleMesh();
for (Vertex vert : inMesh.getVertices ()) {
//Sphere points
Sphere sphere = new Sphere(vert, pointRadius);
WETriangleMesh sphereMesh = new WETriangleMesh();
sphere.toMesh(sphereMesh, resolution);
meshes[2].addMesh((WETriangleMesh)sphereMesh);
}
for (WingedEdge edge : inMesh.edges.values ()) {
Vec3D pointA = edge.a;
Vec3D pointB = edge.b;
//Meshes to store socket shapes
ZAxisCylinder socket;
ZAxisCylinder rod;
WETriangleMesh socketAMesh = new WETriangleMesh();
WETriangleMesh socketBMesh = new WETriangleMesh();
WETriangleMesh rodMesh = new WETriangleMesh();
float distanceBetweenPoints = pointA.distanceTo(pointB);
//Create sockets and point towards target
socket = new ZAxisCylinder(new Vec3D(0.0, 0.0, 0.0), socketRadius, socketLength);
socket.toMesh(socketAMesh, resolution, 0.0);
socketAMesh.pointTowards(pointB.sub(pointA));
//Translate socket to start from the center of the point
socketAMesh.translate( offsetTranslation(pointA, pointB, socketLength ));
//Create second socket and look in the opposite direction to face the start point
socket.toMesh(socketBMesh, resolution, 0.0);
socketBMesh.pointTowards(pointA.sub(pointB));
socketBMesh.translate( offsetTranslation(pointB, pointA, socketLength ));
//Create rod with a length matching the distance between the two points
rod = new ZAxisCylinder(new Vec3D(0.0, 0.0, 0.0), rodRadius, distanceBetweenPoints);
rod.toMesh(rodMesh, resolution, 0.0);
rodMesh.pointTowards(pointB.sub(pointA));
//Translate the rod to the midpoint of both points
rodMesh.translate(pointA.add(pointB).scale(0.5));
//Combine meshes together
meshes[0].addMesh((WETriangleMesh)rodMesh);
meshes[1].addMesh((WETriangleMesh)socketAMesh);
meshes[1].addMesh((WETriangleMesh)socketBMesh);
}
return meshes;
}
//Function to return a vector that is slightly offset by a length.
Vec3D offsetTranslation(Vec3D a, Vec3D b, float l) {
return a.interpolateTo(b, (l*0.5 / a.distanceTo(b)) );
}