func (pl *Plot2D) PlotConfig() {
	sv := pl.SVGPlot()
	sv.InitScale()
	sv.Fill = true
	sv.SetProp("background-color", &gi.Prefs.Colors.Background)
	sv.SetStretchMax()
}
PlotConfig configures the PlotView
package eplot
import (
	"fmt"
	"image"
	"log"
	"math"
	"strings"
	"github.com/emer/etable/etable"
	"github.com/emer/etable/etensor"
	"github.com/goki/gi/gi"
	"github.com/goki/gi/gist"
	"github.com/goki/gi/giv"
	"github.com/goki/gi/svg"
	"github.com/goki/gi/units"
	"github.com/goki/ki/ints"
	"github.com/goki/ki/ki"
	"github.com/goki/ki/kit"
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/font"
)
type Plot2D struct {
	gi.Layout
	Table    *etable.IdxView `desc:"the idxview of the table that we're plotting"`
	Params   PlotParams      `desc:"the overall plot parameters"`
	Cols     []*ColParams    `desc:"the parameters for each column of the table"`
	GPlot    *plot.Plot      `desc:"the gonum plot that actually does the plotting -- always save the last one generated"`
	SVGFile  gi.FileName     `desc:"current svg file"`
	DataFile gi.FileName     `desc:"current csv data file"`
	InPlot   bool            `inactive:"+" desc:"currently doing a plot"`
}
var KiT_Plot2D = kit.Types.AddType(&Plot2D{}, Plot2DProps)
func AddNewPlot2D(parent ki.Ki, name string) *Plot2D {
	return parent.AddNewChild(KiT_Plot2D, name).(*Plot2D)
}
func (pl *Plot2D) CopyFieldsFrom(frm interface{}) {
	fr := frm.(*Plot2D)
	pl.Layout.CopyFieldsFrom(&fr.Layout)
	pl.Params.CopyFrom(&fr.Params)
	pl.SetTableView(fr.Table)
	mx := ints.MinInt(len(pl.Cols), len(fr.Cols))
	for i := 0; i < mx; i++ {
		pl.Cols[i].CopyFrom(fr.Cols[i])
	}
}
func (pl *Plot2D) Defaults() {
	pl.Params.Plot = pl
	pl.Params.Defaults()
	plot.DefaultFont = font.Font{Typeface: "Liberation", Variant: "Sans"}
}
func (pl *Plot2D) SetTable(tab *etable.Table) {
	pl.Defaults()
	pl.Table = etable.NewIdxView(tab)
	pl.Config()
}
func (pl *Plot2D) SetTableView(tab *etable.IdxView) {
	pl.Defaults()
	pl.Table = tab
	pl.Cols = nil
	pl.Config()
}
func (pl *Plot2D) ColParamsTry(colNm string) (*ColParams, error) {
	for _, cp := range pl.Cols {
		if cp.Col == colNm {
			return cp, nil
		}
	}
	return nil, fmt.Errorf("eplot plot: %v column named: %v not found", pl.Nm, colNm)
}
func (pl *Plot2D) ColParams(colNm string) *ColParams {
	cp, _ := pl.ColParamsTry(colNm)
	return cp
}
const (
	On       bool = true
	Off           = false
	FixMin        = true
	FloatMin      = false
	FixMax        = true
	FloatMax      = false
)
func (pl *Plot2D) SetColParams(colNm string, on bool, fixMin bool, min float64, fixMax bool, max float64) *ColParams {
	cp, err := pl.ColParamsTry(colNm)
	if err != nil {
		log.Println(err)
		return nil
	}
	cp.On = on
	cp.Range.FixMin = fixMin
	if fixMin {
		cp.Range.Min = min
	}
	cp.Range.FixMax = fixMax
	if fixMax {
		cp.Range.Max = max
	}
	return cp
}
func (pl *Plot2D) SaveSVG(fname gi.FileName) {
	pl.Update()
	sv := pl.SVGPlot()
	SaveSVGView(string(fname), pl.GPlot, sv, 2)
	pl.SVGFile = fname
}
func (pl *Plot2D) SavePNG(fname gi.FileName) {
	sv := pl.SVGPlot()
	sv.SavePNG(string(fname))
}
func (pl *Plot2D) SaveCSV(fname gi.FileName, delim etable.Delims) {
	pl.Table.SaveCSV(fname, delim, etable.Headers)
	pl.DataFile = fname
}
func (pl *Plot2D) OpenCSV(fname gi.FileName, delim etable.Delims) {
	pl.Table.Table.OpenCSV(fname, delim)
	pl.DataFile = fname
	pl.Config()
	pl.Update()
}
func (pl *Plot2D) YLabel() string {
	if pl.Params.YAxisLabel != "" {
		return pl.Params.YAxisLabel
	}
	for _, cp := range pl.Cols {
		if cp.On {
			return cp.Label()
		}
	}
	return "Y"
}
func (pl *Plot2D) XLabel() string {
	if pl.Params.XAxisLabel != "" {
		return pl.Params.XAxisLabel
	}
	if pl.Params.XAxisCol != "" {
		cp := pl.ColParams(pl.Params.XAxisCol)
		if cp != nil {
			return cp.Label()
		}
		return pl.Params.XAxisCol
	}
	return "X"
}
func (pl *Plot2D) GoUpdate() {
	if pl == nil || pl.This() == nil {
		return
	}
	if pl.Table == nil || pl.Table.Table == nil {
		return
	}
	pl.Table.Sequential()
	pl.GoUpdatePlot()
}
func (pl *Plot2D) GoUpdatePlot() {
	if pl == nil || pl.This() == nil {
		return
	}
	if !pl.IsVisible() || pl.Table == nil || pl.Table.Table == nil || pl.InPlot {
		return
	}
	mvp := pl.ViewportSafe()
	if mvp.IsUpdatingNode() {  
		return
	}
	mvp.BlockUpdates()
	plupdt := false
	if len(pl.Kids) != 2 || len(pl.Cols) != pl.Table.Table.NumCols() {
		plupdt = pl.UpdateStart()
		pl.Config()
	}
	sv := pl.SVGPlot()
	updt := sv.UpdateStart()
	pl.GenPlot()
	mvp.UnblockUpdates()
	sv.UpdateEnd(updt)
	pl.UpdateEnd(plupdt)
}
func (pl *Plot2D) Update() {
	if pl == nil || pl.This() == nil {
		return
	}
	if pl.Table == nil || pl.Table.Table == nil {
		return
	}
	pl.Table.Sequential()
	pl.UpdatePlot()
}
func (pl *Plot2D) UpdatePlot() {
	if pl == nil || pl.This() == nil {
		return
	}
	if !pl.IsVisible() || pl.Table == nil || pl.Table.Table == nil || pl.InPlot {
		return
	}
	if len(pl.Kids) != 2 || len(pl.Cols) != pl.Table.Table.NumCols() {
		pl.Config()
	}
	if pl.ViewportSafe().IsUpdatingNode() {  
		return
	}
	pl.GenPlot()
}
func (pl *Plot2D) GenPlot() {
	if !pl.IsVisible() {  
		return
	}
	if pl.InPlot {
		fmt.Printf("error: in plot already\n")
		return
	}
	pl.InPlot = true
	sv := pl.SVGPlot()
	if pl.Table == nil || pl.Table.Table == nil || pl.Table.Len() == 0 {
		sv.DeleteChildren(ki.DestroyKids)
		pl.InPlot = false
		return
	}
	pl.GPlot = nil
	switch pl.Params.Type {
	case XY:
		pl.GenPlotXY()
	case Bar:
		pl.GenPlotBar()
	}
	if pl.GPlot != nil {
		PlotViewSVG(pl.GPlot, sv, pl.Params.Scale)
	}
	pl.InPlot = false
}
func (pl *Plot2D) PlotXAxis(plt *plot.Plot, ixvw *etable.IdxView) (xi int, xview *etable.IdxView, xbreaks []int, err error) {
	xi, err = ixvw.Table.ColIdxTry(pl.Params.XAxisCol)
	if err != nil {
		log.Println("eplot.PlotXAxis: " + err.Error())
		return
	}
	xview = ixvw
	xc := ixvw.Table.Cols[xi]
	xp := pl.Cols[xi]
	sz := 1
	lim := false
	if xp.Range.FixMin {
		lim = true
		plt.X.Min = math.Min(plt.X.Min, xp.Range.Min)
	}
	if xp.Range.FixMax {
		lim = true
		plt.X.Max = math.Max(plt.X.Max, xp.Range.Max)
	}
	if xc.NumDims() > 1 {
		sz = xc.Len() / xc.Dim(0)
		if xp.TensorIdx > sz || xp.TensorIdx < 0 {
			log.Printf("eplot.PlotXAxis: TensorIdx invalid -- reset to 0")
			xp.TensorIdx = 0
		}
	}
	if lim {
		xview = ixvw.Clone()
		xview.Filter(func(et *etable.Table, row int) bool {
			if !ixvw.Table.IsValidRow(row) {  
				return false
			}
			var xv float64
			if xc.NumDims() > 1 {
				xv = xc.FloatValRowCell(row, xp.TensorIdx)
			} else {
				xv = xc.FloatVal1D(row)
			}
			if xp.Range.FixMin && xv < xp.Range.Min {
				return false
			}
			if xp.Range.FixMax && xv > xp.Range.Max {
				return false
			}
			return true
		})
	}
	if pl.Params.NegXDraw {
		return
	}
	lastx := -math.MaxFloat64
	for row := 0; row < xview.Len(); row++ {
		trow := xview.Idxs[row]  
		var xv float64
		if xc.NumDims() > 1 {
			xv = xc.FloatValRowCell(trow, xp.TensorIdx)
		} else {
			xv = xc.FloatVal1D(trow)
		}
		if xv < lastx {
			xbreaks = append(xbreaks, row)
		}
		lastx = xv
	}
	xbreaks = append(xbreaks, xview.Len())
	return
}
func (pl *Plot2D) Config() {
	pl.Lay = gi.LayoutVert
	pl.Defaults()
	pl.Params.FmMeta(pl.Table.Table)
	pl.SetProp("spacing", gi.StdDialogVSpaceUnits)
	config := kit.TypeAndNameList{}
	config.Add(gi.KiT_ToolBar, "tbar")
	config.Add(gi.KiT_Layout, "plot")
	mods, updt := pl.ConfigChildren(config)
	if !mods {
		updt = pl.UpdateStart()
	}
	play := pl.PlotLay()
	play.Lay = gi.LayoutHoriz
	play.SetProp("max-width", -1)
	play.SetProp("max-height", -1)
	play.SetProp("spacing", gi.StdDialogVSpaceUnits)
	vncfg := kit.TypeAndNameList{}
	vncfg.Add(gi.KiT_Frame, "cols")
	vncfg.Add(svg.KiT_Editor, "plot")
	play.ConfigChildren(vncfg)  
	pl.ColsConfig()
	pl.PlotConfig()
	pl.ToolbarConfig()
	pl.UpdateEnd(updt)
}
func (pl *Plot2D) IsConfiged() bool {
	if len(pl.Kids) == 0 {
		return false
	}
	ppl := pl.PlotLay()
	if len(ppl.Kids) == 0 {
		return false
	}
	return true
}
func (pl *Plot2D) Toolbar() *gi.ToolBar {
	return pl.ChildByName("tbar", 0).(*gi.ToolBar)
}
func (pl *Plot2D) PlotLay() *gi.Layout {
	return pl.ChildByName("plot", 1).(*gi.Layout)
}
func (pl *Plot2D) SVGPlot() *svg.Editor {
	return pl.PlotLay().ChildByName("plot", 1).(*svg.Editor)
}
func (pl *Plot2D) ColsLay() *gi.Frame {
	return pl.PlotLay().ChildByName("cols", 0).(*gi.Frame)
}
const NColsHeader = 2
func (pl *Plot2D) ColsListUpdate() {
	if pl.Table == nil || pl.Table.Table == nil {
		pl.Cols = nil
		return
	}
	dt := pl.Table.Table
	nc := dt.NumCols()
	if nc == len(pl.Cols) {
		return
	}
	npc := len(PlotColorNames)
	pl.Cols = make([]*ColParams, nc)
	clri := 0
	for ci := range dt.Cols {
		cn := dt.ColNames[ci]
		inc := 1
		if cn == pl.Params.XAxisCol {  
			inc = 0
		}
		cp := &ColParams{Col: cn, ColorName: gi.ColorName(PlotColorNames[clri%npc])}
		cp.Defaults()
		tcol := dt.Cols[ci]
		if tcol.DataType() == etensor.STRING {
			cp.IsString = true
		} else {
			cp.IsString = false
		}
		pl.Cols[ci] = cp
		clri += inc
	}
}
func (pl *Plot2D) ColsUpdate() {
	vl := pl.ColsLay()
	for i, cli := range *vl.Children() {
		if i < NColsHeader {
			continue
		}
		ci := i - NColsHeader
		cp := pl.Cols[ci]
		cl := cli.(*gi.Layout)
		cb := cl.Child(0).(*gi.CheckBox)
		cb.SetChecked(cp.On)
	}
}
func (pl *Plot2D) SetAllCols(on bool) {
	vl := pl.ColsLay()
	for i, cli := range *vl.Children() {
		if i < NColsHeader {
			continue
		}
		ci := i - NColsHeader
		cp := pl.Cols[ci]
		if cp.Col == pl.Params.XAxisCol {
			continue
		}
		cp.On = on
		cl := cli.(*gi.Layout)
		cb := cl.Child(0).(*gi.CheckBox)
		cb.SetChecked(cp.On)
	}
	pl.Update()
}
func (pl *Plot2D) SetColsByName(nameContains string, on bool) {
	vl := pl.ColsLay()
	for i, cli := range *vl.Children() {
		if i < NColsHeader {
			continue
		}
		ci := i - NColsHeader
		cp := pl.Cols[ci]
		if cp.Col == pl.Params.XAxisCol {
			continue
		}
		if !strings.Contains(cp.Col, nameContains) {
			continue
		}
		cp.On = on
		cl := cli.(*gi.Layout)
		cb := cl.Child(0).(*gi.CheckBox)
		cb.SetChecked(cp.On)
	}
	pl.Update()
}
func (pl *Plot2D) ColsConfig() {
	vl := pl.ColsLay()
	vl.SetReRenderAnchor()
	vl.Lay = gi.LayoutVert
	vl.SetProp("spacing", 0)
	vl.SetProp("vertical-align", gist.AlignTop)
	vl.SetMinPrefHeight(units.NewEm(5))  
	vl.SetStretchMaxHeight()
	pl.ColsListUpdate()
	if len(pl.Cols) == 0 {
		vl.DeleteChildren(true)
		return
	}
	config := kit.TypeAndNameList{}
	config.Add(gi.KiT_Layout, "sel-cols")
	config.Add(gi.KiT_Separator, "sep")
	for _, cn := range pl.Cols {
		config.Add(gi.KiT_Layout, cn.Col)
	}
	mods, updt := vl.ConfigChildren(config)
	if !mods {
		updt = vl.UpdateStart()
	}
	clcfg := kit.TypeAndNameList{}
	clcfg.Add(gi.KiT_CheckBox, "on")
	clcfg.Add(gi.KiT_Action, "col")
	for i, cli := range *vl.Children() {
		if i == 1 {
			sp := cli.(*gi.Separator)
			sp.Horiz = true
			continue
		}
		cl := cli.(*gi.Layout)
		cl.Lay = gi.LayoutHoriz
		cl.ConfigChildren(clcfg)
		cl.SetProp("margin", 0)
		cl.SetProp("max-width", -1)
		cb := cl.Child(0).(*gi.CheckBox)
		ca := cl.Child(1).(*gi.Action)
		if i == 0 {
			cb.SetChecked(false)
			cb.Tooltip = "click to turn all columns on or off"
			cb.ButtonSig.Connect(pl.This(), func(recv, send ki.Ki, sig int64, data interface{}) {
				if sig == int64(gi.ButtonToggled) {
					pll := recv.Embed(KiT_Plot2D).(*Plot2D)
					cbb := send.(*gi.CheckBox)
					pll.SetAllCols(cbb.IsChecked())
				}
			})
			ca.SetText("Select Cols")
			ca.Tooltip = "click to select columns based on column name"
			ca.ActionSig.Connect(pl.This(), func(recv, send ki.Ki, sig int64, data interface{}) {
				pll := recv.Embed(KiT_Plot2D).(*Plot2D)
				giv.CallMethod(pll, "SetColsByName", pll.ViewportSafe())
			})
		} else {
			ci := i - NColsHeader
			cp := pl.Cols[ci]
			cp.Plot = pl
			cb.SetChecked(cp.On)
			cb.SetProp("idx", ci)
			cb.ButtonSig.Connect(pl.This(), func(recv, send ki.Ki, sig int64, data interface{}) {
				if sig == int64(gi.ButtonToggled) {
					pll := recv.Embed(KiT_Plot2D).(*Plot2D)
					cbb := send.(*gi.CheckBox)
					idx := cb.Prop("idx").(int)
					cpp := pll.Cols[idx]
					cpp.On = cbb.IsChecked()
					pll.Update()
				}
			})
			ca.SetText(cp.Col)
			ca.Data = ci
			ca.ActionSig.Connect(pl.This(), func(recv, send ki.Ki, sig int64, data interface{}) {
				pll := recv.Embed(KiT_Plot2D).(*Plot2D)
				caa := send.(*gi.Action)
				idx := caa.Data.(int)
				cpp := pll.Cols[idx]
				giv.StructViewDialog(pl.ViewportSafe(), cpp, giv.DlgOpts{Title: "ColParams"}, nil, nil)
			})
		}
	}
	vl.UpdateEnd(updt)
}