cz.vutbr.fit.layout.segm.op.GroupAnalyzerByStyles Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fitlayout-segm-base Show documentation
Show all versions of fitlayout-segm-base Show documentation
FitLayout - Basic segmentation algorithm and area tree operators.
The newest version!
/**
* GroupAnalyzerByStyles.java
*
* Created on 23.1.2007, 15:49:52 by burgetr
*/
package cz.vutbr.fit.layout.segm.op;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import cz.vutbr.fit.layout.model.Area;
import cz.vutbr.fit.layout.model.AreaTopology;
import cz.vutbr.fit.layout.model.Rectangular;
import cz.vutbr.fit.layout.segm.AreaStyle;
import cz.vutbr.fit.layout.segm.Separators;
/**
* This group analyzer tries to expand the selected box to all directions stopping on
* whitespace separators. Only the areas with the same font properties are connected.
*
* @author burgetr
*/
public class GroupAnalyzerByStyles extends GroupAnalyzer
{
private static final short DIR_DOWN = 0;
private static final short DIR_UP = 1;
private static final short DIR_RIGHT = 2;
private static final short DIR_LEFT = 3;
private static final short REQ_BOTH = 0;
private static final short REQ_HORIZONTAL = 1;
private static final short REQ_VERTICAL = 2;
private SeparatorSet seps = null;
/**
* The maximal level of areas we're connecting. The levels used by this analyzer are:
* 0=basic areas formed by boxes
* 1=artificial areas
*/
private int maxlevel;
/** Compare styles while expanding? */
private boolean matchstyles;
//======================================================================================
/**
* Creates a new area analyzer.
* @param parent the area processed by the analyzer
* @param maxlevel maximal level of the areas that can be joined into a super area
* @param matchstyles defines whether to compare the element styles while expanding
*/
public GroupAnalyzerByStyles(Area parent, int maxlevel, boolean matchstyles)
{
super(parent);
this.maxlevel = maxlevel;
this.matchstyles = matchstyles;
}
@Override
public Area findSuperArea(Area sub, List selected)
{
//parent.createSeparators();
seps = Separators.getSeparatorsForArea(parent);
AreaTopology t = parent.getTopology();
//starting grid position
Rectangular gp = new Rectangular(sub.getGridPosition());
//System.out.println("GSS************* Start: " + gp + " - " + sub);
//try to expand to the whole grid
Rectangular limit = new Rectangular(0, 0, getTopology().getTopologyWidth()-1, getTopology().getTopologyHeight()-1);
expandToLimit(sub, gp, limit, sub, true, true, DIR_RIGHT, REQ_BOTH);
//select areas inside of the area found
selected.clear();
Rectangular mingp = null;
for (int i = 0; i < parent.getChildCount(); i++)
{
final Area chld = parent.getChildAt(i);
final Rectangular cgp = t.getPosition(chld);
if (gp.encloses(cgp))
{
selected.add(chld);
if (mingp == null)
mingp = new Rectangular(cgp);
else
mingp.expandToEnclose(cgp);
}
}
//create the new area
Rectangular abspos = getTopology().toPixelPosition(mingp);
abspos.move(parent.getX1(), parent.getY1());
Area area = sub.getAreaTree().createArea(abspos);
//area.setBorders(true, true, true, true);
area.setLevel(1);
//if (!mingp.equals(sub.getGridPosition()))
// System.out.println("Found area: " + area + " : " + mingp);
area.setGridPosition(mingp);
return area;
}
/**
* Tries to expand the area in the grid to a greater rectangle in the given limits.
* @param sub the area to be expanded
* @param gp the initial grid position of the area. This structure is modified by the expansion.
* @param limit the maximal size to expand to
* @param hsep stop on horizontal separators
* @param vsep stop on vertical separators
* @param prefDir preferred expansion direction (use DIR_DOWN for vertical
* or DIR_RIGHT for horizontal)
* @param required indicates whether it is required to reach the specified limit horizontaly
* vertically or in both directions (use REQ_* constants)
*/
private void expandToLimit(Area sub, Rectangular gp, Rectangular limit, Area template,
boolean hsep, boolean vsep,
short prefDir, short required)
{
//System.out.println();
//System.out.println(" Expand " + sub + " DIR=" + prefDir + " sep=" + hsep + ":" + vsep);
//debugColor = new java.awt.Color(debugColor.getBlue(), debugColor.getRed(), debugColor.getGreen());*/
//hsep = true;
//vsep = true;
if (getTopology().getTopologyWidth() > 0 && getTopology().getTopologyHeight() > 0 && !sub.isBackgroundSeparated())
{
int dir = prefDir;
int attempts = 0;
while (attempts < 4 && !limitReached(gp, limit, required))
{
boolean change = false;
int newx, newy;
//System.out.println(dir + " - " +gp);
switch (dir)
{
case DIR_DOWN:
//expand down
if (gp.getY2() < limit.getY2() &&
(!hsep || !separatorDown(sub.getGridPosition().replaceY(gp)))) //look for the separator under the current expanded bounds
{
newy = expandVertically(gp, limit, template, true, hsep);
if (newy > gp.getY2())
{
gp.setY2(newy);
change = true;
}
}
break;
case DIR_RIGHT:
//expand right
if (gp.getX2() < limit.getX2() &&
(!vsep || !separatorRight(sub.getGridPosition().replaceX(gp))))
{
newx = expandHorizontally(gp, limit, template, true, vsep);
if (newx > gp.getX2())
{
gp.setX2(newx);
change = true;
}
}
break;
case DIR_UP:
//expand up
if (gp.getY1() > limit.getY1() &&
(!hsep || !separatorUp(sub.getGridPosition().replaceY(gp))))
{
newy = expandVertically(gp, limit, template, false, hsep);
if (newy < gp.getY1())
{
gp.setY1(newy);
change = true;
}
}
break;
case DIR_LEFT:
//expand left
if (gp.getX1() > limit.getX1() &&
(!vsep || !separatorLeft(sub.getGridPosition().replaceX(gp))))
{
newx = expandHorizontally(gp, limit, template, false, vsep);
if (newx < gp.getX1())
{
gp.setX1(newx);
change = true;
}
}
break;
}
if (!change) //not succeeded in this direction
{
dir++; //another direction
if (dir >= 4) dir = 0;
attempts++;
}
else //succeeded - keep the direction
{
attempts = 0;
}
/*if (Config.DEBUG_AREAS)
{
dispArea(gp);
wait(Config.DEBUG_DELAY);
}*/
}
}
}
/**
* Checks if the grid bounds have reached a specified limit in the specified direction.
* @param gp the bounds to check
* @param limit the limit to be reached
* @param required the required direction (use the REQ_* constants)
* @return true if the limit has been reached or exceeded
*/
private boolean limitReached(Rectangular gp, Rectangular limit, short required)
{
switch (required)
{
case REQ_HORIZONTAL:
return gp.getX1() <= limit.getX1() && gp.getX2() >= limit.getX2();
case REQ_VERTICAL:
return gp.getY1() <= limit.getY1() && gp.getY2() >= limit.getY2();
case REQ_BOTH:
return gp.getX1() <= limit.getX1() && gp.getX2() >= limit.getX2()
&& gp.getY1() <= limit.getY1() && gp.getY2() >= limit.getY2();
}
return false;
}
/**
* Try to expand the area vertically by a smallest step possible
* @param gp the area position in the grid
* @param limit the maximal expansion limit
* @param down true
meand expand down, false means expand up
* @param sep stop on separators
* @return the new vertical end of the area.
*/
private int expandVertically(Rectangular gp, Rectangular limit, Area template, boolean down, boolean sep)
{
//System.out.println("exp: " + gp + (down?" _":" ^") + " " + sep);
int na = down ? gp.getY2() : gp.getY1(); //what to return when it's not possible to expand
int targety = down ? (gp.getY2() + 1) : (gp.getY1() - 1);
//find candidate boxes
List cands = new ArrayList<>();
int x = gp.getX1();
while (x <= gp.getX2()) //scan everything at the target position
{
Area cand = (Area) getTopology().findAreaAt(x, targety);
//ignore candidates that intersect with our area (could leat to an infinite loop)
if (cand == null || cand.getGridPosition().intersects(gp))
x++;
else
{
cands.add(cand);
x = cand.getGridPosition().getX2() + 1;
}
}
//everything below/above empty, can safely expand
if (cands.size() == 0)
return targety;
//try to align the candidate boxes
for (Iterator it = cands.iterator(); it.hasNext(); )
{
Area cand = it.next();
if (sep &&
((down && separatorUp(cand.getGridPosition())) ||
(!down && separatorDown(cand.getGridPosition()))))
return na; //separated, cannot expand
else if ((matchstyles && !AreaStyle.hasSameStyle(template, cand)) || cand.getLevel() > maxlevel)
return na; //not the same style or level
else
{
Rectangular cgp = new Rectangular(cand.getGridPosition());
if (cgp.getX1() == gp.getX1() && cgp.getX2() == gp.getX2())
return targety; //simple match
else if (cgp.getX1() < gp.getX1() || cgp.getX2() > gp.getX2())
return na; //area overflows, cannot expand
else //candidate is smaller, try to expand align to our width
{
if (down)
{
Rectangular newlimit = new Rectangular(gp.getX1(), targety, gp.getX2(), limit.getY2());
expandToLimit(cand, cgp, newlimit, template, true, false, DIR_RIGHT, REQ_HORIZONTAL);
if (cgp.getX1() == gp.getX1() && cgp.getX2() == gp.getX2())
return cgp.getY2(); //successfully aligned
}
else
{
Rectangular newlimit = new Rectangular(gp.getX1(), limit.getY1(), gp.getX2(), targety);
expandToLimit(cand, cgp, newlimit, template, true, false, DIR_RIGHT, REQ_HORIZONTAL);
if (cgp.getX1() == gp.getX1() && cgp.getX2() == gp.getX2())
return cgp.getY1(); //successfully aligned
}
}
}
}
return na; //some candidates but none usable
}
/**
* Try to expand the area horizontally by a smallest step possible
* @param gp the area position in the grid
* @param limit the maximal expansion limit
* @param right true
meand expand right, false means expand left
* @param sep stop on separators
* @return the new vertical end of the area.
*/
private int expandHorizontally(Rectangular gp, Rectangular limit, Area template, boolean right, boolean sep)
{
//System.out.println("exp: " + gp + (right?" ->":" <-") + " " + sep);
int na = right ? gp.getX2() : gp.getX1(); //what to return when it's not possible to expand
int targetx = right ? (gp.getX2() + 1) : (gp.getX1() - 1);
//find candidate boxes
boolean found = false;
int y = gp.getY1();
while (y <= gp.getY2()) //scan everything at the target position
{
Area cand = (Area) getTopology().findAreaAt(targetx, y);
//ignore candidates that intersect with our area (could leat to an infinite loop)
if (cand != null && !cand.getGridPosition().intersects(gp))
{
found = true;
if (sep &&
((right && separatorLeft(cand.getGridPosition())) ||
(!right && separatorRight(cand.getGridPosition()))))
return na; //separated, cannot expand
else if ((matchstyles && !AreaStyle.hasSameStyle(template, cand)) || cand.getLevel() > maxlevel)
return na; //not the same style or level
else
{
Rectangular cgp = new Rectangular(cand.getGridPosition());
if (cgp.getY1() == gp.getY1() && cgp.getY2() == gp.getY2())
return targetx; //simple match
else if (cgp.getY1() < gp.getY1() || cgp.getY2() > gp.getY2())
return na; //area overflows, cannot expand
else //candidate is smaller, try to expand align to our width
{
if (right)
{
Rectangular newlimit = new Rectangular(targetx, gp.getY1(), limit.getX2(), gp.getY2());
expandToLimit(cand, cgp, newlimit, template, false, true, DIR_DOWN, REQ_VERTICAL);
if (cgp.getY1() == gp.getY1() && cgp.getY2() == gp.getY2())
return cgp.getX2(); //successfully aligned
}
else
{
Rectangular newlimit = new Rectangular(limit.getX1(), gp.getY1(), targetx, gp.getY2());
expandToLimit(cand, cgp, newlimit, template, false, true, DIR_DOWN, REQ_VERTICAL);
if (cgp.getY1() == gp.getY1() && cgp.getY2() == gp.getY2())
return cgp.getX1(); //successfully aligned
}
}
}
//skip the candidate
y += cand.getGridPosition().getY2() + 1;
}
else
y++;
}
if (!found)
return targetx; //everything below/above empty, can safely expand
else
return na; //some candidates but none usable
}
//====================================================================================
private boolean separatorDown(Rectangular pos)
{
if (pos.getY2() < getTopology().getTopologyHeight()-1)
{
Rectangular spos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX1(), pos.getY2(), pos.getX2(), pos.getY2()));
Rectangular epos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX1(), pos.getY2() + 1, pos.getX2(), pos.getY2() + 1));
return seps.isSeparatorAt(spos.midX(), spos.getY2()) ||
seps.isSeparatorAt(epos.midX(), epos.getY1());
}
else
return true;
}
private boolean separatorUp(Rectangular pos)
{
if (pos.getY1() > 0)
{
Rectangular spos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX1(), pos.getY1(), pos.getX2(), pos.getY1()));
Rectangular epos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX1(), pos.getY1() - 1, pos.getX2(), pos.getY1() - 1));
return seps.isSeparatorAt(spos.midX(), spos.getY1()) ||
seps.isSeparatorAt(epos.midX(), epos.getY2());
}
else
return true;
}
private boolean separatorLeft(Rectangular pos)
{
if (pos.getX1() > 0)
{
Rectangular spos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX1(), pos.getY1(), pos.getX1(), pos.getY2()));
Rectangular epos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX1() - 1, pos.getY1(), pos.getX1() - 1, pos.getY2()));
return seps.isSeparatorAt(spos.getX1(), spos.midY()) ||
seps.isSeparatorAt(epos.getX2(), epos.midY());
}
else
return true;
}
private boolean separatorRight(Rectangular pos)
{
if (pos.getX2() < getTopology().getTopologyWidth()-1)
{
Rectangular spos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX2(), pos.getY1(), pos.getX2(), pos.getY2()));
Rectangular epos = getTopology().toPixelPositionAbsolute(new Rectangular(pos.getX2() + 1, pos.getY1(), pos.getX2() + 1, pos.getY2()));
return seps.isSeparatorAt(spos.getX2(), spos.midY()) ||
seps.isSeparatorAt(epos.getX1(), epos.midY());
}
else
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy