I'm hacking at a fiddle and cannot control the path width.
see the 2nd last line, really baffling:
var paper = Raphael(20, 20, 320, 320);
function arc(center, radius, startAngle, endAngle) {
angle = startAngle;
coords = toCoords(center, radius, angle);
path = "M " + coords[0] + " " + coords[1];
while(angle<=endAngle) {
coords = toCoords(center, radius, angle);
path += " L " + coords[0] + " " + coords[1];
angle += 1;
}
return path;
}
function toCoords(center, radius, angle) {
var radians = (angle/180) * Math.PI;
var x = center[0] + Math.cos(radians) * radius;
var y = center[1] + Math.sin(radians) * radius;
return [x, y];
}
var ps = paper.path(arc([100, 100], 80, -90, 90));
ps.attr({stroke:'#C00',width:100});
ps.glow({color: '#f00',width: 20});
here's the fiddle:
http://jsfiddle.net/U5Kpx/5/
can anyone see what I'm doing wrong here?
Thanks very much
You should use stroke-width
eg:
ps.attr({stroke:'#C00',"stroke-width":5});
ps.glow({color: '#f00',width: 40});
Check this fiddle
Related
var circleA = {
x: 100,
y: 40,
radius: 20,
color: '63, 81, 181',
pathx: 220,
pathy: 150
}
var circleB = {
x: 250,
y: 50,
radius: 30,
color: '76, 175, 80',
pathx: 120,
pathy: 140
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = 500;
var centerY = 500;
//draw circle A
context.beginPath();
context.arc(circleA.x, circleA.y, circleA.radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(" + circleA.color + ", 1)";
context.fill();
//draw circle A path destination
context.beginPath();
context.arc(circleA.pathx, circleA.pathy, circleA.radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(" + circleA.color + ", 0.5)";
context.fill();
//draw line in circle A path
context.beginPath();
context.moveTo(circleA.x,circleA.y);
context.lineTo(circleA.pathx,circleA.pathy);
context.strokeStyle = "rgba(" + circleA.color + ", 1)";
context.stroke();
//draw circle B
context.beginPath();
context.arc(circleB.x, circleB.y, circleB.radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(" + circleB.color + ", 1)";
context.fill();
//draw circle B path destination
context.beginPath();
context.arc(circleB.pathx, circleB.pathy, circleA.radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(" + circleB.color + ", 0.5)";
context.fill();
//draw line in circle A path
context.beginPath();
context.moveTo(circleB.x,circleB.y);
context.lineTo(circleB.pathx,circleB.pathy);
context.strokeStyle = "rgba(" + circleB.color + ", 1)";
context.stroke();
//I NEED HELP HERE - i have no idea how to calculate this
function willCollide(ca_start, ca_end, cb_start, cb_end)
{
var RSum = circleA.radius + circleB.radius;
var t = 10;
var a = getPos(circleA, t);
var b = getPos(circleB, t);
var distance = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
var sum = RSum*=2;
context.font = "20px Arial";
context.fillText('distance: ' + distance + " sum: " + sum,10,200);
}
function getPos(circle, t)
{
//position changes
var dax = (circle.pathx - circle.x);
var day = (circle.pathy - circle.y);
//normalize components
var lenA = Math.sqrt(dax * dax + day * day);
dax = dax / lenA;
day = day / lenA;
//position vs time
var ax = circleA.x + dax * t;
var ay = circleA.y + day * t;
return {
x: ax,
y: ay
}
}
willCollide(
{ x: circleA.x, y: circleA.y },
{ x: circleA.pathx, y: circleA.pathy },
circleA.radius,
{ x: circleB.x, y: circleB.y },
{ x: circleB.pathx, y: circleB.pathy },
circleB.radius
);
body {
margin: 0px;
padding: 0px;
}
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
</body>
</html>
I have circles running around in space with a given path on each frame.
so I can say that circle A will go to xy(10, 8) and that circle B will go to xy(5, -3).
and I need to make sure the path each circle is going is clear and that there is no other circle that will go on that path, is so, I want to give it a new path.
I've attached an image explaining the situation and the desired result on each case.
your help is much appreciated. thank you!
Position of the first circle is described as
x1 = x1_0 + vx1 * t
y1 = y1_0 + vy1 * t
where x10 is initial x-coordinate, vx1 is x-component of velocity vector, t is time.
Make similar expression for the second circle center, build expression for squared distance and find time when squared distance becomes equal to 4R^2 (for equal radii) - if solution exists at needed time interval, it is the moment of circles' collision.
In your designations (seems you have equal velocities):
RSum = circleA.radius + circleB.radius
//position changes
dax = (circleA.pathx - circleA.x)
day = (circleA.pathy - circleA.y)
//normalize components
lenA = Sqrt(dax * dax + day * day)
dax = dax / lenA
day = day / lenA
//position vs time
ax = circleA.x + dax * t
ay = circleA.y + day * t
and similar for B circle
Now make distance equation, substitute ax, bx, ay, by expressions and solve it for unknown t (quadratic equation, might have 0,1,2 roots)
(ax - bx) * (ax - bx) + (ay - by) * (ay - by) = RSum * RSum
or
(circleA.x + dax * t - circleB.x - dbx * t) * ....
I am helping someone solve this. We have SVG elements in g tag. We are using the transformation on the g tag and converting it into matrix.
Here is the jsbin link.
We are a bit confused by the following:
How to calculate the 'resistor', denoted by dx & dy in line 23 & 24 in the jsbin.
Where we are going wrong with the calculations for rotate & scale using matrices.
Is there a better way of doing this mathematically? I need to mention that the relevant person has been struggling with this for over two weeks so appreciate the help.
The relevant Js code:
var mouseXY; //object to store the mouse event x & y
var angle1; //first angle before rotate
var center; //center of the shape that we want to rotate
//this function is call when mouse down
//on the resize button
//this function bind the mouse event
//to scale the shape
function bindScaleShapeEvent(){
mouseXYPosition(event);
document.addEventListener("mousemove",scaleImage);
document.addEventListener("mouseup",removeScaleImage);
}
//function to scale the shape when
//user mouse move
function scaleImage(event){
var parent = document.getElementById("1"); //parent of shape
var dx = (event.pageX - mouseXY.x);
var dy = (event.pageY - mouseXY.y);
dx /= 80; // 80 is the resistor value
dy /= 80;
var scaleX = dx;
var scaleY = dy;
var matrix = parent.getAttribute("transform");
var matrixArray = matrixToArray(matrix);
//make the new matrix of the shape
scaleString = "matrix(" +
(parseFloat(matrixArray[0]) + scaleX) + " " +
parseFloat(matrixArray[1]) + " " +
parseFloat(matrixArray[2]) + " " +
(parseFloat(matrixArray[3]) + scaleY) + " " +
parseFloat(matrixArray[4]) + " " +
parseFloat(matrixArray[5]) +")";
parent.setAttribute("transform", scaleString);
mouseXYPosition(event);
}
//convert the transformation to array
function matrixToArray(matrix){
matrix = matrix.split('(');
matrix = matrix[1].split(')');
matrix = matrix[0].split(' ');
return matrix;
}
//remove eventListener from the
//shape resize button
function removeScaleImage(){
document.removeEventListener("mousemove",scaleImage);
document.removeEventListener("mouseup",removeScaleImage);
}
//this function is call when mouse down
//on the rotate button
//this function bind the mouse event
//to rotate the shape
function bindRotateShapeEvent(event){
mouseXYPosition(event);
centerXY();//store the center of the shape that we want to rotate
var dx = (mouseXY.x) *3;
var dy = (mouseXY.y) *3;
angle1 = (180 * Math.atan2(dy, dx) / Math.PI); //first angle of the shape
document.addEventListener("mousemove",rotateImage);
document.addEventListener("mouseup",removeRotateImage);
}
//applying rotation on the shape
function rotateImage(event){
var dx = (event.pageX - mouseXY.x);
var dy = (event.pageY - mouseXY.y);
var parent = document.getElementById("1");
var angle2 = (180 * (Math.atan2(dy,dx)) / Math.PI);
var angle = angle2 - angle1;
var radian= 0.06 * angle;
var cos = Math.cos(radian);
var sin = Math.sin(radian);
var matrix ='matrix(' +
(cos) +
' ' + (sin) +
' ' + (-sin) +
' ' + (cos) +
' ' + (-center.cx * cos + center.cy * sin + center.cx) +
' ' + (-center.cx * sin - center.cy * cos + center.cy) +
')';
parent.setAttribute("transform",matrix);
}
function removeRotateImage(){
document.removeEventListener("mousemove",rotateImage);
document.removeEventListener("mouseup",removeRotateImage);
}
//save the center of the shape
function centerXY(){
var shape = document.getElementById("1");
var bbox = shape.getBBox();
center ={
cx: bbox.x + (bbox.width / 2),
cy: bbox.y + (bbox.height / 2)
};
}
//save the move x and y
function mouseXYPosition(event){
mouseXY = {
x: event.pageX,
y: event.pageY
};
}
The Jsbin link to the working demo.
I'm trying to take the demo pie from the Raphael.js (http://raphaeljs.com/pie.html) and draw it clockwise - currently it's being drawn anticlockwise which doesn't make an awful lot of sense. This jsfiddle is the code from the demo:
http://jsfiddle.net/6bWuT/
On line 14 (beginning 'return paper.path' below) changing the second 0 to a 1 should change the rotation to clockwise but actually just screws the plot. Any help would be massively appreciated :) thanks in advance.
Code from the fiddle for reference:
Raphael.fn.pieChart = function (cx, cy, r, values, labels, stroke) {
var paper = this,
rad = Math.PI / 180,
chart = this.set();
function sector(cx, cy, r, startAngle, endAngle, params) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
}
var angle = 0,
total = 0,
start = 0,
process = function (j) {
var value = values[j],
angleplus = 360 * value / total,
popangle = angle + (angleplus / 2),
color = Raphael.hsb(start, .75, 1),
ms = 500,
delta = 30,
bcolor = Raphael.hsb(start, 1, 1),
p = sector(cx, cy, r, angle, angle + angleplus, {fill: "90-" + bcolor + "-" + color, stroke: stroke, "stroke-width": 3}),
txt = paper.text(cx + (r + delta + 55) * Math.cos(-popangle * rad), cy + (r + delta + 25) * Math.sin(-popangle * rad), labels[j]).attr({fill: bcolor, stroke: "none", opacity: 0, "font-size": 20});
p.mouseover(function () {
p.stop().animate({transform: "s1.1 1.1 " + cx + " " + cy}, ms, "elastic");
txt.stop().animate({opacity: 1}, ms, "elastic");
}).mouseout(function () {
p.stop().animate({transform: ""}, ms, "elastic");
txt.stop().animate({opacity: 0}, ms);
});
angle += angleplus;
chart.push(p);
chart.push(txt);
start += .1;
};
for (var i = 0, ii = values.length; i < ii; i++) {
total += values[i];
}
for (i = 0; i < ii; i++) {
process(i);
}
return chart;
};
$(function () {
var values = [],
labels = [];
$("tr").each(function () {
values.push(parseInt($("td", this).text(), 10));
labels.push($("th", this).text());
});
$("table").hide();
Raphael("holder", 700, 700).pieChart(350, 350, 200, values, labels, "#fff");
});
Oop, found it.. change the sector function:
function sector(cx, cy, r, startAngle, endAngle, params) {
var x1 = cx + r * Math.cos(startAngle * rad),
x2 = cx + r * Math.cos(endAngle * rad),
y1 = cy + r * Math.sin(startAngle * rad),
y2 = cy + r * Math.sin(endAngle * rad);
return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 1, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
}
The cos and sin are now being made to positive numbers instead of negative, and the 0 is changed to a 1 on the paper.path line.
Win! :)
Ok so granted, its not a bug, but I am confounded by how to get a perfect circle arc between points via Bézier curve.
I need a shape like this:
So I've been calculating the four corner points like this from the center point, radius and angle with the following formula: (x?,y?)=(x+d cos α,y+d sin α), which in my coffeescript looks something like this:
x1 = centerPointX+outerRadius*Math.cos(currentAngle)
y1 = centerPointY+outerRadius*Math.sin(currentAngle)
x2 = centerPointX+innerRadius*Math.cos(currentAngle)
y2 = centerPointY+innerRadius*Math.sin(currentAngle)
x3 = centerPointX+outerRadius*Math.cos(currentAngle2)
y3 = centerPointY+outerRadius*Math.sin(currentAngle2)
x4 = centerPointX+innerRadius*Math.cos(currentAngle2)
y4 = centerPointY+innerRadius*Math.sin(currentAngle2)
How can I take the information I have and result in a path element with perfect circular curves?
(PS I am newish to SVG and if you want to help me out with the proper syntax for d= that would be cool, but I can always just write it myself. The challenge I would like help with is really more to do with Bézier.
UPDATE / SOLUTION
Using the answer below a guidance below is the function I actually used:
annularSector = (centerX,centerY,startAngle,endAngle,innerRadius,outerRadius) ->
startAngle = degreesToRadians startAngle+180
endAngle = degreesToRadians endAngle+180
p = [
[ centerX+innerRadius*Math.cos(startAngle), centerY+innerRadius*Math.sin(startAngle) ]
[ centerX+outerRadius*Math.cos(startAngle), centerY+outerRadius*Math.sin(startAngle) ]
[ centerX+outerRadius*Math.cos(endAngle), centerY+outerRadius*Math.sin(endAngle) ]
[ centerX+innerRadius*Math.cos(endAngle), centerY+innerRadius*Math.sin(endAngle) ]
]
angleDiff = endAngle - startAngle
largeArc = (if (angleDiff % (Math.PI * 2)) > Math.PI then 1 else 0)
commands = []
commands.push "M" + p[0].join()
commands.push "L" + p[1].join()
commands.push "A" + [ outerRadius, outerRadius ].join() + " 0 " + largeArc + " 1 " + p[2].join()
commands.push "L" + p[3].join()
commands.push "A" + [ innerRadius, innerRadius ].join() + " 0 " + largeArc + " 0 " + p[0].join()
commands.push "z"
return commands.join(" ")
Demo: http://phrogz.net/svg/procedural_annular_sector.xhtml
Usage:
annularSector( myPathElement, {
centerX:100, centerY:150,
startDegrees:190, endDegrees:230,
innerRadius:75, outerRadius:100
});
Core function:
// Options:
// - centerX, centerY: coordinates for the center of the circle
// - startDegrees, endDegrees: fill between these angles, clockwise
// - innerRadius, outerRadius: distance from the center
// - thickness: distance between innerRadius and outerRadius
// You should only specify two out of three of the radii and thickness
function annularSector(path,options){
var opts = optionsWithDefaults(options);
var p = [ // points
[opts.cx + opts.r2*Math.cos(opts.startRadians),
opts.cy + opts.r2*Math.sin(opts.startRadians)],
[opts.cx + opts.r2*Math.cos(opts.closeRadians),
opts.cy + opts.r2*Math.sin(opts.closeRadians)],
[opts.cx + opts.r1*Math.cos(opts.closeRadians),
opts.cy + opts.r1*Math.sin(opts.closeRadians)],
[opts.cx + opts.r1*Math.cos(opts.startRadians),
opts.cy + opts.r1*Math.sin(opts.startRadians)],
];
var angleDiff = opts.closeRadians - opts.startRadians;
var largeArc = (angleDiff % (Math.PI*2)) > Math.PI ? 1 : 0;
var cmds = [];
cmds.push("M"+p[0].join()); // Move to P0
cmds.push("A"+[opts.r2,opts.r2,0,largeArc,1,p[1]].join()); // Arc to P1
cmds.push("L"+p[2].join()); // Line to P2
cmds.push("A"+[opts.r1,opts.r1,0,largeArc,0,p[3]].join()); // Arc to P3
cmds.push("z"); // Close path (Line to P0)
path.setAttribute('d',cmds.join(' '));
function optionsWithDefaults(o){
// Create a new object so that we don't mutate the original
var o2 = {
cx : o.centerX || 0,
cy : o.centerY || 0,
startRadians : (o.startDegrees || 0) * Math.PI/180,
closeRadians : (o.endDegrees || 0) * Math.PI/180,
};
var t = o.thickness!==undefined ? o.thickness : 100;
if (o.innerRadius!==undefined) o2.r1 = o.innerRadius;
else if (o.outerRadius!==undefined) o2.r1 = o.outerRadius - t;
else o2.r1 = 200 - t;
if (o.outerRadius!==undefined) o2.r2 = o.outerRadius;
else o2.r2 = o2.r1 + t;
if (o2.r1<0) o2.r1 = 0;
if (o2.r2<0) o2.r2 = 0;
return o2;
}
}
I have a problem with Raphael.js. I want to rotate the "compassScale" - set in the following code - in a relative manner.
This works for the paths, but all the texts "animate" to the absolute rotation of 30 degree. I want them to rotate to the 30 degrees relative from their actual positions.
var compassScale = paper.set();
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
compassScale.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
Like you said, the problem is that you're animating all elements to 30 degrees, not their current rotation + 30 degrees. It's actually quite simple once you think of it that way. Here is your revised code that works:
var compassScale = paper.set();
var texts = []; // array to hold the text elements
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
texts.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
// loop through the text elements, adjusting their rotation by adding 30 to their individual rotation
for (var i = 0, l = texts.length; i < l; i += 1) {
// node.attr("rotation") returns something like 50 200 200, so we have to split the string and grab the first number with shift
texts[i].animate({rotation: (30 + +texts[i].attr("rotation").split(" ").shift()) + " " + centerX + " " + centerY}, 5000);
}
Just a quick observation:
Looks like "Rotation" isn't part of the Atrr anymore since ver 2, so you can't use it in "animate", but you can replace that with "transform: "r" + (some degree)"..
eg:
element.animate( {transform: "r" + (-90)}, 2000, 'bounce');