How can labels be embedded within edges in Cytoscape.js? - javascript

We are looking to embed labels within edges in Cytoscape.js, reproducing edges that look like this.
We recently asked this as a feature request on the GitHub issues page but it seems like it's already possible.
Maxkfranz says that in order to get edge labels inside the edges with a small arrow indicating the direction the only thing that is needed is two items in the style properties for the edges:
text-background-color: white;
text-rotation: autorotate;
We tried this and it doesn't seem to work work, so were wondering if something was missing. Here is the code we are using.
Lines 83 and 84 show the fix that Maxkfranz suggested being applied, but it doesn't seem to be doing the trick. It is definitely possible that we are misunderstanding their reply, but I'm not sure where else the particular fix might go. Any suggestions would be appreciated.

Your text will always overlap the mid arrow. Mid arrows are intended as replacements to endpoint arrows. There's not much point to having both.
If stylistically you want to show an arrow beside the text, use a unicode triangle character (e.g. "\u25B6"). Shifting arrows will not be supported, as can be prohibitively expensive for bezier curve edges.

Related

Canvas lineTo drawing and blending [duplicate]

Please look at the picture (sorry, new users can't insert an image directly into post).
Lines are drawn semi-transparent colors (alpha = 0.5).
When the red line crosses itself, the double overlay translucent colors does not occur. At the same time, separate the green line superimposed on the red as it should.
It can be concluded that the lines are drawn on canvas is not linear, as well as the whole area. I think this is incorrect behavior.
Live demo: jsfiddle.net/dom1n1k/xb2AY/
I will not ask how to fix it :) The question is ideological: how do you think about this behavior?
This is logical and it should be;
This is not logical, but if it happened - we assume that feature;
Canvas work that way for technological reasons - the implementation is simpler.
This is an obvious bug, and the authors of browsers should fix it.
P.S. Sorry for my bad english.
Great question! The spec writer (and I) believe that the answer is:
This is logical and it should be;
Lets explore the reasoning for this.
You are not drawing separate lines when you draw the red path. You are drawing an entire path, and an entire path is drawn all at once and stroked all at once, and the color of the path cannot "overlap" itself. This is intentionally defined by the specification. It reads:
Since the subpaths are all stroked as one, overlapping parts of the paths in one stroke operation are treated as if their union was what was painted.
If you wanted to get an overlay effect you could simply use multiple paths, as you do by adding the green line. So you can easily do it the other way when necessary.
You should consider this feature a good thing: If the Canvas spec were to require each subpath of the path to cause an additional overlay then the corners of every path (where each line is joined) would look horrible! (see the red connections here for an example of what each corner would look like)
Since having the path overlap on the crosses also means it would overlap on every corner, the specification decides to only use the union'd path when stroking, which keeps normal-looking corners as the expected default (I think most people would expect them to look as they do, not to look as I showed). If the lines were overlaid on the crossings but not every corner then it would not be a consistent rule, which makes it much harder to learn and work around.
So the reasoning is clear I hope. The specification has to give us 3 things, usually in this order: The most-common expected output (corners look as they do), consistency (if we overlaid on line crosses we'd also be doing it on corners, so we shouldn't do it), and ease of understanding.
A good specification is always consistent. If something is consistent then it is more learnable, which makes it easier to understand once you know why something is done that way.

D3 map SVG performance

I've been struggling the past few days to optimize performance on a D3 map, especially on mobile. I am using SVG transforms for zooming and panning but made the following observation: the overkill comes from path strokes used to fake spacing between countries.
I have uploaded a pair of sample maps for comparison:
http://www.nicksotiriadis.gr/d3/d3-map-1.html
http://www.nicksotiriadis.gr/d3/d3-map-2.html
The only difference between the two maps is the stroke path along the country paths, and the difference in performance is even noticeable on desktop devices - but more obvious on mobile. Removing the path strokes makes mobile performance a breeze..
I tried all kinds of svg stroke shape-rendering options without significant results.
Now to the question. Is there any way to remove a thin border from each country to fake the spacing between countries instead of using a stroke?
If anyone else has a different suggestion I'd love to hear it!
Update: Attaching explanation photo.
What I have drawn is this. The red arrow points to the country joints. When adding a stroke in a color same as the background to the country paths (here depicted in dark grey color) it creates the sense that the countries are seprated - however this adds a serious performance hit on mobile devices. What I am looking for is somehow re-shape the countries paths so that their borderlines are where the blue arrow points, but without having a stroke.
Update 2: People seem not to be able to understand what I am looking for, so I am updating this in order to make the question even clearer.
Let's assume that the original countries paths are shown on the left of this image. What I am looking for is a way that I can somehow 'contract' the paths inwards so that the newly created paths shown in red, leave enough empty space between them that will 'emulate' a stroke between them.
Doing this, will leave no use to having an extra layer of strokes, thus gain performance from only using paths instead of paths+strokes.
Update 2: Hello again, I seem to have found a half-solution to my problem. I managed to extract the topojson to shapefile, edit the shapefile the way I want (used a program named OpenJump), but the conversion takes away all the topojson properties I need - id, country name, so I can't convert back to the original topojson.
Does anyone have any suggestions?
D3 has a thing just for that: topojson.mesh() (see documentation). The idea is that since most countries share borders, there's no need to draw the shared borders twice. If you can draw each border only once, you get as much as 80% reduction in the number of strokes you have to draw. The mesh method does the javascript processing to turn a bunch of closed shapes (countries) into the multiline path of just the borders between them. You can then draw that multiline path into a single <path> object that you position on top of the fills.
The mesh looks like this.
Here's another example.
Finally found the answer. This radically improves d3 map performance!
1) I got my topojson file and extracted to shapefile using mapshaper.org. This gives 3 files: .shp, .shx, .dbf . From what I realized the .dbf file holds all the TopoJSON properties/attributes.
2) Opened the .shp shape file to OpenJUMP http://www.openjump.org/ - Which automatically imports the .dbf file as well.
3) I selected the countries layer and went to Tools > Analysis > Buffer.
4) Checked the Update geometry in source layer box so that the geometry is edited without losing the rest of the attributes/properties and added a negative Fixed Distance -0.1. This shrinked all the country geometries to the result I was looking for.
5) Saved Dataset as ESRI Shapefile
6) Reimported BOTH .shp and .dbf that were produced from OpenJUMP back to mapshaper.org - careful, BOTH files.
7) Exported as TopoJSON. Contains new shape and all original properties/attributes!
The following link has been updated with the new produced map; we have a 'bordered' look without the need of strokes.
http://v7.nicksotiriadis.gr/d3/d3-map-1.html
Compare the performance to this link that has the original shapes + stroke. Please try on mobile to see the performance difference!
http://v7.nicksotiriadis.gr/d3/d3-map-2.html
Also, here is the updated world map TopoJSON file in case someone wants some extra performance! :D
http://v7.nicksotiriadis.gr/d3/js/world-topo-bordered.json
There might be a couple of reasons of this behaviour (on my computer, everything is working fine at the same speed ):
Browser
Which browser do you use ? On Chrome, your exemples are working perfectly.
TopoJson
eg. previous answer.
Animation
You are launching the animation when the page is loading. You might want to add a delay (animation().delay(in ms)). There is also a function in D3: queue(), https://github.com/mbostock/queue which load the data before launching a function.
--
If none of this change your problem, and if you want it to work fine on mobile, you can try to mix D3 and Leaflet (map for mobiles), which is great in term of performance by loading tiles.
One example:
http://bl.ocks.org/zross/6a31f4ef9e778d94c204
Hope it helps

HighCharts: Any way to control positioning or grouping of legend items?

By default, Highcharts fills horizontal legends with legend items from left to right, introducing a line break between items when the next item would overflow the legend's bounding box.
I would like to be able to control where such line breaks occur, so that (for example) instead of this:
I could visually group the "Target" and "Actual" items together, and then list the remaining series on subsequent lines like this:
Using useHTML and labelFormatter, I've tried a few different approaches involving <span>s, <div>s and styling to try to achieve this, but none of them have had any effect. The one approach I did manage to make work (from which I generated the second image above) is horribly kludgey, and probably can't be easily generalized for use in real code.
I'm thinking this ought to be a fairly simple thing to do, and I suspect that I'm merely overlooking some existing Highcharts feature that would do it.
Can someone point me in the right direction?

How to synchronize the Three.js and HTML/SVG coordinate systems (especially w.r.t. the y-axis)?

I'm combining 3D content with Three.js with HTML and SVG content. The Three.js CSSLoader does a pretty good job synchronizing placement of HTML and SVG content in the 3D world.
But the SVG/HTML coordinate systems are 'left-handed', whereas Three.js coordinate system is 'right-handed'. This basically means that their y-axes are reversed. In SVG/HTML, y/top goes up as you go down the screen, and Three.js uses the more standard mathematical convention of y going down as you go down the screen.
I have to continually convert from one to the other, which is pretty error prone. I know I am not the first to run into this (for example, look here). Has someone come up with a general solution? Here's what I tried:
Do everything in an Object3D with .scale.y = -1. As you may suspect, this turns out to be a disaster. It turns everything inside-out, and don't even try to put your camera in there.
Do everything in an Object3D with .rotate.x = Math.PI. This is more promising, but the z axis is no longer consistent with the HTML concept of z-index. Still, this is what I'm using now.
In HTML, don't use top, use bottom. In SVG, do everything inside a <g transform="scale(1, -1)"> inside a <g transform="translate(0, imageHeight)">. However, I feel this would be more confusing for developers, and the imageHeight has to be kept up to date at all times, which is yet another burden.
Has anyone come up with something better? Perhaps a library to help with this?
I would suggest you to use the SVG Global Transform Attribute, if you post an example of your code, i could edit the answer and post the example here, maybe a JSfiddle.
Basically you will need to add the transformation to your SVG, in your case to change the direction of y-axis, you can do a "scale(1,-1)".
See the W3 documentation with examples in the following link:
http://www.w3.org/TR/SVG/coords.html#SVGGlobalTransformAttribute
The first common use of this attribute:
Most ProjectedCRS have the north direction represented by positive
values of the second axis and conversely SVG has a y-down coordinate
system. That's why, in order to follow the usual way to represent a
map with the north at its top, it is recommended for that kind of
ProjectedCRS to use the ‘svg:transform’ global attribute with a
'scale(1, -1)' value as in the third example below.
They have some examples there too, I hope it solves your problem. :)

HTML5 Canvas: How to fake globalCompositeOperation="darker"

I've googled and googled about this, and all I can find, including on StackOverflow, is "support was and is broken in most major browsers." Not an actual solution to my problem.
This month's Playboy came with a pair of 3D glasses (red/cyan) to view the eye-popping centerfold. Naturally, I hit the Internets to find every single red/cyan anaglyph I could and look at how awesome they are. Eventually I found some animated GIFs, which led to the idea that maybe I should make some cool HTML5 Canvas thing that lets you put shapes on a scene in 3D.
This is how far I got. Only works well in Google Chrome. In Firefox, the "Elevated Text" should look correct, but not the rectangles.
The way I'm generating the scene is thus: There are layers that each contain a Z-index, and you can place a rectangle or some text on whichever layer you want. The concept is simple. When drawing the object, it draws one [Z-index] pixels to the left in pure red, then it draws one [Z-index] pixels to the right in pure cyan.
In theory, the overlapping parts should subtract to become pure black. In Chrome, this happens for filling rectangles, stroking text, but not for filling text. In Firefox, this only happens for stroking text.
Although the intended effect of globalCompositeOperation="darker" should do exactly what I want, it's obvious that going down this road is going to bring nothing but pain.
Does anyone here have an idea as to how I can get the effect I want without using globalCompositeOperation? I tried messing with the alpha channel on the colors but didn't really like how that came together (they never add up to pure black). I could draw a third black rectangle between the red and cyan ones, but that doesn't solve the problem for text or arbitrary shapes.
I could do the pixel-for-pixel rendering myself in the Javascript, but that just seems like overkill. Any thoughts?
If you still need this, I have written a free context-blender library that lets you perform Photoshop-style blend modes between two canvases. I have not yet added 'darker', but you could either:
Fork the project on GitHub, add your own support for darker (it's pretty easy to see how to add a mode) and then send me a pull request, or
Ply me with promises of upvotes to get it added for you. :) The only hard part (as with many of the blending modes) will be attempting to determine what is correct when blending one or two areas which are <100% opacity.
It seems that the correct mode in Firefox is globalCompositeOperation="difference". Haven't tested in Chrome or IE.
Because "difference" is a mathematical operation, there is no ambiguity in the implementation, unlike the subjective term "darker".
Maybe you would like to use darken instead of darker. darker has been removed from the specification in 2007
It's a bit of a hacky way but it worked for me.
You can invert the entire canvas by doing
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
Then render whatever you want to render using globalCompositeOperation = "lighter". Then invert the entire canvas again and it should give the same results as a "darker" blend mode would.

Categories

Resources