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

io.data2viz.hierarchy.Hierarchy.kt Maven / Gradle / Ivy

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

interface Valued { var value: Double? }
interface Children { val parent: ParentValued? }
interface ParentValued: Valued {val children: List>}

data class Node(
    val data: D,
    var depth: Int = 0,
    var height: Int = 0,
    override var value: Double? = null,
    override val children: MutableList> = mutableListOf(),
    override var parent: Node? = null
): ParentValued>, Children>

data class Link(
    val source: Node?,
    var target: Node
)

/**
 * Constructs a root node from the specified hierarchical data. The specified data must be an object
 * representing the root node.
 * The specified children accessor function is invoked for each datum, starting with the root data, and must
 * return an array of data representing the children, or null if the current datum has no children.
 * The specified value accessor function is invoked for each datum, starting with the root data, and must return
 * a Double value representing the data. If value is not specified, it defaults to null (no value for nodes).
 * TODO : value
 */
fun  hierarchy(data: D, children: (D) -> List?, value: ((D) -> Double)? = null): Node {
    val root = Node(data)
    val nodes = mutableListOf(root)

    while (nodes.size > 0) {
        val node = nodes.removeAt(nodes.lastIndex)
        val childs = children(node.data)
        if (childs != null) {
            childs.forEach { c ->
                val child = Node(c)
                child.parent = node
                child.depth = node.depth + 1
                //if (value != null) child.value = value(c)
                node.children.add(child)
                nodes.add(child)
            }
        }
    }
    return root.eachBefore(::computeHeight)
}

/**
 * Computes the number of leaves under this node and assigns it to node.value, and similarly for every
 * descendant of node.
 * If this node is a leaf, its count is one.
 * Returns this node.
 * See also node.sum.
 */
fun  Node.count(): Node {
    return this.eachAfter(::nodeCount)
}

/**
 * TODO check behavior (especially non-negative value)
 * Evaluates the specified value function for this node and each descendant in post-order traversal,
 * and returns this node.
 * The node.value property of each node is set to the numeric value returned by the specified function plus
 * the combined value of all descendants. The function is passed the node’s data, and must return a non-negative number.
 * The value accessor is evaluated for node and every descendant, including internal nodes; if you only want leaf
 * nodes to have internal value, then return zero for any node with children.
 */
fun  Node.sum(value: ((D) -> Double)? = null): Node {
    return this.eachAfter({ node: Node ->
        var sum = if (value != null) value(node.data) else .0
        node.children.forEach { child -> if (child.value != null) sum += child.value!! }
        node.value = sum
    })
}

/**
 * Returns the array of ancestors nodes, starting with this node, then followed by each parent up to the root.
 */
fun  Node.ancestors(): List> {
    val nodes: MutableList> = mutableListOf(this)
    var node: Node? = this
    while (node != null
    ) {
        nodes.add(node)
        node = node.parent
    }
    return nodes.toList()
}

/**
 * Returns the array of descendant nodes, starting with this node, then followed by each child in topological order.
 */
fun  Node.descendants(): List> {
    val nodes: MutableList> = mutableListOf()
    this.each({ node: Node ->
        nodes.add(node)
    })
    return nodes
}

/**
 * Returns the array of leaf nodes in traversal order; leaves are nodes with no children.
 */
fun  Node.leaves(): List> {
    val leaves: MutableList> = mutableListOf()
    this.eachBefore({ node: Node ->
        if (node.children.isEmpty()) leaves.add(node)
    })
    return leaves
}

/**
 * Returns an array of links for this node, where each link is an object that defines source and target properties.
 * The source of each link is the parent node, and the target is a child node.
 */
fun  Node.links(): List> {
    val root = this
    val links: MutableList> = mutableListOf()
    root.each({ node: Node ->
        if (node != root) links.add(Link(node.parent, node))    // Don’t include the root’s parent, if any.
    })
    return links.toList()
}

inline fun , D> separation(nodeA: N, nodeB: N) = if (nodeA.parent == nodeB.parent) 1 else 2

/**
 * Invokes the specified function for node and each descendant in breadth-first order, such that a given
 * node is only visited if all nodes of lesser depth have already been visited, as well as all preceeding
 * nodes of the same depth.
 * The specified function is passed the current node.
 */
inline fun , D> N.each(callback: (N) -> Unit): N {
    val next = mutableListOf(this)
    while (next.size > 0) {
        val current = next.reversed().toMutableList()
        next.clear()
        val node = current.removeAt(current.lastIndex)
        callback(node)
        val children = node.children
        if (children.isNotEmpty()) {
            (children.lastIndex downTo 0).forEach {
                next.add(children[it] as N)
            }
        }
    }
    return this
}

/**
 * Invokes the specified function for node and each descendant in pre-order traversal, such that a given node
 * is only visited after all of its ancestors have already been visited.
 * The specified function is passed the current node.
 */
inline fun , D> N.eachBefore(callback: (N) -> Unit): N {
    val nodes = mutableListOf(this)
    while (nodes.isNotEmpty()) {
        val node = nodes.removeAt(nodes.lastIndex)
        callback(node)
        val children = node.children
        if (children.isNotEmpty()) {
            (children.lastIndex downTo 0).forEach {
                nodes.add(children[it] as N)
            }
        }
    }
    return this
}

/**
 * Invokes the specified function for node and each descendant in post-order traversal, such that a given node
 * is only visited after all of its descendants have already been visited.
 * The specified function is passed the current node.
 */
inline fun , D> N.eachAfter(callback: (N) -> Unit): N {
    val nodes = mutableListOf(this)
    val next = mutableListOf()
    while (nodes.isNotEmpty()) {
        val node = nodes.removeAt(nodes.lastIndex)
        next.add(node)
        val children = node.children
        if (children.isNotEmpty()) {
            children.forEach {
                nodes.add(it as N)
            }
        }
    }
    next.reversed().forEach(callback)
    return this
}

private fun  computeHeight(node: Node) {
    var n: Node = node
    var height = 0
    n.height = height
    height++

    while (n.parent != null && n.parent!!.height < height) {
        n.parent!!.height = height
        n = n.parent!!
        height++
    }
}

private fun  nodeCount(node: Node) {
    var sum = .0
    val children = node.children
    if (children.isEmpty()) sum = 1.0
    else {
        children.forEach { if (it.value != null) sum += it.value!! }
    }
    node.value = sum
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy