Edit Page

Core mechanisms

All visuals done with data2viz share some simple elements, this page describes the "basics" of the library.

Utility types

Drawing an arc on Javascript requires radians, but degrees on Android... To avoid confusion and guarantee a unique behavior on different platform data2viz offers some "utility types" that can be easily instantiated with extension properties:

  • Angle: create an Angle using 180.deg or PI.rad
  • Percent: create a percentage using 50.pct

these utility types are located in io.data2viz.math so a good start is always to import this package using a wildcard.

import io.data2viz.color.* import io.data2viz.geom.* import io.data2viz.viz.* //sampleStart import io.data2viz.math.* // access to Percent fun main() { viz { rect { fill = Colors.Web.crimson.withAlpha(50.pct) // fill with 50% alpha size = size(50, 50) } }.bindRendererOnNewCanvas() } //sampleEnd

Common types

In order to make the code more concise, several types are used as parameters:

  • Point, Vector: for positioning
  • Size: for sizing
  • Extent: for defining a zone

These types are located in io.data2viz.geom so we strongly recommend to star-import this package and to use the factories which are a bit more practical.

import io.data2viz.color.* import io.data2viz.math.* import io.data2viz.viz.* //sampleStart import io.data2viz.geom.* // gives access to size, point... fun main() { viz { rect { size = size(100, 50) // rectangle is 100x50 fill = Colors.Web.crimson } }.bindRendererOnNewCanvas() } //sampleEnd

Main visual

As seen before, your visualization will be described using the viz keyword and DSL. Although the code in viz seems like declarative code, you're in a standard Kotlin function so you can use any of the language features.

You'll learn how to be effective fast, but these are some basic tips to help you start:

  • keep some references on your visual objects
  • use group to group visuals and manipulate them easily
  • use inheritance of styles
  • use loops and lists to manage several items
import io.data2viz.color.* import io.data2viz.math.* import io.data2viz.viz.* import io.data2viz.geom.* //sampleStart fun main() { val positions = (0 until 100).map { point(it%10, it/10) } val visuals = mutableListOf<GroupNode>() val myViz = viz { size = size(400, 400) // use a loop to create a visual for each "position" positions.forEach { position -> // add the newly created group in our list and in the viz visuals += group { transform { translate(position.x * 40, 10.0 + position.y * 40) } // use style inheritance to share styles within the group fill = Colors.Web.blueviolet stroke = Colors.Web.black rect { size = size(20, 20) } circle { // position is relative to the group position x = 20.0 radius = 10.0 } } } } // change a specific group properties visuals[68].fill = Colors.Web.crimson myViz.bindRendererOnNewCanvas() } //sampleEnd

Animation

To animate and time your visualizations you can use the animation lambda in your viz element. This creates a new Timer that execute your inner code block on each frame.

You can stop a Timer by using the stop() function, this will not stop the rendering of each frame. If animations are no more needed call stopAnimations() on your viz element.

import io.data2viz.color.* import io.data2viz.geom.* import io.data2viz.math.* import io.data2viz.viz.* import kotlin.math.* //sampleStart fun main() { viz { size = size(300, 300) var counter = .0 var increment = .03 val colors = listOf( Colors.Web.greenyellow, Colors.Web.blueviolet, Colors.Web.crimson ) // creating 3 moiré patterns val moires = (0 .. 2).map { index -> group { strokeWidth = 5.0 stroke = colors[index] transform { translate(150.0, 150.0) } (0..15).forEach { circle { radius = it * 8.0 } } } } animation { counter += increment if (counter > 20) increment = -increment // this will stop ALL animation timers and further rendering if (counter < -20) stopAnimations() // moving moiré patterns 1 & 2 val pos1 = 150.0 + counter * cos(counter) val pos2 = 150.0 + counter * sin(counter) moires[1].transform { translate(pos1, pos2) } moires[2].transform { translate(pos2, pos1) } } }.bindRendererOnNewCanvas() } //sampleEnd