glot
glot copied to clipboard
Improvement ideas
I have been playing around with the library a bit and made some changes over time. These are just ideas I wanted to share and see if there's much interest. Most of them are half-baked, as such no PR yet, but if you have interest in these, I am sure I can bring them closer to being useful.
diff --git c/common.go w/common.go
index e57bee1..712b8c4 100644
--- c/common.go
+++ w/common.go
@@ -139,7 +139,7 @@ func (plot *Plot) SetLogscale(axis string, base int) error {
// plot.AddPointGroup("Sample 1", "lines", []float64{2, 3, 4, 1})
// plot.SetTitle("Test Results")
// plot.SetYrange(-2,2)
-func (plot *Plot) SetYrange(start int, end int) error {
+func (plot *Plot) SetYrange(start, end int) error {
return plot.Cmd(fmt.Sprintf("set yrange [%d:%d]", start, end))
}
@@ -174,7 +174,22 @@ func (plot *Plot) SavePlot(filename string) (err error) {
if plot.nplots == 0 {
return &gnuplotError{fmt.Sprintf("This plot has 0 curves and therefore its a redundant plot and it can't be printed.")}
}
- outputFormat := "set terminal " + plot.format
+ outputFormat := "set terminal " + plot.format.String()
+ fmt.Printf("outputFormat: %s\n", outputFormat)
+ plot.CheckedCmd(outputFormat)
+ outputFileCommand := "set output " + "'" + filename + "'"
+ fmt.Printf("outputFileCommand: %s\n", outputFileCommand)
+ plot.CheckedCmd(outputFileCommand)
+ plot.CheckedCmd("replot ")
+ return nil
+}
+
+func (plot *Plot) SaveHistogram(filename string) (err error) {
+ if plot.nplots == 0 {
+ return &gnuplotError{fmt.Sprintf("This plot has 0 curves and therefore its a redundant plot and it can't be printed.")}
+ }
+ plot.style = Histogram
+ outputFormat := "set terminal " + plot.format.String()
plot.CheckedCmd(outputFormat)
outputFileCommand := "set output" + "'" + filename + "'"
plot.CheckedCmd(outputFileCommand)
@@ -196,9 +211,9 @@ func (plot *Plot) SavePlot(filename string) (err error) {
// plot.SetFormat("pdf")
// plot.SavePlot("1.pdf")
// NOTE: png is default format for saving files.
-func (plot *Plot) SetFormat(newformat string) error {
- allowed := []string{
- "png", "pdf"}
+func (plot *Plot) SetFormat(newformat PlotFormat) error {
+ allowed := []PlotFormat{
+ Png, Pdf}
for _, s := range allowed {
if newformat == s {
plot.format = newformat
@@ -210,3 +225,32 @@ func (plot *Plot) SetFormat(newformat string) error {
err := &gnuplotError{fmt.Sprintf("invalid format '%s'", newformat)}
return err
}
+
+// SetLineStyleBrewerQualitative1 sets color palette based on
+// ColorBrewer Qualitative Paired Scheme with 9 colors.
+func (plot *Plot) SetLineStyleBrewerQualitative1() {
+ pal := `
+ # line styles
+ set style line 1 lt 1 lc rgb '#a6cee3' # light blue
+ set style line 2 lt 1 lc rgb '#1f78b4' # dark blue
+ set style line 3 lt 1 lc rgb '#b2df8a' # light green
+ set style line 4 lt 1 lc rgb '#33a02c' # dark green
+ set style line 5 lt 1 lc rgb '#fb9a99' # light red
+ set style line 6 lt 1 lc rgb '#e31a1c' # dark red
+ set style line 7 lt 1 lc rgb '#fdbf6f' # light orange
+ set style line 8 lt 1 lc rgb '#ff7f00' # dark orange
+ set style line 9 lt 1 lc rgb '#cab2d6' # light purple
+
+ # palette
+ set palette defined ( 0 '#a6cee3',\
+ 1 '#1f78b4',\
+ 2 '#b2df8a',\
+ 3 '#33a02c',\
+ 4 '#fb9a99',\
+ 5 '#e31a1c',\
+ 6 '#fdbf6f',\
+ 7 '#ff7f00',\
+ 8 '#cab2d6')`
+ plot.Cmd(pal)
+ plot.Execute()
+}
\ No newline at end of file
diff --git c/common_test.go w/common_test.go
index 0c84fc7..d31f97f 100644
--- c/common_test.go
+++ w/common_test.go
@@ -2,24 +2,123 @@ package glot
import "testing"
-func TestSetLabels(t *testing.T) {
+var Args = PlotArgs{
+ Debug: true,
+ Persist: true,
+}
+
+func TestSetLabels3Dims(t *testing.T) {
dimensions := 3
- persist := false
- debug := false
- plot, _ := NewPlot(dimensions, persist, debug)
+ plot, _ := NewPlot(dimensions, Args)
+ err := plot.SetLabels()
+ if err == nil {
+ t.Error("SetLabels raises error when empty string is passed")
+ }
+}
+
+func TestSetLabels2Dims(t *testing.T) {
+ dimensions := 2
+ plot, _ := NewPlot(dimensions, Args)
err := plot.SetLabels()
if err == nil {
t.Error("SetLabels raises error when empty string is passed")
}
}
-func TestSetFormat(t *testing.T) {
+func TestSetLabels1Dims(t *testing.T) {
+ dimensions := 1
+ plot, _ := NewPlot(dimensions, Args)
+ err := plot.SetLabels()
+ if err == nil {
+ t.Error("SetLabels raises error when empty string is passed")
+ }
+}
+
+func TestSetFormat3Dims(t *testing.T) {
dimensions := 3
- persist := false
- debug := false
- plot, _ := NewPlot(dimensions, persist, debug)
- err := plot.SetFormat("tls")
+
+ // Test unsupported formats
+ plot, _ := NewPlot(dimensions, Args)
+ err := plot.SetFormat(0)
if err == nil {
t.Error("SetLabels raises error when non-supported format is passed as an argument.")
}
+
+ // Test supported formats
+ plot, _ = NewPlot(dimensions, Args)
+ err = plot.SetFormat(Pdf)
+ if err != nil {
+ t.Error("SetLabels failed to set supported format (Pdf).")
+ }
+ err = plot.SetFormat(Png)
+ if err != nil {
+ t.Error("SetLabels failed to set supported format (Png).")
+ }
}
+
+func TestSetFormat2Dims(t *testing.T) {
+ dimensions := 2
+
+ // Test unsupported formats
+ plot, _ := NewPlot(dimensions, Args)
+ err := plot.SetFormat(0)
+ if err == nil {
+ t.Error("SetLabels raises error when non-supported format is passed as an argument.")
+ }
+
+ // Test supported formats
+ plot, _ = NewPlot(dimensions, Args)
+ err = plot.SetFormat(Pdf)
+ if err != nil {
+ t.Error("SetLabels failed to set supported format (Pdf).")
+ }
+ err = plot.SetFormat(Png)
+ if err != nil {
+ t.Error("SetLabels failed to set supported format (Png).")
+ }
+}
+
+func TestSetFormat1Dims(t *testing.T) {
+ dimensions := 1
+
+ // Test unsupported formats
+ plot, _ := NewPlot(dimensions, Args)
+ err := plot.SetFormat(0)
+ if err == nil {
+ t.Error("SetLabels raises error when non-supported format is passed as an argument.")
+ }
+
+ // Test supported formats
+ plot, _ = NewPlot(dimensions, Args)
+ err = plot.SetFormat(Pdf)
+ if err != nil {
+ t.Error("SetLabels failed to set supported format (Pdf).")
+ }
+ err = plot.SetFormat(Png)
+ if err != nil {
+ t.Error("SetLabels failed to set supported format (Png).")
+ }
+}
+
+func TestLinesStyleBrewerQualitative1(t *testing.T) {
+ dimensions := 1
+
+ // Test unsupported formats
+ plot1, _ := NewPlot(dimensions, Args)
+ plot2, _ := NewPlot(dimensions, Args)
+ t1 := `
+ set multiplot layout 2,2 ;
+ plot sin(x) ls 1 ; plot sin(x/2) ls 2 ;
+ plot sin(x/4) ls 3 ; plot cos(x/2) ls 4
+ `
+ t2 := `
+ set multiplot layout 2,2 ;
+ plot sin(x) ls 5 ; plot sin(x/2) ls 6 ;
+ plot sin(x/4) ls 7 ; plot cos(x/2) ls 8
+ `
+ plot1.SetLineStyleBrewerQualitative1()
+ plot2.SetLineStyleBrewerQualitative1()
+ plot1.Cmd(t1)
+ plot2.Cmd(t2)
+
+}
\ No newline at end of file
diff --git c/core.go w/core.go
index 5146ff4..701774d 100644
--- c/core.go
+++ w/core.go
@@ -73,7 +73,9 @@ func (plot *Plot) Cmd(format string, a ...interface{}) error {
if plot.debug {
//buf := new(bytes.Buffer)
//io.Copy(buf, plot.proc.handle.Stdout)
- fmt.Printf("cmd> %v", cmd)
+ fmt.Printf("fmt> %v\n", format)
+ fmt.Printf("a> %v\n", a)
+ fmt.Printf("cmd> %v\n", cmd)
fmt.Printf("res> %v\n", n)
}
return err
diff --git c/core_test.go w/core_test.go
index 077ff59..8e8cc5f 100644
--- c/core_test.go
+++ w/core_test.go
@@ -1,6 +1,9 @@
package glot
-import "testing"
+import (
+ "math"
+ "testing"
+)
func TestMin(t *testing.T) {
var v int
@@ -9,3 +12,31 @@ func TestMin(t *testing.T) {
t.Error("Expected 1, got ", v)
}
}
+
+func TestCmd(t *testing.T) {
+ base2n := func(n float64) []float64 {
+ res := []float64{}
+ for i := 0.; i < n; i++ {
+ res = append(res, math.Pow(2, i))
+ }
+ return res
+ }
+ var testArgs = PlotArgs{
+ Debug: true,
+ Format: Pdf,
+ Persist: true,
+ Style: Points,
+ Command: "plot ",
+ }
+ var pg = &PointGroup{
+ name: "TestCmd",
+ dimensions: 1,
+ style: Points,
+ castedData: base2n(9),
+ }
+ p, err := NewPlot(1, testArgs)
+ if err != nil {
+ t.Errorf("NewPlot failed creation with: %v", err)
+ }
+ p.plotX(pg)
+}
diff --git c/function.go w/function.go
index 29fee9b..ae371bd 100644
--- c/function.go
+++ w/function.go
@@ -30,7 +30,7 @@ type Func3d func(x float64, y float64) float64
// pointsX :=> The x Value of the points to be plotted. y = func(x) is plotted on the curve.
// style :=> Style of the curve
// NOTE: Currently only float64 type is supported for this function
-func (plot *Plot) AddFunc2d(name string, style string, x []float64, fct Func2d) error {
+func (plot *Plot) AddFunc2d(name string, style PointStyle, x []float64, fct Func2d) error {
y := make([]float64, len(x))
for index := range x {
y[index] = fct(x[index])
@@ -67,7 +67,7 @@ func (plot *Plot) AddFunc2d(name string, style string, x []float64, fct Func2d)
// style :=> Style of the curve
// pointsX :=> The x Value of the points to be plotted. y = func(x) is plotted on the curve.
// NOTE: Currently only float64 type is supported for this function
-func (plot *Plot) AddFunc3d(name string, style string, x []float64, y []float64, fct Func3d) error {
+func (plot *Plot) AddFunc3d(name string, style PointStyle, x []float64, y []float64, fct Func3d) error {
if len(x) != len(y) {
return &gnuplotError{fmt.Sprintf("The length of the x-axis array and y-axis array are not same.")}
}
diff --git c/function_test.go w/function_test.go
index c605395..c9b4871 100644
--- c/function_test.go
+++ w/function_test.go
@@ -5,16 +5,17 @@ import (
)
func TestAddFunc3d(t *testing.T) {
+ args := PlotArgs{
+ Debug: false,
+ Persist: false,
+ }
dimensions := 3
- persist := false
- debug := false
- plot, _ := NewPlot(dimensions, persist, debug)
+ plot, _ := NewPlot(dimensions, args)
fct := func(x, y float64) float64 { return x - y }
- groupName := "Stright Line"
- style := "lines"
+ groupName := "Straight Line"
pointsY := []float64{1, 2, 3}
pointsX := []float64{1, 2, 3, 4, 5}
- err := plot.AddFunc3d(groupName, style, pointsX, pointsY, fct)
+ err := plot.AddFunc3d(groupName, Lines, pointsX, pointsY, fct)
if err == nil {
t.Error("TestAddFunc3d raises error when the size of X and Y arrays are not equal.")
}
diff --git c/glot.go w/glot.go
index a21e367..0dbe541 100644
--- c/glot.go
+++ w/glot.go
@@ -25,16 +25,93 @@ import (
type Plot struct {
proc *plotterProcess
debug bool
- plotcmd string
+ plotcmd PlotCommand
nplots int // number of currently active plots
tmpfiles tmpfilesDb // A temporary file used for saving data
dimensions int // dimensions of the plot
PointGroup map[string]*PointGroup // A map between Curve name and curve type. This maps a name to a given curve in a plot. Only one curve with a given name exists in a plot.
- format string // The saving format of the plot. This could be PDF, PNG, JPEG and so on.
- style string // style of the plot
+ format PlotFormat // The saving format of the plot. This could be PDF, PNG, JPEG and so on.
+ style PointStyle // style of the plot
title string // The title of the plot.
}
+const (
+ // Points is default style
+ Points = iota
+ // Bar is a Barplot type
+ Bar
+ BoxErrorBars
+ Circle
+ Dots
+ ErrorBars
+ FillSolid
+ // Histogram, i.e. fancy barplot
+ Histogram
+ // Lines is a lineplot
+ Lines
+ // LinesPoints is a lineplot with points
+ LinesPoints
+ Steps
+ InvalidPointStyle
+
+ // Pdf is a pdf output format
+ Pdf = iota
+ // Png is a png output format
+ Png
+)
+
+// PlotStyle ...
+type PointStyle uint
+
+// String is an implementation of the Stringer Interface
+// for PointStyle type.
+func (p PointStyle) String() string {
+ var m = map[PointStyle]string{
+ Bar: "bar",
+ BoxErrorBars: "boxerrorbars",
+ Circle: "circle",
+ Dots: "dots",
+ ErrorBars: "errorbars",
+ FillSolid: "fill solid",
+ Histogram: "histogram",
+ Lines: "lines",
+ LinesPoints: "linespoints",
+ Points: "points",
+ }
+ if _, ok := m[p]; !ok {
+ return m[Points]
+ }
+ return m[p]
+}
+
+// PlotFormat ...
+type PlotFormat uint
+
+// String is an implementation of the Stringer Interface
+// for PlotFormat type.
+func (pf PlotFormat) String() string {
+ switch pf {
+ case Pdf:
+ return "pdf"
+ case Png:
+ return "png"
+ default:
+ return "unsupported"
+ }
+}
+
+// PlotCommand ...
+type PlotCommand string
+
+// PlotArgs ...
+type PlotArgs struct {
+ Debug bool
+ Format PlotFormat
+ Persist bool
+ Command PlotCommand
+ Style PointStyle
+}
+
// NewPlot Function makes a new plot with the specified dimensions.
//
// Usage
@@ -46,12 +123,22 @@ type Plot struct {
// dimensions :=> refers to the dimensions of the plot.
// debug :=> can be used by developers to check the actual commands sent to gnu plot.
// persist :=> used to make the gnu plot window stay open.
-func NewPlot(dimensions int, persist, debug bool) (*Plot, error) {
- p := &Plot{proc: nil, debug: debug, plotcmd: "plot",
- nplots: 0, dimensions: dimensions, style: "points", format: "png"}
+func NewPlot(dimensions int, args PlotArgs) (*Plot, error) {
+ p := &Plot{
+ proc: nil,
+ debug: args.Debug,
+ plotcmd: func() PlotCommand {
+ if args.Command == "" {
+ return "plot"
+ } else {
+ return args.Command
+ }
+ }(),
+ nplots: 0, dimensions: dimensions,
+ style: args.Style, format: args.Format}
p.PointGroup = make(map[string]*PointGroup) // Adding a mapping between a curve name and a curve
p.tmpfiles = make(tmpfilesDb)
- proc, err := newPlotterProc(persist)
+ proc, err := newPlotterProc(args.Persist)
if err != nil {
return nil, err
}
@@ -63,69 +150,151 @@ func NewPlot(dimensions int, persist, debug bool) (*Plot, error) {
return p, nil
}
-func (plot *Plot) plotX(PointGroup *PointGroup) error {
+func (plot *Plot) pointGroupSliceLen() int {
+ pgs, err := plot.pointGroupSlice()
+ if err != nil {
+ return 0
+ }
+ return len(pgs)
+}
+func (plot *Plot) pointGroupSlice() ([]*PointGroup, error) {
+ pgsl := []*PointGroup{}
+ if len(plot.PointGroup) == 0 {
+ return []*PointGroup{},
+ &gnuplotError{fmt.Sprintf("no pointgroups were found")}
+ }
+ for _, pg := range plot.PointGroup {
+ pgsl = append(pgsl, pg)
+ }
+ return pgsl, nil
+}
+
+func (plot *Plot) plotHistogram(PointGroup *PointGroup) error {
+ x := PointGroup.castedData.([][]float64)[0]
+ // y := PointGroup.castedData.([][]float64)[1]
+ npoints := len(x)
+ // npoints := min(len(x), len(y))
+
f, err := ioutil.TempFile(os.TempDir(), gGnuplotPrefix)
if err != nil {
return err
}
fname := f.Name()
plot.tmpfiles[fname] = f
- for _, d := range PointGroup.castedData.([]float64) {
- f.WriteString(fmt.Sprintf("%v\n", d))
+
+ for i := 0; i < npoints; i++ {
+ f.WriteString(fmt.Sprintf("%v\n", x[i]))
}
+
f.Close()
cmd := plot.plotcmd
if plot.nplots > 0 {
cmd = plotCommand
}
- if PointGroup.style == "" {
- PointGroup.style = defaultStyle
+
+ PointGroup.style = Histogram
+
+ var line string
+ if PointGroup.name == "" {
+ line = fmt.Sprintf("%s \"%s\" with %s", cmd, fname, plot.style)
+ } else {
+ line = fmt.Sprintf("%s \"%s\" title \"%s\" with %s",
+ cmd, fname, PointGroup.name, PointGroup.style)
+ }
+ plot.nplots++
+ return plot.Cmd(line)
+}
+func (plot *Plot) plotX(PointGroup *PointGroup) error {
+ f, err := ioutil.TempFile(os.TempDir(), gGnuplotPrefix)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ fname := f.Name()
+ plot.tmpfiles[fname] = f
+ for _, d := range PointGroup.castedData.([]float64) {
+ f.WriteString(fmt.Sprintf("%v\n", d))
+ }
+
+ var cmd PlotCommand
+ if plot.nplots > 0 {
+ cmd = ""
+ } else {
+ cmd = plot.plotcmd
+ }
+
+ if PointGroup.style < 0 || PointGroup.style >= InvalidPointStyle {
+ PointGroup.style = Points
}
var line string
if PointGroup.name == "" {
+
line = fmt.Sprintf("%s \"%s\" with %s", cmd, fname, PointGroup.style)
} else {
line = fmt.Sprintf("%s \"%s\" title \"%s\" with %s",
cmd, fname, PointGroup.name, PointGroup.style)
}
+ if plot.nplots > 0 {
+ plot.plotcmd = plot.plotcmd + ", " + PlotCommand(line)
+ } else {
+ plot.plotcmd = PlotCommand(line)
+ }
plot.nplots++
- return plot.Cmd(line)
+ // return plot.Cmd(line)
+ return nil
}
func (plot *Plot) plotXY(PointGroup *PointGroup) error {
x := PointGroup.castedData.([][]float64)[0]
y := PointGroup.castedData.([][]float64)[1]
npoints := min(len(x), len(y))
-
+ pointString := ""
f, err := ioutil.TempFile(os.TempDir(), gGnuplotPrefix)
if err != nil {
return err
}
+ defer f.Close()
+
fname := f.Name()
plot.tmpfiles[fname] = f
for i := 0; i < npoints; i++ {
- f.WriteString(fmt.Sprintf("%v %v\n", x[i], y[i]))
+ pointString += fmt.Sprintf("%v %v\n", x[i], y[i])
+ // f.WriteString(fmt.Sprintf("%v %v\n", x[i], y[i]))
+ if i%10000 == 0 { // flush every 10,000 lines
+ f.WriteString(pointString)
+ pointString = ""
+ }
}
+ f.WriteString(pointString)
- f.Close()
- cmd := plot.plotcmd
+ var cmd PlotCommand
if plot.nplots > 0 {
- cmd = plotCommand
+ cmd = ""
+ } else {
+ cmd = plot.plotcmd
}
- if PointGroup.style == "" {
- PointGroup.style = "points"
- }
+ // if plot.nplots > 0 {
+ // cmd = plotCommand
+ // }
var line string
if PointGroup.name == "" {
- line = fmt.Sprintf("%s \"%s\" with %s", cmd, fname, PointGroup.style)
+ line = fmt.Sprintf("%s \"%s\" with %s", cmd, fname, PointGroup.style.String())
} else {
line = fmt.Sprintf("%s \"%s\" title \"%s\" with %s",
cmd, fname, PointGroup.name, PointGroup.style)
}
+ if plot.nplots > 0 {
+ plot.plotcmd = plot.plotcmd + ", " + PlotCommand(line)
+ } else {
+ plot.plotcmd = PlotCommand(line)
+ }
plot.nplots++
- return plot.Cmd(line)
+
+ // return plot.Cmd(line)
+ return nil
}
func (plot *Plot) plotXYZ(points *PointGroup) error {
@@ -138,6 +307,7 @@ func (plot *Plot) plotXYZ(points *PointGroup) error {
if err != nil {
return err
}
+ defer f.Close()
fname := f.Name()
plot.tmpfiles[fname] = f
@@ -161,3 +331,27 @@ func (plot *Plot) plotXYZ(points *PointGroup) error {
plot.nplots++
return plot.Cmd(line)
}
+
+// Execute triggers generation of actual figure
+func (plot *Plot) Execute() error {
+ pgs, err := plot.pointGroupSlice()
+ if len(pgs) == 0 {
+ return err
+ }
+ for _, pg := range pgs {
+ switch pg.dimensions {
+ case 1:
+ plot.plotX(pg)
+ case 2:
+ fmt.Printf("%v\n", pg)
+ plot.plotXY(pg)
+ case 3:
+ plot.plotXYZ(pg)
+ default:
+ return &gnuplotError{
+ fmt.Sprintf("unexpected number of dimensions in pointgroup"),
+ }
+ }
+ }
+ return plot.Cmd(string(plot.plotcmd))
+}
diff --git c/glot_test.go w/glot_test.go
index a93e9be..a912ddc 100644
--- c/glot_test.go
+++ w/glot_test.go
@@ -3,9 +3,11 @@ package glot
import "testing"
func TestNewPlot(t *testing.T) {
- persist := false
- debug := true
- _, err := NewPlot(0, persist, debug)
+ args := PlotArgs{
+ Debug: false,
+ Persist: false,
+ }
+ _, err := NewPlot(0, args)
if err == nil {
t.Error("Expected error when making a 0 dimensional plot.")
}
diff --git c/pointgroup.go w/pointgroup.go
index dce5d91..fa0fe96 100644
--- c/pointgroup.go
+++ w/pointgroup.go
@@ -1,19 +1,22 @@
package glot
-import (
- "fmt"
-)
+import "fmt"
-// A PointGroup refers to a set of points that need to plotted.
+// A PointGroup refers to a set of points that need to be plotted.
// It could either be a set of points or a function of co-ordinates.
// For Example z = Function(x,y)(3 Dimensional) or y = Function(x) (2-Dimensional)
type PointGroup struct {
name string // Name of the curve
dimensions int // dimensions of the curve
- style string // current plotting style
+ style PointStyle // current plotting style
data interface{} // Data inside the curve in any integer/float format
castedData interface{} // The data inside the curve typecasted to float64
set bool //
+ index int // Relative index of pointgroup in the plot
+}
+
+func (pg *PointGroup) setIndex(idx int) {
+ pg.index = idx
}
// AddPointGroup function adds a group of points to a plot.
@@ -26,39 +29,46 @@ type PointGroup struct {
// plot.AddPointGroup("Sample1", "points", []int32{51, 8, 4, 11})
// plot.AddPointGroup("Sample2", "points", []int32{1, 2, 4, 11})
// plot.SavePlot("1.png")
-func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (err error) {
+func (plot *Plot) AddPointGroup(name string, style PointStyle, data interface{}) (err error) {
_, exists := plot.PointGroup[name]
if exists {
return &gnuplotError{fmt.Sprintf("A PointGroup with the name %s already exists, please use another name of the curve or remove this curve before using another one with the same name.", name)}
}
- curve := &PointGroup{name: name, dimensions: plot.dimensions, data: data, set: true}
- allowed := []string{
- "lines", "points", "linepoints",
- "impulses", "dots", "bar",
- "steps", "fill solid", "histogram", "circle",
- "errorbars", "boxerrorbars",
- "boxes", "lp"}
- curve.style = defaultStyle
+ curve := &PointGroup{name: name, dimensions: plot.dimensions, data: data, set: true, style: style}
+
+ // We want to make sure that pointGroups are added to figure in a
+ // consistent and repeatable manner. Because we are using maps, the
+ // order is inherently unpredictable and using an index for each group
+ // allows us to have reproducible plots.
+ curve.setIndex(plot.pointGroupSliceLen())
discovered := 0
- for _, s := range allowed {
- if s == style {
- curve.style = style
- err = nil
+ // If the style value is an empty string and there's only a single
+ // dimension, assume histogram by default.
+
+ if style < 0 || style >= InvalidPointStyle {
+ switch plot.dimensions {
+ case 0:
+ return &gnuplotError{
+ fmt.Sprintf("Wrong number of dimensions in this plot."),
+ }
+ case 1:
+ curve.style = Histogram
+ discovered = 1
+ case 2, 3:
+ curve.style = Points
discovered = 1
}
+ } else {
+ discovered++
}
+
switch data.(type) {
case [][]float64:
if plot.dimensions != len(data.([][]float64)) {
return &gnuplotError{fmt.Sprintf("The dimensions of this PointGroup are not compatible with the dimensions of the plot.\nIf you want to make a 2-d curve you must specify a 2-d plot.")}
}
curve.castedData = data.([][]float64)
- if plot.dimensions == 2 {
- plot.plotXY(curve)
- } else {
- plot.plotXYZ(curve)
- }
plot.PointGroup[name] = curve
case [][]float32:
@@ -74,11 +84,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
}
}
curve.castedData = typeCasteSlice
- if plot.dimensions == 2 {
- plot.plotXY(curve)
- } else {
- plot.plotXYZ(curve)
- }
plot.PointGroup[name] = curve
case [][]int:
@@ -97,11 +102,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
}
}
curve.castedData = typeCasteSlice
- if plot.dimensions == 2 {
- plot.plotXY(curve)
- } else {
- plot.plotXYZ(curve)
- }
plot.PointGroup[name] = curve
case [][]int8:
@@ -120,12 +120,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
}
}
curve.castedData = typeCasteSlice
-
- if plot.dimensions == 2 {
- plot.plotXY(curve)
- } else {
- plot.plotXYZ(curve)
- }
plot.PointGroup[name] = curve
case [][]int16:
@@ -144,12 +138,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
}
}
curve.castedData = typeCasteSlice
-
- if plot.dimensions == 2 {
- plot.plotXY(curve)
- } else {
- plot.plotXYZ(curve)
- }
plot.PointGroup[name] = curve
case [][]int32:
@@ -168,12 +156,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
}
}
curve.castedData = typeCasteSlice
-
- if plot.dimensions == 2 {
- plot.plotXY(curve)
- } else {
- plot.plotXYZ(curve)
- }
plot.PointGroup[name] = curve
case [][]int64:
@@ -192,17 +174,10 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
}
}
curve.castedData = typeCasteSlice
-
- if plot.dimensions == 2 {
- plot.plotXY(curve)
- } else {
- plot.plotXYZ(curve)
- }
plot.PointGroup[name] = curve
case []float64:
curve.castedData = data.([]float64)
- plot.plotX(curve)
plot.PointGroup[name] = curve
case []float32:
originalSlice := data.([]float32)
@@ -211,7 +186,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
typeCasteSlice[i] = float64(originalSlice[i])
}
curve.castedData = typeCasteSlice
- plot.plotX(curve)
plot.PointGroup[name] = curve
case []int:
originalSlice := data.([]int)
@@ -220,7 +194,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
typeCasteSlice[i] = float64(originalSlice[i])
}
curve.castedData = typeCasteSlice
- plot.plotX(curve)
plot.PointGroup[name] = curve
case []int8:
originalSlice := data.([]int8)
@@ -229,7 +202,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
typeCasteSlice[i] = float64(originalSlice[i])
}
curve.castedData = typeCasteSlice
- plot.plotX(curve)
plot.PointGroup[name] = curve
case []int16:
originalSlice := data.([]int16)
@@ -238,7 +210,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
typeCasteSlice[i] = float64(originalSlice[i])
}
curve.castedData = typeCasteSlice
- plot.plotX(curve)
plot.PointGroup[name] = curve
case []int32:
originalSlice := data.([]int32)
@@ -247,7 +218,6 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
typeCasteSlice[i] = float64(originalSlice[i])
}
curve.castedData = typeCasteSlice
- plot.plotX(curve)
plot.PointGroup[name] = curve
case []int64:
originalSlice := data.([]int64)
@@ -256,14 +226,12 @@ func (plot *Plot) AddPointGroup(name string, style string, data interface{}) (er
typeCasteSlice[i] = float64(originalSlice[i])
}
curve.castedData = typeCasteSlice
- plot.plotX(curve)
plot.PointGroup[name] = curve
default:
- return &gnuplotError{fmt.Sprintf("invalid number of dims ")}
-
+ return &gnuplotError{fmt.Sprintf("invalid number of dimensions")}
}
if discovered == 0 {
- fmt.Printf("** style '%v' not in allowed list %v\n", style, allowed)
+ fmt.Printf("** style '%s' not supported ", style.String())
fmt.Printf("** default to 'points'\n")
err = &gnuplotError{fmt.Sprintf("invalid style '%s'", style)}
}
@@ -300,7 +268,7 @@ func (plot *Plot) RemovePointGroup(name string) {
// plot, _ := glot.NewPlot(dimensions, persist, debug)
// plot.AddPointGroup("Sample1", "points", []int32{51, 8, 4, 11})
// plot.ResetPointGroupStyle("Sample1", "points")
-func (plot *Plot) ResetPointGroupStyle(name string, style string) (err error) {
+func (plot *Plot) ResetPointGroupStyle(name string, style PointStyle) (err error) {
pointGroup, exists := plot.PointGroup[name]
if !exists {
return &gnuplotError{fmt.Sprintf("A curve with name %s does not exist.", name)}
diff --git c/pointgroup_test.go w/pointgroup_test.go
index 63f8f4c..f5a934e 100644
--- c/pointgroup_test.go
+++ w/pointgroup_test.go
@@ -1,15 +1,228 @@
package glot
-import "testing"
+import (
+ "fmt"
+ "testing"
+)
+
+func squareInt(n int) int {
+ return n * n
+}
+func cubeInt(n int) int {
+ return n * squareInt(n)
+}
+
+func squareInt16(n int16) int16 {
+ return n * n
+}
+func cubeInt16(n int16) int16 {
+ return n * squareInt16(n)
+}
func TestResetPointGroupStyle(t *testing.T) {
+ args := PlotArgs{
+ Debug: false,
+ Persist: false,
+ }
dimensions := 2
- persist := false
- debug := false
- plot, _ := NewPlot(dimensions, persist, debug)
- plot.AddPointGroup("Sample1", "points", []int32{51, 8, 4, 11})
- err := plot.ResetPointGroupStyle("Sam", "lines")
+ plot, _ := NewPlot(dimensions, args)
+ plot.AddPointGroup("Sample1", Points, []int32{51, 8, 4, 11})
+ err := plot.ResetPointGroupStyle("Sam", Lines)
if err == nil {
t.Error("The specified pointgroup to be reset does not exist")
}
}
+
+func TestTwoPointGroups(t *testing.T) {
+ args := PlotArgs{
+ Debug: true,
+ Persist: true,
+ Format: Pdf,
+ Style: Points,
+ }
+
+ plot, _ := NewPlot(1, args)
+ plot.AddPointGroup("TestGroup_1", Points, []float64{
+ -0.512695,
+ 0.591778,
+ -0.0939544,
+ -0.510766,
+ -0.859442,
+ 0.0340482,
+ 0.887461,
+ 0.277168,
+ -0.998753,
+ 0.356656,
+ })
+ plot.AddPointGroup("TestGroup_2", Points, []float64{
+ 0.712863,
+ 0.975935,
+ 0.875864,
+ 0.737082,
+ -0.185717,
+ -0.936551,
+ 0.779397,
+ 0.916793,
+ 0.622004,
+ -0.0860084,
+ })
+ fmt.Printf("pg: %v\n", plot.PointGroup)
+ plot.Execute()
+}
+
+func TestThreePointGroupsFloat64(t *testing.T) {
+ args := PlotArgs{
+ Debug: true,
+ Persist: true,
+ Format: Pdf,
+ Style: Points,
+ }
+
+ plot, _ := NewPlot(1, args)
+ plot.AddPointGroup("TestGroup_1", Points, []float64{
+ -0.512695,
+ 0.591778,
+ -0.0939544,
+ -0.510766,
+ -0.859442,
+ 0.0340482,
+ 0.887461,
+ 0.277168,
+ -0.998753,
+ 0.356656,
+ })
+ plot.AddPointGroup("TestGroup_2", Points, []float64{
+ 0.712863,
+ 0.975935,
+ 0.875864,
+ 0.737082,
+ -0.185717,
+ -0.936551,
+ 0.779397,
+ 0.916793,
+ 0.622004,
+ -0.0860084,
+ })
+ plot.AddPointGroup("TestGroup_3", LinesPoints, []float64{
+ 0.28927,
+ -0.945002,
+ -0.904681,
+ 0.924912,
+ 0.990415,
+ 0.326935,
+ -0.927919,
+ 0.994446,
+ 0.270194,
+ -0.0378568,
+ })
+ fmt.Printf("pg: %v\n", plot.PointGroup)
+ plot.Execute()
+}
+
+func TestThreePointGroupsFloatMixed(t *testing.T) {
+ args := PlotArgs{
+ Debug: true,
+ Persist: true,
+ Format: Pdf,
+ Style: Points,
+ }
+
+ plot, _ := NewPlot(1, args)
+ plot.AddPointGroup("TestGroup^1", Points, []float32{
+ -0.512695,
+ 0.591778,
+ -0.0939544,
+ -0.510766,
+ -0.859442,
+ 0.0340482,
+ 0.887461,
+ 0.277168,
+ -0.998753,
+ 0.356656,
+ })
+ plot.AddPointGroup("TestGroup^2", Points, []float64{
+ 0.712863,
+ 0.975935,
+ 0.875864,
+ 0.737082,
+ -0.185717,
+ -0.936551,
+ 0.779397,
+ 0.916793,
+ 0.622004,
+ -0.0860084,
+ })
+ plot.AddPointGroup("TestGroup^3", LinesPoints, []float32{
+ 0.28927,
+ -0.945002,
+ -0.904681,
+ 0.924912,
+ 0.990415,
+ 0.326935,
+ -0.927919,
+ 0.994446,
+ 0.270194,
+ -0.0378568,
+ })
+ fmt.Printf("%v\n", plot.PointGroup["TestGroup^1"])
+ fmt.Printf("%v\n", plot.PointGroup["TestGroup^2"])
+ fmt.Printf("%v\n", plot.PointGroup["TestGroup^3"])
+ plot.Execute()
+}
+
+func TestOnePointGroupInt8(t *testing.T) {
+ args := PlotArgs{
+ Debug: true,
+ Persist: true,
+ Format: Pdf,
+ Style: Points,
+ }
+
+ plot, _ := NewPlot(1, args)
+ plot.AddPointGroup("TestGroup_1", Points, []int8{
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ -9, -8, -7, -6, -5, -4, -3, -2, -1, 0,
+ })
+ plot.Execute()
+}
+func TestOnePointGroupInt16(t *testing.T) {
+ args := PlotArgs{
+ Debug: true,
+ Persist: true,
+ Format: Pdf,
+ Style: Points,
+ }
+
+ plot, _ := NewPlot(1, args)
+ plot.AddPointGroup("TestGroup_1", Points, []int16{
+ 1, 2, 8, 16, 32, 64, 128, 256, 512, 1024,
+ })
+ plot.Execute()
+}
+
+func TestTwoPointGroupsInt16(t *testing.T) {
+ args := PlotArgs{
+ Debug: true,
+ Persist: true,
+ Format: Pdf,
+ Style: Points,
+ }
+
+ plot, _ := NewPlot(1, args)
+ plot.AddPointGroup("PowerOfTwo^1", Points, []int16{
+ 1, 2, 8, 16, 32, 64, 128, 256, 512, 1024,
+ })
+ plot.AddPointGroup("Cubed^2", Points, []int16{
+ 0,
+ 1,
+ cubeInt16(2),
+ cubeInt16(3),
+ cubeInt16(4),
+ cubeInt16(5),
+ cubeInt16(6),
+ cubeInt16(7),
+ cubeInt16(8),
+ cubeInt16(9),
+ })
+ plot.Execute()
+}
@szaydel
This looks really amazing.
Please do send a PR and I will make some comments/suggestions and will eventually merge it.
Thanks a lot.
@Arafatk, Let me clean-up a bit, add some tests, and implement a few things I wanted to implement and I will send a PR. Thanks for considering it!
Cheers.
Just wanted to give an update. I am still working on a few improvements before I do a PR. I wanted to propose a model where there are distinct types representing plot styles, i.e. there's a type for histograms, a type for points, etc., and each such type encodes all necessary information, as well as allows for some degree of customization. The type is then tied to a PointGroup, and multiple PointGroups are tied to a plot area or canvas. I am thinking of this as a more modular way to structure things, and a bit more object oriented.
Basic goal is to define a Geom, a Geometric Object struct, like in this example we have a HistogramGeom which includes a style struct, and this struct satisfies a PlotStyler Interface, which generalizes configuration of the different Geoms. The goal is to have a common interface for configuring different plotting styles, which depending on the style may require using the set style
command or not, etc.
// HistogramStyle describes stylistic elements that may be attributed to histogram
type HistogramStyle struct {
Empty bool
Solid bool
Border bool
}
// HistogramGeom --
type HistogramGeom struct {
style HistogramStyle
}
@szaydel Really sorry for the late reply.
This looks really good and modular and will certainly help a lot in the long term when we try to extend the library functionalities.
Please go ahead with the pull request or maybe keep it a work in progress, the very first pull request doesn't have to be directly merged but atleast I can take a look at it and make comments if needed.
Thanks @Arafatk. I will get this stuff prepared in the next few days, or over weekend and do a PR. If you want to start a Dev branch of some sort, I can do a PR against it instead of Master.