Modify path with anchor points on HTML5 canvas - javascript

My problem is how to make a UI where the user can modify a complex path by dragging anchor points that are on the path. Note on - for example the control points for Bezier curves in this example in the Konvajs documentation are off the path itself, whereas I prefer anchor points on a complex path.
I will be trying to modify a curve created as in this question.
Here is a snippet that draws a sample path. Can anyone help me please? Scroll the snippet window down slightly then click on the rectangle to see the path animation.
var stage = new Konva.Stage({
container: 'container',
width: 500,
height: 500
});
var layer = new Konva.Layer();
var rect = new Konva.Rect({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
stroke: '#555',
strokeWidth: 3,
fill: '#ddd',
width: 50,
height: 50,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: [10, 10],
shadowOpacity: 0.2,
cornerRadius: 10
});
var path = new Konva.Path({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
data: 'M30 10 C-20 -50 -20 -70 50 -80 C150 -100 50 -150 -100 -80',
stroke: '#00D2FF',
strokeWidth: 5
});
var pathLen = path.getLength() + 10;
path.dashOffset(pathLen);
path.dash([pathLen]);
var anim = new Konva.Animation(function (frame) {
var dashLen = pathLen - frame.time / 5;
if (dashLen < 0) {
var tween = new Konva.Tween({
node: path,
duration: 1,
strokeWidth: 10,
easing: Konva.Easings.ElasticEaseOut
});
tween.play();
anim.stop();
} else {
path.dashOffset(dashLen);
}
}, layer);
rect.on('mousedown', function() {
anim.start();
})
layer.add(path);
layer.add(rect);
stage.add(layer);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 500px, height: 500px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
Also, can anyone suggest why part of the curve is drawn before the animation runs?

My solution without anchor on control point:
var stage = new Konva.Stage({
container: "container",
width: 500,
height: 500,
draggable: true
});
var layer = new Konva.Layer();
var curveLayer = new Konva.Layer();
var k = 0;
var rect = new Konva.Rect({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
stroke: "#555",
strokeWidth: 3,
fill: "#ddd",
width: 50,
height: 50,
shadowColor: "black",
shadowBlur: 10,
shadowOffset: [10, 10],
shadowOpacity: 0.2,
cornerRadius: 10
});
var path = buildPath("M25 25 L-75 -200 C-75 -200 -120 -300 -200 -200");
var pathLen = path.getLength() + 1000;
path.dashOffset(pathLen);
path.dash([pathLen]);
var anim = new Konva.Animation(function(frame) {
var dashLen = pathLen - frame.time * 10;
if (dashLen < 0) {
var tween = new Konva.Tween({
node: path,
duration: 1,
strokeWidth: 10,
easing: Konva.Easings.ElasticEaseOut
});
var circle = buildAnchorPoint(-75, -200);
var circle1 = buildAnchorPoint(-200, -200);
//var circle2 = buildAnchorPoint(-120, -242.5);
layer.draw();
circle.on("dragmove", function() {
var point = stage.getPointerPosition();
var pointx = point.x - stage.x();
var pointy = point.y - stage.y();
console.log(
"X: " +
(pointx - stage.getWidth() / 2) +
" Y: " +
(pointy - stage.getHeight() / 2)
);
path.data(
"M25 25 L" +
(pointx - stage.getWidth() / 2) +
" " +
(pointy - stage.getHeight() / 2) +
" C" +
(pointx - stage.getWidth() / 2) +
" " +
(pointy - stage.getHeight() / 2) +
"-120 -300" +
(circle1.getX() - stage.getWidth() / 2) +
" " +
(circle1.getY() - stage.getHeight() / 2)
);
curveLayer.draw();
layer.draw();
});
circle1.on("dragmove", function() {
var point = stage.getPointerPosition();
var pointx = point.x - stage.x();
var pointy = point.y - stage.y();
console.log(
"X: " +
(point.x - stage.getWidth() / 2) +
" Y: " +
(point.y - stage.getHeight() / 2)
);
path.data(
"M25 25 L" +
(circle.getX() - stage.getWidth() / 2) +
" " +
(circle.getY() - stage.getHeight() / 2) +
"C" +
(circle.getX() - stage.getWidth() / 2) +
" " +
(circle.getY() - stage.getHeight() / 2) +
" -120 -300 " +
(pointx - stage.getWidth() / 2) +
" " +
(pointy - stage.getHeight() / 2)
);
curveLayer.draw();
layer.draw();
});
tween.play();
anim.stop();
} else {
path.dashOffset(dashLen);
}
}, curveLayer);
rect.on("mousedown", function() {
if (k == 0) {
anim.start();
k++;
}
});
layer.add(rect);
stage.add(curveLayer);
stage.add(layer);
function buildPath(data) {
var path = new Konva.Path({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
data: data,
stroke: "#00D2FF",
strokeWidth: 5,
lineJoin: "round",
lineCap: "round"
});
curveLayer.add(path);
return path;
}
function buildAnchorPoint(x, y) {
var circle = new Konva.Circle({
x: stage.getWidth() / 2 + x,
y: stage.getHeight() / 2 + y,
stroke: "#c1fbff",
strokeWidth: 1,
fill: "#00D2FF",
radius: 5,
cornerRadius: 10,
draggable: true
});
layer.add(circle);
return circle;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 500px, height: 500px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
Can someone help with anchor on control point?
Also, I'd like to clean code a bit, because I will have many paths witch anchors, that's why I need to make handlers not like in my code, help please!

Related

Returning location within a circle circumference

I am trying to do circle approximation that is a regular polygon with N corners using d3.js.
My idea was to always have a circle in the background and use the "transform" function to slide around the circle and obtain a position (x,y), which I would pass to a polygon.
For example, if I must have the circle divided in 3 parts, it would become a triangle. With that, I would rotate within the circle 3 times starting at (0,0) and return the position of the two other points within the circumference of the circle.
My problem is that the "transform" function does not return me a x,y coordinate.
var svg = d3.select('svg');
var originX = 200;
var originY = 200;
var outerCircleRadius = 60;
var outerCircle = svg.append("circle").attr({
cx: originX,
cy: originY,
r: outerCircleRadius,
fill: "none",
stroke: "black"
});
var chairOriginX = originX + ((outerCircleRadius) * Math.sin(0));
var chairOriginY = originY - ((outerCircleRadius) * Math.cos(0));
var chairWidth = 20;
console.log(chairOriginX);
console.log(chairOriginY);
var chair = svg.append("rect").attr({
x: chairOriginX - (chairWidth / 2),
y: chairOriginY - (chairWidth / 2),
width: chairWidth,
opacity: 1,
height: 20,
fill: "none",
stroke: "blue"
});
var chairOriginX2 = originX + ((outerCircleRadius) * Math.sin(0));
var chairOriginY2 = originY - ((outerCircleRadius) * Math.cos(0));
console.log(chairOriginX2);
console.log(chairOriginY2);
var chair2 = svg.append("rect").attr({
x: chairOriginX2 - (chairWidth / 2),
y: chairOriginY2 - (chairWidth / 2),
width: chairWidth,
opacity: 1,
height: 50,
fill: "none",
stroke: "red"
});
var n_number = 5
var n_angles = 360/n_number
var angle_start=0;
var angle_next;
chair2.attr("transform", "rotate(" + (angle_start+n_angles+n_angles) + ", 200, 200)");
console.log(chair2.attr("transform", "rotate(" + (angle_start+n_angles+n_angles) + ", 200, 200)"));
var chairOriginX3 = originX + ((outerCircleRadius) * Math.sin(0));
var chairOriginY3 = originY - ((outerCircleRadius) * Math.cos(0));
console.log(chairOriginX3);
console.log(chairOriginY3);
var chair3 = svg.append("rect").attr({
x: chairOriginX3 - (chairWidth / 2),
y: chairOriginY3 - (chairWidth / 2),
width: chairWidth,
opacity: 1,
height: 50,
fill: "none",
stroke: "black"
});
var n_number = 5
var n_angles = 360/n_number
var angle_start=0;
var angle_next;
chair3.attr("transform", "rotate(" + (angle_start+n_angles+n_angles+n_angles) + ", 200, 200)");
console.log(chair3.attr("transform", "rotate(" + (angle_start+n_angles+n_angles+n_angles) + ", 200, 200)"));

Fabric.js - rotate one object the other rotate too

I have an application using HTML5 canvas via Fabric.js. And I would like to do arrow with text. Then moving arrow the text is in correct position, but is problem with rotation:
incorrect text position:
text position have to be:
My code is:
var canvas = new fabric.Canvas('c');
var line = new fabric.Line([10, 50, 200, 50], {
stroke: 'black',
strokeWidth: 3,
strokeDashArray: [10, 5]
});
var line2 = new fabric.Line([199, 50, 180, 60], {
stroke: 'black',
strokeWidth: 3
});
var line3 = new fabric.Line([199, 50, 180, 40], {
stroke: 'black',
strokeWidth: 3
});
var strele = new fabric.Group([line, line2, line3], {});
tekst = new fabric.IText('<<extend>>', {
left: 50,
top: 20,
fontFamily: 'Arial',
fill: '#333',
fontSize: 20
});
canvas.add(strele, tekst);
function onMoving(options) {
if (options.target === strele) {
tekst.left = strele.left + ((strele.width * strele.scaleX) / 2) - (tekst.width * tekst.scaleX) / 2;
tekst.top = strele.top - strele.height * strele.scaleY;
} else if (options.target === tekst) {
strele.left = tekst.left - ((strele.width * strele.scaleX) / 2 - (tekst.width * tekst.scaleX) / 2);
strele.top = tekst.top + strele.height * strele.scaleY;
}
canvas.forEachObject(function(o) {
o.setCoords()
});
}
function onRotating(options) {
if (options.target === strele) {
tekst.angle = strele.angle;
tekst.left = strele.left((strele.width * strele.scaleX) / 2) - (tekst.width * tekst.scaleX) / 2;
tekst.top = strele.top - strele.height * strele.scaleY;
}
}
canvas.on('object:moving', onMoving);
canvas.on('object:rotating', onRotating);
My code in fiddle
How can I fix this?
Here is an approach that closely resembles your image.
To simplify the calculations I changed the objects origin(X & Y) to center, the rest is just some math and conditions for the angles. My approach was to keep the text readable and not overlap the line.
var canvas = new fabric.Canvas('c');
var line = new fabric.Line([160, 100, 350, 100], {stroke: 'black', strokeWidth: 3, strokeDashArray: [10, 5]});
var line2 = new fabric.Line([350, 100, 330, 110], {stroke: 'black', strokeWidth: 3});
var line3 = new fabric.Line([350, 100, 330, 90], {stroke: 'black', strokeWidth: 3});
var strele = new fabric.Group([line, line2, line3], {originX: "center", originY: "center"});
var tekst = new fabric.IText('<<extend>>', {left: 160, top: 70, fontFamily: 'Arial',fill: 'red', fontSize: 20, selectable: false,
originX: "center", originY: "center"
});
canvas.add(strele, tekst);
canvas.setActiveObject(canvas.item(0));
canvas.on('object:moving', onMoving);
canvas.on('object:rotating', onRotating);
function onMoving() {
tekst.left = strele.left + 20 * Math.sin(strele.angle * Math.PI /180);
tekst.top = strele.top - 20 * Math.cos(strele.angle * Math.PI /180);
}
function onRotating() {
var ang = strele.angle % 360;
if (ang < 270) {
if (ang > 180) {
ang = strele.angle % 180;
} else if (ang > 90) {
ang = 180+strele.angle;
}
}
tekst.angle = ang
onMoving()
}
function rotate() {
strele.angle += 22.2;
strele.setCoords()
onRotating()
canvas.renderAll();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.4/fabric.min.js"></script>
<button id="rotate" onclick="rotate()">rotate</button>
<canvas id="c" width="600" height="600"></canvas>

How to draw sector (not arc) in fabric js?

I am trying to draw semecircle (sector) with fabricjs:
$('#button').click(function(){
fov+=10;
drawSector(fov);
});
$('#button2').click(function(){
fov-=10;
drawSector(fov);
});
var w=500,h=500;
var R = 100;
var fov = 75.0;
var lastFOV;
var ele = {
center: {x: 0.5, y:0.5},
focal: {x: 0.5, y: 0.5},
radius: 0.6,
transform: {
rotate: 0,
translate: {
x: 0,
y: 0,
},
scale: {
x: 1,
y: 1,
}
},
stops: [
{offset: '0', color: "purple",alpha:'0.75'},
{offset: '0.9', color: "transparent",opacity:'0'}
]
};
var tr_str = "rotate("+ele.transform.rotate+",0.5,0.5) translate("+ele.transform.translate.x*w+","+ele.transform.translate.y*h+") scale("+ele.transform.scale.x+","+ele.transform.scale.y+")";
var tr_matrix = fabric.parseTransformAttribute(tr_str);
var rg = {
type: 'radial',
x1: ele.center.x,
y1: ele.center.y,
r1: 0,
x2: ele.focal.x,
y2: ele.focal.y,
r2: R,
//transformMatrix: [1,0,0,2,0,0],
//gradientTransform: [1,0,0,2,0,0],
gradientTransform: tr_matrix,
colorStops: (function(){
var color_stops = {};
for(var i=0;i<ele.stops.length;i++){
color_stops[ele.stops[i].offset] = ele.stops[i].color;
}
return color_stops;
})()
};
var HideControls = {
'tl':false,
'tr':false,
'bl':false,
'br':false,
'ml':false,
'mt':false,
'mr':false,
'mb':false,
'mtr':true
};
var canvas = new fabric.Canvas('canvas1');
canvas.setWidth(w);
canvas.setHeight(h);
var x,y,my,startPoints;
var sector;
var rotationAngle = 0;
drawSector(fov);
function drawSector(fov){
$('#fov').html("FOV = "+fov);
x = Math.cos(fov*Math.PI/180.0)*R;
y = Math.sin(fov*Math.PI/180.0)*R;
my = -Math.sin(fov/2.*Math.PI/180.0)*R/2.;
startPoints = [
{x: 0, y: 0},
{x: R, y: 0},
{x: x, y: y}
];
if(sector) {
rotationAngle = sector.angle;
canvas.remove(sector);
}
sector = new fabric.Polygon(startPoints,{
left: w/2,
top: h/2,
originX:'left',
originY:'top',
centeredRotation:false,
hasBorders:false,
lockMovementX:true,
lockMovementY:true,
rotatingPointOffset:my,
width: R,
height: R
});
sector.setControlsVisibility(HideControls);
sector.setGradient('fill', rg);
rotationAngle = rotationAngle==0?-fov/2:rotationAngle-(fov-lastFOV)/2;
sector.setAngle(rotationAngle);
canvas.add(sector);
lastFOV = fov;
}
https://jsfiddle.net/2v0es4xh/35/
But when FOV is bigger than 90, rotationX/Y is changing.
There is something in Leaflet http://jieter.github.io/Leaflet-semicircle/examples/semicircle.html
It would be great if I can draw semicircle like this using fabric.
is there anyone who tried to do this?
Thank you in advance.
Try something like this: http://jsfiddle.net/qoadhrop/
Set two semi-circles on top of a main circle to give you this kind of "radius" effect
HTML:
<canvas id="c" width="300" height="300"></canvas>
JS:
var canvas = new fabric.Canvas('c');
var c1 = new fabric.Circle({
radius: 50,
left: 100,
top: 100,
stroke: '#000',
strokeWidth: 1,
fill: 'lightblue',
opacity: 0.8
});
var c2 = new fabric.Circle({
radius: 50,
left: 100,
top: 100,
startAngle: 0,
endAngle: Math.PI,
stroke: '#000',
strokeWidth: 1,
fill: 'red'
});
c2.setAngle(45);
var c3 = new fabric.Circle({
radius: 50,
left: 100,
top: 100,
flipX: true,
startAngle: 0,
endAngle: Math.PI,
stroke: '#000',
strokeWidth: 1,
fill: 'red'
});
c3.setAngle(-45);
canvas.add(c1);
canvas.add(c2);
canvas.add(c3);
The previous code by A. Lau doesn't work. So, this is my simple implementation. You can draw any sectors and rotate them. Try live example at https://jsfiddle.net/irwinwelsh/rn9emuqd/
var canvas = new fabric.Canvas('c');
function arc(left,top,radius,angle, rotate) {
var coeff = Math.PI/180;
var xshift = Math.sqrt(2) * radius * ( Math.cos(45 * coeff) - Math.cos((45 + rotate) * coeff));
var yshift = Math.sqrt(2) * radius * ( Math.sin(45 * coeff) - Math.sin((45 + rotate) * coeff));
left = left + xshift;
top = top + yshift;
var width = 2 * radius * Math.sin((angle/2) * coeff);
var height = radius * Math.cos((angle/2) * coeff);
var triangle = new fabric.Triangle({
width: width,
height: height,
left: left + width/2,
top: top,
fill: 'rgba(0,200,0,0.3)',
angle: 180,
});
var circle = new fabric.Circle({
radius: radius,
fill: 'rgba(0,200,0,0.3)',
left: left - radius,
top: top - radius,
startAngle: (270 - angle/2) * coeff,
endAngle: (270 + angle/2) * coeff,
angle: 0,
});
var group = new fabric.Group([circle,triangle],{
angle : rotate,
});
return group;
}
var sector = arc(100,200,200,30, 30);
canvas.add(sector);

How to rotate 180 text lables on canvas

Hey I have a canvas element which I am rotate 180 degree using the following code:
-moz-transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
-o-transform: scale(-1, 1);
transform: scale(-1, 1);
filter: FlipH;
The canvas is get rotated but the text is flipped out.
Here is the the result:
I want to flip the text, after I have rotate the canvas 180 degree,apparently the text get rotate 180 too and I want to reverse it.
I want to rotate only the text and not all the other elements.
The original code is here:
go to ticklines function
http://codepen.io/Barak/pen/PNXROK
$(document).ready(function() {
$(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.transform(1, 0, 0, -1, 0, canvas.height);
// https://www.researchgate.net/publication/4345236_A_Software_Implementation_of_the_Duval_Triangle_Method
var v0 = {
x: 50 * 10,
y: (86.603 * 10)
};
var v1 = {
x: 0 * 10,
y: (0 * 10)
};
var v2 = {
x: 100 * 10,
y: (0 * 10)
};
var triangle = [v0, v1, v2];
// Define all your segments here
var segments = [{
points: [{
x: 0 * 10,
y: 0 * 10
}, {
x: 43.5 * 10,
y: 75.34461 * 10
}, {
x: 55 * 10,
y: 55.42 * 10
}, {
x: 23 * 10,
y: 0 * 10
}],
fill: 'rgb(172,236,222)',
label: {
text: '',
cx: 300,
cy: 645,
withLine: false,
endX: null,
endY: null
},
}, {
points: [{
x: 23 * 10,
y: 0 * 10
}, {
x: 55 * 10,
y: 55.42 * 10
}, {
x: 63.5 * 10,
y: 40.70 * 10
}, {
x: 55.5 * 10,
y: 26.84 * 10
}, {
x: 71 * 10,
y: 0 * 10
}],
fill: 'deepskyblue',
label: {
text: '',
cx: 490,
cy: 645,
withLine: false,
endX: null,
endY: null
},
}, {
points: [{
x: 71 * 10,
y: 0 * 10
}, {
x: 55.5 * 10,
y: 26.84693 * 10
}, {
x: 63.5 * 10,
y: 40.70341 * 10
}, {
x: 43.5 * 10,
y: 75.34461 * 10
}, {
x: 48 * 10,
y: 83.13888 * 10
}, {
x: 73 * 10,
y: 39.83737 * 10
}, {
x: 67.5 * 10,
y: 30.311 * 10
}, {
x: 85 * 10,
y: 0 * 10
}],
fill: 'lightCyan',
label: {
text: '',
cx: 656,
cy: 645,
withLine: false,
endX: 366,
endY: 120
},
}, { //here - I am in hell.-s5
points: [{
x: 49 * 10,
y: 84.87093999999999 * 10
}, {
x: 50 * 10,
y: 86.603 * 10
}, {
x: 51 * 10,
y: 84.87093999999999 * 10
}],
fill: 'black',
label: {
text: 'PD',
cx: 600,
cy: 52,
withLine: true,
endX: 520,
endY: 70
},
}, {
points: [{
x: 48 * 10,
y: 83.1388 * 10
}, {
x: 49 * 10,
y: 84.87093 * 10
}, {
x: 51 * 10,
y: 84.87093 * 10
}, {
x: 60 * 10,
y: 69.2824 * 10
}, {
x: 58 * 10,
y: 65.81828 * 10
}],
fill: 'navajoWhite',
label: {
text: '',
cx: 670,
cy: 140,
withLine: true,
endX: 574,
endY: 105
},
}, {
points: [{
x: 58 * 10,
y: 65.81828 * 10
}, {
x: 60 * 10,
y: 69.2824 * 10
}, {
x: 75 * 10,
y: 43.3015 * 10
}, {
x: 73 * 10,
y: 39.837 * 10
}],
fill: 'tan',
label: {
text: '',
cx: 800,
cy: 290,
withLine: true,
endX: 662,
endY: 120
},
}, {
points: [{
x: 85 * 10,
y: 0 * 10
}, {
x: 67.5 * 10,
y: 30.311 * 10
}, {
x: 75 * 10,
y: 43.3015 * 10
}, {
x: 100 * 10,
y: 0
}],
fill: 'peru',
label: {
text: '',
cx: 800,
cy: 645,
withLine: false,
endX: null,
endY: null
},
}, ];
// label styles
var labelfontsize = 12;
var labelfontface = 'verdana';
var labelpadding = 3;
// pre-create a canvas-image of the arrowhead
var arrowheadLength = 10;
var arrowheadWidth = 8;
var arrowhead = document.createElement('canvas');
premakeArrowhead();
var legendTexts = ['PD = Partial Discharge',
'DT = Discharges and Thermal',
'T1 = Thermal fault T < 300 ℃',
'T2 = Thermal fault 300 ℃ < T < 700 ℃',
'T3 = Thermal fault T > 700 ℃',
'D1 = Discharges of low energy',
'D2 = Discharges of high energy'
];
// start drawing
/////////////////////
// draw colored segments inside triangle
for (var i = 0; i < segments.length; i++) {
drawSegment(segments[i]);
}
// draw ticklines
//
ticklines(v0, v1, 9, Math.PI * 1.2, 20);
ticklines(v1, v2, 9, Math.PI * 3 / 4, 20);
ticklines(v2, v0, 9, Math.PI * 2, 20);
// molecules
// moleculeLabel(v0, v1, 100, Math.PI / 2, '% CH4');
// moleculeLabel(v1, v2, 100, 0, '% C2H4');
// moleculeLabel(v2, v0, 100, Math.PI, '% C2H2');
// draw outer triangle
drawTriangle(triangle);
// draw legend
//drawLegend(legendTexts, 10, 10, 12.86);
drawCircle(canvas.width / 3, canvas.height / 2, 2.5, 'red');
// end drawing
/////////////////////
function drawCircle(point1, point2, radius, color) {
ctx.beginPath();
ctx.arc(point1, point2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
}
function drawSegment(s) {
// draw and fill the segment path
ctx.beginPath();
ctx.moveTo(s.points[0].x, s.points[0].y);
for (var i = 1; i < s.points.length; i++) {
ctx.lineTo(s.points[i].x, s.points[i].y);
}
ctx.closePath();
ctx.fillStyle = s.fill;
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw segment's box label
/* if (s.label.withLine) {
lineBoxedLabel(s, labelfontsize, labelfontface, labelpadding);
} else {
boxedLabel(s, labelfontsize, labelfontface, labelpadding);
} */
}
function moleculeLabel(start, end, offsetLength, angle, text) {
ctx.textAlign = 'center';
ctx.textBaseline = 'middle'
ctx.font = '14px verdana';
var dx = end.x - start.x;
var dy = end.y - start.y;
var x0 = parseInt(start.x + dx * 0.50);
var y0 = parseInt(start.y + dy * 0.50);
var x1 = parseInt(x0 + offsetLength * Math.cos(angle));
var y1 = parseInt(y0 + offsetLength * Math.sin(angle));
ctx.fillStyle = 'black';
ctx.fillText(text, x1, y1);
// arrow
var x0 = parseInt(start.x + dx * 0.35);
var y0 = parseInt(start.y + dy * 0.35);
var x1 = parseInt(x0 + 50 * Math.cos(angle));
var y1 = parseInt(y0 + 50 * Math.sin(angle));
var x2 = parseInt(start.x + dx * 0.65);
var y2 = parseInt(start.y + dy * 0.65);
var x3 = parseInt(x2 + 50 * Math.cos(angle));
var y3 = parseInt(y2 + 50 * Math.sin(angle));
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x3, y3);
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.stroke();
var angle = Math.atan2(dy, dx);
ctx.save(); // save
ctx.translate(x3, y3);
ctx.rotate(angle);
ctx.drawImage(arrowhead, -arrowheadLength, -arrowheadWidth / 2);
ctx.restore()
}
function boxedLabel(s, fontsize, fontface, padding) {
var centerX = s.label.cx;
var centerY = s.label.cy;
var text = s.label.text;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle'
ctx.font = fontsize + 'px ' + fontface
var textwidth = ctx.measureText(text).width;
var textheight = fontsize * 1.286;
var leftX = centerX - textwidth / 2 - padding;
var topY = centerY - textheight / 2 - padding;
ctx.fillStyle = 'white';
ctx.fillRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.lineWidth = 1;
ctx.strokeRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.fillStyle = 'black';
ctx.fillText(text, centerX, centerY);
}
function lineBoxedLabel(s, fontsize, fontface, padding) {
var centerX = s.label.cx;
var centerY = s.label.cy;
var text = s.label.text;
var lineToX = s.label.endX;
var lineToY = s.label.endY;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle'
ctx.font = fontsize + 'px ' + fontface
var textwidth = ctx.measureText(text).width;
var textheight = fontsize * 1.286;
var leftX = centerX - textwidth / 2 - padding;
var topY = centerY - textheight / 2 - padding;
// the line
ctx.beginPath();
ctx.moveTo(leftX, topY + textheight / 2);
ctx.lineTo(lineToX, topY + textheight / 2);
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.stroke();
// the boxed text
ctx.fillStyle = 'white';
ctx.fillRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.strokeRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.fillStyle = 'black';
ctx.fillText(text, centerX, centerY);
}
function ticklines(start, end, count, angle, length) {
var dx = end.x - start.x;
var dy = end.y - start.y;
ctx.lineWidth = 1;
for (var i = 1; i < count; i++) {
var x0 = parseInt(start.x + dx * i / count);
var y0 = parseInt(start.y + dy * i / count);
var x1 = parseInt(x0 + length * Math.cos(180 - angle));
var y1 = parseInt(y0 + length * Math.sin(180 - angle));
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
if (i == 2 || i == 4 || i == 6 || i == 8) {
var labelOffset = length * 3 / 4;
var x1 = parseInt(x0 - labelOffset * Math.cos(180 - angle));
var y1 = parseInt(y0 - labelOffset * Math.sin(180 - angle));
ctx.save();
ctx.fillStyle = 'black';
ctx.rotate(Math.PI);
ctx.fillText(parseInt(i * 10), x1, y1);
ctx.restore();
}
}
}
function premakeArrowhead() {
var actx = arrowhead.getContext('2d');
arrowhead.width = arrowheadLength;
arrowhead.height = arrowheadWidth;
actx.beginPath();
actx.moveTo(0, 0);
actx.lineTo(arrowheadLength, arrowheadWidth / 2);
actx.lineTo(0, arrowheadWidth);
actx.closePath();
actx.fillStyle = 'black';
actx.fill();
}
function drawTriangle(t) {
ctx.beginPath();
ctx.moveTo(t[0].x, t[0].y);
ctx.lineTo(t[1].x, t[1].y);
ctx.lineTo(t[2].x, t[2].y);
ctx.closePath();
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
}
function drawLegend(texts, x, y, lineheight) {
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillStyle = 'black';
ctx.font = '12px arial';
for (var i = 0; i < texts.length; i++) {
ctx.fillText(texts[i], x, y + i * lineheight);
}
}
})
});
body {
background-color: ivory;
padding: 10px;
}
#canvas {
border: 1px solid red;
margin: 0 auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<canvas id="canvas" width=1024 height=1024></canvas>
</div>

KineticJS: Curved line between two draggable shapes

I am using KineticJS in my project. I need to connect two shapes using a curved line. One of the shapes can be dragged. I am able to put the curved line between shapes. The problem arises when user starts dragging the shapes. The requirement is that it should be properly curved (please refer to screen shots), irrespective of distance between them and their position with respect to each other. I am doing this:
var utils = {
_getCenter: function(x1, y1, x2, y2) {
return {
x: (x1 + x2) / 2,
y: (y1 + y2) / 2
}
},
// Converts from degrees to radians.
_radians: function(degrees) {
return degrees * Math.PI / 180;
},
// Converts from radians to degrees.
_degrees: function(radians) {
return radians * 180 / Math.PI;
}
};
function amplitude(point) {
var rad_90 = utils._radians(90);
var rad_45 = utils._radians(45);
var rad_60 = utils._radians(60);
console.log(rad_90);
return {
x: point.x * Math.cos(rad_60),
y: point.y * Math.sin(rad_60)
};
}
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Kinetic.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 20,
fill: 'red',
stroke: 'black',
strokeWidth: 2
});
var attachedCircle = new Kinetic.Circle({
x: stage.getWidth() / 4,
y: stage.getHeight() / 4,
radius: 20,
fill: 'red',
stroke: 'black',
strokeWidth: 2,
draggable: true
});
var center = amplitude(utils._getCenter(circle.getX(), circle.getY(), attachedCircle.getX(), attachedCircle.getY()));
var line = new Kinetic.Line({
points: [circle.getX(), circle.getY(), center.x, center.y, attachedCircle.getX(), attachedCircle.getY()],
fill: 'black',
stroke: 'green',
strokeWidth: 3,
/*
* line segments with a length of 33px
* with a gap of 10px
*/
dash: [33, 10],
id: 'line',
tension: 0.5
});
attachedCircle.on('dragmove', function(e) {
var targetCircle = e.target;
var tempCenter = amplitude(utils._getCenter(circle.getX(), circle.getY(), targetCircle.getX(), targetCircle.getY()));
console.log(tempCenter);
line.setPoints([circle.getX(), circle.getY(), tempCenter.x, tempCenter.y, targetCircle.getX(), targetCircle.getY()]);
});
// add the shape to the layer
layer.add(line);
layer.add(attachedCircle);
layer.add(circle);
// add the layer to the stage
stage.add(layer);
I don't know what I am missing. I have created the plunkr for this.
To define you amplitude function you need to use two input points:
function amplidure2(p1, p2) {
var alpha = Math.atan((p1.x - p2.x) / (p1.y - p2.y)) + Math.PI / 2;
if (p1.y < p2.y) {
alpha += Math.PI;
}
var center = utils._getCenter(p1.x, p1.y, p2.x, p2.y);
var r = 50;
return {
x: center.x + r * Math.sin(alpha),
y: center.y + r * Math.cos(alpha)
}
}
DEMO

Categories