简体   繁体   English

在 html5-canvas 上绘制函数图的正确方法

[英]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.出于自我教育的目的,我试图在 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)所以问题是:如何绘制函数图,例如: f(x) = 1/x 或 f(x) = tan(x)

The way I tried to do it:我尝试这样做的方式:

  • loop from (for ex. x = -100 to x == 100)循环从(例如 x = -100 到 x == 100)
  • calculate value at x计算 x 处的值
  • draw a line from (x, valueAtX) to (x+1, valueAtX_plus1)从 (x, valueAtX) 到 (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. PS:在问这个问题之前我已经搜索了很长时间的答案,但我找不到任何答案,如果这个问题是重复的那么我真的很抱歉,请把我链接到原始问题。

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.当您逐步执行离散步骤时,您将跳过 x 值,其中 y 在顶部和底部被剪裁。

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.您可以求解 f(x) = graphTop 和 f(x) = graphBottom 或为每个垂直渐近线找到 x,但对于更复杂的函数,这可能变得非常困难。

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.你有 -100 到 100,我猜你是步进 1 个单位。 Rather than step 1 unit step 1/10th or smaller.而不是第 1 步单位步长的 1/10 或更小。 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.但是在子步骤中检查 y 值是否在 y 范围之外。

You have several possibilities from one sub step to the next.从一个子步骤到下一个子步骤,您有多种可能性。

  1. Y moving from inside to outside plot Y从内到外的情节
  2. y moving from outside to inside plot y 从外到内的情节
  3. y moved from outside to outside across plot y 从外到外跨地块
  4. y moved from inside to inside y 从内移到内

For 1,2 we can begin a new path when that happens, for 4 just plot as normal.对于 1,2 我们可以在发生这种情况时开始一条新路径,对于 4 只是正常绘制。

The big problem is 3 and there really is no solution apart from eliminating the chance of that happening.最大的问题是 3,除了消除这种情况发生的可能性之外,真的没有解决方案。 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.考虑 f(x) = tan(x ^ 2) 来捕获所有情况 3 需要一个 subStep 以对数方式变小,当您的 CPU 资源有限时,这不是一件好事。 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.如果渐近线左侧和右侧的 y 剪辑在 x 轴(缩放像素)上相距2 / subStepCount2 / subStepCount请参阅代码以了解详细信息)像素,则以下将起作用,但如果它们靠得更近,它将失败。 To increase the sub steps set var subStepCount = ?要增加子步骤设置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.我添加了一个检查,看看最后两个图之间的差异是否大于图高度的 1/3,以捕捉一些不好的图。

 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 .您需要考虑radiansdegrees之间的差异。 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.您可以通过更改xAxisPI变量以弧度为单位设置x-Axis的起点/终点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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