diff --git a/CHANGELOG.md b/CHANGELOG.md index 8404cc39..e8069d0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Changes since last release] +## [0.5.x] - 2018-??-?? +- added a low priority default Theme so this cumbersome import is only necessary when desired +- added a ExplicitImplicit trait to help library authors catch unintentional implicit theme's + ## [0.5.0] - 2018-09-21 ### Added - `ComponentGroup` for combining plot components diff --git a/docs/src/main/tut/getting-started.md b/docs/src/main/tut/getting-started.md index e04fb53d..9a74da50 100644 --- a/docs/src/main/tut/getting-started.md +++ b/docs/src/main/tut/getting-started.md @@ -43,7 +43,6 @@ and take a look at it. ```scala import com.cibo.evilplot._ import com.cibo.evilplot.plot._ -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import com.cibo.evilplot.numeric.Point val data = Seq.tabulate(100) { i => @@ -77,7 +76,6 @@ some labels, so our audience knows what we're talking about. That's easy as well
```scala import com.cibo.evilplot.plot._ -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import com.cibo.evilplot.numeric.Point val data = Seq.tabulate(100) { i => diff --git a/docs/src/main/tut/plot-catalog.md b/docs/src/main/tut/plot-catalog.md index dbad2885..53a1ca58 100644 --- a/docs/src/main/tut/plot-catalog.md +++ b/docs/src/main/tut/plot-catalog.md @@ -293,7 +293,6 @@ A pairs plot can be built by combining `ScatterPlot` and `Histogram` plots with ```scala import com.cibo.evilplot.numeric.Point import com.cibo.evilplot.plot._ -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import scala.util.Random val labels = Vector("a", "b", "c", "d") @@ -332,7 +331,6 @@ A `FunctionPlot` can be used to build density plots. import com.cibo.evilplot.colors.Color import com.cibo.evilplot.numeric.Bounds import com.cibo.evilplot.plot._ -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import com.cibo.evilplot.plot.renderers.PathRenderer import scala.util.Random @@ -379,7 +377,6 @@ Overlay( import com.cibo.evilplot.colors.HTMLNamedColors.{green, red} import com.cibo.evilplot.geometry.Extent import com.cibo.evilplot.plot._ -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import com.cibo.evilplot.plot.renderers.BarRenderer import scala.util.Random @@ -408,7 +405,6 @@ Overlay( ```scala import com.cibo.evilplot.numeric.Point import com.cibo.evilplot.plot._ -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import scala.util.Random val data = Seq.fill(100) { diff --git a/docs/src/main/tut/plots.md b/docs/src/main/tut/plots.md index 349a267f..d71a8661 100644 --- a/docs/src/main/tut/plots.md +++ b/docs/src/main/tut/plots.md @@ -37,7 +37,6 @@ A `PointRenderer` tells your plot how to draw the data. When we don't pass one i import com.cibo.evilplot.numeric.Point import com.cibo.evilplot.plot._ import com.cibo.evilplot.plot.renderers.PointRenderer -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import scala.util.Random val qualities = Seq("good", "bad") diff --git a/docs/src/main/tut/render-context.md b/docs/src/main/tut/render-context.md index 5454e441..63f5f10c 100644 --- a/docs/src/main/tut/render-context.md +++ b/docs/src/main/tut/render-context.md @@ -18,7 +18,6 @@ Started page using `CanvasRenderContext`. ```scala import com.cibo.evilplot.geometry.{CanvasRenderContext, Extent} import com.cibo.evilplot.plot._ -import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ import com.cibo.evilplot.numeric.Point import org.scalajs.dom diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/BarChart.scala b/shared/src/main/scala/com/cibo/evilplot/plot/BarChart.scala index 37b4e1fe..378150cc 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/BarChart.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/BarChart.scala @@ -61,13 +61,13 @@ final case class Bar( ) } -object Bar { +object Bar extends ExplicitImplicits{ def apply(value: Double)(implicit theme: Theme): Bar = Bar(Seq(value), 0, theme.colors.stream) def apply(value: Double, cluster: Int)(implicit theme: Theme): Bar = Bar(Seq(value), cluster = cluster, theme.colors.stream) } -object BarChart { +object BarChart extends ExplicitImplicits{ val defaultBoundBuffer: Double = 0.1 @@ -151,9 +151,9 @@ object BarChart { spacing: Option[Double] = None, boundBuffer: Option[Double] = None )(implicit theme: Theme): Plot = { - val barRenderer = BarRenderer.default(color) - val bars = values.map(Bar(_)) - custom(bars, Some(barRenderer), spacing, None, boundBuffer) + val barRenderer = BarRenderer.default(color)(theme) + val bars = values.map(Bar(_)(theme)) + custom(bars, Some(barRenderer), spacing, None, boundBuffer)(theme) } /** Create a bar chart where bars are divided into clusters. */ @@ -195,7 +195,7 @@ object BarChart { Some(barRenderer), spacing, Some(clusterSpacing.getOrElse(theme.elements.clusterSpacing)), - boundBuffer) + boundBuffer)(theme) } /** Create a stacked bar chart. @@ -220,7 +220,7 @@ object BarChart { val bars = values.map { stack => Bar(stack, colors = colorStream, labels = barLabels, cluster = 0) } - custom(bars, Some(barRenderer), spacing, None, boundBuffer) + custom(bars, Some(barRenderer), spacing, None, boundBuffer)(theme) } /** Create a clustered bar chart of stacked bars. @@ -261,7 +261,7 @@ object BarChart { Some(barRenderer), spacing, Some(clusterSpacing.getOrElse(theme.elements.clusterSpacing)), - boundBuffer) + boundBuffer)(theme) } /** Create a custom bar chart. @@ -292,7 +292,7 @@ object BarChart { ybounds, BarChartRenderer( bars, - barRenderer.getOrElse(BarRenderer.default()), + barRenderer.getOrElse(BarRenderer.default()(theme)), spacing.getOrElse(theme.elements.barSpacing), clusterSpacing ) diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/BoxPlot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/BoxPlot.scala index e213d85e..d02131dc 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/BoxPlot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/BoxPlot.scala @@ -42,7 +42,7 @@ final case class BoxPlotRenderer( pointRenderer: PointRenderer, spacing: Double, clusterSpacing: Option[Double] -) extends PlotRenderer { +) extends PlotRenderer with ExplicitImplicits{ private val isClustered = clusterSpacing.isDefined private val clusterPadding = clusterSpacing.getOrElse(spacing) @@ -106,7 +106,7 @@ final case class BoxPlotRenderer( override def legendContext: LegendContext = boxRenderer.legendContext } -object BoxPlot { +object BoxPlot extends ExplicitImplicits{ /** Create box plots for a sequence of distributions. * @@ -141,7 +141,7 @@ object BoxPlot { boundBuffer, boxRenderer, pointRenderer - ) + )(theme) } @@ -185,7 +185,7 @@ object BoxPlot { boundBuffer, boxRenderer, pointRenderer - ) + )(theme) } private def makePlot( @@ -210,8 +210,8 @@ object BoxPlot { ybounds, BoxPlotRenderer( boxContexts, - boxRenderer.getOrElse(BoxRenderer.default()), - pointRenderer.getOrElse(PointRenderer.default()), + boxRenderer.getOrElse(BoxRenderer.default()(theme)), + pointRenderer.getOrElse(PointRenderer.default()(theme)), spacing.getOrElse(theme.elements.boxSpacing), clusterSpacing ) @@ -236,6 +236,6 @@ object BoxPlot { spacing: Option[Double] = None, boundBuffer: Option[Double] = None )(implicit theme: Theme): Plot = { - apply(data, quantiles, spacing, boundBuffer, Some(boxRenderer), Some(pointRenderer)) + apply(data, quantiles, spacing, boundBuffer, Some(boxRenderer), Some(pointRenderer))(theme) } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/ExplicitImplicits.scala b/shared/src/main/scala/com/cibo/evilplot/plot/ExplicitImplicits.scala new file mode 100644 index 00000000..97fc372b --- /dev/null +++ b/shared/src/main/scala/com/cibo/evilplot/plot/ExplicitImplicits.scala @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018, CiBO Technologies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.cibo.evilplot.plot + +import com.cibo.evilplot.plot.aesthetics.{Theme, DefaultTheme} + +trait ExplicitImplicits{ + implicit val defaultTheme: Theme = DefaultTheme.defaultTheme +} diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/Facets.scala b/shared/src/main/scala/com/cibo/evilplot/plot/Facets.scala index 8fb0aec1..e3b08858 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/Facets.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/Facets.scala @@ -77,7 +77,7 @@ object Facets { row.zipWithIndex.map { case (subplot, xIndex) => val x = xIndex * innerExtent.width - subplot.render(innerExtent).translate(x = x, y = y) + subplot.render(innerExtent)(theme).translate(x = x, y = y) }.group }.group } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/FunctionPlot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/FunctionPlot.scala index fb16f82e..c26bc3eb 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/FunctionPlot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/FunctionPlot.scala @@ -37,7 +37,7 @@ import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.components.FunctionPlotLine import com.cibo.evilplot.plot.renderers.{PathRenderer, PointRenderer} -object FunctionPlot { +object FunctionPlot extends ExplicitImplicits{ val defaultBounds: Bounds = Bounds(0, 1) val defaultNumPoints: Int = 800 @@ -66,7 +66,7 @@ object FunctionPlot { pointRenderer.orElse(Some(PointRenderer.empty())), pathRenderer, xBoundBuffer, - yBoundBuffer) + yBoundBuffer)(theme) } /** Plot a function using a name for the legend. @@ -85,8 +85,8 @@ object FunctionPlot { xBoundBuffer: Option[Double] = None, yBoundBuffer: Option[Double] = None )(implicit theme: Theme): Plot = { - val renderer = Some(PathRenderer.named(name, color, strokeWidth)) - apply(function, xbounds, numPoints, renderer, None, xBoundBuffer, yBoundBuffer) + val renderer = Some(PathRenderer.named(name, color, strokeWidth)(theme)) + apply(function, xbounds, numPoints, renderer, None, xBoundBuffer, yBoundBuffer)(theme) } /** Plot a function using a name for the legend. @@ -104,7 +104,7 @@ object FunctionPlot { strokeWidth: Option[Double], xBoundBuffer: Option[Double], yBoundBuffer: Option[Double])(implicit theme: Theme): Plot = { - val renderer = Some(PathRenderer.default(strokeWidth, Some(color), label)) - apply(function, xbounds, numPoints, renderer, None, xBoundBuffer, yBoundBuffer) + val renderer = Some(PathRenderer.default(strokeWidth, Some(color), label)(theme)) + apply(function, xbounds, numPoints, renderer, None, xBoundBuffer, yBoundBuffer)(theme) } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/Heatmap.scala b/shared/src/main/scala/com/cibo/evilplot/plot/Heatmap.scala index c79159c2..de617e94 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/Heatmap.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/Heatmap.scala @@ -36,7 +36,7 @@ import com.cibo.evilplot.numeric.Bounds import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.PlotRenderer -object Heatmap { +object Heatmap extends ExplicitImplicits{ val defaultColorCount: Int = 10 @@ -80,7 +80,7 @@ object Heatmap { ybounds = ybounds, xfixed = true, yfixed = true, - renderer = HeatmapRenderer(data, colorBar) + renderer = HeatmapRenderer(data, colorBar)(theme) ) } @@ -99,7 +99,7 @@ object Heatmap { val minValue = flattenedData.reduceOption[Double](math.min).getOrElse(0.0) val maxValue = flattenedData.reduceOption[Double](math.max).getOrElse(0.0) val colorBar = ScaledColorBar(colorStream.take(colorCount), minValue, maxValue) - apply(data, colorBar) + apply(data, colorBar)(theme) } def apply(data: Seq[Seq[Double]], @@ -108,9 +108,9 @@ object Heatmap { val minValue = flattenedData.reduceOption[Double](math.min).getOrElse(0.0) val maxValue = flattenedData.reduceOption[Double](math.max).getOrElse(0.0) val useColoring = coloring.getOrElse(theme.colors.continuousColoring) - val colorFunc = useColoring(flattenedData) + val colorFunc = useColoring(flattenedData)(theme) val colorBar = ScaledColorBar(flattenedData.map(point => colorFunc.apply(point)), minValue, maxValue) - apply(data, colorBar) + apply(data, colorBar)(theme) } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/Histogram.scala b/shared/src/main/scala/com/cibo/evilplot/plot/Histogram.scala index 2589181b..6fa845ec 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/Histogram.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/Histogram.scala @@ -35,7 +35,7 @@ import com.cibo.evilplot.numeric.{Bounds, Point} import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.{BarRenderer, PlotRenderer} -object Histogram { +object Histogram extends ExplicitImplicits{ val defaultBinCount: Int = 20 @@ -128,7 +128,7 @@ object Histogram { val clippedY = math.min(point.y * yscale, plot.ybounds.max) val y = ytransformer(clippedY) val barWidth = math.max(xtransformer(point.x + binWidth) - x - spacing, 0) - val bar = Bar(clippedY) + val bar = Bar(clippedY)(theme) val barHeight = yintercept - y barRenderer.render(plot, Extent(barWidth, barHeight), bar).translate(x = x, y = y) }.group @@ -173,7 +173,7 @@ object Histogram { ybounds = Bounds(0, maxY * (1.0 + boundBuffer.getOrElse(theme.elements.boundBuffer))), renderer = HistogramRenderer( values, - barRenderer.getOrElse(BarRenderer.default()), + barRenderer.getOrElse(BarRenderer.default()(theme)), bins, spacing.getOrElse(theme.elements.barSpacing), boundBuffer.getOrElse(theme.elements.boundBuffer), diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/LegendContext.scala b/shared/src/main/scala/com/cibo/evilplot/plot/LegendContext.scala index a0217537..c791baad 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/LegendContext.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/LegendContext.scala @@ -68,7 +68,7 @@ case class LegendContext( } } -object LegendContext { +object LegendContext extends ExplicitImplicits{ def empty: LegendContext = LegendContext() def single( diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/LinePlot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/LinePlot.scala index ae45ed8f..f904e506 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/LinePlot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/LinePlot.scala @@ -36,7 +36,7 @@ import com.cibo.evilplot.numeric.Point import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.{PathRenderer, PointRenderer} -object LinePlot { +object LinePlot extends ExplicitImplicits{ /** Create a line plot from some data. Convenience method on top of XyPlot * @@ -56,10 +56,10 @@ object LinePlot { XyPlot( data, pointRenderer = Some(pointRenderer.getOrElse(PointRenderer.empty())), - pathRenderer = Some(pathRenderer.getOrElse(PathRenderer.default())), + pathRenderer = Some(pathRenderer.getOrElse(PathRenderer.default()(theme))), xboundBuffer.orElse(Some(0)), yboundBuffer - ) + )(theme) } /** Create a line plot from some data. Convenience method on top of XyPlot @@ -78,7 +78,7 @@ object LinePlot { xboundBuffer: Double, yboundBuffer: Double )(implicit theme: Theme): Plot = { - XyPlot(data, Some(pointRenderer), Some(pathRenderer), Some(xboundBuffer), Some(yboundBuffer)) + XyPlot(data, Some(pointRenderer), Some(pathRenderer), Some(xboundBuffer), Some(yboundBuffer))(theme) } /** Create a line plot with the specified name and color. @@ -98,14 +98,14 @@ object LinePlot { yboundBuffer: Option[Double] = None )(implicit theme: Theme): Plot = { val pointRenderer = PointRenderer.empty() - val pathRenderer = PathRenderer.named(name, color, strokeWidth) + val pathRenderer = PathRenderer.named(name, color, strokeWidth)(theme) XyPlot( data, Some(pointRenderer), Some(pathRenderer), xboundBuffer, yboundBuffer - ) + )(theme) } /** Create a line plot with the specified name and color. @@ -125,7 +125,7 @@ object LinePlot { yboundBuffer: Option[Double] )(implicit theme: Theme): Plot = { val pointRenderer = PointRenderer.empty() - val pathRenderer = PathRenderer.default(strokeWidth, Some(color), label) - XyPlot(data, Some(pointRenderer), Some(pathRenderer), xboundBuffer, yboundBuffer) + val pathRenderer = PathRenderer.default(strokeWidth, Some(color), label)(theme) + XyPlot(data, Some(pointRenderer), Some(pathRenderer), xboundBuffer, yboundBuffer)(theme) } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/MixedBoundsOverlay.scala b/shared/src/main/scala/com/cibo/evilplot/plot/MixedBoundsOverlay.scala index 12d49828..c348b091 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/MixedBoundsOverlay.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/MixedBoundsOverlay.scala @@ -41,7 +41,7 @@ object MixedBoundsOverlay { override def legendContext: LegendContext = LegendContext.combine(subplots.map(_.renderer.legendContext)) def render(plot: Plot, plotExtent: Extent)(implicit theme: Theme): Drawable = - Plot.padPlots(Seq(subplots), plotExtent, 0, 0).head.map(_.render(plotExtent)).group + Plot.padPlots(Seq(subplots), plotExtent, 0, 0).head.map(_.render(plotExtent)(theme)).group } /** Overlay plots without updating bounds or transforms for individual plots. diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/Overlay.scala b/shared/src/main/scala/com/cibo/evilplot/plot/Overlay.scala index b3d2cb48..751e8a8d 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/Overlay.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/Overlay.scala @@ -35,7 +35,7 @@ import com.cibo.evilplot.numeric.Bounds import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.PlotRenderer -object Overlay { +object Overlay extends ExplicitImplicits{ // Update subplots to have the specified bounds (if not already fixed). private def updateSubplotBounds( @@ -71,7 +71,7 @@ object Overlay { xbounds = plot.xbounds, ybounds = plot.ybounds ) - updatedPlots.map(_.render(plotExtent)).group + updatedPlots.map(_.render(plotExtent)(theme)).group } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/PieChart.scala b/shared/src/main/scala/com/cibo/evilplot/plot/PieChart.scala index dd64ca0e..354fa958 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/PieChart.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/PieChart.scala @@ -36,7 +36,7 @@ import com.cibo.evilplot.numeric.Bounds import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.PlotRenderer -object PieChart { +object PieChart extends ExplicitImplicits{ case class PieChartRenderer( data: Seq[(Drawable, Double)], diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/Plot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/Plot.scala index 059c2931..df821593 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/Plot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/Plot.scala @@ -57,7 +57,7 @@ final case class Plot( xfixed: Boolean = false, yfixed: Boolean = false, components: Seq[FacetedPlotComponent] = Seq.empty -) { +) extends ExplicitImplicits{ private[plot] def inBounds(point: Point): Boolean = xbounds.isInBounds(point.x) && ybounds.isInBounds(point.y) @@ -135,11 +135,11 @@ final case class Plot( * @param extent the desired size of the resulting Drawable */ def render(extent: Extent = Plot.defaultExtent)(implicit theme: Theme): Drawable = { - val overlays = componentRenderer.renderFront(this, extent) - val backgrounds = componentRenderer.renderBack(this, extent) + val overlays = componentRenderer.renderFront(this, extent)(theme) + val backgrounds = componentRenderer.renderBack(this, extent)(theme) val pextent = plotExtent(extent) val renderedPlot = - renderer.render(this, pextent).resize(pextent).translate(x = plotOffset.x, y = plotOffset.y) + renderer.render(this, pextent)(theme).resize(pextent).translate(x = plotOffset.x, y = plotOffset.y) backgrounds behind renderedPlot behind overlays } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/ScatterPlot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/ScatterPlot.scala index 1bc3a804..a5068857 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/ScatterPlot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/ScatterPlot.scala @@ -36,7 +36,7 @@ import com.cibo.evilplot.numeric.Point import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.{PathRenderer, PointRenderer} -object ScatterPlot { +object ScatterPlot extends ExplicitImplicits{ /** Create a scatter plot from some data. * @param data The points to plot. @@ -49,7 +49,7 @@ object ScatterPlot { pointRenderer: Option[PointRenderer] = None, boundBuffer: Option[Double] = None )(implicit theme: Theme): Plot = { - XyPlot(data, pointRenderer, Some(PathRenderer.empty()), boundBuffer, boundBuffer) + XyPlot(data, pointRenderer, Some(PathRenderer.empty()), boundBuffer, boundBuffer)(theme) } /** Create a scatter plot with the specified name and color. @@ -74,7 +74,7 @@ object ScatterPlot { color, pointSize, boundBuffer - ) + )(theme) /** Create a scatter plot with the specified name and color. * @param data The points to plot. @@ -90,8 +90,8 @@ object ScatterPlot { pointSize: Option[Double], boundBuffer: Option[Double] )(implicit theme: Theme): Plot = { - val pointRenderer = PointRenderer.default(Some(color), pointSize, name) + val pointRenderer = PointRenderer.default(Some(color), pointSize, name)(theme) val pathRenderer = PathRenderer.empty() - XyPlot(data, Some(pointRenderer), Some(pathRenderer), boundBuffer, boundBuffer) + XyPlot(data, Some(pointRenderer), Some(pathRenderer), boundBuffer, boundBuffer)(theme) } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/SurfacePlot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/SurfacePlot.scala index 370e24ea..028b7e94 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/SurfacePlot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/SurfacePlot.scala @@ -36,7 +36,7 @@ import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.SurfaceRenderer.SurfaceRenderContext import com.cibo.evilplot.plot.renderers.{PlotRenderer, SurfaceRenderer} -object SurfacePlot { +object SurfacePlot extends ExplicitImplicits{ private[plot] case class SurfacePlotRenderer( data: Seq[Seq[Seq[Point3]]], surfaceRenderer: SurfaceRenderer @@ -72,7 +72,7 @@ object SurfacePlot { } } -object ContourPlot { +object ContourPlot extends ExplicitImplicits{ import SurfacePlot._ val defaultGridDimensions: (Int, Int) = (100, 100) @@ -120,7 +120,7 @@ object ContourPlot { } } - val sr = surfaceRenderer.getOrElse(SurfaceRenderer.densityColorContours()) + val sr = surfaceRenderer.getOrElse(SurfaceRenderer.densityColorContours()(theme)) Plot( xbounds, ybounds, diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/XyPlot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/XyPlot.scala index 1bfd21ba..44640026 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/XyPlot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/XyPlot.scala @@ -35,7 +35,7 @@ import com.cibo.evilplot.numeric.{Bounds, Point} import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.{PathRenderer, PlotRenderer, PointRenderer} -object XyPlot { +object XyPlot extends ExplicitImplicits{ final case class XyPlotRenderer( data: Seq[Point], @@ -104,8 +104,8 @@ object XyPlot { ybounds, XyPlotRenderer( data, - pointRenderer.getOrElse(PointRenderer.default()), - pathRenderer.getOrElse(PathRenderer.default())) + pointRenderer.getOrElse(PointRenderer.default()(theme)), + pathRenderer.getOrElse(PathRenderer.default()(theme))) ) } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/DefaultTheme.scala b/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/DefaultTheme.scala index 2ea23641..5b9efaa5 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/DefaultTheme.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/DefaultTheme.scala @@ -34,7 +34,7 @@ import com.cibo.evilplot.colors._ import com.cibo.evilplot.colors.ContinuousColoring.gradient import com.cibo.evilplot.geometry.LineStyle -object DefaultTheme { +object DefaultTheme{ private val darkGray: HSLA = HSLA(0, 0, 12, 1.0) private val lightGray: HSLA = HSLA(0, 0, 65, 0.8) private val darkBlue: HSLA = HSLA(211, 38, 48, 1.0) @@ -108,6 +108,5 @@ object DefaultTheme { elements = DefaultElements ) - implicit val defaultTheme: Theme = DefaultTheme - + implicit val defaultTheme: Theme = this.DefaultTheme } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/Theme.scala b/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/Theme.scala index 7060b143..74a42cf6 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/Theme.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/aesthetics/Theme.scala @@ -44,3 +44,14 @@ final case class Theme( colors: Colors, elements: Elements ) + +object Theme { + + /**An automatic default theme at a low priority precedence*/ + implicit val default:Theme = DefaultTheme.defaultTheme + // /**An automatic default theme at a low priority precedence*/ + // implicit val default:Theme = Default.theme + + /**A simpler constructor for a classic Theme without requiring the entire Classic import */ + val classic:Theme = ClassicTheme.classicTheme +} diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/components/Background.scala b/shared/src/main/scala/com/cibo/evilplot/plot/components/Background.scala index b8d0ea9d..51d428d5 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/components/Background.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/components/Background.scala @@ -34,6 +34,7 @@ import com.cibo.evilplot.colors.Color import com.cibo.evilplot.geometry.{Drawable, Extent, Line, Rect, StrokeStyle} import com.cibo.evilplot.plot.Plot import com.cibo.evilplot.plot.aesthetics.Theme +import com.cibo.evilplot.plot.ExplicitImplicits final case class Background( f: (Plot, Extent) => Drawable @@ -43,7 +44,7 @@ final case class Background( def render(plot: Plot, extent: Extent)(implicit theme: Theme): Drawable = f(plot, extent) } -trait BackgroundImplicits { +trait BackgroundImplicits extends ExplicitImplicits{ protected val plot: Plot /** Set the background (this will replace any existing background). diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/components/BorderPlot.scala b/shared/src/main/scala/com/cibo/evilplot/plot/components/BorderPlot.scala index f47a1780..8cb52730 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/components/BorderPlot.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/components/BorderPlot.scala @@ -46,13 +46,13 @@ case class BorderPlot( border .xbounds(plot.xbounds) .copy(xtransform = plot.xtransform) - .render(extent.copy(height = borderSize)) + .render(extent.copy(height = borderSize))(theme) case Position.Bottom => val borderExent = extent.copy(height = borderSize) border .xbounds(plot.xbounds) .copy(xtransform = plot.xtransform) - .render(borderExent) + .render(borderExent)(theme) .rotated(180) .flipX case Position.Left => @@ -60,7 +60,7 @@ case class BorderPlot( border .xbounds(plot.ybounds) .copy(xtransform = plot.xtransform) - .render(borderExtent) + .render(borderExtent)(theme) .resize(borderExtent) .rotated(270) case Position.Right => @@ -68,12 +68,12 @@ case class BorderPlot( border .xbounds(plot.ybounds) .copy(xtransform = plot.xtransform) - .render(borderExtent) + .render(borderExtent)(theme) .resize(borderExtent) .rotated(90) .flipY case _ => - border.render(extent) + border.render(extent)(theme) } } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/components/FacetedPlotComponent.scala b/shared/src/main/scala/com/cibo/evilplot/plot/components/FacetedPlotComponent.scala index 1a08fa4e..bc188940 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/components/FacetedPlotComponent.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/components/FacetedPlotComponent.scala @@ -33,9 +33,10 @@ package com.cibo.evilplot.plot.components import com.cibo.evilplot.geometry.{Drawable, Extent} import com.cibo.evilplot.plot.Plot import com.cibo.evilplot.plot.aesthetics.Theme +import com.cibo.evilplot.plot.ExplicitImplicits /** A component that is aligned with the data of a plot. */ -trait FacetedPlotComponent { +trait FacetedPlotComponent extends ExplicitImplicits{ /** The position of this component. */ val position: Position diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/components/Label.scala b/shared/src/main/scala/com/cibo/evilplot/plot/components/Label.scala index b0f84347..e0ebc2cb 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/components/Label.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/components/Label.scala @@ -34,6 +34,7 @@ import com.cibo.evilplot.colors.{Color, HTMLNamedColors} import com.cibo.evilplot.geometry.{Drawable, Extent, StrokeStyle, Style, Text} import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.Plot +import com.cibo.evilplot.plot.ExplicitImplicits /** A plot label. * @param position The position of this component. @@ -44,7 +45,7 @@ case class Label( position: Position, f: Extent => Drawable, minExtent: Extent -) extends PlotComponent { +) extends PlotComponent with ExplicitImplicits{ override def size(plot: Plot): Extent = minExtent def render(plot: Plot, extent: Extent)(implicit theme: Theme): Drawable = position match { case Position.Top => f(extent).center(extent.width) @@ -59,7 +60,7 @@ object Label { def apply(position: Position, d: Drawable): Label = Label(position, _ => d, d.extent) } -trait LabelImplicits { +trait LabelImplicits extends ExplicitImplicits{ protected val plot: Plot def title(d: Drawable): Plot = plot :+ Label(Position.Top, d) @@ -138,12 +139,12 @@ trait LabelImplicits { label: String, size: Option[Double] = None, color: Option[Color] = None - )(implicit theme: Theme): Plot = bottomLabel(label, size, color) + )(implicit theme: Theme): Plot = bottomLabel(label, size, color)(theme) def yLabel(d: Drawable): Plot = leftLabel(d) def yLabel( label: String, size: Option[Double] = None, color: Option[Color] = None - )(implicit theme: Theme): Plot = leftLabel(label, size, color) + )(implicit theme: Theme): Plot = leftLabel(label, size, color)(theme) } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/components/Legend.scala b/shared/src/main/scala/com/cibo/evilplot/plot/components/Legend.scala index cdcda9a2..936440a6 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/components/Legend.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/components/Legend.scala @@ -34,6 +34,7 @@ import com.cibo.evilplot.geometry._ import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.LegendRenderer import com.cibo.evilplot.plot.{LegendContext, Plot} +import com.cibo.evilplot.plot.ExplicitImplicits case class Legend( position: Position, @@ -55,7 +56,7 @@ case class Legend( } } -trait LegendImplicits { +trait LegendImplicits extends ExplicitImplicits{ protected val plot: Plot private def setLegend( @@ -87,25 +88,25 @@ trait LegendImplicits { def rightLegend( renderer: LegendRenderer = LegendRenderer.vertical(), labels: Option[Seq[String]] = None - )(implicit theme: Theme): Plot = setLegend(Position.Right, renderer, 0, 0.5, labels) + )(implicit theme: Theme): Plot = setLegend(Position.Right, renderer, 0, 0.5, labels)(theme) /** Place a legend on the left side of the plot. */ def leftLegend( renderer: LegendRenderer = LegendRenderer.vertical(), labels: Option[Seq[String]] = None - )(implicit theme: Theme): Plot = setLegend(Position.Left, renderer, 0, 0.5, labels) + )(implicit theme: Theme): Plot = setLegend(Position.Left, renderer, 0, 0.5, labels)(theme) /** Place a legend on the top of the plot. */ def topLegend( renderer: LegendRenderer = LegendRenderer.horizontal(), labels: Option[Seq[String]] = None - )(implicit theme: Theme): Plot = setLegend(Position.Top, renderer, 0.5, 0, labels) + )(implicit theme: Theme): Plot = setLegend(Position.Top, renderer, 0.5, 0, labels)(theme) /** Place a legend on the bottom of the plot. */ def bottomLegend( renderer: LegendRenderer = LegendRenderer.horizontal(), labels: Option[Seq[String]] = None - )(implicit theme: Theme): Plot = setLegend(Position.Bottom, renderer, 0.5, 0, labels) + )(implicit theme: Theme): Plot = setLegend(Position.Bottom, renderer, 0.5, 0, labels)(theme) /** Overlay a legend on the plot. * @param x The relative X position (0 to 1). @@ -117,7 +118,7 @@ trait LegendImplicits { y: Double = 0.0, renderer: LegendRenderer = LegendRenderer.vertical(), labels: Option[Seq[String]] = None - )(implicit theme: Theme): Plot = setLegend(Position.Overlay, renderer, x, y, labels) + )(implicit theme: Theme): Plot = setLegend(Position.Overlay, renderer, x, y, labels)(theme) /** Get the legend as a drawable. */ def renderLegend( @@ -125,6 +126,6 @@ trait LegendImplicits { )(implicit theme: Theme): Option[Drawable] = if (plot.renderer.legendContext.nonEmpty) { val legend = Legend(Position.Right, plot.renderer.legendContext, renderer, 0, 0) - Some(legend.render(plot, legend.size(plot))) + Some(legend.render(plot, legend.size(plot))(theme)) } else None } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotComponent.scala b/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotComponent.scala index e197d5bd..5ae38992 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotComponent.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotComponent.scala @@ -35,7 +35,7 @@ import com.cibo.evilplot.plot.Plot import com.cibo.evilplot.plot.aesthetics.Theme /** A component that is aligned with the data of a plot (used when all facets are treated identically). */ -trait PlotComponent extends FacetedPlotComponent { +trait PlotComponent extends FacetedPlotComponent{ // Render the component (assumes all facets are handled the same way). def render(plot: Plot, extent: Extent)(implicit theme: Theme): Drawable @@ -44,5 +44,5 @@ trait PlotComponent extends FacetedPlotComponent { // This this calls the implementation that ignores facet information. final def render(plot: Plot, extent: Extent, row: Int, column: Int)( implicit theme: Theme): Drawable = - render(plot, extent) + render(plot, extent)(theme) } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotLine.scala b/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotLine.scala index 2a0fd439..ad4207d4 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotLine.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/components/PlotLine.scala @@ -35,6 +35,7 @@ import com.cibo.evilplot.geometry.{Drawable, EmptyDrawable, Extent, Line, LineSt import com.cibo.evilplot.numeric.{Bounds, Point} import com.cibo.evilplot.plot.Plot import com.cibo.evilplot.plot.aesthetics.Theme +import com.cibo.evilplot.plot.ExplicitImplicits import scala.annotation.tailrec @@ -167,7 +168,7 @@ object FunctionPlotLine { } } -trait PlotLineImplicits { +trait PlotLineImplicits extends ExplicitImplicits{ protected val plot: Plot val defaultThickness: Double = 2.0 diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BarRenderer.scala b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BarRenderer.scala index 465908a1..aab03334 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BarRenderer.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BarRenderer.scala @@ -34,13 +34,14 @@ import com.cibo.evilplot.colors.Color import com.cibo.evilplot.geometry._ import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.{Bar, LegendContext, Plot} +import com.cibo.evilplot.plot.ExplicitImplicits trait BarRenderer extends PlotElementRenderer[Bar] { def render(plot: Plot, extent: Extent, category: Bar): Drawable def legendContext: Option[LegendContext] = None } -object BarRenderer { +object BarRenderer extends ExplicitImplicits{ /** Default bar renderer. */ def default( @@ -68,7 +69,7 @@ object BarRenderer { Rect(legSize, legSize).filled(color.getOrElse(theme.colors.bar)) }, label = n - ) + )(theme) } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BoxRenderer.scala b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BoxRenderer.scala index 75b137d8..d3c915a9 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BoxRenderer.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/BoxRenderer.scala @@ -47,8 +47,9 @@ import com.cibo.evilplot.numeric.BoxPlotSummaryStatistics import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.BoxRenderer.BoxRendererContext import com.cibo.evilplot.plot.{LegendContext, Plot} +import com.cibo.evilplot.plot.ExplicitImplicits -trait BoxRenderer extends PlotElementRenderer[BoxRendererContext] { br => +trait BoxRenderer extends PlotElementRenderer[BoxRendererContext] with ExplicitImplicits{ br => def render(plot: Plot, extent: Extent, summary: BoxRendererContext): Drawable def legendContext: LegendContext = LegendContext.empty @@ -76,7 +77,7 @@ trait BoxRenderer extends PlotElementRenderer[BoxRendererContext] { br => } } -object BoxRenderer { +object BoxRenderer extends ExplicitImplicits { final case class BoxRendererContext( summaryStatistics: BoxPlotSummaryStatistics, index: Int, @@ -173,16 +174,16 @@ object BoxRenderer { strokeWidth: Option[Double] = None )(implicit theme: Theme): BoxRenderer = new BoxRenderer { private val useColoring = fillColoring.getOrElse(CategoricalColoring.themed[A]) - private val colorFunc = useColoring(colorDimension) + private val colorFunc = useColoring(colorDimension)(theme) def render(plot: Plot, extent: Extent, context: BoxRendererContext): Drawable = { BoxRenderer - .default(fillColor = Some(colorFunc(colorDimension(context.index)))) + .default(fillColor = Some(colorFunc(colorDimension(context.index))))(theme) .render(plot, extent, context) } override def legendContext: LegendContext = { - useColoring.legendContext(colorDimension, legendGlyph = d => Rect(d)) + useColoring.legendContext(colorDimension, legendGlyph = d => Rect(d))(theme) } } } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/ComponentRenderer.scala b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/ComponentRenderer.scala index 1fc542b8..9737cff3 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/ComponentRenderer.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/ComponentRenderer.scala @@ -34,6 +34,7 @@ import com.cibo.evilplot.geometry.{Drawable, EmptyDrawable, Extent} import com.cibo.evilplot.numeric.Point import com.cibo.evilplot.plot.Plot import com.cibo.evilplot.plot.aesthetics.Theme +import com.cibo.evilplot.plot.ExplicitImplicits /** Renderer for non-plot area components of a plot (labels, etc.). */ trait ComponentRenderer { @@ -48,7 +49,7 @@ trait ComponentRenderer { def plotOffset(plot: Plot): Point } -object ComponentRenderer { +object ComponentRenderer extends ExplicitImplicits{ case class Default() extends ComponentRenderer { @@ -58,7 +59,7 @@ object ComponentRenderer { val plotExtent = plot.plotExtent(extent) plot.topComponents.reverse.foldLeft(empty) { (d, c) => val componentExtent = plotExtent.copy(height = c.size(plot).height) - c.render(plot, componentExtent, 0, 0) + c.render(plot, componentExtent, 0, 0)(theme) .translate(x = plot.plotOffset.x, y = d.extent.height) behind d } } @@ -69,7 +70,7 @@ object ComponentRenderer { .foldLeft((extent.height, empty)) { case ((y, d), c) => val componentExtent = plotExtent.copy(height = c.size(plot).height) - val rendered = c.render(plot, plotExtent, 0, 0) + val rendered = c.render(plot, plotExtent, 0, 0)(theme) val newY = y - rendered.extent.height (newY, rendered.translate(x = plot.plotOffset.x, y = newY) behind d) } @@ -80,7 +81,7 @@ object ComponentRenderer { val plotExtent = plot.plotExtent(extent) plot.leftComponents.foldLeft(empty) { (d, c) => val componentExtent = plotExtent.copy(width = c.size(plot).width) - c.render(plot, componentExtent, 0, 0).translate(y = plot.plotOffset.y) beside d + c.render(plot, componentExtent, 0, 0)(theme).translate(y = plot.plotOffset.y) beside d } } @@ -90,7 +91,7 @@ object ComponentRenderer { .foldLeft((extent.width, empty)) { case ((x, d), c) => val componentExtent = plotExtent.copy(width = c.size(plot).width) - val rendered = c.render(plot, componentExtent, 0, 0) + val rendered = c.render(plot, componentExtent, 0, 0)(theme) val newX = x - rendered.extent.width (newX, rendered.translate(x = newX, y = plot.plotOffset.y) behind d) } @@ -100,22 +101,22 @@ object ComponentRenderer { private def renderOverlay(plot: Plot, extent: Extent)(implicit theme: Theme): Drawable = { val plotExtent = plot.plotExtent(extent) plot.overlayComponents.map { a => - a.render(plot, plotExtent, 0, 0).translate(x = plot.plotOffset.x, y = plot.plotOffset.y) + a.render(plot, plotExtent, 0, 0)(theme).translate(x = plot.plotOffset.x, y = plot.plotOffset.y) }.group } def renderFront(plot: Plot, extent: Extent)(implicit theme: Theme): Drawable = { - renderOverlay(plot, extent) - .behind(renderLeft(plot, extent)) - .behind(renderRight(plot, extent)) - .behind(renderBottom(plot, extent)) - .behind(renderTop(plot, extent)) + renderOverlay(plot, extent)(theme) + .behind(renderLeft(plot, extent)(theme)) + .behind(renderRight(plot, extent)(theme)) + .behind(renderBottom(plot, extent)(theme)) + .behind(renderTop(plot, extent)(theme)) } def renderBack(plot: Plot, extent: Extent)(implicit theme: Theme): Drawable = { val plotExtent = plot.plotExtent(extent) plot.backgroundComponents.map { a => - a.render(plot, plotExtent, 0, 0).translate(x = plot.plotOffset.x, y = plot.plotOffset.y) + a.render(plot, plotExtent, 0, 0)(theme).translate(x = plot.plotOffset.x, y = plot.plotOffset.y) }.group } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PathRenderer.scala b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PathRenderer.scala index 9c14639a..77af103b 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PathRenderer.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PathRenderer.scala @@ -46,13 +46,14 @@ import com.cibo.evilplot.geometry.{ import com.cibo.evilplot.numeric.Point import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.{LegendContext, Plot} +import com.cibo.evilplot.plot.ExplicitImplicits trait PathRenderer extends PlotElementRenderer[Seq[Point]] { def legendContext: LegendContext = LegendContext.empty def render(plot: Plot, extent: Extent, path: Seq[Point]): Drawable } -object PathRenderer { +object PathRenderer extends ExplicitImplicits{ private[renderers] val baseLegendStrokeLength: Double = 8.0 /** The default path renderer. @@ -90,13 +91,13 @@ object PathRenderer { Text(name, theme.fonts.legendLabelSize, theme.fonts.fontFace), theme.colors.legendLabel), lineStyle - ) + )(theme) /** Path renderer for closed paths. The first point is connected to the last point. * @param color the color of this path. */ @deprecated("Use the overload taking a strokeWidth, color, label and lineStyle", "2 April 2018") - def closed(color: Color)(implicit theme: Theme): PathRenderer = closed(color = Some(color)) + def closed(color: Color)(implicit theme: Theme): PathRenderer = closed(color = Some(color))(theme) /** Path renderer for closed paths. The first point is connected to the last point. * @param strokeWidth the stroke width @@ -111,7 +112,7 @@ object PathRenderer { )(implicit theme: Theme): PathRenderer = new PathRenderer { def render(plot: Plot, extent: Extent, path: Seq[Point]): Drawable = { path.headOption.fold(EmptyDrawable(): Drawable) { head => - default(strokeWidth, color, label, lineStyle).render(plot, extent, path :+ head) + default(strokeWidth, color, label, lineStyle)(theme).render(plot, extent, path :+ head) } } } @@ -175,4 +176,4 @@ class DefaultPathRenderer (strokeWidth: Double, ) .group } -} \ No newline at end of file +} diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PlotRenderer.scala b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PlotRenderer.scala index 2e12ef03..6a2a076c 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PlotRenderer.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PlotRenderer.scala @@ -33,9 +33,10 @@ package com.cibo.evilplot.plot.renderers import com.cibo.evilplot.geometry.{Drawable, Extent} import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.{LegendContext, Plot} +import com.cibo.evilplot.plot.ExplicitImplicits /** Renderer for the plot area. */ -trait PlotRenderer { +trait PlotRenderer extends ExplicitImplicits{ def legendContext: LegendContext = LegendContext.empty def render(plot: Plot, plotExtent: Extent)(implicit theme: Theme): Drawable } diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PointRenderer.scala b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PointRenderer.scala index 956f32b5..916b2aab 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PointRenderer.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/PointRenderer.scala @@ -34,13 +34,14 @@ import com.cibo.evilplot.colors._ import com.cibo.evilplot.geometry.{Disc, Drawable, EmptyDrawable, Extent, Style, Text} import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.{LegendContext, LegendStyle, Plot} +import com.cibo.evilplot.plot.ExplicitImplicits trait PointRenderer extends PlotElementRenderer[Int] { def legendContext: LegendContext = LegendContext() def render(plot: Plot, extent: Extent, index: Int): Drawable } -object PointRenderer { +object PointRenderer extends ExplicitImplicits{ val defaultColorCount: Int = 10 @@ -78,7 +79,7 @@ object PointRenderer { size: Option[Double] = None )(implicit theme: Theme): PointRenderer = new PointRenderer { private val useColoring = coloring.getOrElse(theme.colors.continuousColoring) - private val colorFunc = useColoring(depths) + private val colorFunc = useColoring(depths)(theme) private val radius = size.getOrElse(theme.elements.pointSize) def render(plot: Plot, extent: Extent, index: Int): Drawable = { @@ -86,7 +87,7 @@ object PointRenderer { } override def legendContext: LegendContext = - useColoring.legendContext(depths) + useColoring.legendContext(depths)(theme) } /** @@ -103,14 +104,14 @@ object PointRenderer { size: Option[Double] = None )(implicit theme: Theme): PointRenderer = new PointRenderer { private val useColoring = coloring.getOrElse(CategoricalColoring.themed[A]) - private val colorFunc = useColoring(colorDimension) + private val colorFunc = useColoring(colorDimension)(theme) private val radius = size.getOrElse(theme.elements.pointSize) def render(plot: Plot, extent: Extent, index: Int): Drawable = { Disc.centered(radius).filled(colorFunc(colorDimension(index))) } - override def legendContext: LegendContext = useColoring.legendContext(colorDimension) + override def legendContext: LegendContext = useColoring.legendContext(colorDimension)(theme) } /** @@ -138,7 +139,7 @@ object PointRenderer { theme.fonts.fontFace), theme.colors.legendLabel) } - oldDepthColor(depths, labels, bar, None) + oldDepthColor(depths, labels, bar, None)(theme) } /** Render points with colors based on depth. @@ -154,7 +155,7 @@ object PointRenderer { bar: ScaledColorBar, size: Option[Double] )(implicit theme: Theme): PointRenderer = { - oldDepthColor(depths, labels, bar, size) + oldDepthColor(depths, labels, bar, size)(theme) } /** Render points with colors based on depth. @@ -167,7 +168,7 @@ object PointRenderer { depths: Seq[Double], labels: Seq[Drawable], bar: ScaledColorBar - )(implicit theme: Theme): PointRenderer = oldDepthColor(depths, labels, bar, None) + )(implicit theme: Theme): PointRenderer = oldDepthColor(depths, labels, bar, None)(theme) /** Render points with colors based on depth. * @param depths The depths. @@ -186,7 +187,7 @@ object PointRenderer { theme.fonts.fontFace), theme.colors.legendLabel) } - oldDepthColor(depths, labels, bar, None) + oldDepthColor(depths, labels, bar, None)(theme) } // Old `depthColor` implementation, called to by all deprecated `depthColor` diff --git a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/SurfaceRenderer.scala b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/SurfaceRenderer.scala index 38922b48..4433d6dd 100644 --- a/shared/src/main/scala/com/cibo/evilplot/plot/renderers/SurfaceRenderer.scala +++ b/shared/src/main/scala/com/cibo/evilplot/plot/renderers/SurfaceRenderer.scala @@ -36,13 +36,14 @@ import com.cibo.evilplot.numeric.{Bounds, Point, Point3} import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.renderers.SurfaceRenderer.SurfaceRenderContext import com.cibo.evilplot.plot.{LegendContext, Plot} +import com.cibo.evilplot.plot.ExplicitImplicits trait SurfaceRenderer extends PlotElementRenderer[SurfaceRenderContext] { def legendContext(levels: Seq[Double]): LegendContext = LegendContext.empty def render(plot: Plot, extent: Extent, surface: SurfaceRenderContext): Drawable } -object SurfaceRenderer { +object SurfaceRenderer extends ExplicitImplicits{ /** The element renderer context for surface renderers. */ case class SurfaceRenderContext( @@ -91,9 +92,9 @@ object SurfaceRenderer { val surfaceRenderer = getBySafe(points)(_.headOption.flatMap(_.headOption.map(_.z))) .map { bs => val bar = ScaledColorBar(getColorSeq(points.length), bs.min, bs.max) - densityColorContours(bar)(points) + densityColorContours(bar)(points)(theme) } - .getOrElse(contours()) + .getOrElse(contours()(theme)) surfaceRenderer.render(plot, extent, surface) } } @@ -105,7 +106,7 @@ object SurfaceRenderer { surface.currentLevelPaths.headOption .map(pts => contours(Some(pts.headOption.fold(theme.colors.path)(_ => - bar.getColor(surface.currentLevel)))) + bar.getColor(surface.currentLevel))))(theme) .render(plot, extent, surface)) .getOrElse(EmptyDrawable()) } @@ -120,17 +121,17 @@ object SurfaceRenderer { coloring.getOrElse(theme.colors.continuousColoring) def render(plot: Plot, extent: Extent, surface: SurfaceRenderContext): Drawable = { - val color = useColoring(surface.levels).apply(surface.currentLevel) + val color = useColoring(surface.levels)(theme).apply(surface.currentLevel) surface.currentLevelPaths .map( pts => - contours(Some(color), strokeWidth, dashPattern) + contours(Some(color), strokeWidth, dashPattern)(theme) .render(plot, extent, surface)) .group } override def legendContext(levels: Seq[Double]): LegendContext = { - useColoring.legendContext(levels) + useColoring.legendContext(levels)(theme) } } } diff --git a/shared/src/test/scala/com/cibo/evilplot/colors/ColoringSpec.scala b/shared/src/test/scala/com/cibo/evilplot/colors/ColoringSpec.scala index ea08857f..071d02a7 100644 --- a/shared/src/test/scala/com/cibo/evilplot/colors/ColoringSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/colors/ColoringSpec.scala @@ -30,13 +30,12 @@ package com.cibo.evilplot.colors -import com.cibo.evilplot.plot.aesthetics.DefaultTheme.{DefaultElements, DefaultFonts} import com.cibo.evilplot.plot.aesthetics.{Colors, Elements, Fonts, Theme} import org.scalatest.{FunSpec, Matchers} class ColoringSpec extends FunSpec with Matchers { + describe("multi color gradient construction") { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ it("should return a function when Colors has only one element") { val min: Double = 0 val max: Double = 100 @@ -77,8 +76,8 @@ class ColoringSpec extends FunSpec with Matchers { coloring(6.0) shouldBe HTMLNamedColors.blue } } - describe("coloring from the theme") { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme.{DefaultColors => AesColors} + describe("overriding the default color from the theme") { + import com.cibo.evilplot.plot.aesthetics.DefaultTheme.{DefaultElements,DefaultFonts,DefaultColors => AesColors} implicit val overriddenTheme: Theme = Theme( fonts = DefaultFonts, elements = DefaultElements, @@ -90,7 +89,6 @@ class ColoringSpec extends FunSpec with Matchers { } } describe("making a coloring out of a custom mapping") { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ it("should actually use the mapping") { val f = (s: String) => if (s == "hello") HTMLNamedColors.blue else HTMLNamedColors.red val coloring = CategoricalColoring.fromFunction(Seq("hello", "world"), f) diff --git a/shared/src/test/scala/com/cibo/evilplot/geometry/AffineTransformSpec.scala b/shared/src/test/scala/com/cibo/evilplot/geometry/AffineTransformSpec.scala index cda1457c..4b8ea53b 100644 --- a/shared/src/test/scala/com/cibo/evilplot/geometry/AffineTransformSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/geometry/AffineTransformSpec.scala @@ -34,8 +34,6 @@ import org.scalatest.{FunSpec, Matchers} class AffineTransformSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("The AffineTransform") { it("should translate a point") { AffineTransform.identity.translate(1.0, 0.0)(1.0, 1.0) should be((2.0, 1.0)) diff --git a/shared/src/test/scala/com/cibo/evilplot/geometry/DrawableSpec.scala b/shared/src/test/scala/com/cibo/evilplot/geometry/DrawableSpec.scala index c5e916e0..4541501a 100644 --- a/shared/src/test/scala/com/cibo/evilplot/geometry/DrawableSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/geometry/DrawableSpec.scala @@ -34,8 +34,6 @@ import org.scalatest.{FunSpec, Matchers} class DrawableSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("EmptyDrawable") { it("has zero size") { EmptyDrawable().extent shouldBe Extent(0, 0) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/BarChartSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/BarChartSpec.scala index 1a61a7ca..06ffc9bd 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/BarChartSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/BarChartSpec.scala @@ -36,8 +36,6 @@ import org.scalatest.{FunSpec, Matchers} class BarChartSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("BarChart") { it("should have the right bounds without buffer") { val plot = BarChart(Seq[Double](10, 20, 15)) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/BoxPlotSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/BoxPlotSpec.scala index ffcb3ed8..2aa58277 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/BoxPlotSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/BoxPlotSpec.scala @@ -36,8 +36,6 @@ import org.scalatest.{FunSpec, Matchers} class BoxPlotSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("BoxPlot") { it("should have the right extents") { val plot = BoxPlot(Seq(Seq(1.0, 2.0))) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/ContourPlotSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/ContourPlotSpec.scala index 57d97f60..1759cb62 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/ContourPlotSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/ContourPlotSpec.scala @@ -36,8 +36,6 @@ import org.scalatest.{FunSpec, Matchers} class ContourPlotSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("ContourPlot") { it("it has the right bounds") { val plot = ContourPlot(Seq(Point(1, 2), Point(3, 4)), boundBuffer = Some(0.0)) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/ExplicitImplicitsSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/ExplicitImplicitsSpec.scala new file mode 100644 index 00000000..e34de83d --- /dev/null +++ b/shared/src/test/scala/com/cibo/evilplot/plot/ExplicitImplicitsSpec.scala @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, CiBO Technologies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.cibo.evilplot.plot + +import org.scalatest.{FunSpec, Matchers} + +class ExplcitImplicitsSpec extends FunSpec with Matchers { + describe("ExplicitImplicit") { + it("allows default implicit themes"){ + val hist = Histogram(Seq(1, 2, 2)) + hist.render().extent shouldBe Plot.defaultExtent + } + + it("Provides a mechansim to *require* explicit themes at compile time"){ + import com.cibo.evilplot.plot.aesthetics.Theme + object CustomHistogram extends ExplicitImplicits{ + def hist(xs:Seq[Double])(implicit theme:Theme) = Histogram(xs)(theme).render()(theme) + } + CustomHistogram.hist( Seq(1,2,2) ).extent shouldBe Plot.defaultExtent + } + + it("still provides a mechansim to catch accidental default themes at compile time with a type Error"){ + + assertTypeError(""" + import com.cibo.evilplot.plot.aesthetics.Theme + object CustomHistogram extends ExplicitImplicits{ + def hist(xs:Seq[Double])(implicit theme:Theme) = Histogram(xs).render() // both implicit + } + """) + assertTypeError(""" + import com.cibo.evilplot.plot.aesthetics.Theme + object CustomHistogram extends ExplicitImplicits{ + def hist(xs:Seq[Double])(implicit theme:Theme) = Histogram(xs)(theme).render() // first explicit + } + """) + assertTypeError(""" + import com.cibo.evilplot.plot.aesthetics.Theme + object CustomHistogram extends ExplicitImplicits{ + def hist(xs:Seq[Double])(implicit theme:Theme) = Histogram(xs)().render()(theme) //second explicit + } + """) + assertCompiles(""" + import com.cibo.evilplot.plot.aesthetics.Theme + object CustomHistogram extends ExplicitImplicits{ + def hist(xs:Seq[Double])(implicit theme:Theme) = Histogram(xs)(theme).render()(theme) //both explicit + } + """) + } + } +} diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/FacetsSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/FacetsSpec.scala index 7e05630b..deee8504 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/FacetsSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/FacetsSpec.scala @@ -38,8 +38,6 @@ import com.cibo.evilplot.plot.components.{FacetedPlotComponent, Position} class FacetsSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("Facets") { it("is the correct size with one facet") { val inner = ScatterPlot(Seq(Point(1, 1), Point(2, 2))) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/HeatmapSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/HeatmapSpec.scala index b2ce77a1..c961d5c1 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/HeatmapSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/HeatmapSpec.scala @@ -36,8 +36,6 @@ import org.scalatest.{FunSpec, Matchers} class HeatmapSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("Heatmap") { it("has the right bounds") { val plot = Heatmap(Seq(Seq(1), Seq(2))) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/HistogramSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/HistogramSpec.scala index 1c921480..cadf5555 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/HistogramSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/HistogramSpec.scala @@ -36,8 +36,6 @@ import org.scalatest.{FunSpec, Matchers} class HistogramSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("Histogram") { val plot = Histogram(Seq(1.0, 1, 1, 2, 3, 4, 4, 5), boundBuffer = Some(0)) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/MixedBoundsOverlaySpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/MixedBoundsOverlaySpec.scala index 3c04a391..ace4bf75 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/MixedBoundsOverlaySpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/MixedBoundsOverlaySpec.scala @@ -38,8 +38,6 @@ import org.scalatest.{FunSpec, Matchers} class MixedBoundsOverlaySpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("MixedBoundsOverlay") { it("it has the bounds that are set for it") { diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/OverlaySpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/OverlaySpec.scala index 0866bc8f..c2d3da91 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/OverlaySpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/OverlaySpec.scala @@ -38,8 +38,6 @@ import org.scalatest.{FunSpec, Matchers} class OverlaySpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("Overlay") { it("it gets the bounds right for a single plot") { val inner = ScatterPlot(Seq(Point(1.0, 10.0), Point(2.0, 20.0))) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/PlotSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/PlotSpec.scala index 3f847a77..2a277b99 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/PlotSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/PlotSpec.scala @@ -39,8 +39,6 @@ import org.scalatest.{FunSpec, Matchers} class PlotSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - DOMInitializer.init() // Renderer to do nothing. diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/ScatterPlotSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/ScatterPlotSpec.scala index 52c7166a..6ee1866b 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/ScatterPlotSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/ScatterPlotSpec.scala @@ -35,8 +35,6 @@ import org.scalatest.{FunSpec, Matchers} class ScatterPlotSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("ScatterPlot") { it("sets adheres to bound buffers") { val data = Seq(Point(-1, 10), Point(20, -5)) diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/XyPlotSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/XyPlotSpec.scala index 1ca0d725..7a24b351 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/XyPlotSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/XyPlotSpec.scala @@ -36,8 +36,6 @@ import org.scalatest.{FunSpec, Matchers} class XyPlotSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("XyPlot") { it("has the right bounds") { val plot = diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/components/AxesSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/components/AxesSpec.scala index 40e02e4a..6987a3a0 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/components/AxesSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/components/AxesSpec.scala @@ -36,8 +36,6 @@ import org.scalatest.{FunSpec, Matchers} class AxesSpec extends FunSpec with Matchers { - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ - describe("discrete X") { it("should set the default bounds") { val plot = BarChart(Seq(3.0, 4)).xAxis() diff --git a/shared/src/test/scala/com/cibo/evilplot/plot/components/ComponentGroupSpec.scala b/shared/src/test/scala/com/cibo/evilplot/plot/components/ComponentGroupSpec.scala index 2c965ad9..ce8f20bb 100644 --- a/shared/src/test/scala/com/cibo/evilplot/plot/components/ComponentGroupSpec.scala +++ b/shared/src/test/scala/com/cibo/evilplot/plot/components/ComponentGroupSpec.scala @@ -35,9 +35,7 @@ import com.cibo.evilplot.plot.aesthetics.Theme import com.cibo.evilplot.plot.{Plot, XyPlot} import org.scalatest.{FunSpec, Matchers} -class ComponentGroupSpec extends FunSpec with Matchers { - - import com.cibo.evilplot.plot.aesthetics.DefaultTheme._ +class ComponentGroupSpec extends FunSpec with Matchers{ abstract class MockComponent(val position: Position) extends FacetedPlotComponent abstract class LeftComponent extends MockComponent(Position.Left)