I have tried to create the following illustration in LaTex with poor results. The problem is that I am not able to determine the starting points nor ending points of the arrows how I want to. Any suggestions/hints how to do succeed in this?
Here is possible implementation using tikz, with the calc and positioning libraries
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,calc}
\begin{document}
\begin{tikzpicture}[box/.style={
draw,thick,
},
label/.style={draw=none,midway,sloped,above,font=\small},
myarrow/.style={->,thick} ,
]
%poition upper and lower nodes
\node[box] (gov) {Government};
\node[box,below=4cm of gov,minimum width=3cm,dotted] (trans) {Transaction} ;
% position first line extremity at, say, 1/3 gov node
\coordinate (extr1) at ($(trans.north west)!0.33!(trans.north east)$) ;
% then extremity 2 slightly at the right of extr1
\coordinate (extr2) at ([xshift=2mm]extr1) ;
% position bank vertically halfway gov/trans and centered wrt extr1 and extr2
% due to some limitations in tikz parser, one must first define intermediate coords
\coordinate (extr12) at ($(extr1)!0.5!(extr2)$) ;
\coordinate (mid) at ($(gov)!0.5!(trans)$);
\node[box] (bank) at (extr12 |- mid) {Bank};
% draw first arrows
\draw[myarrow] (extr1) -- (extr1 |- bank.south) node[label] {information};
\draw[myarrow] (extr2 |- bank.south) -- (extr2) node[label] {monitoring};
\draw[myarrow] (extr1 |- bank.north) -- (extr1 |- gov.south) node[label] {reporting} ;
\draw[myarrow] (extr2 |- gov.south) -- (extr2 |- bank.north) node[label] {fines} ;
% position last arrow midway from east border of bank and gov
\coordinate (extr3) at ($(bank.east)!0.5!(gov.east)$) ;
\draw[myarrow] (extr3 |- gov.south) -- (extr3 |- trans.north) node[label] {investigation} ;
\end{tikzpicture}
\end{document}
You have to modify a couple parameters for the positioning that you want and add style to nodes/lines as required.
Using north and south (and phantom) makes it ok vertically. Instead, I wanted to use calc TikZ library to also horizontally offset endpoints of arrows from nodes (a), (b), (c) in a more elegant way but I didn't succeed:
\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{arrows,decorations.markings}
\begin{document}
\begin{tikzpicture}[>=triangle 45, thick, sloped]
\node [rectangle, very thick, draw, right] (a) at (0,6) {\large\textbf{Government}};
\node [rectangle, very thick, draw, right] (b) at (0,3) {\large\textbf{Bank}};
\node [rectangle, densely dotted, draw, right] (c) at (0,0) {\large Transaction};
\node (d) at (.2,6) {\phantom{G}};
\node (e) at (.2,3) {\phantom{B}};
\node (f) at (.2,0) {\phantom{T}};
\node (g) at (1.1,6) {\phantom{G}};
\node (h) at (1.1,3) {\phantom{B}};
\node (i) at (1.1,0) {\phantom{T}};
\node (j) at (2,6) {\phantom{G}};
\node (k) at (2,0) {\phantom{T}};
\draw [->] (e.north) -- (d.south) node[pos=0.5, above] {reporting};
\draw [->] (g.south) -- (h.north) node[pos=0.5, above] {fines};
\draw [->] (f.north) -- (e.south) node[pos=0.5, above] {information};
\draw [->] (h.south) -- (i.north) node[pos=0.5, above] {monitoring};
\draw [->] (j.south) -- (k.north) node[pos=0.5, above] {investigation};
\end{tikzpicture}
\end{document}
This code results in:
Then you can tune this depending on your taste!
Related
I just started to experiment with GraphViz's "neato" command for
Kamada-Kawai graph visualization (solved using stress majorization).
I am particularly interested in extracting the laid out node
coordinates for external processing.
I was looking at this
question
to figure out the "pos" attribute for graph edges in the output DOT
file. The attribute consists of 4 coordinate points. One helpful
answer says that the
series of coordinats (not necessarily 4) are the control points of a
spline, likely a Bezier
spline.
The answerer is "compound eye", who I'll refer to as "CE". Here is
the example given:
// digrfG.dot
//-----------
digraph G {
A [label = "A" xlabel="a" pos="0,100" ]
B [label = "B" xlabel="b" pos="100,0"]
A -> B [label = "A->B www" xlabel="a->b"]
[pos="0,100 0,0 0,0 100,0"]
}
In Bash, it is processed by GraphViz using:
# Make image:
#------------
dot -Kneato -n2 -Tpng digrfG.dot >| digrfG.png
# Make output DOT file:
#----------------------
dot -Kneato -n2 digrfG.dot >| digrfG_out.dot
The output is:
# digrfG_out.dot
#---------------
digraph G {
graph [bb="-7,0,154,151"];
node [label="\N"];
A [height=0.5, label=A, pos="27,118",
width=0.75, xlabel=a, xlp="-3.5,143.5"];
B [height=0.5, label=B, pos="127,18",
width=0.75, xlabel=b, xlp="96,43.5"];
A -> B [label="A->B www", lp="42.5,75.5",
pos="27,118 27,18 27,18 127,18",
xlabel="a->b", xlp="63.5,60.5"];
}
My edge coordinates do not match
CE's, neither in the
numerical quantities nor in the number of coordinates. While I have 4
coordinates, CE has 6, including 2 coordinates at the start of the
series prefixed by "s," and "e,". The Bezier curve
page
cited by CE gives the impression that typical Bezier splines have 4
control points, including start and end points, though the math
allows for more. This Micrsoft
page
reinforces this impression of a 4-point default.
This GraphViz
page shows that
an edge's "pos" attribute contains a spline, starting with start and
end points, prefixed with "s," and "e," as per CE's output, but the
syntax is puzzling. If it is regular expression syntax, then there
are 4 or more coordinates following optional start and end points.
That might explain why I have no coordinates prefixed with "s," or
"e,", but I have 4 coordinates.
In digrfG_out.dot above, if I compare the 4 coordinates with the
node coordinates, it is clear that the first and last of the 4
coordinates match the node coordinates. It seems that CE's GraphViz
defaults to 6 control points and explicitly designates start and end
points at the head of the list of coordinates, whereas my GraphViz
defaults to 4, without special treatment of the start and end
points. Since GraphViz's spline
page is so
ambiguous, I was wondering if this interpretation can be confirmed.
Thanks.
I would suggest skipping CE's explanation (not necessarily wrong, but maybe ambiguous) and go to the sources on the Graphviz website.
Yes, Bezier curves require 4 points per "segment" (my term) but the last point in a segment is also used as the first in the next segment. Graphviz's arrowheads use separate points - for one end of the arrowhead.
The optional s (start) and e (end) points are for optional arrowheads. Then 4 required points and then optional sets of 3 points.
See p.36 https://www.graphviz.org/pdf/dotguide.pdf, https://forum.graphviz.org/t/how-to-control-the-points-of-splines/1087, and https://forum.graphviz.org/t/fun-with-edges/888 - in addition to the (yes, regular expression) http://www.graphviz.org/docs/attr-types/splineType/
I hope this helps.
Any edge can have arrowheads, even in a non-directed graph. Digraph just sets the default (arrowhead or no arrowhead). The dir attribute (https://graphviz.org/docs/attrs/dir/) explicitly sets arrowheads.
Finally, arrowhead shape (https://graphviz.org/doc/info/arrows.html) can be "none".
graph {
A--B [dir=forward]
C--D [dir=back]
E--F [dir=both]
G--H [dir=none]
}
Dot produces this:
I would like to create a spline curve between the blue point and the green point with the red point as a control point.
Graphviz documentation says :
splines attribute, of type bool or string, is valid on Graphs
splineType with the pattern : spline ( ';' spline )* is a valid type for pos attribute
pos attribute is valid on Edges and Nodes
I Tried this graph
graph G{
layout ="neato" outputorder="edgesfirst" splines="true"
a[shape="point" pos="0,0!" color="blue"]
b[shape="point" pos="1,0!" color="green"]
c[shape="point" pos=" 0.5,0.5!" color="red"]
a -- b [pos="e,1,0 s,0,0 0.5,0.5!"]
}
then on Windows 10 PowerShell :
neato.exe -Tsvg .\spline.dot > .\spline.svg
with
neato - graphviz version 2.49.3 (20211023.0002)
result
What is the proper way of achieving this?
Thanks.
Close, but ...
! notation seems to only apply to nodes (not edges) (poorly documented, but see: https://graphviz.org/docs/attr-types/point/ and https://graphviz.org/docs/attrs/pin/)
by default, pos value units are points (inch/72). Your values are ~ too small
neato -n2 will use node & edge values you provide (https://graphviz.org/faq/#FaqDotWithCoords)
all edges (not just curves) are defined as cubic B-splines and are defined by 1 + n*3 points (n is integer >=1) (https://graphviz.org/docs/attr-types/splineType/) (i.e. 4 or 7 or 11 or ...)
So:
graph G{
// default units for pos values are in points (72/inch)
// multiplied by 100 out of laziness
// ! only applies to nodes, not edges
layout ="neato"
outputorder="edgesfirst" // why???
splines="true"
a[shape="point" pos="0,0!" color="blue"]
b[shape="point" pos="100,0!" color="green"]
c[shape="point" pos="50,50!" color="red"]
// "s" arrowhead must preceed "e" arrowhead, so swaped them
// BUT, "--" says non-directed graph, NO arrowheads, so they are deleted
// also need 4, 7, 11, ... points NOT including arrowhead points
// so added points (just guesses)
a -- b [pos="0,0 30,66 70,66 100,0"]
}
Gives:
Whew
\begin{tikzpicture}
\filldraw[color=black!60, fill=white!5, ultra thick](-1,0) circle (0.8);
\filldraw[color=black!60, fill=white!5, ultra thick](5,0) circle (0.8);
\draw[black, ultra thick] (-1,0) -- (5,0);
\end{tikzpicture}
This code creates a picture with two circles and a line joining their centres. I need to label the left circle as Left Circle and the right one as Right Circle. Both labels have to be at the top of the circles, NOT below them.
Can I please have some help with this?
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\coordinate (A) at (-1,0);
\coordinate (B) at (5,0);
\filldraw[color=black!60, fill=white!5, ultra thick] (A) circle (0.8);
\filldraw[color=black!60, fill=white!5, ultra thick] (B) circle (0.8);
\draw[black, ultra thick] (A) -- (B);
\node[yshift=1.2cm] at (A) {left circle};
\node[yshift=1.2cm] at (B) {right circle};
\end{tikzpicture}
\end{document}
How to use Graphviz to align nodes circular in clusters with additional text? Optionally with identical node positions (always 8 nodes per cluster)?
I tried circo, however, faced some shortcomings:
No clustering
No comments
Problems with margins for larger labels (10+ char)
Alignment varies with label size
This (Graphviz Online), nothing spectacular, was the closest I could get. Any hints to other layouts (or even tools) appreciated.
graph {
layout = circo;
node [shape = circle,
fontname = Helvetica,
margin = 0]
edge [style=invis]
subgraph 1 {
a1 -- b1 -- c1 -- d1 -- e1 -- f1 -- g1 -- h1 -- a1
}
subgraph 2 {
a -- b -- c -- d -- e -- f -- g -- h -- a
}
}
Not exactly the answer as I was asking for (Graphviz), but I found a much nicer solution with MATLAB. It was about plotting a seating plan for an event.
What I did broken down:
imread() image of the floor plan
Roughly determined pixel spacing, used as x & y vector for image() so that tables are in scale with the room.
Manually defined centers for the clusters (here tables) with the help of ginput() (or imellipse())
Plotted circles with plot() and added text with text()
I have a question on this algorithmic problem; I'll paste the problem then go over my current thoughts and solution.
There are N (up to 100,000) line segments defined as [(x1, y1), (x2, y2)], where x1 < x2 and y1 < y2 (e.g. The line segments have positive slope). No line segments touch or intersect, even at endpoints. The first segment has (x1, y1) = (0, 0). Imagine each segment as a 2-D hill a person has to climb.
A person starts at (0, 0) and lands on the first hill. Whenever a person lands on a hill, he climbs to the end, which is (x2, y2) and jumps straight down. If he lands on another hill (anywhere on the segment), the process continues: he climbs that hill and jumps. If there are no more hills, he falls to -INFINITY and the process is over. Each hill (x1, y1) -> (x2, y2) should be
regarded as containing the point (x1, y1) but not containing the point (x2,
y2), so that the person will land on the hill if he falls on it from above at
a position with x = x1, but he will not land on the hill if he falls on
it from above at x = x2.
The objective is to count how many hills he touches.
My current thoughts
I'm thinking of sweeping a line across the plane along the x-axis. Each segment consists of a BEGIN and END event; everytime we encounter the beginning of a line segment, we add it into a set. Every time we encounter the ending of a line segment, we remove it from the set. And when we hit the END point of the current hill we are on, we should check the set for the highest hill that we can land on. However, I don't know how to determine how to check this quickly, because there could be potentially N entries inside the set. Also, after jumping on to another hill, the order of these will change because the slopes of each segment are probably different, and I don't know how to account for this difference.
Any thoughts?
In pre-processing you can traverse all segments and add points in an stl multimap< pair, linesegment> or something similar. Cost of this pre-processing would be O(NlogN). Then you can continue with your sweep line method. You need iterate points from multimap. Because all points are sorted, and contains reference to line the point corresponds to, it would cost O(N).
Barron, your algorithm is perfectly correct. The order of elements in your sorted list will not change as the sweep line moves, because if that happened you would have an intersection of line segments.
You just need a way to keep track of the sorted line segments. One way to do this would be to keep a map of line segments, in which the comparison operator compares line segments by the y value on the segment as calculated by the current x value of the current sweep location. Inserting, deleting, and querying from this map is O(log(n)).
Here's a rough direction in Haskell. "segments" are the line segments. (In this example, the third segment is slightly above the second segment in order to test the code.) "matches" finds the hills/segments that place the top of the last segment, pt (x0,y0), within their x bounds and above or equal to the y corresponding to their affine transformation of x0 ("affine" calculates the affine function for the segment -- the ax+b, so to speak). Finally, countHills tests the possible matches for the next hill and chooses the one with the closest y to y0 (calculated by the affine a*x0+b), and outputs the result, accumulating the hills climbed on in order. Clearly this idea may need optimization for much longer segment lists.
The result output below shows the first and third segments. The second hill/segment is not in the result because it is lower than the third - we land on the third instead:
*Main> countHills segments
[((0.0,0.0),(2.0,5.0)),((1.0,1.5),(5.0,3.0))]
import Data.List
segments = [((0,0),(2,5)),((1,1),(5,2)),((1,1.5),(5,3))]
top segment = snd segment
matches pt =
let x0 = fst pt
y0 = snd pt
in filter (\x -> x0 >= fst (fst x)
&& x0 < fst (snd x)
&& (affine x) x0 <= y0) segments
affine segment =
let x1 = fst $ fst segment
y1 = snd $ fst segment
x2 = fst $ snd segment
y2 = snd $ snd segment
in (+ ((x1*y2-x2*y1) / (x1-x2))) . (* ((y2-y1) / (x2-x1)))
countHills segments = countHills' (head segments) [] where
countHills' x result =
let hills = matches $ top x
x0 = fst (top x)
y0 = snd (top x)
in if null hills
then result ++ [x]
else let nextHill =
minimumBy (\a b -> compare
(y0 - (affine a) x0)
(y0 - (affine b) x0)) hills
in countHills' nextHill (result ++ [x])
I think a line sweep algorithm is a good idea here. Let me summarize your algorithm so far and add my improvements:
You are sweeping a line from left to right.
You have an active list which lists all currently active segments.
These are segments intersecting with the sweepline
Every endpoint of every line segment is considered an 'event'
when the line sweeps across the 'start' of a segment, the segment gets added into the list of active segments
When the line sweeps across the 'end' of a segment, the segment gets removed from the list of active segments
If there are no line segments in the active set upon removal of a line segment, the process ends
If there are line segments in the active set upon removal of a line segment, we need to determine
A) Whether there are any line segments in the active set with portions 'below' the previously removed end vertex, and
B) Which of these line segments the person will land on.
The idea is to sort your line segments in the 'active set' such that this query is efficient. What I'm thinking is that if we know a line's slope and y intercept we can compute intersection points for a start vertex's x position
GreaterThan(segment1,segment2){ // is segment 1 higher than segment 2?
//y = mx + b; compute y value of point on segment 2 for a given x value from s1
//that is, m and b are slope and y-intercept of s2
yVal = m * (segment1.first.x) + b
if (yVal < segment1.first.y)
return true //the point on s2 corresponding to s1.first is lower than s1.first
return false
}
Because lines don't intersect, then you can assume no other line will 'go through and over' this line.
If we avoid adding any line segments whose start vertices are higher than the end vertex of our "person's" current line, then we should successfully avoid adding any extraneous line segments to the active set (i.e. line segments "above" our current one)
Now we just need to worry about the special case of the vertex of the last line segment not being 'landable'. Because vertices are events, we will process all events before we do our segment testing. this way, you will not accidentally land on the end vertex of a line in the active set, but you WILL land on a line segment that has just been added.
Now that we have a sorted list of line segments in the active set, we can query it in constant time to just get the top one, and adding a new one should only take logarithmic time.
How does this sound?