出于自学目的,我尝试在 html5-canvas 上绘制数学函数图。
我知道有些图书馆可以做到这一点,但正如我所说:教育目的。
<小时/>所以问题是:如何绘制函数的图形,例如:f(x) = 1/x 或 f(x) = tan(x)强>
我尝试这样做的方式:
- 从(例如 x = -100 到 x == 100)循环
- 计算 x 处的值
- 从 (x, valueAtX) 到 (x+1, valueAtX_plus1) 画一条线
如果有人可以:给我一些伪代码或步骤如何检测函数在给定点是否连续,然后正确绘制它,我将非常感激!
P.S:在问这个问题之前,我已经搜索了很长时间的答案,但我找不到任何答案,如果这个问题是重复的,那么我真的很抱歉,请链接到原始问题。
最佳答案
处理垂直渐近线。
您需要在边界处裁剪函数。当您逐步执行离散步骤时,您将跳过 x 值,其中 y 在顶部和底部被剪裁。
您可以求解 f(x) = graphTop 和 f(x) = graphBottom 或找到每个垂直渐近线的 x,但这对于更复杂的函数来说可能会变得非常困难。
进行近似的最简单方法,除非您使用带有标尺的图表并从中获取数据,否则近似值与真实值一样好。
只需减少循环步骤即可。你有 -100 到 100,我猜你正在步进 1 个单位。而不是步长 1 单位步长的 1/10 或更小。不要绘制每个步骤,只需绘制每个单元。但在子步骤中检查 y 值是否超出 y 范围。
从一个子步骤到下一个子步骤,您有多种可能性。
- Y 从图内移动到图外
- y 从图外移动到图内
- y 在地 block 上从外部移动到外部
- y 从内移动到内
对于 1,2,我们可以在发生这种情况时开始一条新路径,对于 4,只需正常绘制即可。
最大的问题是 3,除了消除这种情况发生的可能性之外,确实没有其他解决方案。为此,您可以减少子步骤,但永远无法将它们做得足够小。考虑 f(x) = tan(x ^ 2) 来捕获所有情况 3 需要一个对数变小的 subStep,当 CPU 资源有限时,这不是一件好事。所以我们只能做出最好的选择。
如果渐近线左侧和右侧的 y 剪辑在 x 轴上相距大约 2/10(2/subStepCount
请参阅代码)像素(缩放像素),则以下内容将起作用但如果他们靠近的话就会失败。要增加子步骤,请设置 var subStepCount = ?
。我添加了一项检查,看看最后两个图之间的差异是否大于图高度的 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>
关于javascript - 在 html5-canvas 上绘制函数图的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43388716/