import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.viz.*
import io.data2viz.scale.*
import io.data2viz.shape.*
import io.data2viz.random.*
import io.data2viz.axis.*
const val chartWidth = 1500.0
const val chartHeight = 500.0
const val margins = 40.0
const val totalSamples = 1000
const val rawValuesPerSample = 64
// limit lines
const val minLimit = 80.0
const val maxLimit = 120.0
// forced min-max
const val minY = 60.0
const val maxY = 140.0
// Seeded random number generator
val rng = RandomDistribution.normal(100.0, 5.0)
val xScale = Scales.Continuous.linear {
domain = listOf(.0, (totalSamples - 1).toDouble())
range = listOf(margins, chartWidth - margins)
}
val yScale = Scales.Continuous.linear {
domain = listOf(minY, maxY)
range = listOf(chartHeight - margins, margins)
}
fun main() {
viz {
size = size(chartWidth, chartHeight)
xScale.nice()
drawGrid()
drawLimits()
val samples = randomSamples(totalSamples, rawValuesPerSample, rng)
// ALL RAW VALUES ARE RENDERED
samples.forEachIndexed { index, sample ->
val x = xScale(index)
val path = path {
stroke = Colors.Web.black
}
// min-max
drawPlot(path, x, yScale(sample.min), false)
drawPlot(path, x, yScale(sample.max), false)
if (sample.rawValues.isNotEmpty()) {
sample.rawValues.forEach { rawValue ->
drawPlot(path, x, yScale(rawValue), true)
}
}
}
drawAxes()
}.bindRendererOnNewCanvas()
}
private fun Viz.drawPlot(path: PathNode, x:Double, y:Double, plotOnly:Boolean) {
if (!plotOnly) path.apply { lineTo(x, y) }
group {
transform { translate(x, y) }
Symbols.Square.symbol.render(path {
stroke = Colors.Web.black
fill = Colors.Web.black
}, 5.0)
}
}
private fun Viz.drawGrid() {
(0 .. totalSamples step 100).forEach {
val x = xScale(it)
path {
stroke = Colors.Web.grey
moveTo(x, margins)
lineTo(x, chartHeight - margins)
}
}
(minY.toInt() .. maxY.toInt() step 10).forEach {
val y = yScale(it)
path {
stroke = Colors.Web.grey
moveTo(margins, y)
lineTo(chartWidth - margins, y)
}
}
}
private fun Viz.drawLimits() {
path {
val y = yScale(minLimit)
stroke = Colors.Web.red
moveTo(margins, y)
lineTo(chartWidth - margins, y)
}
path {
val y = yScale(maxLimit)
stroke = Colors.Web.red
moveTo(margins, y)
lineTo(chartWidth - margins, y)
}
}
private fun Viz.drawAxes() {
group {
transform { translate(x = margins) }
axis(Orient.LEFT, yScale)
}
group {
transform { translate(y = chartHeight - margins) }
axis(Orient.BOTTOM, xScale)
}
}
private fun randomSamples(numSamples: Int, numRecords: Int, rng: RandomGenerator): List<Sample> {
return Array(numSamples) {
Array(numRecords) { rng() }
}.map { rawValues ->
var min = Double.MAX_VALUE
var max = Double.MIN_VALUE
var mean = .0
rawValues.forEach { datum ->
if (datum < min) min = datum
if (datum > max) max = datum
mean += datum
}
mean /= numRecords
Sample(min, max, mean, rawValues)
}
}
data class Sample(val min: Double, val max: Double, val mean: Double, val rawValues:Array<Double>)