/**
* pdfXtk - PDF Extraction Toolkit
* Copyright (c) by the authors/contributors. All rights reserved.
* This project includes code from PDFBox and TouchGraph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the names pdfXtk or PDF Extraction Toolkit; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://pdfxtk.sourceforge.net
*
*/
package at.ac.tuwien.dbai.pdfwrap.model.graph;
import at.ac.tuwien.dbai.pdfwrap.gui.EdgeSegment;
import at.ac.tuwien.dbai.pdfwrap.model.document.GenericSegment;
import at.ac.tuwien.dbai.pdfwrap.model.document.TextSegment;
import at.ac.tuwien.dbai.pdfwrap.utils.Utils;
import java.io.Serializable;
/*
import edu.uci.ics.jung.graph.impl.SimpleUndirectedSparseVertex;
*/
/**
* This represents an edge in the neighbourhood graph
*
* @author Tamir Hassan, pdfanalyser@tamirhassan.com
* @version PDF Analyser 0.9
*/
public class AdjacencyEdge<T extends GenericSegment> implements Serializable {
private static final long serialVersionUID = 1L;
protected T nodeFrom, nodeTo;
public static final int REL_LEFT = 0;
public static final int REL_RIGHT = 1;
public static final int REL_ABOVE = 2;
public static final int REL_BELOW = 3;
protected float weight;
// public static final int REL_SUPERIOR = 10;
// public static final int REL_INFERIOR = 11;
// 20.11.06
// added the new fields:
// hasAlignment(int, specified by GenericSegment.H/V_ALIGN...)
// hasSameFontSize(bool)
// hasSameFont(bool) -- TBA, once we can read it from PDFBox!
// isSeparatedByRulingLine(bool)
// hasConstantLeading -- this one needs to be algorithmically deduced ??
// or better to just leave this for an on-the-fly calculation?
protected boolean ruled;
protected int direction;
/**
* Constructor.
*
* @param todo: add parameters :)
*
*/
public AdjacencyEdge(T nodeFrom, T nodeTo, int direction)
{
this.nodeFrom = nodeFrom;
this.nodeTo = nodeTo;
this.direction = direction;
ruled = false;
}
public AdjacencyEdge(T nodeFrom, T nodeTo, int direction, float weight)
{
this.nodeFrom = nodeFrom;
this.nodeTo = nodeTo;
this.direction = direction;
this.weight = weight;
ruled = false;
}
public AdjacencyEdge<T> duplicate()
{
AdjacencyEdge<T> retVal = new AdjacencyEdge<T>(nodeFrom, nodeTo, direction, weight);
retVal.ruled = this.ruled;
return retVal;
}
/*
public AttributedEdge toAttributedEdge(OntModel model)
{
return new AttributedEdge(this, model);
}
*/
// not in use yet?
// NOTE: there is currently a dependency between the
// positions of both nodes and the direction -- a
// nonsensical combination can be chosen
public T getNodeFrom() {
return nodeFrom;
}
public void setNodeFrom(T nodeFrom) {
this.nodeFrom = nodeFrom;
}
public T getNodeTo() {
return nodeTo;
}
public void setNodeTo(T nodeTo) {
this.nodeTo = nodeTo;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
public float lowCoord()
{
switch(direction)
{
case REL_LEFT:
return nodeTo.getX2();
case REL_RIGHT:
return nodeFrom.getX2();
case REL_ABOVE:
return nodeFrom.getY2();
case REL_BELOW:
return nodeTo.getY2();
default:
// yet another nonsensical comparison!
return -1.0f;
}
}
public float hiCoord()
{
switch(direction)
{
case REL_LEFT:
return nodeFrom.getX1();
case REL_RIGHT:
return nodeTo.getX1();
case REL_ABOVE:
return nodeTo.getY1();
case REL_BELOW:
return nodeFrom.getY1();
default:
// yet another nonsensical comparison!
return -1.0f;
}
}
public float midCoord()
{
return (lowCoord() + hiCoord()) / 2.0f;
}
public boolean isHorizontal()
{
if (direction == REL_LEFT ||
direction == REL_RIGHT)
return true;
else
return false;
}
public boolean isVertical()
{
if (direction == REL_ABOVE ||
direction == REL_BELOW)
return true;
else
return false;
}
// 2011-01-26: TODO: CLEAN UP ALL THIS!
public float physicalLength()
{
// 2011-01-26 -- now always uses bounding box measurements
// baseline comparisons to be moved elsewhere
// float topLineBaseline, bottomLineBaseline;
switch(direction)
{
case REL_LEFT:
return nodeFrom.getX1() - nodeTo.getX2();
case REL_RIGHT:
return nodeTo.getX1() - nodeFrom.getX2();
case REL_ABOVE:
return nodeTo.getY1() - nodeFrom.getY2();
case REL_BELOW:
return nodeFrom.getY1() - nodeTo.getY2();
default:
return -1.0f;
}
}
public float baselineDistance()
{
// 2011-01-26 -- now always uses bounding box measurements
// baseline comparisons to be moved elsewhere
// float topLineBaseline, bottomLineBaseline;
switch(direction)
{
case REL_ABOVE:
return nodeTo.getY1() - nodeFrom.getY1();
case REL_BELOW:
return nodeFrom.getY1() - nodeTo.getY1();
default:
return -1.0f;
}
}
// in case ruling line intersects the block of text itself
// (asiafrontpage/ihtfrontpage examples)
public GenericSegment toEnlargedBoundingSegment()
{
float tolerance = 0.0f;
if (nodeFrom instanceof TextSegment && nodeTo instanceof TextSegment)
{
float afs = Utils.avg(((TextSegment)nodeFrom).getFontSize(),
((TextSegment)nodeTo).getFontSize());
tolerance = afs * 0.25f;
}
// note: in comparison to the next method, max and min
// are swapped for yo and xo calculations.
float newX1, newX2, newY1, newY2; //, xo1, xo2, yo1, yo2;
//System.out.println("direction: " + direction);
switch(direction)
{
case REL_LEFT:
newX1 = nodeTo.getX2(); newX2 = nodeFrom.getX1();
// newY1 = (nodeFrom.getYcen() + nodeTo.getYcen()) / 2;
// find overlap coordinates yo1, yo2:
newY1 = Utils.minimum(nodeFrom.getY1(), nodeTo.getY1());
newY2 = Utils.maximum(nodeFrom.getY2(), nodeTo.getY2());
//newY1 = (yo1 + yo2) / 2;
//newY2 = newY1;
break;
case REL_RIGHT:
newX2 = nodeTo.getX1(); newX1 = nodeFrom.getX2();
// newY1 = (nodeFrom.getYcen() + nodeTo.getYcen()) / 2;
// find overlap coordinates yo1, yo2:
newY1 = Utils.minimum(nodeFrom.getY1(), nodeTo.getY1());
newY2 = Utils.maximum(nodeFrom.getY2(), nodeTo.getY2());
//newY1 = (yo1 + yo2) / 2;
//newY2 = newY1;
break;
case REL_ABOVE:
newY1 = nodeFrom.getY2() - tolerance;
newY2 = nodeTo.getY1() + tolerance;
// newX1 = (nodeFrom.getXcen() + nodeTo.getXcen()) / 2;
// find overlap coordinates xo1, xo2:
newX1 = Utils.minimum(nodeFrom.getX1(), nodeTo.getX1());
newX2 = Utils.maximum(nodeFrom.getX2(), nodeTo.getX2());
//newX1 = (xo1 + xo2) / 2;
//newX2 = newX1;
break;
case REL_BELOW:
newY2 = nodeFrom.getY1() + tolerance;
newY1 = nodeTo.getY2() - tolerance;
// newX1 = (nodeFrom.getXcen() + nodeTo.getXcen()) / 2;
// find overlap coordinates xo1, xo2:
newX1 = Utils.minimum(nodeFrom.getX1(), nodeTo.getX1());
newX2 = Utils.maximum(nodeFrom.getX2(), nodeTo.getX2());
//newX1 = (xo1 + xo2) / 2;
//newX2 = newX1;
break;
default:
//System.out.println("whoops!");
newX1 = -1; newX2 = -1; newY1 = -1; newY2 = -1;
}
return new GenericSegment(newX1, newX2, newY1, newY2);
}
public GenericSegment toBoundingSegment()
{
// note: in comparison to the next method, max and min
// are swapped for yo and xo calculations.
float newX1, newX2, newY1, newY2; //, xo1, xo2, yo1, yo2;
//System.out.println("direction: " + direction);
switch(direction)
{
case REL_LEFT:
newX1 = nodeTo.getX2(); newX2 = nodeFrom.getX1();
// newY1 = (nodeFrom.getYcen() + nodeTo.getYcen()) / 2;
// find overlap coordinates yo1, yo2:
newY1 = Utils.minimum(nodeFrom.getY1(), nodeTo.getY1());
newY2 = Utils.maximum(nodeFrom.getY2(), nodeTo.getY2());
//newY1 = (yo1 + yo2) / 2;
//newY2 = newY1;
break;
case REL_RIGHT:
newX2 = nodeTo.getX1(); newX1 = nodeFrom.getX2();
// newY1 = (nodeFrom.getYcen() + nodeTo.getYcen()) / 2;
// find overlap coordinates yo1, yo2:
newY1 = Utils.minimum(nodeFrom.getY1(), nodeTo.getY1());
newY2 = Utils.maximum(nodeFrom.getY2(), nodeTo.getY2());
//newY1 = (yo1 + yo2) / 2;
//newY2 = newY1;
break;
case REL_ABOVE:
newY1 = nodeFrom.getY2(); newY2 = nodeTo.getY1();
// newX1 = (nodeFrom.getXcen() + nodeTo.getXcen()) / 2;
// find overlap coordinates xo1, xo2:
newX1 = Utils.minimum(nodeFrom.getX1(), nodeTo.getX1());
newX2 = Utils.maximum(nodeFrom.getX2(), nodeTo.getX2());
//newX1 = (xo1 + xo2) / 2;
//newX2 = newX1;
break;
case REL_BELOW:
newY2 = nodeFrom.getY1(); newY1 = nodeTo.getY2();
// newX1 = (nodeFrom.getXcen() + nodeTo.getXcen()) / 2;
// find overlap coordinates xo1, xo2:
newX1 = Utils.minimum(nodeFrom.getX1(), nodeTo.getX1());
newX2 = Utils.maximum(nodeFrom.getX2(), nodeTo.getX2());
//newX1 = (xo1 + xo2) / 2;
//newX2 = newX1;
break;
default:
//System.out.println("whoops!");
newX1 = -1; newX2 = -1; newY1 = -1; newY2 = -1;
}
return new GenericSegment(newX1, newX2, newY1, newY2);
}
public EdgeSegment toDisplayableSegment()
{
float newX1, newX2, newY1, newY2, xo1, xo2, yo1, yo2;
//System.out.println("direction: " + direction);
switch(direction)
{
case REL_LEFT:
newX1 = nodeTo.getX2(); newX2 = nodeFrom.getX1();
// newY1 = (nodeFrom.getYcen() + nodeTo.getYcen()) / 2;
// find overlap coordinates yo1, yo2:
yo1 = Utils.maximum(nodeFrom.getY1(), nodeTo.getY1());
yo2 = Utils.minimum(nodeFrom.getY2(), nodeTo.getY2());
newY1 = (yo1 + yo2) / 2;
newY2 = newY1;
break;
case REL_RIGHT:
newX2 = nodeTo.getX1(); newX1 = nodeFrom.getX2();
// newY1 = (nodeFrom.getYcen() + nodeTo.getYcen()) / 2;
// find overlap coordinates yo1, yo2:
yo1 = Utils.maximum(nodeFrom.getY1(), nodeTo.getY1());
yo2 = Utils.minimum(nodeFrom.getY2(), nodeTo.getY2());
newY1 = (yo1 + yo2) / 2;
newY2 = newY1;
break;
case REL_ABOVE:
newY1 = nodeFrom.getY2(); newY2 = nodeTo.getY1();
// newX1 = (nodeFrom.getXcen() + nodeTo.getXcen()) / 2;
// find overlap coordinates xo1, xo2:
xo1 = Utils.maximum(nodeFrom.getX1(), nodeTo.getX1());
xo2 = Utils.minimum(nodeFrom.getX2(), nodeTo.getX2());
newX1 = (xo1 + xo2) / 2;
newX2 = newX1;
break;
case REL_BELOW:
newY2 = nodeFrom.getY1(); newY1 = nodeTo.getY2();
// newX1 = (nodeFrom.getXcen() + nodeTo.getXcen()) / 2;
// find overlap coordinates xo1, xo2:
xo1 = Utils.maximum(nodeFrom.getX1(), nodeTo.getX1());
xo2 = Utils.minimum(nodeFrom.getX2(), nodeTo.getX2());
newX1 = (xo1 + xo2) / 2;
newX2 = newX1;
break;
default:
//System.out.println("whoops!");
newX1 = -1; newX2 = -1; newY1 = -1; newY2 = -1;
}
return new EdgeSegment(newX1, newX2, newY1, newY2);
}
public float avgFontSize()
{
// TODO: exception or -1 returned if a horizontal edge?
if (!(nodeFrom instanceof TextSegment) ||
!(nodeTo instanceof TextSegment)) return -1.0f;
TextSegment nFrom = (TextSegment)nodeFrom;
TextSegment nTo = (TextSegment)nodeTo;
return (nFrom.getFontSize() + nTo.getFontSize()) / 2;
}
public float highFontSize()
{
// TODO: exception or -1 returned if a horizontal edge?
if (!(nodeFrom instanceof TextSegment) ||
!(nodeTo instanceof TextSegment)) return -1.0f;
TextSegment nFrom = (TextSegment)nodeFrom;
TextSegment nTo = (TextSegment)nodeTo;
if (nFrom.getFontSize() > nTo.getFontSize())
return nFrom.getFontSize();
else return nTo.getFontSize();
}
public float lowFontSize()
{
// TODO: exception or -1 returned if a horizontal edge?
if (!(nodeFrom instanceof TextSegment) ||
!(nodeTo instanceof TextSegment)) return -1.0f;
TextSegment nFrom = (TextSegment)nodeFrom;
TextSegment nTo = (TextSegment)nodeTo;
if (nFrom.getFontSize() < nTo.getFontSize())
return nFrom.getFontSize();
else return nTo.getFontSize();
}
/**
* @return Returns the direction.
*/
public int getDirection() {
return direction;
}
/**
* @param direction The direction to set.
*/
public void setDirection(int direction) {
this.direction = direction;
}
public String toString()
{
String direction_text = "?";
if (direction == REL_ABOVE) direction_text = "above";
if (direction == REL_BELOW) direction_text = "below";
if (direction == REL_LEFT) direction_text = "left";
if (direction == REL_RIGHT) direction_text = "right";
return ("AdjacencyEdge: Direction: " + direction_text + "\n NodeFrom: " + nodeFrom + "\nNodeTo: " + nodeTo + "direction: " + direction + "\n");
}
public boolean isRuled()
{
return ruled;
}
public void setRuled(boolean ruled)
{
this.ruled = ruled;
}
}