简体   繁体   English

如何使用JS根据方程式绘制图形

[英]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> 
[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. [我在math.js中添加了一个示例绘图仪]我想如何全屏绘制图形,并且鼠标在图形中的任意位置都可以显示x&y value的详细信息。所以如何更改请帮助我。

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; } 用户输入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 规则

  • Only x and y will be considered variables. 仅x和y将被视为变量。
  • A term is a scalar and a variable 2x or a constant +1 . 项是标量和变量2x或常数+1
  • All additional characters will be ignored including * , / , % 所有其他字符都将被忽略,包括*/%
  • Numbers can have decimal places. 数字可以有小数位。 Valid numbers 1 +1 0.2 -2 10e5 有效数字1 +1 0.2 -2 10e5
  • Scalars must be adjacent to variables 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个属性。

  • scalar A number 标量 A数
  • variable the name of the variable x,y or null for constants 变量 x,y的名称或常量的null
  • 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. 解析完所有项后,即可通过对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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM