簡體   English   中英

在 html5-canvas 上繪制函數圖的正確方法

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

出於自我教育的目的,我試圖在 html5-canvas 上繪制數學函數。

我知道有些圖書館可以做到這一點,但正如我所說:教育目的。


所以問題是:如何繪制函數圖,例如: f(x) = 1/x 或 f(x) = tan(x)

我嘗試這樣做的方式:

  • 循環從(例如 x = -100 到 x == 100)
  • 計算 x 處的值
  • 從 (x, valueAtX) 到 (x+1, valueAtX_plus1) 畫一條線

結果: 在此處輸入圖片說明

我想要的是: 在此處輸入圖片說明


如果有人可以:給我一些偽代碼或步驟如何檢測函數在給定點是否連續,然后正確繪制它,我將不勝感激!

PS:在問這個問題之前我已經搜索了很長時間的答案,但我找不到任何答案,如果這個問題是重復的那么我真的很抱歉,請把我鏈接到原始問題。

處理垂直漸近線。

您需要在邊界處裁剪函數。 當您逐步執行離散步驟時,您將跳過 x 值,其中 y 在頂部和底部被剪裁。

您可以求解 f(x) = graphTop 和 f(x) = graphBottom 或為每個垂直漸近線找到 x,但對於更復雜的函數,這可能變得非常困難。

進行近似的最簡單方法,除非您使用帶有標尺的圖形並從中獲取數據,否則近似與真實情況一樣好。

要做的只是減少循環步驟。 你有 -100 到 100,我猜你是步進 1 個單位。 而不是第 1 步單位步長的 1/10 或更小。 不要繪制每個步驟,只需繪制每個單元。 但是在子步驟中檢查 y 值是否在 y 范圍之外。

從一個子步驟到下一個子步驟,您有多種可能性。

  1. Y從內到外的情節
  2. y 從外到內的情節
  3. y 從外到外跨地塊
  4. y 從內移到內

對於 1,2 我們可以在發生這種情況時開始一條新路徑,對於 4 只是正常繪制。

最大的問題是 3,除了消除這種情況發生的可能性之外,真的沒有解決方案。 要做到這一點,您可以減少子步驟,但您永遠無法將它們做得足夠小。 考慮 f(x) = tan(x ^ 2) 來捕獲所有情況 3 需要一個 subStep 以對數方式變小,當您的 CPU 資源有限時,這不是一件好事。 所以我們只能做一個最合適的人。

如果漸近線左側和右側的 y 剪輯在 x 軸(縮放像素)上相距2 / subStepCount2 / 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>

您需要考慮radiansdegrees之間的差異。 轉換比較簡單:

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

這是一個小提琴 您可以通過更改xAxisPI變量以弧度為單位設置x-Axis的起點/終點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM