diff --git a/plotter/plotter.go b/plotter/plotter.go index 86c668c0..08bcc807 100644 --- a/plotter/plotter.go +++ b/plotter/plotter.go @@ -119,6 +119,27 @@ type XYer interface { XY(int) (x, y float64) } +// XYers wraps a set of (x, y) pair sets. +type XYers interface { + // Len returns the number of XYers. + Len() int + + // LenAt returns the length of XYer i. + LenAt(i int) int + + // XYer returns a set of x, y pairs. + XY(i, j int) (x, y float64) +} + +// xyerAt implements the XYer interface for an item in an XYers. +type xyerAt struct { + XYers + i int +} + +func (xy xyerAt) Len() int { return xy.XYers.LenAt(xy.i) } +func (xy xyerAt) XY(j int) (x, y float64) { return xy.XYers.XY(xy.i, j) } + // XYRange returns the minimum and maximum // x and y values. func XYRange(xys XYer) (xmin, xmax, ymin, ymax float64) { diff --git a/plotter/polygon.go b/plotter/polygon.go index ac7ae5c6..6fd27bb8 100644 --- a/plotter/polygon.go +++ b/plotter/polygon.go @@ -34,11 +34,11 @@ type Polygon struct { // differently, but all built-in backends treat inner rings // with the opposite winding order from the outer ring as // holes. -func NewPolygon(xys ...XYer) (*Polygon, error) { - data := make([]XYs, len(xys)) - for i, d := range xys { +func NewPolygon(rings XYers) (*Polygon, error) { + data := make([]XYs, rings.Len()) + for i := 0; i < rings.Len(); i++ { var err error - data[i], err = CopyXYs(d) + data[i], err = CopyXYs(xyerAt{XYers: rings, i: i}) if err != nil { return nil, err } diff --git a/plotter/polygon_test.go b/plotter/polygon_test.go index 496b303a..ea49c1ff 100644 --- a/plotter/polygon_test.go +++ b/plotter/polygon_test.go @@ -19,6 +19,16 @@ import ( "gonum.org/v1/plot/vg/recorder" ) +// rings implements the XYers interface. +type rings []plotter.XYs + +func (r rings) Len() int { return len(r) } +func (r rings) LenAt(i int) int { return len(r[i]) } +func (r rings) XY(i, j int) (x, y float64) { + p := r[i][j] + return p.X, p.Y +} + // ExamplePolygon_holes draws a polygon with holes, showing how // the different built-in vg backends render polygons with holes. // The output of this example is at @@ -38,7 +48,7 @@ func ExamplePolygon_holes() { // winding order as the outer polygon. inner2 := plotter.XYs{{X: 3.5, Y: 2.5}, {X: 2.5, Y: 2.5}, {X: 2.5, Y: 3.5}, {X: 3.5, Y: 3.5}} - poly, err := plotter.NewPolygon(outer1, inner1, inner2) + poly, err := plotter.NewPolygon(rings{outer1, inner1, inner2}) if err != nil { log.Panic(err) } @@ -94,13 +104,13 @@ func TestPolygon_holes(t *testing.T) { // https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_hexagons_golden.png. func ExamplePolygon_hexagons() { // hex returns a hexagon centered at (x,y) with radius r. - hex := func(x, y, r float64) plotter.XYs { + hex := func(x, y, r float64) rings { g := make(plotter.XYs, 6) for i := range g { g[i].X = x + r*math.Cos(math.Pi/3*float64(i)) g[i].Y = y + r*math.Sin(math.Pi/3*float64(i)) } - return g + return rings{g} } p, err := plot.New() @@ -166,7 +176,7 @@ func TestPolygon_hexagons(t *testing.T) { // polygons wholly outside of the plotting range. func TestPolygon_clip(t *testing.T) { poly, err := plotter.NewPolygon( - plotter.XYs{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, + rings{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}}, ) if err != nil { t.Fatal(err)