RaphaelJs: Circle doesn't show proper value - javascript

I have a problem with Raphael library.
I'm trying to load image inside circle in four steps:
1 step = 25% of circle
2 step = 50% of circle
3 step = 75% of circle
4 step = 100% of circle
If I start from step 2(50%) everything works fine, but if I start from 25% image doesn't show proper. It looks like image is out of circle. Here is live example, I hope it will explain what I mean. http://jsfiddle.net/H4CJF/1/
var amount = 25;
var archtype = Raphael("canvas", 350, 350);
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
};
};
var my_arc = archtype.path().attr({
"fill": "url(http://i.imgur.com/YR5gCBV.png)",
arc: [100, 100, amount, 100, 50]
}).rotate(180);
function next_step() {;
amount = amount + 25;
my_arc.attr({
arc: [100, 100, amount, 100, 50]
})
};
var el = document.getElementById("more");
el.addEventListener("click", next_step, false);
Image is 100x100, radius 50px. I would to like fill circle with image, without white space or reapeted background.
Thanks for any help.
UPDATE: Here is how does look my image:
If I set amount to 25 and then I click Next Step, circle looks like this:
while it should be half of orginal image (red on top, yellow on bottom).
I think it something with position from where is start draw circle, but I can't figure out how to fix this problem.

OK it was just a strange little bug! The incrementing of the amount was causing the value to exceed 100 - therefore the code that checks to see if the value is at the total amount == total only got ran the first time:
Start: 25
Click 1: 50
Click 2: 75
Click 3: 100 <--only time it got ran at full
Click 4: 125
To avoid this, just add in a mod line to reduce the number down and all is fixed:
function next_step() {
amount = amount % 100; <--amount will never exceed 100 now
amount = amount + 25;
my_arc.attr({
arc: [100, 100, amount, 100, 50]
})
};
The mod line needs to be above the increment line, or it will turn a full value of 100 into a 0, and we have the same problem.

Related

Calculating distances between cubes (when wraparound exists)

I have a large cube composed of smaller cubes. The large cube consists of 10 cubes wide, by 10 cubes in length, by 10 cubes in height. For a total of 1000 cubes.
One cube will be randomly chosen to be blue
Three cubes will be randomly chosen to be green
I want to be able to determine which is the closest green cube to the blue cube.
One other thing that is important is that each side of the cube is connected to the opposite side (i.e. row 10 is considered next to row 1). This is the wraparound effect.
So, for example, if the blue cube is at coordinates 9:8:8 and the green cubes are each at 1:2:2, 5:5:3, and 6:3:4. Then the green cube at 1:2:2 should be considered the closest cube. If my calculations are correct, it should have a distance of 10 whereas the other two would each have a distance of 12.
Without the cube wraparound (side 1 connected with side 10) I have been able to come up with the following in JavaScript:
let lowest = 1000;
let lowest_index = -1;
for (i = 0; i < green_cube.length; i++){
let x_offset = Math.abs(blue_cube.x - green_cube[i].x);
let y_offset = Math.abs(blue_cube.y - green_cube[i].y);
let z_offset = Math.abs(blue_cube.z - green_cube[i].z);
let distance = x_offset + y_offset + z_offset;
if (distance < lowest){
lowest = distance;
lowest_index = i;
}
}
What is the proper way to code this when taking wraparound into effect?
Update
To clarify, the distance needs to be distance by number of cubes traveled to get from point A to point B. Distance must be traveled only along the X, Y, and Z axis, therefore, diagonal distance will not work. I believe this is referred to as taxicab distance in 3D space.
I believe it's often termed wraparound.
To take wraparound into account your distance measure, e.g. for the x dimension, should be:
let x_offset = Math.min((10 + blue.x - green[i].x) % 10, (10 + green[i].x - blue.x) % 10)
x_offset will always be positive.
Here is a stupid trick to keep your thinking straight.
Let v be the vector (5, 5, 5) - blue_cube. Add v to every cube's position, adding/subtracting 10 if it goes off an edge. Now the blue cube is at (5, 5, 5) and the shortest path to the other cubes no longer goes off the edge.
In your example, v = (5, 5, 5) - (9, 8, 8) = (-4, -3, -3). The first green cube moves to (1, 2, 2) + (-4, -3, -3) = (-3, -1, -1) = (7, 9, 9) and its distance is 10. The second green cube moves to (5, 5, 3) + (-4, -3, -3) = (1, 2, 0) and its distance is 12. The third green cube moves to (6, 3, 4) + (-4, -3, -3) = (2, 0, 1) and its distance is again 12. So the first is indeed closest.
In this code I am using distance calculation formula for 2 points in 3d (reference).
const calculateDistance3d = ({x: x1, y: y1, z: z1}, {x: x2, y: y2, z: z2}) => {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2));
}
const calculateLoopedDistance = (cubeA, cubeB) => {
return calculateDistance3d(cubeA, {
x: cubeA.x + 10 - Math.abs(cubeB.x - cubeA.x),
y: cubeA.y + 10 - Math.abs(cubeB.y - cubeA.y),
z: cubeA.z + 10 - Math.abs(cubeB.z - cubeA.z)
});
};
const getClosest = (green_cube, blue_cube) => {
let minDistance = 1000;
let closestIndex = 0;
blue_cube.forEach((cube, index) => {
const distance = calculateDistance3d(green_cube, cube);
const loopedDistance = calculateLoopedDistance(green_cube, cube);
if (distance < minDistance || loopedDistance < minDistance) {
minDistance = Math.min(distance, loopedDistance);
closestIndex = index;
}
});
return closestIndex;
}
console.log(getClosest({x: 9, y: 8, z: 8}, [
{x: 1, y: 2, z: 2},
{x: 5, y: 5, z: 3},
{x: 6, y: 3, z: 4}
]));
console.log(getClosest({x: 9, y: 8, z: 8}, [
{x: 5, y: 5, z: 3},
{x: 1, y: 2, z: 2},
{x: 6, y: 3, z: 4}
]));
At the end of this script there are 2 logs with cube's data. You can test different data there.
I updated / fixed calculateLoopedDistance() function, which was incorrect.
Virtually replicate the green cubes as if they appeared at x, x-10 and x+10 and keep the minimum delta. This is done on the three axis independently.
I've come across another solution that also works:
let cube_width = 10;
let mid_point = cube_width / 2;
let x_offset = Math.abs(point1 - point2);
if (x_offset > mid_point){
x_offset = cube_width - x_offset;
}
I'm having a hard time figuring out whether this one or SirRaffleBuffle's solution is more efficient for time.

As mass goes up, speed goes down

Trying to develop an Agar clone, and I've got a lot of it, but I can't quite figure out how to decrement the player's speed as its mass increases. I've tried several different ways, but nothing works. How would I make the speed go down as the mass goes up? Here's my jsFiddle. This is where I set the speed of of the players:
var playerOneMass = 36;
var player1X = (canvas.width / 2) + 50;
var player = new Player({
x: player1X,
y: canvas.height / 2,
radius: playerOneMass,
speed: {
x: 5,
y: 5
},
name: "player 1",
dir: null
});
var playerTwoMass = 36;
var player2X = (canvas.width / 2) - 50;
var player2 = new Player({
x: player2X,
y: canvas.height / 2,
radius: playerTwoMass,
speed: {
x: 5,
y: 5
},
name: "player 2",
dir: null
});
Let us bring some math in to help us out a little bit. When you want something to grow smaller as another grows bigger, the best option that I have found is to use an inversely proportional relationship. This will allow a smooth smaller and smaller look for you.
new_speed = scalar * start_speed / current_mass
When coming up with the scalar, I have found it best to trial and error until it looks how you want it to.
Here is an example of the equation in action utilizing Two.js.
var two = new Two({width:320, height:180}).appendTo(document.getElementById("mytwo")),
rect = two.makeRectangle(100, 100, 10, 10),
circ = two.makeCircle(5, 100, 5),
mass = 10,
rspeed = Math.PI / 10,
mspeed = 14,
scalar = 10;
// Make it look pretty!
rect.fill = "rgb(100,255,100)";
circ.fill = "rgb(100,100,255)";
// Looping...
two.bind('update', function(fc) {
// Prevents from growing indefinitely
if(mass > 150) return;
mass += 1.5;
rect.scale += .1;
circ.scale += .1;
rect.rotation += scalar * rspeed / mass;
circ.translation.addSelf(new Two.Vector(
scalar * mspeed / mass, 0));
}).play();
<script type="text/javascript" src="http://cdn.jsdelivr.net/gh/jonobr1/two.js/build/two.min.js"></script>
<div id="mytwo"><div></div></div>

Optimal algorithm for segmenting set of integers into labels for a chart axis?

Say you get values anywhere from 0 to 1,000,000,000, and you want to plot 30 days. So one particular chart may have a set like:
[ 1, 465, 123, 9, ... ]
While another chart can have a set with much larger numbers:
[ 761010, 418781, ... ]
Is there an "optimal algorithm" that can take those values and segment them into "clean" numbers? Sorry for the wording, don't know the right terminology, I will try to explain.
By "optimal algorithm", I mean both in terms of minimum number of computational steps, given that it creates labels (say for the y-axis) that are simplest from a human perspective.
For example, say you always want to divide the y-axis into 5 labels. You could do this:
var max = Math.max.apply(Math, values); // 465 (from the first set of values)
var interval = max / 5;
var labels = [ interval * 0, interval * 1, interval * 2, ... ];
But that creates labels like:
[ 0, 93, 186, ... ]
And that would be complex for humans to understand. What would be better (but still not ideal) is to create labels like:
[ 0, 125, 250, 375, 500 ]
But that's still to specific. Somehow it should figure out that a better segmentation is:
[ 0, 200, 400, 600, 800 ]
That way, it's divided into more intuitive chunks.
Is there a standard way to solve this problem? What algorithm works best?
Some maths
var getLabelWidth = function(sep, max_value){
var l = (""+max_value).length;
var av = max_value/sep/Math.pow(10,l-2); // get the length max 2 digit
/// 15.22
var width = (Math.ceil(av)*Math.pow(10,l-2)); // do a ceil on the value retrieved
// and apply it to the width of max_value.
// 16 * 10 000
return width;
}
console.log(getLabelWidth(2,59)); // 30 : [0, 30, 60]
console.log(getLabelWidth(2,100)); // 50 : [0, 50, 100]
console.log(getLabelWidth(2,968)); // 490 : [0, 490, 980]
console.log(getLabelWidth(3,368)); // 130 : [0, 130, 260, 390]
console.log(getLabelWidth(3,859)); // 290 : [0, 290, 580, 870]
console.log(getLabelWidth(3,175)); // 60 : [0, 60, 120, 180]
console.log(getLabelWidth(3,580)); // 200 : [0, 200, 400, 600]
console.log(getLabelWidth(3,74)); // 25 : [0, 25, 50, 75]
console.log(getLabelWidth(4,1111)); // 300 :[0, 300, 600, 900, 1200]
console.log(getLabelWidth(4,761010)); // 200 000: [0, 200000, 400000, 600000, 800000]
It could be improved a little bit i guess,
sorry for my bad english .
For reference, here's what I ended up doing.
function computeLabels(count, max) {
var magnitude = orderOfMagnitude(max);
var multiplier = magnitude * count;
// 1
if (multiplier >= max) return buildLabels(count, multiplier);
// 2
multiplier *= 2;
if (multiplier >= max) return buildLabels(count, multiplier);
// 5
multiplier *= 5;
if (multiplier >= max) return buildLabels(count, multiplier);
// 10, don't think it will ever get here but just in case.
multiplier *= 10;
if (multiplier >= max) return buildLabels(count, multiplier);
}
function buildLabels(count, multiplier) {
var labels = new Array(count);
while (count--) labels[count] = formatLabel(count * multiplier);
return labels;
}
function formatLabel(value) {
if (value > 10e5) return (value / 10e5) + 'M'; // millions
if (value > 10e2) return (value / 10e2) + 'K'; // thousands
return value; // <= hundreds
}
function orderOfMagnitude(val) {
var order = Math.floor(log10(val) + 0.000000001);
return Math.pow(10, order);
}
After drawing it out on paper, the "desirable" labels seemed to follow a simple pattern:
Find the max value in the set.
Get the order of magnitude for it.
Multiply the order of magnitude by the number of ticks.
Iterate: If that previous calculation is greater than the max value, then use it. Otherwise, multiply the value times 2 and check. If not, try times 5. So the pattern is, 1, 2, 5.
This gives you labels that are like:
10, 20 (2 ticks)
20, 40
50, 100
100, 200
200, 400
500, 1000
...
10, 20, 30 (3 ticks)
20, 40, 60
50, 100, 150 (don't like this one too much but oh well)
100, 200, 300
10, 20, 30, 40 (4 ticks)
...
It seems like it can be improved, both in producing better quality "human readable" labels, and in using more optimized functionality, but don't quite see it yet. This works for now.
Would love to know if you find a better way!

Raphael.js: Drawing a circle with an edge border and a fill colour

drawing SVGs with numbers is confusing but very, very useful. By modding other people's examples, I have a Raphael.js function that takes 60 seconds to draw a thick green line around a circle with text in the center of it. Here it is in JSFiddle form, with a black background on the container Div to show that there is currently no background colour on the circle:
http://jsfiddle.net/8NZfU/1/
Here's the JS:
//60s circular timer
function timer60s() {
var archtype = Raphael("canvas60s", 200, 200);
var set = archtype.set();
function drawCircle() {
var archtype = Raphael("canvas60s", 200, 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
};
};
var my_arc = archtype.path().attr({
"stroke": "#339933",
"stroke-width": 10,
arc: [100, 100, 0, 100, 50]
});
my_arc.animate({
arc: [100, 100, 100, 100, 50]
}, 60000);
} //end drawCircle
drawCircle();
} // end timer60s
timer60s();
What I want is to create the effect of a white background on the whole circle inside of where the arc is drawn. I also want to add static thin green borders to either side of the arc, so that it appears that the arc is slowly filling in between the lines.
I haven't had any success drawing this background + borders in either CSS or Raphael- can anyone suggest an approach that could work, based on my JSFiddle?
I am not sure, but are you able to draw a second circle?
Like this: http://jsfiddle.net/5knjn/
archtype.circle(100, 100, 50).attr({
"fill": "#fff",
"stroke": "#fff",
"stroke-width": "10"
});
I didn't understand the part:
I also want to add static thin green borders to either side of the arc, so that it appears that the arc is slowly filling in between the lines.
So I came up with :) http://jsfiddle.net/5knjn/1/
Also I don't like the way you enter text in the canvas.
You could use Raphael.text()
Included text: http://jsfiddle.net/5knjn/2/

How to change the size of an animated circle onclick using raphael js

I've got a animated circle which looks like this:
the blue part counts down, so for example from full to nothing in 10 seconds. The orange circle is just a circle. But I want that the circle will be smaller when you click on it. So i made an onclick event for the circle.
circleDraw.node.onclick = function () {
circleDraw.animate({
stroke: "#E0B6B2",
arc: [100, 100, 100, 100, 100]
}, 500);
circleDraw.toFront();
};
That works, i've made it for both of the circles, they both become smaller but, After the 500 mili seconds the blue circle becomes big again because the timer for the blue circle got the parameters that it should be bigger.
circleDraw.animate({
arc: [100, 100, 0, 100, 500]
}, 10000);
Because the blue circle counts for 10 seconds it becomes automatically bigger again. How do I make both circles smaller but keep the timer counting down?
I was thinking of stopping the animation for the blue circle and save the remaining mili seconds of the animation draw it again smaller and start the animation again with the remaining seconds, but I don't know how to do this. But maybe i'm looking in the wrong direction and do I have to make it different.
Thanks.
All my code:
/************************************************************************/
/* Raphael JS magic
*************************************************************************/
var drawTheCircleVector = 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 the circles
*************************************************************************/
var timerCircle = Raphael("timer", 320, 320);
var circleBg = Raphael("backgroundCircle", 320, 320);
timerCircle.customAttributes.arc = drawTheCircleVector
circleBg.customAttributes.arc = drawTheCircleVector
/************************************************************************/
/* draw the circles
*************************************************************************/
var drawMe = circleBg.path().attr({
"fill": "#FF7F66",
"stroke": 0,
arc: [160, 160, 100, 100, 140]
});
var clickOnes = true;
drawMe.node.onclick = function() {
if (clickOnes == true) {
circleDraw.animate({
arc: [100, 100, 0, 100, 100]
}, 500);
circleDraw.toFront();
drawMe.animate({
arc: [100, 100, 100, 100, 100]
}, 500);
circleDraw.toFront();
clickOnes = false;
} else {
circleDraw.animate({
arc: [160, 160, 0, 100, 150]
}, 500);
circleDraw.toFront();
drawMe.animate({
arc: [160, 160, 100, 100, 140]
}, 500);
circleDraw.toFront();
clickOnes = true;
}
};
// arc: [Xposition, Yposition, how much 1 = begin 100 = end, ? = 100, 150];
/************************************************************************/
/* Draw the circle
*************************************************************************/
var circleDraw = timerCircle.path().attr({
"stroke": "#2185C5",
"stroke-width": 10,
arc: [160, 160, 100, 100, 150]
});
circleDraw.animate({
arc: [160, 160, 0, 100, 150]
}, 9000);
window.setInterval(function() {
goToNextStopGardensPointBus210()
}, 9000);
Here is my code the timer works and if you click on the circle it will become smaller but if you click on it before the bue cirlce is done it will become big again.
UPDATE
working version of what I got on jsFiddle,
http://jsfiddle.net/hgwd92/2S4Dm/
Here's a fiddle for you..
The issue was you where redrawing the items, with new animation speeds and such. Using the transform function, you can add a scaling transform that acts independent of the draws.
circleDraw.animate({transform: 'S0.5,0.5,160,160', 'stroke-width': 5}, 500);
and then to set it back...
circleDraw.animate({transform: 'S1,1,160,160', 'stroke-width': 10}, 500);
Note you need to set the center for the blue arc (the 160, 160), or once it gets past half way the center of the arc will move and it will scale to a different position.
EDIT: Updated the fiddle and code to scale the blue line to so it looks better.

Categories

Resources