Как построить график на основе уравнения с помощью JS

javascript html5 canvas algebra

2419 просмотра

2 ответа

Мне нужно построить график на холсте. Но как я могу использовать уравнение алгебры в качестве входных данных и на основе уравнения нарисовать кривую, используя 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. Как это сделать, пожалуйста, помогите мне.

Автор: Prasanth Klr Источник Размещён: 08.11.2019 11:09

Ответы (2)


4 плюса

Разбор линейного уравнения.

Или, может быть, речь идет об разборе уравнения?

Этот ответ показывает, как разобрать простое линейное уравнение.

Пользователь вводит x2+5y=230и вам нужно решить и построить график yдля f(x)которого будет функцияfunction(x) { return (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 свойства.

  • скалярное число
  • переменная имя переменной x, y или null для констант
  • сторона, с какой стороны уравнения термин слева или справа

Пример уравнения

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

демонстрация

Я добавил это к коду в ответе 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>

Автор: Blindman67 Размещён: 20.08.2016 09:52

2 плюса

Я думаю, я понимаю, что вы спрашиваете ...

Ваш существующий код автоматически помещает вашу ось y внизу холста, так что отрицательные значения y будут вне холста.

Быстрое решение

Самое быстрое решение состоит в том, чтобы разделить graph.height()/2так, чтобы ваш график имел свою ось Y рядом с центром холста. Это оставляет место для отрицательных значений.

Лучшее решение

Лучшее решение - перепроектировать вашу графическую систему, чтобы учесть решения во всех направлениях оси.

Измененный код, показывающий быстрое решение:

Я оставляю вам возможность расширять метки оси 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> 

Автор: markE Размещён: 20.08.2016 04:49
Вопросы из категории :
32x32