package com.github.lindenb.jvarkit.tools.treepack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.awt.geom.Rectangle2D;
public class TreePacker
{
private enum Orientation { VERTICAL, HORIZONTAL};
private enum Direction { ASCENDING, DESCENDING}
private Comparator<TreePack> comparator=new Comparator<TreePack>()
{
public int compare(TreePack pack1, TreePack pack2)
{
double w1=pack1.getWeight();
double w2=pack2.getWeight();
if(w1<w2) return 1;
if(w1>w2) return -1;
return 0;
}
};
public void layout(List<TreePack> items,final Rectangle2D bounds)
{
layout(sortDescending(items),0,items.size()-1,bounds);
}
private double sum(List<TreePack> items, int start, int end)
{
double sum=0;
while(start<=end)//yes <=
{
sum+=items.get(start++).getWeight();
}
return sum;
}
private List<TreePack> sortDescending(List<TreePack> items)
{
List<TreePack> L=new ArrayList<TreePack>(items);
Collections.sort(L,comparator);
return L;
}
private void layout(List<TreePack> items, int start, int end,
final Rectangle2D bounds)
{
if (start>end) return;
if (end-start<2)
{
layoutBest(items,start,end,bounds);
return;
}
double x=bounds.getX(), y=bounds.getY(), w=bounds.getWidth(), h=bounds.getHeight();
double total=sum(items, start, end);
int mid=start;
double a=items.get(start).getWeight()/total;
double b=a;
if (w<h)
{
// height/width
while (mid<=end)
{
double aspect=normAspect(h,w,a,b);
double q=items.get(mid).getWeight()/total;
if (normAspect(h,w,a,b+q)>aspect) break;
mid++;
b+=q;
}
layoutBest(items,start,mid,new Rectangle2D.Double(x,y,w,h*b));
layout(items,mid+1,end,new Rectangle2D.Double(x,y+h*b,w,h*(1-b)));
}
else
{
// width/height
while (mid<=end)
{
double aspect=normAspect(w,h,a,b);
double q=items.get(mid).getWeight()/total;
if (normAspect(w,h,a,b+q)>aspect) break;
mid++;
b+=q;
}
layoutBest(items,start,mid,new Rectangle2D.Double(x,y,w*b,h));
layout(items,mid+1,end,new Rectangle2D.Double(x+w*b,y,w*(1-b),h));
}
}
private double aspect(double big, double small, double a, double b)
{
return (big*b)/(small*a/b);
}
private double normAspect(double big, double small, double a, double b)
{
double x=aspect(big,small,a,b);
if (x<1) return 1/x;
return x;
}
private void layoutBest(List<TreePack> items, int start, int end, final Rectangle2D bounds)
{
sliceLayout(
items,start,end,bounds,
bounds.getWidth()>bounds.getHeight() ? Orientation.HORIZONTAL : Orientation.VERTICAL,
Direction.ASCENDING);
}
private void sliceLayout(List<TreePack> items, int start, int end, final Rectangle2D bounds, Orientation orientation, Direction order)
{
double total=sum(items, start, end);
double a=0;
boolean vertical=orientation==Orientation.VERTICAL;
for (int i=start; i<=end; i++)
{
Rectangle2D.Double r=new Rectangle2D.Double();
double b=items.get(i).getWeight()/total;
if (vertical)
{
r.x=bounds.getX();
r.width=bounds.getWidth();
if (order==Direction.ASCENDING)
r.y=bounds.getY()+bounds.getHeight()*a;
else
r.y=bounds.getY()+bounds.getHeight()*(1-a-b);
r.height=bounds.getHeight()*b;
}
else
{
if (order==Direction.ASCENDING)
r.x=bounds.getX()+bounds.getWidth()*a;
else
r.x=bounds.getX()+bounds.getWidth()*(1-a-b);
r.width=bounds.getWidth()*b;
r.y=bounds.getY();
r.height=bounds.getHeight();
}
items.get(i).setBounds(r);
a+=b;
}
}
}