gwtd3 / gwt-d3

A GWT wrapper library around the d3.js library
Other
131 stars 53 forks source link

I'm creating a collapsible radial tree and facing issues, links are colapsing but nodes remains at same positions can you me with that. #139

Open jaspreet-ephesoft opened 8 years ago

jaspreet-ephesoft commented 8 years ago
package com.github.gwtd3.demo.client.democases.layout;

import com.github.gwtd3.api.Coords;
import com.github.gwtd3.api.D3;
import com.github.gwtd3.api.arrays.Array;
import com.github.gwtd3.api.arrays.ForEachCallback;
import com.github.gwtd3.api.core.Selection;
import com.github.gwtd3.api.core.Transition;
import com.github.gwtd3.api.core.UpdateSelection;
import com.github.gwtd3.api.core.Value;
import com.github.gwtd3.api.functions.DatumFunction;
import com.github.gwtd3.api.functions.KeyFunction;
import com.github.gwtd3.api.layout.Cluster.Node;
import com.github.gwtd3.api.layout.HierarchicalLayout.Link;
import com.github.gwtd3.api.layout.SeparationFunction;
import com.github.gwtd3.api.layout.Tree;
import com.github.gwtd3.api.svg.Diagonal;
import com.github.gwtd3.demo.client.DemoCase;
import com.github.gwtd3.demo.client.Factory;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsonUtils;
import com.google.gwt.dom.client.Element;
import com.google.gwt.http.client.*;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlowPanel;

import java.util.ArrayList;
import java.util.List;

/**
 * Radial Reingold–Tilford Tree
 * Example inspired from original Javascript example available at http://bl.ocks.org/mbostock/4063550
 *
 * @author Eric Citaire
 */
public class RadialReingoldTilfordTree extends FlowPanel implements DemoCase {
    private static final String JSON_URL = "demo-data/flare.json";
    private final MyResources css;
    FlareNode root1 = null;

    Tree<FlareNode> tree;

    int i = 0;

    Diagonal diagonal;

    Selection svg;

    Array<Tree.Node<FlareNode>> nodes;

    Array<Link<FlareNode>> links;

    Boolean flag = true;

    public RadialReingoldTilfordTree() {
        css = Bundle.INSTANCE.css();
        css.ensureInjected();
    }

    public static Factory factory() {
        return new Factory() {
            @Override
            public DemoCase newInstance() {
                return new RadialReingoldTilfordTree();
            }
        };
    }

    @Override
    public void start() {
        double diameter = 960;

        tree = D3.layout().tree();
        tree.children(new DatumFunction<List<FlareNode>>() {
            @Override
            public List<FlareNode> apply(final Element context, final Value d, final int index) {
                FlareNode node = d.as(FlareNode.class);
                return node.isLeaf() ? new ArrayList<FlareNode>() : node.children().asList();
            }
        })
                .size(360, diameter / 2 - 120)
                .separation(new SeparationFunction<Tree.Node<FlareNode>>() {
                    @Override
                    public double separation(final Tree.Node<FlareNode> a,
                                             final Tree.Node<FlareNode> b) {
                        return (double) (a.parent() == b.parent() ? 1 : 2) / a.depth();
                    }
                });

        diagonal = D3.svg().radialDiagonal()
                .projection(new DatumFunction<Array<Double>>() {
                    @Override
                    public Array<Double> apply(final Element context, final Value value, final int index) {
                        return Array.fromDoubles(value.asCoords().y(), value.asCoords().x() / 180 * Math.PI);
                    }
                });

        svg = D3.select(this).append("svg")
                .attr("width", diameter)
                .attr("height", diameter - 150)
                .append("g")
                .attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");

        // Send request to server and catch any errors.

        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, JSON_URL);

        try {
            Request request = builder.sendRequest(null, new RequestCallback() {
                @Override
                public void onError(final Request request, final Throwable exception) {
                    Window.alert("Couldn't retrieve JSON");
                }

                @Override
                public void onResponseReceived(final Request request, final Response response) {
                    if (200 == response.getStatusCode()) {

                        root1 = JsonUtils.safeEval(response.getText());
                        root1.x0(250);
                        root1.y0(0);

                        root1.children().forEach(new ForEachCallback<Void>() {
                            @Override
                            public Void forEach(Object thisArg, Value element, int index, Array<?> array) {
                                FlareNode node = element.<FlareNode>as();

                                 //node.set_children(node.children());
                                //node.setChildren();
                                return null;
                            }
                        });

                        update(root1);

                    } else {
                        Window.alert("Couldn't retrieve JSON (" + response.getStatusText() + ")");
                    }
                }
            });

        } catch (RequestException e) {
            Window.alert("Couldn't retrieve JSON");
        }
        D3.select(RadialReingoldTilfordTree.this).style("height", diameter - 150 + "px");
    }

    void update(FlareNode source) {

        Window.alert("...");

            nodes = tree.nodes(root1);
        links = tree.links(nodes);

        nodes.forEach(new ForEachCallback<Void>() {
            @Override
            public Void forEach(Object thisArg, Value d, int index, Array<?> array) {
                Tree.Node<FlareNode> node = d.<Tree.Node<FlareNode>>as();
                node.y(node.depth() * 130);
                return null;
            }
        });

        DatumFunction<String> transform = new DatumFunction<String>() {
            @Override
            public String apply(final Element context, final Value d, final int index) {
                Node<FlareNode> node = d.<Node<FlareNode>>as();
                double x = node.x();
                double y = node.y();
                return "rotate(" + (x - 90) + ")translate(" + y + ")";
            }
        };

        UpdateSelection node = svg.selectAll("g.node")
                .data(nodes, new KeyFunction<Integer>() {
                    @Override
                    public Integer map(Element context, Array<?> newDataArray, Value datum, int index) {
                        Node<FlareNode> node = datum.<Node<FlareNode>>as();
                        Integer id = node.id();
                        if(id == null) {
                            node.id(++i);
                        }
                        Window.alert(node.id()+"");
                        return node.id();
                    }
                });

        DatumFunction<Void> click = new DatumFunction<Void>() {
            @Override
            public Void apply(Element context, Value d, int index) {

                flag = false;
                FlareNode node = d.<FlareNode>as();
                Array<FlareNode> array = node.children();

                node.setChildren();
                // node.children()=node._children();
                //node.swapChildren(node._children());
                update(node);

                return null;
            }
        };

        final Selection nodeEnter = node.enter().append("g")
                .attr("class", css.node())
                .attr("transform", transform)
                .on("click", click);

        nodeEnter.append("circle")
                .attr("r", 4.5);

        nodeEnter.append("text")
                .attr("dy", ".31em")
                .attr("text-anchor", new DatumFunction<String>() {
                    @Override
                    public String apply(final Element context, final Value d, final int index) {
                        Tree.Node<FlareNode> node = d.<Tree.Node<FlareNode>>as();
                        return node.x() < 180 ? "start" : "end";
                    }
                })
                .attr("transform", new DatumFunction<String>() {
                    @Override
                    public String apply(final Element context, final Value d, final int index) {
                        Tree.Node<FlareNode> node = d.<Tree.Node<FlareNode>>as();
                        return node.x() < 180 ? "translate(8)" : "rotate(180)translate(-8)";
                    }
                })
                .text(new DatumFunction<String>() {
                    @Override
                    public String apply(final Element context, final Value d, final int index) {
                        Node<FlareNode> node = d.<Node<FlareNode>>as();
                        return node.datum().name();
                    }
                });

        //Transitions

        Transition nodeUpdate = node.transition().duration(350).attr("transform", new DatumFunction<String>() {

            @Override
            public String apply(final Element context, final Value d, final int index) {

                Tree.Node<FlareNode> node = d.<Tree.Node<FlareNode>>as();
                double x = node.x();
                double y = node.y();
                return "rotate(" + (x - 90) + ")translate(" + y + ")";
            }
        });

        nodeUpdate.select("circle").attr("r", 4.5).style("fill", new DatumFunction<String>() {
            @Override
            public String apply(Element context, Value d, int index) {

               Node<FlareNode> node = d.<Node<FlareNode>>as();
                node.children().length();

                return node.children().length()>0 ? "lightsteelblue" : "#fff";
            }
        });

        nodeUpdate.select("text").style("fill-opacity", 1);

      /*  Transition nodeExit = node.exit().transition().duration(350).remove();

        nodeExit.select("circle").attr("r", 1e-6);

        nodeExit.select("text").style("fill-opacity", 1e-6);*/

        UpdateSelection link = svg.selectAll("path"+"." + css.link())
                .data(links, new KeyFunction<Integer>() {
                    @Override
                    public Integer map(Element context, Array<?> newDataArray, Value datum, int index) {

                        Tree.Link<FlareNode> link = datum.<Tree.Link<FlareNode>> as();

                        Node target=link.target();
                        return target.id();
                    }
                });

        Selection linkEnter = link.enter().append("path")
                .attr("class", css.link())
                .attr("d", diagonal);

        //update Links
        link.transition().duration(350).attr("d", diagonal);

        link.exit().transition()./*duration(350).attr("d", new DatumFunction<Diagonal>() {
            @Override
            public Diagonal apply(Element context, Value d, int index) {

                Diagonal di= D3.svg().radialDiagonal();
                Tree.Node<FlareNode> link = d.<Tree.Node<FlareNode>> as();
                di.source(Coords.create(source.x,source.y));
                di.target(Coords.create(source.x, source.y));
                return null;
            }
        }).*/remove();

        nodes.forEach(new ForEachCallback<Void>() {
            @Override
            public Void forEach(Object thisArg, Value d, int index, Array<?> array) {

                Tree.Node<FlareNode> node = d.<Tree.Node<FlareNode>>as();
                double x = node.x();
                double y = node.y();

                FlareNode node1 = d.<FlareNode>as();

                node1.x0((int) x);
                node1.y0((int) y);

                return null;

            }
        });

    }

    @Override
    public void stop() {

    }

    public interface Bundle extends ClientBundle {
        public static final Bundle INSTANCE = GWT.create(Bundle.class);

        @Source("RadialReingoldTilfordTree.css")
        public MyResources css();
    }

    interface MyResources extends CssResource {
        String link();

        String node();
    }

    public static class FlareNode extends JavaScriptObject {

        protected FlareNode() {

        }

        public final native String name() /*-{
            return this.name;
        }-*/;

        public final native int size() /*-{
            return this.size;
        }-*/;

        public final native Array<FlareNode> children()/*-{
            return this.children;
        }-*/;

        public final native void set_children(Array<FlareNode> array)/*-{
            this._children = array;
        }-*/;

        public final native Array<FlareNode> _children()/*-{
            return this._children;
        }-*/;

        public final native void setChildren()/*-{
            this.children = null;
        }-*/;

        public final native void swapChildren(Array<FlareNode> array)/*-{
            this.children = array;
        }-*/;

        public final native void setId(int i)/*-{
            this.id = i;
        }-*/;

        public final native int id()/*-{
            this.id;
        }-*/;

        public final native int x0(int i)/*-{
            this.x0 = i;
        }-*/;

        public final native int y0(int i)/*-{
            this.y0 = i;
        }-*/;

        public final native boolean isLeaf()/*-{
            return !this.children;
        }-*/;
    }
}