func (p *Plot) Add(plotters ...Plotter) *Plot {
	for _, plotter := range plotters {
		plotter.Apply(p)
	}
	return p
}
Add applies each of plotters to Plot in order.
package gg
import (
	"fmt"
	"log"
	"os"
	"github.com/aclements/go-gg/table"
)
var Warning = log.New(os.Stderr, "[gg] ", log.Lshortfile)
type Plot struct {
	env    *plotEnv
	scales map[string]scalerTree
	scaledData map[scaledDataKey]*scaledData
	scaleSet   map[scaleKey]bool
	marks      []plotMark
	axisLabels     map[string]string
	autoAxisLabels map[string][]string
	title string
	constNonce int
}
func NewPlot(data table.Grouping) *Plot {
	p := &Plot{
		env: &plotEnv{
			data: data,
		},
		scales:         make(map[string]scalerTree),
		scaledData:     make(map[scaledDataKey]*scaledData),
		scaleSet:       make(map[scaleKey]bool),
		axisLabels:     make(map[string]string),
		autoAxisLabels: make(map[string][]string),
	}
	return p
}
type plotEnv struct {
	parent *plotEnv
	data   table.Grouping
}
type scaleKey struct {
	gid   table.GroupID
	aes   string
	scale Scaler
}
func (p *Plot) SetData(data table.Grouping) *Plot {
	p.env.data = data
	return p
}
func (p *Plot) Data() table.Grouping {
	return p.env.data
}
func (p *Plot) Const(val interface{}) string {
	tab := p.Data()
retry:
	col := fmt.Sprintf("[gg-const-%d]", p.constNonce)
	p.constNonce++
	for _, col2 := range tab.Columns() {
		if col == col2 {
			goto retry
		}
	}
	p.SetData(table.MapTables(tab, func(_ table.GroupID, t *table.Table) *table.Table {
		return table.NewBuilder(t).AddConst(col, val).Done()
	}))
	return col
}
type scalerTree struct {
	scales map[table.GroupID]Scaler
}
func newScalerTree() scalerTree {
	return scalerTree{map[table.GroupID]Scaler{
		table.RootGroupID: &defaultScale{},
	}}
}
func (t scalerTree) bind(gid table.GroupID, s Scaler) {
	for ogid := range t.scales {
		if gid == table.RootGroupID {
			delete(t.scales, ogid)
			continue
		}
		for p := ogid; ; p = p.Parent() {
			if p == gid {
				delete(t.scales, ogid)
				break
			}
			if p == table.RootGroupID {
				break
			}
		}
	}
	t.scales[gid] = s
}
func (t scalerTree) find(gid table.GroupID) Scaler {
	for {
		if s, ok := t.scales[gid]; ok {
			return s
		}
		if gid == table.RootGroupID {
			panic("no scale for group " + gid.String())
		}
		gid = gid.Parent()
	}
}
func (p *Plot) getScales(aes string) scalerTree {
	st, ok := p.scales[aes]
	if !ok {
		st = newScalerTree()
		p.scales[aes] = st
	}
	return st
}
func (p *Plot) copyScales(old, new table.GroupID) {
	for _, st := range p.scales {
		st.scales[new] = st.find(old)
	}
}
func (p *Plot) SetScale(aes string, s Scaler) *Plot {
	return p.SetScaleAt(aes, s, table.RootGroupID)
}
func (p *Plot) SetScaleAt(aes string, s Scaler, gid table.GroupID) *Plot {
	p.getScales(aes).bind(gid, s)
	return p
}
func (p *Plot) GetScale(aes string) Scaler {
	return p.GetScaleAt(aes, table.RootGroupID)
}
func (p *Plot) GetScaleAt(aes string, gid table.GroupID) Scaler {
	return p.getScales(aes).find(gid)
}
type scaledDataKey struct {
	aes  string
	data table.Grouping
	col  string
}
func (p *Plot) use(aes string, col string) *scaledData {
	if col == "" {
		return nil
	}
	sd := p.scaledData[scaledDataKey{aes, p.Data(), col}]
	if sd == nil {
		sd = &scaledData{
			seqs: make(map[table.GroupID]scaledSeq),
		}
		st := p.getScales(aes)
		for _, gid := range p.Data().Tables() {
			table := p.Data().Table(gid)
			seq := table.MustColumn(col)
			scaler := st.find(gid)
			p.scaleSet[scaleKey{gid, aes, scaler}] = true
			if _, ok := seq.([]Unscaled); !ok {
				scaler.ExpandDomain(seq)
			}
			sd.seqs[gid] = scaledSeq{seq, scaler}
		}
		p.scaledData[scaledDataKey{aes, p.Data(), col}] = sd
	}
	if aes == "x" || aes == "y" {
		p.autoAxisLabels[aes] = append(p.autoAxisLabels[aes], col)
	}
	return sd
}
func (p *Plot) Save() *Plot {
	p.env = &plotEnv{
		parent: p.env,
		data:   p.env.data,
	}
	return p
}
func (p *Plot) Restore() *Plot {
	if p.env.parent == nil {
		panic("unbalanced Save/Restore")
	}
	p.env = p.env.parent
	return p
}
type Plotter interface {
	Apply(*Plot)
}