[英]How to plot the graph based on equation using js
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? 但是,如何使用代数方程作为输入,并基于该方程使用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>
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; }
用户输入x2+5y=230
,您需要求解f(x)
y
并将其绘制为函数function(x) { return (3 * x -230) / -5; }
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
将假定方程式始终与x和y以及某些标量和常量的形式相同scalar * x + const + scalar * y = const
Define the rules 定义规则
Rules 规则
2x
or a constant +1
. 项是标量和变量2x
或常数+1
。 *
, /
, %
所有其他字符都将被忽略,包括*
, /
, %
1
+1
0.2
-2
10e5
有效数字1
+1
0.2
-2
10e5
3y2
becomes 6y
3y-2
stays as is. 标量必须与变量3y2
相邻才变为6y
3y-2
保持原样。 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. 在这种情况下,我称其为术语的单元将具有3个属性。
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. 解析完所有项后,即可通过对x,y和常数的所有项求和并将所有内容移至左侧,从而翻转右侧任何值的符号来求解方程。
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. 然后将y移至右侧,然后将其除以左侧的标量。
-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. 结果是一个可以从javascript调用的函数,它将获得x的值并获得y的值。
function(x){ return (-2 * x + 2) / 4; }
The Parser 解析器
The following function parses and returns a function for input equation for x. 以下函数解析并返回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. 我已经将其添加到已经给出的答案markE中的代码中。 (hope you don't mind markE) (希望您不介意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. 您现有的代码会自动将y轴放置在画布的底部,因此负y值将偏离画布。
Quick solution 快速解决方案
The quickest solution is to divide graph.height()/2
so that your graph has it's y-axis near center-canvas. 最快的解决方案是将graph.height()/2
除以使您的图的y轴接近中心画布。 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) 我将它留给您,以便将y轴标签沿负方向延伸(如果需要)
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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.