## Scala API to Plot Widgets¶

### Overview¶

The Scala API for plot widgets maps the widgets' Java bean-style properties to Scala properties. It provides both type safety and a script-friendly syntax with the same techniques as ScalaFX.

For example, the operations getDisplayName and setDisplayName turn into a displayName getter and setter. In general, methods that expect a Java (or Groovy) List will accept a Scala Seq. Arrays should also work where a Seq is expected, although Array is not a subtype of Seq.

The Java/Groovy API frequently allows setting some property to be either a single value or a list of values. In those cases, there are typically two getters, one with a singular name (like the setter) and one with a plural name (e.g., getFill and getFills). The Scala API follows this pattern for consistency. If the single value might be undefined, the getter will be an Option type. If the list value is undefined, the API will map it to an empty Seq.

### Usage¶

Groovy allows setting an arbitrary set of properties when constructing an object. To get similar terseness in creating Scala plot widgets, you can use the anonymous subclass initialization idiom. Instead of this:

val myLine = new Line()
myLine.x = 1 to 3
myLine.y = 5 to 15 by 5


you can use this:

val myLine = new Line {
x = 1 to 3
y = 5 to 15 by 5
}


This allows setting the properties without creating a named value for the new Line in many cases:

val plot = new Plot
plot.add(new Line { x = 1 to 3; y = (1 to 3) map (1.0 / _) })


Note that this syntax is not using named parameters. There is one important caveat about using this style: inside the body of the initializer, the names of the getters will shadow any identifiers in the outer scope. So, don't try this:

// THIS WON'T BEHAVE AS EXPECTED!
val x = List(1, 2, 3)
val myPoints = new Points {
x = x
}


Instead of using the outer value of x, this will just invoke the getter for x.

#### Compatibility¶

In general, the Scala plot classes extend the corresponding Java classes. This means that the set<Property> methods are still available on the Scala objects. This may be useful if you need to deal with data that is not statically typed.

In [ ]:
val plot = new Plot()
val y1 = Seq(1.5, 1, 6, 5, 2, 8)
val cs = Seq(Color.black, Color.red, Color.gray, Color.green, Color.blue, Color.pink)
val ss = Seq(StrokeType.SOLID, StrokeType.SOLID, StrokeType.DASH, StrokeType.DOT, StrokeType.DASHDOT, StrokeType.LONGDASH)
y = y1
color = cs
style = ss
width = 5
})

In [ ]:
val plot = new Plot { title = "Elo" }
var cs = new Color(255, 0, 0, 128)// transparent bars
//cs[3] = Color.red // set color of a single bar, solid colored bar
x = Seq(1, 2, 3, 4)
y = Seq(3, 5, 2, 3, 7)
color = cs
outlineColor = Color.black
width = 0.3
})

In [ ]:
val plot = new Plot { title = "Changing Point Size, Color, Shape" }
val y1 = Seq(6, 7, 12, 11, 8, 14)
val y2 = y1.map(x => x - 1)
val y3 = y1.map(x => x - 2)
val y4 = y1.map(x => x - 3)
plot.add(new Points { y = y1 })
y = y2
shape = ShapeType.CIRCLE
})
y = y3
size = 8.0
shape = ShapeType.DIAMOND
})
y = y4
size = 12.0
color = Color.orange
outlineColor = Color.red
})

In [ ]:
val plot = new Plot { title = "Changing point properties with list" }
val cs = Seq(Color.black, Color.red, Color.orange, Color.green, Color.blue, Color.pink)
val ss = Seq(6.0, 9.0, 12.0, 15.0, 18.0, 21.0)
val fs = Seq(false, false, false, true, false, false)

val list = List(
new Points {
y = Seq(5,5,5,5,5,5)
size = 12.0
color = cs
},
new Points {
y = Seq(4,4,4,4,4,4)
size = 12.0
color = Color.gray
outlineColor = cs
},
new Points {
y = Seq(3,3,3,3,3,3)
size = 12
color = Color.red
},
new Points {
y = Seq(2,2,2,2,2,2)
size = 12.0
color = Color.black
fill = fs
outlineColor = Color.black
}
)


In [ ]:
val plot = new Plot()
val ys = Seq(3, 5, 2, 3)
val x0 = Seq(0, 1, 2, 3)
val x1 = Seq(3, 4, 5, 8)
x = x0
y = ys
})
x = x1
y = ys
color = new Color(128, 128, 128, 50)
interpolation = 0
})

In [ ]:
val p = new Plot()
y = Seq(3, 6, 12, 24)
displayName = "Median"
})
base = Seq(4, 8, 16, 32)
y = Seq(2, 4, 8, 16)
color = new Color(255, 0, 0, 50)
displayName = "Q1 to Q3"
})

In [ ]:
val y1 = Seq(1,5,3,2,3)
val y2 = Seq(7,2,4,1,3)
val p = new Plot { title = "Plot with XYStacker"; initHeight = 200 }
val a1 = new Area { y = y1; displayName =  "y1" }
val a2 = new Area { y = y2; displayName = "y2" }

In [ ]:
val p = new Plot()
p.add(new Line { y = Seq(-1, 1) })
x = 0
y = 0.65
style = StrokeType.DOT
color = Color.blue
})
x = 0
y = 1
style = StrokeType.DASHDOT
color = Color.blue
})
x = 1
y = 0.4
color = Color.gray
width = 5
showLabel = true
})

In [ ]:
val constBand = new ConstantBand { x = Seq(1, 2); y = Seq(1, 3) }
val lineVal = new Line { y = Seq(-3, 1, 3, 4, 5) }
val plot = new Plot()

In [ ]:
val p = new Plot()
p.add(new Line { x = Seq(-3, 1, 2, 4, 5); y = Seq(4, 2, 6, 1, 5) })
p.add(new ConstantBand { x = Seq(Double.NegativeInfinity, 1); color = new Color(128, 128, 128, 50) })
p.add(new ConstantBand { x = Seq(1, 2) })
p.add(new ConstantBand { x = Seq(4, Double.PositiveInfinity) })

In [ ]:
val plot = new Plot()
val xs = Seq(1,2,3,4,5,6,7,8,9,10)
val ys = Seq(8.6, 6.1, 7.4, 2.5, 0.4, 0.0, 0.5, 1.7, 8.4, 1)

x = xs
y = ys
})

def label(i: Int, ys: Seq[Double]): String = {
val leftSign = Math.signum(ys(i) - ys(i - 1))
val rightSign = Math.signum(ys(i + 1) - ys(i))
(leftSign, rightSign) match {
case (1, -1) => "max"
case (-1, 1) => "min"
case (1, _) => "rising"
case (-1, _) => "falling"
case _ => ""
}
}

for (i <- 0 to xs.size) {
if (i > 0 && i < xs.size - 1) {
x = xs(i)
y = ys(i)
text = label(i, ys)
pointerAngle = -i/3.0
})
}
}

plot

In [ ]:
val ch = new Crosshair {
color = new Color(255, 128, 5)
width = 2
style = StrokeType.DOT
}
val pp = new Plot {
crosshair = ch
omitCheckboxes = true
legendLayout = LegendLayout.HORIZONTAL
legendPosition = LegendPosition.TOP
}
def xs = Seq(1, 4, 6, 8, 10)
def ys = Seq(3, 6, 4, 5, 9)
displayName = "Line"
x = xs
y = ys
width = 3
})
displayName = "Bar"
x = Seq(1,2,3,4,5,6,7,8,9,10)
y = Seq(2, 2, 4, 4, 2, 2, 0, 2, 2, 4)
width = 0.5
})
x = xs
y = ys
size = 10
// TODO: implement ToolTipBuilder
toolTip = Seq("x = ","y = ")
})

In [ ]:
val rates = new CSV().readFile("../resources/data/interest-rates.csv")

new SimpleTimePlot {
data = rates
columns = Seq("y1", "y10")
yLabel = "Price"
displayNames = Seq("All", "1 Year", "10 Year")
}

In [ ]:
val points = 100
val logBase = 10

val xs = for (i <- 0 to points) yield i / 15.0
val expys = for (x <- xs) yield Math.exp(x)

val cplot = new CombinedPlot()
val logYPlot = new Plot {
title = "Linear x, Log y"
xLabel = "Log"
logY=true
yLogBase = logBase
}
displayName = "f(x) = exp(x)"
x = xs
y = expys
width = 3.0f
})
displayName = "g(x) = x"
x = xs
y = xs
width = 3.0f
})

val linearYPlot = new Plot {
title = "Linear x, Linear y"
xLabel = "Linear"
}
displayName = "f(x) = exp(x)"
x = xs
y = expys
width = 3.0f
})
displayName = "g(x) = x"
x = xs
y = xs
width = 3.0f
})

cplot

In [ ]:
val points = 100
val logBase = 10
val xs = for (i <- 0 to points) yield i / 15.0
val expys = for (x <- xs) yield Math.exp(x)

val plot = new Plot {
title = "Log x, Log y"
xLabel = "Log"
yLabel = "Log"
logX = true
xLogBase = logBase
logY = true
yLogBase = logBase
}

displayName = "f(x) = exp(x)"
x = xs
y = expys
width = 3.0f
})
displayName = "g(x) = x"
x = xs
y = xs
width = 3.0f
})
plot

In [ ]:
import java.util.{Calendar, Date}
import java.util.SimpleTimeZone

val cal = Calendar.getInstance();

val today = new Date()
val millis = today.getTime()
val hour = 1000 * 60 * 60;

val plot = new TimePlot {
timeZone = new SimpleTimeZone(10800000, "America/New_York")
}

//list of milliseconds
x = (0 to 10).map(x => millis + hour * x)
y = (0 to 10)
size = 10
displayName = "milliseconds"
})

x = (0 to 10).map(x => {cal.add(Calendar.HOUR, 1); cal.getTime()})
y = (0 to 10)
size = 5
displayName = "date objects"
})

In [ ]:
import java.util.Date

val today  = new Date()
val millis = today.getTime()
val nanos  = millis * 1000 * 1000// g makes it arbitrary precision
val np = new NanoPlot()

x = (0 to 10).map(x => nanos + 7 * x)
y = (0 to 10)
})

In [ ]:
import scala.util.Random

val r = new Random()
val p = new Plot {
labelStyle = "font-size:32px; font-weight: bold; font-family: courier; fill: green;"
gridLineStyle = "stroke: purple; stroke-width: 3;"
titleStyle = "color: green;"
}

x = (1 to 1000).map(x => r.nextGaussian() * 10.0d)
y = (1 to 1000).map(x => r.nextGaussian() * 20.0d)
})

In [ ]:
import java.nio.file.Files
import java.io.File

val picture: Array[Byte] = Files.readAllBytes(new File("../resources/img/widgetArch.png").toPath())

var p =  new Plot();
// x y width height are coordinates, opacity is a double in 0~1

// image can be loaded via bytes, filepath, or url
x = List(-10,3)
y = List(3,1.5)
height = List(6,5)
width = List(10,8)
opacity = List(1,0.5)
dataString = picture
})

//p << new Rasters(x: -1, y: 4.5, width: 5, height: 8, opacity:0.5, filePath: "../resources/img/widgetArch.png");
x = List(-4)
y = List(10.5)
height = List(2)
width = List(10)
opacity = List(1)
fileUrl = "https://www.twosigma.com/static/img/twosigma.png"
})

// a list of images!
def xs = List(-8, -5, -3, -2, -1, 1, 2, 4, 6, 8)
def ys = List(4, 5, 1, 2, 0 ,3, 6, 4, 5, 9)
def widths = List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
def opacities = List(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1)
x = xs
y = ys
width = widths
height = widths
opacity = opacities
fileUrl = "http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/sign-check-icon.png"
})

In [ ]:
val plot = new Plot { title = "Setting 2nd Axis bounds" }
val ys = List(0, 2, 4, 6, 15, 10)
val ys2 = List(-40, 50, 6, 4, 2, 0)
val ys3 = List(3, 6, 3, 6, 70, 6)