d3 force directed graphs have grouped items overlap each other - d3.js

I have this plunker (https://next.plnkr.co/edit/17t5ujwC71IK3PCi) which shows d3 graph of nodes grouped together based on group ID. Graph appears fine but I need to make sure that the groups never overlap each other (as shown below where orange, blue and lightblue are different groups but they are appearing on top of each other).
Dragging them causes the graph to endlessly move (which is another issue) and doesn't always fix the overlapping issue. I saw another example (http://bl.ocks.org/GerHobbelt/3071239) which is a bit better but its made with d3.v2. It has quite some good space between each group which makes it easier to analyze.
I thought setting charge to a negative value will do the magic but setting it like .force('charge', d3.forceManyBody().strength(-30)) didn't help at all.
Problem:
Now, trying to make the groups distant apart by coding something like following from the d3.v2 example I mentioned above but having hard time cooking something similar for my d3.v4. Any good suggestions on how to dynamically keep all groups away from each other?

I ended up following https://bl.ocks.org/emeeks/302096884d5fbc1817062492605b50dd to use forceX and forceY for positioning same group nodes together - away from other groups. Problem with it is that group positions are hardcoded as following:
this.grpPositions = {
'1': {x: 100, y: 100},
'2': {x: 900, y: 200},
'3': {x: 700, y: 400},
'4': {x: 200, y: 400},
'5': {x: 500, y: 400},
'6': {x: 600, y: 500}
};
I will have to come up with some good algorithm to dynamically generate far apart group x, y co-ordinates based on total number of groups and available width and height of given SVG.
If you have a better way of doing it via d3, please feel free to post your answer and I will accept it.

Related

Attribute based rotation and offset with OpenLayers WebGL Points Layer

Using normal vector layers, I already draw directed antenna symbolizers like this:
Basically this is a point with a direction (and a horizontal beam width, but let's forget about that for now). This is what I try to recreate in OL 6.8.1 using WebGLPoints layer, but have issues with creating the correct style. I way able to rotate simple triangle symbols but they are overlapping, and they should touch by the corners:
I guess the key to the problem is setting some offset, but this is where I failed to find a working solution. This is the style I currently use:
symbol: {
symbolType: 'triangle',
size: ['interpolate', ['exponential', 2.5], ['zoom'], 6, 10, 20, 100],
color: ['case',["==", ['get','band'],2100], '#aa0000',["==", ['get','band'],3500], '#00aa00', '#0000aa' ],
rotateWithView: true,
offset: [ 0,0 ],
opacity: 0.4,
rotation: ['-', ['*', ['get','azy'] , 0.01745329251 ], 3.1415926],
}
(notes: as you can see the symbol size is zoom dependent, I use 2 different colors based on the 'band' attribute and rotate it using the 'azy' attribute (which is the desired direction in degrees, counted from N as 0, clockwise))
The rotation works okay, but for the offset, I need some help. I'm not even sure of it's affected by the rotation or not... Thanks in advance! If you have any other solution for the problem using WebGL, I'd appreciate that too!
So, here's the solution, thanks to #Mike! The trick is to multiply the offset with -1, so the corners of the triangles touch.
symbol: {
symbolType: 'triangle',
size: ['interpolate', ['exponential', 2.5], ['zoom'], 6, 10, 20, 100],
color: ['case',["==", ['get','band'],2100], '#aa0000',["==", ['get','band'],3500], '#00aa00', '#0000aa' ],
rotateWithView: false,
offset: ['array', 0, ['*', ['interpolate', ['exponential', 2.5], ['zoom'], 6, 3, 20, 48], -1] ],
opacity: 0.75,
rotation: ['-', ['*', ['get','azy'] , 0.01745329251 ], 3.1415926],
}
Note: the offset is smaller with 2 pixels that half the size. With half size, the triangles did not touch, had a few pixels gap. It looks like this:
Another solution is that I created a png with the desired symbol:
Note that the center of picture is the center of circle of which the sector was cut out, so when rotated, it will stay over the point of the rotation. The style is:
symbol: {
symbolType: 'image',
src: 'tools/testbench/olmap2/cell_symbol.png',
size: ['interpolate', ['exponential', 2.5], ['zoom'], 6, 10, 20, 100],
color: ['case',["==", ['get','band'],2100], '#aa0000',["==", ['get','band'],3500], '#00aa00', '#0000aa' ],
rotateWithView: false,
opacity: 0.75,
rotation: ['*', ['get','azy'] , 0.01745329251 ]
},
The result is:
This is somewhat slower, but I guess the 800x800px png is an overkill for this, so maybe downsizing it would solve the performance issue :) The pro side of this solution that I can create circle segments in different opening angles, so the antenna's (horizontal) beamwidth could be shown too. With the triangle symbolizer I cannot do this. (If later I could draw custom polygons in OL, that would be the perfect solution for this.)

Move axis x from bottom to top in c3 js

I am working on a project and use c3 js library (don't use d3 js)
And now I'm having a problem, that is when using c3 js, the x-axis of the graph will default to the bottom, but the design they want it to be above, so how do I handle it?
Any help on how to achieve this would be greatly appreciated.
Thanks every body
Can you set the axis min and Max to limit the displayed area? https://c3js.org/reference.html#api-axis-min
chart.axis.min({
x: -10,
y: 1000,
y2: 100
});
chart.axis.max({
x: 100,
y: 1000,
y2: 10000
});

How to display the axis to the right?

I have 3 axes (velue-axis)
Can one axis be displayed on the left
Other two axes to display on the right?
http://demos.telerik.com/kendo-ui/line-charts/multiple-axes
(In this example, the axis is located on the right, but I do not know how to configure it)
Thanks.
Granted this isn't entirely clear when you first look at the demo. The important thing to look at is this section:
categoryAxis: {
categories: [],
axisCrossingValues:[]//This is the fella you are looking for.
}
I have tweaked the demo slightly to show you one of the axis in the middle of the chart. http://dojo.telerik.com/ASidu
The number is simply the position column on the chart that the axis should be rendered. By default if this in't set then all axis should be on the left hand side as normal. but if we start applying a number greater than 0 then the axis will shift. So in the example we have 3 value axis set up:
valueAxes: [{
name: "rain",
color: "#007eff",
min: 0,
max: 60
}, {
name: "wind",
color: "#73c100",
min: 0,
max: 60
}, {
name: "temp",
min: -30,
max: 30
}],
so if we look at them from crossing the y-axis (i.e. the bottom axis) we have 31 columns available to us 1- 31 so in my tweak I have applied this to the crossingAxis:
axisCrossingValues: [32, 15, 0]
This is effectively telling each of the value axes where they should be positioned:
so:
"rain" should be at position 32
"wind" should be at position 15
"temp" should be at position 0
So the order in which you add your value axes will determine which setting they take based on the order you include them.
Hopefully that helps clear things up for you. If you need any more info let me know and I will update accordingly

D3.js breaking up overlapping shapes

I'm trying to create a chart, where the input is a list of circles (position and radius) (or better ellipses) and the overlaps of the circles become shapes and a mouseover event can be applied. I also wish for the circles to move to the front, and have a mouseover effect, almost exactly like this
http://benfred.github.io/venn.js/examples/intersection_tooltip.html
The size of the overlap does not need to be known.
I've tried using D3.js Venn diagrams by Ben Frederickson. Although I can't understand some of the chart(selection) function, I've made it so that the circles can be inputted, and are drawn fine, including the overlaps, but this still relies on having the 'data' as an input as well and all of the sets (seen in the jsonp file) are still require. I realise that I can just make a script to list all of the possible sets, but this is ideal.
http://www.benfrederickson.com/venn-diagrams-with-d3.js/
I'm struggling to understand how the code creates these overlaps and then assigns them to the set.
Cheers, Ryan
Each intersection area has an SVG path computed for it by the 'venn.intersectionAreaPath' function. It takes a list of circles and returns a path element for the intersection area.
If you already have positions for the circles, you can override the 'layoutFunction' attribute on the venn diagram object like:
var circles = [{'x' : 0, 'y': 100, 'radius' : 80},
{'x' : 0, 'y': 0, 'radius' : 90 },];
var chart = venn.VennDiagram().layoutFunction(function() { return circles; });
d3.select("#venn").datum([{sets: [0]}, {sets:[1]}, {sets:[0,1]}]).call(chart);
This still requires having a list of all possible regions that you wish to draw (like "[{sets: [0]}, {sets:[1]}, {sets:[0,1]}]"), but this way you don't need to specify sizes for the regions.

Why does ArcTo sometimes not update the current position

Background
I'm working a legacy MFC application which uses GDI draw its content.
I need to draw rounded rectangles where each corner has a (potentially) different radius.
This means that I can no longer use RoundRect and have to roll my own using ArcTo.
I'm using SetWindowExtEx, SetWindowOrgEx, SetViewportExtEx and SetViewportOrgExt to implement zooming.
This works fine in most situations.
Problem
On certain zoom levels, my code fails to construct a proper path of the outline of the roundrect.
The following screenshots is of my RoundRect code used to create a path, then used to clip a bigger rectangle (to get an idea of it's shape).
The clipping region created by this path is sometimes missing a corner, clips everything (two missing corners?) or clips nothing.
My guess is that due to rounding errors, the arcs are too small, and is skipped alltogether by GDI.
I find this hard to believe though since it is working correctly for smaller zoom factors than the ones pictured here.
Working correctly:
Missing a corner:
The Code
I have tried to reduce the code needed to reproduce it and have ended up with the following. Note that the number in the screenshots is the value of zoomFactor, the only variable.
You should be able to paste this code into the OnPaint function of a newly created Win32 application project and manually declare zoomFactor a constant.
SetMapMode(hdc, MM_ISOTROPIC);
SetWindowOrgEx(hdc, 0, 40, nullptr);
SetWindowExtEx(hdc, 8000, 6000, nullptr);
SetViewportOrgEx(hdc, 16, 56, nullptr);
SetViewportExtEx(hdc, 16 + (396)*zoomFactor/1000,
48 + (279)*zoomFactor/1000, nullptr);
BeginPath(hdc);
MoveToEx(hdc, 70, 1250, nullptr);
ArcTo(hdc,
50, 1250, 90, 1290,
70, 1250,
50, 1270);
ArcTo(hdc,
50, 2311, 90, 2351,
50, 2331,
70, 2351);
ArcTo(hdc,
1068, 2311, 1108, 2351,
1088, 2351,
1108, 2331);
ArcTo(hdc,
1068, 1250, 1108, 1290,
1108, 1270,
1088, 1250);
CloseFigure(hdc);
EndPath(hdc);
SelectClipPath(hdc, RGN_AND);
HBRUSH br = CreateSolidBrush(RGB(255,0,255));
const RECT r = {0, 0, 8000, 6000};
FillRect(hdc, &r, br);
Here is a simpler bit of code to illustrate the problem:
const int r = 20;
MoveToEx(hdc, 200, 100, 0);
BOOL b = ArcTo(hdc,
100 + 2 * r, 100,
100, 100 + 2 * r,
100 + r, 100,
100, 100 + r);
POINT p;
GetCurrentPositionEx(hdc, &p);
This draws a single corner of radius r. This works fine for non-zero values of r and the position p is correctly updated to match the end of the arc: (100, 100+r), give or take a pixel.
However, when r is zero ArcTo returns TRUE but the position is not updated: p contains the starting position of (200,100).
The documentation states that "If no error occurs, the current position is set to the ending point of the arc." The function returned TRUE indicating success so the position should have been updated.
In my view this a bug. The function should return FALSE because the rectangle is empty so there is no arc and thus no well-defined endpoint. However, it would be more useful in practice if the function returned TRUE and updated the current position to match the final coordinate pair in the parameter list. But it does neither of these things. EDIT: An even better implementation in your case would be to calculate the arc end points in logical coordinates before converting to device coordinates, but GDI in general doesn't work like this.
The problem occurs in your code because your coordinate transformation collapses the second arc's rectangle to an empty rectangle when the zoom is 266. You can see this yourself by adding the following to your code to transform the coordinates of the second arc:
POINT points[4] = {{50,2311},{90,2351},{50,2331},{70,2351}};
LPtoDP(hdc, points, 4);
With the zoom set to 266 the points are transformed to (17,90), (17,91), (17,91), (17,91) so the rectangle has no width and is empty. And you hit the ArcTo bug.
I guess it works for smaller zooms when the rounding happens to put the x-coordinates into adjacent integers rather than the same integer.
A simple fix would be to create a MyArcTo function that replaces the arc with a LineTo when it is too small to be visible.

Resources