3D等距投影的着色

huangapple go评论81阅读模式
英文:

Coloring for 3D Isometric projection

问题

以下是翻译好的内容:

要求是基于以下程序进行操作:
https://github.com/adonovan/gopl.io/blob/master/ch3/surface/main.go

  1. 将其转换为一个Web服务器,并将SVG渲染为网页。
  2. 对SVG进行着色,使峰值为红色,谷底为蓝色。

我确定已经完成了第一部分,而且我认为我也完成了第二部分,但显然不正确,但我不知道错在哪里。请帮忙。

package main

import (
	"fmt"
	"math"
	"net/http"
	"strconv"
)

const (
	cells   = 100         // 网格单元数
	xyrange = 30.0        // 坐标轴范围 (-xyrange..+xyrange)
	angle   = math.Pi / 6 // x、y轴的角度 (=30°)
)

var height, width = 300, 600 // 画布大小(像素)

var xyscale = width / 2 / xyrange  // 每个x或y单位的像素数
var zscale = float64(height) * 0.4 // 每个z单位的像素数

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func main() {
	addr := ":8000"
	fmt.Printf("访问\n  http://localhost%s/\n  http://localhost%s/?height=600&width=1200\n", addr, addr)

	//http服务器
	http.HandleFunc("/", handle)
	http.ListenAndServe(addr, nil)
}

func handle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "image/svg+xml")
	if err := r.ParseForm(); err != nil {
		return
	}
	for k, v := range r.Form {
		if k == "height" {
			h, _ := strconv.Atoi(v[0])
			if h > 0 {
				height = h
			}
		}
		if k == "width" {
			w, _ := strconv.Atoi(v[0])
			if w > 0 {
				width = w
			}
		}
	}

	xyscale = width / 2 / xyrange
	zscale = float64(height) * 0.4
	fmt.Fprintf(w, "<svg xmlns='http://www.w3.org/2000/svg' "+
		"style='stroke: grey; 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)
			r, g, b := getColor(i, j)
			fmt.Fprintf(w, "<polygon points='%g,%g %g,%g %g,%g %g,%g' fill='#%x%x%x'/>\n",
				ax, ay, bx, by, cx, cy, dx, dy, r, g, b)
		}
	}
	fmt.Fprintf(w, "</svg>")
}

func corner(i, j int) (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)等距地投影到2-D SVG画布上(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
}

func f(x, y float64) float64 {
	r := math.Hypot(x, y) // (0,0)到(x,y)的距离
	return math.Sin(r) / r
}

func getColor(i, j int) (int, int, int) {
	// 找到单元格(i,j)到单元格(i+1,j+1)中心的中点(x,y)。
	x := xyrange * (float64(i)/cells + 0.5/cells - 0.5)
	y := xyrange * (float64(j)/cells + 0.5/cells - 0.5)

	// 计算表面高度z。
	z := math.Hypot(x, y) // (0,0)到(x,y)的距离
	v := int(math.Sin(z)*127) + 128
	r := v
	g := 0
	b := 255 - v
	return r, g, b
}

这是我得到的结果:

3D等距投影的着色

注意,尽管问题似乎是关于Go语言的,但实际上我问的是getColor()算法。即使你不用Go语言编写,你也可以理解/回答这个问题。

英文:

The ask is, base on the following program
https://github.com/adonovan/gopl.io/blob/master/ch3/surface/main.go

  1. Turn it to a web server and render the SVG as web page
  2. Color the SVG so that the peak is red and valley is blue

I've got the 1st part right for sure, and I think I got the 2nd part right but apparently not, yet I have no idea where I'm wrong. Please help.

package main

import (
	&quot;fmt&quot;
	&quot;math&quot;
	&quot;net/http&quot;
	&quot;strconv&quot;
)

const (
	cells   = 100         // number of grid cells
	xyrange = 30.0        // axis ranges (-xyrange..+xyrange)
	angle   = math.Pi / 6 // angle of x, y axes (=30&#176;)
)

var height, width = 300, 600 // canvas size in pixels

var xyscale = width / 2 / xyrange  // pixels per x or y unit
var zscale = float64(height) * 0.4 // pixels per z unit

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30&#176;), cos(30&#176;)

func main() {
	addr := &quot;:8000&quot;
	fmt.Printf(&quot;Visit\n  http://localhost%s/\n  http://localhost%[1]s/?height=600&amp;width=1200\n&quot;, addr)

	//http server
	http.HandleFunc(&quot;/&quot;, handle)
	http.ListenAndServe(addr, nil)
}

func handle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set(&quot;Content-Type&quot;, &quot;image/svg+xml&quot;)
	if err := r.ParseForm(); err != nil {
		return
	}
	for k, v := range r.Form {
		if k == &quot;height&quot; {
			h, _ := strconv.Atoi(v[0])
			if h &gt; 0 {
				height = h
			}
		}
		if k == &quot;width&quot; {
			w, _ := strconv.Atoi(v[0])
			if w &gt; 0 {
				width = w
			}
		}
	}

	xyscale = width / 2 / xyrange
	zscale = float64(height) * 0.4
	fmt.Fprintf(w, &quot;&lt;svg xmlns=&#39;http://www.w3.org/2000/svg&#39; &quot;+
		&quot;style=&#39;stroke: grey; stroke-width: 0.7&#39; &quot;+
		&quot;width=&#39;%d&#39; height=&#39;%d&#39;&gt;&quot;, width, height)
	for i := 0; i &lt; cells; i++ {
		for j := 0; j &lt; 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)
			r, g, b := getColor(i, j)
			fmt.Fprintf(w, &quot;&lt;polygon points=&#39;%g,%g %g,%g %g,%g %g,%g&#39; fill=&#39;#%x%x%x&#39;/&gt;\n&quot;,
				ax, ay, bx, by, cx, cy, dx, dy, r, g, b)
		}
	}
	fmt.Fprintf(w, &quot;&lt;/svg&gt;&quot;)
}

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 := float64(width/2) + (x-y)*cos30*float64(xyscale)
	sy := float64(height/2) + (x+y)*sin30*float64(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
}

func getColor(i, j int) (int, int, int) {
	// Find point (x,y) at middle of corner of cell (i,j) to cell (i+1,j+1).
	x := xyrange * (float64(i)/cells + 0.5/cells - 0.5)
	y := xyrange * (float64(j)/cells + 0.5/cells - 0.5)

	// Compute surface height z.
	z := math.Hypot(x, y) // distance from (0,0)
	v := int(math.Sin(z)*127) + 128
	r := v
	g := 0
	b := 255 - v
	return r, g, b
}

Here is the result that I got:

3D等距投影的着色

NB, although the question seems to be for Go, but it is actually the
getColor() algorithm that I'm asking about. You can understand/answer even if you don't write in Go.

答案1

得分: 1

你的代码使用格式动词%x将十六进制值打印到SVG的fill属性中:

fmt.Fprintf(w, "<polygon points='%g,%g %g,%g %g,%g %g,%g' fill='#%x%x%x'/>\n",
                ax, ay, bx, by, cx, cy, dx, dy, r, g, b)

这会导致一些数字(如0和1)以一个十六进制数字的形式进行格式化。例如,RGB(254, 0, 1)将被格式化为fe01。然后,浏览器会错误地渲染颜色。

将格式动词更改为%02x,以确保RGB始终以两个十六进制数字打印。

现在RGB(254, 0, 1)被打印为fe0001,这是正确的十六进制颜色。

输出:

3D等距投影的着色

英文:

Your code uses the format verb %x to print the hex values to the SVG's fill attribute:

fmt.Fprintf(w, &quot;&lt;polygon points=&#39;%g,%g %g,%g %g,%g %g,%g&#39; fill=&#39;#%x%x%x&#39;/&gt;\n&quot;,
ax, ay, bx, by, cx, cy, dx, dy, r, g, b)

This causes some numbers like 0 and 1 to be formatted with one hex digit. For example RGB (254, 0, 1) would be formatted as fe01. The browser then render colors incorrectly.

Change the format verbs to %02x to ensure the RGB is always printed with two hex digits.

Now RGB (254, 0, 1) is printed as fe0001, which is the correct hex color.

Output:

3D等距投影的着色

huangapple
  • 本文由 发表于 2022年3月25日 00:13:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/71605847.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定