diff options
author | Jakob Odersky <jodersky@gmail.com> | 2012-10-18 12:47:14 +0200 |
---|---|---|
committer | Jakob Odersky <jodersky@gmail.com> | 2012-10-18 12:47:14 +0200 |
commit | ccd005b60d87e800718fed605ef4ce2b226b942c (patch) | |
tree | 4c6dbc241c95e1bc4d8c5ac6edbb1dc7e6247b21 | |
parent | a4625a6010de7bd32f791200c688a5b5bb9f7b78 (diff) | |
download | scalam-ccd005b60d87e800718fed605ef4ce2b226b942c.tar.gz scalam-ccd005b60d87e800718fed605ef4ce2b226b942c.tar.bz2 scalam-ccd005b60d87e800718fed605ef4ce2b226b942c.zip |
lots of changes:
*refactor styles to seperate packages
*add io support
*add interpreter support (removes need for seperate matlab instance for every figure))
*add plotting syntactic sugar
16 files changed, 191 insertions, 76 deletions
@@ -1,6 +1,6 @@ name := "scalam" -version := "1.0" +version := "1.0-SNAPSHOT" scalaVersion := "2.9.2" @@ -10,11 +10,6 @@ libraryDependencies += "com.github.scala-incubator.io" %% "scala-io-file" % "0.4 libraryDependencies += "org.scalanlp" %% "breeze-math" % "0.1" -//onejar -seq(com.github.retronym.SbtOneJar.oneJarSettings: _*) - -libraryDependencies += "commons-lang" % "commons-lang" % "2.6" - //libraryDependencies += "org.scala-lang" % "scala-swing" % "2.9.2" diff --git a/project/plugins.sbt b/project/plugins.sbt index 0b524c3..cbf2566 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,3 @@ resolvers += Classpaths.typesafeResolver addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0") - -addSbtPlugin("com.github.retronym" % "sbt-onejar" % "0.8") diff --git a/src/main/scala/scalam/io/package.scala b/src/main/scala/scalam/io/package.scala new file mode 100644 index 0000000..a5217de --- /dev/null +++ b/src/main/scala/scalam/io/package.scala @@ -0,0 +1,23 @@ +package scalam + +import scalax.file.Path +import breeze.linalg._ + +package object io { + + def load(path: Path, separator: String = "\\s"): DenseMatrix[Double] = { + val lines = path.lines().dropWhile(_.isEmpty).toArray + val elements: Array[Array[String]] = lines map (_.split(separator)) + require(elements.forall(_.length == elements(0).length), "Cannot load non-rectangular matrix. Check your data file.") + + val rows: Array[Array[Double]] = elements.map(_.map(_.toDouble)) + DenseMatrix(rows: _*) + } + + def save(path: Path, m: DenseMatrix[Double]): Unit = { + path.createFile(createParents = true, failIfExists = false) + for (processor <- path.outputProcessor; out = processor.asOutput) + for (i <- 0 until m.rows) m(i, ::).valuesIterator.mkString(""," ","\n") + } + +}
\ No newline at end of file diff --git a/src/main/scala/scalam/m/Interpreter.scala b/src/main/scala/scalam/m/Interpreter.scala new file mode 100644 index 0000000..6c23929 --- /dev/null +++ b/src/main/scala/scalam/m/Interpreter.scala @@ -0,0 +1,27 @@ +package scalam.m + +import scalax.file.Path +import scala.sys.process._ +import scala.io._ +import java.io._ +import scala.concurrent._ + +class Interpreter(command: String, pwd: Path) { + + private val inputStream = new SyncVar[OutputStream]; + + val process = Process(command, pwd.fileOption, "" -> "").run( + new ProcessIO( + stdin => inputStream.put(stdin), + stdout => Source.fromInputStream(stdout).getLines.foreach(println), + stderr => Source.fromInputStream(stderr).getLines.foreach(println))); + + def write(s: String): Unit = synchronized { + inputStream.get.write((s + "\n").getBytes) + inputStream.get.flush() + } + + def close(): Unit = { + inputStream.get.close + } +}
\ No newline at end of file diff --git a/src/main/scala/scalam/m/MatlabInterpreter.scala b/src/main/scala/scalam/m/MatlabInterpreter.scala new file mode 100644 index 0000000..94cd148 --- /dev/null +++ b/src/main/scala/scalam/m/MatlabInterpreter.scala @@ -0,0 +1,9 @@ +package scalam.m + +import scalax.file.Path + +class MatlabInterpreter(pwd: Path = Path(".")) extends Interpreter("matlab -nosplash -nodesktop", pwd) { + + def evaluate(statement: ast.Statement) = write(statement.m) + +}
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/FontSize.scala b/src/main/scala/scalam/plotting/FontSize.scala new file mode 100644 index 0000000..edd2323 --- /dev/null +++ b/src/main/scala/scalam/plotting/FontSize.scala @@ -0,0 +1,6 @@ +package scalam.plotting + + +/** Helper class used for implicit font size specification. A font size is nothing but an Int, + * but defining an implicit of type int could pollute a lot of code. */ +case class FontSize(fs: Int)
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/Plot.scala b/src/main/scala/scalam/plotting/Plot.scala index 38a4da6..f354997 100644 --- a/src/main/scala/scalam/plotting/Plot.scala +++ b/src/main/scala/scalam/plotting/Plot.scala @@ -14,7 +14,7 @@ class Plot( grid: Boolean = true, legend: Boolean = true, fontSize: Int = 10, - styleSchemes: Seq[StyleScheme[Style]] = Seq(), + styles: Seq[Style[_]] = Seq(), name: String = "plot" + Plot.next) { val directory = Path(name) @@ -28,7 +28,7 @@ class Plot( lazy val statements: List[Statement] = { def loadData(dataSet: RichDataSet) = Assign(dataSet.id, Function(Identifier("load"), StringLiteral(dataSet.localPath.path))) - val (initial: Seq[Seq[Statement]], styleMaps: Seq[DataSet => Style]) = styleSchemes.map(_.apply(dataSets)).unzip + val (initial: Seq[Seq[Statement]], styleMaps: Iterable[DataSet => StyleElement]) = styles.map(_.apply(dataSets)).unzip val figureId = Identifier("fh") val on = StringLiteral("on") @@ -52,7 +52,6 @@ class Plot( } def legend(dataSets: Seq[DataSet]) = Evaluate(Function(Identifier("legend"), (for (d <- dataSets) yield StringLiteral(d.name)): _*)) :: Nil - def wait(figureId: Identifier) = List(Evaluate(Function(Identifier("waitfor"), Variable(figureId)))) val commands = new scala.collection.mutable.ListBuffer[Statement] commands ++= (for (d <- richDataSets) yield loadData(d)) @@ -66,7 +65,6 @@ class Plot( commands += yLabel(this.yLabel) commands ++= (for (d <- richDataSets) yield plot(d)) if (this.legend) commands ++= legend(dataSets) - commands ++= wait(figureId) commands.toList } @@ -74,10 +72,10 @@ class Plot( val df = new java.text.SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss") val now = (new java.util.Date(System.currentTimeMillis())) - "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + - "% Generated by scalam, v1.0 %\n" + - "% " + df.format(now) + " %\n" + - "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + + "%% Generated by scalam, v1.0\n" + + "%% " + df.format(now) + "\n" + } def save() = { @@ -109,7 +107,7 @@ object Plot { private def randomDataSet(length: Int) = { import scala.util.Random val data = for (i <- 0 until length) yield (i * 1.0, Random.nextDouble() * 10) - val name = Random.nextString(10) + val name = "a" new DataSet(data, name) } diff --git a/src/main/scala/scalam/plotting/Plotter.scala b/src/main/scala/scalam/plotting/Plotter.scala new file mode 100644 index 0000000..9292490 --- /dev/null +++ b/src/main/scala/scalam/plotting/Plotter.scala @@ -0,0 +1,37 @@ +package scalam.plotting + +import scalam.DataSet +import scalam.m._ +import scalam.m.ast._ +import scalam.plotting.styles._ + +trait Plotter { + import Plotter._ + + val pwd: scalax.file.Path + + lazy val interpreter = new MatlabInterpreter(pwd) + + //def plot(dataSets: Seq[DataSet], title: String, x: String, y: String, grid: Boolean = true, legend: Boolean = true)(implicit styles: Seq[Style[_]] = defaultStyles, fontSize: FontSize = defaultFontSize): Plot = + //new Plot(dataSets, title, x, y, grid, legend, styles = styles, fontSize = fontSize.fs) + + def plot(dataSets: Seq[DataSet], title: String, x: String, y: String, grid: Boolean = true, legend: Boolean = true)(implicit styles: Seq[Style[_]] = defaultStyles, fontSize: FontSize = defaultFontSize) = { + val p = new Plot(dataSets, title, x, y, grid, legend, styles = styles, fontSize = fontSize.fs) + p.save() + val s = Evaluate(Function(Identifier("run"), StringLiteral((p.directory / p.localPlotFile).path))) + //val s = "run '" + (p.directory / p.localPlotFile).path + "'" + println(s.m) + interpreter.evaluate(s) + } + + def exit() { + interpreter.write("exit") + interpreter.close() + } + +} + +object Plotter { + val defaultStyles = Seq(color.JET, Uniform(marker.Plus), Uniform(line.Solid)) + val defaultFontSize = FontSize(16) +}
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/package.scala b/src/main/scala/scalam/plotting/package.scala new file mode 100644 index 0000000..3539470 --- /dev/null +++ b/src/main/scala/scalam/plotting/package.scala @@ -0,0 +1,7 @@ +package scalam + +import scalam.plotting.styles._ + +package object plotting { + +}
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/color/colors.scala b/src/main/scala/scalam/plotting/styles/color/colors.scala new file mode 100644 index 0000000..73fa221 --- /dev/null +++ b/src/main/scala/scalam/plotting/styles/color/colors.scala @@ -0,0 +1,26 @@ +package scalam.plotting.styles.color + +import scalam.m.ast._ +import scalam.DataSet +import scalam.plotting.styles.Style +import scalam.plotting.styles.StyleElement + +trait Color extends StyleElement { + def name = StringLiteral("Color") +} + +class RGB(r: Double, g: Double, b: Double) extends Color { + def expression = ArrayLiteral(DoubleLiteral(r), DoubleLiteral(g), DoubleLiteral(b)) +} + +class LiteralColor(value: String) extends Color { + def expression = StringLiteral(value) +} + +case object Red extends LiteralColor("r") +case object Green extends LiteralColor("g") +case object Blue extends LiteralColor("b") +case object Magenta extends LiteralColor("m") +case object Cyan extends LiteralColor("c") +case object Yellow extends LiteralColor("y") +case object Black extends LiteralColor("k")
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/color/styles.scala b/src/main/scala/scalam/plotting/styles/color/styles.scala new file mode 100644 index 0000000..e50f507 --- /dev/null +++ b/src/main/scala/scalam/plotting/styles/color/styles.scala @@ -0,0 +1,20 @@ +package scalam.plotting.styles.color + +import scalam.m.ast._ +import scalam.DataSet +import scalam.plotting.styles.Style +import scalam.plotting.styles.StyleElement + +class MColorStyle(val function: Identifier) extends Style[Color] { + private val ColorVariable = Identifier("cc") + + private def initial(dataSets: Seq[DataSet]) = Seq(Assign(ColorVariable, Function(function, IntLiteral(dataSets.length)))) + private def map(dataSets: Iterable[DataSet]) = (for ((d, i) <- dataSets.zipWithIndex) yield d -> new Color { + def expression = IndexMatrix(ColorVariable, IntLiteral(i + 1), SliceLiteral) + }).toMap + + override def apply(dataSets: Seq[DataSet]) = (initial(dataSets), map(dataSets)) +} + +object HSV extends MColorStyle(Identifier("hsv")) +object JET extends MColorStyle(Identifier("jet"))
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/colors.scala b/src/main/scala/scalam/plotting/styles/colors.scala deleted file mode 100644 index d7463e5..0000000 --- a/src/main/scala/scalam/plotting/styles/colors.scala +++ /dev/null @@ -1,39 +0,0 @@ -package scalam.plotting.styles - -import scalam.m.ast._ -import scalam.DataSet - -trait Color extends Style { - def name = StringLiteral("Color") -} - -class RGB(r: Double, g: Double, b: Double) extends Color { - def expression = ArrayLiteral(DoubleLiteral(r), DoubleLiteral(g), DoubleLiteral(b)) -} - -class LiteralColor(value: String) extends Color { - def expression = StringLiteral(value) -} - -case object Red extends LiteralColor("r") -case object Green extends LiteralColor("g") -case object Blue extends LiteralColor("b") -case object Magenta extends LiteralColor("c") -case object Cyan extends LiteralColor("m") -case object Yellow extends LiteralColor("y") -case object Black extends LiteralColor("k") - - -class MColorScheme(val function: Identifier) extends StyleScheme[Color] { - private val ColorVariable = Identifier("cc") - - private def initial(dataSets: Seq[DataSet]) = Seq(Assign(ColorVariable, Function(function, IntLiteral(dataSets.length)))) - private def map(dataSets: Seq[DataSet]) = (for ((d, i) <- dataSets.zipWithIndex) yield d -> new Color { - def expression = IndexMatrix(ColorVariable, IntLiteral(i + 1), SliceLiteral) - }).toMap - - override def apply(dataSets: Seq[DataSet]) = (initial(dataSets), map(dataSets)) -} - -object HSV extends MColorScheme(Identifier("hsv")) -object JET extends MColorScheme(Identifier("jet"))
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/lines.scala b/src/main/scala/scalam/plotting/styles/line/lines.scala index c8c8bf3..63c6d23 100644 --- a/src/main/scala/scalam/plotting/styles/lines.scala +++ b/src/main/scala/scalam/plotting/styles/line/lines.scala @@ -1,8 +1,9 @@ -package scalam.plotting.styles +package scalam.plotting.styles.line import scalam.m.ast._ +import scalam.plotting.styles.StyleElement -trait Line extends Style { +trait Line extends StyleElement { def name = StringLiteral("LineStyle") } diff --git a/src/main/scala/scalam/plotting/styles/markers.scala b/src/main/scala/scalam/plotting/styles/marker/markers.scala index 6c83cdb..f821396 100644 --- a/src/main/scala/scalam/plotting/styles/markers.scala +++ b/src/main/scala/scalam/plotting/styles/marker/markers.scala @@ -1,9 +1,11 @@ -package scalam.plotting.styles +package scalam.plotting.styles.marker import scalam.DataSet import scalam.m.ast._ +import scalam.plotting.styles.Style +import scalam.plotting.styles.StyleElement -trait Marker extends Style { +trait Marker extends StyleElement { def name = StringLiteral("Marker") } case object Plus extends Marker { def expression = StringLiteral("+") } @@ -19,15 +21,4 @@ case object RightTriangle extends Marker { def expression = StringLiteral(">") } case object LeftTriangle extends Marker { def expression = StringLiteral("<") } case object Pentagram extends Marker { def expression = StringLiteral("p") } case object Hexagram extends Marker { def expression = StringLiteral("h") } -case object NoMarker extends Marker { def expression = StringLiteral("none") } - -object AllMarkerScheme extends StyleScheme[Marker] { - - val markers = List(Plus, Circle, Asterisk, Point, Cross, Square, Diamond, UpTriangle, DownTriangle, RightTriangle, LeftTriangle, Pentagram, Hexagram) - private def map(dataSets: Seq[DataSet]): Map[DataSet, Marker] = dataSets.zipWithIndex.map{ - case (d, i) => d -> markers(i % (markers.length-1)) - }.toMap - - override def apply(dataSets: Seq[DataSet]) = (Seq.empty[Statement], map(dataSets)) - -}
\ No newline at end of file +case object NoMarker extends Marker { def expression = StringLiteral("none") }
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/marker/styles.scala b/src/main/scala/scalam/plotting/styles/marker/styles.scala new file mode 100644 index 0000000..9663778 --- /dev/null +++ b/src/main/scala/scalam/plotting/styles/marker/styles.scala @@ -0,0 +1,17 @@ +package scalam.plotting.styles.marker + +import scalam.DataSet +import scalam.m.ast._ +import scalam.plotting.styles.Style +import scalam.plotting.styles.StyleElement + +object AllMarkerStyle extends Style[Marker] { + + val markers = List(Plus, Circle, Asterisk, Point, Cross, Square, Diamond, UpTriangle, DownTriangle, RightTriangle, LeftTriangle, Pentagram, Hexagram) + private def map(dataSets: Iterable[DataSet]): Map[DataSet, Marker] = dataSets.zipWithIndex.map{ + case (d, i) => d -> markers(i % (markers.length-1)) + }.toMap + + def apply(dataSets: Seq[DataSet]) = (Seq.empty[Statement], map(dataSets)) + +}
\ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/styles.scala b/src/main/scala/scalam/plotting/styles/styles.scala index 7b05955..098fcf7 100644 --- a/src/main/scala/scalam/plotting/styles/styles.scala +++ b/src/main/scala/scalam/plotting/styles/styles.scala @@ -4,18 +4,17 @@ import scalam.DataSet import scalam.m.ast._ -trait Style { +trait StyleElement { //command line option def name: StringLiteral //expression def expression: Expression } - -trait StyleScheme[+S <: Style] { +trait Style[+S <: StyleElement] { def apply(dataSets: Seq[DataSet]): Tuple2[Seq[Statement], DataSet => S] } -case class Uniform[S <: Style](element: S) extends StyleScheme[S] { +case class Uniform[S <: StyleElement](element: S) extends Style[S] { override def apply(dataSets: Seq[DataSet]) = (Seq.empty[Statement], (d: DataSet) => element) }
\ No newline at end of file |