Refector and documentations

master
Jonas Thiel 2013-03-15 11:27:14 +01:00
rodzic 4ac3f97e18
commit 0b53dd9baf
13 zmienionych plików z 348 dodań i 207 usunięć

Wyświetl plik

@ -3,7 +3,7 @@ inputFile=input.txt
outputFile=out.png
width=500
height=636
minSize=4
minSize=8
maxSize=38
colors=#000000,#3a3a3a,#787878,#b2b2b2
background=#ffffff

Wyświetl plik

@ -1,10 +1,26 @@
import java.awt.*;
/**
* Utility class to transform and parse colors.
*/
public class ColorHelper {
public static int transform(Color c){
/**
* Transforms a java.awt.Color to processings internal int representation.
*
* @param c the color to be transformed
* @return A processing color value
*/
public static int transform(Color c) {
return (c.getAlpha() << 24) | (c.getRed() << 16) | (c.getGreen() << 8) | c.getBlue();
}
public static int decode(String c){
/**
* Decodes a color string in HEX-Values
*
* @param c the color string to be transformed
* @return A processing color value
*/
public static int decodeHex(String c) {
return transform(Color.decode(c));
}
}

Wyświetl plik

@ -1,37 +1,70 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Singleton holding the TagCloud configuration.
*/
public class Configuration {
private static Configuration instance = null;
private Properties prop = new Properties();
private Configuration(){}
private Configuration() {
}
public String getShapeFile(){ return prop.getProperty("shapeFile"); }
public String getInputFile(){ return prop.getProperty("inputFile"); }
public String getOutputFile(){ return prop.getProperty("outputFile"); }
public int getWidth() { return Integer.parseInt(prop.getProperty("width")); }
public int getHeight(){ return Integer.parseInt(prop.getProperty("height")); }
public int getMinSize(){ return Integer.parseInt(prop.getProperty("minSize")); }
public int getMaxSize(){ return Integer.parseInt(prop.getProperty("maxSize")); }
public String getColors(){ return prop.getProperty("colors"); }
public String getBackgroundColor(){return prop.getProperty("background"); }
public boolean isDebug(){
public String getShapeFile() {
return prop.getProperty("shapeFile");
}
public String getInputFile() {
return prop.getProperty("inputFile");
}
public String getOutputFile() {
return prop.getProperty("outputFile");
}
public int getWidth() {
return Integer.parseInt(prop.getProperty("width"));
}
public int getHeight() {
return Integer.parseInt(prop.getProperty("height"));
}
public int getMinSize() {
return Integer.parseInt(prop.getProperty("minSize"));
}
public int getMaxSize() {
return Integer.parseInt(prop.getProperty("maxSize"));
}
public String getColors() {
return prop.getProperty("colors");
}
public String getBackgroundColor() {
return prop.getProperty("background");
}
public boolean isDebug() {
return prop.getProperty("debug") != null && prop.getProperty("debug").equals("true");
}
public void load(){
try {
prop.load(new FileInputStream("config.properties"));
} catch (IOException e) {
e.printStackTrace();
}
/**
* Load configuration from an input stream
*
* @param inputStream to load from
* @throws IOException
*/
public void load(InputStream inputStream) throws IOException {
prop.load(inputStream);
}
public static Configuration getInstance(){
if(instance == null){
public static Configuration getInstance() {
if (instance == null) {
instance = new Configuration();
}
return instance;

Wyświetl plik

@ -1,42 +0,0 @@
import wordcram.Word;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
public class InputWords {
private String file;
public InputWords(String file){
this.file = file;
}
public Word[] getWords(){
ArrayList<Word> words = new ArrayList<Word>();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
return new Word[0];
}
String line = null;
try {
while((line = reader.readLine()) != null){
String[] values = line.split(",");
if(values.length == 2){
words.add( new Word(values[0].trim(), Float.parseFloat(values[1].trim())));
}
}
} catch (IOException e) {
e.printStackTrace();
return new Word[0];
}
return words.toArray(new Word[words.size()]);
}
}

Wyświetl plik

@ -1,9 +1,21 @@
import processing.core.PApplet;
import java.io.FileInputStream;
import java.io.IOException;
/**
* Simple application wrapper to run the TagCloudGenerator from command line.
* Please provide a config.properties for configuration.
*/
public class TagCloud {
public static void main(String[] args){
Configuration.getInstance().load();
PApplet.main(new String[] { "--present", "Tree" });
public static void main(String[] args) {
try {
Configuration.getInstance().load(new FileInputStream("config.properties"));
} catch (IOException e) {
e.printStackTrace();
return;
}
PApplet.main(new String[]{"--present", "TagCloudGenerator"});
}
}

Wyświetl plik

@ -0,0 +1,42 @@
import wordcram.Word;
import wordcram.WordColorer;
import java.util.ArrayList;
import java.util.Random;
/**
* Colors a tag cloud instance by using a random color per word, from a CSV list of
* HEX string colors.
*/
public class TagCloudColorer implements WordColorer {
private ArrayList<Integer> colors = new ArrayList<Integer>();
private Random randomGenerator = new Random();
public static final int FALLBACK_COLOR = ColorHelper.decodeHex("#000000");
public static final String SEPARATOR = ",";
/**
* Initializes a instance with CSV of HEX color strings.
* Will use FALLBACK_COLOR as default, if no color could be parsed.
*
* @param colorString A CSV of HEX color string, e.g. "#000000,#111111,#222222"
*/
public TagCloudColorer(String colorString) {
loadColorString(colorString);
if (colors.isEmpty()) {
colors.add(FALLBACK_COLOR);
}
}
@Override
public int colorFor(Word word) {
return colors.get(randomGenerator.nextInt(colors.size()));
}
private void loadColorString(String colorString) {
for (String c : colorString.split(SEPARATOR)) {
colors.add(ColorHelper.decodeHex(c));
}
}
}

Wyświetl plik

@ -0,0 +1,80 @@
import processing.core.*;
import wordcram.WordCram;
import java.awt.*;
import wordcram.*;
/**
* Generate a TagCloud image of a text file and a shape image.
* <p/>
* Uses WordsReader to parse the text file.
* The shape image should have a white (#FFFFFF) background and a black (#000000) foreground.
* <p/>
* The draw method is called by the main Processing application. As this generator mustn't show more than one
* frame, it save the image and exists the main application after the first drawing.
* <p/>
* Images are saved in TIFF, TARGA, JPEG, and PNG format depending on the extension within the config output file name.
*/
public class TagCloudGenerator extends PApplet {
private WordCram cram;
/**
* Called before the first draw from the processing main application
*/
public void setup() {
Configuration config = Configuration.getInstance();
size(config.getWidth(), config.getHeight());
background(ColorHelper.decodeHex(config.getBackgroundColor()));
TagCloudPlacer placer = TagCloudPlacer.fromFile(config.getShapeFile(), Color.black);
WordsReader input = new WordsReader(config.getInputFile());
Word[] words = input.getWords();
TagCloudColorer colorer = new TagCloudColorer(config.getColors());
TagCloudSizer sizer = new TagCloudSizer(config.getMinSize(), config.getMaxSize(), words);
cram = new WordCram(this)
.fromWords(words)
.withColorer(colorer)
.withPlacer(placer)
.withNudger(placer)
.withSizer(sizer);
}
/**
* Called repeatedly by the main processing application. As only one render run is needed
* to generate the image, this function saves the generated image and
* exists after the first frame.
*/
public void draw() {
cram.drawAll();
if (Configuration.getInstance().isDebug()) {
printDebug();
}
save(Configuration.getInstance().getOutputFile());
exit();
}
private void printDebug() {
int skippedBecauseOfNoSpace = 0;
int skippedBecauseOfTooSmall = 0;
Word[] skippedWords = cram.getSkippedWords();
Word[] placedWords = cram.getWords();
for (Word skipped : skippedWords) {
if (skipped.wasSkippedBecause() == WordCram.NO_SPACE) {
skippedBecauseOfNoSpace++;
} else if (skipped.wasSkippedBecause() == WordCram.SHAPE_WAS_TOO_SMALL) {
skippedBecauseOfTooSmall++;
}
}
System.out.println("- STATISTICS ---------------------------------------");
System.out.println("Total placed Words: " + placedWords.length);
System.out.println("Total skipped Words: " + skippedWords.length);
System.out.println("-> because of no space: " + skippedBecauseOfNoSpace);
System.out.println("-> because of too small: " + skippedBecauseOfTooSmall);
System.out.println("----------------------------------------------------");
}
}

Wyświetl plik

@ -21,7 +21,7 @@ import processing.core.PVector;
import wordcram.Word;
import wordcram.WordNudger;
import wordcram.WordPlacer;
class ShapeBasedPlacer implements WordPlacer, WordNudger {
class TagCloudPlacer implements WordPlacer, WordNudger {
public static int TOLERANCE = 5;
public static boolean PRECISE = false;
@ -36,7 +36,7 @@ class ShapeBasedPlacer implements WordPlacer, WordNudger {
Random random;
BufferedImage image = null;
public ShapeBasedPlacer(Shape shape) {
public TagCloudPlacer(Shape shape) {
this.area = new Area(shape);
init();
}
@ -50,7 +50,7 @@ class ShapeBasedPlacer implements WordPlacer, WordNudger {
this.maxY = (float) areaBounds.getMaxY();
}
public static ShapeBasedPlacer fromTextGlyphs(String text, String fontName) {
public static TagCloudPlacer fromTextGlyphs(String text, String fontName) {
Font font = null;
Graphics2D g2d;
FontRenderContext frc;
@ -60,21 +60,21 @@ class ShapeBasedPlacer implements WordPlacer, WordNudger {
g2d = img.createGraphics();
frc = g2d.getFontRenderContext();
gv = font.createGlyphVector(frc, text);
return new ShapeBasedPlacer(gv.getOutline(GLYPH_SIZE / 10,GLYPH_SIZE));
return new TagCloudPlacer(gv.getOutline(GLYPH_SIZE / 10,GLYPH_SIZE));
}
private ShapeBasedPlacer(BufferedImage image) {
private TagCloudPlacer(BufferedImage image) {
this.image = image;
}
public static ShapeBasedPlacer fromFile(String path, Color color) {
public static TagCloudPlacer fromFile(String path, Color color) {
BufferedImage image = null;
try {
image = ImageIO.read(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
ShapeBasedPlacer result = new ShapeBasedPlacer(image);
TagCloudPlacer result = new TagCloudPlacer(image);
if (PRECISE) {
result.fromImagePrecise(color);
} else {

Wyświetl plik

@ -0,0 +1,63 @@
import processing.core.PApplet;
import wordcram.Word;
/**
* Determines the size of words in a TagCloud instance.
* The size will be determined as following:
* A) All words have the same weight: use (maxSize - minSize) / 3
* B) Words have different weights: use a linear interpolation between [minSize, maxSize] for [0,1]
* <p/>
* Hint: WordCramp normalizes after loading all word the word weight's to [0,1]
*/
public class TagCloudSizer implements wordcram.WordSizer {
private boolean allEqual = true;
private float minSize;
private float maxSize;
public static final float DEFAULT_TOLERANCE = 0.1f;
/**
* Initialize an instance for minSize to maxSize for a given array of words.
* Determines if all words have equal weight using the DEFAULT_TOLERANCE
*
* @param minSize minimum text size
* @param maxSize maximum text size
* @param words the list of words
*/
public TagCloudSizer(float minSize, float maxSize, Word[] words) {
this(minSize, maxSize, words, DEFAULT_TOLERANCE);
}
/**
* Initialize an instance for minSize to maxSize for a given array of words.
* Determines if all words have equal weight using the tolerance
*
* @param minSize minimum text size
* @param maxSize maximum text size
* @param words the list of words
* @param tolerance the maximum difference beween weight to be equal
*/
public TagCloudSizer(float minSize, float maxSize, Word[] words, float tolerance) {
this.minSize = minSize;
this.maxSize = maxSize;
if (words.length > 0) {
float initial = words[0].weight;
for (Word w : words) {
if (Math.abs(w.weight - initial) > tolerance) {
allEqual = false;
break;
}
}
}
}
@Override
public float sizeFor(Word word, int wordRank, int wordCount) {
if (allEqual) {
return (maxSize - minSize) / 3.0f;
} else {
return PApplet.lerp(minSize, maxSize, word.weight);
}
}
}

Wyświetl plik

@ -1,73 +0,0 @@
import processing.core.*;
import wordcram.WordCram;
import java.awt.*;
import java.awt.image.PackedColorModel;
import java.io.*;
import java.util.ArrayList;
import wordcram.*;
import wordcram.text.TextSource;
public class Tree extends PApplet {
private WordCram cram;
private boolean draw = true;
public void setup(){
Configuration config = Configuration.getInstance();
size(config.getWidth(),config.getHeight());
background(ColorHelper.decode(config.getBackgroundColor()));
ShapeBasedPlacer placer = ShapeBasedPlacer.fromFile(config.getShapeFile(), Color.black);
InputWords input = new InputWords(config.getInputFile());
Word[] words = input.getWords();
TreeColorer colorer = new TreeColorer(config.getColors());
WordSizer sizer = new WordSizer(config.getMinSize(), config.getMaxSize(), words);
cram = new WordCram(this)
.fromWords(words)
.withColorer(colorer)
.withPlacer(placer)
.withNudger(placer)
.withSizer(sizer);
}
public void draw() {
if(this.draw){
System.out.println("Start drawing tag cloud...");
cram.drawAll();
this.draw = false;
System.out.println("Finished drawing");
if(Configuration.getInstance().isDebug()){
printDebug();
}
save(Configuration.getInstance().getOutputFile());
exit();
}
}
private void printDebug(){
//tell me what didnt get drawn
int noSpace = 0;
int tooSmall = 0;
Word[] skippedWords = cram.getSkippedWords();
Word[] placedWords = cram.getWords();
for (Word skipped: skippedWords) {
if (skipped.wasSkippedBecause() == WordCram.NO_SPACE) {
noSpace++;
} else if (skipped.wasSkippedBecause() == WordCram.SHAPE_WAS_TOO_SMALL) {
tooSmall++;
}
}
System.out.println("Total placed Words: " + placedWords.length);
System.out.println("Total Skipped Words: " + skippedWords.length);
System.out.println("Skipped because no Space: " + noSpace);
System.out.println("Skipped because too small: " + tooSmall);
System.out.println("Finished");
}
}

Wyświetl plik

@ -1,26 +0,0 @@
import wordcram.Word;
import wordcram.WordColorer;
import java.awt.*;
import java.util.ArrayList;
import java.util.Random;
public class TreeColorer implements WordColorer {
private ArrayList<Integer> colors = new ArrayList<Integer>();
private Random randomGenerator = new Random();
public TreeColorer(String colorString){
for(String c : colorString.split(",")){
colors.add(ColorHelper.decode(c));
}
if(colors.isEmpty()){
colors.add(ColorHelper.decode("#000000"));
}
}
@Override
public int colorFor(Word word) {
return colors.get(randomGenerator.nextInt(colors.size()));
}
}

Wyświetl plik

@ -1,33 +0,0 @@
import processing.core.PApplet;
import wordcram.Word;
public class WordSizer implements wordcram.WordSizer{
private boolean allEqual = true;
private float minSize = 4;
private float maxSize = 38;
public WordSizer(float minSize, float maxSize, Word[] words){
this.minSize = minSize;
this.maxSize = maxSize;
if(words.length > 0){
float initial = words[0].weight;
for(Word w : words){
if(Math.abs(w.weight - initial) > 0.1){
allEqual = false;
break;
}
}
}
}
@Override
public float sizeFor(Word word, int wordRank, int wordCount) {
if(allEqual){
return (maxSize - minSize) / 3.0f;
}
else{
return PApplet.lerp(minSize, maxSize, word.weight);
}
}
}

Wyświetl plik

@ -0,0 +1,69 @@
import wordcram.Word;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
/**
* Helper to read list of Words with initial weights from a simple Textfile.
* Supported text format:
* WORD1,WEIGHT1
* WORD2,WEIGHT2
*
* WORD must not contain a comma, but can include a space character.
* WEIGHT will be parsed as a Float
*/
public class WordsReader {
private String file;
public static final String SEPARATOR = ",";
/**
* Constructs a reader
* @param file to be parsed
*/
public WordsReader(String file){
this.file = file;
}
/**
* Read the file and parse a list of words.
* @return an array of weighted words.
*/
public Word[] getWords(){
ArrayList<Word> words = new ArrayList<Word>();
BufferedReader reader;
try {
reader = new BufferedReader(new FileReader(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
return new Word[0];
}
String line;
try {
while((line = reader.readLine()) != null){
Word word = parse(line);
if(word != null)
words.add(word);
}
} catch (IOException e) {
e.printStackTrace();
return new Word[0];
}
return words.toArray(new Word[words.size()]);
}
private Word parse(String line){
String[] values = line.split(SEPARATOR);
if(values.length == 2){
return new Word(values[0].trim(), Float.parseFloat(values[1].trim()));
}
else {
return null;
}
}
}