Приведенная программа иллюстрирует графические вычисления с плавающей точкой. Она строит график функции от двух переменных z = f(x,y) в виде трехмерной сетки с помощью SVG (Scalable Vector Graphics — масштабируемая векторная графика), стандартного XML-формата для черчения линий. На рис. 1 показан пример вывода программы для функции sin(r)/r, где r представляет собой sqrt(x*x+y*y).
Раскрыть/скрыть решение и комментарии.
package main
import (
"fmt"
"math"
"strconv"
)
const (
width, height = 600, 320 // Размер канвы в пикселях
cells = 100 // Количество ячеек сетки
xyrange = 30.0 // Диапазон осей (-xyrange..+xyrange)
xyscale = width / 2 / xyrange // Пикселей в единице х или у
zscale = height * 0.4 // Пикселей в единице z
angle = math.Pi / 6 // Углы осей x, y (=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: none; 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'" +
"style=\"fill: rgb(" +
strconv.Itoa((int)(math.Abs((float64)(255-(j*5)))))+
"," + strconv.Itoa(255-i)+ "," +
strconv.Itoa(255-j) + "); stroke: none;\"/>\n",
ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Println("</svg>")
}
func corner(i, j int) (float64, float64) {
// Ищем угловую точку (x,y) ячейки (i,j)
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 sx, sy
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // Расстояние от (0,0)
return math.Sin(r) / r
}
Архив примера можно взять здесь.
Суть программы состоит в выполнении отображения между тремя различными системами координат, показанными на рис. 2.
Рис.2. Три различные системы координат
Первая — это двумерная сетка размером 100х100 ячеек, идентифицируемых значениями целочисленных координат (i, j), начиная с (0, 0) в дальнем углу. Мы выполняем черчение от дальнего конца к ближнему, так что дальние многоугольники могут быть скрыты находящимися ближе.
Вторая система координат представляет собой сетку трехмерных координат (х, у, z) с плавающей точкой, где х и у являются линейными функциями от i и j, перемещенные так, что начало координат находится в центре, и масштабированные с помощью константы xyrange. Высота z представляет собой значение функции поверхности f(х, у).
Третья система координат представляет собой канву двумерного изображения с точкой (0, 0) в левом верхнем углу. Точки на этой плоскости обозначается как (sx, sy). Мы используем изометрическую проекцию для отображения каждой трехмерной точки (х, у, z) на двумерную канву. Чем дальше справа на канве находится точка, тем больше ее значение х или меньше ее значение y. А чем ниже на канве находится точка, тем больше ее значение х или значение у и меньше значение z. Вертикальные и горизонтальные масштабные множители для х и у вычисляются как синус и косинус угла 30°. Масштабный множитель для z, равный 0.4, выбран произвольно.
Для каждой ячейки в двумерной сетке функция main вычисляет координаты на канве четырех углов многоугольника ABCD, где вершина В соответствует (i, j), а вершины А, С и D являются ее соседями, а затем выводит SVG-команду его черчения.