Draw rects inside Konva layer bound by a region with uniform distribution - konvajs

I am using Konva.js to render some colourful boxes.
I have a Stage with some set size and regions within that Stage, bound by points (e.g. rectangles, but they can be any polygons).
I then need to add some number of smaller rects inside this area such that they look nice and are uniformly distributed.
E.g.
// these numbers are an example only, irrelevant
let width = 500 * 1.936402653140851;
let height = 500;
// setup stage, boilerplate
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// region layer - boundaries
var regionLayer = new Konva.Layer({
scale: {x: width/4963, y: height/2563} // these numbers are an example only, irrelevant
});
stage.add(regionLayer);
// for this example, a single region, L-shaped on it's side
let box = new Konva.Line({
name: 'region-1',
points: [1067, 681,
3337, 681,
3337, 987,
3037, 987,
3037, 787,
1067, 787],
closed: true,
fill: 'blue',
opacity: 0.5
});
regionLayer.add(box);
regionLayer.draw();
// layer to draw rects in
let rectsLayer = new Konva.Layer({
scale: {x: width/4963, y: height/2563} // these numbers are an example only, irrelevant
});
for (let i = 0; i < 90; i++) { // e.g. some number up to X
let rect = new Konva.Rect({
width: 20,
height: 20,
fill: 'red',
x: 1067 + 40 * (i + 1),
y: 681 + 20
});
rectsLayer.add(rect);
}
stage.add(rectsLayer);
rectsLayer.draw();
The result of the above is something like this
What I'd like to have, is those boxes filling free area within the boundaries of my polygon only.
I read a whole heap about Delauney triangulation, Voronoi diagrams and sequencing for normal distribution to make it happen, but sadly I came up with zilch on practical approach of doing this.
https://jsfiddle.net/x4aksz1y/2/

Related

Invert Y axis in Konva

I have to do a lot of drawing stuff with angles and I'm afraid of messing it up with the y axis going toward bottom. And I want the Y axis going upward.
I know that in normal canvas you can invert Y axis with a simple transform:
var stage = document.getElementById("stage");
const layer=stage.getContext("2d");
layer.transform(1, 0, 0, -1, 0, 3000);
But I do not know if I can do that in Konva. Is that possible?
You just need to apply a negative scale -1 and move a container to make it visible.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
y: window.innerHeight,
scaleY: -1
});
demo: https://jsbin.com/vuhuzuroca/1/edit?html,js,output

How to drag a group in Konva JS only from an inner shape and not from other areas of the group?

I want to drag the entire group from the center red square, (see fiddle)
but restrict dragging from the outer rectangle, while maintaining the inner rectangle's position inside the group.
Possibly when mouse is down I want the crosshair cursor to be dead in the center of the inner red rectangle.
Any help would be appreciated, thanks.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
let layer = new Konva.Layer();
let group = new Konva.Group({
draggable: true,
height:50,
width: 50
});
const containerRect = new Konva.Rect({
width:50,
height: 50,
fill: 'yellow'
});
const rect = new Konva.Rect({
x: 15,
y: 15,
width: 20,
height: 20,
fill: 'red'
});
stage.add(layer);
group.add(containerRect);
group.add(rect);
layer.add(group);
layer.draw();
I suggest making the red rectangle draggable. Once you start a drag, you can stop it and start dragging of the group:
rect.on('dragstart', () => {
// stop rect dragging
rect.stopDrag();
// move group to the center
const size = group.getClientRect();
const pos = stage.getPointerPosition();
group.setAttrs({
x: pos.x - size.width / 2,
y: pos.y - size.height / 2,
});
group.startDrag();
});
https://jsfiddle.net/0cdw3ya7/21/

How to make a text appear from 0 to 100% (fold out effect) in KonvaJs

In KonvaJS how to make a text appear from 0 to 100% (fold out effect)
i want to create same effect as in below video, but with text
https://www.youtube.com/watch?v=HO6mco2MGH0
i tried giving width 0 to the node and then increased the width to 100% in the tween. But it is not giving me the desired effect. Text is appear character by character.
TIA
For that case, you can use group clipping.
const group = new Konva.Group({
clipX: 100,
clipY: 0,
clipWidth: 100,
clipHeight: 100
});
layer.add(group);
const circle = new Konva.Circle({
x: 0,
y: 50,
radius: 50,
fill: 'green'
});
group.add(circle);
circle.to({
x: 150,
duration: 1
});
Demo: http://jsbin.com/miketotuvo/1/edit?html,js,output

Image must be added to group before group added to layer?

I'm finding a potential bug in KonvaJS. Or I'm not sure about the capabilities...
1) Create a layer and add it to stage
2) Create a group and add it to layer
3) Create an Image node and add it to group (Note my images are loaded using Konva.Image.fromURL which waits for the image to load then adds it to the group.)
Result: Image does not appear.
But if you add the image to the group then add the group to the layer, the image appears. This is going to cause problems if I want to attach an image to a group dynamical if it just disappears.
I'm trying to create the concept of a tray or plate. Where the user can place items onto the plate. If the user drags the plate it creates a group with all the intersecting nodes and moves them all together. At drag end it releases all the objects back to the user.
EDIT: The problem I was experiencing had to do with group coordinates as I mentioned in my comment bellow.
"I think I misunderstood, for the longest time how positioning works with groups. Read the comments: jsfiddle.net/jusatx6s"
LL: Make sure you're checking the position of the nodes your are rendering and that they do appear on screen.
I have created a plunkr and followed steps which you have mentioned. Everything is working fine. Here's my code.
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// 1. created layer added it to stage.
var layer = new Konva.Layer();
stage.add(layer);
// 2. created group added it to layer
var group = new Konva.Group({
x: 120,
y: 40,
rotation: 20
});
layer.add(group);
var src = 'https://konvajs.github.io/assets/yoda.jpg';
// 3. Create an Image node and add it to group
Konva.Image.fromURL(src, function(yoda) {
console.log(yoda);
yoda.setAttrs({
x: 50,
y: 50,
width: 106,
height: 118
});
// 4. Add it to group.
group.add(yoda);
layer.batchDraw(); // It's required to draw changes.
});
Here's the plnkr to play around. Please let me know if I have missed anything.
Edited in light of OP's edit re confusion over co-ordinates.
Principles:
layers use the same co-ordinate system as the stage. So setting a shape position to {X: 10, y: 20} is at 10,20 in relation to the top left of the stage.
Adding a shape to a group is different, but you will not notice this if you do not set the group x & y position. For shapes that are added to the group, the shape positions are ADDED to the parent group positions.
Here is an example using a layer and a group. The gold rect is on the layer only. The group is illustrated by the green rect and has been set to position (60, 50). The red rect is a copy of the gold rect except the color. It has the same x & y position as red, BUT it is added to the group. And so its position (5, 5) is added to the group position (60, 50) to give its absolute position on the stage as (65, 55).
// add a stage
var s = new Konva.Stage({
container: 'container',
width: 400,
height: 400
});
// add a layer
var l = new Konva.Layer();
s.add(l);
// Add a gold rect to the LAYER
var gold = new Konva.Rect({stroke: 'gold', width: 40, height: 50, x: 5, y: 5});
l.add(gold);
// add a group
var g = new Konva.Group({x:60, y:50});
l.add(g);
// Add a green rect to the LAYER then add to the group
var green = new Konva.Rect({stroke: 'lime', width:100, height: 100, x: 0, y: 0});
g.add(green);
// Add a red rect, same apparent co-ords as red, but this time to the group
var red = new Konva.Rect({stroke: 'red', width: 40, height: 50, x: 5, y: 5});
g.add(red);
var abspos = red.getAbsolutePosition();
var pos = red.position();
console.log('Abs position of red is (' + abspos.x + ', ' + abspos.y + ') but its co-ords are ' + '(' + pos.x + ', ' + pos.y + ')');
l.draw(); // redraw the layer it all sits on.
#container {
border: 1px solid #ccc;
}
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.2/konva.min.js"></script>
<body>
<div id="container"></div>
</body>

Label position between series in Highcharts polar chart

I'm trying to position custom labels on the outside of a polar chart but can't figure out any way to do it. Please image below for what I'm trying to achieve.
Doesn't have to use the actual series labels but haven't found anything else that can be positioned relative to the actual chart (top level labels can be positioned anywhere but only using absolute left and top).
Have also tried changing the pointPlacement and tickmarkPlacement to "between" which sort of works but it rotates the actual chart so I get a diamond shape instead of a square, the effect I'm after would be more like rotating the labels and leaving the ticks in place.
IN such case custom labels can be positioned relative to grid line group.
Method for drawing the labels:
function drawLabels () {
let labels = this.customLabels,
bbox = this.xAxis[0].gridGroup.getBBox(),
positions = [
[bbox.x + bbox.width / 2, bbox.y],
[bbox.x + bbox.width, bbox.y + bbox.height / 2],
[bbox.x + bbox.width / 2, bbox.y + bbox.height],
[bbox.x, bbox.y + bbox.height / 2]
];
if (!labels) {
labels = this.customLabels = ['lab1', 'lab2', 'lab3', 'lab4'].map(text => this.renderer.label(text, 0, -9999).add().attr({zIndex: 4}));
}
labels.forEach((label, i) => {
label.align({
align: 'center',
verticalAlign: 'middle',
width: label.width,
height: label.height
}, null, {
x: positions[i][0],
y: positions[i][1],
width: 0,
height: 0
});
});
}
Draw labels on load/redraw:
Highcharts.chart('container', {
chart: {
polar: true,
type: 'line',
events: {
load: drawLabels,
redraw: drawLabels
}
},
example: http://jsfiddle.net/d6y1bn31/

Resources