函式圖形繪製


工程相關領域的行業,通常少不了面對函式繪圖,現在有許多的軟體都可以使用更方便的功能來繪製複雜的函式圖形,如著名的Matlab。

在這邊介紹三維立體空間的函式繪製,將函式分為兩類:「顯函式」與「隱函式」。

顯函式

如果可以將函式表示為y = f(x, z) 的形式,這種函式稱之為顯函式,由於顯函式的(x,z)只會產生一個結果,所以顯函式所表示的圖形若使用一條與y軸平行的直線來與圖形相交,最多只有一個交點。

繪製顯函式的方法就是分別對x與z進行迴圈計數,並帶入原方程式求出y值,然後在螢幕上繪出(x, y)的對應圖(z = f(x, y)或x = f(y, z)只是換個角度繪圖,方法一樣)。

隱函式

如果函式是f(x, y, z) = 0的形式,稱之為隱函式,雖然y - f(x, z) = 0也可以算是屬於此種形式,不過這邊所說的隱函式是指那些無法化為y = f(x, z)形式的函式;當畫上一條與y軸平行的直線時,可能會發生幾個情況,例如以球面函式x^2 + y^2 + z^2 - 1 = 0來說,可能沒有交點、一個交點或是兩個交點,如下圖所示:


由於一對(x, z)值可能會有兩個交點,所以我們必須將隱函式設法改變為以下的形式才能進行繪圖:
x = x(s, t)
y = y(s, t)
z = z(s, t)

以上的形式稱之為參數式,之後我們就可以利用(s, t)進行迴圈計數並計算出(x, y, z)的值並進行繪圖,當然(x, y, z)的參數式代回原來的函式必須滿足原來的函式,以球面函式x2 + y2 + z2 - 1 = 0為例,較好的參數表示式當然就是極座標表示法。

大部份的隱函式都可以使用參數式來進行繪圖,不過不同的函式會需要不同的參數式,以下示範顯函式的繪圖:
  • Demo.java
package cc.openhome;

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JApplet;

import static java.lang.Math.*;

public class Demo extends JApplet {
private int orgX;
private int orgY;

public void init() {
super.init();
setBackground(Color.black);
setSize(640, 480);
orgX = getWidth() / 2;
orgY = getHeight() / 2;
}

public void paint(Graphics g) {
g.setColor(Color.yellow);

// 從斜角繪製
// 繞 x 軸轉 30 度,繞 y 軸轉 -30 度
double rotateX = toRadians(30);
double rotateY = toRadians(-30);

double sinRotateX = sin(rotateX);
double cosRotateX = cos(rotateX);
double sinRotateY = sin(rotateY);
double cosRotateY = cos(rotateY);

for(int z = 200; z >= -200; z-=10) {
for(int x = -200; x <= 200; x++) {
double y = 30*(cos(toRadians(sqrt(x * x + z * z)))
+ cos(toRadians(3 * sqrt(x * x + z * z))));

// 立體旋轉,從斜角繪製,調整繪圖中心至視窗中心
int pointX = (int) (orgX + x * cosRotateY + z * sinRotateY);
int pointY = (int) (orgY - (y * cosRotateX -
(-x * sinRotateY + z * cosRotateY) * sinRotateX));
g.drawLine(pointX, pointY, pointX, pointY);
}
}
}
}

以下是使用HTML5 Canvas的方式(如果瀏覽器支援HTML5 Canvas,例如最新版的Firexfox、Chrome、IE9等,可以直接將下面的內容存為HTML或按下檔名連結,直接載入瀏覽器執行觀看結果:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=Big5" http-equiv="content-type">
<script type="text/javascript">
window.onload = function() {
function toRadians(angle) {
return angle * Math.PI / 180;
}
var sin = Math.sin;
var cos = Math.cos;
var sqrt = Math.sqrt;

var canvas1 = document.getElementById('canvas1');
var context = canvas1.getContext('2d');

var orgX = canvas1.width / 2;
var orgY = canvas1.height / 2;

var rotateX = toRadians(30);
var rotateY = toRadians(-30);

var sinRotateX = Math.sin(rotateX);
var cosRotateX = Math.cos(rotateX);
var sinRotateY = Math.sin(rotateY);
var cosRotateY = Math.cos(rotateY);

context.beginPath();
for(var z = 200; z >= -200; z-=10) {
for(var x = -200; x <= 200; x++) {
var y = 30*(cos(toRadians(sqrt(x * x + z * z)))
+ cos(toRadians(3 * sqrt(x * x + z * z))));
// 立體旋轉,從斜角繪製,調整繪圖中心至視窗中心
var pointX = parseInt(
orgX + x * cosRotateY + z * sinRotateY);
var pointY = parseInt(orgY - (y * cosRotateX -
(-x * sinRotateY + z * cosRotateY) * sinRotateX));
context.moveTo(pointX, pointY);
context.lineTo(pointX + 1, pointY + 1);
}
}
context.stroke();
context.closePath();
};
</script>
</head>
<body>
<canvas id="canvas1" width="640" height="480"></canvas>
</body>
</html>

在Firefox下的效果如下: