Closed Aaz21rus closed 7 months ago
Hi @Aaz21rus,
Right now, there is unfortunately no way to properly represent parallel edges - and we have no solution in mind. The main reason is that drawing edges as curves is very expensive using WebGL, and will scale way less. Also, collision detection will be very hard to implement as well.
The best solution I can suggest is to aggregate parallel edges into one single edge, that is visually different enough to make it clear that it's aggregated. For instance, in a recent project with labelled directed unweighted edges with sigma.js, I represented aggregated edges as thicker (with a thickness proportional to the number of parallel edges), with the concatenation of the labels and a different color.
Thanks for the answer. The answer helped me a lot. Question: Is it possible to set the color inside the edge label? For example, the edge label: "1 2 3". Each digit has its own color
It is possible, with a custom edge labels renderer. Here is one way to do that:
label
as full string that will be displayed (to make sure sigma knows the edge has some label, and also to compute the full text width), but also a customLabels
property that stores each string / color pair ([{ str: "1 ", color: "blue" }, { str: "2 ", color: "red" }, { str: "3", color: "green" }]
for instance)drawEdgeLabel
function from sigma in your code, and call it in your sigma settings, like edgeLabelRenderer: myCustomDrawEdgeLabel
(as it is done in the demo with the node label renderer, for instance)myCustomDrawEdgeLabel
, replace the context.fillText
call (here) by some code that draws each string with the proper color:// [...]
if (edgeData.customLabels) {
let xOffset = 0;
edgeData.forEach(({ str, color }) => {
context.fillStyle = color;
context.fillText(str, -textLength / 2 + xOffset, edgeData.size / 2 + size);
xOffset += context.measureText(str).width;
});
} else {
context.fillText(label, -textLength / 2, edgeData.size / 2 + size);
}
// [...]
I haven't tested this code so there might me some remaining issues to solve, but that should be mostly it.
@jacomyal How about the option of falling back on canvas for curved edges? That should allow both parallel edges and help with https://github.com/jacomyal/sigma.js/issues/1138. Of course it would be slower than WebGL but I can see some real advantages of having WebGL being the default but then allowing the user to specify canvas node and edge programs if they like (which could be used alongside the WebGL defaults). My reading of the code at the moment is that that is not possible..you can use canvas for labels but not for nodes / edges generally.
@apitts a workaround for now is to hook yourself on the beforeRender
event and draw the edges using canvas on a dedicated context. You can use the getEdgeDisplayData
and translation methods to help. There is also a way to create a dummy custom program class whose render method actually draw canvas but this is probably less straightforward.
Thanks @Yomguithereal! It was the custom program class that I was thinking about....but it seemed like the node and edge programs are designed to use WebGL. I will experiment and see how I go! If I use the beforeRender event and hook into that, I'm assuming I should turn off edge rendering in the settings to avoid double rendering? Also, if I do manage to figure out the custom program class approach would you be open to a pull request on it?
I'm assuming I should turn off edge rendering in the settings to avoid double rendering?
Probably so yes.
Also, if I do manage to figure out the custom program class approach would you be open to a pull request on it?
I am not sure we want to go this way yet, we need to discuss it with @jacomyal
First, I think with @Yomguithereal we do not want to go back to sigma as it was, ie. two separate and independant renderers within the same codebase. This version 2 is WebGL only, and won't integrate canvas-based edge rendering in its core.
But opening a door to help people render custom edges with canvas using beforeRender
sounds very nice to me. To do this, I think some things are missing though:
hidden
nodes, hidden
edges, etc...)canvas
elementhidden
but still not being rendered in WebGL without throwing) With this, you could still use sigma with custom edges rendering, and without too much impact on the codebase.
That makes sense @jacomyal! I can certainly see the advantages of keeping the core of v2 focused on WebGL. I should be able to add a boolean renderEdges setting to take care of item 3 above...once I have I'll submit a pull request.
Hi @jacomyal,
first off, amazing library you guys created here! Very impressive work. I have been using graphs visualization and modeling purposes at work for some time and am very excited about this WebGL implementation.
I wanted to come back to you about your statement on performance:
Right now, there is unfortunately no way to properly represent parallel edges - and we have no solution in mind. The main reason is that drawing edges as curves is very expensive using WebGL, and will scale way less. Also, collision detection will be very hard to implement as well.
I understand your concern about performance issues during rendering, but I have a use case at the moment in which most of my edges should be rendered as straight lines, with few of them (~10%) as curved ones. Due to the low number of curved lines, I would accept a performance penalty.
In short: the availability of curved edges would give library users more tools to work with, and they can decide themselves whether it's worth paying the performance price on that feature.
Just my two cents.
Hi guys, just wanted to know if there's a plan to make it work.
@anpari's suggestion sounds about right. I also need curvature only for overlapping edges and there's not a lot of them.
I haven't tried it yet but I see resources online for old sigma versions with curvedArrow
, will try it for now
Hi @jacomyal, @Yomguithereal ,
I've done a little experimenting with an alternative approach for rendering multi-digraphs, and so far it looks promising. Instead of using curved lines, each directed edge is drawn using a straight line with an arrowhead. However, each line is offset from the [source, target] center line by a specified amount - i.e. by computing endpoints for the lines that are offset from the node centers. This way the lines do not overlap, and each can be seen individually.
If you leave the endpoints in the interior of the node 'circle', the math is pretty simple (i.e. normal to the slope of the original line) . Alternatively, if you want to find new coordinates for the line endpoints where each line intersects the node 'circle', so that the arrow tip is in the right place, there is a little more computation involved (but still much less than curved lines, I think).
I am pretty new to webgl, but was able to get a basic example working, and also position the edge labels so that they are (further) offset from the (already) offset lines. Someone with more webgl experience could probably improve on the approach.
One question is the API for how to define the offset distance. This approach only works when the number of parallel edges is not too large relative to the diameter of the node circles. The offset for a given edge can be computed based on a multiple the key id - e.g. 0, 1, 2 - for a given [source, target] pair, and could be defined in terms of percentage of node radius width, or in pixels, etc. (and could perhaps be normalized on a node-pair basis based on the current number of edges between the pair).
I'd be interested in your thoughts on adding this feature, and would be happy to assist with the code if you think it could work (and fit with the rest of the overall design). I have only tested with fairly small graphs, so we would probably want to check the performance as the graphs scale up.
In any case - thanks for your work on this library!
@Yomguithereal , @jacomyal ,
I plan to start working on an implementation of the approach outlined in the previous comment. Since this is a decent-sized 'feature', I would appreciate any input you might have on integrating with existing settings - e.g. include logic to automatically compute offsets based on properties of the source-target pair (that are defined by other settings), explicitly expose new 'top-level' settings for parallel edges, or use some combo (use explicit if provided, otherwise fall back to auto-computed).
My thinking is to add code to the v3 branch - is that correct? Is this feature something that might make sense to target for the 3.0 release? (not sure of the planned timeline)
Most importantly - it seemed like some of the other approaches proposed above were not a good fit with the overall design goals of sigma v2 and beyond. Does this approach seem reasonable to you, at least, 'reasonable enough' to spend time on an implementation / pull request?
Thanks!
Hello everyone,
We just released @sigma/edge-curve, that will become the easier way to address parallel edges display. You can see a live example here: https://www.sigmajs.org/storybook/?path=/story/edge-curve--parallel-edges
Hello. I couldn't figure out how to display parallel edges. Please help me find an example.