Radial tech tree layout

This commit is contained in:
Anuken
2019-10-13 19:36:47 -04:00
parent adc7c2bdb6
commit 7eb3e5c0ea
5 changed files with 98 additions and 27 deletions

View File

@@ -23,7 +23,8 @@ import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.io.SaveIO.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*;
import io.anuke.mindustry.ui.TreeLayout.*;
import io.anuke.mindustry.ui.layout.*;
import io.anuke.mindustry.ui.layout.TreeLayout.*;
import static io.anuke.mindustry.Vars.*;
@@ -39,7 +40,7 @@ public class DeployDialog extends FloatingDialog{
ZoneNode root = new ZoneNode(Zones.groundZero, null);
TreeLayout layout = new TreeLayout();
BranchTreeLayout layout = new BranchTreeLayout();
layout.gapBetweenLevels = layout.gapBetweenNodes = Scl.scl(60f);
layout.gapBetweenNodes = Scl.scl(120f);
layout.layout(root);

View File

@@ -22,7 +22,8 @@ import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*;
import io.anuke.mindustry.ui.TreeLayout.*;
import io.anuke.mindustry.ui.layout.*;
import io.anuke.mindustry.ui.layout.TreeLayout.*;
import static io.anuke.mindustry.Vars.*;
@@ -101,13 +102,10 @@ public class TechTreeDialog extends FloatingDialog{
}
void treeLayout(){
TreeLayout layout = new TreeLayout();
layout.gapBetweenLevels = Scl.scl(60f);
layout.gapBetweenNodes = Scl.scl(40f);
RadialTreeLayout layout = new RadialTreeLayout();
LayoutNode node = new LayoutNode(root, null);
layout.layout(node);
bounds.set(layout.getBounds());
bounds.y += nodeSize*1.5f;
//bounds.y += nodeSize*1.5f;
copyInfo(node);
}
@@ -156,7 +154,7 @@ public class TechTreeDialog extends FloatingDialog{
this.parent = parent;
this.width = this.height = nodeSize;
if(node.children != null){
children = Array.with(node.children).select(n -> n.visible).map(t -> new LayoutNode(t, this)).toArray(LayoutNode.class);
children = Array.with(node.children).map(t -> new LayoutNode(t, this)).toArray(LayoutNode.class);
}
}
}

View File

@@ -1,4 +1,4 @@
package io.anuke.mindustry.ui;
package io.anuke.mindustry.ui.layout;
import io.anuke.arc.collection.*;
import io.anuke.arc.math.geom.*;
@@ -6,7 +6,7 @@ import io.anuke.arc.math.geom.*;
/**
* Algorithm taken from <a href="https://github.com/abego/treelayout">TreeLayout</a>.
*/
public class TreeLayout{
public class BranchTreeLayout implements TreeLayout{
public TreeLocation rootLocation = TreeLocation.top;
public TreeAlignment alignment = TreeAlignment.awayFromRoot;
public float gapBetweenLevels = 10;
@@ -18,6 +18,7 @@ public class TreeLayout{
private float boundsTop = Float.MAX_VALUE;
private float boundsBottom = Float.MIN_VALUE;
@Override
public void layout(TreeNode root){
firstWalk(root, null);
calcSizeOfLevels(root, 0);
@@ -288,20 +289,4 @@ public class TreeLayout{
public enum TreeAlignment{
center, towardsRoot, awayFromRoot
}
public static class TreeNode<T extends TreeNode>{
public float width, height, x, y;
//should be initialized by user
public T[] children;
public T parent;
private float mode, prelim, change, shift;
private int number = -1;
private TreeNode thread, ancestor;
boolean isLeaf(){
return children == null || children.length == 0;
}
}
}

View File

@@ -0,0 +1,65 @@
package io.anuke.mindustry.ui.layout;
import io.anuke.arc.collection.*;
import io.anuke.arc.math.*;
public class RadialTreeLayout implements TreeLayout{
public float startRadius, delta;
@Override
public void layout(TreeNode root){
startRadius = root.height * 2.4f;
delta = root.height * 2.4f;
bfs(root, true);
radialize(root, 0, 360);
}
void radialize(TreeNode root, float from, float to){
int depthOfVertex = root.number;
float theta = from;
float radius = startRadius + (delta * depthOfVertex);
int leavesNumber = bfs(root, false);
for(TreeNode child : root.children){
int lambda = bfs(child, false);
float mi = theta + ((float)lambda / leavesNumber * (to - from));
float x = radius * Mathf.cos((theta + mi) / 2f * Mathf.degRad);
float y = radius * Mathf.sin((theta + mi) / 2f * Mathf.degRad);
child.x = x;
child.y = y;
if(child.children.length > 0){
radialize(child, theta, mi);
}
theta = mi;
}
}
int bfs(TreeNode node, boolean assign){
if(assign) node.number = 0;
ObjectSet<TreeNode> visited = new ObjectSet<>();
Queue<TreeNode> queue = new Queue<>();
int leaves = 0;
visited.add(node);
queue.addFirst(node);
while(!queue.isEmpty()){
TreeNode current = queue.removeFirst();
if(current.children.length == 0) leaves++;
for(TreeNode child : current.children){
if(assign) child.number = current.number + 1;
if(!visited.contains(child)){
visited.add(child);
queue.addLast(child);
}
}
}
return leaves;
}
}

View File

@@ -0,0 +1,22 @@
package io.anuke.mindustry.ui.layout;
public interface TreeLayout{
void layout(TreeNode root);
class TreeNode<T extends TreeNode>{
public float width, height, x, y;
//should be initialized by user
public T[] children;
public T parent;
//internal stuff
public float mode, prelim, change, shift;
public int number = -1, ancestors;
public TreeNode thread, ancestor;
public boolean isLeaf(){
return children == null || children.length == 0;
}
}
}