/******************************************************************************* * Copyright (c) 2009-2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.rascalmpl.eclipse.library.vis.util; import java.util.TreeMap; import org.rascalmpl.eclipse.library.vis.graphics.FontStyle; import org.rascalmpl.eclipse.library.vis.swt.SWTFontsAndColors; public class WordWrapMinRaggedness { public class WordWrapElement{ String[] syllables; double[] widthsBeforeHyphen; double[] widthsAfterHyphen; double wordWidth; public WordWrapElement(String[] syllables){ this.syllables = syllables; widthsBeforeHyphen = new double[syllables.length]; widthsAfterHyphen = new double[syllables.length]; String s = ""; for(int i = 0 ; i < syllables.length; i++){ widthsBeforeHyphen[i] = widthsAfterHyphen[i] = -1; s+= syllables[i]; } wordWidth = SWTFontsAndColors.textWidth(s, fontName, fontSize, styles); } double getWidth(){ return getWidthAfterHyphen(0); } double getWidthBeforeHyphen(int syllableBeforeHyphen){ int syllableAfterHyphen = syllableBeforeHyphen +1; if(widthsBeforeHyphen[syllableAfterHyphen] >= 0){ return widthsBeforeHyphen[syllableAfterHyphen]; } if(syllableAfterHyphen == 0){ widthsBeforeHyphen[0] = 0; } String s = syllables[0]; for(int i = 1 ; i <= syllableAfterHyphen ; i++){ s += syllables[i]; } s+="-"; widthsBeforeHyphen[syllableAfterHyphen] = SWTFontsAndColors.textWidth(s, fontName, fontSize, styles); return widthsBeforeHyphen[syllableAfterHyphen]; } double getWidthAfterHyphen(int syllableAfterHyphen){ if(widthsAfterHyphen[syllableAfterHyphen] >= 0){ return widthsAfterHyphen[syllableAfterHyphen]; } String s = syllables[syllableAfterHyphen+1]; for(int i = syllableAfterHyphen+1 ; i <= syllables.length ; i++){ s += syllables[i]; } widthsAfterHyphen[syllableAfterHyphen] = SWTFontsAndColors.textWidth(s, fontName, fontSize, styles); return widthsAfterHyphen[syllableAfterHyphen]; } boolean isHyphenated(int lastSyllable){ return lastSyllable != syllables.length-1; } } public class LineElements implements Comparable<LineElements>{ int startWord, endWord; int startSyllable, endSyllable; double lineWidth; LineElements(int startWord, int startSyllable, int endWord, int endSyllable,double lineWidth){ this.startWord = startWord; this.startSyllable = startSyllable; this.endWord = endWord; this.endSyllable = endSyllable; this.lineWidth = lineWidth; } @Override public int compareTo(LineElements o) { if(o.lineWidth != lineWidth){ return (int)Math.signum(lineWidth - o.lineWidth); } if(o.startWord != o.startWord){ return startWord - o.startWord; } else if(o.startSyllable != startSyllable){ return startSyllable - o.startSyllable; } else if ( endWord != o.endWord){ return endWord - o.endWord; } else { return endSyllable - o.endSyllable; } } public int getStartWord() { return startWord; } public int getEndWord() { return startWord; } public int getStartHyphen() { return startWord; } public int getEndHyphen() { return startWord; } } WordWrapElement[] elems; double desiredSpaceSize; double minSpaceWidth; double[] lineWidths; String fontName; int fontSize; FontStyle[] styles; double hyphenPenalty; TreeMap<LineElements,Double> costs; double minCost; public WordWrapMinRaggedness(WordWrapElement[] elems, double desiredSpaceSize, double[] lineWidths, double hyphenPenalty, String fontName, int fontSize,FontStyle ... styles) { this.elems = elems; this.desiredSpaceSize = desiredSpaceSize; this.lineWidths = lineWidths; this.fontName = fontName; this.fontSize = fontSize; this.styles = styles; costs = new TreeMap<LineElements,Double>(); minCost = Double.POSITIVE_INFINITY; } double costOf(int startWord, int endWord, double lineWidth){ return costOf(startWord,0,endWord,elems[endWord].syllables.length-1,lineWidth,0,false); } double costOf(int startWord, int endWord, double lineWidth,double prevCost){ return costOf(startWord,0,endWord,elems[endWord].syllables.length-1,lineWidth,lineWidth-prevCost,true); } double costOf(int startWord, int startSyllable, int endWord, int endSyllable, double lineWidth, double prevWidth, boolean prev){ LineElements thisLine = new LineElements(startWord, startSyllable, endWord, endSyllable,lineWidth); if(costs.containsKey(thisLine)){ return costs.get(thisLine); } else{ double width = 0; thisLine.endWord--; if(prev){ width = prevWidth; thisLine.endWord++; width+=minSpaceWidth; width+=elems[endWord].getWidthBeforeHyphen(endSyllable); } else { width+=elems[startWord].getWidthAfterHyphen(startSyllable); width+=elems[endWord].getWidthBeforeHyphen(endSyllable); width+=minSpaceWidth; for(int i = startWord +1; i <= endWord && width <= lineWidth; i++){ width+=minSpaceWidth; width+=elems[i].getWidth(); } } double cost; if(width > lineWidth){ cost = Double.POSITIVE_INFINITY; } else { cost = lineWidth - width + (elems[endWord].isHyphenated(endSyllable) ? hyphenPenalty : 0); } costs.put(thisLine, cost); return cost; } } // LineElements getBest(){ // ArrayList<LineElements> lines = new ArrayList<LineElements>(); // int line = 0; // for(int startWord = 0; startWord < elems.length; startWord++){ // int endWord = startWord; // while(costOf(startWord,endWord,) // } // } }