我们现在使用Go语言来绘制上图,即函数sin(r)/r所形成的画面。
代码如下:
// Surface computes an SVG redenring of a 3-D surface function.
package main
import (
"fmt"
"math"
)
const (
width, height = 600, 320 // 帆布大小,单位是像素
cells = 100 // 方格数量
xyrange = 30.0 // 座标轴范围,从-xyrange到xyrange
xyscale = width / 2 / xyrange // 像素数每xy座标轴单位长度
zscale = height * 0.4 // 像素数每z轴单位长度
angle = math.Pi / 6 // 视线与水平面夹角
)
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func main() {
// svg文件的风格信息
fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
"style='stroke: grey; fill: white; stroke-width: 0.7' "+
"width='%d' height='%d'>", width, height)
for i := 0; i < cells; i++ {
for j := 0; j < cells; j++ {
ax, ay, ac := corner(i+1, j)
bx, by, _ := corner(i, j)
cx, cy, _ := corner(i, j+1)
dx, dy, _ := corner(i+1, j+1)
expression := math.IsInf(ax, 0) || math.IsInf(ay, 0)
expression = expression || math.IsInf(bx, 0) || math.IsInf(by, 0)
expression = expression || math.IsInf(cx, 0) || math.IsInf(cy, 0)
expression = expression || math.IsInf(dx, 0) || math.IsInf(dy, 0)
// 如果高度为无穷,则放弃该格子
if expression {
continue
}
// 绘制其中一个格子,points标志格子四个角座标,style=fill表示填充颜色
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g' "+
"style='fill: #%x'/>\n",
ax, ay, bx, by, cx, cy, dx, dy, ac)
}
}
fmt.Println("</svg>") // svg格式与HTML类似
}
func corner(i, j int) (float64, float64, int) {
// Find point (x,y) at corner of cell (i,j).
x := xyrange * (float64(i) / cells - 0.5)
y := xyrange * (float64(j) / cells - 0.5)
// Compute surface height z
z := f(x, y)
// 最低点的颜色接近纯蓝色(#0000ff)
// 最高点的颜色接近纯红色(#ff0000)
color := int((0xff0000 - 0x0000ff) * (z + 0.3) / 1.3 + 0x0000ff)
//Project (x, y, z) isometrically onto 2-D SVG canvas (sx, sy)
sx := width/2 + (x-y)*cos30*xyscale
sy := height/2 + (x+y)*sin30*xyscale - z*zscale
return sx, sy, color
}
func f(x, y float64) float64 {
r := math.Hypot(x, y)
return math.Sin(r) / r
}
在命令行输入:
go run Go文件名.go > svg文件名.svg
在浏览器输入地址
svg文件路径/svg文件名.svg
即可得到上图。
除了把svg信息写到文件中外,我们还可以设置一个服务器,用浏览器访问该服务器就可以观看此图。代码如下。
// Surface computes an SVG redenring of a 3-D surface function.
package main
import (
"fmt"
"math"
"log"
"net/http"
)
const (
width, height = 1200, 640 // 帆布大小,单位是像素
cells = 100 // 方格数量
xyrange = 30.0 // 座标轴范围,从-xyrange到xyrange
xyscale = width / 2 / xyrange // 像素数每xy座标轴单位长度
zscale = height * 0.4 // 像素数每z轴单位长度
angle = math.Pi / 6 // 视线与水平面夹角
)
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func main() {
http.HandleFunc("/", handler) // 每收到一个请求,就调用handler
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
// 设置响应文件头信息,这是必须的。否则浏览器无法成功解析
w.Header().Set("Content-Type", "image/svg+xml")
// svg文件的风格信息
fmt.Fprintf(w, "<svg xmlns='http://www.w3.org/2000/svg' "+
"style='stroke: grey; fill: white; stroke-width: 0.7' "+
"width='%d' height='%d'>", width, height)
for i := 0; i < cells; i++ {
for j := 0; j < cells; j++ {
ax, ay, ac := corner(i+1, j)
bx, by, _ := corner(i, j)
cx, cy, _ := corner(i, j+1)
dx, dy, _ := corner(i+1, j+1)
expression := math.IsInf(ax, 0) || math.IsInf(ay, 0)
expression = expression || math.IsInf(bx, 0) || math.IsInf(by, 0)
expression = expression || math.IsInf(cx, 0) || math.IsInf(cy, 0)
expression = expression || math.IsInf(dx, 0) || math.IsInf(dy, 0)
// 如果高度为无穷,则放弃该格子
if expression {
continue
}
// 绘制其中一个格子,points标志格子四个角座标,style=fill表示填充颜色
fmt.Fprintf(w, "<polygon points='%g,%g %g,%g %g,%g %g,%g' "+
"style='fill: #%x'/>\n",
ax, ay, bx, by, cx, cy, dx, dy, ac)
}
}
fmt.Fprintf(w, "</svg>\n") // svg格式与HTML类似
}
func corner(i, j int) (float64, float64, int) {
// Find point (x,y) at corner of cell (i,j).
x := xyrange * (float64(i) / cells - 0.5)
y := xyrange * (float64(j) / cells - 0.5)
// Compute surface height z
z := f(x, y)
// 最低点的颜色接近纯蓝色(#0000ff)
// 最高点的颜色接近纯红色(#ff0000)
color := int((0xff0000 - 0x0000ff) * (z + 0.3) / 1.3 + 0x0000ff)
//Project (x, y, z) isometrically onto 2-D SVG canvas (sx, sy)
sx := width/2 + (x-y)*cos30*xyscale
sy := height/2 + (x+y)*sin30*xyscale - z*zscale
return sx, sy, color
}
func f(x, y float64) float64 {
r := math.Hypot(x, y)
return math.Sin(r) / r
}
在命令行中运行该文件
go run Go文件名.go
在浏览器中访问地址
localhost:8000
即可😊😋😁
上面代码实际上是《Go程序设计语言》练习3.1、3.3、3.4的解答。