ruby_wordcram 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.mvn/extensions.xml +8 -0
- data/.mvn/wrapper/maven-wrapper.properties +1 -0
- data/Rakefile +28 -5
- data/docs/_posts/2017-03-07-getting_started.md +3 -2
- data/docs/_posts/2017-03-07-under_the_hood.md +33 -0
- data/lib/WordCram.jar +0 -0
- data/lib/jsoup-1.10.2.jar +0 -0
- data/lib/ruby_wordcram/version.rb +1 -1
- data/lib/ruby_wordcram.rb +1 -2
- data/pom.rb +53 -0
- data/pom.xml +87 -0
- data/ruby_wordcram.gemspec +1 -2
- data/src/cue/lang/Counter.java +141 -0
- data/src/cue/lang/IterableText.java +10 -0
- data/src/cue/lang/NGramIterator.java +151 -0
- data/src/cue/lang/SentenceIterator.java +86 -0
- data/src/cue/lang/WordIterator.java +60 -0
- data/src/cue/lang/stop/StopWords.java +114 -0
- data/src/cue/lang/stop/arabic +351 -0
- data/src/cue/lang/stop/armenian +45 -0
- data/src/cue/lang/stop/catalan +219 -0
- data/src/cue/lang/stop/croatian +2024 -0
- data/src/cue/lang/stop/czech +256 -0
- data/src/cue/lang/stop/danish +94 -0
- data/src/cue/lang/stop/dutch +107 -0
- data/src/cue/lang/stop/english +183 -0
- data/src/cue/lang/stop/esperanto +180 -0
- data/src/cue/lang/stop/farsi +966 -0
- data/src/cue/lang/stop/finnish +235 -0
- data/src/cue/lang/stop/french +543 -0
- data/src/cue/lang/stop/german +231 -0
- data/src/cue/lang/stop/greek +637 -0
- data/src/cue/lang/stop/hebrew +220 -0
- data/src/cue/lang/stop/hindi +97 -0
- data/src/cue/lang/stop/hungarian +202 -0
- data/src/cue/lang/stop/italian +279 -0
- data/src/cue/lang/stop/latin +1 -0
- data/src/cue/lang/stop/norwegian +176 -0
- data/src/cue/lang/stop/polish +138 -0
- data/src/cue/lang/stop/portuguese +204 -0
- data/src/cue/lang/stop/romanian +284 -0
- data/src/cue/lang/stop/russian +652 -0
- data/src/cue/lang/stop/slovak +110 -0
- data/src/cue/lang/stop/slovenian +448 -0
- data/src/cue/lang/stop/spanish +308 -0
- data/src/cue/lang/stop/swedish +114 -0
- data/src/cue/lang/stop/turkish +117 -0
- data/src/cue/lang/unicode/BlockUtil.java +103 -0
- data/src/cue/lang/unicode/Normalizer.java +55 -0
- data/src/cue/lang/unicode/Normalizer6.java +32 -0
- data/src/license.txt +201 -0
- data/src/wordcram/Anglers.java +137 -0
- data/src/wordcram/BBTree.java +133 -0
- data/src/wordcram/BBTreeBuilder.java +61 -0
- data/src/wordcram/Colorers.java +52 -0
- data/src/wordcram/EngineWord.java +73 -0
- data/src/wordcram/Fonters.java +17 -0
- data/src/wordcram/HsbWordColorer.java +28 -0
- data/src/wordcram/ImageShaper.java +91 -0
- data/src/wordcram/Observer.java +9 -0
- data/src/wordcram/PlacerHeatMap.java +134 -0
- data/src/wordcram/Placers.java +74 -0
- data/src/wordcram/PlottingWordNudger.java +38 -0
- data/src/wordcram/PlottingWordPlacer.java +36 -0
- data/src/wordcram/ProcessingWordRenderer.java +42 -0
- data/src/wordcram/RandomWordNudger.java +44 -0
- data/src/wordcram/RenderOptions.java +10 -0
- data/src/wordcram/ShapeBasedPlacer.java +66 -0
- data/src/wordcram/Sizers.java +54 -0
- data/src/wordcram/SketchCallbackObserver.java +70 -0
- data/src/wordcram/SpiralWordNudger.java +31 -0
- data/src/wordcram/SvgWordRenderer.java +110 -0
- data/src/wordcram/SwirlWordPlacer.java +25 -0
- data/src/wordcram/UpperLeftWordPlacer.java +27 -0
- data/src/wordcram/WaveWordPlacer.java +25 -0
- data/src/wordcram/Word.java +357 -0
- data/src/wordcram/WordAngler.java +20 -0
- data/src/wordcram/WordArray.java +18 -0
- data/src/wordcram/WordBag.java +31 -0
- data/src/wordcram/WordColorer.java +25 -0
- data/src/wordcram/WordCounter.java +96 -0
- data/src/wordcram/WordCram.java +920 -0
- data/src/wordcram/WordCramEngine.java +196 -0
- data/src/wordcram/WordFonter.java +24 -0
- data/src/wordcram/WordNudger.java +44 -0
- data/src/wordcram/WordPlacer.java +44 -0
- data/src/wordcram/WordRenderer.java +10 -0
- data/src/wordcram/WordShaper.java +78 -0
- data/src/wordcram/WordSizer.java +46 -0
- data/src/wordcram/WordSkipReason.java +42 -0
- data/src/wordcram/WordSorterAndScaler.java +31 -0
- data/src/wordcram/WordSource.java +5 -0
- data/src/wordcram/text/Html.java +15 -0
- data/src/wordcram/text/Html2Text.java +17 -0
- data/src/wordcram/text/Text.java +15 -0
- data/src/wordcram/text/TextFile.java +23 -0
- data/src/wordcram/text/TextSource.java +5 -0
- data/src/wordcram/text/WebPage.java +23 -0
- metadata +94 -5
- data/lib/cue.language.jar +0 -0
- data/lib/jsoup-1.7.2.jar +0 -0
- data/vendors/Rakefile +0 -51
@@ -0,0 +1,54 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import processing.core.PApplet;
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Sizers contains pre-made {@link WordSizer} implementations that you might
|
7
|
+
* find useful. Right now, it's only {@link Sizers#byWeight(int, int)} and
|
8
|
+
* {@link Sizers#byRank(int, int)}.
|
9
|
+
* @see WordSizer
|
10
|
+
*/
|
11
|
+
public class Sizers {
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Returns a WordSizer that sizes words by their weight, where the
|
15
|
+
* "heaviest" word will be sized at <code>maxSize</code>.
|
16
|
+
* <p>
|
17
|
+
* To be specific, the font size for each word will be calculated with:
|
18
|
+
*
|
19
|
+
* <pre>
|
20
|
+
* PApplet.lerp(minSize, maxSize, (float) word.weight)
|
21
|
+
* </pre>
|
22
|
+
*
|
23
|
+
* @param minSize
|
24
|
+
* the size to draw a Word with weight = 0
|
25
|
+
* @param maxSize
|
26
|
+
* the size to draw a Word with weight = 1
|
27
|
+
* @return the WordSizer
|
28
|
+
*/
|
29
|
+
public static WordSizer byWeight(final int minSize, final int maxSize) {
|
30
|
+
return (Word word, int wordRank, int wordCount) -> PApplet.lerp(minSize, maxSize, word.weight);
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Returns a WordSizer that sizes words by their rank. The first word will
|
35
|
+
* be sized at <code>maxSize</code>.
|
36
|
+
* <p>
|
37
|
+
* To be specific, the font size for each word will be calculated with:
|
38
|
+
*
|
39
|
+
* <pre>
|
40
|
+
* PApplet.map(wordRank, 0, wordCount, maxSize, minSize)
|
41
|
+
* </pre>
|
42
|
+
*
|
43
|
+
* @param minSize
|
44
|
+
* the size to draw the last Word
|
45
|
+
* @param maxSize
|
46
|
+
* the size to draw the first Word
|
47
|
+
* @return the WordSizer
|
48
|
+
*/
|
49
|
+
public static WordSizer byRank(final int minSize, final int maxSize) {
|
50
|
+
return (Word word, int wordRank, int wordCount) -> PApplet.map(wordRank, 0, wordCount, maxSize, minSize);
|
51
|
+
}
|
52
|
+
|
53
|
+
// TODO try exponent scales, rather than linear.
|
54
|
+
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import java.lang.reflect.InvocationTargetException;
|
4
|
+
import java.lang.reflect.Method;
|
5
|
+
import java.util.HashMap;
|
6
|
+
import processing.core.PApplet;
|
7
|
+
|
8
|
+
// This is a healthy rip-off of
|
9
|
+
// https://github.com/processing/processing/wiki/Library-Basics#adding-your-own-library-events
|
10
|
+
|
11
|
+
class SketchCallbackObserver implements Observer {
|
12
|
+
PApplet parent;
|
13
|
+
|
14
|
+
HashMap<String, Method> sketchMethods = new HashMap<>();
|
15
|
+
|
16
|
+
SketchCallbackObserver(PApplet parent) {
|
17
|
+
|
18
|
+
this.parent = parent;
|
19
|
+
|
20
|
+
registerSketchMethod("wordsCounted", Word[].class);
|
21
|
+
registerSketchMethod("beginDraw");
|
22
|
+
registerSketchMethod("wordDrawn", Word.class);
|
23
|
+
registerSketchMethod("wordSkipped", Word.class);
|
24
|
+
registerSketchMethod("endDraw");
|
25
|
+
}
|
26
|
+
|
27
|
+
@Override
|
28
|
+
public void wordsCounted(Word[] words) {
|
29
|
+
invoke("wordsCounted", new Object[] { words });
|
30
|
+
}
|
31
|
+
@Override
|
32
|
+
public void beginDraw() {
|
33
|
+
invoke("beginDraw", new Object[0]);
|
34
|
+
}
|
35
|
+
@Override
|
36
|
+
public void wordDrawn(Word word) {
|
37
|
+
invoke("wordDrawn", new Object[] { word });
|
38
|
+
}
|
39
|
+
@Override
|
40
|
+
public void wordSkipped(Word word) {
|
41
|
+
invoke("wordSkipped", new Object[] { word });
|
42
|
+
}
|
43
|
+
@Override
|
44
|
+
public void endDraw() {
|
45
|
+
invoke("endDraw", new Object[0]);
|
46
|
+
}
|
47
|
+
|
48
|
+
private void registerSketchMethod(String name, Class... parameterTypes) {
|
49
|
+
try {
|
50
|
+
Method method = parent.getClass().getMethod(name, parameterTypes);
|
51
|
+
sketchMethods.put(name, method);
|
52
|
+
}
|
53
|
+
catch (NoSuchMethodException | SecurityException e) {
|
54
|
+
// The sketch doesn't have this method name. No worries.
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
private void invoke(String name, Object[] arguments) {
|
59
|
+
if (sketchMethods.containsKey(name)) {
|
60
|
+
Method method = sketchMethods.get(name);
|
61
|
+
try {
|
62
|
+
method.invoke(parent, arguments);
|
63
|
+
}
|
64
|
+
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
65
|
+
System.err.println("Disabling method " + name + " because of an error.");
|
66
|
+
sketchMethods.remove(name);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import processing.core.PApplet;
|
4
|
+
import processing.core.PVector;
|
5
|
+
|
6
|
+
public class SpiralWordNudger implements WordNudger {
|
7
|
+
|
8
|
+
// Who knows? this seems to be good, but it seems to depend on the font --
|
9
|
+
// bigger fonts need a bigger thetaIncrement.
|
10
|
+
private float thetaIncrement = (float) (Math.PI * 0.03);
|
11
|
+
|
12
|
+
@Override
|
13
|
+
public PVector nudgeFor(Word w, int attempt) {
|
14
|
+
float rad = powerMap(0.6f, attempt, 0, 600, 1, 100);
|
15
|
+
|
16
|
+
thetaIncrement = powerMap(1, attempt, 0, 600, 0.5f, 0.3f);
|
17
|
+
float theta = thetaIncrement * attempt;
|
18
|
+
float x = PApplet.cos(theta) * rad;
|
19
|
+
float y = PApplet.sin(theta) * rad;
|
20
|
+
return new PVector(x, y);
|
21
|
+
}
|
22
|
+
|
23
|
+
private float powerMap(float power, float v, float min1, float max1,
|
24
|
+
float min2, float max2) {
|
25
|
+
|
26
|
+
float val = PApplet.norm(v, min1, max1);
|
27
|
+
val = PApplet.pow(val, power);
|
28
|
+
return PApplet.lerp(min2, max2, val);
|
29
|
+
}
|
30
|
+
|
31
|
+
}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import java.awt.Color; // awt: color->hex
|
4
|
+
import java.awt.Shape; // awt: shape->path
|
5
|
+
import java.awt.geom.PathIterator; // awt: path->svg
|
6
|
+
import java.awt.geom.Path2D; // awt: path->svg
|
7
|
+
import java.io.PrintWriter;
|
8
|
+
import java.io.FileNotFoundException;
|
9
|
+
|
10
|
+
class SvgWordRenderer implements WordRenderer {
|
11
|
+
private final PrintWriter out;
|
12
|
+
private final int width;
|
13
|
+
private final int height;
|
14
|
+
|
15
|
+
SvgWordRenderer(String filename, int width, int height) throws FileNotFoundException {
|
16
|
+
this.out = new PrintWriter(filename);
|
17
|
+
this.width = width;
|
18
|
+
this.height = height;
|
19
|
+
|
20
|
+
pl("<?xml version=\"1.0\"?>");
|
21
|
+
pl("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">");
|
22
|
+
// TODO add wordcram metadata
|
23
|
+
pl("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"" + width + "\" height=\"" + height + "\" fill-rule=\"evenodd\">"); // or nonzero
|
24
|
+
}
|
25
|
+
|
26
|
+
@Override
|
27
|
+
public int getWidth() {
|
28
|
+
return this.width;
|
29
|
+
}
|
30
|
+
|
31
|
+
@Override
|
32
|
+
public int getHeight() {
|
33
|
+
return this.height;
|
34
|
+
}
|
35
|
+
|
36
|
+
@Override
|
37
|
+
public void drawWord(EngineWord word, Color color) {
|
38
|
+
// TODO add word metadata
|
39
|
+
pl("<g style=\"fill:" + getColor(color) + "; stroke-width:0px\">");
|
40
|
+
renderShape(word.getShape());
|
41
|
+
pl("</g>");
|
42
|
+
}
|
43
|
+
|
44
|
+
@Override
|
45
|
+
public void finish() {
|
46
|
+
pl("</svg>");
|
47
|
+
out.flush();
|
48
|
+
out.close();
|
49
|
+
}
|
50
|
+
|
51
|
+
private String getColor(Color color) {
|
52
|
+
// rgb(30, 200, 90)
|
53
|
+
// #ff0023
|
54
|
+
return "#" + decToHex(color.getRed()) + decToHex(color.getGreen()) + decToHex(color.getBlue());
|
55
|
+
}
|
56
|
+
|
57
|
+
private String decToHex(int dec) {
|
58
|
+
return dec > 16 ? Integer.toHexString(dec) :
|
59
|
+
"0" + Integer.toHexString(dec);
|
60
|
+
}
|
61
|
+
|
62
|
+
private void renderShape(Shape shape) {
|
63
|
+
|
64
|
+
Path2D.Double path2d = new Path2D.Double(shape);
|
65
|
+
path2d.setWindingRule(Path2D.WIND_EVEN_ODD); // or WIND_NON_ZERO
|
66
|
+
PathIterator pathIter = path2d.getPathIterator(null);
|
67
|
+
|
68
|
+
float[] coords = new float[6];
|
69
|
+
|
70
|
+
p("<path d=\"");
|
71
|
+
while (!pathIter.isDone()) {
|
72
|
+
|
73
|
+
int type = pathIter.currentSegment(coords);
|
74
|
+
|
75
|
+
switch(type) {
|
76
|
+
case PathIterator.SEG_MOVETO:
|
77
|
+
p("M" + coords[0] + " " + coords[1]);
|
78
|
+
break;
|
79
|
+
|
80
|
+
case PathIterator.SEG_LINETO:
|
81
|
+
p("L" + coords[0] + " " + coords[1]);
|
82
|
+
break;
|
83
|
+
|
84
|
+
case PathIterator.SEG_QUADTO:
|
85
|
+
p("Q" + coords[0] + " " + coords[1] + " " + coords[2] + " " + coords[3]);
|
86
|
+
break;
|
87
|
+
|
88
|
+
case PathIterator.SEG_CUBICTO:
|
89
|
+
p("C" + coords[0] + " " + coords[1] + " " + coords[2] + " " + coords[3] + " " + coords[4] + " " + coords[5]);
|
90
|
+
break;
|
91
|
+
|
92
|
+
case PathIterator.SEG_CLOSE:
|
93
|
+
p("Z");
|
94
|
+
break;
|
95
|
+
}
|
96
|
+
|
97
|
+
pathIter.next();
|
98
|
+
}
|
99
|
+
|
100
|
+
pl("\"/>");
|
101
|
+
}
|
102
|
+
|
103
|
+
private void pl(String s) {
|
104
|
+
out.println(s);
|
105
|
+
}
|
106
|
+
|
107
|
+
private void p(String s) {
|
108
|
+
out.print(s);
|
109
|
+
}
|
110
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import processing.core.*;
|
4
|
+
|
5
|
+
public class SwirlWordPlacer implements WordPlacer {
|
6
|
+
|
7
|
+
@Override
|
8
|
+
public PVector place(Word word, int wordIndex, int wordsCount,
|
9
|
+
int wordImageWidth, int wordImageHeight, int fieldWidth,
|
10
|
+
int fieldHeight) {
|
11
|
+
|
12
|
+
float normalizedIndex = (float) wordIndex / wordsCount;
|
13
|
+
|
14
|
+
float theta = normalizedIndex * 6 * PConstants.TWO_PI;
|
15
|
+
float radius = normalizedIndex * fieldWidth / 2f;
|
16
|
+
|
17
|
+
float centerX = fieldWidth * 0.5f;
|
18
|
+
float centerY = fieldHeight * 0.5f;
|
19
|
+
|
20
|
+
float x = PApplet.cos(theta) * radius;
|
21
|
+
float y = PApplet.sin(theta) * radius;
|
22
|
+
|
23
|
+
return new PVector(centerX + x, centerY + y);
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import java.util.Random;
|
4
|
+
|
5
|
+
import processing.core.PApplet;
|
6
|
+
import processing.core.PVector;
|
7
|
+
|
8
|
+
public class UpperLeftWordPlacer implements WordPlacer {
|
9
|
+
|
10
|
+
private final Random r = new Random();
|
11
|
+
|
12
|
+
@Override
|
13
|
+
public PVector place(Word word, int wordIndex, int wordsCount, int wordImageWidth, int wordImageHeight, int fieldWidth, int fieldHeight) {
|
14
|
+
int x = getOneUnder(fieldWidth - wordImageWidth);
|
15
|
+
int y = getOneUnder(fieldHeight - wordImageHeight);
|
16
|
+
return new PVector(x, y);
|
17
|
+
}
|
18
|
+
|
19
|
+
private int getOneUnder(int limit) {
|
20
|
+
return PApplet.round(PApplet.map(random(random(random(random(random(1.0f))))), 0, 1, 0, limit));
|
21
|
+
}
|
22
|
+
|
23
|
+
private float random(float limit) {
|
24
|
+
return r.nextFloat() * limit;
|
25
|
+
}
|
26
|
+
|
27
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import processing.core.*;
|
4
|
+
|
5
|
+
public class WaveWordPlacer implements WordPlacer {
|
6
|
+
|
7
|
+
@Override
|
8
|
+
public PVector place(Word word, int wordIndex, int wordsCount,
|
9
|
+
int wordImageWidth, int wordImageHeight, int fieldWidth,
|
10
|
+
int fieldHeight) {
|
11
|
+
|
12
|
+
float normalizedIndex = (float) wordIndex / wordsCount;
|
13
|
+
float x = normalizedIndex * fieldWidth;
|
14
|
+
float y = normalizedIndex * fieldHeight;
|
15
|
+
|
16
|
+
float yOffset = getYOffset(wordIndex, wordsCount, fieldHeight);
|
17
|
+
return new PVector(x, y + yOffset);
|
18
|
+
}
|
19
|
+
|
20
|
+
private float getYOffset(int wordIndex, int wordsCount, int fieldHeight) {
|
21
|
+
float theta = PApplet.map(wordIndex, 0, wordsCount, PConstants.PI, -PConstants.PI);
|
22
|
+
|
23
|
+
return (float) Math.sin(theta) * (fieldHeight / 3f);
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,357 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import java.util.HashMap;
|
4
|
+
|
5
|
+
import processing.core.PFont;
|
6
|
+
import processing.core.PVector;
|
7
|
+
|
8
|
+
/**
|
9
|
+
* A weighted word, for rendering in the word cloud image.
|
10
|
+
* <p>
|
11
|
+
* Each Word object has a {@link #word} String, and its associated {@link #weight}, and it's constructed
|
12
|
+
* with these two things.
|
13
|
+
*
|
14
|
+
*
|
15
|
+
* <h3>Hand-crafting Your Words</h3>
|
16
|
+
*
|
17
|
+
* If you're creating your own <code>Word[]</code> to pass
|
18
|
+
* to the WordCram (rather than using something like {@link WordCram#fromWebPage(String)}),
|
19
|
+
* you can specify how a Word should be rendered: set a Word's font, size, etc:
|
20
|
+
*
|
21
|
+
* <pre>
|
22
|
+
* Word w = new Word("texty", 20);
|
23
|
+
* w.setFont(createFont("myFontName", 1));
|
24
|
+
* w.setAngle(radians(45));
|
25
|
+
* </pre>
|
26
|
+
*
|
27
|
+
* Any values set on a Word will override the corresponding component ({@link WordColorer},
|
28
|
+
* {@link WordAngler}, etc) - it won't even be called for that word.
|
29
|
+
*
|
30
|
+
* <h3>Word Properties</h3>
|
31
|
+
* A word can also have properties. If you're creating custom components,
|
32
|
+
* you might want to send other information along with the word, for the components to use:
|
33
|
+
*
|
34
|
+
* <pre>
|
35
|
+
* Word strawberry = new Word("strawberry", 10);
|
36
|
+
* strawberry.setProperty("isFruit", true);
|
37
|
+
*
|
38
|
+
* Word pea = new Word("pea", 10);
|
39
|
+
* pea.setProperty("isFruit", false);
|
40
|
+
*
|
41
|
+
* new WordCram(this)
|
42
|
+
* .fromWords(new Word[] { strawberry, pea })
|
43
|
+
* .withColorer(new WordColorer() {
|
44
|
+
* public int colorFor(Word w) {
|
45
|
+
* if (w.getProperty("isFruit") == true) {
|
46
|
+
* return color(255, 0, 0);
|
47
|
+
* }
|
48
|
+
* else {
|
49
|
+
* return color(0, 200, 0);
|
50
|
+
* }
|
51
|
+
* }
|
52
|
+
* });
|
53
|
+
* </pre>
|
54
|
+
*
|
55
|
+
* @author Dan Bernier
|
56
|
+
*/
|
57
|
+
public class Word implements Comparable<Word> {
|
58
|
+
|
59
|
+
public String word;
|
60
|
+
public float weight;
|
61
|
+
|
62
|
+
private Float presetSize;
|
63
|
+
private Float presetAngle;
|
64
|
+
private PFont presetFont;
|
65
|
+
private Integer presetColor;
|
66
|
+
private PVector presetTargetPlace;
|
67
|
+
|
68
|
+
// These are null until they're rendered, and can be wiped out for a re-render.
|
69
|
+
private Float renderedSize;
|
70
|
+
private Float renderedAngle;
|
71
|
+
private PFont renderedFont;
|
72
|
+
private Integer renderedColor;
|
73
|
+
private PVector targetPlace;
|
74
|
+
private PVector renderedPlace;
|
75
|
+
private WordSkipReason skippedBecause;
|
76
|
+
|
77
|
+
private final HashMap<String,Object> properties = new HashMap<>();
|
78
|
+
|
79
|
+
public Word(String word, float weight) {
|
80
|
+
this.word = word;
|
81
|
+
this.weight = weight;
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Set the size this Word should be rendered at - WordCram won't even call the WordSizer.
|
86
|
+
* @param size
|
87
|
+
* @return the Word, for more configuration
|
88
|
+
*/
|
89
|
+
public Word setSize(float size) {
|
90
|
+
this.presetSize = size;
|
91
|
+
return this;
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Set the angle this Word should be rendered at - WordCram won't even call the WordAngler.
|
96
|
+
* @param angle
|
97
|
+
* @return the Word, for more configuration
|
98
|
+
*/
|
99
|
+
public Word setAngle(float angle) {
|
100
|
+
this.presetAngle = angle;
|
101
|
+
return this;
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* Set the font this Word should be rendered in - WordCram won't call the WordFonter.
|
106
|
+
* @param font
|
107
|
+
* @return the Word, for more configuration
|
108
|
+
*/
|
109
|
+
public Word setFont(PFont font) { // TODO provide a string overload? Will need the PApplet...
|
110
|
+
this.presetFont = font;
|
111
|
+
return this;
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Set the color this Word should be rendered in - WordCram won't call the WordColorer.
|
116
|
+
* @param color
|
117
|
+
* @return the Word, for more configuration
|
118
|
+
*/
|
119
|
+
public Word setColor(int color) {
|
120
|
+
this.presetColor = color;
|
121
|
+
return this;
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Set the place this Word should be rendered at - WordCram won't call the WordPlacer.
|
126
|
+
* @param place
|
127
|
+
* @return the Word, for more configuration
|
128
|
+
*/
|
129
|
+
public Word setPlace(PVector place) {
|
130
|
+
this.presetTargetPlace = place.copy();
|
131
|
+
return this;
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Set the place this Word should be rendered at - WordCram won't call the WordPlacer.
|
136
|
+
* @param x
|
137
|
+
* @param y
|
138
|
+
* @return the Word, for more configuration
|
139
|
+
*/
|
140
|
+
public Word setPlace(float x, float y) {
|
141
|
+
this.presetTargetPlace = new PVector(x, y);
|
142
|
+
return this;
|
143
|
+
}
|
144
|
+
|
145
|
+
/*
|
146
|
+
* These methods are called by EngineWord: they return (for instance)
|
147
|
+
* either the color the user set via setColor(), or the value returned
|
148
|
+
* by the WordColorer. They're package-local, so they can't be called by the sketch.
|
149
|
+
*/
|
150
|
+
|
151
|
+
Float getSize(WordSizer sizer, int rank, int wordCount) {
|
152
|
+
renderedSize = presetSize != null ? presetSize : sizer.sizeFor(this, rank, wordCount);
|
153
|
+
return renderedSize;
|
154
|
+
}
|
155
|
+
|
156
|
+
Float getAngle(WordAngler angler) {
|
157
|
+
renderedAngle = presetAngle != null ? presetAngle : angler.angleFor(this);
|
158
|
+
return renderedAngle;
|
159
|
+
}
|
160
|
+
|
161
|
+
PFont getFont(WordFonter fonter) {
|
162
|
+
renderedFont = presetFont != null ? presetFont : fonter.fontFor(this);
|
163
|
+
return renderedFont;
|
164
|
+
}
|
165
|
+
|
166
|
+
Integer getColor(WordColorer colorer) {
|
167
|
+
renderedColor = presetColor != null ? presetColor : colorer.colorFor(this);
|
168
|
+
return renderedColor;
|
169
|
+
}
|
170
|
+
|
171
|
+
PVector getTargetPlace(WordPlacer placer, int rank, int count, int wordImageWidth, int wordImageHeight, int fieldWidth, int fieldHeight) {
|
172
|
+
targetPlace = presetTargetPlace != null ? presetTargetPlace : placer.place(this, rank, count, wordImageWidth, wordImageHeight, fieldWidth, fieldHeight);
|
173
|
+
return targetPlace;
|
174
|
+
}
|
175
|
+
|
176
|
+
void setRenderedPlace(PVector place) {
|
177
|
+
renderedPlace = place.copy();
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Get the size the Word was rendered at: either the value passed to setSize(), or the value returned from the WordSizer.
|
182
|
+
* @return the rendered size
|
183
|
+
*/
|
184
|
+
public float getRenderedSize() {
|
185
|
+
return renderedSize;
|
186
|
+
}
|
187
|
+
|
188
|
+
/**
|
189
|
+
* Get the angle the Word was rendered at: either the value passed to setAngle(), or the value returned from the WordAngler.
|
190
|
+
* @return the rendered angle
|
191
|
+
*/
|
192
|
+
public float getRenderedAngle() {
|
193
|
+
return renderedAngle;
|
194
|
+
}
|
195
|
+
|
196
|
+
/**
|
197
|
+
* Get the font the Word was rendered in: either the value passed to setFont(), or the value returned from the WordFonter.
|
198
|
+
* @return the rendered font
|
199
|
+
*/
|
200
|
+
public PFont getRenderedFont() {
|
201
|
+
return renderedFont;
|
202
|
+
}
|
203
|
+
|
204
|
+
/**
|
205
|
+
* Get the color the Word was rendered in: either the value passed to setColor(), or the value returned from the WordColorer.
|
206
|
+
* @return the rendered color
|
207
|
+
*/
|
208
|
+
public int getRenderedColor() {
|
209
|
+
return renderedColor;
|
210
|
+
}
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Get the place the Word was supposed to be rendered at: either the value passed to setPlace(),
|
214
|
+
* or the value returned from the WordPlacer.
|
215
|
+
* @return
|
216
|
+
*/
|
217
|
+
public PVector getTargetPlace() {
|
218
|
+
return targetPlace;
|
219
|
+
}
|
220
|
+
|
221
|
+
/**
|
222
|
+
* Get the final place the Word was rendered at, or null if it couldn't be placed.
|
223
|
+
* It returns the original target location (which is either the value passed to setPlace(),
|
224
|
+
* or the value returned from the WordPlacer), plus the nudge vector returned by the WordNudger.
|
225
|
+
* @return If word was placed, it's the (x,y) coordinates of the word's final location; else it's null.
|
226
|
+
*/
|
227
|
+
public PVector getRenderedPlace() {
|
228
|
+
return renderedPlace;
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Indicates whether the Word was placed successfully. It's the same as calling word.getRenderedPlace() != null.
|
233
|
+
* If this returns false, it's either because a) WordCram didn't get to this Word yet,
|
234
|
+
* or b) it was skipped for some reason (see {@link #wasSkipped()} and {@link #wasSkippedBecause()}).
|
235
|
+
* @return true only if the word was placed.
|
236
|
+
*/
|
237
|
+
public boolean wasPlaced() {
|
238
|
+
return renderedPlace != null;
|
239
|
+
}
|
240
|
+
|
241
|
+
/**
|
242
|
+
* Indicates whether the Word was skipped.
|
243
|
+
* @see Word#wasSkippedBecause()
|
244
|
+
* @return true if the word was skipped
|
245
|
+
*/
|
246
|
+
public boolean wasSkipped() {
|
247
|
+
return wasSkippedBecause() != null;
|
248
|
+
}
|
249
|
+
|
250
|
+
/**
|
251
|
+
* Tells you why this Word was skipped.
|
252
|
+
*
|
253
|
+
* If the word was skipped, this will return a {@link WordSkipReason}
|
254
|
+
* explaining why.
|
255
|
+
*
|
256
|
+
* If the word was successfully placed, or WordCram hasn't
|
257
|
+
* gotten to this word yet, this will return null.
|
258
|
+
*
|
259
|
+
* @return the {@link WordSkipReason} why the word was skipped, or null if
|
260
|
+
* it wasn't skipped.
|
261
|
+
*/
|
262
|
+
public WordSkipReason wasSkippedBecause() {
|
263
|
+
return skippedBecause;
|
264
|
+
}
|
265
|
+
|
266
|
+
void wasSkippedBecause(WordSkipReason reason) {
|
267
|
+
skippedBecause = reason;
|
268
|
+
}
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Get a property value from this Word, for a WordColorer, a WordPlacer, etc.
|
272
|
+
* @param propertyName
|
273
|
+
* @return the value of the property, or <code>null</code>, if it's not there.
|
274
|
+
*/
|
275
|
+
public Object getProperty(String propertyName) {
|
276
|
+
return properties.get(propertyName);
|
277
|
+
}
|
278
|
+
|
279
|
+
/**
|
280
|
+
* Set a property on this Word, to be used by a WordColorer, a WordPlacer, etc, down the line.
|
281
|
+
* @param propertyName
|
282
|
+
* @param propertyValue
|
283
|
+
* @return the Word, for more configuration
|
284
|
+
*/
|
285
|
+
public Word setProperty(String propertyName, Object propertyValue) {
|
286
|
+
properties.put(propertyName, propertyValue);
|
287
|
+
return this;
|
288
|
+
}
|
289
|
+
|
290
|
+
/**
|
291
|
+
* Displays the word, and its weight (in parentheses).
|
292
|
+
* <code>new Word("hello", 1.3).toString()</code> will return "hello (0.3)".
|
293
|
+
*/
|
294
|
+
@Override
|
295
|
+
public String toString() {
|
296
|
+
String status = "";
|
297
|
+
if (wasPlaced()) {
|
298
|
+
status = renderedPlace.x + "," + renderedPlace.y;
|
299
|
+
}
|
300
|
+
else if (wasSkipped()) {
|
301
|
+
status = skippedBecause.toString();
|
302
|
+
}
|
303
|
+
if (status.length() != 0) {
|
304
|
+
status = " [" + status + "]";
|
305
|
+
}
|
306
|
+
return word + " (" + weight + ")" + status;
|
307
|
+
}
|
308
|
+
|
309
|
+
/**
|
310
|
+
* Compares Words based on weight only. Words with equal weight are arbitrarily sorted.
|
311
|
+
* @param w
|
312
|
+
*/
|
313
|
+
@Override
|
314
|
+
public int compareTo(Word w) {
|
315
|
+
if (w.weight < weight) {
|
316
|
+
return -1;
|
317
|
+
}
|
318
|
+
else if (w.weight > weight) {
|
319
|
+
return 1;
|
320
|
+
}
|
321
|
+
else return 0;
|
322
|
+
}
|
323
|
+
|
324
|
+
// Note: these are only so we can delegate to EngineWord for getShape().
|
325
|
+
private EngineWord engineWord;
|
326
|
+
void setEngineWord(EngineWord engineWord) {
|
327
|
+
this.engineWord = engineWord;
|
328
|
+
}
|
329
|
+
|
330
|
+
|
331
|
+
// These are down here, because it comes from the EngineWord, *not* from the Fonter, Angler, etc.
|
332
|
+
/**
|
333
|
+
* Gets the width, in pixels, of the java.awt.Shape that will be rendered for
|
334
|
+
* this Word, based on the Font, Angle, and Size for this Word.
|
335
|
+
* If that all hasn't been figured out yet, then this returns 0.
|
336
|
+
* @return The width in pixels of this Word's Shape, or 0, if it hasn't
|
337
|
+
* been rendered yet.
|
338
|
+
* @see #getRenderedHeight()
|
339
|
+
*/
|
340
|
+
public float getRenderedWidth() {
|
341
|
+
if (engineWord == null) return 0.0f;
|
342
|
+
return (float)engineWord.getShape().getBounds2D().getWidth();
|
343
|
+
}
|
344
|
+
|
345
|
+
/**
|
346
|
+
* Gets the height, in pixels, of the java.awt.Shape that will be rendered for
|
347
|
+
* this Word, based on the Font, Angle, and Size for this Word.
|
348
|
+
* If that all hasn't been figured out yet, then this returns 0.
|
349
|
+
* @return The height in pixels of this Word's Shape, or 0, if it hasn't
|
350
|
+
* been rendered yet.
|
351
|
+
* @see #getRenderedWidth()
|
352
|
+
*/
|
353
|
+
public float getRenderedHeight() {
|
354
|
+
if (engineWord == null) return 0.0f;
|
355
|
+
return (float)engineWord.getShape().getBounds2D().getHeight();
|
356
|
+
}
|
357
|
+
}
|