Does anyone here use the library react-d3-graph?
https://danielcaldas.github.io/react-d3-graph/docs/index.html
I'm trying to find a way to give my nodes starting positions, I'm unsure how their positions are set upon render right now. Any idea how to do so ?
Thanks
I couldn't find it in the documentation, but you can just add an x and y property to a node to give it an initial position. (Works whether you set the prop staticGraph or not.)
The following will place node 'a' above node 'b' every time, instead of placing them randomly.
<Graph
id="test-graph"
data={{
nodes: [
{ id: 'a', x: 0, y: 0 },
{ id: 'b', x: 0, y: 1 },
],
links: [{ source: 'a', target: 'b' }],
}}
/>
It's also possible to have coordinates on only some nodes. Then the ones without coordinates will be placed randomly.
Related
I have a bubble chart with multiple datasets. Two points of two different datasets may have the same coordinates (x and y-value) and lay on the same place in the chart. Because the display order of the points is determined according the order of the datasets, the smaller point could be completely covered by the bigger point in front of it.
Is there a option or a way, to display the points in order of their bubble size?
Simplified example of four points. The solution must also work for multiple datasets with each 30+ points.
I am searching a solution to draw the blue point in front of the red point, for the left pair and let the right pair as it is. This order must be independent of the order of the datasets, as it is per point and not per dataset.
Sorting the datasets seems to be no option for me, as the order cannot be determined per dataset, but instead must be determined for every coordinate/point. When drawing a point, it must be checked for this particular coordinate, if any other point with the same coordinates exists and if this point is greater than the current point (if true, the greater point must be drawn before, to not cover up the current point).
const config = {
type: 'bubble',
data: {
datasets: [{
label: 'Dataset 1',
data: [{
x: 1,
y: 1,
r: 20
},
{
x: 2,
y: 1,
r: 15
}
],
borderColor: 'red',
backgroundColor: 'red'
},
{
label: 'Dataset 2',
data: [{
x: 1,
y: 1,
r: 15
},
{
x: 2,
y: 1,
r: 20
}
],
borderColor: 'blue',
backgroundColor: 'blue'
}
]
},
options: {
responsive: true,
scales: {
x: {
suggestedMin: 0,
suggestedMax: 3
}
},
plugins: {
legend: {
position: 'top',
}
}
}
};
var ctx = document.getElementById('chartJSCanvas').getContext('2d');
const chart = new Chart(ctx, config);
<body>
<canvas id="chartJSCanvas" width="300" height="100"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.1.0/chart.js" integrity="sha512-LlFvdZpYhQdASf4aZfSpmyHD6+waYVfJRwfJrBgki7/Uh+TXMLFYcKMRim65+o3lFsfk20vrK9sJDute7BUAUw==" crossorigin="anonymous"></script>
</body>
The easiest way would be to just sort the data in the datasets and then the datasets themselves before drawing them.
An easy way to do this is provided by Array.prototype.forEach and Array.prototype.sort
First sort the data within each dataset like this:
config.data.datasets.forEach(function(element){
element.data.sort(function (a, b) {
return a.r - b.r;
});
});
Then you can sort the data sets by their smallest element like this:
config.data.datasets.sort(function (a, b) {
return a.data[0].r - b.data[0].r;
});
After that, you can regularly pass your config object with ordered datasets to your library call just the way you do it above:
const chart = new Chart(ctx, config);
How to make x-grid line named "Today" to be shown only to some y value (for example from y =0 to y=25)
grid: {
x: {
lines: [
{value: 4, text: 'Today',position: 'start'},
{value: 12}
]
},
Fiddle: https://jsfiddle.net/KrisKuliv/74fkuyw4/1/
C3.js does not support this behaviour. You can only customize these lines with own css classes which doesn't include the length of the lines because svg doesn't work this way.
The only way to get the wanted behaviour is using d3 directly. You can give your lines some classes to make them easier to select.
grid: {
x: {
lines: [
{value: 4, text: 'Today',position: 'start', class: 'myClass'},
{value: 12}
]
}
d3.selectAll(".myClass")
I haven't done something like this yet but I hope this will help you.
I have a working OOP code that recursively renders a composition of graphical elements to a canvas. There's quite a bit to dislike about it and I'm trying to see what a functional version will look like.
Sure, one can write a specialised recursive pure function, but as the framework involves similar algorithms, I'd like to:
Harness the power of function composition.
See how FP - and its data piping paradigm (transforming data through pure functions) - lands itself to more complex structures than lists (trees/graphs) and less trivial algorithms (than say, finding all odd number by sequentially iterating the list).
Inspired by Lazy.js, I've started coding and got this far:
LazyTree.from( drawing )
.keepNodes( visible )
.keepChildrenOf( nonClipping )
.traverse( log );
But as for map and fold - I have many unanswered questions.
Goal
Here's a simplified version of the problem I'm trying to solve:
Data
A composition (hierarchy) of rectangles. The bounds of each are in relative coordinates (to its parent):
const drawing = {
name: 'Face',
bounds: { x: 10, y: 10, w: 100, h: 100 },
children: [{
name: 'Left eye',
bounds: { x: 10, y: 10, w: 20, h: 20 }, // Abs: (20, 20, 20, 20)
children: [{
name: 'Left pupil',
bounds: { x: 5, y: 5, w: 10, h: 10 } // Abs: (25, 25, 10, 10)
}]
},{
name: 'Right eye',
bounds: { x: 70, y: 10, w: 20, h: 20 }, // Abs: (80, 20, 20, 20)
children: [{
name: 'Right pupil',
bounds: { x: 5, y: 5, w: 10, h: 10 } // Abs: (85, 25, 10, 10)
}]
}]
};
Task - getAbsoluteBounds
The task is to convert this composition to one that has absolute coordinates (as shown in the comments).
Issues and thoughts
Fold?
The absolute coordinates of a child is its relative coordinates transposed by its parent absolute coordinates. So a fold with its accumulator are candidates to do this.
But fold is associated with catamorphism and verbs like combine, and typically returns a single value.
The transformation in question takes a tree and returns an identical structure but with different values - so it sounds more like a map, but one that needs an accumulator.
As far as the accumulator goes, it is worth noting that all the children of a specific node should get the same accumulator. For the data above, both Left eye and Right eye should get the same absolute coordinates of Face (as opposed to the Right eye getting the returned accumulator of Left eye in depth-first-traversal).
Another thing I'm not clear about is who should be in charge of constructing the output tree. Should it be the high-order functions (fold, map, or whatever), or should it be the aggregator?
Stop conditions
Related the the previous section, consider all rectangles to clip their children, and the following composition:
const drawing = {
name: 'Parent',
bounds: { x: 10, y: 10, w: 10, h: 10 },
children: [{
name: 'Child',
bounds: { x: 1000000, y: 1000000, w: 10, h: 10 },
children: [{
name: 'Grandchild',
bounds: { x: 5, y: 5, w: 5, h: 5 }
}]
}]
};
The Child bounds are out-of-bound with relation to its parent (Parent), so branch traversal should stop when traversing to Child (no point traversing to Grandchild).
The question is: How can this be implemented with a fold function? One solution is to stop branch traversal when the accumulator returns an agreed valued (say undefined). But this is somewhat a departure from the fold API for lists.
Pre and post visit
The rendering algorithm involves:
fill( shape );
renderChildren( shape );
stroke( shape );
I wonder how this can be achieved with something like traverse() or each(). Should these take 2 callbacks (pre, post)?
Traversal strategies
Tree traversal may be:
Depth or Breadth first.
Top-down or Bottom-up (for the latter, see a specialised example for transforming an AST, using reduce).
With lists, we have functions like reverse(). Lazy.js allows adding a custom iterator that can then be chained.
So it seems the the FP way to handle traversal strategy is a transforming function. Is there anything else to it?
Summary
I've touched upon a few of the challenges in implementing a rendering algorithm for a tree structure using the data piping model.
I question if any other FP approaches would be more appropriate here? And perhaps the data piping model is not fit for these sort of problems. Or perhaps, I should simply forget the APIs one sees in FP libraries (that deal nearly exclusively lists) and create one that is appropriate for the task at hand (eg, having a map function that also involves an accumulator).
I couldn't find any FP library dedicated for trees, and information out there is typically limited to very simple problems.
So hopefully, someone would reply with something along the lines of 'this is how it should be done'.
As far as I have understood the details you might do as follows.
It will proceed traversing those items remaining within the parent's boundaries, converting their coordinates to absolute and rendering them just afterwards. However if the child's boundaries overlaps the parent's boundaries the child and it's descendants are skipped. No conversion to absolute coordinates and rendering are done for those.
function render(bounds){
console.log("Rendered:", bounds);
}
function relToAbs(o, b = {x: 0, y:0, w:Infinity, h:Infinity}, go = true){
go = o.bounds.x < b.w && o.bounds.y < b.h ? (o.bounds.x += b.x, o.bounds.y += b.y, render(o.bounds), go) : !go;
o.children && go && (o.children = o.children.map(p => relToAbs(p,o.bounds,go)));
return o;
}
var drawing = { name: 'Face',
bounds: { x: 10, y: 10, w: 100, h: 100 },
children: [{ name: 'Left eye',
bounds: { x: 200, y: 10, w: 20, h: 20 }, // Abs: (20, 20, 20, 20)
children: [{ name: 'Left pupil',
bounds: { x: 5, y: 5, w: 10, h: 10 } // Abs: (25, 25, 10, 10)
}]
},
{ name: 'Right eye',
bounds: { x: 70, y: 10, w: 20, h: 20 }, // Abs: (80, 20, 20, 20)
children: [{ name: 'Right pupil',
bounds: { x: 5, y: 5, w: 10, h: 10 } // Abs: (85, 25, 10, 10)
}]
}]
};
console.log(JSON.stringify(relToAbs(drawing),null,2));
In the impact.js framework, what is the structure of level objects produce by the Weltmeister level editor ?
The most information I was able to find on this is the documentation for ig.game.loadlevel, but it is very limited. In particular what is the meaning of the conent of a layer's data property ?
The example from the docs:
{
entities: [
{type: "EntityClassName", x: 64, y: 32, settings: {}},
{type: "EntityClassName", x: 16, y: 0, settings: {}},
],
layer: [
{
name: "background1",
tilesetName: "media/tiles/biolab.png",
repeat: false,
distance: 1,
tilesize: 8,
foreground: false,
data: [
[1,2,6],
[0,3,5],
[2,8,1],
]
},
]
}
The level object has 2 sections, the entities and layers, both of which are arrays. The entities array contains entities that will spawned during level load. In the example above this is equivalent to calling ig.game.spawnEntity(EntityClassName, 64, 32, {})
The objects in the layers array create either an ig.BackgroundMap or an ig.CollsionMap, depending on the name of the map. If the name is "collision", then the game will create a ig.CollisionMap at ig.game.collisionMap. If it's anything else, then it will create an ig.BackgroundMap and add it to the ig.game.backgroundMaps array.
The data property of the layer structure is the tile map itself. The array determines which tile from the tileset to draw.
I want to add a cytoscape node at the location of mouse arrow, on a click event on the canvas.
How can I do this?
My approach: (not working so well)
I am able to create a node on a click but I am not able to make sure the position of the created node is at the place where I have clicked.
Using something like this:
$("#cy").click(function(event){
pos = getMousePosition(this, event)
cy.add([
{ group: "nodes", data: { id: "testid" }, position: pos },
]);
});
I have not been able to define getMousePosition() correctly. How should I define this function to get the node rendered at the right position irrespective of the location of the cytoscape canvas?
The mouse works with rendered coordinates on the screen. If you would like to add a node at a specific rendered position, it would be easiest to specify the new node position in rendered coordinates, e.g.:
cy.add([
{ group: "nodes", data: { id: "testid" }, renderedPosition: rpos } // , ...
]);
It then becomes trivial to pass the rendered mouse position (relative to the cy.js div), e.g. http://api.jquery.com/position/
In cytoscape.js 3.2, there are convenience functions for this:
cy.on("tap", function(e) {
cy.add([{
group: "nodes",
id: "testid",
renderedPosition: {
x: e.renderedPosition.x,
y: e.renderedPosition.y,
},
});
});
This ends up being equivalent to (assuming you've set container: $("#cy-container")):
cy.on("tap", function(e) {
let canvas = $("#cy-container").find("canvas").get(0);
cy.add([{
group: "nodes",
id: "testid",
renderedPosition: {
x: e.originalEvent.pageX - $(canvas).offset().left,
y: e.originalEvent.pageY - $(canvas).offset().top,
},
});
});
It was not clear to me how to use the jQuery's position funciton in order to get the mouse coordinates, as the #maxfranz suggested.
The code I use is:
$("#cy").click(function(event) {
var pos = {x: event.offsetX, y: event.offsetY};
cy.add([
{ group: "nodes", data: { id: "testid" }, renderedPosition: pos },
]);
});