how to enable/draw grid lines for JointJS graph - jointjs

Does JointJs has an option to enable/draw grid lines for a graph?
If it doesn't, is there a way to customize the implementation and draw grid lines?

According to jointjs changelog you can now display grid via drawGrid option of joint.dia.paper.
Here's an example :
var paper = new joint.dia.Paper({
el: $('#myholder-small'),
width: 600,
height: 100,
model: graph,
gridSize: 15,
drawGrid:true
});
PS : jointjs doc doesn't explicit this option.

Here's one way to draw grid lines in JointJS paper, taking advantage of the HTML 5 canvas element:
function setGrid(paper, gridSize, color) {
// Set grid size on the JointJS paper object (joint.dia.Paper instance)
paper.options.gridSize = gridSize;
// Draw a grid into the HTML 5 canvas and convert it to a data URI image
var canvas = $('<canvas/>', { width: gridSize, height: gridSize });
canvas[0].width = gridSize;
canvas[0].height = gridSize;
var context = canvas[0].getContext('2d');
context.beginPath();
context.rect(1, 1, 1, 1);
context.fillStyle = color || '#AAAAAA';
context.fill();
// Finally, set the grid background image of the paper container element.
var gridBackgroundImage = canvas[0].toDataURL('image/png');
paper.$el.css('background-image', 'url("' + gridBackgroundImage + '")');
}
// Example usage:
setGrid(paper, 10, '#FF0000');

Related

Polygon rendering with pixijs

I'm trying to display more than 6000 Polygons on a mobile device.
Currently, I'm doing this with SVG paths in Android WebView using the d3.js library.
It works but I have to deal with performance issues, my map becomes very laggy when I drag my map or zoom.
My Idea now is to try the same with pixijs. My data comes originally from ESRI Shapefiles. I'm convert these Shapefiles to GeoJSON and then to SVG. My array of vertices looks like this, which I'm trying to pass to the drawPolygon function
0: 994.9867684400124
1: 22.308409862458518
2: 1042.2789743912592
3: 61.07148769269074
But when I try to render these polygon nothing being displayed. This is my code:
var renderer = PIXI.autoDetectRenderer(1800, 1800, { backgroundColor: 0x000000, antialias: true });
document.body.appendChild(renderer.view);
var stage = new PIXI.Container();
var graphics = new PIXI.Graphics();
var totalShapes = feat.features.length;
for (var i = 1; i <= totalShapes -1; i++) {
var shape = feat.features[i];
var geometry = shape.geometry.bbox;
graphics.beginFill(0xe74c3c);
graphics.drawPolygon([ geometry]);
graphics.endFill();
stage.addChild(graphics);
renderer.render(stage);
}
Can someone help me or could suggest me a different way?
I have not seen that way of initializing a pixie project.
Usually you add the application to the html document like:
var app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x2c3e50
});
document.body.appendChild(app.view);
If you do this you can add your draw calls to the setup of the application:
app.loader.load(startup);
function startup()
{
var g = new PIXI.Graphics();
g.beginFill(0x5d0015);
g.drawPolygon(
10, 10, 120, 100, 120, 200, 70, 200
);
g.endFill();
app.stage.addChild(g);
}
This will render the polygon once.

How to get top left of an object inside group fabricjs

How can I get canvas-relative position (top, left) of triangle inside an group as bellow image?
I followed this topic: How to get the canvas-relative position of an object that is in a group? but it only right when group is not rotated.
Working example you may find here: http://jsfiddle.net/mmalex/2rsevdLa/
Fabricjs provides a comprehensive explanation of how transformations are applied to the objects: http://fabricjs.com/using-transformations
Quick answer: the coordinates of an object inside the group is a point [0,0] transformed exactly how the object in the group was transformed.
Follow my comments in code to get the idea.
// 1. arrange canvas layout with group of two rectangles
var canvas = new fabric.Canvas(document.getElementById('c'));
var rect = new fabric.Rect({
width: 100,
height: 100,
left: 50,
top: 50,
fill: 'rgba(255,0,0,0.25)'
});
var smallRect = new fabric.Rect({
width: 12,
height: 12,
left: 150 - 12,
top: 50 + 50 - 12 / 2 - 10,
fill: 'rgba(250,250,0,0.5)'
});
// 2. add a position marker (red dot) for visibility and debug reasons
var refRect = new fabric.Rect({
width: 3,
height: 3,
left: 100,
top: 100,
fill: 'rgba(255,0,0,0.75)'
});
var group = new fabric.Group([rect, smallRect], {
originX: 'center',
originY: 'center'
});
canvas.add(group);
canvas.add(refRect);
canvas.renderAll();
// 3. calculate coordinates of child object in canvas space coords
function getCoords() {
// get transformation matrixes for object and group individually
var mGroup = group.calcTransformMatrix(true);
// flag true means that we need local transformation for the object,
// i.e. how object is positioned INSIDE the group
var mObject = smallRect.calcTransformMatrix(true);
console.log("group: ", fabric.util.qrDecompose(mGroup));
console.log("rect: ", fabric.util.qrDecompose(mObject));
// get total transformattions that were applied to the child object,
// the child is transformed in following order:
// canvas zoom and pan => group transformation => nested object => nested object => etc...
// for simplicity, ignore canvas zoom and pan
var mTotal = fabric.util.multiplyTransformMatrices(mGroup, mObject);
console.log("total: ", fabric.util.qrDecompose(mTotal));
// just apply transforms to origin to get what we want
var c = new fabric.Point(0, 0);
var p = fabric.util.transformPoint(c, mTotal);
console.log("coords: ", p);
document.getElementById("output").innerHTML = "Coords: " + JSON.stringify(p);
// do some chores, place red point
refRect.left = p.x - 3 / 2;
refRect.top = p.y - 3 / 2;
canvas.bringToFront(refRect);
canvas.renderAll();
}
a very simple way to get topleft is
var cords = object._getLeftTopCoords();
cords.x and cord.y will give you the result

how to stretch image size to match fabric.Image object

I am using fabricjs to implement image editing and I try to use a fabric.Image object as the background image of canvas to store the data. And the following is the code:
var canvas = new fabric.Canvas('canvasId');
var imageObject = new fabric.Image($originImage);
canvas.add(imageObject);
but I found the $originImage's size is much larger than canvas' size and also imageObject's size, so the canvas can only show part of the image. I want to know how to stretch the $originImage to adapt the canvas then canvas can display all of the $originImage?
Here what I have done
$canvas.width = $originImage.clientWidth;
$canvas.height = $originImage.clientHeight;
var fabricCanvas = new fabric.Canvas('canvasId');
// canvas.setFabricCanvas(fabricCanvas);
var imageObject = new fabric.Image($originImage);
// fabricCanvas.add(imageObject);
// fabricCanvas.isDrawingMode = true;
fabricCanvas.setBackgroundImage(imageObject, fabricCanvas.renderAll.bind(fabricCanvas), {
scaleX: fabricCanvas.width / $originImage.naturalWidth,
scaleY: fabricCanvas.height / $originImage.naturalHeight
});
the upper is my related code and below is the display:
it is solved, and the previous question is because I resize the $originImage first, so when I input the image src as setBackgroundImage's parameter, it can display normally.
For version 2.0 of fabricjs, they have changed the way to handle width/height compared to previous version. If you apply width/height, then it basically crop the image to that particular size compared to the original image size. In order to resize it to a proper size, you have to switch to use scaleX/scaleY like other people have suggested.
For example, I use image.fromUrl to load an image and set it to be the background:
fabric.Image.fromURL(imgUrl, function(img) {
img.set({
scaleX: currentWidth / img.width,
scaleY: currentHeight / img.height,
top: topPosition,
left: leftPosition
originX: 'left', originY: 'top'
});
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
});
where currentWidth/currentHeight can be your canvas size if you want full background or they can be a specific width/height that you want (in my project, I want to do letter-boxed image instead so I set the width and height based on my letter-boxing algorithm), and top/left is the location that you want to place your image in the canvas (leave it none if you want to be full background image). Do not set any width and height since that will crop the scaled image rather than setting the image to be the exact size.
If this does not work, check the backgroundImage object from the canvas and see if its dimension and scale properties are different compared to a manual calculation. If you take a look, you will see the width and height properties are your natural image width and height, but it will have scaleX and scaleY less than 1 if your canvas size is less than the image size or more than 1 if it is bigger.
Also I see you are loading image directly into an image object. That might be different compared to load the data and set it directly using setBackgroundImage.
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
scaleX: canvas.width / $originalImg.naturalWidth,
scaleY: canvas.width / $originalImg.naturalHeight
});
use scaleX,scaleY to resize.
(function() {
var $originalImg = $('#originalImage')[0];
console.dir($originalImg)
var canvas = this.__canvas = new fabric.Canvas('c');
var img = new fabric.Image($originalImg);
canvas.setBackgroundImage(img,canvas.renderAll.bind(canvas),{
scaleX:canvas.width/$originalImg.naturalWidth,
scaleY:canvas.width/$originalImg.naturalHeight
});
})();
canvas{
border-width: 1pz;
border-style: solid;
border-color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id='originalImage'src='http://fabricjs.com/assets/pug_small.jpg'>
<canvas id='c' width=200 height=200></canvas>
I am using the following script to ensure the image is no larger than the canvas width:
var aspect = image.width / image.height; //Aspect ratio of image
new fabric.Image(image, {
scaleX: canvas.width / image.width,
scaleY: canvas.width / (image.height * aspect)
});

Applying clipTo path on image in fabric.js incorrectly repositions the image

Please take a look at this fiddle:
http://jsfiddle.net/2ktvyk4e/5/
var imgUrl, snapshotCanvas;
imgUrl = 'http://cdn-development.wecora.com/boards/backgrounds/000/001/388/cover/ocean-background.jpg';
snapshotCanvas = new fabric.StaticCanvas('snapshotCanvas', {
backgroundColor: '#e0e0e0',
width: 1000,
height: 1500
});
fabric.Image.fromURL(imgUrl, function(img) {
img.set({
width: 1000,
left: 0,
top: 0,
clipTo: function(ctx) {
return ctx.rect(0, 0, 1000, 400);
}
});
return snapshotCanvas.add(img).renderAll();
}, {
crossOrigin: 'Anonymous'
});
It's pretty simple. I'm loading an image and then trying to clip it so that the full width of the canvas but clipped so only the top 400 pixels are showing. For some reason, the clipTo causes the image to move and resize inexplicably:
As you can see, when the clipping path is applied the image is repositioned on the canvas inexplicably. If I remove the clipTo, then the image loads full canvas width no problem (of course its also full height, which we don't want).
I have no idea what is happening here or why this is occuring so any help is appreciated.
Make sure you have originX and originY left to top and left on both the canvas and your image. Also - you don't really need to clip anything to the canvas. The only real use I've found for clipTo was clipping collage images to their bounding shapes. If you want to restrict the image from being dragged above that 400px, I would recommend rendering a rectangle below it (evented = false, selectable = false) and then clipping to that rectangle.
I put this together without clipTo (and changed some numbers so I wasn't scrolling sideways). It renders the image half way down the canvas.
http://jsfiddle.net/2ktvyk4e/6/
Edit:
I dug through some source code to find the clipByName method and the two helper methods for finding stuff. I use this for keeping track of collage images and their bounding rectanlges ("images" and "clips"). I store them in an object:
imageObjects: {
'collage_0': http://some.tld/to/image.ext,
'collage_1': http://some.tld/to/image2.ext
}
Helper methods for finding either the clip or image:
findClipByClipName: function (clipName) {
var clip = _(canvas.getObjects()).where({ clipFor: clipName }).first();
return clip;
},
findImageByClipFor: function (clipFor) {
var image = _(canvas.getObjects()).where({ clipName: clipFor }).first();
return image;
},
Actual clipping method:
clipByName: function (ctx) {
this.setCoords();
var clipRect, scaleXTo1, scaleYTo1;
clipRect = collage.findClipByClipName(this.clipName);
scaleXTo1 = (1 / this.scaleX);
scaleYTo1 = (1 / this.scaleY);
ctx.save();
var ctxLeft, ctxTop;
ctxLeft = -(this.width / 2) + clipRect.strokeWidth;
ctxTop = -(this.height / 2) + clipRect.strokeWidth;
ctx.translate(ctxLeft, ctxTop);
ctx.rotate(degToRad(this.angle * -1));
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
ctx.rect(
clipRect.left - this.oCoords.tl.x,
clipRect.top - this.oCoords.tl.y,
clipRect.width,
clipRect.height
);
ctx.closePath();
ctx.restore();
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
},
And finally adding images to canvas where all of this comes together:
var clipName, clip, image;
clipName = helpers.findKeyByValue(url, this.imageObjects);
clip = this.findClipByClipName(clipName);
image = new Image();
image.onload = function () {
var collageImage = new fabric.Image(image, $.extend({}, collage.commonImageProps, {
left: clip.left,
top: clip.top,
clipName: clipName,
clipTo: function (ctx) {
return _.bind(collage.clipByName, collageImage)(ctx);
}
}));
collage.scaleImagesToClip(collageImage);
canvas.add(collageImage);
};

Draw a rectangle with pixi.js

[Chrome v32]
How to draw a basic RED rectangle with PIXI.js library ?
I tried this (not working)
var stage = new PIXI.Stage(0xFFFFFF);
var renderer = PIXI.autoDetectRenderer(400, 300);
document.body.appendChild(renderer.view);
renderer.render(stage);
var rect = new PIXI.Rectangle(100, 150, 50, 50);
stage.addChild(rect);
Error:
Uncaught TypeError: Object [object Object] has no method
'setStageReference'
You can't render geometry (Pixi.Rectangle), they are meant for calculations only. You can do the following instead:
var graphics = new PIXI.Graphics();
graphics.beginFill(0xFFFF00);
// set the line style to have a width of 5 and set the color to red
graphics.lineStyle(5, 0xFF0000);
// draw a rectangle
graphics.drawRect(0, 0, 300, 200);
stage.addChild(graphics);
source
Geometries are not renderable, they are for doing geometric
calculations.
Source #xerver
So we have to use PIXI.Graphics()
There is a nice way to do this using PIXI.Texture.WHITE.
const rectangle = PIXI.Sprite.from(PIXI.Texture.WHITE);
rectangle.width = 300;
rectangle.height = 200;
rectangle.tint = 0xFF0000;
stage.addChild(rectangle);

Resources