I've read other examples on here on finding the centroid of a polygon, I do not see where I am going wrong, why my centroid is so far off. If someone could please explain to me where my calculation is off.
I came up with this solution:
function getCentroid() {
var coords = getCoords();
var signedArea = 0;
var x = 0;
var y = 0;
for (var i = 0; i < coords.length - 1; i++) {
var temp = (coords[i].x * coords[i + 1].y) - (coords[i + 1].x * coords[i].y);
signedArea += temp;
x += (coords[i].x + coords[i + 1].x) * temp;
y += (coords[i].y + coords[i + 1].y) * temp;
}
signedArea *= 0.5;
x /= 6 * signedArea;
y /= 6 * signedArea;
return "" + Math.round(x) + "," + Math.round(y);
}
getCoords() returns a JSON array in this form:
[
{
"x":"600",
"y":"124"
},
{
"x":"560",
"y":"396"
},
{
"x":"994",
"y":"370"
},
{
"x":"918",
"y":"121"
},
{
"x":"600",
"y":"124"
}
]
The function getCentroid() returns the centroid (or atleast it should) of the polygon as a commaseperated string in the form: x,y
However, what it is returning is so far off the center.
returns: 312239,219226
Can anyone point me in the right direction?
Silly me, I didn't realize that the values of the coordinates were stored as strings in the json array. I casted them to Numbers and all is well.
Related
I looked around and I couldn't find any info on the topic... probably because I can't iterate my problem accurately into a search engine.
I'm trying to take raw line data from a dxf, sort it into squares, find the center of each square, number the center, and print the result to pdf.
I have a data structure similar to the following:
[
[{x: 50, y:50}, {x:52, y:52}],
[{x: 52, y:52}, {x:54, y:54}],
[{x: 54, y:54}, {x:56, y:56}]...
]
These coordinates are obtained from parsing a dxf using dxf-parser, which returns an array of objects that describe the path of the line. Four of these combine to make a square, which I segment using
function chunkArrayInGroups(arr, size) {
let result = [];
let pos = 0;
while (pos < arr.length) {
result.push(arr.slice(pos, pos + size));
pos += size;
}
return result;
}
((Size = 4))
This behaves as intended for the most part, except these coordinates were created with the origin in the center of the screen. The pdf library I'm using to create the final document does not use the same coordinate system. I believe it starts the origin at the top left of the page. This made all of my negative values ((Anything to the left of the center of the model)) cut off the page.
To remedy this, I iterate through the array and collect '0 - all x and y values' in a new array, which I find the max of to give me my offset. I add this offset to my x values before plugging them into my pdf creator to draw the lines.
I'm not sure what is causing it, but the output is 'Da Vinci Style' as I like to call it, it's rotated 180 degrees and written backwards. I thought adding some value to each cell would fix the negative issue... and it did, but the data is still with respect to a central origin. Is there a way I can redefine this data to work with this library, or is there some other library where I can graph this data and also add text at specific spots as my case dictates. I would like to continue to use this library as I use it for other parts of my program, but I am open to new and more efficient ideas.
I appreciate your time and expertise!
What it's supposed to look like
Source Picture
"Da Vinci'fied" Result
Current Output
Copy of the code:
const PDFDocument = require('pdfkit');
const doc = new PDFDocument({ autoFirstPage: true })
const DxfParser = require('dxf-parser')
let fileText = fs.readFileSync('fulltest.dxf', { encoding: 'utf-8' })
let data = []
let data2 = []
let data3 = []
let shiftx = []
let shifty = []
let factor = 5
var parser = new DxfParser();
let i = 0
doc.pipe(fs.createWriteStream('test.pdf'));
try {
var dxf = parser.parseSync(fileText);
let array = dxf.entities
array.forEach(line => {
if (line.layer === "Modules") {
data.push(line.vertices)
}
if (line.layer === "Buildings") {
data2.push(line.vertices)
}
if (line.layer === "Setbacks") {
data3.push(line.vertices)
}
let segment = line.vertices
segment.forEach(point => {
shiftx.push(0 - point.x)
shifty.push(0 - point.y)
})
})
let shift = biggestNumberInArray(shiftx)
console.log(shift)
data = chunkArrayInGroups(data, 4)
data.forEach(module => {
let midx = []
let midy = []
module.forEach(line => {
let oldx = (line[1].x + shift) * factor
let oldy = (line[1].y + shift) * factor
let newx = (line[0].x + shift) * factor
let newy = (line[0].y + shift) * factor
doc
.moveTo(oldx, oldy)
.lineTo(newx, newy)
.stroke()
midx.push(oldx + (newx - oldx) / 2)
midy.push(oldy + (newy - oldy) / 2)
})
let centerx = (midx[0] + (midx[2] - midx[0]) / 2)
let centery = (midy[0] + (midy[2] - midy[0]) / 2)
let z = (i + 1).toString()
doc
.fontSize(10)
.text(z, centerx-5, centery-5)
i++
})
data2.forEach(line => {
let oldx = (line[0].x + shift) * factor
let oldy = (line[0].y + shift) * factor
let newx = (line[1].x + shift) * factor
let newy = (line[1].y + shift) * factor
doc
.moveTo(oldx, oldy)
.lineTo(newx, newy)
.stroke()
})
data3.forEach(line => {
let oldx = (line[0].x + shift) * factor
let oldy = (line[0].y + shift) * factor
let newx = (line[1].x + shift) * factor
let newy = (line[1].y + shift) * factor
doc
.moveTo(oldx, oldy)
.lineTo(newx, newy)
.stroke('red')
})
doc.end();
} catch (err) {
return console.error(err.stack);
}
function biggestNumberInArray(arr) {
const max = Math.max(...arr);
return max;
}
function chunkArrayInGroups(arr, size) {
let result = [];
let pos = 0;
while (pos < arr.length) {
result.push(arr.slice(pos, pos + size));
pos += size;
}
return result;
}
After sitting outside and staring at the fence for a bit, I revisited my computer and looked at the output again. I rotated it 180 as I did before, and studied it. Then I imagined it flipped over the y axis, like right off my computer. THAT WAS IT! I grabbed some paper and drew out the original coordinates, and the coordinates the pdf library.
input coords ^ >
output coords v >
I realized the only difference in the coordinate systems was that the y axis was inverted! Changing the lines to
let oldx = (line[1].x + shift) * factor
let oldy = (-line[1].y + shift) * factor
let newx = (line[0].x + shift) * factor
let newy = (-line[0].y + shift) * factor
inverted with respect to y and after the shift, printed correctly! Math wins again hahaha
I have a for loop that returns a decimal between 0 and 1. I'd like to make a curve that appears more like a rounded corner than it is now. I'd also like to have it start ramping up only after 0.25. I can't quite figure out how to do it with the math I have now. I'm using Math.log and a linear conversion function, but maybe I need something more related to a parabolic curve.
for (i = 1; i < 101; ++i) {
var dec = i / 100
if (dec >= 0.25) {
console.log("dec = " + dec);
var large = i * 100
var log = Math.log(large);
console.log("log = " + log);
var linCon = applyLinearConversion(log, 2.8, 9.2104, -2.7, 1)
console.log("linCon " + i + " = " + linCon);
document.getElementById("graph").innerHTML += "<div style='background-color:#000000; width:" + (linCon * 1000) + "px; height:5px;'></div>";
}
}
function applyLinearConversion(OldValue, OldMin, OldMax, NewMin, NewMax) {
OldRange = (OldMax - OldMin)
if (OldRange == 0)
NewValue = NewMin
else {
NewRange = (NewMax - NewMin)
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
}
return NewValue
}
<div id="graph"></div>
I have it populating a div with more styled divs.
Mine is like this:
I want mine more like this:
You can use the formula of the half circle graph which is:
It results in the following graph:
Since you are using horizontal divs that are stacked vertically to draw the graph, the x and y coordinates will be reversed and the left quarter of the circle will be used from the above graph.
var width = 200; // the width of the graph
var height = 200; // the height of the graph
var xOffset = 0.25 * width; // the x offset at which the graph will start ramping up (this offset is added to the graph width)
var html = ""; // to accumulate the generated html before assigning it to innerHTML (it's not a good idea to constantly update innerHTML)
for (i = 1; i < 101; ++i) {
var x = 1 - i / 100; // the x coordinate, we are using the left side of the graph so x should be negative going from -1 to 0
var y = Math.sqrt(1 - x * x); // the y coordinate as per the formula (this will be used for the width)
html += "<div style='background-color:#000000; width:" + (xOffset + y * width) + "px; height:" + (height / 100) + "px;'></div>";
}
document.getElementById("graph").innerHTML = html;
<div id="graph"></div>
So, I have an array like this:
var my_array = [
'00000000000000000',
'00000111111110000',
'00000111111110000',
'00000111111110000',
'00000111111110000',
'00000111111110000',
'00000000000000000'
]
...and I'd like to get a function that is able to rotate the array (2d matrix above) by a certain angle (e.g 30°) around a certain point (x/y center of array : e.g X: 8, Y: 3).
So the result for 30° should maybe look like this: (Array is not that exact, just an example...)
var rotated_array = [
"00000000000000000",
"00000000001100000",
"00000000111110000",
"00000001111111000",
"00000111111111100",
"00000111111111000",
"00000011111100000",
"00000001110000000",
"00000000100000000",
"00000000000000000"
]
I've already created a function you can find below, but the function has some errors (The rotated matrix is missing some points and looks kind of destroyed --> You can test it on your own below).
I hope somebody can help me fixing my code, so however...
Thanks in advance, jonas.
BTW: I have marked all 'new' points which were never used with a '*'-character, afterwards I will use zeros instead!
var my_array = [
'00000000000000000000000',
'00000000000000000000000',
'00000000000000000000000',
'00000000000000000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000000000000000000000',
'00000000000000000000000',
'00000000000000000000000',
'00000000000000000000000'
]
Array.prototype.rotate_matrix = function(angle, cx, cy) {
function generate_matrix(rows,cols,value){
var arr = [];
for (var i=0; i < rows; i++) {
arr.push([]);arr[i].push(new Array(cols));
for (var j=0; j < cols; j++) arr[i][j] = value;
}; return arr;
}
var radians = (Math.PI / 180) * angle;
var array = generate_matrix(this.length, this[0].length, '*')
for (var i=0; i<this.length; i++) {
for (var j=0; j<this[i].length;j++) {
var character = this[i][j],
cos = Math.cos(radians),
sin = Math.sin(radians),
newx = Math.round((cos * (j - cx)) + (sin * (i - cy)) + cx) < this[i].length ? Math.round((cos * (j - cx)) + (sin * (i - cy)) + cx) : -1,
newy = Math.round((cos * (i - cy)) - (sin * (j - cx)) + cy) < this[i].length ? Math.round((cos * (i - cy)) - (sin * (j - cx)) + cy) : 1;
try {array[newy][newx]=character;}
catch(e) {
array[i][j] = character
}
}
}
array = array.map(a => [a.join('')])
return array.concat.apply([],array);
}
console.log(my_array.rotate_matrix(30,(my_array[0].length/2),(my_array.length/2)))
I need to plot a graph in a canvas. But how can I use an algebra equation as input, and based on the equation, draw the curve, using javascript?
For example:
x2+5y=250
The equation plots a graph with both positive and negative values.
<!DOCTYPE html>
<html>
<head>
<title>Interactive Line Graph</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.min.js"></script>
<script>
var graph;
var xPadding = 30;
var yPadding = 30;
var data = { values:[
{ X: "1", Y: 15 },
{ X: "2", Y: 35 },
{ X: "3", Y: 60 },
{ X: "4", Y: 14 },
{ X: "5", Y: 20 },
{ X: "6", Y: 95 },
]};
// Returns the max Y value in our data list
function getMaxY() {
var max = 0;
for(var i = 0; i < data.values.length; i ++) {
if(data.values[i].Y > max) {
max = data.values[i].Y;
}
}
max += 10 - max % 10;
return max;
}
// Return the x pixel for a graph point
function getXPixel(val) {
return ((graph.width() - xPadding) / data.values.length) * val + (xPadding * 1.5);
}
// Return the y pixel for a graph point
function getYPixel(val) {
return graph.height() - (((graph.height() - yPadding) / getMaxY()) * val) - yPadding;
}
$(document).ready(function() {
graph = $('#graph');
var c = graph[0].getContext('2d');
c.lineWidth = 2;
c.strokeStyle = '#333';
c.font = 'italic 8pt sans-serif';
c.textAlign = "center";
// Draw the axises
c.beginPath();
c.moveTo(xPadding, 0);
c.lineTo(xPadding, graph.height() - yPadding);
c.lineTo(graph.width(), graph.height() - yPadding);
c.stroke();
// Draw the X value texts
for(var i = 0; i < data.values.length; i ++) {
c.fillText(data.values[i].X, getXPixel(i), graph.height() - yPadding + 20);
}
// Draw the Y value texts
c.textAlign = "right"
c.textBaseline = "middle";
for(var i = 0; i < getMaxY(); i += 10) {
c.fillText(i, xPadding - 10, getYPixel(i));
}
c.strokeStyle = '#f00';
// Draw the line graph
c.beginPath();
c.moveTo(getXPixel(0), getYPixel(data.values[0].Y));
for(var i = 1; i < data.values.length; i ++) {
c.lineTo(getXPixel(i), getYPixel(data.values[i].Y));
}
c.stroke();
// Draw the dots
c.fillStyle = '#333';
for(var i = 0; i < data.values.length; i ++) {
c.beginPath();
c.arc(getXPixel(i), getYPixel(data.values[i].Y), 4, 0, Math.PI * 2, true);
c.fill();
}
});
</script>
</head>
<body>
<canvas id="graph" width="200" height="150">
</canvas>
</body>
</html>
[i am add one example ploter in math.js ] i want to how to full screen plot the graph and mouse are cilck in graph any point to show the details in x&y value.so how to change please help me.
Parsing linear equation.
Or maybe it is the Parsing of the equation that the question is about.
This answer shows how to parse a simple linear equation.
User inputs x2+5y=230 and you need to solve and plot for y for f(x) which would be the function function(x) { return (3 * x -230) / -5; }
Will assume the equation is always in the same form with x and y and some scalars and constants scalar * x + const + scalar * y = const
Define the rules
Rules
Only x and y will be considered variables.
A term is a scalar and a variable 2x or a constant +1.
All additional characters will be ignored including *,/,%
Numbers can have decimal places. Valid numbers 1 +1 0.2 -2 10e5
Scalars must be adjacent to variables 3y2 becomes 6y 3y-2 stays as is.
Parsing
To parse a equation we must break it down into unambiguous easy to manipulate units. In this case a unit I call a term and will have 3 properties.
scalar A number
variable the name of the variable x,y or null for constants
side which side of the equation the term is Left or right
An example equation
2x + 2 + 3y = 4x - 1y
First parsed to create
terms
// shorthand not code
{2,x,true; // true is for left
{2,null,true; // null is a constant
{3,y,true;
{4,x,false;
{-1,y,false;
Once all the terms are parsed then the equation is solved by summing all the terms for x, y and constants and moving everything to the left flipping the sign of any values on the right.
sumX = 2 + -4; //as 4x is on the right it becomes negative
sumY = 3 + 1;
const = 2;
Making the equation
-2x + 4y + 2 = 0
Then move the y out to the right and divide the left by its scalar.
-2x + 2 = 4y
(-2x + 2)/-4 = y
The result is a function that we can call from javascript will the value of x and get the value of y.
function(x){ return (-2 * x + 2) / 4; }
The Parser
The following function parses and returns a function for input equation for x. That function then use to plot the points in the demo below.
function parseEquation(input){
// Important that white spaces are removed first
input = input.replace(/\s+/g,""); // remove whitespaces
input = input.replace(/([\-\+])([xy])/g,"$11$2"); // convert -x -y or +x +y to -1x -1y or +1x +1y
// just to make the logic below a little simpler
var newTerm = () => {term = { val : null, scalar : 1, left : left, };} // create a new term
var pushTerm = () => {terms.push(term); term = null;} // push term and null current
// regExp [xy=] gets "x","y", or "="" or [\-\+]??[0-9\.]+ gets +- number with decimal
var reg =/[xy=]|[\-\+]??[0-9\.eE]+/g; // regExp to split the input string into parts
var parts = input.match(reg); // get all the parts of the equation
var terms = []; // an array of all terms parsed
var term = null; // Numbers as constants and variables with scalars are terms
var left = true; // which side of equation a term is
parts.forEach( p=> {
if (p === "x" || p === "y") {
if (term !== null && term.val !== null) { // is the variable defined
pushTerm(); // yes so push to the stack and null
}
if (term === null) { newTerm(); } // do we need a new term?
term.val = p;
} else if( p === "=") { // is it the equals sign
if (!left) { throw new SyntaxError("Unxpected `=` in equation."); }
if (term === null) { throw new SyntaxError("No left hand side of equation."); }// make sure that there is a left side
terms.push(term); // push the last left side term onto the stack
term = null;
left = false; // everything on the right from here on in
} else { // all that is left are numbers (we hope)
if (isNaN(p)){ throw new SyntaxError("Unknown value '"+p+"' in equation"); }//check that there is a number
if (term !== null && (p[0] === "+" || p[0] === "-")) { // check if number is a new term
pushTerm(); // yes so push to the stack and null
}
if (term === null) { newTerm(); } // do we need a new term?
term.scalar *= Number(p); // set the scalar to the new value
}
});
if (term !== null) { // there may or may not be a term left to push to the stack
pushTerm();
}
// now simplify the equation getting the scalar for left and right sides . x on left y on right
var scalarX = 0;
var scalarY = 0
var valC = 0; // any constants
terms.forEach(t => {
t.scalar *= !t.left ? -1 : 1; // everything on right is negative
if (t.val === "y") {
scalarY += -t.scalar; // reverse sign
} else if (t.val === "x") {
scalarX += t.scalar;
} else {
valC += t.scalar;
}
})
// now build the code string for the equation to solve for x and return y
var code = "return (" + scalarX + " * x + (" + valC + ")) / "+scalarY +";\n";
var equation = new Function("x",code); // create the function
return equation;
}
The following usage examples are all the same equation
var equation = parseEquation("x2+5y+x=230");
var y = equation(10); // get y for x = 10;
equation = parseEquation("x2+x=230-5y");
equation = parseEquation("x2+x-30=200-2y-3y");
equation = parseEquation("200- 2y-3y = x2+x-30");
equation = parseEquation("200-2y- 3y - x2-x+30=0");
equation = parseEquation("100.0 + 100-2y- 3y - x2-x+30=0");
equation = parseEquation("1e2 + 10E1-2y- 3y - x2-x+30=0");
Demo
I have added it to the code in the answer markE has already given. (hope you don't mind markE)
function plot(equation) {
var graph;
var xPadding = 30;
var yPadding = 30;
var data = {
values : [{
X : "1",
Y : 15
}, {
X : "2",
Y : 35
}, {
X : "3",
Y : 60
}, {
X : "4",
Y : 14
}, {
X : "5",
Y : 20
}, {
X : "6",
Y : -30
},
]
};
// Returns the max Y value in our data list
function getMaxY() {
var max = 0;
for (var i = 0; i < data.values.length; i++) {
if (data.values[i].Y > max) {
max = data.values[i].Y;
}
}
max += 10 - max % 10;
return max;
}
var scaleA = 1.4;
// Return the x pixel for a graph point
function getXPixel(val) {
return ((graph.width() / scaleA - xPadding) / data.values.length) * val + (xPadding * 1.5);
}
// Return the y pixel for a graph point
function getYPixel(val) {
return graph.height() / scaleA - (((graph.height() / scaleA - yPadding) / getMaxY()) * val) - yPadding;
}
graph = $('#graph');
var c = graph[0].getContext('2d');
c.clearRect(0,0,graph[0].width,graph[0].height);
c.lineWidth = 2;
c.strokeStyle = '#333';
c.font = 'italic 8pt sans-serif';
c.textAlign = "center";
// Draw the axises
c.beginPath();
c.moveTo(xPadding, 0);
c.lineTo(xPadding, graph.height() / scaleA - yPadding);
c.lineTo(graph.width(), graph.height() / scaleA - yPadding);
c.stroke();
// Draw the X value texts
for (var i = 0; i < data.values.length; i++) {
c.fillText(data.values[i].X, getXPixel(i), graph.height() / scaleA - yPadding + 20);
}
// Draw the Y value texts
c.textAlign = "right"
c.textBaseline = "middle";
for (var i = 0; i < getMaxY(); i += 10) {
c.fillText(i, xPadding - 10, getYPixel(i));
}
c.strokeStyle = '#f00';
// Draw the line graph
c.beginPath();
c.moveTo(getXPixel(0), getYPixel(equation(0)));
for (var i = 1; i < data.values.length; i++) {
c.lineTo(getXPixel(i), getYPixel(equation(i)));
}
c.stroke();
// Draw the dots
c.fillStyle = '#333';
for (var i = 0; i < data.values.length; i++) {
c.beginPath();
c.arc(getXPixel(i), getYPixel(equation(i)), 4, 0, Math.PI * 2, true);
c.fill();
}
}
var codeText = "";
function parseEquation(input){
// Important that white spaces are removed first
input = input.replace(/\s+/g,""); // remove whitespaces
input = input.replace(/([\-\+])([xy])/g,"$11$2"); // convert -x -y or +x +y to -1x -1y or +1x +1y
// just to make the logic below a little simpler
var newTerm = () => {term = { val : null, scalar : 1, left : left, };} // create a new term
var pushTerm = () => {terms.push(term); term = null;} // push term and null current
// regExp [xy=] gets "x","y", or "="" or [\-\+]??[0-9\.]+ gets +- number with decimal
var reg =/[xy=]|[\-\+]??[0-9\.eE]+/g; // regExp to split the input string into parts
var parts = input.match(reg); // get all the parts of the equation
var terms = []; // an array of all terms parsed
var term = null; // Numbers as constants and variables with scalars are terms
var left = true; // which side of equation a term is
parts.forEach(p=>{
if (p === "x" || p === "y") {
if (term !== null && term.val !== null) { // is the variable defined
pushTerm(); // yes so push to the stack and null
}
if (term === null) { newTerm(); } // do we need a new term?
term.val = p;
} else if( p === "="){ // is it the equals sign
if (!left) { throw new SyntaxError("Unxpected `=` in equation."); }
if (term === null) { throw new SyntaxError("No left hand side of equation."); }// make sure that there is a left side
terms.push(term); // push the last left side term onto the stack
term = null;
left = false; // everything on the right from here on in
} else { // all that is left are numbers (we hope)
if (isNaN(p)){ throw new SyntaxError("Unknown value '"+p+"' in equation"); }//check that there is a number
if (term !== null && (p[0] === "+" || p[0] === "-")){ // check if number is a new term
pushTerm(); // yes so push to the stack and null
}
if(term === null){ newTerm(); } // do we need a new term?
term.scalar *= Number(p); // set the scalar to the new value
}
});
if(term !== null){// there may or may not be a term left to push to the stack
pushTerm();
}
// now simplify the equation getting the scalar for left and right sides . x on left y on right
var scalarX = 0;
var scalarY = 0
var valC = 0; // any constants
terms.forEach(t => {
t.scalar *= !t.left ? -1 : 1; // everything on right is negative
if (t.val === "y") {
scalarY += -t.scalar; // reverse sign
} else if (t.val === "x") {
scalarX += t.scalar;
} else {
valC += t.scalar;
}
})
// now build the code string for the equation to solve for x and return y
var code = "return (" + scalarX + " * x + (" + valC + ")) / "+scalarY +";\n";
codeText = code;
var equation = new Function("x",code); // create the function
return equation;
}
function parseAndPlot(){
var input = eqInput.value;
try{
var equation = parseEquation(input);
plot(equation);
error.textContent ="Plot of "+input+ " as 'function(x){ "+codeText+"}'";
}catch(e){
error.textContent = "Error parsing equation. " + e.message;
}
}
var button = document.getElementById("plot");
var eqInput = document.getElementById("equation-text");
var error = document.getElementById("status");
button.addEventListener("click",parseAndPlot);
parseAndPlot();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="graph" width="200" height="150"></canvas> <br>
Enter a linear equation : <input id="equation-text" value="x2 + 5y = 250" type="text"></input><input id="plot" value="plot" type=button></input><div id="status"></div>
I think I understand what you're asking...
Your existing code automatically puts your y-axis at the bottom of the canvas so negative y-values will be off-canvas.
Quick solution
The quickest solution is to divide graph.height()/2 so that your graph has it's y-axis near center-canvas. This leaves room for negative values.
Better solution
The better solution is to redesign your graphing system to allow for solutions in all axis directions.
Refactored code showing the quick solution:
I leave it to you to extend the y-axis labels in the negative direction (if desired)
var graph;
var xPadding = 30;
var yPadding = 30;
var data = { values:[
{ X: "1", Y: 15 },
{ X: "2", Y: 35 },
{ X: "3", Y: 60 },
{ X: "4", Y: 14 },
{ X: "5", Y: 20 },
{ X: "6", Y: -30 },
]};
// Returns the max Y value in our data list
function getMaxY() {
var max = 0;
for(var i = 0; i < data.values.length; i ++) {
if(data.values[i].Y > max) {
max = data.values[i].Y;
}
}
max += 10 - max % 10;
return max;
}
// Return the x pixel for a graph point
function getXPixel(val) {
return ((graph.width()/2 - xPadding) / data.values.length) * val + (xPadding * 1.5);
}
// Return the y pixel for a graph point
function getYPixel(val) {
return graph.height()/2 - (((graph.height()/2 - yPadding) / getMaxY()) * val) - yPadding;
}
graph = $('#graph');
var c = graph[0].getContext('2d');
c.lineWidth = 2;
c.strokeStyle = '#333';
c.font = 'italic 8pt sans-serif';
c.textAlign = "center";
// Draw the axises
c.beginPath();
c.moveTo(xPadding, 0);
c.lineTo(xPadding, graph.height()/2 - yPadding);
c.lineTo(graph.width(), graph.height()/2 - yPadding);
c.stroke();
// Draw the X value texts
for(var i = 0; i < data.values.length; i ++) {
c.fillText(data.values[i].X, getXPixel(i), graph.height()/2 - yPadding + 20);
}
// Draw the Y value texts
c.textAlign = "right"
c.textBaseline = "middle";
for(var i = 0; i < getMaxY(); i += 10) {
c.fillText(i, xPadding - 10, getYPixel(i));
}
c.strokeStyle = '#f00';
// Draw the line graph
c.beginPath();
c.moveTo(getXPixel(0), getYPixel(data.values[0].Y));
for(var i = 1; i < data.values.length; i ++) {
c.lineTo(getXPixel(i), getYPixel(data.values[i].Y));
}
c.stroke();
// Draw the dots
c.fillStyle = '#333';
for(var i = 0; i < data.values.length; i ++) {
c.beginPath();
c.arc(getXPixel(i), getYPixel(data.values[i].Y), 4, 0, Math.PI * 2, true);
c.fill();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="graph" width="200" height="300"></canvas>
I'm splitting a element into multiple blocks (defined by a number of rows and columns), and then fade these blocks to create animation effects. The type of animation is decided by the delay() value:
$('.block').each(function (i) {
$(this).stop().delay(30 * i).animate({
'opacity': 1
}, {
duration: 420
});
});
In this case each block's fade effect is delayed by (30 * current block index). The first block gets 0 delay, the second block 30 delay, ..... the last block 30 * (number of blocks) delay. So this will fade all blocks horizontally.
I've posted a list of effects I've come up so far here: http://jsfiddle.net/MRPDw/.
What I need help with is to find the delay expression for a spiral type effect, and maybe others that you think are possible :D
Here is an example of code for a spiral pattern:
case 'spiral':
$('.block', grid).css({
'opacity': 0
});
var order = new Array();
var rows2 = rows/2, x, y, z, n=0;
for (z = 0; z < rows2; z++){
y = z;
for (x = z; x < cols - z - 1; x++) {
order[n++] = y * cols + x;
}
x = cols - z - 1;
for (y = z; y < rows - z - 1; y++) {
order[n++] = y * cols + x;
}
y = rows - z - 1;
for (x = cols - z - 1; x > z; x--) {
order[n++] = y * cols + x;
}
x = z;
for (y = rows - z - 1; y > z; y--) {
order[n++] = y * cols + x;
}
}
for (var m = 0; m < n; m++) {
$('.block-' + order[m], grid).stop().delay(100*m).animate({
opacity: 1
}, {
duration: 420,
complete: (m != n - 1) ||
function () {
alert('done');
}
});
}
break;
See it working in this fiddle.
I also improved on your "RANDOM" animation, to show all the squares, not just a subset. The code for that is:
case 'random':
var order = new Array();
var numbers = new Array();
var x, y, n=0, m=0, ncells = rows*cols;
for (y = 0; y < rows; y++){
for (x = 0; x < cols; x++){
numbers[n] = n++;
}
}
while(m < ncells){
n = Math.floor(Math.random()*ncells);
if (numbers[n] != -1){
order[m++] = n;
numbers[n] = -1;
}
}
$('.block', grid).css({
'opacity': 0
});
for (var m = 0; m < ncells; m++) {
$('.block-' + order[m], grid).stop().delay(100*m).animate({
opacity: 1
}, {
duration: 420,
complete: (m != ncells - 1) ||
function () {
alert('done');
}
});
}
break;
See it working in this fiddle.
Maybe the easiest way to think about making a spiral animation, is to think about your matrix as a piece of paper.
If you fold 2 times that paper in the x and y center axes, you end up getting a smaller square (or rectangle) quadrant.
Now, if you animate this quadrant only from bottom right to top left corner (in the same way you did for your 'diagonal-reverse'), you can propagate this movement to the other 3 quadrants in order to get the final effect of having an animation running from the center of your matrix up to the four corners.
case 'spiral':
$('.block', grid).css({
'opacity': 0
});
n = 0;
var center = {
x: cols / 2,
y: rows / 2
};
// iterate on the second quadrant only
for (var y = 0; y < center.y; y++)
for (var x = 0; x < center.x; x++) {
// and apply the animation to all quadrants, by using the multiple jQuery selector
$('.block-' + (y * rows + x) + ', ' + // 2nd quadrant
'.block-' + (y * rows + cols - x - 1) + ', ' + // 1st quadrant
'.block-' + ((rows - y - 1) * rows + x) + ', ' + // 3rd quadrant
'.block-' + ((rows - y - 1) * rows + cols - x - 1) // 4th quadrant
, grid).stop().delay(100 * (center.y - y + center.x - x)).animate({
opacity: 1
}, {
duration: 420,
complete: function () {
if (++n == rows * cols) {
alert('done'); // fire next animation...
}
}
});
}
Here is the demo (click the spiral link)