├── .gitignore ├── README.md ├── build.sbt ├── project ├── build.properties └── build.sbt └── src └── main └── scala └── paths ├── high ├── Bar.scala ├── Graph.scala ├── Pie.scala ├── Radar.scala ├── Sankey.scala ├── SmoothLine.scala ├── Stack.scala ├── Stock.scala ├── Tree.scala ├── Voronoi.scala └── Waterfall.scala ├── low └── Path.scala ├── mid ├── Bezier.scala ├── Connector.scala ├── CurvedRectangle.scala ├── Polygon.scala ├── Rectangle.scala ├── Sector.scala ├── SemiRegularPolygon.scala └── Shape.scala ├── misc └── Linear.scala └── paths.scala /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .cache 3 | .classpath 4 | .project 5 | .settings/ 6 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Paths.scala.js 2 | ============== 3 | 4 | [Paths.js](https://github.com/andreaferretti/paths-js) is a library to generate [SVG paths](http://www.w3.org/TR/SVG/paths.html), allowing you to create your own charts using a functional and testable API. Paths.scala.js is the binding of Paths.js for [Scala.js](http://www.scala-js.org/). 5 | 6 | Documentation 7 | ------------- 8 | 9 | The usage of Paths.scala.js is mostly similar to its parent library. You can 10 | 11 | - browse the [documentation of Paths.js](https://github.com/andreaferretti/paths-js/wiki) 12 | - explore the [Scaladocs API](http://andreaferretti.github.io/paths-scala-js) 13 | - see an [example application](https://github.com/andreaferretti/paths-scala-js-demo) ([live demo](http://andreaferretti.github.io/paths-scala-js-demo/)) 14 | 15 | The demo application is still incomplete, and fails to show many of Paths.scala.js features. The [Paths.js demo](http://andreaferretti.github.io/paths-js-demo/) better showcases what can be done. 16 | 17 | Usage 18 | ----- 19 | 20 | Paths.scala.js is published for Scala 2.11 and Scala 2.12 with Scala.js 0.6. In a Scala.js project, you can depend on Paths.scala.js with 21 | 22 | libraryDependencies += "eu.unicredit" %%% "paths-scala-js" % "0.4.5" 23 | 24 | Compatibility 25 | ------------- 26 | 27 | Paths.scala.js is meant to have an API that is exactly equivalent to its parent library. The only exception is in the `Graph` and `Sankey` charts, where instead of accepting a parameter `data` with `nodes` and `links` fields, the Scala.js API directly requires `nodes` and `links` parameters. This removes one level of nesting and eliminates the need for structural typing in this particular case. 28 | 29 | Please, file any other incompatibility between Paths.js and Paths.scala.js as an issue. -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | // Turn this project into a Scala.js project by importing these settings 2 | enablePlugins(ScalaJSPlugin) 3 | 4 | organization := "eu.unicredit" 5 | 6 | name := "paths-scala-js" 7 | 8 | version := "0.4.5" 9 | 10 | scalaVersion := "2.12.4" 11 | 12 | crossScalaVersions := Seq("2.11.7", "2.12.4") 13 | 14 | scalaJSUseMainModuleInitializer in Compile := true 15 | 16 | scalaJSUseMainModuleInitializer in Test := false 17 | 18 | testFrameworks += new TestFramework("utest.runner.Framework") 19 | 20 | jsDependencies += "org.webjars.bower" % "paths-js" % "0.4.5" / "paths.js" 21 | 22 | skip in packageJSDependencies := false 23 | 24 | scalacOptions ++= Seq( 25 | "-feature", 26 | "-deprecation", 27 | "-language:reflectiveCalls" 28 | ) 29 | 30 | publishMavenStyle := true 31 | 32 | pomIncludeRepository := { x => false } 33 | 34 | publishTo := sonatypePublishTo.value 35 | 36 | credentials += Credentials(Path.userHome / ".ivy2" / "sonatype.credentials") 37 | 38 | pomExtra := { 39 | https://github.com/andreaferretti/paths-scala-js 40 | 41 | 42 | Apache 2 43 | http://www.apache.org/licenses/LICENSE-2.0.txt 44 | 45 | 46 | 47 | scm:git:github.com/andreaferretti/paths-scala-js 48 | scm:git:git@github.com:andreaferretti/paths-scala-js 49 | github.com/andreaferretti/paths-scala-js 50 | 51 | 52 | 53 | andreaferretti 54 | Andrea Ferretti 55 | https://github.com/andreaferretti/ 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.17 2 | -------------------------------------------------------------------------------- /project/build.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.22") 2 | 3 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.3") 4 | 5 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") -------------------------------------------------------------------------------- /src/main/scala/paths/high/Bar.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Rectangle 9 | import misc.Linear 10 | 11 | @js.native 12 | trait BarOpts[A] extends js.Object { 13 | val data: js.Array[js.Array[A]] = js.native 14 | val accessor: js.Function1[A, Double] = js.native 15 | val width: Int = js.native 16 | val height: Int = js.native 17 | val gutter: Int = js.native 18 | val offset: js.Array[Double] = js.native 19 | } 20 | 21 | object BarOpts { 22 | def apply[A](data: js.Array[js.Array[A]], accessor: js.Function1[A, Double], 23 | width: Int, height: Int, gutter: Int, offset: js.Array[Double]): BarOpts[A] = 24 | js.Dynamic.literal( 25 | data = data, 26 | accessor = accessor, 27 | width = width, 28 | height = height, 29 | gutter = gutter, 30 | offset = offset 31 | ).asInstanceOf[BarOpts[A]] 32 | } 33 | 34 | @js.native 35 | @JSName("Paths.Bar") 36 | object BarNative extends js.Object { 37 | def apply[A](options: BarOpts[A]): Bar[A] = js.native 38 | } 39 | 40 | @js.native 41 | trait BarCurve[A] extends js.Object { 42 | val line: Rectangle = js.native 43 | val item: A = js.native 44 | val index: Int = js.native 45 | val group: Int = js.native 46 | } 47 | 48 | @js.native 49 | trait Bar[A] extends js.Object { 50 | val curves: js.Array[BarCurve[A]] = js.native 51 | val scale: Linear = js.native 52 | } 53 | 54 | object Bar { 55 | def apply[A](data: Seq[Seq[A]], accessor: A => Double, width: Int, height: Int, gutter: Int, offset: (Double, Double) = (0, 0)) = { 56 | val d = data.toJSArray.map(_.toJSArray) 57 | BarNative(BarOpts(d, accessor, width, height, gutter, tuple2point(offset))) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/scala/paths/high/Graph.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Polygon 9 | 10 | 11 | @js.native 12 | trait GraphData[V, E] extends js.Object { 13 | val nodes: js.Array[V] = js.native 14 | val links: js.Array[E] = js.native 15 | } 16 | 17 | @js.native 18 | trait GraphEdge extends js.Object { 19 | val start: String = js.native 20 | val end: String = js.native 21 | val weight: Double = js.native 22 | } 23 | 24 | @js.native 25 | trait GraphOpts[V, E] extends js.Object { 26 | val data: GraphData[V, E] = js.native 27 | val nodeaccessor: js.Function1[V, String] = js.native 28 | val linkaccessor: js.Function1[E, GraphEdge] = js.native 29 | val width: Int = js.native 30 | val height: Int = js.native 31 | val attraction: js.UndefOr[Double] = js.native 32 | val repulsion: js.UndefOr[Double] = js.native 33 | val threshold: js.UndefOr[Double] = js.native 34 | } 35 | 36 | object GraphOpts { 37 | def apply[V, E](data: GraphData[V, E], nodeaccessor: js.Function1[V, String], 38 | linkaccessor: js.Function1[E, GraphEdge], width: Int, height: Int, 39 | attraction: Double = 1, repulsion: Double = 1, threshold: Double = 0.5): GraphOpts[V, E] = 40 | js.Dynamic.literal( 41 | data = data, 42 | nodeaccessor = nodeaccessor, 43 | linkaccessor = linkaccessor, 44 | width = width, 45 | height = height, 46 | attraction = attraction, 47 | repulsion = repulsion, 48 | threshold = threshold 49 | ).asInstanceOf[GraphOpts[V, E]] 50 | } 51 | 52 | @js.native 53 | @JSName("Paths.Graph") 54 | object GraphNative extends js.Object { 55 | def apply[V, E](options: GraphOpts[V, E]): Graph[V, E] = js.native 56 | } 57 | 58 | @js.native 59 | trait GraphCurve[E] extends js.Object { 60 | val link: Polygon = js.native 61 | val item: E = js.native 62 | val index: Int = js.native 63 | } 64 | 65 | @js.native 66 | trait GraphNode[V] extends js.Object { 67 | val point: Point = js.native 68 | val item: V = js.native 69 | } 70 | 71 | @js.native 72 | trait Graph[V, E] extends js.Object { 73 | var curves: js.Array[GraphCurve[E]] = js.native 74 | var nodes: js.Array[GraphNode[V]] = js.native 75 | def tick(): Graph[V, E] = js.native 76 | def constrain(id: String, coordinates: Point): Unit = js.native 77 | def unconstrain(id: String): Unit = js.native 78 | } 79 | 80 | object Graph { 81 | type GraphScalaEdge = { def start: String; def end: String; def weight: Double } 82 | 83 | def apply[V, E](nodes: Seq[V], links: Seq[E], nodeaccessor: V => String, linkaccessor: E => GraphScalaEdge, 84 | width: Int, height: Int, attraction: Double = 1, repulsion: Double = 1, threshold: Double = 0.5) = { 85 | 86 | def dynamic(e: GraphScalaEdge): GraphEdge = js.Dynamic.literal( 87 | start = e.start, 88 | end = e.end, 89 | weight = e.weight 90 | ).asInstanceOf[GraphEdge] 91 | 92 | val data = js.Dynamic.literal( 93 | nodes = nodes.toJSArray, 94 | links = links.toJSArray 95 | ).asInstanceOf[GraphData[V, E]] 96 | 97 | GraphNative(GraphOpts(data, nodeaccessor, linkaccessor.andThen(dynamic), 98 | width, height, attraction, repulsion, threshold)) 99 | } 100 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/Pie.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Sector 9 | 10 | 11 | @js.native 12 | trait PieOpts[A] extends js.Object { 13 | val data: js.Array[A] = js.native 14 | val accessor: js.Function1[A, Double] = js.native 15 | val center: Point = js.native 16 | val r: Double = js.native 17 | val R: Double = js.native 18 | } 19 | 20 | object PieOpts { 21 | def apply[A](data: js.Array[A], accessor: js.Function1[A, Double], 22 | center: Point, r: Double, R: Double): PieOpts[A] = 23 | js.Dynamic.literal( 24 | data = data, 25 | accessor = accessor, 26 | center = center, 27 | r = r, 28 | R = R 29 | ).asInstanceOf[PieOpts[A]] 30 | } 31 | 32 | @js.native 33 | @JSName("Paths.Pie") 34 | object PieNative extends js.Object { 35 | def apply[A](options: PieOpts[A]): Pie[A] = js.native 36 | } 37 | 38 | @js.native 39 | trait PieCurve[A] extends js.Object { 40 | val sector: Sector = js.native 41 | val item: A = js.native 42 | val index: Int = js.native 43 | } 44 | 45 | @js.native 46 | trait Pie[A] extends js.Object { 47 | val curves: js.Array[PieCurve[A]] = js.native 48 | } 49 | 50 | object Pie { 51 | def apply[A](data: Seq[A], accessor: A => Double, center: (Double, Double), r: Double, R: Double) = { 52 | val (x, y) = center 53 | PieNative(PieOpts(data.toJSArray, accessor, js.Array(x, y), r, R)) 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/Radar.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.{ Shape, SemiRegularPolygon } 9 | 10 | 11 | @js.native 12 | trait RadarOpts[A] extends js.Object { 13 | val data: js.Array[A] = js.native 14 | val accessor: Map[String, js.Function1[A, Double]] = js.native 15 | val max: js.UndefOr[Double] = js.native 16 | val center: Point = js.native 17 | val r: Double = js.native 18 | val rings: Int = js.native 19 | } 20 | 21 | object RadarOpts { 22 | def apply[A](data: js.Array[A], accessor: js.Dictionary[js.Function1[A, Double]], 23 | max: js.UndefOr[Double], center: Point, r: Double, rings: Int = 3): RadarOpts[A] = 24 | js.Dynamic.literal( 25 | data = data, 26 | accessor = accessor, 27 | max = max, 28 | center = center, 29 | r = r, 30 | rings = rings 31 | ).asInstanceOf[RadarOpts[A]] 32 | } 33 | 34 | @js.native 35 | @JSName("Paths.Radar") 36 | object RadarNative extends js.Object { 37 | def apply[A](options: RadarOpts[A]): Radar[A] = js.native 38 | } 39 | 40 | @js.native 41 | trait RadarCurve[A] extends js.Object { 42 | val polygon: SemiRegularPolygon = js.native 43 | val item: A = js.native 44 | val index: Int = js.native 45 | } 46 | 47 | @js.native 48 | trait Radar[A] extends js.Object { 49 | val curves: js.Array[RadarCurve[A]] = js.native 50 | val rings: js.Array[Shape] = js.native 51 | } 52 | 53 | object Radar { 54 | def apply[A](data: Seq[A], accessor: Map[String, A => Double], max: Option[Double] = None, 55 | center: (Double, Double), r: Double, rings: Int = 3) = { 56 | // val d = data.toJSArray.asInstanceOf[js.Array[Any]] 57 | // val acc = accessor mapValues { f => (x: Any) => f(x.asInstanceOf[A]) } 58 | val acc: Map[String, js.Function1[A, Double]] = accessor mapValues { x => x } 59 | val (x, y) = center 60 | RadarNative(RadarOpts(data.toJSArray, acc.toJSDictionary, max.orUndefined, js.Array(x, y), r, rings)) 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/Sankey.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.{ CurvedRectangle, Rectangle } 9 | 10 | 11 | @js.native 12 | trait SankeyData[V, E] extends js.Object { 13 | val nodes: js.Array[js.Array[V]] = js.native 14 | val links: js.Array[E] = js.native 15 | } 16 | 17 | @js.native 18 | trait SankeyEdge extends js.Object { 19 | val start: String = js.native 20 | val end: String = js.native 21 | val weight: Double = js.native 22 | } 23 | 24 | @js.native 25 | trait SankeyOpts[V, E] extends js.Object { 26 | val data: SankeyData[V, E] = js.native 27 | val nodeaccessor: js.Function1[V, String] = js.native 28 | val linkaccessor: js.Function1[E, SankeyEdge] = js.native 29 | val width: Int = js.native 30 | val height: Int = js.native 31 | val gutter: js.UndefOr[Int] = js.native 32 | val rect_width: js.UndefOr[Int] = js.native 33 | } 34 | 35 | object SankeyOpts { 36 | def apply[V, E](data: SankeyData[V, E], nodeaccessor: js.Function1[V, String], 37 | linkaccessor: js.Function1[E, SankeyEdge], width: Int, height: Int, 38 | gutter: Int = 10, rect_width: Int = 10): SankeyOpts[V, E] = 39 | js.Dynamic.literal( 40 | data = data, 41 | nodeaccessor = nodeaccessor, 42 | linkaccessor = linkaccessor, 43 | width = width, 44 | height = height, 45 | gutter = gutter, 46 | rect_width = rect_width 47 | ).asInstanceOf[SankeyOpts[V, E]] 48 | } 49 | 50 | @js.native 51 | @JSName("Paths.Sankey") 52 | object SankeyNative extends js.Object { 53 | def apply[V, E](options: SankeyOpts[V, E]): Sankey[V, E] = js.native 54 | } 55 | 56 | @js.native 57 | trait SankeyCurve[E] extends js.Object { 58 | val curve: CurvedRectangle = js.native 59 | val item: E = js.native 60 | val index: Int = js.native 61 | } 62 | 63 | @js.native 64 | trait SankeyNode[V] extends js.Object { 65 | val curve: Rectangle = js.native 66 | val item: V = js.native 67 | val index: Int = js.native 68 | val group: Int = js.native 69 | } 70 | 71 | @js.native 72 | trait Sankey[V, E] extends js.Object { 73 | var curvedRectangles: js.Array[SankeyCurve[E]] = js.native 74 | var rectangles: js.Array[SankeyNode[V]] = js.native 75 | } 76 | 77 | object Sankey { 78 | type SankeyScalaEdge = { def start: String; def end: String; def weight: Double } 79 | 80 | def apply[V, E](nodes: Seq[Seq[V]], links: Seq[E], nodeaccessor: V => String, linkaccessor: E => SankeyScalaEdge, 81 | width: Int, height: Int, gutter: Int = 10, rectWidth: Int = 10) = { 82 | 83 | def dynamic(e: SankeyScalaEdge): SankeyEdge = js.Dynamic.literal( 84 | start = e.start, 85 | end = e.end, 86 | weight = e.weight 87 | ).asInstanceOf[SankeyEdge] 88 | 89 | val data = js.Dynamic.literal( 90 | nodes = nodes.toJSArray.map(_.toJSArray), 91 | links = links.toJSArray 92 | ).asInstanceOf[SankeyData[V, E]] 93 | 94 | SankeyNative(SankeyOpts(data, nodeaccessor, linkaccessor.andThen(dynamic), 95 | width, height, gutter, rectWidth)) 96 | } 97 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/SmoothLine.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Shape 9 | import misc.Linear 10 | 11 | 12 | @js.native 13 | trait SmoothLineOpts[A] extends js.Object { 14 | val data: js.Array[js.Array[A]] = js.native 15 | val xaccessor: js.Function1[A, Double] = js.native 16 | val yaccessor: js.Function1[A, Double] = js.native 17 | val width: Int = js.native 18 | val height: Int = js.native 19 | val closed: Boolean = js.native 20 | val sort: Boolean = js.native 21 | } 22 | 23 | object SmoothLineOpts { 24 | def apply[A](data: js.Array[js.Array[A]], xaccessor: js.Function1[A, Double], 25 | yaccessor: js.Function1[A, Double], width: Int, height: Int, 26 | closed: Boolean = false, sort: Boolean = true): SmoothLineOpts[A] = 27 | js.Dynamic.literal( 28 | data = data, 29 | xaccessor = xaccessor, 30 | yaccessor = yaccessor, 31 | width = width, 32 | height = height, 33 | closed = closed, 34 | sort = sort 35 | ).asInstanceOf[SmoothLineOpts[A]] 36 | } 37 | 38 | @js.native 39 | @JSName("Paths.SmoothLine") 40 | object SmoothLineNative extends js.Object { 41 | def apply[A](options: SmoothLineOpts[A]): SmoothLine[A] = js.native 42 | } 43 | 44 | @js.native 45 | trait SmoothLineCurve[A] extends js.Object { 46 | val line: Shape = js.native 47 | val area: Shape = js.native 48 | val item: js.Array[A] = js.native 49 | val index: Int = js.native 50 | } 51 | 52 | @js.native 53 | trait SmoothLine[A] extends js.Object { 54 | val curves: js.Array[SmoothLineCurve[A]] = js.native 55 | val xscale: Linear = js.native 56 | val yscale: Linear = js.native 57 | } 58 | 59 | object SmoothLine { 60 | def apply[A](data: Seq[Seq[A]], xaccessor: A => Double, yaccessor: A => Double, 61 | width: Int, height: Int, closed: Boolean = false, sort: Boolean = true) = { 62 | val d = data.toJSArray.map(_.toJSArray) 63 | SmoothLineNative(SmoothLineOpts(d, xaccessor, yaccessor, width, height, closed, sort)) 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/Stack.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Rectangle 9 | 10 | 11 | @js.native 12 | trait StackOpts[A] extends js.Object { 13 | val data: js.Array[js.Array[A]] = js.native 14 | val accessor: js.Function1[A, Double] = js.native 15 | val width: Int = js.native 16 | val height: Int = js.native 17 | val gutter: Int = js.native 18 | } 19 | 20 | object StackOpts { 21 | def apply[A](data: js.Array[js.Array[A]], accessor: js.Function1[A, Double], 22 | width: Int, height: Int, gutter: Int): StackOpts[A] = 23 | js.Dynamic.literal( 24 | data = data, 25 | accessor = accessor, 26 | width = width, 27 | height = height, 28 | gutter = gutter 29 | ).asInstanceOf[StackOpts[A]] 30 | } 31 | 32 | @js.native 33 | @JSName("Paths.Stack") 34 | object StackNative extends js.Object { 35 | def apply[A](options: StackOpts[A]): Stack[A] = js.native 36 | } 37 | 38 | @js.native 39 | trait StackCurve[A] extends js.Object { 40 | val line: Rectangle = js.native 41 | val item: A = js.native 42 | val index: Int = js.native 43 | val group: Int = js.native 44 | } 45 | 46 | @js.native 47 | trait Stack[A] extends js.Object { 48 | val curves: js.Array[StackCurve[A]] = js.native 49 | } 50 | 51 | object Stack { 52 | def apply[A](data: Seq[Seq[A]], accessor: A => Double, width: Int, height: Int, gutter: Int) = { 53 | val d = data.toJSArray.map(_.toJSArray) 54 | StackNative(StackOpts(d, accessor, width, height, gutter)) 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/Stock.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Polygon 9 | import misc.Linear 10 | 11 | 12 | @js.native 13 | trait StockOpts[A] extends js.Object { 14 | val data: js.Array[js.Array[A]] = js.native 15 | val xaccessor: js.Function1[A, Double] = js.native 16 | val yaccessor: js.Function1[A, Double] = js.native 17 | val width: Int = js.native 18 | val height: Int = js.native 19 | val closed: Boolean = js.native 20 | val sort: Boolean = js.native 21 | } 22 | 23 | object StockOpts { 24 | def apply[A](data: js.Array[js.Array[A]], xaccessor: js.Function1[A, Double], 25 | yaccessor: js.Function1[A, Double], width: Int, height: Int, 26 | closed: Boolean = false, sort: Boolean = true): StockOpts[A] = 27 | js.Dynamic.literal( 28 | data = data, 29 | xaccessor = xaccessor, 30 | yaccessor = yaccessor, 31 | width = width, 32 | height = height, 33 | closed = closed, 34 | sort = sort 35 | ).asInstanceOf[StockOpts[A]] 36 | } 37 | 38 | @js.native 39 | @JSName("Paths.Stock") 40 | object StockNative extends js.Object { 41 | def apply[A](options: StockOpts[A]): Stock[A] = js.native 42 | } 43 | 44 | @js.native 45 | trait StockCurve[A] extends js.Object { 46 | val line: Polygon = js.native 47 | val area: Polygon = js.native 48 | val item: js.Array[A] = js.native 49 | val index: Int = js.native 50 | } 51 | 52 | @js.native 53 | trait Stock[A] extends js.Object { 54 | val curves: js.Array[StockCurve[A]] = js.native 55 | val xscale: Linear = js.native 56 | val yscale: Linear = js.native 57 | } 58 | 59 | object Stock { 60 | def apply[A](data: Seq[Seq[A]], xaccessor: A => Double, yaccessor: A => Double, 61 | width: Int, height: Int, closed: Boolean = false, sort: Boolean = true) = { 62 | val d = data.toJSArray.map(_.toJSArray) 63 | StockNative(StockOpts(d, xaccessor, yaccessor, width, height, closed, sort)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/scala/paths/high/Tree.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Connector 9 | 10 | 11 | @js.native 12 | trait TreeOpts[A] extends js.Object { 13 | val data: A = js.native 14 | val children: js.Function1[A, js.Array[A]] = js.native 15 | val width: Int = js.native 16 | val height: Int = js.native 17 | } 18 | 19 | object TreeOpts { 20 | def apply[A](data: A, children: js.Function1[A, js.Array[A]], 21 | width: Int, height: Int): TreeOpts[A] = 22 | js.Dynamic.literal( 23 | data = data.asInstanceOf[js.Any], 24 | children = children, 25 | width = width, 26 | height = height 27 | ).asInstanceOf[TreeOpts[A]] 28 | } 29 | 30 | @js.native 31 | @JSName("Paths.Tree") 32 | object TreeNative extends js.Object { 33 | def apply[A](options: TreeOpts[A]): Tree[A] = js.native 34 | } 35 | 36 | @js.native 37 | trait TreeCurve[A] extends js.Object { 38 | val connector: Connector = js.native 39 | val item: A = js.native 40 | val index: Int = js.native 41 | } 42 | 43 | @js.native 44 | trait TreeNode[A] extends js.Object { 45 | val point: Point = js.native 46 | val item: A = js.native 47 | } 48 | 49 | @js.native 50 | trait Tree[A] extends js.Object { 51 | val curves: js.Array[TreeCurve[A]] = js.native 52 | val nodes: js.Array[TreeNode[A]] = js.native 53 | } 54 | 55 | object Tree { 56 | def apply[A](data: A, children: A => Seq[A], width: Int, height: Int) = { 57 | val c = children.andThen(_.toJSArray) 58 | TreeNative(TreeOpts(data, c, width, height)) 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/Voronoi.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Sector 9 | 10 | 11 | @js.native 12 | trait VoronoiOpts[A] extends js.Object { 13 | val data: js.Array[A] = js.native 14 | val accessor: js.Function1[A, Point] = js.native 15 | val xrange: Point = js.native 16 | val yrange: Point = js.native 17 | val width: Int = js.native 18 | val height: Int = js.native 19 | } 20 | 21 | object VoronoiOpts { 22 | def apply[A](data: js.Array[A], accessor: js.Function1[A, Point], 23 | xrange: Point, yrange: Point, width: Int, height: Int): VoronoiOpts[A] = 24 | js.Dynamic.literal( 25 | data = data, 26 | accessor = accessor, 27 | xrange = xrange, 28 | yrange = yrange, 29 | width = width, 30 | height = height 31 | ).asInstanceOf[VoronoiOpts[A]] 32 | } 33 | 34 | @js.native 35 | @JSName("Paths.Voronoi") 36 | object VoronoiNative extends js.Object { 37 | def apply[A](options: VoronoiOpts[A]): Voronoi[A] = js.native 38 | } 39 | 40 | @js.native 41 | trait VoronoiCurve[A] extends js.Object { 42 | val line: Sector = js.native 43 | val item: A = js.native 44 | val index: Int = js.native 45 | } 46 | 47 | @js.native 48 | trait VoronoiPoint[A] extends js.Object { 49 | val point: Point = js.native 50 | val item: A = js.native 51 | } 52 | 53 | @js.native 54 | trait Voronoi[A] extends js.Object { 55 | val curves: js.Array[VoronoiCurve[A]] = js.native 56 | val nodes: js.Array[VoronoiPoint[A]] = js.native 57 | } 58 | 59 | object Voronoi { 60 | def apply[A](data: Seq[A], accessor: A => (Double, Double), 61 | xrange: (Double, Double), yrange: (Double, Double), width: Int, height: Int) = { 62 | 63 | val acc: js.Function1[A, Point] = accessor andThen tuple2point 64 | 65 | VoronoiNative(VoronoiOpts(data.toJSArray, acc, tuple2point(xrange), tuple2point(yrange), width, height)) 66 | } 67 | } -------------------------------------------------------------------------------- /src/main/scala/paths/high/Waterfall.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package high 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | import mid.Rectangle 9 | 10 | 11 | @js.native 12 | trait WaterfallItem extends js.Object { 13 | val value: js.UndefOr[Double] = js.native 14 | val absolute: js.UndefOr[Boolean] = js.native 15 | } 16 | 17 | trait WaterfallScalaItem { 18 | def value: Option[Double] 19 | def absolute: Boolean 20 | } 21 | 22 | @js.native 23 | trait WaterfallOpts[A] extends js.Object { 24 | val data: js.Array[A] = js.native 25 | val accessor: js.Function1[A, WaterfallItem] = js.native 26 | val max: js.UndefOr[Double] = js.native 27 | val min: js.UndefOr[Double] = js.native 28 | val width: Int = js.native 29 | val height: Int = js.native 30 | val gutter: js.UndefOr[Int] = js.native 31 | } 32 | 33 | object WaterfallOpts { 34 | def apply[A](data: js.Array[A], accessor: js.Function1[A, WaterfallItem], 35 | max: js.UndefOr[Double], min: js.UndefOr[Double], width: Int, height: Int, 36 | gutter: js.UndefOr[Int]): WaterfallOpts[A] = 37 | js.Dynamic.literal( 38 | data = data, 39 | accessor = accessor, 40 | max = max, 41 | min = min, 42 | width = width, 43 | height = height, 44 | gutter = gutter 45 | ).asInstanceOf[WaterfallOpts[A]] 46 | } 47 | 48 | @js.native 49 | @JSName("Paths.Waterfall") 50 | object WaterfallNative extends js.Object { 51 | def apply[A](options: WaterfallOpts[A]): Waterfall[A] = js.native 52 | } 53 | 54 | @js.native 55 | trait WaterfallCurve[A] extends js.Object { 56 | val line: Rectangle = js.native 57 | val value: Double = js.native 58 | val item: A = js.native 59 | val index: Int = js.native 60 | } 61 | 62 | @js.native 63 | trait Waterfall[A] extends js.Object { 64 | val curves: js.Array[WaterfallCurve[A]] = js.native 65 | } 66 | 67 | object Waterfall { 68 | type WaterfallScalaItem = { def value: Option[Double]; def absolute: Boolean } 69 | 70 | def apply[A](data: Seq[A], accessor: A => WaterfallScalaItem, max: Option[Double] = None, 71 | min: Option[Double] = None, width: Int, height: Int, gutter: Option[Int] = None) = { 72 | 73 | def dynamic(s: WaterfallScalaItem): WaterfallItem = js.Dynamic.literal( 74 | value = s.value.orUndefined, 75 | absolute = s.absolute 76 | ).asInstanceOf[WaterfallItem] 77 | 78 | WaterfallNative(WaterfallOpts(data.toJSArray, accessor.andThen(dynamic), max.orUndefined, min.orUndefined, 79 | width, height, gutter.orUndefined)) 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/scala/paths/low/Path.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package low 3 | 4 | import scala.scalajs.js 5 | import scala.scalajs.js.annotation.JSName 6 | 7 | @js.native 8 | trait Path extends js.Object { 9 | def print(): String = js.native 10 | def points(): js.Array[Point] = js.native 11 | def connect(path: Path): Path = js.native 12 | 13 | def moveto(x: Number, y: Number): Path = js.native 14 | def lineto(x: Number, y: Number): Path = js.native 15 | def hlineto(x: Number): Path = js.native 16 | def vlineto(y: Number): Path = js.native 17 | def curveto(x1: Number, y1: Number, x2: Number, y2: Number, x: Number, y: Number): Path = js.native 18 | def smoothcurveto(x2: Number, y2: Number, x: Number, y: Number): Path = js.native 19 | def qcurveto(x1: Number, y1: Number, x: Number, y: Number): Path = js.native 20 | def smoothqcurveto(x: Number, y: Number): Path = js.native 21 | def arc(rx: Number, ry: Number, xrot: Number, largeArcFlag: Number, sweepFlag: Number, x: Number, y: Number): Path = js.native 22 | def closepath(): Path = js.native 23 | } 24 | 25 | @js.native 26 | @JSName("Paths.Path") 27 | object Path extends js.Object { 28 | def apply(): Path = js.native 29 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/Bezier.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | @js.native 9 | trait BezierOpts extends js.Object { 10 | val points: js.Array[Point] = js.native 11 | val tension: Double = js.native 12 | } 13 | 14 | object BezierOpts { 15 | def apply(points: js.Array[Point], tension: Double = 0.3): BezierOpts = 16 | js.Dynamic.literal( 17 | points = points, 18 | tension = tension 19 | ).asInstanceOf[BezierOpts] 20 | } 21 | 22 | @js.native 23 | @JSName("Paths.Bezier") 24 | object BezierNative extends js.Object { 25 | def apply(options: BezierOpts): Bezier = js.native 26 | } 27 | 28 | @js.native 29 | trait Bezier extends Shape 30 | 31 | object Bezier { 32 | def apply(points: Seq[(Double, Double)], tension: Double = 0.3): Bezier = { 33 | val jsPoints = for ((x, y) <- points.toJSArray) yield js.Array(x, y) 34 | BezierNative(BezierOpts(jsPoints, tension)) 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/Connector.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | 9 | @js.native 10 | trait ConnectorOpts extends js.Object { 11 | val start: Point = js.native 12 | val end: Point = js.native 13 | } 14 | 15 | object ConnectorOpts { 16 | def apply(start: Point, end: Point): ConnectorOpts = 17 | js.Dynamic.literal( 18 | start = start, 19 | end = end 20 | ).asInstanceOf[ConnectorOpts] 21 | } 22 | 23 | @js.native 24 | @JSName("Paths.Connector") 25 | object ConnectorNative extends js.Object { 26 | def apply(options: ConnectorOpts): Connector = js.native 27 | } 28 | 29 | @js.native 30 | trait Connector extends Shape 31 | 32 | object Connector { 33 | def apply(start: (Double, Double), end: (Double, Double)): Connector = { 34 | val (a, b) = start 35 | val (c, d) = end 36 | ConnectorNative(ConnectorOpts(js.Array(a, b), js.Array(c, d))) 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/CurvedRectangle.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | 9 | @js.native 10 | trait CurvedRectangleOpts extends js.Object { 11 | val topleft: Point = js.native 12 | val topright: Point = js.native 13 | val bottomleft: Point = js.native 14 | val bottomright: Point = js.native 15 | } 16 | 17 | object CurvedRectangleOpts { 18 | def apply(topleft: Point, topright: Point, bottomleft: Point, bottomright: Point): CurvedRectangleOpts = 19 | js.Dynamic.literal( 20 | topleft = topleft, 21 | topright = topright, 22 | bottomleft = bottomleft, 23 | bottomright = bottomright 24 | ).asInstanceOf[CurvedRectangleOpts] 25 | } 26 | 27 | @js.native 28 | @JSName("Paths.CurvedRectangle") 29 | object CurvedRectangleNative extends js.Object { 30 | def apply(options: CurvedRectangleOpts): CurvedRectangle = js.native 31 | } 32 | 33 | @js.native 34 | trait CurvedRectangle extends Shape 35 | 36 | object CurvedRectangle { 37 | def apply(topleft: (Double, Double), topright: (Double, Double), 38 | bottomleft: (Double, Double), bottomright: (Double, Double)): CurvedRectangle = { 39 | CurvedRectangleNative(CurvedRectangleOpts(tuple2point(topleft), tuple2point(topright), 40 | tuple2point(bottomleft), tuple2point(bottomright))) 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/Polygon.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | 9 | @js.native 10 | trait PolygonOpts extends js.Object { 11 | val points: js.Array[Point] = js.native 12 | val closed: Boolean = js.native 13 | } 14 | 15 | object PolygonOpts { 16 | def apply(points: js.Array[Point], closed: Boolean = false): PolygonOpts = 17 | js.Dynamic.literal( 18 | points = points, 19 | closed = closed 20 | ).asInstanceOf[PolygonOpts] 21 | } 22 | 23 | @js.native 24 | @JSName("Paths.Polygon") 25 | object PolygonNative extends js.Object { 26 | def apply(options: PolygonOpts): Polygon = js.native 27 | } 28 | 29 | @js.native 30 | trait Polygon extends Shape 31 | 32 | object Polygon { 33 | def apply(points: Seq[(Double, Double)], closed: Boolean = false): Polygon = { 34 | val jsPoints = for ((x, y) <- points.toJSArray) yield js.Array(x, y) 35 | PolygonNative(PolygonOpts(jsPoints, closed)) 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/Rectangle.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | 9 | @js.native 10 | trait RectangleOpts extends js.Object { 11 | val top: Double = js.native 12 | val bottom: Double = js.native 13 | val left: Double = js.native 14 | val right: Double = js.native 15 | } 16 | 17 | object RectangleOpts { 18 | def apply(top: Double, bottom: Double, left: Double, right: Double): RectangleOpts = 19 | js.Dynamic.literal( 20 | top = top, 21 | bottom = bottom, 22 | left = left, 23 | right = right 24 | ).asInstanceOf[RectangleOpts] 25 | } 26 | 27 | @js.native 28 | @JSName("Paths.Rectangle") 29 | object RectangleNative extends js.Object { 30 | def apply(options: RectangleOpts): Rectangle = js.native 31 | } 32 | 33 | @js.native 34 | trait Rectangle extends Shape 35 | 36 | object Rectangle { 37 | def apply(top: Double, bottom: Double, left: Double, right: Double): Rectangle = { 38 | RectangleNative(RectangleOpts(top, bottom, left, right)) 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/Sector.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | 9 | @js.native 10 | trait SectorOpts extends js.Object { 11 | val center: Point = js.native 12 | val r: Double = js.native 13 | val R: Double = js.native 14 | val start: Double = js.native 15 | val end: Double = js.native 16 | } 17 | 18 | object SectorOpts { 19 | def apply(center: Point, r: Double, R: Double, start: Double, end: Double): SectorOpts = 20 | js.Dynamic.literal( 21 | center = center, 22 | r = r, 23 | R = R, 24 | start = start, 25 | end = end 26 | ).asInstanceOf[SectorOpts] 27 | } 28 | 29 | @js.native 30 | @JSName("Paths.Sector") 31 | object SectorNative extends js.Object { 32 | def apply(options: SectorOpts): Sector = js.native 33 | } 34 | 35 | @js.native 36 | trait Sector extends Shape 37 | 38 | object Sector { 39 | def apply(center: (Double, Double), r: Double, R: Double, start: Double, end: Double): Sector = { 40 | val (x, y) = center 41 | SectorNative(SectorOpts(js.Array(x, y), r, R, start, end)) 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/SemiRegularPolygon.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | import js.JSConverters._ 7 | 8 | 9 | @js.native 10 | trait SemiRegularPolygonOpts extends js.Object { 11 | val center: Point = js.native 12 | val radii: js.Array[Double] = js.native 13 | } 14 | 15 | object SemiRegularPolygonOpts { 16 | def apply(center: Point, radii: js.Array[Double]): SemiRegularPolygonOpts = 17 | js.Dynamic.literal( 18 | center = center, 19 | radii = radii 20 | ).asInstanceOf[SemiRegularPolygonOpts] 21 | } 22 | 23 | @js.native 24 | @JSName("Paths.SemiRegularPolygon") 25 | object SemiRegularPolygonNative extends js.Object { 26 | def apply(options: SemiRegularPolygonOpts): SemiRegularPolygon = js.native 27 | } 28 | 29 | @js.native 30 | trait SemiRegularPolygon extends Shape 31 | 32 | object SemiRegularPolygon { 33 | def apply(center: (Double, Double), radii: Seq[Double]): SemiRegularPolygon = { 34 | val (x, y) = center 35 | SemiRegularPolygonNative(SemiRegularPolygonOpts(js.Array(x, y), radii.toJSArray)) 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/scala/paths/mid/Shape.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package mid 3 | 4 | import scala.scalajs.js 5 | 6 | import low.Path 7 | 8 | 9 | @js.native 10 | trait Shape extends js.Object { 11 | val path: Path = js.native 12 | val centroid: Point = js.native 13 | } -------------------------------------------------------------------------------- /src/main/scala/paths/misc/Linear.scala: -------------------------------------------------------------------------------- 1 | package paths 2 | package misc 3 | 4 | import scala.scalajs.js 5 | import js.annotation.JSName 6 | 7 | @js.native 8 | @JSName("Paths.Linear") 9 | object LinearNative extends js.Object { 10 | def apply(source: js.Array[Double], target: js.Array[Double]): Linear = js.native 11 | } 12 | 13 | @js.native 14 | trait Linear extends js.Object { 15 | def apply(x: Double): Double = js.native 16 | def inverse(): Linear = js.native 17 | } 18 | 19 | object Linear { 20 | def apply(source: (Double, Double), target: (Double, Double)): Linear = { 21 | val (a, b) = source 22 | val (c, d) = target 23 | LinearNative(js.Array(a, b), js.Array(c, d)) 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/scala/paths/paths.scala: -------------------------------------------------------------------------------- 1 | import scala.scalajs.js 2 | import js.JSConverters._ 3 | 4 | package object paths { 5 | type Point = js.Array[Double] 6 | private[paths] def tuple2point(x: (Double, Double)) = js.Array(x._1, x._2) 7 | } --------------------------------------------------------------------------------