Skip to content

Commit

Permalink
Merge pull request #13 from sisl/abstract
Browse files Browse the repository at this point in the history
added AbstractTrees compatibility to fix #8
  • Loading branch information
zsunberg authored Apr 4, 2018
2 parents 3da9827 + 578b74e commit 024de93
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 17 deletions.
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@

Flexible interactive visualization for large trees using [D3.js](d3js.org).

![Tree](img/tree.png)

## Installation

```julia
Pkg.add("D3Trees")
```
or
```julia
Pkg.clone("https://github.com/sisl/D3Trees.jl.git")
```

## Usage

There are two ways to create a D3Tree object described below:

### With AbstractTrees

Any object that implements the interface from AbstractTrees can given to the constructor: `D3Tree(object)`.

### Without AbstractTrees

The structure of a D3Tree is specified with *lists of children for each node* stored in a `Vector` of `Int` `Vector`s. For example
```julia
D3Tree([[2,3], [], [4], []])
Expand All @@ -31,8 +37,7 @@ t = D3Tree(children)

inchrome(t)
```
By clicking on the nodes, you can expand it to look like this:
![Tree](img/tree.png)
By clicking on the nodes, you can expand it to look like the image at the top of the page.

Optional arguments control other aspects of the style (use `julia> ?D3Tree` for a complete list), for example
```julia
Expand Down Expand Up @@ -80,3 +85,8 @@ julia> t = D3Tree(children)
## Browser compatibility

This package works best in the Google chrome or chromium browser.

## Limitations

- The tree is currently transmitted to the browser in one big json string, so it can be slow for large trees and cannot handle infinite trees.
- This will not work offline because it downloads the D3 library on the fly (https://github.com/sisl/D3Trees.jl/issues/10)
1 change: 1 addition & 0 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
julia 0.6
JSON
Blink
AbstractTrees 0.1
61 changes: 55 additions & 6 deletions src/D3Trees.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module D3Trees

using JSON
using Blink
using AbstractTrees
import AbstractTrees: printnode

export
D3Tree,
Expand Down Expand Up @@ -46,7 +48,7 @@ Construct a tree to be displayed using D3 in a browser or ipython notebook.
- `init_duration::Number` - duration of the initial animation in ms.
- `svg_height::Number` - height of the svg containing the tree in px.
"""
function D3Tree(children; kwargs...)
function D3Tree(children::AbstractVector{<:AbstractVector}; kwargs...)
kwd = Dict(kwargs)
n = length(children)
return D3Tree(children,
Expand All @@ -58,17 +60,64 @@ function D3Tree(children; kwargs...)
convert(Dict{Symbol, Any}, kwd)
)
end


"""
D3Tree(node; detect_repeat=true)
Construct a tree to be displayed using D3 in a browser or ipython notebook from any object that implements the AbstractTrees interface.
# Arguments
## Required
- `node`: an object that has AbstractTrees.children(node) and AbstractTrees.printnode(io::IO, node)
## Keyword
- `detect_repeat`: if true, uses a dictionary to detect whether a node has appeared previously
"""
function D3Tree(node; detect_repeat::Bool=true)
t = D3Tree(Vector{Int}[])
if detect_repeat
node_dict = Dict{Any, Int}()
push_node!(t, node, node_dict)
else
push_node!(t, node)
end
return t
end

function push_node!(t, node, node_dict=nothing)
if !(node_dict isa Void) && haskey(node_dict, node)
return node_dict[node]
end

ind = length(t.children) + 1
if !(node_dict isa Void)
node_dict[node] = ind
end
if length(t.children) < ind
push!(t.children, Int[])
iob = IOBuffer()
printnode(iob, node)
push!(t.text, String(take!(iob)))
end
for c in children(node)
c_ind = push_node!(t, c, node_dict)
push!(t.children[ind], c_ind)
end
return ind
end

struct D3TreeNode
tree::D3Tree
index::Int
end

# this should be AbstractTrees.children
children(n::D3TreeNode) = (D3TreeNode(n.tree, c) for c in n.tree.children[n.index])
AbstractTrees.children(n::D3TreeNode) = (D3TreeNode(n.tree, c) for c in n.tree.children[n.index])
AbstractTrees.children(t::D3Tree) = children(D3TreeNode(t, 1))
n_children(n::D3TreeNode) = length(n.tree.children[n.index])
# this should be AbstractTrees.printnode
printnode(io::IO, n::D3TreeNode) = print(io, n.tree.text[n.index])
AbstractTrees.printnode(io::IO, n::D3TreeNode) = print(io, n.tree.text[n.index])

struct D3TreeView
root::D3TreeNode
Expand Down
14 changes: 9 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ text = ["one\n(second line)", "2", "III", "four"]
style = ["", "fill:red", "r:14", "opacity:0.7"]
link_style = ["", "stroke:blue", "", "stroke-width:10px"]
tooltip = ["pops", "up", "on", "hover"]
t = D3Tree(children,
t1 = D3Tree(children,
text=text,
style=style,
tooltip=tooltip,
link_style=link_style)

@show json(t)
@show json(t1)

stringmime(MIME("text/html"), t)
stringmime(MIME("text/html"), t1)

t2 = D3Tree(children,
text=text,
Expand All @@ -31,8 +31,8 @@ t2 = D3Tree(children,
# inchrome(t)
# inchrome(t2)

@show D3Trees.children(D3TreeNode(t, 1))
show(STDOUT, MIME("text/plain"), t)
@show D3Trees.children(D3TreeNode(t1, 1))
show(STDOUT, MIME("text/plain"), t1)

nbinclude("../examples/hello.ipynb")

Expand All @@ -48,3 +48,7 @@ println("creating tree object")

println("html string")
@time stringmime(MIME("text/html"), t)

println("AbstractTrees constructor")
@time t3 = D3Tree(t1)
@time t4 = D3Tree(t1, detect_repeat=false)

0 comments on commit 024de93

Please sign in to comment.