javascript - 如何使用js绘制基于方程的图形

标签 javascript html canvas algebra

我需要在 Canvas 上绘制图表。但是我如何使用代数方程作为输入,并基于方程,使用 javascript 绘制曲线?

例如:

x2+5y=250

方程绘制了一个包含正值和负值的图形。

<!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>
[我在 math.js 中添加了一个示例绘图器] 我想知道如何全屏绘制图形,鼠标在图形中的任意点单击以显示 x 和 y 值中的详细信息。所以如何更改请帮助我。

最佳答案

解析线性方程。

或者问题可能是方程式的解析。

这个答案展示了如何解析一个简单的线性方程。

用户输入 x2+5y=230,您需要为 f(x) 求解和绘制 y,这就是函数函数(x){ 返回(3 * x -230)/-5;

将假设方程始终采用与 x 和 y 以及一些标量和常数相同的形式 scalar * x + const + scalar * y = const

定义规则

规则

  • 只有 x 和 y 会被视为变量。
  • 项是标量和变量 2x 或常量 +1
  • 将忽略所有其他字符,包括 */%
  • 数字可以有小数位。有效数字 1 +1 0.2 -2 10e5
  • 标量必须与变量相邻 3y2 变为 6y 3y-2 保持原样。

解析

要解析方程式,我们必须将其分解为明确且易于操作的单位。在这种情况下,我称之为术语的单元将具有 3 个属性。

  • 标量一个数字
  • variable 变量 x,y 的名称或常量为 null
  • side 项在等式的左边或右边

方程式示例

2x + 2 + 3y = 4x - 1y

首先解析创建 条款

// 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;

一旦所有的项都被解析,方程式就通过对 x、y 和常数的所有项求和并将所有项向左移动并翻转右侧任何值的符号来求解。

sumX = 2 + -4; //as 4x is on the right it becomes negative 
sumY = 3 + 1;
const = 2;

做等式

 -2x + 4y + 2 = 0

然后将 y 向右移出并用其标量除以左边。

 -2x + 2 = 4y
 (-2x + 2)/-4 = y

结果是一个我们可以从 javascript 调用的函数,它会计算 x 的值并获取 y 的值。

 function(x){ return (-2 * x + 2) / 4; }

解析器

以下函数解析并返回 x 的输入方程式的函数。然后该函数用于在下面的演示中绘制点。

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;
}

下面的用法例子都是同一个方程

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");

演示

我已将它添加到 ma​​rkE 已经给出的答案中的代码中。 (希望你不介意 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>

关于javascript - 如何使用js绘制基于方程的图形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39052890/

相关文章:

javascript - 我怎样才能播放像 9gag 这样的 gif?

php - 网址后面的用户名

javascript - HTML5 Canvas 绘图像素与提供的颜色不同

javascript - Chrome 上的 HTML5 canvas 和 php 无法正常工作

javascript - 如何在上传图像之前使用 JavaScript 检查图像尺寸和图像格式?

javascript - 在不发送表单的情况下使用jquery获取受tinymce编辑器控制的textarea的值

html - CSS 选择类型的第一个元素

javascript - 您能告诉我为什么这个网络抓取工具无法正确登录吗?

javascript - 在 JavaScript 中提升对象

javascript - Fabric.js - 多用户 Canvas