1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
// Copyright ©2016 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package plotter
import (
"image/color"
"math"
"gonum.org/v1/plot"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
// Polygon implements the Plotter interface, drawing a polygon.
type Polygon struct {
// XYs is a copy of the vertices of this polygon.
// Each item in the array holds one ring in the
// Polygon.
XYs []XYs
// LineStyle is the style of the line around the edge
// of the polygon.
draw.LineStyle
// Color is the fill color of the polygon.
Color color.Color
}
// NewPolygon returns a polygon that uses the default line style and
// no fill color, where xys are the rings of the polygon.
// Different backends may render overlapping rings and self-intersections
// 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 {
var err error
data[i], err = CopyXYs(d)
if err != nil {
return nil, err
}
}
return &Polygon{
XYs: data,
LineStyle: DefaultLineStyle,
}, nil
}
// Plot draws the polygon, implementing the plot.Plotter
// interface.
func (pts *Polygon) Plot(c draw.Canvas, plt *plot.Plot) {
trX, trY := plt.Transforms(&c)
ps := make([][]vg.Point, len(pts.XYs))
for i, ring := range pts.XYs {
ps[i] = make([]vg.Point, len(ring))
for j, p := range ring {
ps[i][j].X = trX(p.X)
ps[i][j].Y = trY(p.Y)
}
ps[i] = c.ClipPolygonXY(ps[i])
}
if pts.Color != nil && len(ps) > 0 {
c.SetColor(pts.Color)
var pa vg.Path
for _, ring := range ps {
if len(ring) == 0 {
continue
}
pa.Move(ring[0])
for _, p := range ring {
pa.Line(p)
}
pa.Close()
}
c.Fill(pa)
}
for _, ring := range ps {
if len(ring) > 0 && ring[len(ring)-1] != ring[0] {
ring = append(ring, ring[0])
}
c.StrokeLines(pts.LineStyle, c.ClipLinesXY(ring)...)
}
}
// DataRange returns the minimum and maximum
// x and y values, implementing the plot.DataRanger
// interface.
func (pts *Polygon) DataRange() (xmin, xmax, ymin, ymax float64) {
xmin = math.Inf(1)
xmax = math.Inf(-1)
ymin = math.Inf(1)
ymax = math.Inf(-1)
for _, ring := range pts.XYs {
xmini, xmaxi := Range(XValues{ring})
ymini, ymaxi := Range(YValues{ring})
xmin = math.Min(xmin, xmini)
xmax = math.Max(xmax, xmaxi)
ymin = math.Min(ymin, ymini)
ymax = math.Max(ymax, ymaxi)
}
return
}
// Thumbnail creates the thumbnail for the Polygon,
// implementing the plot.Thumbnailer interface.
func (pts *Polygon) Thumbnail(c *draw.Canvas) {
if pts.Color != nil {
points := []vg.Point{
{X: c.Min.X, Y: c.Min.Y},
{X: c.Min.X, Y: c.Max.Y},
{X: c.Max.X, Y: c.Max.Y},
{X: c.Max.X, Y: c.Min.Y},
}
poly := c.ClipPolygonY(points)
c.FillPolygon(pts.Color, poly)
points = append(points, vg.Point{X: c.Min.X, Y: c.Min.Y})
c.StrokeLines(pts.LineStyle, points)
} else {
y := c.Center().Y
c.StrokeLine2(pts.LineStyle, c.Min.X, y, c.Max.X, y)
}
}
|