diff --git a/common.go b/common.go index e57bee1..85c98fa 100644 --- a/common.go +++ b/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 diff --git a/common_test.go b/common_test.go index 0c84fc7..24509fa 100644 --- a/common_test.go +++ b/common_test.go @@ -2,24 +2,121 @@ 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 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 TestSetFormat(t *testing.T) { +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.Cmd(t1) + plot2.Cmd(t2) + } diff --git a/core.go b/core.go index 5146ff4..701774d 100644 --- a/core.go +++ b/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 a/core_test.go b/core_test.go index 077ff59..8e8cc5f 100644 --- a/core_test.go +++ b/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 a/function.go b/function.go index 29fee9b..ae371bd 100644 --- a/function.go +++ b/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 a/function_test.go b/function_test.go index c605395..a911ad7 100644 --- a/function_test.go +++ b/function_test.go @@ -4,17 +4,19 @@ import ( "testing" ) +var funcTestArgs = PlotArgs{ + Debug: true, + Persist: true, +} + func TestAddFunc3d(t *testing.T) { dimensions := 3 - persist := false - debug := false - plot, _ := NewPlot(dimensions, persist, debug) + plot, _ := NewPlot(dimensions, funcTestArgs) 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 a/glot.go b/glot.go index a21e367..58c0451 100644 --- a/glot.go +++ b/glot.go @@ -25,16 +25,100 @@ 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 ( + // PlotDefaultStyle results in assignment of whatever style + // the global plot object is using. + PlotDefaultStyle = iota + // Points is default style which which plots are created + Points + // Bar is a Barplot type + Bar + BoxErrorBars + Boxplot + 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 +) + +// PointStyle specifies which style to use for plotting a set of points. +// Points here imply data points not points as in points on the plot, since +// on the plot points may be represented by boxes, circles, etc. +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", + Boxplot: "boxplot", + 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 +130,24 @@ 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" + } + 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,31 +159,75 @@ func NewPlot(dimensions int, persist, debug bool) (*Plot, error) { return p, nil } +// ChangeStyle changes PointStyle currently configured on plot +// to style, assuming value is valid. If outside the allowed range, +// i.e. invalid value, use PlotDefaultStyle instead. +func (plot *Plot) ChangeStyle(style PointStyle) error { + if style < PlotDefaultStyle || style >= InvalidPointStyle { + plot.style = PlotDefaultStyle + return &gnuplotError{ + fmt.Sprintf("invalid style given, using plot default"), + } + } + plot.style = style + return nil +} + +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) 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)) } - f.Close() - cmd := plot.plotcmd + + var cmd PlotCommand if plot.nplots > 0 { - cmd = plotCommand + cmd = "" + } else { + cmd = plot.plotcmd } - if PointGroup.style == "" { - PointGroup.style = defaultStyle + + if PointGroup.style < 0 || PointGroup.style >= InvalidPointStyle { + PointGroup.style = plot.style } 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) } @@ -96,34 +236,45 @@ 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" - } 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) } @@ -138,6 +289,7 @@ func (plot *Plot) plotXYZ(points *PointGroup) error { if err != nil { return err } + defer f.Close() fname := f.Name() plot.tmpfiles[fname] = f diff --git a/glot_test.go b/glot_test.go index a93e9be..a671939 100644 --- a/glot_test.go +++ b/glot_test.go @@ -1,12 +1,33 @@ package glot -import "testing" +import ( + "testing" +) -func TestNewPlot(t *testing.T) { - persist := false - debug := true - _, err := NewPlot(0, persist, debug) +var glotTestArgs = PlotArgs{ + Debug: false, + Persist: false, +} + +func TestNewPlotZeroDimensions(t *testing.T) { + _, err := NewPlot(0, glotTestArgs) if err == nil { t.Error("Expected error when making a 0 dimensional plot.") } } + +func TestNewPlotOneDimensions(t *testing.T) { + p, err := NewPlot(1, glotTestArgs) + p.AddPointGroup("First Group", 0, + []float64{pow(2, 1), pow(2, 2), pow(2, 3)}) + if err != nil { + t.Error(err) + } +} + +func pow(n float64, p int) float64 { + if p == 0 { + return n + } + return n * (pow(n, p-1)) +} diff --git a/pointgroup.go b/pointgroup.go index dce5d91..cea1d1a 100644 --- a/pointgroup.go +++ b/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,44 +29,54 @@ 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) { - _, exists := plot.PointGroup[name] +func (plot *Plot) AddPointGroup(name string, style PointStyle, data interface{}) (curve *PointGroup, err error) { + curve, 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)} + return curve, &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, + } + // 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 nil, &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.")} + return nil, &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: if plot.dimensions != len(data.([][]float32)) { - 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.")} + return nil, &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.")} } originalSlice := data.([][]float32) typeCasteSlice := make([][]float64, len(originalSlice)) @@ -74,20 +87,15 @@ 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: if plot.dimensions != len(data.([][]int)) { - 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.")} + return nil, &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.")} } originalSlice := data.([][]int) if len(originalSlice) != 2 { - return &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} + return nil, &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} } typeCasteSlice := make([][]float64, len(originalSlice)) for i := 0; i < len(originalSlice); i++ { @@ -97,20 +105,15 @@ 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: if plot.dimensions != len(data.([][]int8)) { - 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.")} + return nil, &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.")} } originalSlice := data.([][]int8) if len(originalSlice) != 2 { - return &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} + return nil, &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} } typeCasteSlice := make([][]float64, len(originalSlice)) for i := 0; i < len(originalSlice); i++ { @@ -120,21 +123,15 @@ 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: if plot.dimensions != len(data.([][]int16)) { - 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.")} + return nil, &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.")} } originalSlice := data.([][]int16) if len(originalSlice) != 2 { - return &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} + return nil, &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} } typeCasteSlice := make([][]float64, len(originalSlice)) for i := 0; i < len(originalSlice); i++ { @@ -144,21 +141,15 @@ 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: if plot.dimensions != len(data.([][]int32)) { - 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.")} + return nil, &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.")} } originalSlice := data.([][]int32) if len(originalSlice) != 2 { - return &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} + return nil, &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} } typeCasteSlice := make([][]float64, len(originalSlice)) for i := 0; i < len(originalSlice); i++ { @@ -168,21 +159,15 @@ 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: if plot.dimensions != len(data.([][]int64)) { - 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.")} + return nil, &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.")} } originalSlice := data.([][]int64) if len(originalSlice) != 2 { - return &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} + return nil, &gnuplotError{fmt.Sprintf("this is not a 2d matrix")} } typeCasteSlice := make([][]float64, len(originalSlice)) for i := 0; i < len(originalSlice); i++ { @@ -192,17 +177,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 +189,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 +197,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 +205,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 +213,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 +221,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,18 +229,16 @@ 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 nil, &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)} } - return err + return curve, err } // RemovePointGroup helps to remove a particular point group from the plot. @@ -300,7 +271,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 a/pointgroup_test.go b/pointgroup_test.go index 63f8f4c..5fe0e1d 100644 --- a/pointgroup_test.go +++ b/pointgroup_test.go @@ -1,15 +1,220 @@ package glot -import "testing" +import ( + "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, + }) +} + +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, + }) + +} + +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, + }) +} + +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, + }) + +} +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, + }) + +} + +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), + }) + +}