ruby_wordcram 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.mvn/extensions.xml +8 -0
  4. data/.mvn/wrapper/maven-wrapper.properties +1 -0
  5. data/Rakefile +28 -5
  6. data/docs/_posts/2017-03-07-getting_started.md +3 -2
  7. data/docs/_posts/2017-03-07-under_the_hood.md +33 -0
  8. data/lib/WordCram.jar +0 -0
  9. data/lib/jsoup-1.10.2.jar +0 -0
  10. data/lib/ruby_wordcram/version.rb +1 -1
  11. data/lib/ruby_wordcram.rb +1 -2
  12. data/pom.rb +53 -0
  13. data/pom.xml +87 -0
  14. data/ruby_wordcram.gemspec +1 -2
  15. data/src/cue/lang/Counter.java +141 -0
  16. data/src/cue/lang/IterableText.java +10 -0
  17. data/src/cue/lang/NGramIterator.java +151 -0
  18. data/src/cue/lang/SentenceIterator.java +86 -0
  19. data/src/cue/lang/WordIterator.java +60 -0
  20. data/src/cue/lang/stop/StopWords.java +114 -0
  21. data/src/cue/lang/stop/arabic +351 -0
  22. data/src/cue/lang/stop/armenian +45 -0
  23. data/src/cue/lang/stop/catalan +219 -0
  24. data/src/cue/lang/stop/croatian +2024 -0
  25. data/src/cue/lang/stop/czech +256 -0
  26. data/src/cue/lang/stop/danish +94 -0
  27. data/src/cue/lang/stop/dutch +107 -0
  28. data/src/cue/lang/stop/english +183 -0
  29. data/src/cue/lang/stop/esperanto +180 -0
  30. data/src/cue/lang/stop/farsi +966 -0
  31. data/src/cue/lang/stop/finnish +235 -0
  32. data/src/cue/lang/stop/french +543 -0
  33. data/src/cue/lang/stop/german +231 -0
  34. data/src/cue/lang/stop/greek +637 -0
  35. data/src/cue/lang/stop/hebrew +220 -0
  36. data/src/cue/lang/stop/hindi +97 -0
  37. data/src/cue/lang/stop/hungarian +202 -0
  38. data/src/cue/lang/stop/italian +279 -0
  39. data/src/cue/lang/stop/latin +1 -0
  40. data/src/cue/lang/stop/norwegian +176 -0
  41. data/src/cue/lang/stop/polish +138 -0
  42. data/src/cue/lang/stop/portuguese +204 -0
  43. data/src/cue/lang/stop/romanian +284 -0
  44. data/src/cue/lang/stop/russian +652 -0
  45. data/src/cue/lang/stop/slovak +110 -0
  46. data/src/cue/lang/stop/slovenian +448 -0
  47. data/src/cue/lang/stop/spanish +308 -0
  48. data/src/cue/lang/stop/swedish +114 -0
  49. data/src/cue/lang/stop/turkish +117 -0
  50. data/src/cue/lang/unicode/BlockUtil.java +103 -0
  51. data/src/cue/lang/unicode/Normalizer.java +55 -0
  52. data/src/cue/lang/unicode/Normalizer6.java +32 -0
  53. data/src/license.txt +201 -0
  54. data/src/wordcram/Anglers.java +137 -0
  55. data/src/wordcram/BBTree.java +133 -0
  56. data/src/wordcram/BBTreeBuilder.java +61 -0
  57. data/src/wordcram/Colorers.java +52 -0
  58. data/src/wordcram/EngineWord.java +73 -0
  59. data/src/wordcram/Fonters.java +17 -0
  60. data/src/wordcram/HsbWordColorer.java +28 -0
  61. data/src/wordcram/ImageShaper.java +91 -0
  62. data/src/wordcram/Observer.java +9 -0
  63. data/src/wordcram/PlacerHeatMap.java +134 -0
  64. data/src/wordcram/Placers.java +74 -0
  65. data/src/wordcram/PlottingWordNudger.java +38 -0
  66. data/src/wordcram/PlottingWordPlacer.java +36 -0
  67. data/src/wordcram/ProcessingWordRenderer.java +42 -0
  68. data/src/wordcram/RandomWordNudger.java +44 -0
  69. data/src/wordcram/RenderOptions.java +10 -0
  70. data/src/wordcram/ShapeBasedPlacer.java +66 -0
  71. data/src/wordcram/Sizers.java +54 -0
  72. data/src/wordcram/SketchCallbackObserver.java +70 -0
  73. data/src/wordcram/SpiralWordNudger.java +31 -0
  74. data/src/wordcram/SvgWordRenderer.java +110 -0
  75. data/src/wordcram/SwirlWordPlacer.java +25 -0
  76. data/src/wordcram/UpperLeftWordPlacer.java +27 -0
  77. data/src/wordcram/WaveWordPlacer.java +25 -0
  78. data/src/wordcram/Word.java +357 -0
  79. data/src/wordcram/WordAngler.java +20 -0
  80. data/src/wordcram/WordArray.java +18 -0
  81. data/src/wordcram/WordBag.java +31 -0
  82. data/src/wordcram/WordColorer.java +25 -0
  83. data/src/wordcram/WordCounter.java +96 -0
  84. data/src/wordcram/WordCram.java +920 -0
  85. data/src/wordcram/WordCramEngine.java +196 -0
  86. data/src/wordcram/WordFonter.java +24 -0
  87. data/src/wordcram/WordNudger.java +44 -0
  88. data/src/wordcram/WordPlacer.java +44 -0
  89. data/src/wordcram/WordRenderer.java +10 -0
  90. data/src/wordcram/WordShaper.java +78 -0
  91. data/src/wordcram/WordSizer.java +46 -0
  92. data/src/wordcram/WordSkipReason.java +42 -0
  93. data/src/wordcram/WordSorterAndScaler.java +31 -0
  94. data/src/wordcram/WordSource.java +5 -0
  95. data/src/wordcram/text/Html.java +15 -0
  96. data/src/wordcram/text/Html2Text.java +17 -0
  97. data/src/wordcram/text/Text.java +15 -0
  98. data/src/wordcram/text/TextFile.java +23 -0
  99. data/src/wordcram/text/TextSource.java +5 -0
  100. data/src/wordcram/text/WebPage.java +23 -0
  101. metadata +94 -5
  102. data/lib/cue.language.jar +0 -0
  103. data/lib/jsoup-1.7.2.jar +0 -0
  104. 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
+ }