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>)