目录
3.1-3.4
前置知识:
官方示例代码:
// Surface computes an SVG rendering of a 3-D surface function.
package main
import (
"fmt"
"math"
)
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() {
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 := corner(i+1, j)
bx, by := corner(i, j)
cx, cy := corner(i, j+1)
dx, dy := corner(i+1, j+1)
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Println("</svg>")
}
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
}
如何使用这段代码?
我们可以注意到该代码生成的是svg格式文件,所以我们要将输出输入到一个svg文件中。
注意:应该视本地环境情况更改文件名字
go run ./surface.go > out.svg
运行命令后应该会在当前文件夹生成一个.svg文件,此时我们需要将svg文件转换成png/jpg文件,这个步骤可以使用这个网址(可能需要科技),或者自行搜索解决。
转换后我们便可以得到书中示例图片
3.1
题目
如果f函数返回的是无限制的float64值,那么SVG文件可能输出无效的多边形元素(虽然许多SVG渲染器会妥善处理这类问题)。修改程序跳过无效的多边形。
解析
这里我们只需要使用golang中Math包的两个函数:Math.IsInf()和Math.IsNaN(),即可对一个float64值进行判断是否无限制,这里作者选择将这两个函数写到一起
// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
if math.IsInf(x, 0) || math.IsNaN(x) {
return true
}
return false
}
随后我们仅需在f函数中和main函数中修改即可
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
if isInfinite(r) {
return 0
}
return math.Sin(r) / r
}
func main() {
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 := corner(i+1, j)
bx, by := corner(i, j)
cx, cy := corner(i, j+1)
dx, dy := corner(i+1, j+1)
if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
continue
}
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Println("</svg>")
}
全代码
// Surface computes an SVG rendering of a 3-D surface function.
// go run ./surface.go > test.svg
package main
import (
"fmt"
"math"
)
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() {
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 := corner(i+1, j)
bx, by := corner(i, j)
cx, cy := corner(i, j+1)
dx, dy := corner(i+1, j+1)
if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
continue
}
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Println("</svg>")
}
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)
if isInfinite(r) {
return 0
}
return math.Sin(r) / r
}
// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
if math.IsInf(x, 0) || math.IsNaN(x) {
return true
}
return false
}
3.2
题目
试验math包中其他函数的渲染图形。你是否能输出一个egg box、moguls或a saddle图案?
解析
三种生成函数如下
func eggBox(x, y float64) float64 {
return (math.Sin(x) + math.Sin(y)) / 10
}
func saddle(x, y float64) float64 {
return math.Pow(y/xyrange*2, 2) - math.Pow(x/xyrange*2, 2)
}
func moguls(x, y float64) float64 {
return math.Pow(math.Sin(x/xyrange*3*math.Pi), 2) * math.Cos(y/xyrange*3*math.Pi)
}
将其替换corner函数中的生成z轴即可,如生成moguls图案:
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 := moguls(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
}
全代码(以生成eggBox为例)
// Surface computes an SVG rendering of a 3-D surface function.
// go run surface.go > test.svg
package main
import (
"fmt"
"math"
)
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() {
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 := corner(i+1, j)
bx, by := corner(i, j)
cx, cy := corner(i, j+1)
dx, dy := corner(i+1, j+1)
if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
continue
}
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Println("</svg>")
}
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 := eggBox(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)
if isInfinite(r) {
return 0
}
return math.Sin(r) / r
}
func eggBox(x, y float64) float64 {
return (math.Sin(x) + math.Sin(y)) / 10
}
func saddle(x, y float64) float64 {
return math.Pow(y/xyrange*2, 2) - math.Pow(x/xyrange*2, 2)
}
func moguls(x, y float64) float64 {
return math.Pow(math.Sin(x/xyrange*3*math.Pi), 2) * math.Cos(y/xyrange*3*math.Pi)
}
// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
if math.IsInf(x, 0) || math.IsNaN(x) {
return true
}
return false
}
3.3
题目
根据高度给每个多边形上色,那样峰值部将是红色(#ff0000),谷部将是蓝色(#0000ff)。
解析
给多边形上色仅需将生成函数更改成以下格式即可
fmt.Printf("<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)
其中峰值和谷部可以根据当前z轴来仅需确定,为了方便此处将z轴<0的设置为谷部,>0的设置为峰部
func corner(i, j int) (float64, float64, bool) {
// 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, z >= 0
}
//新的生成函数
for i := 0; i < cells; i++ {
for j := 0; j < cells; j++ {
ax, ay, _ := corner(i+1, j)
bx, by, _ := corner(i, j)
cx, cy, _ := corner(i, j+1)
dx, dy, isPeak := corner(i+1, j+1)
if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
continue
}
color := blue
if isPeak {
color = red
}
fmt.Printf("<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)
}
}
全代码:
// Surface computes an SVG rendering of a 3-D surface function.
// go run ./surface.go > test.svg
package main
import (
"fmt"
"math"
)
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°)
red = "#ff0000"
blue = "#0000ff"
)
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func main() {
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, _ := corner(i+1, j)
bx, by, _ := corner(i, j)
cx, cy, _ := corner(i, j+1)
dx, dy, isPeak := corner(i+1, j+1)
if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
continue
}
color := blue
if isPeak {
color = red
}
fmt.Printf("<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Println("</svg>")
}
func corner(i, j int) (float64, float64, bool) {
// 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, z >= 0
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
if isInfinite(r) {
return 0
}
return math.Sin(r) / r
}
// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
if math.IsInf(x, 0) || math.IsNaN(x) {
return true
}
return false
}
3.4
题目:
参考1.7节Lissajous例子的函数,构造一个web服务器,用于计算函数曲面然后返回SVG数据给客户端。服务器必须设置Content-Type头部:
w.Header().Set("Content-Type", "image/svg+xml")
(这一步在Lissajous例子中不是必须的,因为服务器使用标准的PNG图像格式,可以根据前面的512个字节自动输出对应的头部。)允许客户端通过HTTP请求参数设置高度、宽度和颜色等参数。
解析
根据ch1/server1进行修改即可web获取高度和宽度:
if err := r.ParseForm(); err != nil {
log.Fatal(err)
}
if r.Form["width"] != nil {
width, _ = strconv.Atoi(r.Form["width"][0])
}
if r.Form["height"] != nil {
height, _ = strconv.Atoi(r.Form["height"][0])
}
修改高度和宽度网址示范:http://localhost:8000/?height=800&width=800
全代码
// Server2 is a minimal "echo" and counter server.
package main
import (
"fmt"
"log"
"math"
"net/http"
"strconv"
)
var (
width, height = 600, 320 // canvas size in pixels
xyscale = width / 2 / xyrange // pixels per x or y unit
zscale = float64(height) * 0.4 // pixels per z unit
)
const (
cells = 100 // number of grid cells
xyrange = 30.0 // axis ranges (-xyrange..+xyrange)
angle = math.Pi / 6 // angle of x, y axes (=30°)
red = "#ff0000"
blue = "#0000ff"
)
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func corner(i, j int) (float64, float64, bool) {
// 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 := float64(width)/2 + (x-y)*cos30*float64(xyscale)
sy := float64(height)/2 + (x+y)*sin30*float64(xyscale) - z*zscale
return sx, sy, z >= 0
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return math.Sin(r) / r
}
// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
if math.IsInf(x, 0) || math.IsNaN(x) {
return true
}
return false
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
if err := r.ParseForm(); err != nil {
log.Fatal(err)
}
if r.Form["width"] != nil {
width, _ = strconv.Atoi(r.Form["width"][0])
}
if r.Form["height"] != nil {
height, _ = strconv.Atoi(r.Form["height"][0])
}
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, _ := corner(i+1, j)
bx, by, _ := corner(i, j)
cx, cy, _ := corner(i, j+1)
dx, dy, isPeak := corner(i+1, j+1)
if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
continue
}
color := blue
if isPeak {
color = red
}
fmt.Fprintf(w, "<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Fprintln(w, "</svg>")
}
3.5-3.9
前置知识
官方示例代码
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main
import (
"image"
"image/color"
"image/png"
"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))
}
}
png.Encode(os.Stdout, img) // NOTE: ignoring errors
}
func mandelbrot(z complex128) color.Color {
const iterations = 200
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.Black
}
为什么看不到生成的png图片?
由于windows奇妙的bug,可能导致os.Stdout输出错误,使得图片生成不了,可以使用以下方法解决
create, _ := os.Create("out.png")
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))
}
}
png.Encode(create, img) // NOTE: ignoring errors
3.5
题目
实现一个彩色的Mandelbrot图像,使用image.NewRGBA创建图像,使用color.RGBA或color.YCbCr生成颜色。
解析
修改返回的color.Color参数即可
全代码:
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main
import (
"image"
"image/color"
"image/png"
"math/cmplx"
"os"
)
func main() {
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
)
create, _ := os.Create("out.png")
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))
}
}
png.Encode(create, img) // NOTE: ignoring errors
}
func mandelbrot(z complex128) color.Color {
const iterations = 200
const contrast = 15
var v complex128
for n := uint8(0); n < iterations; n++ {
v = v*v + z
if cmplx.Abs(v) > 2 {
return color.YCbCr{255 - contrast*n, 255 - contrast*n, 255 - contrast*n}
}
}
return color.White
}
3.6
题目:
升采样技术可以降低每个像素对计算颜色值和平均值的影响。简单的方法是将每个像素分成四个子像素,实现它。
解析:
每次对py, px进行枚举时分成四个点分别进行计算即可,注意宽和高参数也应该进行相应变化
for py := 0; py < height; py += 2 {
y1 := float64(py)/height*(ymax-ymin) + ymin
y2 := float64(py+1)/height*(ymax-ymin) + ymin
for px := 0; px < width; px += 2 {
x1 := float64(px)/width*(xmax-xmin) + xmin
x2 := float64(px+1)/width*(xmax-xmin) + xmin
z1 := complex(x1, y1)
z2 := complex(x1, y2)
z3 := complex(x2, y1)
z4 := complex(x2, y2)
// Image point (px, py) represents complex value z.
img.Set(px/2, py/2, color.Gray{
(mandelbrot(z1).Y + mandelbrot(z2).Y + mandelbrot(z3).Y + mandelbrot(z4).Y) / 4.0,
})
}
}
全代码
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main
import (
"image"
"image/color"
"image/png"
"math/cmplx"
"os"
)
func main() {
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 2048, 2048
)
create, _ := os.Create("out.png")
img := image.NewRGBA(image.Rect(0, 0, width, height))
for py := 0; py < height; py += 2 {
y1 := float64(py)/height*(ymax-ymin) + ymin
y2 := float64(py+1)/height*(ymax-ymin) + ymin
for px := 0; px < width; px += 2 {
x1 := float64(px)/width*(xmax-xmin) + xmin
x2 := float64(px+1)/width*(xmax-xmin) + xmin
z1 := complex(x1, y1)
z2 := complex(x1, y2)
z3 := complex(x2, y1)
z4 := complex(x2, y2)
// Image point (px, py) represents complex value z.
img.Set(px/2, py/2, color.Gray{
(mandelbrot(z1).Y + mandelbrot(z2).Y + mandelbrot(z3).Y + mandelbrot(z4).Y) / 4.0,
})
}
}
png.Encode(create, img) // NOTE: ignoring errors
}
func mandelbrot(z complex128) color.Gray {
const iterations = 200
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}
}
3.7
题目
另一个生成分形图像的方式是使用牛顿法来求解一个复数方程,例如$z^4-1=0$。每个起点到四个根的迭代次数对应阴影的灰度。方程根对应的点用颜色表示。
解析
牛顿法细节详情见链接,此处使用牛顿迭代法
func newton(z complex128) color.Color {
const iterations = 37
const contrast = 7
//0 <= iterations * contrast <= 255
// f(x) = z * z * z * z - 1
for n := uint8(0); n < iterations; n++ {
z -= (z - 1/(z*z*z)) / 4
// f(x + 1) = f(x) - f'(x) / f(x)
if cmplx.Abs(z*z*z*z-1) < 1e-6 {
return color.Gray{255 - contrast*n}
}
}
return color.Black
}
全代码
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main
import (
"image"
"image/color"
"image/png"
"math/cmplx"
"os"
)
func main() {
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
)
create, _ := os.Create("out.png")
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, newton(z))
}
}
png.Encode(create, img) // NOTE: ignoring errors
}
func newton(z complex128) color.Color {
const iterations = 37
const contrast = 7
//0 <= iterations * contrast <= 255
// f(x) = z * z * z * z - 1
for n := uint8(0); n < iterations; n++ {
z -= (z - 1/(z*z*z)) / 4
// f(x + 1) = f(x) - f'(x) / f(x)
if cmplx.Abs(z*z*z*z-1) < 1e-6 {
return color.Gray{255 - contrast*n}
}
}
return color.Black
}
3.8
题目
通过提高精度来生成更多级别的分形。使用四种不同精度类型的数字实现相同的分形:complex64、complex128、big.Float和big.Rat。(后面两种类型在math/big包声明。Float是有指定限精度的浮点数;Rat是无限精度的有理数。)它们间的性能和内存使用对比如何?当渲染图可见时缩放的级别是多少?
解析
不会,此处直接使用官网代码
全代码
package main
import (
"image/color"
"math"
"math/big"
"math/cmplx"
)
func mandelbrot64(z complex128) color.Color {
const iterations = 200
const contrast = 15
var v complex64
for n := uint8(0); n < iterations; n++ {
v = v*v + complex64(z)
if cmplx.Abs(complex128(v)) > 2 {
if n > 50 {
return color.RGBA{100, 0, 0, 255}
}
scale := math.Log(float64(n)) / math.Log(float64(iterations))
return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
}
}
return color.Black
}
func mandelbrot128(z complex128) color.Color {
const iterations = 200
const contrast = 15
var v complex128
for n := uint8(0); n < iterations; n++ {
v = v*v + z
if cmplx.Abs(v) > 2 {
if n > 50 {
return color.RGBA{100, 0, 0, 255}
}
scale := math.Log(float64(n)) / math.Log(float64(iterations))
return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
}
}
return color.Black
}
func mandelbrotBigFloat(z complex128) color.Color {
const iterations = 200
const contrast = 15
zR := (&big.Float{}).SetFloat64(real(z))
zI := (&big.Float{}).SetFloat64(imag(z))
var vR, vI = &big.Float{}, &big.Float{}
for i := uint8(0); i < iterations; i++ {
vR2, vI2 := &big.Float{}, &big.Float{}
vR2.Mul(vR, vR).Sub(vR2, (&big.Float{}).Mul(vI, vI)).Add(vR2, zR)
vI2.Mul(vR, vI).Mul(vI2, big.NewFloat(2)).Add(vI2, zI)
vR, vI = vR2, vI2
squareSum := &big.Float{}
squareSum.Mul(vR, vR).Add(squareSum, (&big.Float{}).Mul(vI, vI))
if squareSum.Cmp(big.NewFloat(4)) == 1 {
if i > 50 {
return color.RGBA{100, 0, 0, 255}
}
scale := math.Log(float64(i)) / math.Log(float64(iterations))
return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
}
}
return color.Black
}
func mandelbrotBigRat(z complex128) color.Color {
const iterations = 200
const contrast = 15
zR := (&big.Rat{}).SetFloat64(real(z))
zI := (&big.Rat{}).SetFloat64(imag(z))
var vR, vI = &big.Rat{}, &big.Rat{}
for i := uint8(0); i < iterations; i++ {
vR2, vI2 := &big.Rat{}, &big.Rat{}
vR2.Mul(vR, vR).Sub(vR2, (&big.Rat{}).Mul(vI, vI)).Add(vR2, zR)
vI2.Mul(vR, vI).Mul(vI2, big.NewRat(2, 1)).Add(vI2, zI)
vR, vI = vR2, vI2
squareSum := &big.Rat{}
squareSum.Mul(vR, vR).Add(squareSum, (&big.Rat{}).Mul(vI, vI))
if squareSum.Cmp(big.NewRat(4, 1)) == 1 {
if i > 50 {
return color.RGBA{100, 0, 0, 255}
}
scale := math.Log(float64(i)) / math.Log(float64(iterations))
return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
}
}
return color.Black
}
3.9
题目
编写一个web服务器,用于给客户端生成分形的图像。运行客户端通过HTTP参数指定x、y和zoom参数。
解析
与3.4题并无区别,注意变量类型转换
全代码
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main
import (
"image"
"image/color"
"image/png"
"io"
"log"
"math"
"math/cmplx"
"net/http"
"strconv"
)
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
x, y, zoom := 0.0, 0.0, 0.0
if err := r.ParseForm(); err != nil {
log.Fatal(err)
}
if r.Form["x"] != nil {
x, _ = strconv.ParseFloat(r.Form["x"][0], 64)
}
if r.Form["y"] != nil {
y, _ = strconv.ParseFloat(r.Form["y"][0], 64)
}
if r.Form["zoom"] != nil {
zoom, _ = strconv.ParseFloat(r.Form["zoom"][0], 64)
}
render(w, x, y, zoom)
}
func render(out io.Writer, x, y, zoom float64) {
const (
// xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
)
exp := math.Exp2(1 - zoom)
xmin, xmax := x-exp, x+exp
ymin, ymax := y-exp, y+exp
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))
}
}
png.Encode(out, img) // NOTE: ignoring errors
}
func mandelbrot(z complex128) color.Color {
const iterations = 200
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.Black
}
3.10-3.12
前置知识
官网代码
// comma inserts commas in a non-negative decimal integer string.
func comma(s string) string {
n := len(s)
if n <= 3 {
return s
}
return comma(s[:n-3]) + "," + s[n-3:]
}
3.10-3.11
题目
练习 3.10: 编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作。
练习 3.11: 完善comma函数,以支持浮点数处理和一个可选的正负号的处理。
解析
Leetcode 练习题,注意小数点后可以不添加逗号,依据题意模拟即可
bytes.Buffer使用,请观察代码
全代码
package main
import (
"bytes"
"fmt"
"strings"
)
func comma(s string) string {
var res bytes.Buffer
if strings.HasPrefix(s, "-") {
res.WriteString("-")
s = s[1:]
}
var last string
if strings.Contains(s, ".") {
last = s[strings.IndexAny(s, "."):]
s = s[:strings.IndexAny(s, ".")]
}
cnt := len(s) % 3
for i := 0; i < cnt; i++ {
res.WriteByte(s[i])
}
for i, j := cnt, 0; i < len(s); i, j = i+1, (j+1)%3 {
if cnt != 0 && j == 0 {
res.WriteString(",")
}
res.WriteByte(s[i])
}
res.WriteString(last)
return res.String()
}
func main() {
var s string
fmt.Scan(&s)
fmt.Println(comma(s))
}
3.12
题目
编写一个函数,判断两个字符串是否是相互打乱的,也就是说它们有着相同的字符,但是对应不同的顺序。
解析
Leetcode练习题,使用map进行判断即可,注意需要完全相等
全代码
package main
import (
"fmt"
)
func isSame(a, b string) bool {
hash := make(map[rune]int)
for _, val := range a {
hash[val]++
}
for _, val := range b {
if hash[val] <= 0 {
return false
}
hash[val]--
}
for _, val := range hash {
if val > 0 {
return false
}
}
return true
}
func main() {
var a, b string
fmt.Scan(&a, &b)
if isSame(a, b) {
fmt.Println("YES")
} else {
fmt.Println("NO")
}
}
3.13
3.13
题目
编写KB、MB的常量声明,然后扩展到YB。
解析
语法题,模拟即可
全代码
package main
const (
KB = 1000
MB = KB * KB
GB = MB * KB
TB = GB * KB
PB = TB * KB
EB = PB * KB
ZB = EB * KB
YB = ZB * KB
)
func main() {
}