admin管理员组

文章数量:1426957

For self-educational purposes I'm trying to graph math functions on html5-canvas.

I know that there are libraries that can do exactly that, but as I said: educational purpose.


So the question is: how do I draw a graph of a function for example : f(x) = 1/x or f(x) = tan(x)

The way I tried to do it:

  • loop from (for ex. x = -100 to x == 100)
  • calculate value at x
  • draw a line from (x, valueAtX) to (x+1, valueAtX_plus1)

Result:

What I want:


If someone could: give me some pseudocode or steps how to detect if function is continuous at a given point, and then correctly draw it I would appreciate it a lot!

P.S: I've searched for answer for a long time before asking this question, but I couldn't find any, if this question is a duplicate then I'm really sorry, and please link me to the original.

For self-educational purposes I'm trying to graph math functions on html5-canvas.

I know that there are libraries that can do exactly that, but as I said: educational purpose.


So the question is: how do I draw a graph of a function for example : f(x) = 1/x or f(x) = tan(x)

The way I tried to do it:

  • loop from (for ex. x = -100 to x == 100)
  • calculate value at x
  • draw a line from (x, valueAtX) to (x+1, valueAtX_plus1)

Result:

What I want:


If someone could: give me some pseudocode or steps how to detect if function is continuous at a given point, and then correctly draw it I would appreciate it a lot!

P.S: I've searched for answer for a long time before asking this question, but I couldn't find any, if this question is a duplicate then I'm really sorry, and please link me to the original.

Share Improve this question asked Apr 13, 2017 at 9:39 Tomasz RadwaskiTomasz Radwaski 1922 silver badges10 bronze badges 6
  • This is cool - a very well asked question so +1 for that. To make life even easier, could you perhaps provide a fiddle of what you're doing so that we can interact with your code? – Frits Commented Apr 13, 2017 at 9:41
  • My code is a plete spaghetti + I'm relatively new to the site, I don't know how to add fiddles and such. But I'll try explain everything I can – Tomasz Radwaski Commented Apr 13, 2017 at 9:49
  • I did put it on free hosting, so you can check it out here: ensigntrojan.cba.pl/wykresTest.html – Tomasz Radwaski Commented Apr 13, 2017 at 9:58
  • 1 It should normally not happen that for a "sensible" function the value difference is larger than 100 times the x difference. If you detect such a jump, leave out the line for that segment. – Lutz Lehmann Commented Apr 13, 2017 at 12:48
  • @LutzL, actually, in this case, that is exactly what should happen. The value of tan(x) jumps from negative infinity to positive infinity around those points. – Ozan Commented Apr 13, 2017 at 13:46
 |  Show 1 more ment

2 Answers 2

Reset to default 4

Dealing with vertical asymptotes.

You need to clip the function at the boundaries. As you are stepping in discrete steps you will skip over the x value where y is clipped at the top and the bottom.

You could solve for f(x) = graphTop and f(x) = graphBottom or find x for each vertical asymptote, but that can bee very difficult for more plex functions.

The easiest way it to do a approximation, unless you are using the graph with a rulers and getting data from it the approximation is as good as the real thing.

To do just reduce the loop step. You had -100 to 100 and I presume you are stepping 1 unit. Rather than step 1 unit step 1/10th or smaller. Don't plot each step, just plot the each unit. But in the sub steps check if the y value is outside the y range.

You have several possibilities from one sub step to the next.

  1. Y moving from inside to outside plot
  2. y moving from outside to inside plot
  3. y moved from outside to outside across plot
  4. y moved from inside to inside

For 1,2 we can begin a new path when that happens, for 4 just plot as normal.

The big problem is 3 and there really is no solution apart from eliminating the chance of that happening. To do that you reduce the sub steps, but you can never make them small enough. Consider f(x) = tan(x ^ 2) to catch all case 3's would need a subStep that gets smaller logarithmically, not a good thing when you have a finite CPU resource. So we are left but to do a best fit.

The following will work if the y clip to the left and right of the asymptotes are around 2/10th (2 / subStepCount see code for details) pixel apart in the x axis (scaled pixel) but if they get closer it will fail. To increase the sub steps set var subStepCount = ?. I added a check to see if the difference between the last two plots is greater than 1/3 of the plots height to catch some of the bad plots.

    
    var ctx = canvas.getContext("2d");
    var h = canvas.height;    
    var w = canvas.width;
    var cw = w / 2; // centers
    var ch = h / 2; 
    var subStepCount = 10;  // number of sub setps
    var scale = 10;         // scale of the plot

  
    function plot(func,col,lineWidth){
        var invScale = 1 / scale;    // inverted scale is the size of a pixel
        var top = ch * invScale;     // get top and bottom
        var bottom = -ch * invScale;
        var subStep = invScale / subStepCount; // get the sub steps between pixels
        var x,y,yy,xx,subX;                    // xx,yy are the coords of prev point
        var start = (-cw - 1) * invScale;      // get the start and end
        var end = (cw + 1) * invScale;
        // set render styles
        ctx.strokeStyle = col;
        ctx.lineWidth = lineWidth * invScale; // scale line to be constant size

        ctx.beginPath();
        for(x = start; x < end; x += invScale){ // pixel steps
            for(subX = 0; subX < invScale; subX += subStep){  // sub steps
                y = func(x+subX);                    // get y for x 
                if(yy !== undefined){                // is this not the first point
                    if(y > top || y < bottom){       // this y outside ?
                        if(yy < top && yy > bottom){ // last yy inside?
                            ctx.lineTo(x + subX,y);
                        }
                    } else {                         // this y must be inside
                        if(yy > top || yy < bottom){ // was last yy outside
                            ctx.moveTo(xx,yy);       // start a new path
                        }
                        if(subX === 0){              // are we at a pixel 
                            if(y > bottom && y < top){  // are we inside
                                // if the step is large then might be a line break
                                if(Math.abs(yy-y) > (top - bottom) * (1/3)){ 
                                    ctx.moveTo(x,y);  
                                }else{
                                    ctx.lineTo(x,y);
                                }
                            }
                        }
                    }
                }else{
                    if(subX === 0){
                        ctx.moveTo(x,y);
                    }
                }
                yy  = y;
                xx = x+ subX;
            }
        }
        ctx.stroke();

        
    }
        
    // set the plot scale and orientation 
    ctx.setTransform(scale,0,0,-scale,cw, ch);
    // two example function plots
    plot((x)=>Math.tan(Math.cos(x/2) * 10),"#F88",1)
    plot((x)=>Math.tan(x),"blue",2)
canvas {
   border : 1px solid black;
 }
<canvas id=canvas width = 512 height = 256></canvas>

You need to consider the difference between radians and degrees. Conversion is relatively simple:

//instead of y = Math.tan(x) , use :
y = Math.tan(x * Math.PI / 180);

Here is a fiddle. You can set the starting/ending point of the x-Axis in radians by changing the xAxisPI variable.

本文标签: javascriptThe correct way to graph of a function on html5canvasStack Overflow