简体   繁体   中英

The correct way to graph of a function on html5-canvas

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!

PS: 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.

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 become very difficult for more complex 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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