Floating-point computation
Source
Donovan and Kernighan, The Go Programming Language
Contents
Description
Exercise 3.1: If the function
f
returns a non-finitefloat64
value, the SVG file will contain invalid<polygon>
elements (although many SVG renderers handle this gracefully). Modify the program to skip invalid polygons.
Exercise 3.3 Color each polygon based on its height, so that the peaks are colored red (
ff0000
) and the valleys blue (#0000ff
).
My solution
Modifying original version given in §3.2 Floating Point Numbers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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++ {
// Modified for Exercise 3.1 and 3.3.
ax, ay, isPeak := corner(i+1, j); if ax < 1 || ay < 1 { continue }
bx, by, _ := corner(i, j); if bx < 1 || by < 1 { continue }
cx, cy, _ := corner(i, j+1); if cx < 1 || cy < 1 { continue }
dx, dy, _ := corner(i+1, j+1); if dx < 1 || dy < 1 { continue }
var fillclr string
if isPeak {
fillclr = "#0000ff"
} else {
fillclr = "#ff0000"
}
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g' " +
"style='fill:%s'/>\n",
ax, ay, bx, by, cx, cy, dx, dy, fillclr)
}
}
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)
// Modified for Exercise 3.3.
var zIsPos bool
if z >= 0 {
zIsPos = true
} else {
zIsPos = false
}
// Project (x, y, z) isometrically onto 2D SVG canvas (sx, sy).
sx := width/2 + (x-y) * cos30 * xyscale
sy := height/2 + (x+y) * sin30 * xyscale - z * zscale
// Modified for Exercise 3.1.
if math.IsInf(sx, 0) || math.IsInf(sy, 0) {
return -1, -1, zIsPos
}
return sx, sy, zIsPos
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0, 0)
return math.Sin(r) / r
}