All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cz.vutbr.fit.layout.segm.op.SeparatorSet Maven / Gradle / Ivy

The newest version!
/**
 * 
 */
package cz.vutbr.fit.layout.segm.op;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import cz.vutbr.fit.layout.model.Area;
import cz.vutbr.fit.layout.model.Box;

/**
 * A generic set of horizontal and vertical separators for a page.
 * @author radek
 */
public abstract class SeparatorSet
{
    /** The minimal height of a horizontal separator in "em" units */
    protected static final double HSEP_MIN_HEIGHT = 0.1; 
    
    /** The minimal width of a vertical separator in "em" units */
    protected static final double VSEP_MIN_WIDTH = 0.1;
    
    /** The minimal width/height ratio of the separator */
    protected static final double SEP_MIN_RATIO = 1;
    
    /** The width of the 'artificial' separators created by background colors */
    protected static final int ART_SEP_WIDTH = 1;
    
	/** The root of the area tree that will be processed */
	protected Area root;
	
	/** List of horizontal separators */
	protected Vector hsep;
	
	/** List of vertical separators */
	protected Vector vsep;

    /** List of separators comming from the box analysis */
    protected Vector bsep;
	
	/**
	 * Creates a new separator set with one horizontal and one vertical separator.
	 */
	public SeparatorSet(Area root)
	{
        this.root = root;
        init(null);
	}

    /**
     * Creates a new separator set with one horizontal and one vertical separator.
     */
    public SeparatorSet(Area root, Area filter)
    {
        this.root = root;
        init(filter);
    }
    
    private void init(Area filter)
    {
        findAreaSeparators(root);
        findSeparators(root, filter);
    }
    
    //=====================================================================================
    
    public Vector getHorizontal()
    {
        return hsep;
    }
    
    public Vector getVertical()
    {
        return vsep;
    }
    
    public Vector getBoxsep()
    {
        return bsep;
    }
    
    /**
     * Obtains the most important (with the greatest weight) separator from all the separators.
     * The separators must be sorted before this metod is called.
     * @return The selected separator or null when there are no separators
     */
    public Separator getMostImportantSeparator()
    {
        Separator sep = null;
        if (!hsep.isEmpty())
            sep = hsep.firstElement();
        if (!vsep.isEmpty() && (sep == null || vsep.firstElement().getWeight() >= sep.getWeight()))
            sep = vsep.firstElement();
        if (!bsep.isEmpty() && (sep == null || bsep.firstElement().getWeight() >= sep.getWeight()))
            sep = bsep.firstElement();
        return sep;
    }
    
    //=====================================================================================
    
    /**
     * Computes the minimal height of a horizontal separator that is accepted with this separator set.
     * Usually, it depends on the average font size of the corresponding visual area.
     * @return the minimal height in pixels
     */
    public int getMinHSepHeight()
    {
        return (int) (root.getTextStyle().getFontSize() * HSEP_MIN_HEIGHT);
    }
    
    /**
     * Computes the minimal width of a vertical separator that is accepted with this separator set.
     * Usually, it depends on the average font size of the corresponding visual area.
     * @return the minimal width in pixels
     */
    public int getMinVSepWidth()
    {
        return (int) (root.getTextStyle().getFontSize() * VSEP_MIN_WIDTH);
    }
    
    //=====================================================================================
    
    /**
     * Checks if a point is covered by a separator.
     * @param x the point x coordinate
     * @param y the point y coordinate
     * @return true if any of the separators in this set covers the specified point
     */
    public boolean isSeparatorAt(int x, int y)
    {
        return containsSeparatorAt(x, y, bsep) ||
               containsSeparatorAt(x, y, hsep) ||
               containsSeparatorAt(x, y, vsep);
    }
    
    private boolean containsSeparatorAt(int x, int y, Vector col)
    {
        for (Iterator it = col.iterator(); it.hasNext();)
        {
        	Separator sep = it.next();
            if (sep.contains(x, y))
                return true;
        }
        return false;
    }
    
    //=====================================================================================
    
    
    /**
     * Finds the horizontal and vertical list of separators
     * @param area the root area
     * @param filter if not null, only the sub areas enclosed in the filter area
     * 	are considered
     */
    protected abstract void findSeparators(Area area, Area filter);
    
    /**
     * Applies various filters on the current separator sets in order to remove irrelevant separators or adjust the sizes.
     * This is calle automatically after each recursive iteration.
     */
    protected void applyRegularFilters()
    {
        //this is the default implementation - we filter the separators by their widths and we process the intersections somehow
        filterSeparators();
        processIntersections();
    }
    
    /**
     * Applies various filters on the current separator sets in order to remove irrelevant separators or adjust the sizes.
     * This must be called manually after the final results are obtained.
     */
    public void applyFinalFilters()
    {
        //this is the default implementation - we filter the separators by their widths and we process the intersections somehow
        filterMarginalSeparators();
        filterSeparators();
        processIntersections();
        sortSeparators();
    }
    
    //=====================================================================================

    /**
     * Removes the separators that are placed on the area borders.
     */
    protected void filterMarginalSeparators()
    {
        for (Iterator it = hsep.iterator(); it.hasNext();)
        {
            Separator sep = it.next();
            if (sep.getY1() == root.getY1() || sep.getY2() == root.getY2())
                it.remove();
        }
        for (Iterator it = vsep.iterator(); it.hasNext();)
        {
            Separator sep = it.next();
            if (sep.getX1() == root.getX1() || sep.getX2() == root.getX2())
                it.remove();
        }
    }
    
    /**
     * Removes all the separators where the weight is lower than the specified threshold.
     */
    protected void filterSeparators()
    {
        /*int hthreshold = (int) (root.getArea().getDeclaredFontSize() * HSEP_MIN_HEIGHT);
        int vthreshold = (int) (root.getArea().getDeclaredFontSize() * VSEP_MIN_WIDTH);*/
        int hthreshold = getMinHSepHeight();
        int vthreshold = getMinVSepWidth();
        
        for (Iterator it = hsep.iterator(); it.hasNext();)
        {
            Separator sep = it.next();
            //Adaptive height threshold: use the font size of the box above the separator for determining the em size for the threshold  
            Area above = findContentAbove(root, sep);
            if (above != null)
                hthreshold = (int) (above.getTextStyle().getFontSize() * HSEP_MIN_HEIGHT);
            else
                hthreshold = (int) (root.getTextStyle().getFontSize() * HSEP_MIN_HEIGHT);
            //System.out.println("For: " + sep + " limit " + hthreshold + " area " + above);
            
            if (sep.getWeight() < hthreshold)
                it.remove();
                //System.out.println("removed");
            else if (sep.getWidth() / (double) sep.getHeight() < SEP_MIN_RATIO)
                it.remove();
        }
        for (Iterator it = vsep.iterator(); it.hasNext();)
        {
            Separator sep = it.next();
            if (sep.getWeight() < vthreshold)
                it.remove();
            else if (sep.getHeight() / (double) sep.getWidth() < SEP_MIN_RATIO)
                it.remove();
        }
    }
    
    /**
     * Processes the separators so that they do not intersect. 
     */
    protected void processIntersections()
    {
        //processIntersectionsRemoveHorizontal();
    }
    
    /**
     * Processes the separators so that they do not intersect.
     * The vertical separators are left untouched, the horizontal separators are
     * split by the vertical ones when necessary.
     */
    protected void processIntersectionsSplitHorizontal()
    {
        boolean change;
        do
        {
            Vector newsep = new Vector(hsep.size());
            change = false;
            for (Separator hs : hsep)
            {
            	boolean split = false;
                for (Separator vs : vsep)
                {
                    if (hs.intersects(vs))
                    {
                        Separator nhs = hs.hsplit(vs);
                        newsep.add(hs);
                        if (nhs != null)
                            newsep.add(nhs);
                        split = true;
                        change = true;
                        break; //do not try other vertical seps
                    }
                }
                if (!split)
                	newsep.add(hs);
            }
            hsep = newsep;
        } while (change);
    }
    
    /**
     * Processes the separators so that they do not intersect.
     * The vertical separators are left untouched, the horizontal separators are
     * removed when they intersect with a vertical one.
     */
    protected void processIntersectionsRemoveHorizontal()
    {
        for (Iterator hit = hsep.iterator(); hit.hasNext(); )
        {
        	Separator hs = hit.next();
            for (Separator vs : vsep)
            {
                if (hs.intersects(vs))
                {
                	hit.remove();
                	break;
                }
            }
        }
    }
    
    private void sortSeparators()
    {
        Collections.sort(hsep);
        Collections.sort(vsep);
        Collections.sort(bsep);
    }
    
    //=====================================================================================

    /**
     * Creates a list of separators that are implemented as visual area borders.
     */
    private void findAreaSeparators(Area root)
    {
        bsep = new Vector();
        for (int i = 0; i < root.getChildCount(); i++)
        {
            final Area child = root.getChildAt(i);
            analyzeAreaSeparators(child);
        }
    }
    
    /**
     * Analyzes the area and detects the separators that are implemented as borders
     * or background changes.
     */
    private void analyzeAreaSeparators(Area area)
    {
        boolean isep = area.isExplicitlySeparated() || area.isBackgroundSeparated();
        if (isep || area.hasTopBorder())
            bsep.add(new Separator(Separator.BOXH,
                                   area.getX1(), area.getY1(), area.getX2(), area.getY1() + ART_SEP_WIDTH - 1));
        if (isep || area.hasBottomBorder())
            bsep.add(new Separator(Separator.BOXH,
                                   area.getX1(), area.getY2() - ART_SEP_WIDTH + 1, area.getX2(), area.getY2()));
        if (isep || area.hasLeftBorder())
            bsep.add(new Separator(Separator.BOXV,
                                   area.getX1(), area.getY1(), area.getX1() + ART_SEP_WIDTH - 1, area.getY2()));
        if (isep || area.hasRightBorder())
            bsep.add(new Separator(Separator.BOXV,
                                   area.getX2() - ART_SEP_WIDTH + 1, area.getY1(), area.getX2(), area.getY2()));
    }
    
    /**
     * Looks for the nearest text box area placed above the separator. If there are more
     * such areas in the same distance, the leftmost one is returned.
     * @param sep the separator 
     * @return the leaf area containing the box or null if there is nothing above the separator
     */
    private Area findContentAbove(Area root, Separator sep)
    {
        return recursiveFindAreaAbove(root, sep.getX1(), sep.getX2(), 0, sep.getY1());
    }
    
    private Area recursiveFindAreaAbove(Area root, int x1, int x2, int y1, int y2)
    {
        Area ret = null;
        int maxx = x2;
        int miny = y1;
        List  boxes = root.getBoxes();
        for (Box box : boxes)
        {
            int bx = box.getBounds().getX1();
            int by = box.getBounds().getY2();
            if ((bx >= x1 && bx <= x2 && by < y2) &&  //is placed above
                    (by > miny ||
                     (by == miny && bx < maxx)))
            {
                ret = root; //found in our boxes
                if (bx < maxx) maxx = bx;
                if (by > miny) miny = by;
            }
        }

        for (int i = 0; i < root.getChildCount(); i++)
        {
            Area child = root.getChildAt(i);
            Area area = recursiveFindAreaAbove(child, x1, x2, miny, y2);
            if (area != null)
            {   
                int bx = area.getX1(); 
                int by = area.getY2();
                int len = area.getText().length();
                if ((len > 0) && //we require some text in the area
                        (by > miny ||
                         (by == miny && bx < maxx)))
                {
                    ret = area;
                    if (bx < maxx) maxx = bx;
                    if (by > miny) miny = by;
                }
            }
        }
        
        return ret;
    }
    
    
    //================================================================
    
    //DEBUG FUNCTIONS
    /*protected void dispSeparators()
    {
        BrowserCanvas canv = BlockBrowser.browser.getBrowserCanvas();
        canv.redrawBoxes();
    	for (Separator sep : hsep)
    		dispSep(sep, Color.RED);
    	for (Separator sep : vsep)
    		dispSep(sep, Color.GREEN);
    }
    
    protected void dispSep(Separator a, Color color)
    {
        BrowserCanvas canv = BlockBrowser.browser.getBrowserCanvas();
        java.awt.Graphics g = canv.getImageGraphics();
        //g.setColor(color);
        if (a.isHorizontal())
            g.setColor(Color.RED);
        else 
            //g.setColor(Color.GREEN);
            return;
        g.fillRect(a.getX1(), a.getY1(), a.getWidth(), a.getHeight());
        //g.setColor(Color.BLACK);
        //g.drawRect(a.getX1(), a.getY1(), a.getWidth(), a.getHeight());
        canv.update(canv.getGraphics());
    }
    
    protected void dispRect(Rectangular a, Color color)
    {
        BrowserCanvas canv = BlockBrowser.browser.getBrowserCanvas();
        java.awt.Graphics g = canv.getImageGraphics();
        Color c = new Color(color.getRed(), color.getGreen(), color.getBlue(), 64); //add transparency
        g.setColor(c);
        g.fillRect(a.getX1(), a.getY1(), a.getWidth(), a.getHeight());
        g.setColor(color);
        g.drawRect(a.getX1(), a.getY1(), a.getWidth(), a.getHeight());
        canv.update(canv.getGraphics());
    }
    
    protected void wait(int ms)
    {
        //System.out.println("waiting");
        try {
            Thread.sleep(ms);
        } catch(InterruptedException e) {}
    }*/
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy