如果f函数返回的是无限制的float64值,那么SVG文件可能输出无效的多边形元素(虽然许多SVG渲染器会妥善处理这类问题)。修改程序跳过无效的多边形。
参考1.7节Lissajous例子的函数,构造一个web服务器,用于计算函数曲面然后返回SVG数据给客户端。服务器必须设置Content-Type头部:
package main
import (
"fmt"
"io"
"log"
"math"
"net/http"
)
const (
width, height = 600, 320 // canvas size in pixels
cells = 100 // number of grid cells
xyrange = 30.0 // axis ranges (-xyrange..+xyrange)
xyscale = width / 2 / xyrange // pixels per x or y unit
zscale = height * 0.4 // pixels per z unit
angle = math.Pi / 6 // angle of x, y axes (=30°)
)
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
surface(w)
}
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:1234", nil))
}
func surface(w io.Writer) {
fmt.Fprintf(w, "")
}
func corner(i, j int) (float64, float64) {
// 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)
// 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
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return math.Sin(r) / r
}
试验math包中其他函数的渲染图形。你是否能输出一个egg box、moguls或a saddle图案?
func eggbox(x, y float64) float64 {
r := 0.2 * (math.Cos(x) + math.Cos(y))
return r
}
func saddle(x, y float64) float64 {
a := 25.0
b := 17.0
a2 := a * a
b2 := b * b
r := y*y/a2 - x*x/b2
return r
}
根据高度给每个多边形上色,那样峰值部将是红色(#ff0000),谷部将是蓝色(#0000ff)。
package main
import (
"fmt"
"io"
"log"
"math"
"net/http"
)
const (
width, height = 600, 320 //画布大小
cells = 100 //单元格的个数
xyrange = 30.0 //坐标轴的范围(-xyrnage..+xyrange)
xyscale = width / 2 / xyrange //x或y轴上每个单位长度的像素
zscale = height * 0.5 //z轴上每个单位长度的像素
angle = math.Pi / 6 //x、y轴的角度(=30°)
)
func Max(nums ...float64) float64 {
var maxNum float64 = math.NaN()
for _, num := range nums {
if num > maxNum || math.IsNaN(maxNum) {
maxNum = num
}
}
return maxNum
}
func Min(nums ...float64) float64 {
var minNum float64 = math.NaN()
for _, num := range nums {
if num < minNum || math.IsNaN(minNum) {
minNum = num
}
}
return minNum
}
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°),cos(30°)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
surface(w)
}
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:1234", nil))
}
func surface(w io.Writer) {
z_min, z_max := min_max()
fmt.Fprintf(w, "")
}
// minmax返回给定x和y的最小值/最大值并假设为方域的z的最小值和最大值。
func min_max() (min, max float64) {
min = math.NaN()
max = math.NaN()
for i := 0; i < cells; i++ {
for j := 0; j < cells; j++ {
for xoff := 0; xoff <= 1; xoff++ {
for yoff := 0; yoff <= 1; yoff++ {
x := xyrange * (float64(i+xoff)/cells - 0.5)
y := xyrange * (float64(j+yoff)/cells - 0.5)
z := f(x, y)
if math.IsNaN(min) || z < min {
min = z
}
if math.IsNaN(max) || z > max {
max = z
}
}
}
}
}
return min, max
}
func color(min, max, zmin, zmax float64) string {
color := ""
if math.Abs(max) > math.Abs(min) {
red := math.Exp(math.Abs(max)) / math.Exp(math.Abs(zmax)) * 255
if red > 255 {
red = 255
}
color = fmt.Sprintf("#%02x0000", int(red))
} else {
blue := math.Exp(math.Abs(min)) / math.Exp(math.Abs(zmin)) * 255
if blue > 255 {
blue = 255
}
color = fmt.Sprintf("#0000%02x", int(blue))
}
return color
}
func corner(i, j int) (float64, float64, float64) {
//求出网格单元(i,j)的顶点坐标(x,y)
x := xyrange * (float64(i)/cells - 0.5)
y := xyrange * (float64(j)/cells - 0.5)
//计算曲面高度z
z := f(x, y)
//将(x, y, z)等角投射到二维SVG绘图平面上,坐标是(sx, sy)
sx := width/2 + (x-y)*cos30*xyscale
sy := height/2 + (x+y)*sin30*xyscale - z*zscale
return z, sx, sy
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) //到(0,0)的距离
return math.Sin(r) / r
}
实现一个彩色的Mandelbrot图像,使用image.NewRGBA创建图像,使用color.RGBA或color.YCbCr生成颜色。
参考博客
package main
import (
"image"
"image/color"
"image/png"
"log"
"math/cmplx"
"os"
)
func main() {
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
)
img := image.NewRGBA(image.Rect(0, 0, width, height)) //生成了一个画布
for py := 0; py < height; py++ {
y := float64(py)/height*(ymax-ymin) + ymin
for px := 0; px < width; px++ {
x := float64(px)/width*(xmax-xmin) + xmin
z := complex(x, y)
// Image point (px, py) represents complex value z.
img.Set(px, py, mandelbrot(z))
}
}
f, err := os.Create("image.png")
if err != nil {
log.Fatal(err)
}
png.Encode(f, img) // NOTE: ignoring errors
}
func mandelbrot(z complex128) color.Color {
const iterations = 200
var v complex128
for n := uint8(0); n < iterations; n++ {
v = v*v + z
if cmplx.Abs(v) > 2 {
return getColor(n)
}
}
return color.Black
}
func getColor(n uint8) color.Color {
paletted := [16]color.Color{
color.RGBA{66, 30, 15, 255}, // # brown 3
color.RGBA{25, 7, 26, 255}, // # dark violett
color.RGBA{9, 1, 47, 255}, //# darkest blue
color.RGBA{4, 4, 73, 255}, //# blue 5
color.RGBA{0, 7, 100, 255}, //# blue 4
color.RGBA{12, 44, 138, 255}, //# blue 3
color.RGBA{24, 82, 177, 255}, //# blue 2
color.RGBA{57, 125, 209, 255}, //# blue 1
color.RGBA{134, 181, 229, 255}, // # blue 0
color.RGBA{211, 236, 248, 255}, // # lightest blue
color.RGBA{241, 233, 191, 255}, // # lightest yellow
color.RGBA{248, 201, 95, 255}, // # light yellow
color.RGBA{255, 170, 0, 255}, // # dirty yellow
color.RGBA{204, 128, 0, 255}, // # brown 0
color.RGBA{153, 87, 0, 255}, // # brown 1
color.RGBA{106, 52, 3, 255}, // # brown 2
}
return paletted[n%16]
}
升采样技术可以降低每个像素对计算颜色值和平均值的影响。简单的方法是将每个像素分成四个子像素,实现它。
package main
import (
"image"
"image/color"
"image/png"
"log"
"math/cmplx"
"os"
)
func avg(colors []color.Gray) color.Gray {
var gary uint8
n := len(colors)
for _, c := range colors {
gary += c.Y / uint8(n)
}
return color.Gray{gary}
}
func main() {
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
epsX = (xmax - xmin) / width
epsY = (ymax - ymin) / height
)
offX := []float64{-epsX, epsX}
offY := []float64{-epsY, epsY}
img := image.NewRGBA(image.Rect(0, 0, width, height))
for py := 0; py < height; py++ {
y := float64(py)/height*(ymax-ymin) + ymin
for px := 0; px < width; px++ {
x := float64(px)/width*(xmax-xmin) + xmin
subPixels := make([]color.Gray, 0)
for i := 0; i < 2; i++ {
for j := 0; j < 2; j++ {
z := complex(x+offX[i], y+offY[j])
subPixels = append(subPixels, mandelbrot(z))
}
}
img.Set(px, py, avg(subPixels))
}
}
f, err := os.Create("image.png")
if err != nil {
log.Fatal(err)
}
png.Encode(f, img) // NOTE: ignoring errors
}
func mandelbrot(z complex128) color.Gray {
const iterations = 100
const contrast = 15
var v complex128
for n := uint8(0); n < iterations; n++ {
v = v*v + z
if cmplx.Abs(v) > 2 {
return color.Gray{255 - contrast*n}
}
}
return color.Gray{0}
}
编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作。
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
for i := 1; i < len(os.Args); i++ {
fmt.Printf(" %s\n", comma(os.Args[i]))
}
}
// !+
// comma inserts commas in a non-negative decimal integer string.
func comma(s string) string {
buf := bytes.NewBuffer([]byte{})
n := len(s)
if n <= 3 {
return s
}
for i := 0; i < n; i++ {
if (n-i)%3 == 0 && i != 0 {
buf.WriteByte(',')
}
buf.WriteByte(s[i])
}
return buf.String()
}
完善comma函数,以支持浮点数处理和一个可选的正负号的处理。
package main
import (
"bytes"
"fmt"
"os"
"strings"
)
func main() {
for i := 1; i < len(os.Args); i++ {
fmt.Printf("%s\n", comma(os.Args[i]))
}
}
func comma(s string) string {
var buf bytes.Buffer
var intPart, decimal string
if strings.Contains(s, ".") {
intPart = s[:strings.Index(s, ".")]
decimal = s[strings.Index(s, "."):]
} else {
intPart = s
}
ind := 0
if intPart[0] == '+' || intPart[0] == '-' {
buf.WriteByte(intPart[0])
ind++
}
for i := ind; i < len(intPart); i++ {
buf.WriteByte(intPart[i])
if (len(intPart)-i-1)%3 == 0 && i != len(intPart)-1 {
buf.WriteByte(',')
}
}
return buf.String() + decimal
}
编写一个函数,判断两个字符串是否是相互打乱的,也就是说它们有着相同的字符,但是对应不同的顺序。
func isHaveEqualChar(s1, s2 string) bool {
if len(s1) != len(s2) {
return false
}
m1 := make(map[rune]int)
m2 := make(map[rune]int)
for _, v := range s1 {
m1[v] += 1
}
for _, v := range s2 {
m2[v] += 1
}
return reflect.DeepEqual(m1, m2)
}
编写KB、MB的常量声明,然后扩展到YB。
const (
Byte = 1
KB = 1000 * Byte
MB = 1000 * KB
GB = 1000 * MB
TB = 1000 * GB
PB = 1000 * TB
EB = 1000 * PB
ZB = 1000 * EB
YB = 1000 * ZB
)