// DrawFontExtents draws some text and denotes the // various extents and width with lines. Expects // about a 4x4 inch canvas. func DrawFontExtents(c vg.Canvas) { x, y := vg.Inches(1), vg.Inches(2) str := "Eloquent" font, err := vg.MakeFont("Times-Roman", 18) if err != nil { panic(err) } width := font.Width(str) ext := font.Extents() des := ext.Descent asc := ext.Ascent c.FillString(font, x, y, str) // baseline path := vg.Path{} path.Move(x, y) path.Line(x+width, y) c.Stroke(path) // descent c.SetColor(color.RGBA{G: 255, A: 255}) path = vg.Path{} path.Move(x, y+des) path.Line(x+width, y+des) c.Stroke(path) // ascent c.SetColor(color.RGBA{B: 255, A: 255}) path = vg.Path{} path.Move(x, y+asc) path.Line(x+width, y+asc) c.Stroke(path) }
// NewLabels returns a new Labels using the DefaultFont and // the DefaultFontSize. func NewLabels(d interface { XYer Labeller }) (*Labels, error) { xys, err := CopyXYs(d) if err != nil { return nil, err } if d.Len() != len(xys) { return nil, errors.New("Number of points does not match the number of labels") } strs := make([]string, d.Len()) for i := range strs { strs[i] = d.Label(i) } fnt, err := vg.MakeFont(DefaultFont, DefaultFontSize) if err != nil { return nil, err } return &Labels{ XYs: xys, Labels: strs, TextStyle: plot.TextStyle{Font: fnt}, }, nil }
// New returns a new plot with some reasonable // default settings. func New() (*Plot, error) { titleFont, err := vg.MakeFont(DefaultFont, 12) if err != nil { return nil, err } x, err := makeAxis() if err != nil { return nil, err } y, err := makeAxis() if err != nil { return nil, err } legend, err := makeLegend() if err != nil { return nil, err } p := &Plot{ BackgroundColor: color.White, X: x, Y: y, Legend: legend, } p.Title.TextStyle = TextStyle{ Color: color.Black, Font: titleFont, } return p, nil }
func init() { var err error plot.DefaultFont = "Helvetica" defaultFont, err = vg.MakeFont("Helvetica", 6) if err != nil { fmt.Println(err.Error()) } }
// makeLegend returns a legend with the default // parameter settings. func makeLegend() (Legend, error) { font, err := vg.MakeFont(defaultFont, vg.Points(12)) if err != nil { return Legend{}, err } return Legend{ ThumbnailWidth: vg.Points(20), TextStyle: TextStyle{Font: font}, }, nil }
// makeAxis returns a default Axis. // // The default range is (∞, ∞), and thus any finite // value is less than Min and greater than Max. func makeAxis() (Axis, error) { labelFont, err := vg.MakeFont(DefaultFont, vg.Points(12)) if err != nil { return Axis{}, err } tickFont, err := vg.MakeFont(DefaultFont, vg.Points(10)) if err != nil { return Axis{}, err } a := Axis{ Min: math.Inf(1), Max: math.Inf(-1), LineStyle: LineStyle{ Color: color.Black, Width: vg.Points(0.5), }, Padding: vg.Points(5), Scale: LinearScale, } a.Label.TextStyle = TextStyle{ Color: color.Black, Font: labelFont, } a.Tick.Label = TextStyle{ Color: color.Black, Font: tickFont, } a.Tick.LineStyle = LineStyle{ Color: color.Black, Width: vg.Points(0.5), } a.Tick.Length = vg.Points(8) a.Tick.Marker = DefaultTicks return a, nil }
func main() { ps[0].plot = linesPlot() ps[1].plot = histPlot() var err error font, err = vg.MakeFont("Times-Roman", vg.Points(12)) if err != nil { panic(err) } win, err := x11.NewWindow() if err != nil { panic(err) } drawPlots(win.Screen()) win.FlushImage() if cpuProfile != "" { f, err := os.Create(cpuProfile) if err != nil { panic(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if memProfile != "" { f, err := os.Create(memProfile) if err != nil { panic(err) } pprof.WriteHeapProfile(f) f.Close() } events := win.EventChan() for ev := range events { if m, ok := ev.(ui.MouseEvent); ok && m.Buttons == 1 { winHeight := 600 // hard-coded for ui/x11… p, x, y := dataCoord(m.Loc.X, winHeight-m.Loc.Y) if p >= 0 { str := fmt.Sprintf("plot: %d, coord: %g, %g\n", p, x, y) crosshair(win.Screen(), m.Loc.X, winHeight-m.Loc.Y, str) win.FlushImage() } } } }
// NewLabels returns a new Labels using // the DefaultFont and the DefaultFontSize. // An error may be returned if there is an // error loading the default font. func NewLabels(d interface { XYer Labeller }) (*Labels, error) { fnt, err := vg.MakeFont(DefaultFont, DefaultFontSize) if err != nil { return nil, err } strs := make([]string, d.Len()) for i := range strs { strs[i] = d.Label(i) } return &Labels{ XYs: CopyXYs(d), Labels: strs, TextStyle: plot.TextStyle{Font: fnt}, }, nil }
// DrawFonts draws some text in all of the various // fonts along with a box to make sure that their // sizes are computed correctly. func DrawFonts(c vg.Canvas) { y := vg.Points(0) var fonts []string for fname := range vg.FontMap { fonts = append(fonts, fname) } sort.Strings(fonts) for _, fname := range fonts { font, err := vg.MakeFont(fname, 20) if err != nil { panic(err) } w := font.Width(fname + "Xqg") h := font.Extents().Ascent c.FillString(font, 0, y-font.Extents().Descent, fname+"Xqg") fmt.Println(fname) var path vg.Path path.Move(0, y+h) path.Line(w, y+h) path.Line(w, y) path.Line(0, y) path.Close() c.Stroke(path) path = vg.Path{} c.SetColor(color.RGBA{B: 255, A: 255}) c.SetLineDash([]vg.Length{vg.Points(5), vg.Points(3)}, 0) path.Move(0, y-font.Extents().Descent) path.Line(w, y-font.Extents().Descent) c.Stroke(path) c.SetColor(color.Black) c.SetLineDash([]vg.Length{}, 0) y += h } }
func mouseTracks(scores []rings.Scorer, diameter vg.Length, lenRange int) ([]plot.Plotter, error) { var p []plot.Plotter radius := diameter / 2 // Relative sizes. const ( label = 117. / 110. karyotypeInner = 100. / 110. karyotypeOuter = 1. heatInner = 30. / 110. heatOuter = 75. / 110. traceInner = 80. / 110. traceOuter = 95. / 110. large = 7. / 110. small = 2. / 110. ) sty := plotter.DefaultLineStyle sty.Width /= 2 chr := make([]feat.Feature, len(mm10.Chromosomes)) for i, c := range mm10.Chromosomes { chr[i] = c } mm, err := rings.NewGappedBlocks( chr, rings.Arc{rings.Complete / 4 * rings.CounterClockwise, rings.Complete * rings.Clockwise}, radius*karyotypeInner, radius*karyotypeOuter, 0.005, ) if err != nil { return nil, err } mm.LineStyle = sty p = append(p, mm) bands := make([]feat.Feature, len(mm10.Bands)) cens := make([]feat.Feature, 0, len(mm10.Chromosomes)) for i, b := range mm10.Bands { bands[i] = colorBand{b} s := b.Start() // This condition depends on p -> q sort order in the $karyotype.Bands variable. // All standard genome packages follow this, though here the test is more general than // actually required since mm is telocentric. if b.Band[0] == 'q' && (s == 0 || mm10.Bands[i-1].Band[0] == 'p') { cens = append(cens, colorBand{&genome.Band{Band: "cen", Desc: "Band", StartPos: s, EndPos: s, Giemsa: "acen", Chr: b.Location()}}) } } b, err := rings.NewBlocks(bands, mm, radius*karyotypeInner, radius*karyotypeOuter) if err != nil { return nil, fmt.Errorf("bands: %v", err) } p = append(p, b) c, err := rings.NewBlocks(cens, mm, radius*karyotypeInner, radius*karyotypeOuter) if err != nil { return nil, fmt.Errorf("centromeres: %v", err) } p = append(p, c) font, err := vg.MakeFont("Helvetica", radius*large) if err != nil { return nil, err } lb, err := rings.NewLabels(mm, radius*label, rings.NameLabels(mm.Set)...) if err != nil { return nil, err } lb.TextStyle = plot.TextStyle{Color: color.Gray16{0}, Font: font} p = append(p, lb) s, err := rings.NewScores(scores, mm, radius*heatInner, radius*heatOuter, &rings.Heat{Palette: palette.Heat(10, 1).Colors()}, ) if err != nil { return nil, err } p = append(p, s) traces := make([]rings.Scorer, len(scores)) for i, s := range scores { traces[i] = &tfs{s.(*fs)} } smallFont, err := vg.MakeFont("Helvetica", radius*small) if err != nil { return nil, err } t, err := rings.NewScores(traces, mm, radius*traceInner, radius*traceOuter, &rings.Trace{ LineStyles: func() []plot.LineStyle { ls := []plot.LineStyle{sty, sty} for i, c := range brewer.Set1[3].Colors()[:len(ls)] { nc := color.NRGBAModel.Convert(c).(color.NRGBA) nc.A = 0x80 ls[i].Color = nc } return ls }(), Join: true, Axis: &rings.Axis{ Angle: rings.Complete / 4, Grid: plotter.DefaultGridLineStyle, LineStyle: sty, Tick: rings.TickConfig{ Marker: plot.DefaultTicks, LineStyle: sty, Length: 2, Label: plot.TextStyle{Color: color.Gray16{0}, Font: smallFont}, }, }, }, ) if err != nil { return nil, err } p = append(p, t) return p, nil }
func boxplot(path string, sets []set) error { var ( fiveEnds = make([]plotter.Values, len(sets)) threeEnds = make([]plotter.Values, len(sets)) err error ln int ) for i := range sets { fiveEnds[i], err = plotter.CopyValues(&normalised{vals: sets[i].fiveEnd}) if err != nil { return err } threeEnds[i], err = plotter.CopyValues(&normalised{vals: sets[i].threeEnd}) if err != nil { return err } if i == 0 { ln = fiveEnds[i].Len() } if fiveEnds[i].Len() != threeEnds[i].Len() || fiveEnds[i].Len() != ln { return errors.New("missing values") } } font, err := vg.MakeFont("Helvetica", 10) if err != nil { return err } titleFont, err := vg.MakeFont("Helvetica", 12) if err != nil { return err } style := plot.TextStyle{Color: color.Gray{0}, Font: font} p, err := plot.New() if err != nil { return err } p.Title.Text = titles[filter] p.Title.TextStyle = plot.TextStyle{Color: color.Gray{0}, Font: titleFont} p.X.Label.Text = "Length Offset" p.Y.Label.Text = "Relative Frequency" p.X.Label.TextStyle = style p.Y.Label.TextStyle = style p.X.Tick.Label = style p.Y.Tick.Label = style p.Legend.TextStyle = style type boxPair struct{ five, three *plotter.BoxPlot } var boxes []boxPair for i := 0; i < ln; i++ { fiveEnd := make(plotter.Values, len(sets)) threeEnd := make(plotter.Values, len(sets)) for j := range sets { fiveEnd[j] = fiveEnds[j][i] threeEnd[j] = threeEnds[j][i] } boxFivePrime, err := plotter.NewBoxPlot(1, float64(i), fiveEnd) // A non-zero width is required to prevent the creation failing. if err != nil { return err } boxFivePrime.MedianStyle.Width = 0.5 boxFivePrime.BoxStyle.Width = 0.75 boxFivePrime.BoxStyle.Color = plotutil.Color(0) boxThreePrime, err := plotter.NewBoxPlot(1, float64(i), threeEnd) // A non-zero width is required to prevent the creation failing. if err != nil { return err } boxThreePrime.MedianStyle.Width = 0.5 boxThreePrime.BoxStyle.Width = 0.75 boxThreePrime.BoxStyle.Color = plotutil.Color(1) boxes = append(boxes, boxPair{boxFivePrime, boxThreePrime}) p.Add(boxFivePrime, boxThreePrime) } p.Legend.Add("5'-end", &plotter.BarChart{Color: plotutil.Color(0)}) p.Legend.Add("3'-end", &plotter.BarChart{Color: plotutil.Color(1)}) p.Legend.Top = true p.NominalX(func() []string { n := make([]string, ln) for i := 0; i < ln; i++ { if v := i - maxLength; v%5 == 0 { n[i] = fmt.Sprint(v) } } return n }()...) p.X.Width = 0.5 p.X.Tick.Width = 0.5 p.X.Tick.Length = 8 p.Add(&plotter.Grid{Vertical: plotter.DefaultGridLineStyle}) c := vgsvg.New(vg.Centimeters(19), vg.Centimeters(10)) da := plot.MakeDrawArea(c) trX, _ := p.Transforms(&da) w := ((trX(float64(2*maxLength)) - trX(float64(0))) / vg.Length(2*maxLength)) / 3 for _, b := range boxes { b.five.Width = w b.five.Offset = -w / 2 b.three.Width = w b.three.Offset = w / 2 } p.Draw(da) f, err := os.Create(decorate(path, "boxplot.svg", filter)) if err != nil { return err } defer f.Close() _, err = c.WriteTo(f) return err }
func barchart(path string, data set) error { font, err := vg.MakeFont("Helvetica", 10) if err != nil { return err } titleFont, err := vg.MakeFont("Helvetica", 12) if err != nil { return err } style := plot.TextStyle{Color: color.Gray{0}, Font: font} p, err := plot.New() if err != nil { return err } p.Title.Text = titles[filter] p.Title.TextStyle = plot.TextStyle{Color: color.Gray{0}, Font: titleFont} p.X.Label.Text = "Length Offset" p.Y.Label.Text = "Relative Frequency" p.X.Label.TextStyle = style p.Y.Label.TextStyle = style p.X.Tick.Label = style p.Y.Tick.Label = style p.Legend.TextStyle = style barsFivePrime, err := plotter.NewBarChart(&normalised{vals: data.fiveEnd}, 1) // A non-zero width is required to prevent the creation failing. if err != nil { return err } barsFivePrime.LineStyle.Width = vg.Length(0) barsFivePrime.Color = plotutil.Color(0) barsThreePrime, err := plotter.NewBarChart(&normalised{vals: data.threeEnd}, 1) // A non-zero width is required to prevent the creation failing. if err != nil { return err } barsThreePrime.LineStyle.Width = vg.Length(0) barsThreePrime.Color = plotutil.Color(1) p.Add(barsFivePrime, barsThreePrime) p.Legend.Add("5'-end", barsFivePrime) p.Legend.Add("3'-end", barsThreePrime) p.Legend.Top = true p.NominalX(func() []string { n := make([]string, len(data.fiveEnd)) for i := range data.fiveEnd { if v := i - maxLength; v%5 == 0 { n[i] = fmt.Sprint(v) } } return n }()...) c := vgsvg.New(vg.Centimeters(19), vg.Centimeters(10)) da := plot.MakeDrawArea(c) trX, _ := p.Transforms(&da) w := ((trX(float64(2*maxLength)) - trX(float64(0))) / vg.Length(2*maxLength)) / 3 barsFivePrime.Width = w barsFivePrime.Offset = -w / 2 barsThreePrime.Width = w barsThreePrime.Offset = w / 2 p.Draw(da) f, err := os.Create(decorate(path, "barchart.svg", filter)) if err != nil { return err } defer f.Close() _, err = c.WriteTo(f) return err }