All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.data2viz.viz.Viz.kt Maven / Gradle / Ivy

There is a newer version: 0.8.0-RC5
Show newest version
package io.data2viz.viz

import io.data2viz.color.ColorOrGradient
import io.data2viz.color.Colors
import io.data2viz.geom.HasSize
import io.data2viz.timer.Timer

/**
 * Viz is the base element of a visualization.
 *
 * It is associated with a renderer which is used to perform the rendering depending on
 * the current platform.
 *
 * It has at least one layer (the activeLayer). Layers provides a way of managing what is
 * drawn on the background and what is drawn on frontend. The rendering process start with
 * the layer with the lower index.
 *
 * Viz respects the `HasChildren` interface. It is possible to directly invoke some creation
 * function on its context. The created element are then added to the active layer. It provides
 * a very easy way to start a visualization.
 *
 * Viz width and height can be seen as the target size of the visualisation on a standard
 * screen (no hi resolution) for a web rendering. These sizes will then be used during the
 * rendering process on each platform to adapt the visualisation to the target device, taking
 * in account the resolution of the screen, and the configuration of the viz.
 */
class Viz(var activeLayer: Layer = Layer()) : HasChildren by activeLayer, HasSize {

    private val style: Style = StyleImpl()


    //Style delegation
    override var fill: ColorOrGradient?
        get() = style.fill
        set(value) {
            style.fill = value
        }

    override var stroke: ColorOrGradient?
        get() = style.stroke
        set(value) {
            style.stroke = value
        }

    override var strokeWidth: Double?
        get() = style.strokeWidth
        set(value) {
            style.strokeWidth = value
        }

    override var textColor: ColorOrGradient?
        get() = style.textColor
        set(value) {
            style.textColor = value
        }

    override var hAlign: TextHAlign
        get() = style.hAlign
        set(value) {
            style.hAlign = value
        }

    override var vAlign: TextVAlign
        get() = style.vAlign
        set(value) {
            style.vAlign = value
        }

    init {
        activeLayer.parent = this
        textColor = Colors.Web.black
    }

    val config = VizConfig()

    override var width: Double = 100.0
    override var height: Double = 100.0

    val layers = mutableListOf(activeLayer)

    private var resizeBehavior: ((Double, Double) -> Unit)? = null

	val eventListeners = mutableListOf>()

    var renderer: VizRenderer? = null
    set(newValue) {
        val oldValue = field
        field = newValue
        eventListeners.forEach {
            oldValue?.removeEventHandle(it)
			newValue?.addEventHandle(it)
        }

    }

    fun  on(eventListener: KEventListener, listener: (T) -> Unit): KEventHandle where  T : KEvent {
        val eventHandle = KEventHandle(eventListener, listener) {
            eventListeners.remove(it)

            renderer?.apply {
                removeEventHandle(it)
            }
        }
        eventListeners.add(eventHandle)
        renderer?.addEventHandle(eventHandle)

        return eventHandle
    }


    fun render() {
        renderer!!.render()
    }

    fun startAnimations() {
        renderer!!.startAnimations()
    }

    fun stopAnimations() {
        renderer!!.stopAnimations()
    }

    internal val animationTimers = mutableListOf Unit>()

    /**
     * Add an animation timer. The given block is an extension function on a Timer.
     * It will be executed inside a timer with the elapsed time in ms as a parameter.
     * It is possible to stop the timer from the block by calling `stop()` function
     * from the block.
     */
    fun animation(block: Timer.(Double) -> Unit) {
        animationTimers.add(block)
    }

    @Deprecated("Should use an animation timer", ReplaceWith("animation(block)"))
    fun onFrame(block: (Double) -> Unit) {
        animation { block(it) }
    }

    fun onResize(block: (newWidth: Double, newHeight: Double) -> Unit) {
        resizeBehavior = block
    }

    fun resize(newWidth: Double, newHeight: Double) {
        resizeBehavior?.invoke(newWidth, newHeight)
    }

    fun layer(): Layer {
        val layer = Layer().also { it.parent = this }
        layers.add(layer)
        activeLayer = layer
        return layer
    }

}


fun viz(init: Viz.() -> Unit): Viz = Viz().apply(init)


interface StateableElement {
    var stateManager: StateManager?
}


/**
 * Indicate an element on which we can apply a Transformation.
 * todo implement other transformation (rotate, ...)
 */
class Transform {
    var translate: Translation? = null
    fun translate(x: Double = 0.0, y: Double = 0.0) {
        translate = Translation(x, y)
    }

    var rotate: Rotation? = null
    fun rotate(delta: Double) {
        rotate = Rotation(delta)
    }

    operator fun plusAssign(transform: Transform) {
        translate?.apply {
            x += transform.translate?.x ?: .0
            y += transform.translate?.y ?: .0
        }

        rotate?.apply {
            delta += transform.rotate?.delta ?: .0
        }

    }

    operator fun minusAssign(transform: Transform) {
        translate?.apply {
            x -= transform.translate?.x ?: .0
            y -= transform.translate?.y ?: .0
        }
        rotate?.apply {
            delta -= transform.rotate?.delta ?: .0
        }

    }
}

data class Translation(var x: Double = 0.0, var y: Double = 0.0)
data class Rotation(var delta: Double = 0.0)


/**
 * All properties of stroke
 * Todo add remaining common properties
 */
interface HasStroke {

    var stroke: ColorOrGradient?
    var strokeWidth: Double?
}

interface HasFill {
    var fill: ColorOrGradient?
}


data class Margins(val top: Double, val right: Double = top, val bottom: Double = top, val left: Double = right) {
    val hMargins = right + left
    val vMargins = top + bottom
}

interface HasTransform {
    val transform: Transform?
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy