Crop or Clip image to Circle using JointJS - javascript

I'm very new to both JointJS and SVG, so I am having a hard time interpreting some of the documentation for JointJS. I am trying to get randomly searched images to display in a circle for this project, in a graph that uses JointJS to display and link the images with arrows. I am convinced this shouldn't be as hard as I am making it, but I have spent hours going through the documentation, so I need help. From what I can tell, joint.shapes.standard.Circle elements cannot take an image as a background or fill. I would be really happy to use the .BorderedImage elements, but when I change the radius to make them a circle, the border changes without cropping the image. I also tried to use the .Image element and change the radius, and again the image didn't change. I am under the impression that I could add a clipPath definition to an .Image or .BorderedImage to force a circle, but I am not familiar enough with the syntax to be clear on where to do that, and none of the examples I have found on stackoverflow have given me enough information to do that. I tried using the .define method, and kept getting "cannot read property define of undefined" errors, so I know I wasn't using that right.
Here's what I have in the way of error-free code (rounded border that doesn't crop):
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
el: document.getElementById('myholder'),
model: graph,
width: 600,
height: 600,
gridSize: 1,
// makes code not draggable
interactive: { elementMove: false, arrowheadMove: false }
});
var borderedImage = new joint.shapes.standard.BorderedImage();
borderedImage.resize(100, 100);
borderedImage.position(100, 100);
borderedImage.attr('root/title', 'joint.shapes.standard.BoarderedImage');
borderedImage.attr('label/text', 'Monster\nImage');
borderedImage.attr('border/rx', "50%");
borderedImage.attr('image/xlinkHref', './assets/images/monster.svg');
borderedImage.addTo(graph);
The code I tried for .define looked like:
joint.shapes.Element.define('standard.BorderedImage', {
attrs: {
body: {
rx: "50%"
}
}
})
Some of the resources I have looked at and couldn't figure out how to use correctly are:
Putting multiple films in a circle in Raphael/Joint.js
How to set background image for a rectangle in JointJs?
Using predefined SVG file for creating a custom JointJS shape with ports
https://resources.jointjs.com/docs/jointjs/v2.1/joint.html#dia.Paper.prototype.properties

Related

Is it possible to implement a Paint Bucket using Konva.js?

I’ve managed to implement, using konva, multiple tools that allow users to draw different shapes and patterns like: rectangle, circle, arrows, free draw, eraser, et. c.
I’m trying to achieve something like: using a paint bucket, users should be able to fill different parts of a shape, if over that shape are drawn other shapes or patterns.
Maybe this use case helps to understand better my question:
The user draws a circle.
Afterwards he draws lines over that circle so will be split in multiple areas.
The user uses now the paint bucket and tries to fill only the areas of that circle.
I’m wondering if, using konva, is possible to achieve this functionality.
Until now I've manage only to fill entire shapes, similar to this.
Update
Added images for the use case above.
1 & 2. User draws a circle and lines over it:
Using paint bucket user can fill certain areas of that circle:
Any feedback will be very welcomed.
Bad news: What you want cannot be done with Konvajs, as it is designed to work with vectorial images. Each figure is created as a whole by an equation and is "separated" of other figures (as the lines X and Y and the circle are separate in the Snippet below. It is not a raster layer. To do a paint bucket tool in vector graphics is hard.
(See Good news at the end!)
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var circle = new Konva.Circle({
x: 180,
y: 120,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
var lineX = new Konva.Line({
x: 180, // 180-50
y: 120,
points: [-100, 0, 100, 0],
stroke: 'black',
strokeWidth: 4
});
var lineY = new Konva.Line({
x: 180, // 180-50
y: 120,
points: [0, -100, 0, 100],
stroke: 'black',
strokeWidth: 4
});
circle.on('click', function() {
var fill = this.fill() == 'red' ? '#00d00f' : 'red';
this.fill(fill);
layer.draw();
});
layer.add(circle);
layer.add(lineX);
layer.add(lineY);
stage.add(layer);
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
<script src="https://unpkg.com/konva#3.2.6/konva.min.js"></script>
<div id="container"></div>
Good news: But you can do it with Canvas, HTML5 and Javascript.
Here you have a good tutorial which includes a DEMO (on top of the page) and the SOURCE CODE to Create a Paint Bucket Tool in HTML5 and JavaScript
Hope this helps you!
Unless konva has a specific implementation for it that I don’t know of, this is more of an algorithmic problem.
One approach you could take if you decide to implement it on your own is something like a cell automaton. You would create one pixel somewhere in the middle, and it would grow over time (of course you don’t need to show the growth). The rules for it would be that any pixel of the specified color must colorize any pixel around it if it is the same as the average color of pixels around original point (where you clicked to fill color).
Hope this helps :)
I’ve came up with a solution: https://codesandbox.io/s/stupefied-northcutt-1y0cg.
In short, what this solution does is that when the stage is mounted, the paint bucket is setup targeting the canvas generated by konva. The pixels around the one clicked are colored using a cell automaton algorithm, as per Antoni's suggestion.
Okay, but the downside of this approach is that whenever you’re drawing a shape after paint bucket is used, the paint bucket changes get lost because (I assume) render() doesn’t know about the "vanilla" changes made in setupPaintBucket().
Another downside of this approach is that the canvas is blurry.
Sources:
Draw circle, arrow and free hand: https://codesandbox.io/s/43wzzv0l37
Vanilla Paint Program: https://codepen.io/falldowngoboone/pen/zxRXjL

Can't select svg element with d3

I am working on a visualization tool that uses a svg image of the brain. Now this svg has paths that are filled with a color. I want to loop over all these paths to set the fill color to white, but for some reason I cannot get the element.
The project can be seen here. The svg is inside a div and I even assigned an identifier brain to the div. The svg itself has an id svg2. So far I've tried the following:
function clearBrainColors() {
var brain = d3.select("#svg2");
console.log(brain);
var paths = brain.selectAll("path");
console.log(paths.length);
brain.selectAll('path').each(function(d,i) { console.log(this); });
}
But it outputs null in the array[0] component of the selection and 0 with paths.length.
I've also tried to use lines such as
var brain = d3.select("#brain svg"); and var brain = d3.select("#brain svg#svg2"); but those do not work either.
So, how can I select the brain svg using d3?
Decided to put the svg inline as it apparently speeds things up.
The code I used to fill the svg is now:
$("#svg2").find("path").each(function(){
$(this).css({ fill: "#ff0000" });
});
You can try setTimeOut() , following example
setTimeOut(function() {
var brain = d3.select("#svg2");
console.log(brain);
}, 1000);
this could be the svg is generate on the spot, d3 unable get at that moment.
Hope this help :)

Highcharts custom text right to left export

Well my problem is that i have a chart and on it i have some custom text that has to be in the top right and top bottom corner of the chart im using renderer.text('text', 600, 30).add();
I first had it like renderer.text('text', 600, 30).css('direction': 'rtl').add();
this worked fine but when i exported it didn't recognize the stlye
then i tried to use renderer.text('<span style="direction:rtl;">text</span>', 600, 30).add();
this also worked only to show it on the page, but the exporting doesn't
Is there any way to fix this to work for exporting?
sample
A way to avoid the whole "CSS and exporting" issue for this particular problem is to use the bounding box of the text to place it correctly, instead of the CSS solution.
function (chart) { // on complete
var rightTop = [590,85];
var element = chart.renderer.text('This is a looooong text', 0, -100).add();
var boundingBox = element.getBBox();
element.destroy();
chart.renderer.text('This is a looooong text',
rightTop[0] - boundingBox.width,
rightTop[1]).add();
}
Here the rightTop variable represents where we want it to be placed. Then the text is added (out of vision), we get the bounding box to know the size of it, and then destroy it. Then we recreate the text in the correct position.
See this JSFiddle example for a demonstration of how it may work.

How to add an image to a window in Pebble.js?

The documentation on window (http://developer.getpebble.com/docs/pebblejs/#window) says that we can add an image to it:
"Window: The Window by itself is the most flexible. It allows you to add different Elements (Circle, Image, Rect, Text, TimeText) and to specify a position and size for each of them. You can also animate them."
An image is an element, but there is no documentation on the image constructor. What parameters does it take? Particularly, how do we specify the image src? Also, there is no example code of adding an image to a window.
We're still working on improving Pebble.js and its documentation, but in the meantime, this should get you started with adding Images with Pebble.js:
var UI = require('ui');
var logo_image = new UI.Image({
position: new Vector2(0, 0),
size: new Vector2(144, 144),
backgroundColor: 'clear',
image: 'images/logo_image.png',
});
wind.add(logo_image);
wind.show();

Canvas coordinates in Fabric.js have offset

I just started with fabric.js and I have a (probably beginner) error:
I am using fabric with jquery with the following code:
$(document).ready(function () {
canvas = new fabric.StaticCanvas('scroller');
canvas.add(
new fabric.Rect({ top: 0, left: 0, width: 140, height: 100, fill: '#f55' })
);
});
This code should draw a 140x100 Rectangle on the canvas.
Sadly, only a quarter of the Rectangle appears.
If I change the top and left values to higher numbers, more of the Rectangle appears on the canvas.
So it seems, that the canvas origin is not at 0/0, but at sth. higher.
Does someone know how to fix this or what I am doing wrong?
Thanks in advance,
McFarlane
Here is a jsfiddle link with some examples http://jsfiddle.net/pukster/sdQ7U/2/
My guess is that fabric.js calculates everything from the origin (middle point) since it is drawing exactly one quarter of a rectangle even with a canvas 10 times the size of the rectangle. My guess is that top and left actually refer to the origin and not the top and left sides of the imaginary bounding box. Trouble is there is very little documentation on fabricjs. Is there any reason you are using fabricjs and not easeljs
EDIT Here's the same fiddle but with squares instead of rectangles (it is more clear) http://jsfiddle.net/pukster/sdQ7U/3/
EDIT OK I am now almost absolutely certain that fabric.js uses the center as the top/left. I ripped their example off of their site and overlayed it with the transparent couterpart to those shapes had they been coded in pure canvas http://jsfiddle.net/pukster/uathZ/2/ (blue border is the limit of the canvas element).
What you see is that the boxes are exactly offset by half but the circle (I only drew a semi circle otherwise it would not have been discernable) is perfectly overlapped. This is b/c in HTML Canvas, the circle coordinates (x,y) refer to the origin and not the top left. I did not bother with the triangle b/c my trigonometry is a bit rusty. I personally think it's misleading to use the terms top and left, when x and y would have been more representative and shorter.
Yes, this is highly unexpected and even more undocumented.
Workaround:
Set
originX: "left"
originY: "top"
for each created object.
edit: or use kangax simpler solution in the comment below.
I want to comment but lack the reputation to do so. So anyway, here is what happens when I do the following:
fabric.Object.prototype.originX = "left";
fabric.Object.prototype.originY = "top";
The shape gets rendered fine but when I select it for resizing or moving, it gets offset to a different location. The best approach seems to be to set the coordinates for every object separately using the set() method.

Categories