1
0
mirror of https://shylinux.com/x/icebergs synced 2025-04-26 01:24:05 +08:00

opt chart

This commit is contained in:
harveyshao 2021-12-11 17:30:02 +08:00
parent 28151f01ea
commit 87b675d294
5 changed files with 80 additions and 314 deletions

View File

@ -121,6 +121,8 @@ const (
CYAN = "cyan"
WHITE = "white"
RANDOM = "random"
GLASS = "#0000"
GRAY = "gray"
)
const QRCODE = "qrcode"

View File

@ -16,19 +16,18 @@ const CMD = "cmd"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
CMD: {Name: CMD, Help: "命令", Value: kit.Data(
kit.MDB_SHORT, "type", kit.MDB_PATH, ice.PWD,
)},
CMD: {Name: CMD, Help: "命令", Value: kit.Data(kit.MDB_SHORT, "type", kit.MDB_PATH, ice.PWD)},
}, Commands: map[string]*ice.Command{
"/cmd/": {Name: "/cmd/", Help: "命令", Action: ice.MergeAction(map[string]*ice.Action{
ice.CTX_INIT: {Name: "_init", Help: "初始化", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, "go", kit.MDB_NAME, "web.code.inner")
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, "mod", kit.MDB_NAME, "web.code.inner")
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, "sum", kit.MDB_NAME, "web.code.inner")
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, "shy", kit.MDB_NAME, "web.wiki.word")
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, "svg", kit.MDB_NAME, "web.wiki.draw")
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, "csv", kit.MDB_NAME, "web.wiki.data")
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, "json", kit.MDB_NAME, "web.wiki.json")
for _, k := range []string{"sh", "go", "js", "mod", "sum"} {
m.Cmdy(CMD, mdb.CREATE, kit.MDB_TYPE, k, kit.MDB_NAME, "web.code.inner")
}
}},
}, ctx.CmdAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if strings.HasSuffix(m.R.URL.Path, ice.PS) {

View File

@ -6,4 +6,5 @@ import (
_ "shylinux.com/x/icebergs/core/mall"
_ "shylinux.com/x/icebergs/core/team"
_ "shylinux.com/x/icebergs/core/wiki"
_ "shylinux.com/x/icebergs/core/wiki/chart"
)

View File

@ -5,24 +5,22 @@ import (
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
type item struct {
type Item struct {
list []string
args []interface{}
}
func newItem(list []string, args ...interface{}) *item {
return &item{list, args}
func NewItem(list []string, args ...interface{}) *Item {
return &Item{list, args}
}
func (item *item) echo(str string, arg ...interface{}) *item {
func (item *Item) Echo(str string, arg ...interface{}) *Item {
item.list = append(item.list, kit.Format(str, arg...))
return item
}
func (item *item) push(str string, arg interface{}) *item {
func (item *Item) Push(str string, arg interface{}) *Item {
switch arg := arg.(type) {
case string:
if arg == "" {
@ -36,13 +34,62 @@ func (item *item) push(str string, arg interface{}) *item {
item.list, item.args = append(item.list, str), append(item.args, arg)
return item
}
func (item *item) dump(m *ice.Message) *item {
func (item *Item) Dump(m *ice.Message) *ice.Message {
m.Echo(kit.Join(item.list, ice.SP), item.args...)
m.Echo(ice.NL)
return item
return m
}
type Group struct {
list map[string]*ice.Message
}
func NewGroup(m *ice.Message, arg ...string) *Group {
g := &Group{list: map[string]*ice.Message{}}
for _, k := range arg {
g.list[k] = m.Spawn()
}
return g
}
func AddGroupOption(m *ice.Message, group string, arg ...string) {
for i := 0; i < len(arg)-1; i += 2 {
m.Option(group+"-"+arg[i], arg[i+1])
}
}
func (g *Group) Option(group string, key string, arg ...interface{}) string {
return g.Get(group).Option(group+"-"+key, arg...)
}
func (g *Group) Get(group string) *ice.Message { return g.list[group] }
func (g *Group) DefsArrow(group string, height, width int, arg ...string) *ice.Message { // name
return g.Echo(group, `<defs>
<marker id="%s" markerHeight="%d" markerWidth="%d" refX="0" refY="%d" orient="auto"><polygon points="0 0, %d %d, 0 %d"/></marker>
</defs>`, kit.Select("arrowhead", arg, 0), height, width, height/2, width, height/2, height)
}
func (g *Group) Echo(group string, str string, arg ...interface{}) *ice.Message {
return g.Get(group).Echo(str, arg...)
}
func (g *Group) EchoText(group string, x, y int, text string) *ice.Message {
return g.Echo(group, "<text x=%d y=%d>%s</text>", x, y, text)
}
func (g *Group) EchoRect(group string, height, width, x, y int, arg ...string) *ice.Message { // rx ry
return g.Echo(group, `<rect height=%d width=%d rx=%s ry=%s x=%d y=%d />`, height, width, kit.Select("4", arg, 0), kit.Select("4", arg, 1), x, y)
}
func (g *Group) EchoLine(group string, x1, y1, x2, y2 int) *ice.Message {
return g.Echo(group, "<line x1=%d y1=%d x2=%d y2=%d></line>", x1, y1, x2, y2)
}
func (g *Group) EchoArrowLine(group string, x1, y1, x2, y2 int, arg ...string) *ice.Message { // marker-end
return g.Echo(group, "<line x1=%d y1=%d x2=%d y2=%d marker-end='url(#%s)'></line>", x1, y1, x2, y2, kit.Select("arrowhead", arg, 0))
}
func (g *Group) Dump(m *ice.Message, group string, arg ...string) *Group {
item := NewItem([]string{"<g name=%s"}, group)
for _, k := range kit.Simple(STROKE_DASHARRAY, STROKE_WIDTH, STROKE, FILL, FONT_SIZE, arg) {
item.Push(kit.Format(`%s="%%v"`, k), m.Option(group+"-"+k))
}
item.Echo(">").Dump(m).Copy(g.Get(group)).Echo("</g>")
return g
}
// 图形接口
type Chart interface {
Init(*ice.Message, ...string) Chart
Data(*ice.Message, interface{}) Chart
@ -52,280 +99,19 @@ type Chart interface {
GetWidth(...string) int
}
// 图形基类
type Block struct {
Text string
FontSize int
FontColor string
BackGround string
TextData string
RectData string
Padding int
MarginX int
MarginY int
Height int
Width int
x, y int
}
func (b *Block) Init(m *ice.Message, arg ...string) Chart {
if len(arg) > 0 {
b.Text = arg[0]
}
if len(arg) > 1 {
b.FontSize = kit.Int(arg[1])
}
if len(arg) > 2 {
b.FontColor = arg[2]
}
if len(arg) > 3 {
b.BackGround = arg[3]
}
if len(arg) > 4 {
b.Padding = kit.Int(arg[4])
}
if len(arg) > 5 {
b.MarginX = kit.Int(arg[5])
b.MarginY = kit.Int(arg[5])
}
if len(arg) > 6 {
b.MarginY = kit.Int(arg[6])
}
return b
}
func (b *Block) Data(m *ice.Message, meta interface{}) Chart {
b.Text = kit.Select(b.Text, kit.Value(meta, kit.MDB_TEXT))
kit.Fetch(meta, func(key string, value string) {
switch key {
case FG:
b.TextData += kit.Format("%s='%s' ", FILL, value)
case BG:
b.RectData += kit.Format("%s='%s' ", FILL, value)
}
})
kit.Fetch(kit.Value(meta, "data"), func(key string, value string) {
b.TextData += kit.Format("%s='%s' ", key, value)
})
kit.Fetch(kit.Value(meta, "rect"), func(key string, value string) {
b.RectData += kit.Format("%s='%s' ", key, value)
})
return b
}
func (b *Block) Draw(m *ice.Message, x, y int) Chart {
float := 0
if strings.Contains(m.Option(ice.MSG_USERUA), "iPhone") {
float += 5
}
if m.Option(HIDE_BLOCK) != ice.TRUE {
item := newItem([]string{`<rect height="%d" width="%d" rx="4" ry="4" x="%d" y="%d"`}, b.GetHeight(), b.GetWidth(), x+b.MarginX/2, y+b.MarginY/2)
item.push(`fill="%s"`, b.BackGround).push(`%v`, b.RectData).echo("/>").dump(m)
}
item := newItem([]string{`<text x="%d" y="%d"`}, x+b.GetWidths()/2, y+b.GetHeights()/2+float)
item.push(`stroke="%s"`, b.FontColor).push(`fill="%s"`, b.FontColor).push("%v", b.TextData).push(`>%v</text>`, b.Text).dump(m)
return b
}
func (b *Block) GetHeight(str ...string) int {
if b.Height != 0 {
return b.Height
}
return b.FontSize + b.Padding
}
func (b *Block) GetWidth(str ...string) int {
if b.Width != 0 {
return b.Width
}
s := kit.Select(b.Text, str, 0)
cn := (len(s) - len([]rune(s))) / 2
en := len([]rune(s)) - cn
return cn*b.FontSize + en*b.FontSize*10/16 + b.Padding
}
func (b *Block) GetWidths(str ...string) int {
return b.GetWidth(str...) + b.MarginX
}
func (b *Block) GetHeights(str ...string) int {
return b.GetHeight() + b.MarginY
}
// 框
type Label struct {
data [][]string
max map[int]int
Block
}
func (b *Label) Init(m *ice.Message, arg ...string) Chart {
b.FontSize = kit.Int(m.Option(FONT_SIZE))
b.Padding = kit.Int(m.Option(PADDING))
b.MarginX = kit.Int(m.Option(MARGINX))
b.MarginY = kit.Int(m.Option(MARGINY))
// 解析数据
b.max = map[int]int{}
for _, v := range strings.Split(arg[0], ice.NL) {
l := kit.Split(v, ice.SP, ice.SP)
b.data = append(b.data, l)
for i, v := range l {
switch data := kit.Parse(nil, "", kit.Split(v)...).(type) {
case map[string]interface{}:
v = kit.Select("", data[kit.MDB_TEXT])
}
if w := b.GetWidth(v); w > b.max[i] {
b.max[i] = w
}
}
}
// 计算尺寸
b.Height = len(b.data) * b.GetHeights()
for _, v := range b.max {
b.Width += v + b.MarginX
}
return b
}
func (b *Label) Draw(m *ice.Message, x, y int) Chart {
var item *Block
top := y
for _, line := range b.data {
left := x
for i, text := range line {
// 数据
item = &Block{FontSize: b.FontSize, Padding: b.Padding, MarginX: b.MarginX, MarginY: b.MarginY}
switch data := kit.Parse(nil, "", kit.Split(text)...).(type) {
case map[string]interface{}:
item.Init(m, kit.Select(text, data[kit.MDB_TEXT])).Data(m, data)
default:
item.Init(m, text)
}
// 输出
switch m.Option(COMPACT) {
case "max":
item.Width = b.Width/len(line) - b.MarginX
case ice.TRUE:
default:
item.Width = b.max[i]
}
item.Draw(m, left, top)
left += item.GetWidths()
}
top += item.GetHeights()
}
return b
}
// 链
type Chain struct {
data map[string]interface{}
ship []string
Block
}
func (b *Chain) Init(m *ice.Message, arg ...string) Chart {
b.FontSize = kit.Int(m.Option(FONT_SIZE))
b.Padding = kit.Int(m.Option(PADDING))
b.MarginX = kit.Int(m.Option(MARGINX))
b.MarginY = kit.Int(m.Option(MARGINY))
// 解析数据
m.Option(nfs.CAT_CONTENT, arg[0])
m.Option(lex.SPLIT_SPACE, "\t \n")
m.Option(lex.SPLIT_BLOCK, "\t \n")
b.data = lex.Split(m, "", kit.MDB_TEXT)
// 计算尺寸
b.Height = b.size(m, b.data) * b.GetHeights()
b.Draw(m, 0, 0)
b.Width += 200
m.Set(ice.MSG_RESULT)
return b
}
func (b *Chain) Draw(m *ice.Message, x, y int) Chart {
b.draw(m, b.data, x, y, &b.Block)
m.Echo(`<g stroke=%s stroke-width=%s>`, m.Option(SHIP_STROKE), m.Option(SHIP_STROKE_WIDTH))
defer m.Echo(`</g>`)
for _, ship := range b.ship {
m.Echo(ship)
}
return b
}
func (b *Chain) size(m *ice.Message, root map[string]interface{}) (height int) {
meta := kit.GetMeta(root)
if list, ok := root[kit.MDB_LIST].([]interface{}); ok && len(list) > 0 {
kit.Fetch(root[kit.MDB_LIST], func(index int, value map[string]interface{}) {
height += b.size(m, value)
})
} else {
height = 1
}
meta[HEIGHT] = height
return height
}
func (b *Chain) draw(m *ice.Message, root map[string]interface{}, x, y int, p *Block) int {
meta := kit.GetMeta(root)
b.Height, b.Width = 0, 0
// 当前节点
item := &Block{
BackGround: kit.Select(p.BackGround, meta[BG]),
FontColor: kit.Select(p.FontColor, meta[FG]),
FontSize: p.FontSize,
Padding: p.Padding,
MarginX: p.MarginX,
MarginY: p.MarginY,
}
item.x, item.y = x, y+(kit.Int(meta[HEIGHT])-1)*b.GetHeights()/2
item.Init(m, kit.Format(meta[kit.MDB_TEXT])).Data(m, meta)
item.Draw(m, item.x, item.y)
// 画面尺寸
if item.y+item.GetHeight()+b.MarginY > b.Height {
b.Height = item.y + item.GetHeight() + b.MarginY
}
if item.x+item.GetWidth()+b.MarginX > b.Width {
b.Width = item.x + item.GetWidth() + b.MarginX
}
// 模块连线
if p != nil && p.y != 0 {
x1, y1 := p.x+p.GetWidths()-(p.MarginX+item.MarginX)/4, p.y+p.GetHeights()/2
x4, y4 := item.x+(p.MarginX+item.MarginX)/4, item.y+item.GetHeights()/2
b.ship = append(b.ship, kit.Format(`<path d="M %d,%d Q %d,%d %d,%d T %d %d" fill=none></path>`,
x1, y1, x1+(x4-x1)/4, y1, x1+(x4-x1)/2, y1+(y4-y1)/2, x4, y4))
}
// 递归节点
h, x := 0, x+item.GetWidths()
if kit.Fetch(root[kit.MDB_LIST], func(index int, value map[string]interface{}) {
h += b.draw(m, value, x, y+h, item)
}); h == 0 {
return item.GetHeights()
}
return h
}
var chart_list = map[string]func(m *ice.Message) Chart{}
func AddChart(name string, hand func(m *ice.Message) Chart) {
chart_list[name] = hand
}
func _chart_show(m *ice.Message, kind, text string, arg ...string) {
// 画笔参数
// 默认参数
m.Option(STROKE_WIDTH, "2")
m.Option(STROKE, cli.BLUE)
m.Option(FILL, cli.YELLOW)
m.Option(FONT_SIZE, "24")
m.Option(FONT_FAMILY, "monospace")
// 几何参数
m.Option(PADDING, "10")
m.Option(MARGINX, "10")
m.Option(MARGINY, "10")
chart := chart_list[kind](m)
// 解析参数
@ -346,30 +132,23 @@ func _chart_show(m *ice.Message, kind, text string, arg ...string) {
chart.Draw(m, 0, 0)
m.RenderResult()
}
func AddChart(name string, hand func(m *ice.Message) Chart) {
chart_list[name] = hand
}
const (
FG = "fg"
BG = "bg"
STROKE_WIDTH = "stroke-width"
STROKE = "stroke"
FILL = "fill"
FONT_SIZE = "font-size"
FONT_FAMILY = "font-family"
STROKE_DASHARRAY = "stroke-dasharray"
STROKE_WIDTH = "stroke-width"
STROKE = "stroke"
FILL = "fill"
FONT_SIZE = "font-size"
FONT_FAMILY = "font-family"
PADDING = "padding"
MARGINX = "marginx"
MARGINY = "marginy"
HEIGHT = "height"
WIDTH = "width"
COMPACT = "compact"
HIDE_BLOCK = "hide-block"
SHIP_STROKE = "ship-stroke"
SHIP_STROKE_WIDTH = "ship-stroke-width"
)
const (
LABEL = "label"
@ -379,25 +158,7 @@ const CHART = "chart"
func init() {
Index.Merge(&ice.Context{Commands: map[string]*ice.Command{
CHART: {Name: "chart type=label,chain auto text", Help: "图表", Action: map[string]*ice.Action{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
AddChart(LABEL, func(m *ice.Message) Chart {
return &Label{}
})
AddChart(CHAIN, func(m *ice.Message) Chart {
m.Option(STROKE_WIDTH, "1")
m.Option(FILL, cli.BLUE)
m.Option(MARGINX, "40")
m.Option(MARGINY, "0")
m.Option(COMPACT, ice.TRUE)
m.Option(HIDE_BLOCK, ice.TRUE)
m.Option(SHIP_STROKE, cli.CYAN)
m.Option(SHIP_STROKE_WIDTH, "1")
return &Chain{}
})
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
CHART: {Name: "chart type=label,chain,sequence auto text", Help: "图表", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) > 1 {
_chart_show(m, arg[0], strings.TrimSpace(arg[1]), arg[2:]...)
}

View File

@ -65,6 +65,9 @@ func (m *Message) OptionTemplate() string {
}
}
for _, key := range kit.Split("type,name,text") {
if key == "text" && m.Option("type") == "spark" {
continue
}
if m.Option(key) != "" {
res = append(res, kit.Format(`data-%s="%s"`, key, m.Option(key)))
}