How to align the jointjs graph to the center / middle of the paper horizontally with the flow direction from top to bottom? - jointjs

I have a graph generated using JointJs and it uses Dagre Layout provided by jointjs. Currently, the graph gets aligned from Top To Bottom which is the correct way but then the complete graph gets aligned to the left of the paper.
I need the graph to be aligned in the middle of the Paper Horizontally.
Current configuration used is this:
joint.layout.DirectedGraph.layout(graph,
{
rankDir: "TB",
marginX: 30,
marginY: 30,
clusterPadding: {
top: 10,
left: 10,
right: 10,
bottom: 10
}
}
I see that the option to make graph flow from Top to bottom is given rankDir: "TB". Is there any similar option which can align it horizontally to the middle instead of left which is by default? or any other way which can do so.

After calling directedGraph.layout you can then use Paper class to center the diagram.
const paper = new joint.dia.Paper({
height: 1000,
width: 1000,
...
});
const paperArea = paper.getArea();
const contentArea = paper.getContentArea();
paper.translate((paperArea.width - contentArea.width) / 2, (paperArea.height - contentArea.height) / 2);
This will effectively center the diagram, although care should be taken to avoid the content being larger than the paper size.

Related

Adding direction handles to geoman markers

I would like to add a direction handle to [some] geoman leaflet markers. There are packages out there already like leaflet-editable-marker that add things like direction/transformation rulers etc to leaflet.
I want [some] outline markers for my polylines to have a direction, and I want to set this direction using those "direction handles" rulers. The rulers should look like an arrow of sorts - a short line and a head.
To set directions, I am thinking about handling dragstart, drag and dragend events.
When users click on such a marker, the editor would show the direction handle; a subsequent click will hide this handle again. Initially, my thinking was to do something like the below:
marker.on('click', (e) => {
const latlng = marker._latlng;
const directionHandleMarker = new L.circleMarker(latlng, {
draggable: true,
radius: 20,
img: {
url: 'icon.png', //image link
size: [40, 40], //image size ( default [40, 40] )
rotate: 10, //image base rotate ( default 0 )
offset: { x: 0, y: 0 }, //image offset ( default { x: 0, y: 0 } )
}
});
...
map.addLayer(directionHandleMarker);
});
The circleMarker is from 'leaflet-canvas-markers' package. It is also pretty trivial to build this arrow marker functionality without a plugin, and there are multiple reference implementations.
However, this won't give me what I want, will it? Since it is a marker, I could not drag it to adjust the angle, or could I? The geoman rotate mixin kind of does it, using cursor position to calculate the angle and using that to place the marker, rather the placing marker based on the cursor position. Would I need to drag the end of an arrow and recompute offset?
Should I add a two-point LineString and fix one point on the marker and use the other end for dragging, or is there a way to make markers [without a polyline] work?

FabricJS: Issue when changing objects coordinates inside a Group after some Transformations

I am looking for a way to space (and move in general) objects inside a group regardless of their transformation.
In the small example i have made below, you will see 2 circles and I am attempting to space them by a certain factor.
It is ok until I rotate or resize the group and it is still ok the very first time the coordinates are changed after a transformation but calling the same function twice while the spacing is still correct, the group starts jumping all over the places..
I am using Fabric JS version: 1.7.7
Here is a sample:
https://jsfiddle.net/u0patgck/
console.clear();
var canvas = new fabric.Canvas('root');
var circles = [];
circles.push(new fabric.Circle({id: "circle1", radius: 20, fill: 'green', left: 150, top: 100}));
circles.push(new fabric.Circle({id: "circle2", radius: 20, fill: 'green', left: 200, top: 100}));
g = new fabric.Group(circles);
//once circles are added to group their coordinates change so I store their new original base coordinates in custom variables
g.forEachObject(function(obj){
obj.originalLeft = obj.getLeft();
obj.originalTop = obj.getTop();
}, g);
canvas.add(g);
canvas.renderAll();
function moveCircles(){
var space = parseFloat(document.getElementById("spacingPixels").value);
g.forEachObject(function(obj){
switch (obj.id){
case "circle1":
obj.setLeft(obj.originalLeft - space)
break;
case "circle2":
obj.setLeft(obj.originalLeft + space)
break;
default:
}
})
g.addWithUpdate();
canvas.renderAll()
}
Steps to Reproduce:
Test 1 (ok):
Click the "Move Circles" button as many times you want (circles coordinates are recalculated and the group is not "misbehaving".
Test 2 (ok first time but not second time):
Rotate the group first.
Click the move circles button ONE time (all is still good)
Click the move circles button another time (you will see the group starts moving by itself around the canvas).
Expected Behaviour:
The group should not move around or change the order of the objects inside.
The group moves erratically.
At the moment I am caching the transformations manually and restoring them after changing the coordinates (like below)
https://jsfiddle.net/0tdg1dof/
var a = {scaleX: g.getScaleX(), scaleY: g.getScaleY(), skewX: g.getSkewX(), skewY: g.getSkewY(), flipX: g.getFlipX(), flipY:g.getFlipX(), angle: g.getAngle()};
fabric.util.resetObjectTransform(g);
g.addWithUpdate();
g.setScaleX(a.scaleX);
g.setScaleY(a.scaleY);
g.setSkewX(a.skewX);
g.setSkewY(a.skewY);
g.setFlipX(a.flipX);
g.setFlipY(a.flipY);
g.setAngle(a.angle);
Is there a better way to do this?
Thank you.
AlessandroDM, I believe you did right solution, because you are manipulating just only group object. If you will not manipulate (restore) group objects (parent) you will need to do manipulation each child inside the group like angle, scale, etc. Also, you have one minor bug, if you will rotate group, and after that resize it (not diagonal controls, scaleX should not be equal to scaleY) and then click "Move Circles" your group will behave as you described in Test 2. In order to fix that you will need to set scaleX and scaleY values after all your values which you want to set:
var a = {scaleX: g.getScaleX(), scaleY: g.getScaleY(), skewX: g.getSkewX(), skewY: g.getSkewY(), flipX: g.getFlipX(), flipY:g.getFlipX(), angle: g.getAngle()};
fabric.util.resetObjectTransform(g);
g.addWithUpdate();
g.setSkewX(a.skewX);
g.setSkewY(a.skewY);
g.setFlipX(a.flipX);
g.setFlipY(a.flipY);
g.setAngle(a.angle);
g.setScaleX(a.scaleX);
g.setScaleY(a.scaleY);
It is not a fabricjs bug setting scale after all manipulations, even native HTML5 canvas required scaling after all manipulations.

How to animate rotate around center in fabric js

When you use one of the handle bars to rotate an object in fabricjs it will rotate the object around center. However, when using
tri.animate('angle', 45, {
onChange: canvas.renderAll.bind(canvas)
});
It will rotate around the top-left corner. How to rotate around center just like with the handle bar?
Example: http://jsfiddle.net/QQ6eA/1/
Add:
originX: 'center',
originY: 'center',
to your triangle creation code.
If you rotate an image with his default origin, this will do it around top left corner. We don't want to set the origin of the image on the center all the time, because it will affect all the functionalities (for example, its left and top positions).
So, we can set the origin to center when we need it and then set it again to the top-left corner and still use all the others functionalities normally.
const target = canvas.getActiveObject(); //or your target
const angleValue = 10; //your angle
target._setOriginToCenter();
target.set('angle', angleValue).setCoords();
target._resetOrigin();

How do I read the properties of a KineticJS Sprite other than X and Y coords?

I am new to using KineticJS and am using it to develop a simple 2D game which involves sprites, collision detection etc.
I need to access various properties of my sprite object (let's call it spriteObj here).
I can get to the sprite coordinates using spriteObj.getX() and spriteObj.getY() and this works perfectly.
Now I need to also retrieve the other properties of the sprite, such as height and width, but there does not seem to be a way to do it. Specifically if I have an animation within my sprite, how can I retrieve the current frame and/or the x, y, height, width of that animation frame.
What I really need is something like spriteObj.getWidth() and spriteObj.getHeight() but these do not exist. Any suggestions please? I can see nothing on the html5 tutorials page for this (see here)
Thanks,
Owen
The methods you are looking for do exist, check the KineticJS Sprite API
Here is the KineticJS Sprite Tutorial
Here are the functions you asked for:
Sprite.getX()
Sprite.getY()
Sprite.getWidth()
Sprite.getHeight()
Sprite.getIndex()
Based off of that tutorial I added some code to fit your needs, check the console.log to see the methods in action. For the width and height, although it is not necessary, you have to specify a width and height property on your spriteObj or else it will just return 0.
var blob = new Kinetic.Sprite({
x: 250,
y: 40,
image: imageObj,
animation: 'idle',
animations: animations,
frameRate: 7,
index: 0,
width: 80,
height: 100
});
And a new button to test the getIndex() function:
// stop animation
document.getElementById('stop').addEventListener('click', function () {
alert(blob.getIndex());
}, false);
jsfiddle

QML Row vs. RowLayout

I'm trying to write a topbar for my application that should contain mainly the app logo (a small image) and the app title (just text). Moreover, I'd like this topbar to automatically resize according to the window's height.
I'm new to QML, but I suppose that I should wrap these components inside a Row or a RowLayout component. This is my sample code:
import QtQuick 2.0
import QtQuick.Layouts 1.0
Rectangle
{
id: mainwindow
width: 1024
height: 600
Row
{
id: rowlayout
height: logoimage.height
spacing: 5
property int count: 3
anchors
{
left: parent.left
right: parent.right
top: parent.top
}
Image
{
id: logoimage
source: "qrc:/images/resources/images/icon.png"
height: mainwindow.height / 20
anchors.top: parent.top
anchors.left: parent.left
}
Text
{
id: logotext
text: qsTr("This is my logo text")
font.pixelSize: parent.height
font.family: "Sans Serif"
height: parent.height
verticalAlignment: Text.AlignVCenter
anchors.top: parent.top
anchors.left: logoimage.right
}
/*
Rectangle
{
id: otherrect
height: parent.height
color: "lightgreen"
anchors.top: parent.top
anchors.left: logotext.right
anchors.right: parent.right
}
*/
}
}
I tell to the Row component that its height should follow the logo's height, and to the Image (logo) component that its height should be 1/20th of the Rectangle (mainwindow) component.
Using a Row container, the code behaves as expected but I get an annoying warning (QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row. Row will not function.) and I have to do a lot of anchoring. Conversely, if I use a RowLayout container, I can remove most of the anchors but the Image completely ignores its height attribute (but the text still resizes correctly). So the questions are:
is this a bug of the RowLayout component? I'm using Qt-5.1.0-Beta with Android support, so this could be an explanation
how can I use a Row component without using anchors in its children and thus avoid the warning?
I'm missing something important or I'm almost on the right track but I have to bear with this beta of Qt until a stable version is released?
You said that you get the expected behavior with Row, so you should probably use it. The warning that Row is giving is asking you to remove the vertical anchors (top and bottom) from its child elements.
The Row element provides horizontal (left and right) anchor-like behavior for its child elements, but it doesn't mind if you use top and bottom anchors (notice that top and bottom were not in the warning).
In other words remove "anchors.left" and/or "anchors.right" lines from "logoimage", "logotext", and "otherrect" (if you plan on uncommenting it at some point), but not the "anchors.top" lines, and that should stop the warning and keep the correct behavior.
An alternative is to just remove the Row element and use Item or FocusScope (if you plan on having input elements in your "top bar" area), which will not try to take over anchoring operations, and that may be a better fit for you if you really like anchors.
You need to give a width to your layout if you want it to strecht its children, either with width: parent.width, or better, with anchors { left: parent.left; right: parent.right } and no anchors on vertical lines inside childrens of the layout.
1) NO, it is no a bug of RowLayout
2) Consider that RowLayout is preferred to Row because is most expressive for components placing. The Row component is better that Rowlayout only for graphics or animation apps
3) The stable version is now available, but your errors are not bugs ;)

Resources