Как применить пользовательский макет в графике JUNG?
У меня есть следующие три набора целых чисел, как следующие:
set0 = {1} //this will always be a singleton set.
set1 = {2, 3, 4, 5}
set2 = {6, 7}
У меня есть граф, у которого есть ребра, которые либо от set1 до set2 ИЛИ от set2 до set3, таким образом формируя четкую древовидную иерархию вершин.
Set0 -- Set1 -- Set2
Чтобы отобразить этот древовидный график, я создал DelegateForest
а также TreeLayout
package Test;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.TreeLayout;
import edu.uci.ics.jung.graph.DelegateForest;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
class Main{
public static void main(String[] args){
Set<Integer> set0 = new HashSet<Integer>();
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> set2 = new HashSet<Integer>();
set0.add(1);
set1.add(2);
set1.add(3);
set1.add(4);
set1.add(5);
set2.add(6);
set2.add(7);
JFrame frame = new JFrame();
frame.add(createGraphPanel(set0, set1, set2));
frame.pack();
frame.setVisible(true);
}
private static JPanel createGraphPanel( Set<Integer> setZero, Set<Integer> firstSet, Set<Integer> secondSet) {
// create a graph
Graph<Integer, String> graph = new DelegateForest<Integer, String>();
Integer vertex1 = setZero.iterator().next();
for (Integer i : firstSet) {
graph.addEdge(vertex1+"-"+i, vertex1, i);
}
Layout<Integer, String> layout = new TreeLayout<Integer, String>((Forest<Integer, String>) graph);
VisualizationViewer<Integer, String> vv = new VisualizationViewer<Integer,String>(layout);
vv.getRenderContext().setVertexLabelTransformer(
new ToStringLabeller<Integer>());
return vv;
}
}
Тем не менее, график, который я получил (в настоящее время содержит только set1 и set2) выглядит следующим образом
Я хотел бы сделать пару вещей с этим графиком:
- Вместо того, чтобы узлы текли сверху вниз, я бы хотел, чтобы они текли слева направо. (Что-то вроде поворота на 90 градусов)
- В настоящее время алгоритм компоновки гарантирует отсутствие перекрытия, что расширяет узлы для set2 линейным образом. Если set2 огромен, он выходит за пределы панели. Я не против перекрытия и хотел бы, чтобы эти узлы выглядели близко друг к другу как кластер с частичным перекрытием.
Как я могу достичь двух требований?
2 ответа
Я расширил класс TreeLayout и поменял местами все переменные x / y. Это должно отобразить дерево горизонтально. Однако вам нужно будет добавить собственный код, чтобы предотвратить размещение вершин в строке (возможно, используйте ограничивающий прямоугольник и начинайте сверху, как только вы пройдете мимо него).
public class HorizontalOverlappingTreeLayout<V, E> extends TreeLayout<V, E> {
public static void main(String[] args) {
Set<Integer> set0 = new HashSet<Integer>();
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> set2 = new HashSet<Integer>();
set0.add(1);
set1.add(2);
set1.add(3);
set1.add(4);
set1.add(5);
set2.add(6);
set2.add(7);
JPanel panel = new JPanel();
Graph<Integer, String> graph = new DelegateForest<Integer, String>();
Integer vertex1 = set0.iterator().next();
for (Integer i : set1) {
graph.addEdge(vertex1 + "-" + i, vertex1, i);
}
Layout<Integer, String> layout = new HorizontalOverlappingTreeLayout<Integer, String>(
(Forest<Integer, String>) graph);
VisualizationViewer<Integer, String> vv = new VisualizationViewer<Integer, String>(layout);
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<Integer>());
panel.add(vv);
JFrame frame = new JFrame();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public HorizontalOverlappingTreeLayout(Forest<V, E> g) {
super(g);
}
@Override
protected void buildTree() {
this.m_currentPoint = new Point(0, 20);
Collection<V> roots = TreeUtils.getRoots(graph);
if (roots.size() > 0 && graph != null) {
calculateDimensionY(roots);
for (V v : roots) {
calculateDimensionY(v);
m_currentPoint.y += this.basePositions.get(v) / 2 + this.distY;
buildTree(v, this.m_currentPoint.y);
}
}
// TODO: removed code here
}
@Override
protected void buildTree(V v, int y) {
if (!alreadyDone.contains(v)) {
alreadyDone.add(v);
// go one level further down
this.m_currentPoint.x += this.distX;
this.m_currentPoint.y = y;
this.setCurrentPositionFor(v);
int sizeYofCurrent = basePositions.get(v);
int lastY = y - sizeYofCurrent / 2;
int sizeYofChild;
int startYofChild;
for (V element : graph.getSuccessors(v)) {
sizeYofChild = this.basePositions.get(element);
startYofChild = lastY + sizeYofChild / 2;
buildTree(element, startYofChild);
lastY = lastY + sizeYofChild + distY;
}
this.m_currentPoint.x -= this.distX;
}
}
private int calculateDimensionY(V v) {
int size = 0;
int childrenNum = graph.getSuccessors(v).size();
if (childrenNum != 0) {
for (V element : graph.getSuccessors(v)) {
size += calculateDimensionY(element) + distY;
}
}
size = Math.max(0, size - distY);
basePositions.put(v, size);
return size;
}
private int calculateDimensionY(Collection<V> roots) {
int size = 0;
for (V v : roots) {
int childrenNum = graph.getSuccessors(v).size();
if (childrenNum != 0) {
for (V element : graph.getSuccessors(v)) {
size += calculateDimensionY(element) + distY;
}
}
size = Math.max(0, size - distY);
basePositions.put(v, size);
}
return size;
}
}
Чтобы ответить на проблему (1): есть демо (L2RTreeLayoutDemo), которое делает это.
Чтобы ответить на задачу (2): измените интервал x и / или y в макете; это устанавливается в конструкторе.