// Required modules : scala, viewer

import jscl.viewer.Viewer

object Graph {
  def apply(str: String) = new Viewer(str)
  def apply(f: Double => Double, xs: Seq[Double]): Viewer = apply("Graphics[Line[{" + xs.map(x => "{" + x.toString +"," + f(x).toString + "}").mkString(",") + "}],{PlotRange->Automatic,AxesStyle->Automatic,AxesOrigin->{0.0,0.0},Axes->True,Background->White}]")
  def multiple(f: Double => Array[Double], xs: Seq[Double]) = apply("Graphics[{" + (for (i <- 0 until f(0.).length) yield "Line[{" + xs.map(x => "{" + x.toString +"," + f(x)(i).toString + "}").mkString(",") + "}]").mkString(",") + "},{PlotRange->Automatic,AxesStyle->Automatic,AxesOrigin->{0.0,0.0},Axes->True,Background->White}]")
  def parametric(f: Double => (Double, Double), ts: Seq[Double]) = apply("Graphics[Line[{" + ts.map(t => { val (x, y) = f(t) ; "{" + x.toString +"," + y.toString + "}" }).mkString(",") + "}],{PlotRange->Automatic,AxesStyle->Automatic,AxesOrigin->{0.0,0.0},Axes->True,Background->White}]")
  def apply(f: (Double, Double) => Double, xs: Seq[Double], ys: Seq[Double]): Viewer = apply("SurfaceGraphics[{" + ys.map(y => "{" + xs.map(x => f(x, y)).mkString(",") + "}").mkString(",") + "},{PlotRange->Automatic,MeshRange->{{" + xs.head.toString + "," + xs.last.toString + "},{" + ys.head.toString + "," + ys.last.toString + "}}}]")
  def parametric(f: (Double, Double) => (Double, Double, Double), us: Seq[Double], vs: Seq[Double]) = apply(us.map(u => vs.map(v => f(u, v)).toArray).toArray)
  def apply(a: Array[Array[(Double, Double, Double)]]): Viewer = apply("Graphics3D[{" + (for (n <- 0 until (a.size - 1)) yield (for (m <- 0 until (a(n).size - 1)) yield "Polygon[{" + point(a(n)(m)) + "," + point(a(n)(m + 1)) + "," + point(a(n + 1)(m + 1)) + "," + point(a(n + 1)(m)) + "}]").mkString(",")).mkString(",") + "},{PlotRange->Automatic,AxesStyle->Automatic,Axes->True,Background->White}]")
  def multiple_parametric(f: (Double, Double) => Array[(Double, Double, Double)], us: Seq[Double], vs: Seq[Double]) = apply((for (i <- 0 until f(0., 0.).length) yield us.map(u => vs.map(v => f(u, v)(i)).toArray).toArray).toArray)
  def apply(a: Array[Array[Array[(Double, Double, Double)]]]): Viewer = apply("Graphics3D[{" + (for (i <- 0 until a.size) yield (for (n <- 0 until (a(i).size - 1)) yield (for (m <- 0 until (a(i)(n).size - 1)) yield "Polygon[{" + point(a(i)(n)(m)) + "," + point(a(i)(n)(m + 1)) + "," + point(a(i)(n + 1)(m + 1)) + "," + point(a(i)(n + 1)(m)) + "}]").mkString(",")).mkString(",")).mkString(",") + "},{PlotRange->Automatic,AxesStyle->Automatic,Axes->True,Background->White}]")
  def point(s: (Double, Double, Double)) = { val (x, y, z) = s ; "{" + x + "," + y + "," + z + "}" }
}

import scala.math.{sin, cos, Pi}

Graph(x => sin(x), for (n <- 0 until 100) yield n / 10.)
Graph.multiple(x => Array(sin(x), cos(x)), for (n <- 0 until 100) yield n / 10.)
Graph.parametric(t => (cos(t), sin(t)), for (n <- 0 until 100) yield n / 100. * 2. * Pi)
Graph((x, y) => sin(x) * cos(y), for (n <- -10 to 10) yield 1. * n, for (m <- -10 to 10) yield 1. * m)

Graph((x, y) => (x * y) / 8., for (n <- -8 to 8) yield 1. * n, for (m <- -8 to 8) yield 1. * m)
Graph.parametric((u, v) => (u, v, (u * v) / 8.), for (n <- -8 to 8) yield 1. * n, for (m <- -8 to 8) yield 1. * m)
Graph.multiple_parametric((u, v) => Array((u, v, (u * v) / 8.), (v, (u * v) / 8., u), ((u * v) / 8., u, v)), for (n <- -8 to 8) yield 1. * n, for (m <- -8 to 8) yield 1. * m)

-8-6-4-202468-8-6-4-202468-8-6-4-202468