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.
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
+ }