1
2
3
4
5 package plotter
6
7 import (
8 "math"
9
10 "gonum.org/v1/plot"
11 "gonum.org/v1/plot/vg"
12 "gonum.org/v1/plot/vg/draw"
13 )
14
15
16 var DefaultCapWidth = vg.Points(5)
17
18
19
20
21 type YErrorBars struct {
22 XYs
23
24
25 YErrors
26
27
28 draw.LineStyle
29
30
31
32 CapWidth vg.Length
33 }
34
35
36
37
38
39
40 func NewYErrorBars(yerrs interface {
41 XYer
42 YErrorer
43 }) (*YErrorBars, error) {
44
45 errors := make(YErrors, yerrs.Len())
46 for i := range errors {
47 errors[i].Low, errors[i].High = yerrs.YError(i)
48 if err := CheckFloats(errors[i].Low, errors[i].High); err != nil {
49 return nil, err
50 }
51 }
52 xys, err := CopyXYs(yerrs)
53 if err != nil {
54 return nil, err
55 }
56
57 return &YErrorBars{
58 XYs: xys,
59 YErrors: errors,
60 LineStyle: DefaultLineStyle,
61 CapWidth: DefaultCapWidth,
62 }, nil
63 }
64
65
66 func (e *YErrorBars) Plot(c draw.Canvas, p *plot.Plot) {
67 trX, trY := p.Transforms(&c)
68 for i, err := range e.YErrors {
69 x := trX(e.XYs[i].X)
70 ylow := trY(e.XYs[i].Y - math.Abs(err.Low))
71 yhigh := trY(e.XYs[i].Y + math.Abs(err.High))
72
73 bar := c.ClipLinesY([]vg.Point{{X: x, Y: ylow}, {X: x, Y: yhigh}})
74 c.StrokeLines(e.LineStyle, bar...)
75 e.drawCap(&c, x, ylow)
76 e.drawCap(&c, x, yhigh)
77 }
78 }
79
80
81 func (e *YErrorBars) drawCap(c *draw.Canvas, x, y vg.Length) {
82 if !c.Contains(vg.Point{X: x, Y: y}) {
83 return
84 }
85 c.StrokeLine2(e.LineStyle, x-e.CapWidth/2, y, x+e.CapWidth/2, y)
86 }
87
88
89 func (e *YErrorBars) DataRange() (xmin, xmax, ymin, ymax float64) {
90 xmin, xmax = Range(XValues{e})
91 ymin = math.Inf(1)
92 ymax = math.Inf(-1)
93 for i, err := range e.YErrors {
94 y := e.XYs[i].Y
95 ylow := y - math.Abs(err.Low)
96 yhigh := y + math.Abs(err.High)
97 ymin = math.Min(math.Min(math.Min(ymin, y), ylow), yhigh)
98 ymax = math.Max(math.Max(math.Max(ymax, y), ylow), yhigh)
99 }
100 return
101 }
102
103
104 func (e *YErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
105 rect := vg.Rectangle{
106 Min: vg.Point{
107 X: -e.CapWidth / 2,
108 Y: -e.LineStyle.Width / 2,
109 },
110 Max: vg.Point{
111 X: +e.CapWidth / 2,
112 Y: +e.LineStyle.Width / 2,
113 },
114 }
115 var bs []plot.GlyphBox
116 for i, err := range e.YErrors {
117 x := plt.X.Norm(e.XYs[i].X)
118 y := e.XYs[i].Y
119 bs = append(bs,
120 plot.GlyphBox{X: x, Y: plt.Y.Norm(y - err.Low), Rectangle: rect},
121 plot.GlyphBox{X: x, Y: plt.Y.Norm(y + err.High), Rectangle: rect})
122 }
123 return bs
124 }
125
126
127
128
129 type XErrorBars struct {
130 XYs
131
132
133 XErrors
134
135
136 draw.LineStyle
137
138
139
140 CapWidth vg.Length
141 }
142
143
144
145
146
147
148 func NewXErrorBars(xerrs interface {
149 XYer
150 XErrorer
151 }) (*XErrorBars, error) {
152
153 errors := make(XErrors, xerrs.Len())
154 for i := range errors {
155 errors[i].Low, errors[i].High = xerrs.XError(i)
156 if err := CheckFloats(errors[i].Low, errors[i].High); err != nil {
157 return nil, err
158 }
159 }
160 xys, err := CopyXYs(xerrs)
161 if err != nil {
162 return nil, err
163 }
164
165 return &XErrorBars{
166 XYs: xys,
167 XErrors: errors,
168 LineStyle: DefaultLineStyle,
169 CapWidth: DefaultCapWidth,
170 }, nil
171 }
172
173
174 func (e *XErrorBars) Plot(c draw.Canvas, p *plot.Plot) {
175 trX, trY := p.Transforms(&c)
176 for i, err := range e.XErrors {
177 y := trY(e.XYs[i].Y)
178 xlow := trX(e.XYs[i].X - math.Abs(err.Low))
179 xhigh := trX(e.XYs[i].X + math.Abs(err.High))
180
181 bar := c.ClipLinesX([]vg.Point{{X: xlow, Y: y}, {X: xhigh, Y: y}})
182 c.StrokeLines(e.LineStyle, bar...)
183 e.drawCap(&c, xlow, y)
184 e.drawCap(&c, xhigh, y)
185 }
186 }
187
188
189 func (e *XErrorBars) drawCap(c *draw.Canvas, x, y vg.Length) {
190 if !c.Contains(vg.Point{X: x, Y: y}) {
191 return
192 }
193 c.StrokeLine2(e.LineStyle, x, y-e.CapWidth/2, x, y+e.CapWidth/2)
194 }
195
196
197 func (e *XErrorBars) DataRange() (xmin, xmax, ymin, ymax float64) {
198 ymin, ymax = Range(YValues{e})
199 xmin = math.Inf(1)
200 xmax = math.Inf(-1)
201 for i, err := range e.XErrors {
202 x := e.XYs[i].X
203 xlow := x - math.Abs(err.Low)
204 xhigh := x + math.Abs(err.High)
205 xmin = math.Min(math.Min(math.Min(xmin, x), xlow), xhigh)
206 xmax = math.Max(math.Max(math.Max(xmax, x), xlow), xhigh)
207 }
208 return
209 }
210
211
212 func (e *XErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
213 rect := vg.Rectangle{
214 Min: vg.Point{
215 X: -e.LineStyle.Width / 2,
216 Y: -e.CapWidth / 2,
217 },
218 Max: vg.Point{
219 X: +e.LineStyle.Width / 2,
220 Y: +e.CapWidth / 2,
221 },
222 }
223 var bs []plot.GlyphBox
224 for i, err := range e.XErrors {
225 x := e.XYs[i].X
226 y := plt.Y.Norm(e.XYs[i].Y)
227 bs = append(bs,
228 plot.GlyphBox{X: plt.X.Norm(x - err.Low), Y: y, Rectangle: rect},
229 plot.GlyphBox{X: plt.X.Norm(x + err.High), Y: y, Rectangle: rect})
230 }
231 return bs
232 }
233
View as plain text