draw Elliptical arc raphael - javascript

I am trying to draw an elliptical arc which can animate(bounce) using raphael. So far I have been following http://jsfiddle.net/jonhartmann/vm0etvz9/light/ to draw the arc. This is the output which I am getting from the link above.
But I need to get an output like an shell(below Image) starting the arc from 2nd quadrant with x=-30 and y=0(x can be anything on negative x axis depends on RADIUS and y should be 0).
I have attached my version of code to achieve the result.
http://jsfiddle.net/vssb7n25/
var archtype = Raphael('graph', 100, 100);
archtype.customAttributes.arc = function (value) {
var xloc = 50,
yloc = 50,
total = 100,
R = 30,
alpha = 180 / total * value,
a = (180 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc + R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}else{
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
It works fine but adds an extra arc on the bottom.
Could anyone help me out in drawing raphael elliptical arc.
Thank you

From the original sample you need to change starting and ending points of the arc.
The starting point (-1,0) becomes
["M", xloc - R, yloc],
And the ending point is
x = xloc - R * Math.sin(a),
y = yloc - R * Math.cos(a),

Related

How to find a middle point of a beizer curve?

I want to make a 'named' bezier curve. I want it to be one-word named so I don't have to worry about word-wrap.
I make bezier curve via P5 bezier(sx,sy,c1x,c1y,c2x,c2y,ex,ey) function and I want a string to be shown in the middle of bezier curve. But I don't know how to find 'the middle' of curve.
For now my result looks like this
(I don't know where to start picking up this problem, so I went with the easier way of just printing text on a start of curve):
But I want it to look like this:
This means that I need P1 and P2 coordinates:
Sorry for paint, but I don't have my code yet. As soon as I will have my hands on it I will add it here.
Here is code that draws a curve:
let
b = dest.inTriangle.middle, // destination triangle
g = this.outTriangle.p3, // tip of out triangle
c = {x:b.x-g.x,y:b.y-g.y}, // distance between objects
r1 = {}, // bezier point 1
r2 = {}; // bezier point 2
if(c.x > 0){
// b is on left
r1 = {
x: g.x + c.x/2,
y: g.y
};
r2 = {
x: b.x - c.x/2,
y: b.y
};
}
else {
// b is on right
r1 = {
x: g.x - c.x/2,
y: g.y + c.y
};
r2 = {
x: b.x + c.x/2,
y: b.y - c.y
};
}
noFill();
stroke(0);
bezier(
g.x, g.y,
r1.x, r1.y,
r2.x, r2.y,
b.x, b.y
);
noStroke();
fill(0);
text(this.name, g.x, (g.y-this.h/2))
You can use the bezierPoint() function that comes with P5.js.
From the reference:
noFill();
var x1 = 85,
x2 = 10,
x3 = 90,
x4 = 15;
var y1 = 20,
y2 = 10,
y3 = 90,
y4 = 80;
bezier(x1, y1, x2, y2, x3, y3, x4, y4);
fill(255);
var steps = 10;
for (var i = 0; i <= steps; i++) {
var t = i / steps;
var x = bezierPoint(x1, x2, x3, x4, t);
var y = bezierPoint(y1, y2, y3, y4, t);
ellipse(x, y, 5, 5);
}
You'd probably want to use a value of 0.5 for t to get the midpoint.
So, the formula to translate the 4 points in a function over time is the following (image from wikipedia):
Since you want the middle, and t ranges from 0 to 1, you just have to set t = 1/2
So
B(1/2) = 1/8 P0 + 3/8 P1 + 3/8 P2 + 1/8 P3

Draw Arc initial point, radius and final point in JavaScript Canvas

I need to draw an arc having initial point, radius and final point.
I am using the HTML5 canvas arc function (x, y, radius, startAngle, endAngle, anticlockwise) in JavaScript.
context.arc(x, y, radius, startAngle, endAngle, anticlockwise)
Have:
var initialPoint = { x: 20, y: 40 };
var radius = 40;
var finalPoint = { x: 180, y: 40 };
The expected results:
please some help
You have to do a little math to find the center of a circle that matches your 3 constraints :
• intersects with initialPoints
• intersects with finalPoint
• has provided radius
Notice that there might be no result : if the points are further from twice the radius from one another no circle can match.
If points are < 2 * radius away, we have two results in fact, i don't know how you'd like your user to choose.
The math uses a few properties :
• the circles center are on the line perpendicular to p1, p2.
• pm, the middle point of (p1, p2) is also the middle point of (c1, c2).
• the triangles (p1, pm, c1) and (p1, pm, c2) have a 90° angle in pm (called 'triangle rectangle' in french, donno in english).
Here's a screenshot with the two possible arcs in green/red :
http://jsbin.com/jutidigepeta/1/edit?js,output
var initialPoint = { x: 100, y: 160 };
var radius = 90;
var finalPoint = { x: 240, y: 190 };
var centers = findCenters(radius,initialPoint, finalPoint );
Core function :
//
function findCenters(r, p1, p2) {
// pm is middle point of (p1, p2)
var pm = { x : 0.5 * (p1.x + p2.x) , y: 0.5*(p1.y+p2.y) } ;
drawPoint(pm, 'PM (middle)');
// compute leading vector of the perpendicular to p1 p2 == C1C2 line
var perpABdx= - ( p2.y - p1.y );
var perpABdy = p2.x - p1.x;
// normalize vector
var norm = Math.sqrt(sq(perpABdx) + sq(perpABdy));
perpABdx/=norm;
perpABdy/=norm;
// compute distance from pm to p1
var dpmp1 = Math.sqrt(sq(pm.x-p1.x) + sq(pm.y-p1.y));
// sin of the angle between { circle center, middle , p1 }
var sin = dpmp1 / r ;
// is such a circle possible ?
if (sin<-1 || sin >1) return null; // no, return null
// yes, compute the two centers
var cos = Math.sqrt(1-sq(sin)); // build cos out of sin
var d = r*cos;
var res1 = { x : pm.x + perpABdx*d, y: pm.y + perpABdy*d };
var res2 = { x : pm.x - perpABdx*d, y: pm.y - perpABdy*d };
return { c1 : res1, c2 : res2} ;
}
utilities :
function sq(x) { return x*x ; }
function drawPoint(p, name) {
ctx.fillRect(p.x - 1,p.y - 1,2, 2);
ctx.textAlign = 'center';
ctx.fillText(name, p.x, p.y+10);
}
function drawCircle(c, r) {
ctx.beginPath();
ctx.arc(c.x, c.y, r, 0, 6.28);
ctx.strokeStyle='#000';
ctx.stroke();
}
function drawCircleArc(c, r, p1, p2, col) {
var ang1 = Math.atan2(p1.y-c.y, p1.x-c.x);
var ang2 = Math.atan2(p2.y-c.y, p2.x-c.x);
ctx.beginPath();
var clockwise = ( ang1 > ang2);
ctx.arc(c.x, c.y, r, ang1, ang2, clockwise);
ctx.strokeStyle=col;
ctx.stroke();
}
Edit :
Here a fiddle using 'side', a boolean that states which side of the arc we should choose.
http://jsbin.com/jutidigepeta/3/edit
If anyone is looking for the equivalent in SVG (using D3.js), using the answer from opsb:
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
function describeArc(x, y, radius, startAngle, endAngle){
var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);
var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, arcSweep, 0, end.x, end.y
].join(" ");
return d;
}
function sq(x) { return x*x ; }
function drawCircleArcSVG(c, r, p1, p2, col) {
var ang1 = Math.atan2(p1.y-c.y, p1.x-c.x)*180/Math.PI+90;
var ang2 = Math.atan2(p2.y-c.y, p2.x-c.x)*180/Math.PI+90;
var clockwise = side;
var path = describeArc(c.x, c.y, r, ang1, ang2)
console.log(path)
svg.append("path").attr("d", path).attr("fill", "none").attr("stroke-width", 3).attr("stroke", col)
}
function drawPointSVG(p, name) {
svg.append("circle").attr("cx", p.x).attr("cy", p.y).attr("r", 2)
svg.append("text").attr("x", p.x).attr("y", p.y+10).style("font-size", 10).text(name).style("font-family", "Arial")
}
function drawCircleSVG(c, r) {
svg.append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", r).style("fill", "none").attr("stroke", "#000")
}
Working example: https://jsbin.com/qawenekesi/edit

Fill outer-circle with Raphael JS

I’m trying to fill the border of a circle, depending on a variable, like so:
I found this example but it’s not what I want. I want the outer border to fill from bottom-up, on both sides of circle.
Here’s the code that I have:
var completion = '5%';
drawcircle("js-raphael-completion", completion);
function drawcircle(div, rate) {
var archtype = Raphael(document.getElementsByClassName(div)[0], "90%", "90%");
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
// inner ring
var circle = archtype.circle("50%", "50%", "40%").attr({
"stroke": "#2787d3",
"stroke-width": 1
});
// text
var text = archtype.text("95%", "50%", rate).attr({
'font-size': 14, 'font-family': 'Avenir',
"fill": "#2787d3"
});
// outer ring (filling)
var my_arc = archtype.path().attr({
"stroke": "#2787d3",
"stroke-width": 10,
arc: [100, 100, 0, 100, 50]
});
// animation of outer ring
my_arc.animate({
arc: [100, 100, 100, 100, 50]
}, 1000);
}
See the demo: http://jsfiddle.net/SUBn6/
Currently, because I’m not using percentages for the outer ring (filling), it’s not centred with the border circle (inner).
Another issue I have is with the number “5%”. It needs to follow the outer ring (filling) as it fills up from bottom to top…
Any ideas? Thanks.
It looks like Raphael doesn't have such a settings for stroke ending. So another approach to this would be to have two arcs and fill the space between them to simulate the stroke.
That involves a little more geometry :) but this should work:
var completion = '5%';
drawcircle("js-raphael-completion", completion);
// JSFiddle needs not onload
// window.onload = function () {
// drawcircle("js-raphael-completion", completion);
// };
function drawcircle(div, rate) {
var archtype = Raphael(document.getElementsByClassName(div)[0], "90%", "90%");
archtype.customAttributes.arc = function (xloc, yloc, value, total, R,lineWidth) {
var alpha = 360 / total * value,
a = value / total * Math.PI,
w = R * Math.sin(a),
h = R * Math.cos(a),
gamma = Math.asin(R / (R + lineWidth) * Math.sin(Math.PI / 2 + a)),
beta = Math.PI/2 - a - gamma,
xOffset = (R + lineWidth) * Math.sin(beta) / Math.sin(Math.PI / 2 + a),
x = xloc - R * Math.sin(a),
y = yloc + R * Math.cos(a),
path;
if (Math.abs(a - Math.PI/2) < 0.0001) {
xOffset = lineWidth;
}
if (total == value) {
path = [
["M", x, y],
["A", R , R , 1, 1, 1, x - 0.01, y],
["L", x - 0.01,y-lineWidth],
["A", R + lineWidth, R + lineWidth, 1, 1, 0, x, y - lineWidth],
["Z"]
/*
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
*/
];
} else {
path = [
["M", x, y],
["A", R , R , 0, +(alpha > 180), 0, x + 2 * w, y],
["L", x+ 2 * w + xOffset,y],
["A", R + lineWidth, R + lineWidth, 0 , +(alpha > 180) , 1, x - xOffset, y],
["Z"]
];
}
return {
path: path
};
};
// inner ring
var circle = archtype.circle(100, 100, 50).attr({
"stroke": "#2787d3",
"stroke-width": 3
});
// text
var text = archtype.text("95%", "50%", rate).attr({
'font-size': 14,
'font-family': 'Avenir',
"fill": "#2787d3"
});
// outer ring (filling)
var my_arc = archtype.path().attr({
"stroke": "#FF0000",
"fill": "#2787d3",
"stroke-width" : 0,
arc: [100, 100, 0, 100, 51,10]
});
// animation of outer ring
my_arc.animate({
arc: [100, 100, 100, 100, 51,10]
}, 1000);
}
See http://jsfiddle.net/Xp77x/2/
I think you messed around the cos and sin stuff :). Basically the starting point of the arc is variable depending on the ratio (xloc - R * sin(a), yloc + R * cos(a)), and the width is also variable (2 * R *sin(a)).
I modified a little bit your stuff:
var completion = '5%';
drawcircle("js-raphael-completion", completion);
// JSFiddle needs not onload
// window.onload = function () {
// drawcircle("js-raphael-completion", completion);
// };
function drawcircle(div, rate) {
var archtype = Raphael(document.getElementsByClassName(div)[0], "90%", "90%");
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = value / total * Math.PI,
w = R * Math.sin(a),
h = R * Math.cos(a),
x = xloc - R * Math.sin(a),
y = yloc + R * Math.cos(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", x, y],
["A", R, R, 0, +(alpha > 180), 0, x + 2 * w, y]
];
}
return {
path: path
};
};
// inner ring
var circle = archtype.circle(100, 100, 50).attr({
"stroke": "#2787d3",
"stroke-width": 1
});
// text
var text = archtype.text("95%", "50%", rate).attr({
'font-size': 14,
'font-family': 'Avenir',
"fill": "#2787d3"
});
// outer ring (filling)
var my_arc = archtype.path().attr({
"stroke": "#2787d3",
"stroke-width": 10,
arc: [100, 100, 0, 100, 50]
});
// animation of outer ring
my_arc.animate({
arc: [100, 100, 100, 100, 55]
}, 1000);
}
see: http://jsfiddle.net/Xp77x/1/
However I used absolute coordinates. To have relative coordinates you need to parse a little bit the input for % and translate this to a 0-100 canvas.

arcs in raphael js Draw like a gauge

My circle starts from 12 o clock. But I need to start from 7 and end at 5 . Always starts horizontally but I need to start vertically . Also how to add Effects like shadow etc.
var amount = ("80");
var archtype = Raphael("canvas", 600, 400);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
"stroke": "#f00",
"stroke-width": 14,
arc: [200, 200, 0, 100, 60]
});
my_arc.animate({
arc: [200, 200, amount, 100, 60]
}, 1500, "bounce");
i finally found it :D just added my_arc.rotate(215, 100 ,100)
<script >
var amount = ("50");
var archtype = Raphael("canvas", 300, 200);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
"stroke": "#f00",
"stroke-width": 14,
arc: [100, 100, 0, 100, 60]
});
my_arc.rotate(215, 100 ,100).animate({
arc: [100, 100, 80, 100, 60]
}, 2000, "bounce");
Tiny update to make this work on IE8.
You need to remove the extra comma at the end of the path array else IE8 will give a JS error.
var arcseg = function (cx, cy, radius, start_r, finish_r) {
start_r = Raphael.rad(start_r);
finish_r = Raphael.rad(finish_r);
var start_x = cx + Math.cos( start_r ) * radius,
start_y = cy + Math.sin( start_r ) * radius,
finish_x = cx + Math.cos( finish_r ) * radius,
finish_y = cy + Math.sin( finish_r ) * radius,
path;
path = [
[ "M", start_x, start_y ],
[ "A", radius, radius, finish_r - start_r,
(finish_r - start_r > Raphael.rad( 180 )) ? 1 : 0,
(finish_r > start_r) ? 1 : 0,
finish_x, finish_y ]
];
return { path: path };
};

Draw a nice neat arc without the massive right angle and have nice corners instead

I've made an arc in Raphael what I was aiming for was just one arc with out the
big right angle in it.
So just one smooth curved line without the right angle.
It's pretty basic and uses the Raphael elliptical arc.
You can see it at http://jsfiddle.net/mailrox/uuAjV/1/
Here's the code:
var raph = Raphael(0, 0, 1000, 1000);
var x = 150;
var y = 150;
var r = 100; //radius
var value = 100;
var maxValue = 360;
var pi = Math.PI;
var cos = Math.cos;
var sin = Math.sin;
var t = (pi/2) * 3; //translate
var rad = (pi*2 * (maxValue-value)) / maxValue + t;
var p = [
"M", x, y,
"l", r * cos(t), r * sin(t),
"A", r, r, 0, +(rad > pi + t), 1, x + r * cos(rad), y + r * sin(rad),
"z"
];
var param = {"stroke-width": 30}
var d = raph.path(p).attr(param);
One way I've done is I could mask the right-angle sections of the lines out however I'd rather not have this and just have once nice curve opposed to managing both that current path and a mask over the top.
Really appreciate some help with this thanks!
Try this. Just take the close path (the 'z') off your SVG path definition (note I didn't test this solution):
var raph = Raphael(0, 0, 1000, 1000);
var x = 150;
var y = 150;
var r = 100; //radius
var value = 100;
var maxValue = 360;
var pi = Math.PI;
var cos = Math.cos;
var sin = Math.sin;
var t = (pi/2) * 3; //translate
var rad = (pi*2 * (maxValue-value)) / maxValue + t;
var p = [
"M", x, y,
"l", r * cos(t), r * sin(t),
"A", r, r, 0, +(rad > pi + t), 1, x + r * cos(rad), y + r * sin(rad)
];
var param = {"stroke-width": 30}
var d = raph.path(p).attr(param);
jsFiddle
You can extend the Raphael object to include an arc function.
The arc calculation has been modfied from Raphael's 'Polar clock' demo: http://raphaeljs.com/polar-clock.html
Demo: http://jsfiddle.net/TmVHq/
Raphael.fn.arc = function(cx, cy, value, total, radius) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = cx + radius * Math.cos(a),
y = cy - radius * Math.sin(a),
path;
if (total === value) {
path = [['M', cx, cy - radius], ['A', radius, radius, 0, 1, 1, (cx - 0.01), cy - radius]];
} else {
path = [['M', cx, cy - radius], ['A', radius, radius, 0, +(alpha > 180), 1, x, y]];
}
return this.path(path);
}
var Paper = new Raphael('canvas', 300, 300);
var arc = Paper.arc(150, 150, 270, 360, 100);
arc.attr('stroke-width', 15);​

Categories