How can I move a Grapviz node to the left? - graphviz

I'm making an application on Sreamlit, which should visualize Gradient Descent. I have this code:
graph= graphviz.Digraph(graph_attr={'rankdir':'LR'})
#black arrows
graph.edge(f'3', '12 ', label='3*4')
graph.edge(f'4', '12 ', label='4*3')
graph.edge('12 ', '12', label='max(0,12)')
graph.edge('12', '60', label='12*5')
graph.edge('5', '60', label='5*12')
graph.edge('60', '1600', label='(100 - 60)**2')
#red arrows
graph.edge('1600', '60', label='2*(100 - 60)=80', color='red')
graph.edge('60', '12', label='5*80=400', color='red',)
graph.edge('60', '5', label='12*80=960', color='red')
graph.edge('12', '12 ', label='1*400=400', color='red')
graph.edge('12 ', '3', label='400*4=1600', color='red')
graph.edge('12 ', '4', label='400*3=1200', color='red')
st.graphviz_chart(graph)
Which produces this graph:
I want all the black arrows to point to the right and all the red arrows to point to the left. For that I would need to move node 12to4 and 60to5 to the left.
I tried graph.edge(... pos="-1,0") but that did not help.
Does anyone have any suggestions? Would really appriciate it.

[This answer is at the Graphviz-level, not specific to Streamlit.]
I don't remember a generic way to force edges to be directional, however there are several ways to "strongly encourage" directionality.
rank=same (https://graphviz.org/docs/attrs/rank/) to force nodes to the same rank
TBbalance=min (https://graphviz.org/docs/attrs/TBbalance/) to force nodes that could be positioned to multiple ranks toward min or max rank
setting minlen for some of the edges to establish "openings" for other nodes
I suggest setting nodes 3 & 4 on the same rank and 5 & 12 on the same rank.
In the Graphviz language this would be:
{rank=same 3 4}
{rank=same 5 12}

Related

How can I tune rigidity, stiffness or turnrate for an edge in graphviz?

I'm attempting to draw some CFGs showing the internal node structure as well as the control edges between subgraphs.
In the image you can see how the thick back-edge that goes from JUMP to ENTRY takes a very sharp turn and goes through the subgraph itself. Likewise you can see back-edges from t19 snake itself through the basic block (subgraph) itself, rather than going on the outside.
The problem I'm trying to solve is to make the back-edges go on the outside of the subgraphs, but my immediate concern is finding a way to enforce a lower turn-rate which allows me to make edges that can't "turn on a dime".
The code generating the edges:
g0op14->g1op0[color=black, weight = 5, penwidth=5, tailport=s, headport=n];
g0op14->g2op0[color=black, weight = 5, penwidth=5, tailport=s, headport=n];
g1op8->g3op0[color=black, weight = 5, penwidth=5, tailport=s, headport=n];
g3op15->g2op0[color=black, weight = 5, penwidth=5, tailport=s, headport=n];
// Misbehaving edge below.
g3op15->g3op0[color=black, weight = 5, penwidth=5, tailport=s, headport=n, constraint = false];
g0op8->g1op1[color=black, tailport=s, headport=n];
On a meta-level, I'm not sure how to input images in my question. The way they're laid out now make them take up way too much space, I'd like to display them as clickable thumbnails instead of as enormous images inline.
This is my first real question, so feedback on how to make it more useful is appreciated.
The full CFG:

d3-force: keep graph compact when filtering/removing nodes

I use d3-force to lay out a graph with about 360 nodes.
const simulation = d3.forceSimulation(nodes)
.force(
'charge',
d3.forceManyBody()
.distanceMax(200)
.strength(-50)
)
.force(
'link',
d3.forceLink(links)
.id((d) => d.id)
.distance(30)
)
.force(
'center',
d3.forceCenter(
$svg.innerWidth() / 2,
$svg.innerHeight() / 2,
)
);
this looks good with all nodes visible – but there will also be the possibility to filter/remove nodes, in which case I would want the graph to be way more compact than it actually is (see animation).
this is probably due to the fact that there are no edges between the remaining nodes, and the fact that they are already spread out a lot when the new simulation starts.
while I could simply reset all node positions to the center of the canvas, that would not look great transition-wise. ideally each node would move from its current position to its new position in a more compact layout.
is there a way to achieve this?
I thought maybe the forceManyBody strength could transition from a positive value (attraction) at first to a negative value (repulsion), but apparently this value is can only be set once for the run of the simulation.
adding an attraction force (https://github.com/ericsoco/d3-force-attract) works well enough.

Find inner geometry from edges

First I am not sure which keywords to use for this and I think I am probably using the wrong ones to google about it, so if someone could give me any hint it would be much appreciated.
My problem is the following:
I need to find the "rooms" inside a house plan. For example take this geometry:
The desired algorithm would tell me which vertexes bound each of the rooms. So for this example it would be:
room A: 1, 2, 9, 10, 3, 4, 5, 8 ,1
room B: 2, 3, 10, 9, 2
room C: 11, 12, 14, 13, 11
room D: 5, 6, 7, 8, 5
I have the vertexes and the edges as input data.
Edit:
The edge data is as follows (edge 8, 1 ,2):
x y
47 196
47 85
258 85
it is in pixel coord.
Graph Theory did not really help me because I have disconnected loops that share information. For example [1 2 9 10 3 4 5 8 1] AND [11 12 14 13 11]. So in the end I ended up doing a image fill, when expanding the boarders of the fill 1 pixel and doing a boolen operation to figure out which vertex are inside the filled image.
This is planar graph. It has V vertices, E edges and F = E - V + 2 faces (including outer face). We have to determine the edge list for all the faces. Every edge will be used twice in these lists (in forward and backward direction).
Create main arc list, add all the arcs (i.e. for 1-2 undirected edge add both 1-2 and 2-1 directed arcs)
Find the lowest vertex point. If there are some such points, choose the leftmost one (7th here).
Travel the outer face (contour) in CCW direction (choose the rightmost outgoing arc at every vertex): 7-6-5-4-3-2-1-7. Remove visited arcs from the main list.
Get any arc from the main list, travel the first inner face, follow the right-hand rule (i.e. 7-8-5-6-7), remove visited arcs.
Repeat until the main list is empty.
Repeat all the procedure for disconnected components (11-12-13-14)
One of possible solutions is to triangulate this area so that every input edge is an edge of some triangle. Then split triangles into connected sets and find their border.
There are several algorithms for triangulation: ear-clipping, Delaunay, ...

algorithm to trace border in 2D array

I have a 2D array of integers that represent groupings (crystal grains) on a 2D surface. something like this:
(each pixel of that image is assigned an integer depending on the group it belongs to, so every red pixel is assigned 1 for example, every blue is 2)
given an X,Y-coordinate on a border between two such groupings (user clicks there) how can I trace that border between these 2 groupings saving every pixel-coordinate that's along the border and get the two endpoint-coordinates (I'm not concerned with the case of an enclosure where there would be no endpoint, but a loop)
whatever algorithm I come up with seems to be so tedious to implement and I just can't imagine noone has done this before. Any help? Preferably would be a solution in c# but any hint about an algorithm is greatly appreciated.
EDIT:
I should have stated that I was going to implement an algorithm to vectorize that line like this:
between the two endpoints define a line
get the boundary point farthest away from the current line, if it is farther than d, split the line at this point, so there are two line segments
repeat until no boundary point is farther away than d from the line strip
I thought that was easy enough to implement that's why I didn't state it. To get there I just needed the solution to the current problem...
Concerning Questions:
how is the raw data formatted? - it's a simple short[,] labeling; with the size being approximately 250x150 something like this:
11111112222
11111222222
11112222222
11112222222 <- user clicks on leftmost 2 or rightmost 1 -> I want to trace that border
11111122222 down to the first
11111133322 encountered 3 and up to the
11333333333 frame-border
what is an endpoint? - as I had been thinking about global solutions, I could describe an endpoint as: a 2x2 area where the 4 pixels consist of color1, color2 and at least one third different color.
what is contiguously connected? - it doesn't really matter for the rest of the algorithm, see below
what about y-shaped regions? - I'm not concerned with them, you can assume the area of color1 behind the border is at least 2 pixels wide, that's why it also doesn't matter if we talk about a 4 or an 8 neighborhood.
what do I currently have? - at first I tried a 'walking'-algorithm, something like mvds posted, but found me doing stepping, neighborcalculation and checking in all 4 directions which was tedious and looked awful. I didn't find a nice representation for "this is the direction the last step came from, don't check that pixel for neighborhood".
I then abandoned the walking algorithm and tried a global approach (like a filter): for each pixel check if it is color1 AND has color2 in its 4-neighborhood. With this I get all boundaries between color1 and color2. I was going to remove all boundaries that are not connected to the user-clicked-coordinate by some kind of floodfill, but then I got the problem of: where are the endpoints?
I'm still thankful for more input. For now I'll see how far I can go with mvds' algorithm.
I'll assume you have already determined the 2 colours in question, e.g. as 'mvds' described, as a preprocessing step.
I think you'll find it helpful to use a coordinate system where each (x,y) represents not a pixel but the point where 4 pixels touch. Then you can write a function to determine whether North is a boundary pixel-border, likewise for South,East,West (or maybe you prefer the terminology up/down/left/right).
Start at a point on the border, e.g. scan the 4x4 neighbourhood for a point which has one of N/S/E/W as a border. Follow this border to the next point, and then scan all 4 directions other than the direction you came in, for the next pixel border. Repeat until you run out of pixel borders. Then you know you're at one endpoint.
Go back to the beginning and trace the border in a different direction to what you initially took, until you reach the other endpoint.
This gives you all the pixel borders. Each pixel border has colour 1 on one side and colour 2 on the other side.
(I would have thought that the vectorisation would be a lot more difficult than identifying the border, but that's not what your question is mainly about, right? To do that I'd start at an end-point and follow the sequence of pixel borders border by border, at each point checking whether the straight line from the end-point to the current point matches the pixel borders. As soon as it doesn't, that's the end of one line and you start a new line.)
Here's a few thoughts and the start of an algorithm:
Finding the outline of an area of equal color is easier than finding a border, especially when the border is not really "binary" (i.e. only 2 colors exactly) as seemingly the case in your image.
Finding adjoining parts of two outlines is not very complicated. For every point on outline A, find the nearest point of outline B. If distance |A-B| < X, the point halfway between A and B is on the border. (X depends on the fuzzyness of your border)
If you can make your users click twice, at both sides of the border, that would be great. If you insist on one click, find the two biggest areas in a radius of X around the clicked point.
Finding the outline of an area is not complicated:
take a point (x,y) to start, take a direction (dx,dy)=(1,0) to start
take color C of point (x,y) which will be the color to trace
run x+=dx,y+=dy until at (x+dx,y+dy) you have another color and are on a boundary
peek at (x+dx,y+dy), if it is not the same color C, you are hitting a boundary: turn left and goto 4.
x+=dx, y+=dy, i.e. take a step
record (x,y) as part of the boundary
if ( x==xstart && y==ystart ) you're done
turn right and goto 4.
turn left means: (dx',dy') = (dy,-dx), revolutions++
turn right means: (dx',dy') = (-dy,dx), revolutions--
revolutions will end up positive or negative, depending on the trace direction (inside/outside)
There is one corner case in which this loops indefinitely, namely when you start in an area of 1 pixel. This is easily checked. Furthermore you may want to check x/y boundaries. "Same color" and "other color" may of course also be implemented as some kind of color distance limit (i.e. |(r,g,b)-(R,G,B)|<D)
disclaimer this is a working, but simple, slow algorithm I cooked up once without the burden of any relevant knowledge or experience.
Your description isn't a hundred percent clear to me, but if I understand you correctly, you want to compute the following:
the set of contiguously connected points of colour A which are adjacent to a point of colour B
that contains the given starting point.
Given that specification, your code practically writes itself. The only thing you need to decide on now is what "contiguously connected" means (e.g., are pixels adjacent only at their corners connected or not?).
Also, your description is ambiguous. Consider a y-shaped region where the arms of the region are a single pixel wide: this would have three "endpoints" if you define endpoint to mean "member of the set with only one neighbour also in the set". If you relax your requirement to allow any number of endpoints then your code can collect the set of endpoints as it goes.
EDIT
Glad you solved your problem. I sketched out a solution which produces this for your sample problem:
1111***2222
111**222222
111*2222222
111*2222222
111***22222
11111*33322
11333333333
Here's the code, provided only since I need validation for having coded it :-) It's written for clarity rather than speed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace StackOverflowEdgeDetection
{
class Program
{
private static HashSet<Point> FindBorder(char[,] grid, Point start, char inside, char outside)
{
var border = new HashSet<Point> {};
var endpoints = new HashSet<Point> { start };
while (endpoints.Count != 0)
{
var p = endpoints.First();
endpoints.Remove(p);
border.Add(p);
var newEndpoints = Neighbours(p).Where(q =>
Grid(grid, q) == inside &&
!border.Contains(q) &&
Neighbours(q).Any(r => Grid(grid, r) == outside)
);
endpoints.UnionWith(newEndpoints);
}
return border;
}
private static IEnumerable<Point> Neighbours(Point p)
{
yield return new Point(p.X - 0, p.Y - 1);
yield return new Point(p.X + 1, p.Y - 1);
yield return new Point(p.X + 1, p.Y + 0);
yield return new Point(p.X + 1, p.Y + 1);
yield return new Point(p.X + 0, p.Y + 1);
yield return new Point(p.X - 1, p.Y + 1);
yield return new Point(p.X - 1, p.Y - 0);
yield return new Point(p.X - 1, p.Y - 1);
}
public static char Grid(char[,] grid, Point p) {
var x = p.X;
var y = p.Y;
var height = grid.GetLength(0);
var width = grid.GetLength(1);
return (0 <= x && x < width && 0 <= y && y < height) ? grid[y, x] : '\0';
}
static void Main(string[] args)
{
var border = FindBorder(TestGrid, TestStart, TestInside, TestOutside);
var points = Enumerable.Range(0, TestHeight)
.SelectMany(y => Enumerable.Range(0, TestWidth)
.Select(x => new Point(x, y)));
foreach (var p in points) {
Console.Write(border.Contains(p) ? '*' : Grid(TestGrid, p));
if (p.X + 1 == TestWidth) Console.WriteLine();
}
Console.ReadLine();
}
private static readonly char[,] TestGrid = new char[,] {
{ '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2' },
{ '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2' },
{ '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2' },
{ '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2' },
{ '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2' },
{ '1', '1', '1', '1', '1', '1', '3', '3', '3', '2', '2' },
{ '1', '1', '3', '3', '3', '3', '3', '3', '3', '3', '3' }
};
private static readonly Point TestStart = new Point(3, 3);
private static readonly Point TestAdjacent = new Point(4, 3);
private static readonly char TestInside = Grid(TestGrid, TestStart);
private static readonly char TestOutside = Grid(TestGrid, TestAdjacent);
private static readonly int TestWidth = TestGrid.GetLength(1);
private static readonly int TestHeight = TestGrid.GetLength(0);
}
}
Nice resources for this stuff here detecting edge pixels with marching squares algorithm and on wikipedia (should that link go away) marching squares algo

Force two nodes to occupy the same rank in Graphviz?

Using ruby-graphviz, I've created a graph that looks like this (border added to emphasize rendering boundaries):
What I really want is for A and K to line up together at the top (or left, if rankdir="LR"). So I added an invisible node (call it X), and added invisible edges from X to A and K. And here's what I got:
X, XA, and XK have no labels, and style set to 'invis'.
X has height, width, and margin set to 0, and fixedsize set to true.
XA and XK have minlen, len, and penwidth set to 0.
But there's still that empty space at the top. Is there any way to get rid of it, short of cropping after the fact?
You do not need invisible nodes to achieve this.
This is the dot syntax to force the same rank for two nodes:
{rank=same; A; K;}
This is called a subgraph.
I don't know ruby-graphviz, I'm not sure how to create a subgraph - but there is an example on github:
c2 = g.subgraph { |c|
c[:rank => "same"]
c.mysite[:label => "\nexample.com\n ", :shape => "component", :fontname => "Arial"]
c.dotgraph[:label => "\ndotgraph.net\n ", :shape => "component", :fontname => "Arial"]
}

Resources