From ccd005b60d87e800718fed605ef4ce2b226b942c Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Thu, 18 Oct 2012 12:47:14 +0200 Subject: 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 --- build.sbt | 7 +--- project/plugins.sbt | 2 -- src/main/scala/scalam/io/package.scala | 23 +++++++++++++ src/main/scala/scalam/m/Interpreter.scala | 27 +++++++++++++++ src/main/scala/scalam/m/MatlabInterpreter.scala | 9 +++++ src/main/scala/scalam/plotting/FontSize.scala | 6 ++++ src/main/scala/scalam/plotting/Plot.scala | 16 ++++----- src/main/scala/scalam/plotting/Plotter.scala | 37 ++++++++++++++++++++ src/main/scala/scalam/plotting/package.scala | 7 ++++ .../scalam/plotting/styles/color/colors.scala | 26 +++++++++++++++ .../scalam/plotting/styles/color/styles.scala | 20 +++++++++++ src/main/scala/scalam/plotting/styles/colors.scala | 39 ---------------------- .../scala/scalam/plotting/styles/line/lines.scala | 14 ++++++++ src/main/scala/scalam/plotting/styles/lines.scala | 13 -------- .../scalam/plotting/styles/marker/markers.scala | 24 +++++++++++++ .../scalam/plotting/styles/marker/styles.scala | 17 ++++++++++ .../scala/scalam/plotting/styles/markers.scala | 33 ------------------ src/main/scala/scalam/plotting/styles/styles.scala | 7 ++-- 18 files changed, 221 insertions(+), 106 deletions(-) create mode 100644 src/main/scala/scalam/io/package.scala create mode 100644 src/main/scala/scalam/m/Interpreter.scala create mode 100644 src/main/scala/scalam/m/MatlabInterpreter.scala create mode 100644 src/main/scala/scalam/plotting/FontSize.scala create mode 100644 src/main/scala/scalam/plotting/Plotter.scala create mode 100644 src/main/scala/scalam/plotting/package.scala create mode 100644 src/main/scala/scalam/plotting/styles/color/colors.scala create mode 100644 src/main/scala/scalam/plotting/styles/color/styles.scala delete mode 100644 src/main/scala/scalam/plotting/styles/colors.scala create mode 100644 src/main/scala/scalam/plotting/styles/line/lines.scala delete mode 100644 src/main/scala/scalam/plotting/styles/lines.scala create mode 100644 src/main/scala/scalam/plotting/styles/marker/markers.scala create mode 100644 src/main/scala/scalam/plotting/styles/marker/styles.scala delete mode 100644 src/main/scala/scalam/plotting/styles/markers.scala diff --git a/build.sbt b/build.sbt index 407a5e3..b3f087e 100644 --- a/build.sbt +++ b/build.sbt @@ -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/line/lines.scala b/src/main/scala/scalam/plotting/styles/line/lines.scala new file mode 100644 index 0000000..63c6d23 --- /dev/null +++ b/src/main/scala/scalam/plotting/styles/line/lines.scala @@ -0,0 +1,14 @@ +package scalam.plotting.styles.line + +import scalam.m.ast._ +import scalam.plotting.styles.StyleElement + +trait Line extends StyleElement { + def name = StringLiteral("LineStyle") +} + +case object Solid extends Line {def expression = StringLiteral("-")} +case object Dashed extends Line {def expression = StringLiteral("--")} +case object Dotted extends Line {def expression = StringLiteral(":")} +case object DashDot extends Line {def expression = StringLiteral("-.")} +case object NoLine extends Line {def expression = StringLiteral("none")} \ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/lines.scala b/src/main/scala/scalam/plotting/styles/lines.scala deleted file mode 100644 index c8c8bf3..0000000 --- a/src/main/scala/scalam/plotting/styles/lines.scala +++ /dev/null @@ -1,13 +0,0 @@ -package scalam.plotting.styles - -import scalam.m.ast._ - -trait Line extends Style { - def name = StringLiteral("LineStyle") -} - -case object Solid extends Line {def expression = StringLiteral("-")} -case object Dashed extends Line {def expression = StringLiteral("--")} -case object Dotted extends Line {def expression = StringLiteral(":")} -case object DashDot extends Line {def expression = StringLiteral("-.")} -case object NoLine extends Line {def expression = StringLiteral("none")} \ No newline at end of file diff --git a/src/main/scala/scalam/plotting/styles/marker/markers.scala b/src/main/scala/scalam/plotting/styles/marker/markers.scala new file mode 100644 index 0000000..f821396 --- /dev/null +++ b/src/main/scala/scalam/plotting/styles/marker/markers.scala @@ -0,0 +1,24 @@ +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 StyleElement { + def name = StringLiteral("Marker") +} +case object Plus extends Marker { def expression = StringLiteral("+") } +case object Circle extends Marker { def expression = StringLiteral("o") } +case object Asterisk extends Marker { def expression = StringLiteral("*") } +case object Point extends Marker { def expression = StringLiteral(".") } +case object Cross extends Marker { def expression = StringLiteral("x") } +case object Square extends Marker { def expression = StringLiteral("s") } +case object Diamond extends Marker { def expression = StringLiteral("d") } +case object UpTriangle extends Marker { def expression = StringLiteral("^") } +case object DownTriangle extends Marker { def expression = StringLiteral("v") } +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") } \ 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/markers.scala b/src/main/scala/scalam/plotting/styles/markers.scala deleted file mode 100644 index 6c83cdb..0000000 --- a/src/main/scala/scalam/plotting/styles/markers.scala +++ /dev/null @@ -1,33 +0,0 @@ -package scalam.plotting.styles - -import scalam.DataSet -import scalam.m.ast._ - -trait Marker extends Style { - def name = StringLiteral("Marker") -} -case object Plus extends Marker { def expression = StringLiteral("+") } -case object Circle extends Marker { def expression = StringLiteral("o") } -case object Asterisk extends Marker { def expression = StringLiteral("*") } -case object Point extends Marker { def expression = StringLiteral(".") } -case object Cross extends Marker { def expression = StringLiteral("x") } -case object Square extends Marker { def expression = StringLiteral("s") } -case object Diamond extends Marker { def expression = StringLiteral("d") } -case object UpTriangle extends Marker { def expression = StringLiteral("^") } -case object DownTriangle extends Marker { def expression = StringLiteral("v") } -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 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 -- cgit v1.2.3