package com.zilbo.flamingSailor.TE.model;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import java.awt.geom.Rectangle2D;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/*
* Copyright 2012 Zilbo.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public abstract class Component implements Comparable<Component> {
private static final Logger logger = Logger.getLogger(Component.class);
Rectangle2D geom;
Long id;
List<Component> pieces;
public static final int HIST_SPACE = 0;
public static final int HIST_DIGIT = 1;
public static final int HIST_UPPER = 2;
public static final int HIST_LOWER = 3;
public static final int HIST_PUNCT = 4;
public static final int HIST_OTHER = 5;
public String getText() {
int end = this.getChildren().size();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < end; i++) {
if (i != 0) {
sb.append('\n');
}
sb.append(this.getChildren().get(i).getText());
}
return sb.toString();
}
public boolean isEmpty() {
for (Component c : pieces) {
if (!c.isEmpty()) {
return false;
}
}
return true;
}
public boolean hasChildren() {
return this.pieces.size() > 0;
}
// TODO make this shallow!
public List<Component> getChildren() {
List<Component> c = new ArrayList<>();
c.addAll(pieces);
return c;
}
/**
* remove a child component.
* this makes no effort to fix up geographic boundaries, and will fail silently if it doesn't exist.
* this also doesn't do a depth-wide search for the component.
*
* @param c the child to remove
*/
public void removeChild(Component c) {
this.pieces.remove(c);
adjustGeom();
}
/**
* recalculate the geometery of the piece, based on the children of it.
*/
protected void adjustGeom() {
geom = new Rectangle2D.Double();
if (pieces.size() > 0) {
geom = pieces.get(0).getGeom().getBounds2D();
for (int i = 1; i < pieces.size(); i++) {
geom = geom.createUnion(pieces.get(i).getGeom());
}
}
}
public int size() {
return this.getChildren().size();
}
public Long getID() {
return this.id;
}
protected Component(Long id) {
this.id = id;
this.geom = new Rectangle2D.Double();
pieces = new ArrayList<>();
}
public void addChild(Component child) {
if (pieces.size() == 0) {
Rectangle2D childRect = child.getGeom();
geom = new Rectangle2D.Double();
geom.setRect(childRect);
// geom = new Rectangle2D.Double(childRect.getMinX(), childRect.getMinY(), childRect.getMaxX(), childRect.getMaxY());
} else {
expandBoundry(child.getGeom());
}
pieces.add(child);
}
public void addChildAtTop(Component child) {
if (pieces.size() == 0) {
Rectangle2D childRect = child.getGeom();
geom = new Rectangle2D.Double();
geom.setRect(childRect);
// geom = new Rectangle2D.Double(childRect.getMinX(), childRect.getMinY(), childRect.getMaxX(), childRect.getMaxY());
} else {
expandBoundry(child.getGeom());
}
pieces.add(0, child);
}
public void merge(Component mergeThis) {
expandBoundry(mergeThis.getGeom());
for (Component tl : mergeThis.getChildren()) {
this.addChild(tl);
}
}
public Rectangle2D getGeom() {
return this.geom;
}
/**
* @return distance between 2 X points (the width)
*/
public double width() {
return getGeom().getWidth();
}
/**
* @return height of the component
*/
public double height() {
return getGeom().getHeight();
}
@Override
/**
* j
* Sorts text pieces by position by comparing the location of this text piece
* with a given text piece t
* @param t
* the text piece to compare with
* @return
* the comparison result
* -1: this text piece is located in the left-side or the top of another text piece t
* 1: this text piece is located in the right-side or the below of another text piece t
* 0: this text piece is located in the same location as another text piece t
*/
public int compareTo(Component t) {
// Line2D g1 = this.getGeom();
// Line2D g2 = t.getGeom();
// int ret;
/*
if (g1.getP1() == g2.getP1() && g1.getP2() == g2.getP2()) {
ret = 0;
// logger.info(ret + "\t" + GeomUtil.getRectangleDebug(g1) + "\t" + GeomUtil.getRectangleDebug(g2));
return ret;
}
*/
if (onSameLine(t)) {
//ret = new Long(Math.round(y1 / 10)).compareTo(Math.round(y2 / 10));
//if (ret == 0) {
//if ( Math.abs(y1-y2) <2) {
Double x1 = getGeom().getMinX();
return x1.compareTo(t.getGeom().getMinX());
} else {
Double y1 = getGeom().getMinY();
// double y2 = g2.getY1();
return y1.compareTo(t.getGeom().getMinY());
}
}
public static class left_comparator implements Comparator<Component> {
public int compare(Component t1, Component t2) {
double result = t1.getGeom().getMinX() - t2.getGeom().getMaxX();
if (result > 0) {
return 1;
}
if (result < 0) {
return -1;
}
return 0;
}
}
public static class top_comparator implements Comparator<Component> {
public int compare(Component t1, Component t2) {
if (t1.onSameLine(t2)) {
return 0;
}
double result = t1.getGeom().getMinY() - t2.getGeom().getMaxY();
if (result > 0) {
return 1;
}
if (result < 0) {
return -1;
}
return 0;
}
}
public static class topleft_comparator implements Comparator<Component> {
public int compare(Component t1, Component t2) {
return t1.compareTo(t2);
}
}
public String getRectangleDebug() {
return GeomUtil.getRectangleDebug(geom);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(this.getClass().getSimpleName() + "{" +
getRectangleDebug()
+ "\n");
for (Component p : getChildren()) {
sb.append("\t\t").append(p.toString()).append("\n");
}
sb.append("}");
return sb.toString();
}
public double density() {
double density = 0.0;
for (Component c : getChildren()) {
density += c.density();
}
return density / (1.0 * getChildren().size());
}
public void expandBoundry(Rectangle2D geom2) {
geom = geom.createUnion(geom2);
/*
Point2D point1;
Point2D point2;
double x;
double y;
if (geom.getX1() > geom2.getX1()) {
x = geom2.getX1();
} else {
x = geom.getX1();
}
if (geom.getY1() > geom2.getY1()) {
y = geom2.getY1();
} else {
y = geom.getY1();
}
point1 = new Point2D.Double(x, y);
if (geom.getX2() < geom2.getX2()) {
x = geom2.getX2();
} else {
x = geom.getX2();
}
if (geom.getY2() < geom2.getY2()) {
y = geom2.getY2();
} else {
y = geom.getY2();
}
point2 = new Point2D.Double(x, y);
return new Rectangle2D.Double(point1, point2);
*/
}
/**
* Is 'l2' inside of 'l1'
*
* @param l1 box1
* @return true if this component is inside the box
*/
public boolean isContainedBy(Rectangle2D l1) {
return l1.contains(geom);
/*
if (l1.getX1() <= geom.getX1() && l1.getX2() >= geom.getX2()) {
if (l1.getY1() <= geom.getY1() && l1.getY2() >= geom.getY2()) {
return true;
}
}
return false;
*/
}
public List<Component> findByGeom(Rectangle2D box) {
List<Component> ret = new ArrayList<>();
if (isContainedBy(box)) {
ret.add(this);
return ret;
}
for (Component c : getChildren()) {
ret.addAll(c.findByGeom(box));
}
return ret;
}
/**
* if the bottom of the lines rectangle is lower than the textpiece's top, then it belongs here
*
* @param c the piece to compare against
* @return true if they are
*/
public boolean onSameLine(Component c) {
return onSameLine(c.getGeom());
}
public boolean onSameLine(Rectangle2D otherGeom) {
Double y1 = this.geom.getMinY();
Double y3 = this.geom.getMaxY();
Double y2 = otherGeom.getMinY();
Double y4 = otherGeom.getMaxY();
// we have some kind of crossover.
if (y1 <= y4 && y1 >= y2) {
// AND the boxes are touching
if (Math.abs(this.geom.getMinX() - otherGeom.getMaxX()) < 0.5 || (this.geom.getMaxX() - otherGeom.getMinX()) < 0.5) {
// assume it's a super/subscript letter and call it the same line.
return true;
}
}
// we have some kind of crossover.
if (y2 <= y3 && y2 >= y1) {
// AND the boxes are touching
// logger.info(this.geom.getMinX() - otherGeom.getMaxX());
if (Math.abs(this.geom.getMinX() - otherGeom.getMaxX()) < 0.5 || (this.geom.getMaxX() - otherGeom.getMinX()) < 0.5) {
// assume it's a super/subscript letter and call it the same line.
return true;
}
}
double yAveT = (y2 + y4) / 2;
// double diff = (y4-y2)/4;
if ((y1 <= (yAveT)) && ((yAveT) < y3)) {
return true;
}
yAveT = (y1 + y3) / 2;
return (y2 <= yAveT) && (yAveT < y4);
}
public void dumpChildren(PrintStream out, int level) {
StringBuilder sb = new StringBuilder();
sb.append(StringUtils.repeat("..", level));
sb.append(getClass().getSimpleName());
if (sb.length() < 20) {
sb.append(StringUtils.repeat(' ', 20 - sb.length()));
}
sb.append('\t');
sb.append(getRectangleDebug()).append(" \t");
// sb.append(getText().replace("\n", "\n" + StringUtils.repeat(' ', 43)));
String text;
if (sb.length() > 256) {
text = sb.substring(0, 256 - 4) + " ...";
} else {
text = sb.toString();
}
out.println(text);
for (Component component : getChildren()) {
component.dumpChildren(out, level + 1);
}
}
boolean isHeading = false;
public boolean isHeading() {
return this.isHeading;
}
public void setIsHeading(boolean flag) {
isHeading = flag;
}
public boolean isMultiLine() {
return false;
}
public long[] getHistogram() {
long[] histogram = new long[HIST_OTHER + 1];
for (int i = 0; i < histogram.length; i++) {
histogram[i] = 0L;
}
for (Component c : this.getChildren()) {
long[] histC = c.getHistogram();
for (int i = 0; i < histogram.length; i++) {
histogram[i] += histC[i];
}
}
return histogram;
}
public Double[] getNormalizedHistogram() {
return getNormalizedHistogram(this.getHistogram());
}
public static Double[] getNormalizedHistogram(long[] histogram) {
double total = 0.0;
for (long aHistogram : histogram) {
total += aHistogram;
}
Double[] normalized = new Double[histogram.length];
for (int i = 0; i < histogram.length; i++) {
normalized[i] = (1.0) * histogram[i] / total;
}
return normalized;
}
public long[] mergeHistogram(long[] hist) {
if (hist == null) {
return getHistogram();
}
return mergeHistogram(getHistogram(), hist);
}
public static long[] mergeHistogram(long[] hist1, long[] hist2) {
if (hist2 == null) {
return hist1;
}
if (hist1 == null) {
return hist2;
}
long result[] = new long[hist1.length];
for (int i = 0; i < hist1.length; i++) {
result[i] = hist1[i] + hist2[i];
}
return result;
}
public String normHistoGramToString() {
return normHistoGramToString(getNormalizedHistogram());
}
public static String normHistoGramToString(Double[] nHist) {
return (String.format("Hist: UC:%4.2f LC:%4.2f DI:%4.2f SP:%4.2f PU:%4.2f O:%4.2f",
nHist[HIST_UPPER], nHist[HIST_LOWER], nHist[HIST_DIGIT],
nHist[HIST_SPACE], nHist[HIST_PUNCT], nHist[HIST_OTHER])
);
}
}