001package conexp.fx.gui.graph;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2019 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.util.Set;
026
027import com.google.common.collect.HashMultimap;
028import com.google.common.collect.Multimap;
029
030import conexp.fx.core.context.Concept;
031import conexp.fx.core.context.ConceptLattice;
032import javafx.event.EventHandler;
033import javafx.scene.Scene;
034import javafx.scene.input.MouseEvent;
035import javafx.scene.layout.Pane;
036import javafx.scene.layout.StackPane;
037import javafx.scene.paint.Color;
038import javafx.scene.shape.Shape;
039import javafx.stage.Stage;
040
041public class CircularGraph<G, M> {
042
043  private final Stage primaryStage;
044
045  public CircularGraph(final ConceptLattice<G, M> lattice) {
046    super();
047    this.primaryStage = new Stage();
048    final Pane rootPane = new StackPane();
049    this.primaryStage.setScene(new Scene(rootPane, 1280, 800));
050    final Multimap<Concept<G, M>, Area> layers = computeLayers(lattice, 360d, 36d);
051    final Multimap<Concept<G, M>, SuperNode> nodes = HashMultimap.<Concept<G, M>, SuperNode> create();
052    for (Concept<G, M> c : layers.keySet())
053      for (Area a : layers.get(c)) {
054        final SuperNode n = new SuperNode(a.y, a.y + a.h, a.x, a.w, fromHashCode(c));
055        nodes.put(c, n);
056        rootPane.getChildren().add(n);
057        addListener(c, n, nodes);
058      }
059  }
060
061  public final void show() {
062    this.primaryStage.show();
063  }
064
065  private void addListener(final Concept<G, M> c, final SuperNode n, final Multimap<Concept<G, M>, SuperNode> nodes) {
066    ((Shape) n.getChildren().get(1)).addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
067
068      @Override
069      public void handle(MouseEvent event) {
070        for (SuperNode node : nodes.get(c))
071          ((Shape) node.getChildren().get(1)).setFill(Color.RED);
072      }
073    });
074    ((Shape) n.getChildren().get(1)).addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
075
076      @Override
077      public void handle(MouseEvent event) {
078        for (SuperNode node : nodes.get(c))
079          ((Shape) node.getChildren().get(1)).setFill(fromHashCode(c));
080      }
081    });
082  }
083
084  private Color fromHashCode(final Object o) {
085//    int rnd = (int) (new Random().nextFloat() * 23f);
086    int h = o.hashCode();// * rnd;
087    int m = 192;
088    int r = (h * 7) % m;
089    int g = (h * 17) % m;
090    int b = (h * 37) % m;
091    if (r < 0)
092      r += m;
093    if (g < 0)
094      g += m;
095    if (b < 0)
096      b += m;
097    r += (256 - m);
098    g += (256 - m);
099    b += (256 - m);
100    return Color.rgb(r, g, b);
101  }
102
103  private class Area {
104
105    private final double x, y, w, h;
106
107    public Area(double x, double y, double w, double h) {
108      super();
109      this.x = x;
110      this.y = y;
111      this.w = w;
112      this.h = h;
113    }
114
115  }
116
117  private Multimap<Concept<G, M>, Area> computeLayers(
118      final ConceptLattice<G, M> lattice,
119      final double width,
120      final double layerHeight) {
121    final Multimap<Concept<G, M>, Area> layers = HashMultimap.<Concept<G, M>, Area> create();
122    final Concept<G, M> top = lattice.context.topConcept();
123    final Area parent = new Area(0, 0, width, layerHeight);
124    layers.put(top, parent);
125    addNextLayer(lattice, layers, parent, lattice.col(top));
126    return layers;
127  }
128
129  private void addNextLayer(
130      final ConceptLattice<G, M> lattice,
131      final Multimap<Concept<G, M>, Area> layers,
132      final Area parent,
133      final Set<Concept<G, M>> concepts) {
134    double l = parent.w / (double) concepts.size();
135    double t = parent.x;
136    for (Concept<G, M> c : concepts) {
137      final Area a = new Area(t, parent.y + parent.h, l, parent.h);
138      layers.put(c, a);
139      addNextLayer(lattice, layers, a, lattice.col(c));
140      t += l;
141    }
142  }
143}