-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlayout.go
138 lines (109 loc) · 2.77 KB
/
layout.go
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package xstack
import (
"errors"
"io"
"math"
"strconv"
"strings"
)
const (
// special case: zero offset
// with no byte prefix
offsetZeroSign = '0'
// sum separator between offsets on same axis
offsetSumSign = '+'
// separator between two total offsets for
// different axis
offsetsSep = '_'
// separator between two cells
cellsSep = '|'
// widht char prefix. width for x axis.
wChar = 'w'
// htight char prefix. height for y axis.
hChar = 'h'
)
// ErrZeroN indicates that passed n to Layout function is 0
// and cann't be processed.
var ErrZeroN = errors.New("layout for 0 elements couldn't be calculated")
// writeOffsetTo writes offset to w.
// Produces data like this:
// 0
// w0
// h0+h1+h2+h3
func writeOffsetTo(w io.Writer, c byte, pos uint64) error {
if pos == 0 {
_, err := w.Write([]byte{offsetZeroSign})
return err
}
for i := uint64(0); i < pos; i++ {
if _, err := w.Write([]byte{c}); err != nil {
return err
}
if _, err := w.Write([]byte(strconv.FormatUint(i, 10))); err != nil {
return err
}
// if not last element
if i != pos-1 {
if _, err := w.Write([]byte{offsetSumSign}); err != nil {
return err
}
}
}
return nil
}
func layoutTo(w io.Writer, n uint64, align Align) error {
if n == 0 {
return ErrZeroN
}
cols := uint64(math.Trunc(math.Sqrt(float64(n))))
rows := uint64(math.Ceil(float64(n) / float64(cols)))
if align == AlignHorizontal {
cols = rows
rows = cols
}
// remaining items to place in grid
remaining := n
for row := uint64(0); row < rows; row++ {
for col := uint64(0); col < cols && remaining != 0; col++ {
if err := writeOffsetTo(w, wChar, col); err != nil {
return err
}
if _, err := w.Write([]byte{offsetsSep}); err != nil {
return err
}
if err := writeOffsetTo(w, hChar, row); err != nil {
return err
}
remaining--
if remaining != 0 {
if _, err := w.Write([]byte{cellsSep}); err != nil {
return err
}
}
}
}
return nil
}
// LayoutTo writes xstack layout description to w.
// Where n is total number of elements in grid.
func LayoutTo(w io.Writer, n uint64) error {
return layoutTo(w, n, AlignHorizontal)
}
// LayoutToWithAlign is same as LayoutTo but with custom align.
func LayoutToWithAlign(w io.Writer, n uint64, align Align) error {
return layoutTo(w, n, align)
}
// Layout returns xstack layout description as string.
// Is's just helper function - wrapper around LayoutTo with
// predefined AlignHorizontal.
func Layout(n uint64) (string, error) {
return LayoutWithAlign(n, AlignHorizontal)
}
// LayoutWithAlign is same as Layout but with custom align.
func LayoutWithAlign(n uint64, align Align) (string, error) {
var b strings.Builder
if err := layoutTo(&b, n, align); err != nil {
return "", err
}
return b.String(), nil
}