forked from gonum/plot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
align.go
119 lines (104 loc) · 3.52 KB
/
align.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
// Copyright ©2017 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package plot
import (
"fmt"
"math"
"github.com/emptywe/plot/vg"
"github.com/emptywe/plot/vg/draw"
)
// Align returns a two-dimensional row-major array of Canvases which will
// produce tiled plots with DataCanvases that are evenly sized and spaced.
// The arguments to the function are a two-dimensional row-major array
// of plots, a tile configuration, and the canvas to which the tiled
// plots are to be drawn.
func Align(plots [][]*Plot, t draw.Tiles, dc draw.Canvas) [][]draw.Canvas {
o := make([][]draw.Canvas, len(plots))
if len(plots) != t.Rows {
panic(fmt.Errorf("plot: plots rows (%d) != tiles rows (%d)", len(plots), t.Rows))
}
// Create the initial tiles.
for j := 0; j < t.Rows; j++ {
if len(plots[j]) != t.Cols {
panic(fmt.Errorf("plot: plots row %d columns (%d) != tiles columns (%d)", j, len(plots[j]), t.Rows))
}
o[j] = make([]draw.Canvas, len(plots[j]))
for i := 0; i < t.Cols; i++ {
o[j][i] = t.At(dc, i, j)
}
}
type posNeg struct {
p, n float64
}
xSpacing := make([]posNeg, t.Cols)
ySpacing := make([]posNeg, t.Rows)
// Calculate the maximum spacing between data canvases
// for each row and column.
for j, row := range plots {
for i, p := range row {
if p == nil {
continue
}
c := o[j][i]
dataC := p.DataCanvas(o[j][i])
xSpacing[i].n = math.Max(float64(dataC.Min.X-c.Min.X), xSpacing[i].n)
xSpacing[i].p = math.Max(float64(c.Max.X-dataC.Max.X), xSpacing[i].p)
ySpacing[j].n = math.Max(float64(dataC.Min.Y-c.Min.Y), ySpacing[j].n)
ySpacing[j].p = math.Max(float64(c.Max.Y-dataC.Max.Y), ySpacing[j].p)
}
}
// Calculate the total row and column spacing.
var xTotalSpace float64
for _, s := range xSpacing {
xTotalSpace += s.n + s.p
}
xTotalSpace += float64(t.PadX)*float64(len(xSpacing)-1) + float64(t.PadLeft+t.PadRight)
var yTotalSpace float64
for _, s := range ySpacing {
yTotalSpace += s.n + s.p
}
yTotalSpace += float64(t.PadY)*float64(len(ySpacing)-1) + float64(t.PadTop+t.PadBottom)
avgWidth := vg.Length((float64(dc.Max.X-dc.Min.X) - xTotalSpace) / float64(t.Cols))
avgHeight := vg.Length((float64(dc.Max.Y-dc.Min.Y) - yTotalSpace) / float64(t.Rows))
moveVertical := make([]vg.Length, t.Cols)
for j := t.Rows - 1; j >= 0; j-- {
row := plots[j]
var moveHorizontal vg.Length
for i, p := range row {
c := o[j][i]
if p != nil {
dataC := p.DataCanvas(c)
// Adjust the horizontal and vertical spacing between
// canvases to match the maximum for each column and row,
// respectively.
c = draw.Crop(c,
vg.Length(xSpacing[i].n)-(dataC.Min.X-c.Min.X),
c.Max.X-dataC.Max.X-vg.Length(xSpacing[i].p),
vg.Length(ySpacing[j].n)-(dataC.Min.Y-c.Min.Y),
c.Max.Y-dataC.Max.Y-vg.Length(ySpacing[j].p),
)
}
var width, height vg.Length
if p == nil {
width = c.Max.X - c.Min.X - vg.Length(xSpacing[i].p+xSpacing[i].n)
height = c.Max.Y - c.Min.Y - vg.Length(ySpacing[j].p+ySpacing[j].n)
} else {
dataC := p.DataCanvas(c)
width = dataC.Max.X - dataC.Min.X
height = dataC.Max.Y - dataC.Min.Y
}
// Adjust the canvas so that the height and width of the
// DataCanvas is the same for all plots.
o[j][i] = draw.Crop(c,
moveHorizontal,
moveHorizontal+avgWidth-width,
moveVertical[i],
moveVertical[i]+avgHeight-height,
)
moveHorizontal += avgWidth - width
moveVertical[i] += avgHeight - height
}
}
return o
}