温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么用Go语言制作svg格式树形图

发布时间:2022-09-05 09:50:43 来源:亿速云 阅读:275 作者:iii 栏目:开发技术

这篇文章主要介绍“怎么用Go语言制作svg格式树形图”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么用Go语言制作svg格式树形图”文章能帮助大家解决问题。

    什么是SVG

    SVG定义

    SVG是可伸缩矢量图形 (Scalable Vector Graphics),于2003年1月14日成为 W3C 推荐标准。

    SVG 用来定义用于网络的基于矢量的图形

    SVG 使用 XML 格式定义图形

    SVG 是万维网联盟的标准

    SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体

    SVG优点

    • SVG 可被非常多的工具读取和修改(比如记事本)

    • SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。

    • SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失

    • SVG 图像可在任何的分辨率下被高质量地打印

    • SVG 可在图像质量不下降的情况下被放大

    • SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)

    • SVG 可以与 JavaScript 技术一起运行

    • SVG 是开放的标准

    • SVG 文件是纯粹的 XML

    预定义元素

    • 矩形 <rect>

    • 圆形 <circle>

    • 椭圆 <ellipse>

    • 直线 <line>

    • 文字 <text>

    • 路径 <path>

    • 折线 <polyline>

    • 多边形 <polygon>

    怎么用Go语言制作svg格式树形图

    制作二叉树的树形图,就使用圆形、直线、文字三种即可:

    圆形 <circle>

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <circle cx="150" cy="50" r="30" stroke="black" stroke-width="2" fill="red"/>
    </svg>

    cx和cy属性定义圆点的x和y坐标;r属性定义圆的半径

    如果省略cx和cy,圆的中心会被设置为(0, 0)

    直线 <line>

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <line x1="50" y1="50" x2="200" y2="200" />
    </svg>

    x1,y2 属性定义线条的起始端点坐标

    x2,y2 属性定义线条的结束端点坐标

    文字 <text>

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <text x="30" y="150" fill="red">Welcome to Hann's HomePage</text>
    </svg>

    x,y 属性定义文字左对齐显示时的起始坐标(居中对齐则是文字中间点)

    fill 属性定义文字的颜色

    怎么用Go语言制作svg格式树形图

    结点SVG格式

    根结点

    由<circle>和<text>组成,存放结点的数据域

        <g id="0,0">
        <circle cx="400" cy="50" r="30" stroke="black" stroke-width="2" fill="orange" />
        <text x="400" y="50" fill="red" font-size="20" text-anchor="middle" dominant-baseline="middle">1</text>
        </g>

    子树结点

    比根结点多出<line>元素,用来表示父结点左或右指针的指向

        <g id="1,0">
        <circle cx="200" cy="170" r="30" stroke="black" stroke-width="2" fill="orange" />
        <text x="200" y="170" fill="red" font-size="20" text-anchor="middle" dominant-baseline="middle">2</text>
        <line x1="200" y1="140" x2="379" y2="71" />
        </g>

    叶结点

    与子树结点相同,为区别显示把<circle>填充色改为greenlight

        <g id="1,1">
        <circle cx="600" cy="170" r="30" stroke="black" stroke-width="2" fill="lightgreen" />
        <text x="600" y="170" fill="red" font-size="20" text-anchor="middle" dominant-baseline="middle">3</text>
        <line x1="600" y1="140" x2="421" y2="71" />
        </g>

    结点坐标

    坐标的确定

    结点坐标确定,把二叉树还原成满二叉树,结点位置标记为:

    [ [0,0], [1,0], [1,1], [2,0], [2,1], [2,2], [2,3], [3,0]......],再用循环计算出各点坐标。

    连线的夹角

    实际上不用考虑连线夹角,只要计算出连线始终两端点的坐标即可:

    怎么用Go语言制作svg格式树形图

    结点文本

    以字符串形式保存好属性变量的特征关键词,用于遍历二叉树时替换成实际数据:

    func XmlNode(M, N, X, Y int, Data string, Color ...string) string {
        var cColor, tColor string
        R := 30
        Node := `<Tab><g id="M,N">
        <circle cx="X" cy="Y" r="RC" stroke="black" stroke-width="2" fill="COLOR" />
        <text x="X" y="Y" fill="TextColor" font-size="20" text-anchor="middle"
            dominant-baseline="middle">DATA</text>
        <ROOT/>
        </g><CrLf>`
        if len(Color) == 0 {
            cColor, tColor = "orange", "red"
        } else if len(Color) == 1 {
            cColor, tColor = Color[0], "red"
        } else {
            cColor, tColor = Color[0], Color[1]
        }
        Node = strings.Replace(Node, "M", strconv.Itoa(M), 1)
        Node = strings.Replace(Node, "N", strconv.Itoa(N), 1)
        Node = strings.Replace(Node, "X", strconv.Itoa(X), 2)
        Node = strings.Replace(Node, "Y", strconv.Itoa(Y), 2)
        Node = strings.Replace(Node, "RC", strconv.Itoa(R), 1)
        Node = strings.Replace(Node, "DATA", Data, 1)
        Node = strings.Replace(Node, "COLOR", cColor, 1)
        Node = strings.Replace(Node, "TextColor", tColor, 1)
        Node = strings.Replace(Node, "<CrLf>", "\n", -1)
        Node = strings.Replace(Node, "<Tab>", "\t", -1)
        Node = strings.Replace(Node, "\n\t\t", " ", -1)
        return Node
    }

    二叉树转SVG

    遍历二叉树对应的满二叉树,读出数据域并计算坐标,转成svg格式:

    格式转换

    func (bt *biTree) Xml4Tree() string {
        var Xml, Node string
        Head := "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/" +
            "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT</svg>"
        Line := `<line x1="X1" y1="Y1" x2="X2" y2="Y2" />`
        Link := `<a xlink:href="https://blog.csdn.net/boysoft2002" target="_blank">
        <text x="5" y="20" fill="blue">Hann's CSDN Homepage</text></a>`
        List := bt.LevelNullwith()
        Levels := len(List)
        for i := Levels - 1; i >= 0; i-- {
            negative := -1
            TmpXml := ""
            for j := 0; j < Pow2(i); j++ {
                t := Pow2(Levels - i - 1)
                x, y := 50*(2*t*j+t), 120*i+50
                if List[i][j] != nil {
                    fillColor := "orange"
                    if i == Levels-1 || i > 0 && i < Levels-1 &&
                        List[i+1][j*2] == nil && List[i+1][j*2+1] == nil {
                        fillColor = "lightgreen"
                    }
                    TmpStr := ""
                    switch List[i][j].(type) {
                    case int:
                        TmpStr = strconv.Itoa(List[i][j].(int))
                    case float64:
                        TmpStr = strconv.FormatFloat(List[i][j].(float64), 'g', -1, 64)
                    case string:
                        TmpStr = List[i][j].(string)
                    default:
                        TmpStr = "Error Type"
                    }
                    Xml = XmlNode(i, j, x, y, TmpStr, fillColor)
                }
                if i > 0 {
                    line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
                    line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
                    negative *= -1
                    x0, y0 := 21, 21
                    x += 50*negative*(2*t*j%2+t) - negative*x0
                    line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
                    line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
                    Xml = strings.Replace(Xml, "<ROOT/>", line1, 1)
                }
                if List[i][j] != nil {
                    TmpXml += Xml
                }
            }
            Node = TmpXml + Node
        }
        Xml = strings.Replace(Head, "CONTENT", Node, 1)
        Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
        Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
        Xml = strings.Replace(Xml, "<ROOT/>", Link, 1)
        return Xml
    }

    写入文件、调取浏览

        //......
        for index, text := range texts {
            svgFile := "./biTree0" + strconv.Itoa(index+1) + ".svg"
            file, err1 = os.Create(svgFile)
            if err1 != nil {
                panic(err1)
            }
            _, err1 = io.WriteString(file, text)
            if err1 != nil {
                panic(err1)
            }
            file.Close()
            exec.Command("cmd", "/c", "start", svgFile).Start()
            //Linux 代码:
            //exec.Command("xdg-open", svgFile).Start()
            //Mac 代码:
            //exec.Command("open", svgFile).Start()
        }
        //......

    全部源代码

    package main
     
    import (
    	"fmt"
    	"io"
    	"os"
    	"os/exec"
    	"strconv"
    	"strings"
    )
     
    type btNode struct {
    	Data   interface{}
    	Lchild *btNode
    	Rchild *btNode
    }
     
    type biTree struct {
    	Root *btNode
    }
     
    func Build(data interface{}) *biTree {
    	var list []interface{}
    	if data == nil {
    		return &biTree{}
    	}
    	switch data.(type) {
    	case []interface{}:
    		list = append(list, data.([]interface{})...)
    	default:
    		list = append(list, data)
    	}
    	if len(list) == 0 {
    		return &biTree{}
    	}
    	node := &btNode{Data: list[0]}
    	list = list[1:]
    	Queue := []*btNode{node}
    	for len(list) > 0 {
    		if len(Queue) == 0 {
    			//panic("Given array can not build binary tree.")
    			return &biTree{Root: node}
    		}
    		cur := Queue[0]
    		val := list[0]
    		Queue = Queue[1:]
    		if val != nil {
    			cur.Lchild = &btNode{Data: val}
    			if cur.Lchild != nil {
    				Queue = append(Queue, cur.Lchild)
    			}
    		}
    		list = list[1:]
    		if len(list) > 0 {
    			val := list[0]
    			if val != nil {
    				cur.Rchild = &btNode{Data: val}
    				if cur.Rchild != nil {
    					Queue = append(Queue, cur.Rchild)
    				}
    			}
    			list = list[1:]
    		}
    	}
    	return &biTree{Root: node}
    }
     
    func (bt *biTree) AppendNode(data interface{}) {
    	root := bt.Root
    	if root == nil {
    		bt.Root = &btNode{Data: data}
    		return
    	}
    	Queue := []*btNode{root}
    	for len(Queue) > 0 {
    		cur := Queue[0]
    		Queue = Queue[1:]
    		if cur.Lchild != nil {
    			Queue = append(Queue, cur.Lchild)
    		} else {
    			cur.Lchild = &btNode{Data: data}
    			return
    		}
    		if cur.Rchild != nil {
    			Queue = append(Queue, cur.Rchild)
    		} else {
    			cur.Rchild = &btNode{Data: data}
    			break
    		}
    	}
    }
     
    func Copy(bt *biTree) *biTree {
    	root := bt.Root
    	if root == nil {
    		return &biTree{}
    	}
    	node := &btNode{Data: root.Data}
    	Queue1, Queue2 := []*btNode{root}, []*btNode{node}
    	for len(Queue1) > 0 {
    		p1, p2 := Queue1[0], Queue2[0]
    		Queue1, Queue2 = Queue1[1:], Queue2[1:]
    		if p1.Lchild != nil {
    			Node := &btNode{Data: p1.Lchild.Data}
    			p2.Lchild = Node
    			Queue1 = append(Queue1, p1.Lchild)
    			Queue2 = append(Queue2, Node)
    		}
    		if p1.Rchild != nil {
    			Node := &btNode{Data: p1.Rchild.Data}
    			p2.Rchild = Node
    			Queue1 = append(Queue1, p1.Rchild)
    			Queue2 = append(Queue2, Node)
    		}
    	}
    	return &biTree{Root: node}
    }
     
    func Mirror(bt *biTree) *biTree {
    	root := bt.Root
    	if root == nil {
    		return &biTree{}
    	}
    	node := &btNode{Data: root.Data}
    	Queue1, Queue2 := []*btNode{root}, []*btNode{node}
    	for len(Queue1) > 0 {
    		p1, p2 := Queue1[0], Queue2[0]
    		Queue1, Queue2 = Queue1[1:], Queue2[1:]
    		if p1.Lchild != nil {
    			Node := &btNode{Data: p1.Lchild.Data}
    			p2.Rchild = Node
    			Queue1 = append(Queue1, p1.Lchild)
    			Queue2 = append(Queue2, Node)
    		}
    		if p1.Rchild != nil {
    			Node := &btNode{Data: p1.Rchild.Data}
    			p2.Lchild = Node
    			Queue1 = append(Queue1, p1.Rchild)
    			Queue2 = append(Queue2, Node)
    		}
    	}
    	return &biTree{Root: node}
    }
     
    func (bt *biTree) BForder2D() [][]interface{} {
    	var res [][]interface{}
    	root := bt.Root
    	if root == nil {
    		return res
    	}
    	Queue := []*btNode{root}
    	for len(Queue) > 0 {
    		Nodes := []interface{}{}
    		Levels := len(Queue)
    		for Levels > 0 {
    			cur := Queue[0]
    			Queue = Queue[1:]
    			Nodes = append(Nodes, cur.Data)
    			Levels--
    			if cur.Lchild != nil {
    				Queue = append(Queue, cur.Lchild)
    			}
    			if cur.Rchild != nil {
    				Queue = append(Queue, cur.Rchild)
    			}
    		}
    		res = append(res, Nodes)
    	}
    	return res
    }
     
    func (bt *biTree) LevelNullwith(Fills ...interface{}) [][]interface{} {
    	var Nodes [][]interface{}
    	var Fill0 interface{}
    	if len(Fills) == 0 {
    		Fill0 = nil
    	} else if len(Fills) == 1 {
    		Fill0 = Fills[0]
    	} else {
    		panic("Error: number of parameters is greater than 1")
    	}
    	root := bt.Root
    	if root == nil {
    		return Nodes
    	}
    	Count := 0
    	Queue := []*btNode{root}
    	for len(Queue) > 0 {
    		nodes := []interface{}{}
    		Level := len(Queue)
    		for Level > 0 {
    			cur := Queue[0]
    			Queue = Queue[1:]
    			nodes = append(nodes, cur.Data)
    			Count++
    			Level--
    			if cur.Lchild != nil {
    				Queue = append(Queue, cur.Lchild)
    			}
    			if cur.Rchild != nil {
    				Queue = append(Queue, cur.Rchild)
    			}
    		}
    		Nodes = append(Nodes, nodes)
    	}
    	newbiTree := Copy(bt)
    	for i := 1; i < Pow2(len(Nodes))-Count; i++ {
    		newbiTree.AppendNode(Fill0)
    	}
    	return newbiTree.BForder2D()
    }
     
    func XmlNode(M, N, X, Y int, Data string, Color ...string) string {
    	var cColor, tColor string
    	R := 30
    	Node := `<Tab><g id="M,N">
    	<circle cx="X" cy="Y" r="RC" stroke="black" stroke-width="2" fill="COLOR" />
    	<text x="X" y="Y" fill="TextColor" font-size="20" text-anchor="middle"
    		dominant-baseline="middle">DATA</text>
    	<ROOT/>
    	</g><CrLf>`
    	if len(Color) == 0 {
    		cColor, tColor = "orange", "red"
    	} else if len(Color) == 1 {
    		cColor, tColor = Color[0], "red"
    	} else {
    		cColor, tColor = Color[0], Color[1]
    	}
    	Node = strings.Replace(Node, "M", strconv.Itoa(M), 1)
    	Node = strings.Replace(Node, "N", strconv.Itoa(N), 1)
    	Node = strings.Replace(Node, "X", strconv.Itoa(X), 2)
    	Node = strings.Replace(Node, "Y", strconv.Itoa(Y), 2)
    	Node = strings.Replace(Node, "RC", strconv.Itoa(R), 1)
    	Node = strings.Replace(Node, "DATA", Data, 1)
    	Node = strings.Replace(Node, "COLOR", cColor, 1)
    	Node = strings.Replace(Node, "TextColor", tColor, 1)
    	Node = strings.Replace(Node, "<CrLf>", "\n", -1)
    	Node = strings.Replace(Node, "<Tab>", "\t", -1)
    	Node = strings.Replace(Node, "\n\t\t", " ", -1)
    	return Node
    }
     
    func Pow2(x int) int { //x>=0
    	res := 1
    	for i := 0; i < x; i++ {
    		res *= 2
    	}
    	return res
    }
     
    func Xml4Full(Levels int) string {
    	var Xml, Node string
    	Head := "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/" +
    		"1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT</svg>"
    	Line := `<line x1="X1" y1="Y1" x2="X2" y2="Y2" />`
    	Link := `<a xlink:href="https://blog.csdn.net/boysoft2002" rel="external nofollow"  rel="external nofollow"  target="_blank">
    	<text x="5" y="20" fill="blue">Hann's CSDN Homepage</text></a>`
    	for i := 0; i < Levels; i++ {
    		negative := -1
    		for j := 0; j < Pow2(i); j++ {
    			t := Pow2(Levels - i - 1)
    			x, y := 50*(2*t*j+t), 120*i+50
    			if Levels != 1 && i == Levels-1 {
    				Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j), "lightgreen")
    			} else {
    				Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j))
    			}
    			if i > 0 {
    				line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
    				line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
    				negative *= -1
    				x0, y0 := 21, 21
    				//过连线起始端的半径与纵轴线夹角取45度时x,y坐标修正值21,21[30/1.414]
    				//取30度时 x0,y0:= 15,30-26;取60度时 x0,y0:= 26[15*1.732],15
    				x += 50*negative*(2*t*j%2+t) - negative*x0
    				line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
    				line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
    				Xml = strings.Replace(Xml, "<ROOT/>", line1, 1)
    			}
    			Node += Xml
    		}
    	}
    	Xml = strings.Replace(Head, "CONTENT", Node, 1)
    	Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
    	Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
    	Xml = strings.Replace(Xml, "<ROOT/>", Link, 1)
    	return Xml
    }
     
    func (bt *biTree) Xml4Tree() string {
    	var Xml, Node string
    	Head := "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/" +
    		"1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT</svg>"
    	Line := `<line x1="X1" y1="Y1" x2="X2" y2="Y2" />`
    	Link := `<a xlink:href="https://blog.csdn.net/boysoft2002" rel="external nofollow"  rel="external nofollow"  target="_blank">
    	<text x="5" y="20" fill="blue">Hann's CSDN Homepage</text></a>`
    	List := bt.LevelNullwith()
    	Levels := len(List)
    	for i := Levels - 1; i >= 0; i-- {
    		negative := -1
    		TmpXml := ""
    		for j := 0; j < Pow2(i); j++ {
    			t := Pow2(Levels - i - 1)
    			x, y := 50*(2*t*j+t), 120*i+50
    			if List[i][j] != nil {
    				fillColor := "orange"
    				if i == Levels-1 || i > 0 && i < Levels-1 &&
    					List[i+1][j*2] == nil && List[i+1][j*2+1] == nil {
    					fillColor = "lightgreen"
    				}
    				TmpStr := ""
    				switch List[i][j].(type) {
    				case int:
    					TmpStr = strconv.Itoa(List[i][j].(int))
    				case float64:
    					TmpStr = strconv.FormatFloat(List[i][j].(float64), 'g', -1, 64)
    				case string:
    					TmpStr = List[i][j].(string)
    				default:
    					TmpStr = "Error Type"
    				}
    				Xml = XmlNode(i, j, x, y, TmpStr, fillColor)
    			}
    			if i > 0 {
    				line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
    				line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
    				negative *= -1
    				x0, y0 := 21, 21
    				x += 50*negative*(2*t*j%2+t) - negative*x0
    				line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
    				line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
    				Xml = strings.Replace(Xml, "<ROOT/>", line1, 1)
    			}
    			if List[i][j] != nil {
    				TmpXml += Xml
    			}
    		}
    		Node = TmpXml + Node
    	}
    	Xml = strings.Replace(Head, "CONTENT", Node, 1)
    	Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
    	Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
    	Xml = strings.Replace(Xml, "<ROOT/>", Link, 1)
    	return Xml
    }
     
    func main() {
     
    	var file *os.File
    	var err1 error
     
    	list1 := []interface{}{"-", "*", 6, "+", 3, nil, nil, 2, 8}
    	list2 := []interface{}{1, 2, 3, 4, 5, nil, 6, 7, 8}
    	tree1 := Build(list1)
    	tree2 := Build(list2)
    	tree3 := Mirror(tree2)
    	texts := []string{tree1.Xml4Tree(), tree2.Xml4Tree(), tree3.Xml4Tree(), Xml4Full(4)}
     
    	for index, text := range texts {
    		svgFile := "./biTree0" + strconv.Itoa(index+1) + ".svg"
    		file, err1 = os.Create(svgFile)
    		if err1 != nil {
    			panic(err1)
    		}
    		_, err1 = io.WriteString(file, text)
    		if err1 != nil {
    			panic(err1)
    		}
    		file.Close()
    		exec.Command("cmd", "/c", "start", svgFile).Start()
    		//Linux 代码:
    		//exec.Command("xdg-open", svgFile).Start()
    		//Mac 代码:
    		//exec.Command("open", svgFile).Start()
    	}
     
    	fmt.Println("Welcome to my homepage: https://blog.csdn.net/boysoft2002")
    	exec.Command("cmd", "/c", "start", "https://blog.csdn.net/boysoft2002").Start()
     
    }

    关于“怎么用Go语言制作svg格式树形图”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI