Correction on merge. Added Get. Added example.

This commit is contained in:
Daniel Gil 2018-05-29 10:10:33 +02:00
parent 01cb3ccd23
commit 6fdaa341ff
7 changed files with 259 additions and 8 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@
.vscode/ .vscode/
debug.test debug.test
example/debug
example/out.png

9
example/data.txt Normal file
View File

@ -0,0 +1,9 @@
5,7
2,4
35,35
3,6
18,20
20,30
25,28
30,32
10,12

232
example/main.go Normal file
View File

@ -0,0 +1,232 @@
package main
import (
"bufio"
"fmt"
"image/color"
"log"
"os"
"bitbucket.org/differenttravel/interval"
"gonum.org/v1/plot"
"gonum.org/v1/plot/palette"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
"gonum.org/v1/plot/vg/vgimg"
)
const (
MinX = 0
MaxX = 40
)
func main() {
filename := "data.txt"
xys, err := readData(filename)
if err != nil {
log.Fatalf("could not read %s: %v", filename, err)
}
intervals := initIntervals(xys)
err = plotData("out.png", intervals)
if err != nil {
log.Fatalf("could not plot data: %v", err)
}
}
type xy struct{ x, y int }
func readData(path string) ([]xy, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var xys []xy
// read line by line using a scanner (because we don't know if the file will be huge)
s := bufio.NewScanner(f)
for s.Scan() {
var x, y int
_, err := fmt.Sscanf(s.Text(), "%d,%d", &x, &y)
if err != nil {
log.Printf("discarding bad data point %v: %v", s.Text(), err)
}
xys = append(xys, xy{x, y})
}
if err := s.Err(); err != nil {
return nil, fmt.Errorf("could not scan: %v", err)
}
return xys, nil
}
func initIntervals(xys []xy) interval.Intervals {
intervals := interval.NewIntervals(MinX, MaxX)
for _, xy := range xys {
intervals.Add(&interval.Interval{Low: xy.x, High: xy.y})
}
intervals.Sort()
return intervals
}
func convertToPlotterXYs(intervals []*interval.Interval) plotter.XYs {
pxys := plotter.XYs{}
for _, intvl := range intervals {
pxys = append(pxys, struct{ X, Y float64 }{X: float64(intvl.Low), Y: float64(intvl.High)})
}
return pxys
}
func alignPlots(plotItems []*plot.Plot) *vgimg.Canvas {
rows, cols := len(plotItems), 1
plots := make([][]*plot.Plot, rows)
for j := 0; j < rows; j++ {
plots[j] = make([]*plot.Plot, cols)
for i := 0; i < cols; i++ {
p := plotItems[j]
// make sure the horizontal scales match
p.X.Min = MinX
p.X.Max = MaxX
plots[j][i] = p
}
}
img := vgimg.New(vg.Points(512), vg.Points(float64(128*rows)))
dc := draw.New(img)
t := draw.Tiles{
Rows: rows,
Cols: cols,
}
canvases := plot.Align(plots, t, dc)
for j := 0; j < rows; j++ {
for i := 0; i < cols; i++ {
if plots[j][i] != nil {
plots[j][i].Draw(canvases[j][i])
}
}
}
return img
}
func createFileFromCanvas(path string, img *vgimg.Canvas) error {
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("could not create %s: %v", path, err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err := png.WriteTo(f); err != nil {
return fmt.Errorf("could not write to %s: %v", path, err)
}
if err := f.Close(); err != nil {
return fmt.Errorf("could not close %s: %v", path, err)
}
return nil
}
func createPlot(title string, xys plotter.XYs) (*plot.Plot, error) {
p, err := plot.New()
if err != nil {
return nil, fmt.Errorf("could not create plot: %v", err)
}
// Draw a grid behind the data
p.Add(plotter.NewGrid())
p.Title.Text = title
p.HideY()
// p.X.Label.Text = "values"
p.X.Padding = vg.Length(5)
p.Y.Padding = vg.Length(20)
plotIntervals(p, xys)
return p, nil
}
func plotData(path string, intervals interval.Intervals) error {
plots := []*plot.Plot{}
// create Intervals plot
xysIntervals := convertToPlotterXYs(intervals.Get())
p1, err := createPlot("Intervals", xysIntervals)
if err != nil {
return fmt.Errorf("could not create plot: %v", err)
}
plots = append(plots, p1)
// create Gaps plot
xysGaps := convertToPlotterXYs(intervals.Gaps())
p2, err := createPlot("Gaps", xysGaps)
if err != nil {
return fmt.Errorf("could not create plot: %v", err)
}
plots = append(plots, p2)
// create Overlapped `plot
xysOverlapped := convertToPlotterXYs(intervals.Overlapped())
p3, err := createPlot("Overlapped", xysOverlapped)
if err != nil {
return fmt.Errorf("could not create plot: %v", err)
}
plots = append(plots, p3)
// create Merged plot
xysMerged := convertToPlotterXYs(intervals.Merge())
p4, err := createPlot("Merged", xysMerged)
if err != nil {
return fmt.Errorf("could not create plot: %v", err)
}
plots = append(plots, p4)
// join all plots, align them
canvas := alignPlots(plots)
err = createFileFromCanvas("out.png", canvas)
if err != nil {
return err
}
return nil
}
func plotIntervals(p *plot.Plot, xys plotter.XYs) error {
var ps []plot.Plotter
colors := getColors()
numColors := len(colors)
for i, xy := range xys {
label := fmt.Sprintf("(%v,%v)", xy.X, xy.Y)
pXYs := plotter.XYs{{xy.X, float64(i)}, {xy.Y, float64(i)}}
color := colors[i%numColors]
s, err := plotter.NewScatter(pXYs)
if xy.X != xy.Y {
l, err := plotter.NewLine(pXYs)
l.Color = color
l.Width = vg.Points(10)
if err != nil {
return fmt.Errorf("could not create a new line: %v", err)
}
ps = append(ps, l)
p.Legend.Add(label, l)
} else {
s.Color = color
p.Legend.Add(label, s)
}
if err != nil {
return fmt.Errorf("could not create a new scatter: %v", err)
}
ps = append(ps, s)
}
p.Legend.Left = false
p.Add(ps...)
return nil
}
func getColors() []color.Color {
palette := palette.Rainbow(10, 0, 1, 1, 1, 1)
return palette.Colors()
}

6
get.go Normal file
View File

@ -0,0 +1,6 @@
package interval
func (intvls *intervals) Get() []*Interval {
intvls.Sort()
return intvls.Intervals
}

View File

@ -19,6 +19,9 @@ type Intervals interface {
// HasGaps returns true if exists gaps for the introduced intervals between MinLow and MaxHigh // HasGaps returns true if exists gaps for the introduced intervals between MinLow and MaxHigh
HasGaps() bool HasGaps() bool
// Get first sorts (if necessary) and then returns the interval list
Get() []*Interval
// Gaps first sorts (if necessary) and then returns the interval gaps // Gaps first sorts (if necessary) and then returns the interval gaps
Gaps() []*Interval Gaps() []*Interval

View File

@ -10,9 +10,12 @@ func (intvls *intervals) Merge() []*Interval {
func (intvls *intervals) calculateMerged() []*Interval { func (intvls *intervals) calculateMerged() []*Interval {
intvls.Sort() intvls.Sort()
list := []*Interval{} list := []*Interval{}
if len(intvls.Intervals) == 0 {
return list
}
var lastLow int var lastLow int
var lastHigh int var lastHigh int
pendingToAdd := false
for i, intvl := range intvls.Intervals { for i, intvl := range intvls.Intervals {
if i == 0 { if i == 0 {
lastLow = intvl.Low lastLow = intvl.Low
@ -26,16 +29,12 @@ func (intvls *intervals) calculateMerged() []*Interval {
if intvl.High > lastHigh { if intvl.High > lastHigh {
lastHigh = intvl.High lastHigh = intvl.High
} }
pendingToAdd = true
continue continue
} }
list = append(list, &Interval{Low: lastLow, High: lastHigh}) list = append(list, &Interval{Low: lastLow, High: lastHigh})
lastLow = intvl.Low lastLow = intvl.Low
lastHigh = intvl.High lastHigh = intvl.High
pendingToAdd = false
}
if pendingToAdd == true {
list = append(list, &Interval{Low: lastLow, High: lastHigh})
} }
list = append(list, &Interval{Low: lastLow, High: lastHigh})
return list return list
} }

View File

@ -24,8 +24,8 @@ func (intvls *intervals) calculateOverlapped() []*Interval {
lastMaxHigh := math.MinInt64 lastMaxHigh := math.MinInt64
for i, intvl := range intvls.Intervals { for i, intvl := range intvls.Intervals {
if i > 0 { if i > 0 {
lowInBetween := isLowInBetween(lastMinLow, lastMaxHigh, intvl.Low, intvl.High) //inBetweenInclusive(lastMinLow, intvl.Low, intvl.High) || inBetweenInclusive(intvl.Low, lastMinLow, lastMaxHigh) lowInBetween := isLowInBetween(lastMinLow, lastMaxHigh, intvl.Low, intvl.High)
highInBetween := isHighInBetween(lastMinLow, lastMaxHigh, intvl.Low, intvl.High) //inBetweenInclusive(lastMaxHigh, intvl.Low, intvl.High) || inBetweenInclusive(intvl.High, lastMinLow, lastMaxHigh) highInBetween := isHighInBetween(lastMinLow, lastMaxHigh, intvl.Low, intvl.High)
if lowInBetween || highInBetween { if lowInBetween || highInBetween {
greaterLow := max(intvl.Low, lastMinLow) greaterLow := max(intvl.Low, lastMinLow)
lowerHigh := min(intvl.High, lastMaxHigh) lowerHigh := min(intvl.High, lastMaxHigh)