cz.vutbr.fit.layout.segm.op.MultiLineOperator 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!
/**
* MultiLineOperator.java
*
* Created on 28. 2. 2015, 23:10:59 by burgetr
*/
package cz.vutbr.fit.layout.segm.op;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cz.vutbr.fit.layout.api.Parameter;
import cz.vutbr.fit.layout.impl.BaseOperator;
import cz.vutbr.fit.layout.impl.ParameterBoolean;
import cz.vutbr.fit.layout.impl.ParameterFloat;
import cz.vutbr.fit.layout.model.Area;
import cz.vutbr.fit.layout.model.AreaTopology;
import cz.vutbr.fit.layout.model.AreaTree;
import cz.vutbr.fit.layout.model.Rectangular;
import cz.vutbr.fit.layout.segm.AreaStyle;
import cz.vutbr.fit.layout.segm.TreeOp;
/**
* Detects sequences of aligned lines and joins them to a single area.
*
* @author burgetr
*/
public class MultiLineOperator extends BaseOperator
{
private static Logger log = LoggerFactory.getLogger(MultiLineOperator.class);
/** Should the lines have a consistent visual style? */
protected boolean useConsistentStyle;
/** The maximal distance of two areas allowed within a single line (in 'em' units) */
protected float maxLineEmSpace;
public MultiLineOperator()
{
useConsistentStyle = false;
maxLineEmSpace = 1.5f;
}
public MultiLineOperator(boolean useConsistentStyle, float maxLineEmSpace)
{
this.useConsistentStyle = useConsistentStyle;
this.maxLineEmSpace = maxLineEmSpace;
}
@Override
public String getId()
{
return "FitLayout.Segm.MultiLine";
}
@Override
public String getName()
{
return "Group aligned lines";
}
@Override
public String getDescription()
{
return "Detects sequences of aligned lines and joins them to a single area.";
}
@Override
public String getCategory()
{
return "Lines";
}
@Override
public List defineParams()
{
List ret = new ArrayList<>();
ret.add(new ParameterBoolean("useConsistentStyle"));
ret.add(new ParameterFloat("maxLineEmSpace"));
return ret;
}
public boolean getUseConsistentStyle()
{
return useConsistentStyle;
}
public void setUseConsistentStyle(boolean useConsistentStyle)
{
this.useConsistentStyle = useConsistentStyle;
}
public float getMaxLineEmSpace()
{
return maxLineEmSpace;
}
public void setMaxLineEmSpace(float maxLineEmSpace)
{
this.maxLineEmSpace = maxLineEmSpace;
}
//==============================================================================
@Override
public void apply(AreaTree atree)
{
recursiveJoinAreas(atree.getRoot());
}
@Override
public void apply(AreaTree atree, Area root)
{
recursiveJoinAreas(root);
}
//==============================================================================
/**
* Goes through all the areas in the tree and tries to join their sub-areas into single
* areas.
*/
protected void recursiveJoinAreas(Area root)
{
joinAreas(root);
for (int i = 0; i < root.getChildCount(); i++)
recursiveJoinAreas(root.getChildAt(i));
}
/**
* Goes through the grid of areas and joins the adjacent visual areas that are not
* separated by anything
*/
protected void joinAreas(Area a)
{
AreaTopology t = a.getTopology();
boolean change = true;
while (change)
{
change = false;
for (int i = 0; i < a.getChildCount(); i++)
{
Area node = a.getChildAt(i);
Rectangular pos = t.getPosition(node);
int nx1 = pos.getX1();
int nx2 = pos.getX2();
int ny2 = pos.getY2();
//try to expand down - find a neighbor
Area neigh = null;
int dist = 1;
while (neigh == null && ny2 + dist < t.getTopologyHeight())
{
//try to find some node below in the given distance
for (int x = nx1; neigh == null && x <= nx2; x++)
{
neigh = (Area) t.findAreaAt(x, ny2 + dist);
if (neigh != null) //something found
{
if ((!useConsistentStyle || AreaStyle.hasSameStyle(node, neigh))
&& neigh.getGridPosition().getX1() == nx1)
{
if (verticalJoin(a, node, neigh, true)) //try to join
{
node.updateTopologies();
change = true;
}
}
}
}
dist++;
}
if (change) break; //something changed, repeat
}
}
}
/**
* Joins two boxes vertically into one area if the node widths are equal or they
* can be aligned to a rectangle using free spaces.
* @param n1 left node to be aligned
* @param n2 right node to be aligned
* @param affect when set to true
, the two nodes are joined and n2 is removed from the tree.
* When set to false
, no changes are performed (only checking)
* @return true
when succeeded
*/
private boolean verticalJoin(Area parent, Area n1, Area n2, boolean affect)
{
//System.out.println("VJoin: " + n1.toString() + " + " + n2.toString());
//check the maximal distance between the nodes
int dist = Math.min(Math.abs(n2.getY1() - n1.getY2()), Math.abs(n1.getY1() - n2.getY2()));
if (dist > n1.getTextStyle().getFontSize() * maxLineEmSpace)
return false;
//check if there is no separating border or background
if (n1.hasBottomBorder() ||
n2.hasTopBorder() ||
!AreaStyle.hasEqualBackground(n1, n2))
return false; //separated, give up
//align the start
int sx1 = n1.getGridPosition().getX1();
int sx2 = n2.getGridPosition().getX1();
while (sx1 != sx2)
{
if (sx1 < sx2) //n1 starts earlier, try to expand n2 to the left
{
if (sx2 > 0 && canExpandX(parent, n2, sx2-1, n1))
sx2--;
else
return false; //cannot align - give up
}
else if (sx1 > sx2) //n2 starts earlier, try to expand n1 to the left
{
if (sx1 > 0 && canExpandX(parent, n1, sx1-1, n2))
sx1--;
else
return false; //cannot align - give up
}
}
//System.out.println("sy1="+sy1);
//align the end
int ex1 = n1.getGridPosition().getX2(); //last
int ex2 = n2.getGridPosition().getX2();
while (ex1 != ex2)
{
if (ex1 < ex2) //n1 ends earlier, try to expand n1 to the right
{
if (ex1 < parent.getTopology().getTopologyHeight()-1 && canExpandX(parent, n1, ex1+1, n2))
ex1++;
else
return false; //cannot align - give up
}
else if (ex1 > ex2) //n2 ends earlier, try to expand n2 to the right
{
if (ex2 < parent.getTopology().getTopologyHeight()-1 && canExpandX(parent, n2, ex2+1, n1))
ex2++;
else
return false; //cannot align - give up
}
}
//System.out.println("ey1="+ey1);
//align succeeded, join the areas
if (affect)
{
log.debug("VJoin: {} + {}", n1, n2);
Rectangular newpos = new Rectangular(sx1, n1.getGridPosition().getY1(),
ex1, n2.getGridPosition().getY2());
TreeOp.joinArea(n1, n2, newpos, true, false);
parent.removeChild(n2);
}
return true;
}
/**
* Checks if the area can be horizontally expanded to the given
* X coordinate, i.e. there is a free space in the space on this X coordinate
* for the whole height of the area.
* @param node the area node that should be expanded
* @param x the X coordinate to that the area should be expanded
* @param except an area that shouldn't be considered for conflicts (e.g. an overlaping area)
* @return true
if the area can be expanded
*/
private boolean canExpandX(Area parent, Area node, int x, Area except)
{
final AreaTopology t = parent.getTopology();
final Rectangular nodePos = t.getPosition(node);
for (int y = nodePos.getY1(); y < nodePos.getY1() + nodePos.getHeight(); y++)
{
Area cand = (Area) t.findAreaAt(x, y);
if (cand != null && cand != except)
return false; //something found - cannot expand
}
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy