com.wbillingsley.veautiful.templates.ScatterPlot.scala Maven / Gradle / Ivy
package com.wbillingsley.veautiful.templates
import com.wbillingsley.veautiful.html.{<, DElement, SVG, VDomNode, ^}
case class ScatterPlot(plotWidth:Int, plotHeight:Int, xName:String, yName:String, xLabel:(Double) => String, yLabel:(Double) => String, defaultxMax:Double, defaultyMax:Double) {
def tickInterval(max:Double, num:Int):Double = max / num
val marginLeft = 100
val marginTop = 30
val marginBottom = 70
val marginRight = 40
val viewBox = s"${-marginLeft} ${-marginTop} ${plotWidth + marginLeft + marginRight} ${plotHeight + marginTop + marginBottom}"
def plot(data:Seq[(Double, Double)]) = {
val xMax = if (data.isEmpty) defaultxMax else {
Math.max(defaultxMax, data.maxBy(_._1)._1)
}
val yMax = if (data.isEmpty) defaultyMax else {
Math.max(defaultyMax, data.maxBy(_._2)._2)
}
val xInterval = tickInterval(xMax, 10)
def xScale(v:Double):Int = {
val adj = 10 * xInterval
val ratio = plotWidth.toDouble / adj
(ratio * v).toInt
}
val yInterval = tickInterval(yMax, 5)
def yScale(v:Double):Int = {
val adj = 5 * yInterval
val ratio = plotHeight.toDouble / adj
plotHeight - (ratio * v).toInt
}
def xAxis(ticks:Int, name:String) = {
SVG.g(
SVG.line(^.attr("x1") := "0", ^.attr("x2") := plotWidth.toString, ^.attr("y1") := plotHeight.toString, ^.attr("y2") := plotHeight.toString),
SVG.text(
^.attr("x") := plotWidth.toString, ^.attr("y") := s"${plotHeight + 60}", ^.cls := "axis-label-x", name
),
for {
i <- 0 to ticks
} yield {
val v = i * xInterval
val x = xScale(v)
SVG.g(
SVG.line(
^.attr("x1") := x.toString, ^.attr("x2") := x.toString, ^.attr("y1") := plotHeight.toString, ^.attr("y2") := (plotHeight + 10).toString, ^.cls := "tick-line"
),
SVG.text(
^.attr("y") := (plotHeight + 30).toString, ^.attr("x") := x.toString, ^.cls := "tick-label-x",
xLabel(v)
)
)
}
)
}
def yAxis(ticks:Int, name:String) = {
SVG.g(
SVG.line(
^.attr("x1") := "0", ^.attr("x2") := "0", ^.attr("y1") := "0", ^.attr("y2") := plotHeight.toString
),
SVG.text(
^.attr("x") := "0", ^.attr("y") := "-50", ^.cls := "axis-label-y", name
),
for {
i <- 0 to ticks
} yield {
val v = i * yInterval
val y = yScale(v)
SVG.g(
SVG.line(
^.attr("x1") := "0", ^.attr("x2") := "-10", ^.attr("y1") := y.toString, ^.attr("y2") := y.toString, ^.cls := "tick-line"
),
SVG.text(^.attr("y") := y.toString, ^.attr("x") := "-20", ^.cls := "tick-label-y",
yLabel(v)
)
)
}
)
}
def plotPoint(x:Double, y:Double) = {
val cx = xScale(x)
val cy = yScale(y)
SVG.circle(
^.attr("cx") := cx.toString, ^.attr("cy") := cy.toString, ^.attr("r") := "3", ^.cls := "plot-point"
)
}
<.svg(
^.cls := "scatterplot",
^.attr("viewBox") := viewBox,
^.attr("width") := plotWidth,
^.attr("height") := plotHeight
)(
xAxis(10, xName),
yAxis(5, yName),
for { (x, y) <- data } yield plotPoint(x, y)
)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy