/*
* Copyright (C) 2009 Quadduc <quadduc@gmail.com>
*
* This file is part of LateralGM.
* LateralGM is free software and comes with ABSOLUTELY NO WARRANTY.
* See LICENSE for details.
*/
package org.lateralgm.ui.swing.visuals;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.lateralgm.util.BinPlane;
import org.lateralgm.util.BinPlane.Candidate;
import org.lateralgm.util.BinPlane.CandidateBin;
import org.lateralgm.util.BinPlane.Edge;
public class BinVisual extends AbstractVisual implements VisualContainer,BoundedVisual
{
private static final Rectangle ZERO_RECTANGLE = new Rectangle();
private final BinPlane binPlane;
private Visual vLeft, vRight, vTop, vBottom;
private final Rectangle boxBounds = new Rectangle();
private Map<Visual,Candidate> candidates;
public BinVisual(VisualContainer c, int s, int x, int y)
{
super(c);
binPlane = new BinPlane(s,x,y);
}
@Override
public void repaint(Rectangle r)
{
super.repaint(r);
}
void add(Visual v, Rectangle b, int d)
{
BinPlane.Candidate c = binPlane.new Candidate();
c.data = v;
c.setDepth(d);
if (candidates == null) candidates = new HashMap<Visual,Candidate>();
candidates.put(v,c);
if (b != null) setBounds(c,b);
}
boolean remove(Visual v)
{
if (candidates == null) return false;
Candidate c = candidates.remove(v);
if (c == null) return false;
repaint(c.getBounds(null));
c.remove();
Rectangle obb = boxBounds.getBounds();
if (v == vLeft) vLeft = null;
if (v == vRight) vRight = null;
if (v == vTop) vTop = null;
if (v == vBottom) vBottom = null;
fixBounds();
if (!obb.equals(boxBounds)) parent.updateBounds();
return true;
}
public void setDepth(Visual v, int d)
{
setDepth(v,d,false);
}
public void setDepth(Visual v, int d, boolean selected)
{
Candidate c = getCandidate(v);
if (c == null) return;
c.setDepth(d,selected);
Rectangle ob = c.getBounds(null);
if (ob != null && !ob.isEmpty()) repaint(ob);
}
public void setBounds(Visual v, Rectangle b)
{
setBounds(getCandidate(v),b);
}
private Candidate getCandidate(Visual v)
{
return candidates == null ? null : candidates.get(v);
}
private void setBounds(Candidate c, Rectangle b)
{
Rectangle ob = c.getBounds(null);
if (!ob.isEmpty()) repaint(ob);
Visual v = (Visual) c.data;
c.setBounds(b);
Rectangle obb = boxBounds.getBounds();
if (b.x <= boxBounds.x)
{
vLeft = v;
boxBounds.width += boxBounds.x - b.x;
boxBounds.x = b.x;
}
else if (v == vLeft) vLeft = null;
int w = b.x + b.width - boxBounds.x;
if (w >= boxBounds.width)
{
vRight = v;
boxBounds.width = w;
}
else if (v == vRight) vRight = null;
if (b.y <= boxBounds.y)
{
vTop = v;
boxBounds.height += boxBounds.y - b.y;
boxBounds.y = b.y;
}
else if (v == vTop) vTop = null;
int h = b.y + b.height - boxBounds.y;
if (h >= boxBounds.height)
{
vBottom = v;
boxBounds.height = h;
}
else if (v == vBottom) vBottom = null;
fixBounds();
if (!obb.equals(boxBounds)) parent.updateBounds();
repaint(b);
}
private void fixBounds()
{
if (vLeft == null)
{
Candidate ec = binPlane.getEdgeCandidate(Edge.LEFT);
int l = ec == null ? 0 : ec.getBounds(null).x;
if (ec != null) vLeft = (VisualBox) ec.data;
boxBounds.width += boxBounds.x - l;
boxBounds.x = l;
}
if (vRight == null)
{
Candidate ec = binPlane.getEdgeCandidate(Edge.RIGHT);
Rectangle cb = ec == null ? ZERO_RECTANGLE : ec.getBounds(null);
if (ec != null) vRight = (VisualBox) ec.data;
boxBounds.width = cb.x + cb.width - boxBounds.x;
}
if (vTop == null)
{
Candidate ec = binPlane.getEdgeCandidate(Edge.TOP);
int t = ec == null ? 0 : ec.getBounds(null).y;
if (ec != null) vTop = (VisualBox) ec.data;
boxBounds.height += boxBounds.y - t;
boxBounds.y = t;
}
if (vBottom == null)
{
Candidate ec = binPlane.getEdgeCandidate(Edge.BOTTOM);
Rectangle cb = ec == null ? ZERO_RECTANGLE : ec.getBounds(null);
if (ec != null) vBottom = (VisualBox) ec.data;
boxBounds.height = cb.y + cb.height - boxBounds.y;
}
}
public Iterator<Visual> intersect(Rectangle r)
{
return intersect(r,Visual.class);
}
public <V extends Visual>Iterator<V> intersect(Rectangle r, Class<V> v)
{
return new BinPlane.CandidateDataIterator<V>(binPlane.intersect(r,true),v);
}
public <V extends Visual>Iterator<V> intersect(Rectangle r, Class<V> v, int depth)
{
return new BinPlane.CandidateDepthDataIterator<V>(binPlane.intersect(r,true),v,depth);
}
public void paint(Graphics g)
{
Rectangle clip = g.getClipBounds();
Iterator<CandidateBin> cbi = clip == null ? binPlane.all(false)
: binPlane.intersect(clip,false);
Rectangle b = null;
while (cbi.hasNext())
{
CandidateBin cb = cbi.next();
g.clipRect(cb.x,cb.y,cb.w,cb.h);
while (cb.iterator.hasNext())
{
Candidate c = cb.iterator.next();
Visual v = (Visual) c.data;
b = c.getBounds(b);
Graphics g2 = g.create(b.x,b.y,b.width,b.height);
v.paint(g2);
g2.dispose();
}
g.setClip(clip);
}
}
public void updateBounds()
{
//Unused
}
public void extendBounds(Rectangle b)
{
b.add(boxBounds);
}
}