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,20 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* A WordAngler tells WordCram what angle to draw a word at, in radians.
|
5
|
+
* <p>
|
6
|
+
* Some useful implementations are available in {@link Anglers}.
|
7
|
+
*
|
8
|
+
* @author Dan Bernier
|
9
|
+
*/
|
10
|
+
public interface WordAngler {
|
11
|
+
|
12
|
+
/**
|
13
|
+
* What angle should this {@link Word} be rotated at?
|
14
|
+
*
|
15
|
+
* @param word
|
16
|
+
* The Word that WordCram is about to draw, and wants to rotate
|
17
|
+
* @return the rotation angle for the Word, in radians
|
18
|
+
*/
|
19
|
+
public float angleFor(Word word);
|
20
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
/*
|
4
|
+
* This is just here so WordCram.fromWords(Word[]) has something to use.
|
5
|
+
*/
|
6
|
+
|
7
|
+
class WordArray implements WordSource {
|
8
|
+
Word[] words;
|
9
|
+
|
10
|
+
WordArray(Word[] words) {
|
11
|
+
this.words = words;
|
12
|
+
}
|
13
|
+
|
14
|
+
@Override
|
15
|
+
public Word[] getWords() {
|
16
|
+
return words;
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
public class WordBag implements WordSource {
|
4
|
+
|
5
|
+
int numWords;
|
6
|
+
String[] wordStrings;
|
7
|
+
double weightDistributionPower = 2;
|
8
|
+
|
9
|
+
public WordBag(int numWords, String... wordStrings) {
|
10
|
+
this.numWords = numWords;
|
11
|
+
this.wordStrings = wordStrings;
|
12
|
+
}
|
13
|
+
|
14
|
+
public WordBag weightDistributionPower(float wdp) {
|
15
|
+
this.weightDistributionPower = wdp;
|
16
|
+
return this;
|
17
|
+
}
|
18
|
+
|
19
|
+
@Override
|
20
|
+
public Word[] getWords() {
|
21
|
+
Word[] words = new Word[numWords];
|
22
|
+
java.util.Random rand = new java.util.Random();
|
23
|
+
|
24
|
+
for (int i = 0, wi = 0; i < words.length; i++, wi = (wi + 1) % wordStrings.length) {
|
25
|
+
String word = wordStrings[wi];
|
26
|
+
double weight = Math.pow(rand.nextDouble(), weightDistributionPower);
|
27
|
+
words[i] = new Word(word, (float)weight);
|
28
|
+
}
|
29
|
+
return words;
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* A WordColorer tells WordCram what color to render a word in.
|
5
|
+
* <p>
|
6
|
+
* <b>Note:</b> if you implement your own WordColorer, you should be familiar
|
7
|
+
* with how <a href="http://processing.org/reference/color_datatype.html"
|
8
|
+
* target="blank">Processing represents colors</a> -- or just make sure it uses
|
9
|
+
* Processing's <a href="http://processing.org/reference/color_.html"
|
10
|
+
* target="blank">color</a> method.
|
11
|
+
* <p>
|
12
|
+
* Some useful implementations are available in {@link Colorers}.
|
13
|
+
*
|
14
|
+
* @author Dan Bernier
|
15
|
+
*/
|
16
|
+
public interface WordColorer {
|
17
|
+
|
18
|
+
/**
|
19
|
+
* What color should this {@link Word} be?
|
20
|
+
*
|
21
|
+
* @param word the word to pick the color for
|
22
|
+
* @return the color for the word
|
23
|
+
*/
|
24
|
+
public int colorFor(Word word);
|
25
|
+
}
|
@@ -0,0 +1,96 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import java.util.*;
|
4
|
+
import java.util.Map.Entry;
|
5
|
+
|
6
|
+
import cue.lang.Counter;
|
7
|
+
import cue.lang.WordIterator;
|
8
|
+
import cue.lang.stop.StopWords;
|
9
|
+
|
10
|
+
class WordCounter {
|
11
|
+
|
12
|
+
private StopWords cueStopWords;
|
13
|
+
private Set<String> extraStopWords = new HashSet<>();
|
14
|
+
private boolean excludeNumbers;
|
15
|
+
|
16
|
+
public WordCounter() {
|
17
|
+
this(null);
|
18
|
+
}
|
19
|
+
public WordCounter(StopWords cueStopWords) {
|
20
|
+
this.cueStopWords = cueStopWords;
|
21
|
+
}
|
22
|
+
|
23
|
+
public WordCounter withExtraStopWords(String extraStopWordsString) {
|
24
|
+
String[] stopWordsArray = extraStopWordsString.toLowerCase().split(" ");
|
25
|
+
extraStopWords = new HashSet<>(Arrays.asList(stopWordsArray));
|
26
|
+
return this;
|
27
|
+
}
|
28
|
+
|
29
|
+
public WordCounter shouldExcludeNumbers(boolean shouldExcludeNumbers) {
|
30
|
+
excludeNumbers = shouldExcludeNumbers;
|
31
|
+
return this;
|
32
|
+
}
|
33
|
+
|
34
|
+
public Word[] count(String text, RenderOptions renderOptions) {
|
35
|
+
if (cueStopWords == null) {
|
36
|
+
cueStopWords = StopWords.guess(text);
|
37
|
+
|
38
|
+
if (cueStopWords == StopWords.Arabic ||
|
39
|
+
cueStopWords == StopWords.Farsi ||
|
40
|
+
cueStopWords == StopWords.Hebrew) {
|
41
|
+
renderOptions.rightToLeft = true;
|
42
|
+
}
|
43
|
+
|
44
|
+
tellScripterAboutTheGuess(cueStopWords);
|
45
|
+
}
|
46
|
+
return countWords(text);
|
47
|
+
}
|
48
|
+
|
49
|
+
private void tellScripterAboutTheGuess(StopWords stopWords) {
|
50
|
+
// TODO Find a better way to do this; it prints out during the tests. =p
|
51
|
+
if (stopWords == null) {
|
52
|
+
System.out.println("cue.language can't guess what language your text is in.");
|
53
|
+
} else {
|
54
|
+
System.out.println("cue.language guesses your text is in " + stopWords);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
private Word[] countWords(String text) {
|
59
|
+
Counter<String> counter = new Counter<>();
|
60
|
+
|
61
|
+
for (String word : new WordIterator(text)) {
|
62
|
+
if (shouldCountWord(word)) {
|
63
|
+
counter.note(word);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
List<Word> words = new ArrayList<>();
|
68
|
+
|
69
|
+
counter.entrySet().forEach((entry) -> {
|
70
|
+
words.add(new Word(entry.getKey(), (int)entry.getValue()));
|
71
|
+
});
|
72
|
+
|
73
|
+
return words.toArray(new Word[0]);
|
74
|
+
}
|
75
|
+
|
76
|
+
private boolean shouldCountWord(String word) {
|
77
|
+
return !isStopWord(word) && !(excludeNumbers && isNumeric(word));
|
78
|
+
}
|
79
|
+
|
80
|
+
private boolean isNumeric(String word) {
|
81
|
+
try {
|
82
|
+
Double.parseDouble(word);
|
83
|
+
return true;
|
84
|
+
}
|
85
|
+
catch (NumberFormatException x) {
|
86
|
+
return false;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
private boolean isStopWord(String word) {
|
91
|
+
boolean cueSaysStopWord = cueStopWords != null && cueStopWords.isStopWord(word);
|
92
|
+
boolean extraSaysStopWord = extraStopWords.contains(word.toLowerCase());
|
93
|
+
return cueSaysStopWord || extraSaysStopWord;
|
94
|
+
}
|
95
|
+
|
96
|
+
}
|
@@ -0,0 +1,920 @@
|
|
1
|
+
package wordcram;
|
2
|
+
|
3
|
+
import processing.core.*;
|
4
|
+
import wordcram.text.*;
|
5
|
+
import java.util.ArrayList;
|
6
|
+
|
7
|
+
/**
|
8
|
+
* The main API for WordCram.
|
9
|
+
*
|
10
|
+
* <p>There are three steps to making a WordCram:
|
11
|
+
* <ol>
|
12
|
+
* <li>weight your words
|
13
|
+
* <li>style your words
|
14
|
+
* <li>draw your WordCram
|
15
|
+
* </ol>
|
16
|
+
* You start with a <code>new WordCram(this)</code>, and then...
|
17
|
+
*
|
18
|
+
* <h2>Step One: Weight Your Words</h2>
|
19
|
+
*
|
20
|
+
* Give WordCram some text to chew on, or an array of Words you've
|
21
|
+
* weighted yourself.
|
22
|
+
*
|
23
|
+
* <h3>Let WordCram Weight Your Words</h3>
|
24
|
+
*
|
25
|
+
* <p>WordCram weights your words by the number of times they appear
|
26
|
+
* in a document. It can load the document a few ways:
|
27
|
+
*
|
28
|
+
* <ul>
|
29
|
+
* <li>{@link #fromWebPage(String)} and {@link #fromHtmlFile(String)} load the HTML and scrape out the words</li>
|
30
|
+
* <li>{@link #fromHtmlString(String...)} takes a String (or String[]), assumes it's HTML, and scrapes out its text</li>
|
31
|
+
* <li>{@link #fromTextFile(String)} loads a file (from the filesystem or the network), and counts the words</li>
|
32
|
+
* <li>{@link #fromTextString(String...)} takes a String (or String[]), and counts the words</li>
|
33
|
+
* </ul>
|
34
|
+
*
|
35
|
+
* <p>If you need some other way to load your text, pass your own
|
36
|
+
* TextSource to {@link #fromText(TextSource)}, and WordCram get its
|
37
|
+
* text via {@link TextSource#getText()}.
|
38
|
+
*
|
39
|
+
* <p>Once the text is loaded, you can control how WordCram counts up the words.
|
40
|
+
*
|
41
|
+
* <p><b>Case sensitivity:</b> If your text contains "hello", "HELLO",
|
42
|
+
* and "Hello",
|
43
|
+
*
|
44
|
+
* <ul><li>{@link #lowerCase()} will count them all as "hello"</li>
|
45
|
+
* <li>{@link #upperCase()} will count them all as "HELLO"</li>
|
46
|
+
* <li>{@link #keepCase()}, the default, will count them separately, as three different words</li></ul>
|
47
|
+
*
|
48
|
+
* <p><b>Numbers:</b> If your text contains words like "42" or
|
49
|
+
* "3.14159", you can remove them with {@link #excludeNumbers()} (the
|
50
|
+
* default), or include them with {@link #includeNumbers()}.
|
51
|
+
*
|
52
|
+
* <p><b>Stop words:</b> WordCram uses <a
|
53
|
+
* href="https://github.com/jdf/cue.language">cue.language</a> to remove common words from the text by default, but you can
|
54
|
+
* add your own stop words with {@link #withStopWords(String)}.
|
55
|
+
*
|
56
|
+
*
|
57
|
+
* <h3>Weight Your Own Words</h3>
|
58
|
+
*
|
59
|
+
* <p>If you have some other way to weight your words, you can pass
|
60
|
+
* them to {@link #fromWords(Word[])}, and in that case, you can use
|
61
|
+
* {@link Word#setColor(int)}, {@link Word#setFont(PFont)}, {@link
|
62
|
+
* Word#setAngle(float)}, and/or {@link Word#setPlace(PVector)} to
|
63
|
+
* control how any (or all) of your Words are drawn.
|
64
|
+
*
|
65
|
+
*
|
66
|
+
*
|
67
|
+
* <h2>Step Two: Style Your Words</h2>
|
68
|
+
*
|
69
|
+
* There are six questions you have to answer when drawing a word on the WordCram:
|
70
|
+
*
|
71
|
+
* <h3>How big should it be?</h3>
|
72
|
+
* A word can be
|
73
|
+
* {@link #sizedByWeight(int, int)},
|
74
|
+
* {@link #sizedByRank(int, int)}, or
|
75
|
+
* {@link #withSizer(WordSizer)}
|
76
|
+
*
|
77
|
+
* <h3>How should it be angled?</h3>
|
78
|
+
* It can be
|
79
|
+
* {@link #angledAt(float...)},
|
80
|
+
* {@link #angledBetween(float, float)}, or
|
81
|
+
* {@link #withAngler(WordAngler)}
|
82
|
+
*
|
83
|
+
* <h3>What font should it be in?</h3> You can render words {@link
|
84
|
+
* #withFont(String)} or {@link #withFonts(String...)} (those both can
|
85
|
+
* also take PFonts), or {@link #withFonter(WordFonter)}
|
86
|
+
*
|
87
|
+
* <h3>How should it be colored?</h3>
|
88
|
+
* {@link #withColor(int)},
|
89
|
+
* {@link #withColors(int...)}, or
|
90
|
+
* {@link #withColorer(WordColorer)}
|
91
|
+
*
|
92
|
+
* <h3>Where on the image should it go?</h3>
|
93
|
+
* {@link #withPlacer(WordPlacer)}
|
94
|
+
*
|
95
|
+
* <h3>If it doesn't fit at first, how should I nudge it?</h3>
|
96
|
+
* {@link #withNudger(WordNudger)}
|
97
|
+
*
|
98
|
+
* <h2>Step Three: Draw Your WordCram</h2>
|
99
|
+
*
|
100
|
+
* <p>After all that, actually rendering the WordCram is simple.
|
101
|
+
*
|
102
|
+
* You can repeatedly call {@link #drawNext()} while the WordCram
|
103
|
+
* {@link #hasMore()} words to draw (probably once per Processing
|
104
|
+
* frame):
|
105
|
+
*
|
106
|
+
* <pre>
|
107
|
+
* void draw() {
|
108
|
+
* if (wordCram.hasMore()) {
|
109
|
+
* wordCram.drawNext();
|
110
|
+
* }
|
111
|
+
* }
|
112
|
+
* </pre>
|
113
|
+
*
|
114
|
+
* Or you can call {@link #drawAll()} once, and let it loop for you:
|
115
|
+
*
|
116
|
+
* <pre>
|
117
|
+
* void draw() {
|
118
|
+
* wordCram.drawAll();
|
119
|
+
* }
|
120
|
+
* </pre>
|
121
|
+
*
|
122
|
+
* <h2>Step Three-and-a-Half: How Did It Go?</h2>
|
123
|
+
*
|
124
|
+
* <p>If you're having trouble getting your words to show up, you
|
125
|
+
* might want to {@link #getSkippedWords()}. Knowing which words were
|
126
|
+
* skipped, and why (see {@link Word#wasSkippedBecause()}), can help
|
127
|
+
* you size and place your words better.
|
128
|
+
*
|
129
|
+
* <p>You can also {@link #getWords()} to see the whole list, and
|
130
|
+
* {@link #getWordAt(float,float)} to see which word covers a given pixel.
|
131
|
+
*
|
132
|
+
* @author Dan Bernier
|
133
|
+
*/
|
134
|
+
public class WordCram {
|
135
|
+
|
136
|
+
/*
|
137
|
+
* This class is really only two parts: the fluent builder API, and
|
138
|
+
* pass-through calls to the WordCramEngine, where all the work happens.
|
139
|
+
* This separation keeps the classes focused on only one thing, but still
|
140
|
+
* gives the user a pretty nice API.
|
141
|
+
*/
|
142
|
+
private Word[] words;
|
143
|
+
private WordSource wordSource;
|
144
|
+
private final ArrayList<TextSource> textSources = new ArrayList<>();
|
145
|
+
private String extraStopWords = "";
|
146
|
+
private boolean excludeNumbers = true;
|
147
|
+
private enum TextCase { Lower, Upper, Keep };
|
148
|
+
private TextCase textCase = TextCase.Keep;
|
149
|
+
|
150
|
+
private WordCramEngine wordCramEngine;
|
151
|
+
|
152
|
+
private final PApplet parent;
|
153
|
+
|
154
|
+
private WordFonter fonter;
|
155
|
+
private WordSizer sizer;
|
156
|
+
private WordColorer colorer;
|
157
|
+
private WordAngler angler;
|
158
|
+
private WordPlacer placer;
|
159
|
+
private WordNudger nudger;
|
160
|
+
|
161
|
+
private WordRenderer renderer;
|
162
|
+
private final RenderOptions renderOptions = new RenderOptions();
|
163
|
+
private Observer observer;
|
164
|
+
|
165
|
+
/**
|
166
|
+
* Make a new WordCram.
|
167
|
+
* <p>
|
168
|
+
* It's the starting point of the fluent API for building WordCrams.
|
169
|
+
*
|
170
|
+
* @param parent Your Processing sketch. Pass it as <code>this</code>.
|
171
|
+
*/
|
172
|
+
public WordCram(PApplet parent) {
|
173
|
+
this.parent = parent;
|
174
|
+
this.renderer = new ProcessingWordRenderer(parent.g);
|
175
|
+
this.observer = new SketchCallbackObserver(parent);
|
176
|
+
}
|
177
|
+
|
178
|
+
/**
|
179
|
+
* Tells WordCram which words to ignore when it counts up the words in your text.
|
180
|
+
* These words won't show up in the image.
|
181
|
+
* <p>
|
182
|
+
* Stop-words are always case-insensitive: if your source text contains "The plane,
|
183
|
+
* the plane!", using "the" for a stop-word is enough to block both "the" and "The".
|
184
|
+
* <p>
|
185
|
+
* It doesn't matter whether this is called before or after the "for{text}" methods.
|
186
|
+
* <p>
|
187
|
+
* <b><i>Note:</i></b> Stop-words have no effect if you're passing in your own custom
|
188
|
+
* {@link Word} array, since WordCram won't do any text analysis on it (other than
|
189
|
+
* sorting the words and scaling their weights).
|
190
|
+
*
|
191
|
+
* @param extraStopWords a space-delimited String of words to ignore when counting the words in your text.
|
192
|
+
* @return The WordCram, for further setup or drawing.
|
193
|
+
*/
|
194
|
+
public WordCram withStopWords(String extraStopWords) {
|
195
|
+
this.extraStopWords = extraStopWords;
|
196
|
+
return this;
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* Exclude numbers from the text in the WordCram. They're excluded by default.
|
201
|
+
* <p>
|
202
|
+
* Words that are all numbers, like 1, 3.14159, 42, or 1492, will be excluded.
|
203
|
+
* Words that have some letters and some numbers like 1A, U2, or funnyguy194 will be included.
|
204
|
+
*
|
205
|
+
* @see #includeNumbers()
|
206
|
+
* @return The WordCram, for further setup or drawing.
|
207
|
+
*/
|
208
|
+
public WordCram excludeNumbers() {
|
209
|
+
this.excludeNumbers = true;
|
210
|
+
return this;
|
211
|
+
}
|
212
|
+
|
213
|
+
/**
|
214
|
+
* Include numbers from the text in the WordCram. They're excluded by default.
|
215
|
+
*
|
216
|
+
* @see #excludeNumbers()
|
217
|
+
* @return The WordCram, for further setup or drawing.
|
218
|
+
*/
|
219
|
+
public WordCram includeNumbers() {
|
220
|
+
this.excludeNumbers = false;
|
221
|
+
return this;
|
222
|
+
}
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Make the WordCram change all words to lower-case.
|
226
|
+
* Stop-words are unaffected; they're always case-insensitive.
|
227
|
+
* The default is to keep words as they appear in the text.
|
228
|
+
*
|
229
|
+
* @return The WordCram, for further setup or drawing.
|
230
|
+
*/
|
231
|
+
public WordCram lowerCase() {
|
232
|
+
this.textCase = TextCase.Lower;
|
233
|
+
return this;
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* Make the WordCram change all words to upper-case.
|
238
|
+
* Stop-words are unaffected; they're always case-insensitive.
|
239
|
+
* The default is to keep words as they appear in the text.
|
240
|
+
*
|
241
|
+
* @return The WordCram, for further setup or drawing.
|
242
|
+
*/
|
243
|
+
public WordCram upperCase() {
|
244
|
+
this.textCase = TextCase.Upper;
|
245
|
+
return this;
|
246
|
+
}
|
247
|
+
|
248
|
+
/**
|
249
|
+
* Make the WordCram leave all words cased as they appear in the text.
|
250
|
+
* Stop-words are unaffected; they're always case-insensitive.
|
251
|
+
* This is the default.
|
252
|
+
*
|
253
|
+
* @return The WordCram, for further setup or drawing.
|
254
|
+
*/
|
255
|
+
public WordCram keepCase() {
|
256
|
+
this.textCase = TextCase.Keep;
|
257
|
+
return this;
|
258
|
+
}
|
259
|
+
|
260
|
+
/**
|
261
|
+
* Make a WordCram from the text on a web page.
|
262
|
+
* Just before the WordCram is drawn, it'll load the web page's HTML, scrape out the text,
|
263
|
+
* and count and sort the words.
|
264
|
+
*
|
265
|
+
* @param webPageAddress the URL of the web page to load
|
266
|
+
* @return The WordCram, for further setup or drawing.
|
267
|
+
*/
|
268
|
+
public WordCram fromWebPage(String webPageAddress) {
|
269
|
+
return fromWebPage(webPageAddress, null);
|
270
|
+
}
|
271
|
+
|
272
|
+
/**
|
273
|
+
* Make a WordCram from the text in any elements on a web page that match the
|
274
|
+
* <tt>cssSelector</tt>.
|
275
|
+
* Just before the WordCram is drawn, it'll load the web page's HTML, scrape
|
276
|
+
* out the text, and count and sort the words.
|
277
|
+
*
|
278
|
+
* HTML parsing is handled by Jsoup, so see
|
279
|
+
* <a href="http://jsoup.org/cookbook/extracting-data/selector-syntax">the
|
280
|
+
* Jsoup selector documentation</a> if you're having trouble writing your
|
281
|
+
* selector.
|
282
|
+
*
|
283
|
+
* @param webPageAddress the URL of the web page to load
|
284
|
+
* @param cssSelector a CSS selector to filter the HTML by, before extracting
|
285
|
+
* text
|
286
|
+
* @return The WordCram, for further setup or drawing.
|
287
|
+
*/
|
288
|
+
public WordCram fromWebPage(String webPageAddress, String cssSelector) {
|
289
|
+
return fromText(new WebPage(webPageAddress, cssSelector, parent));
|
290
|
+
}
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Make a WordCram from the text in a HTML file.
|
294
|
+
* Just before the WordCram is drawn, it'll load the file's HTML, scrape out the text,
|
295
|
+
* and count and sort the words.
|
296
|
+
*
|
297
|
+
* @param htmlFilePath the path of the html file to load
|
298
|
+
* @return The WordCram, for further setup or drawing.
|
299
|
+
*/
|
300
|
+
public WordCram fromHtmlFile(String htmlFilePath) {
|
301
|
+
return fromHtmlFile(htmlFilePath, null);
|
302
|
+
}
|
303
|
+
|
304
|
+
/**
|
305
|
+
* Make a WordCram from the text in any elements on a web page that match the
|
306
|
+
* <tt>cssSelector</tt>.
|
307
|
+
* Just before the WordCram is drawn, it'll load the file's HTML, scrape out the text,
|
308
|
+
* and count and sort the words.
|
309
|
+
*
|
310
|
+
* HTML parsing is handled by Jsoup, so see
|
311
|
+
* <a href="http://jsoup.org/cookbook/extracting-data/selector-syntax">the
|
312
|
+
* Jsoup selector documentation</a> if you're having trouble writing your
|
313
|
+
* selector.
|
314
|
+
*
|
315
|
+
* @param htmlFilePath the path of the html file to load
|
316
|
+
* @param cssSelector a CSS selector to filter the HTML by, before extracting
|
317
|
+
* text
|
318
|
+
* @return The WordCram, for further setup or drawing.
|
319
|
+
*/
|
320
|
+
public WordCram fromHtmlFile(String htmlFilePath, String cssSelector) {
|
321
|
+
return fromText(new WebPage(htmlFilePath, cssSelector, parent));
|
322
|
+
}
|
323
|
+
|
324
|
+
// TODO from an inputstream! or reader, anyway
|
325
|
+
|
326
|
+
/**
|
327
|
+
* Makes a WordCram from a String of HTML. Just before the
|
328
|
+
* WordCram is drawn, it'll scrape out the text from the HTML,
|
329
|
+
* and count and sort the words. It takes one String, or any
|
330
|
+
* number of Strings, or an array of Strings, so you can
|
331
|
+
* easily use it with <a
|
332
|
+
* href="http://processing.org/reference/loadStrings_.html"
|
333
|
+
* target="blank">loadStrings()</a>.
|
334
|
+
*
|
335
|
+
* @deprecated because its signature is annoying, and makes it hard to
|
336
|
+
* pass a CSS Selector. If you love this method, and want it to stick around,
|
337
|
+
* let me know: <a href="http://github.com/danbernier/WordCram/issues">open
|
338
|
+
* a github issue</a>, send me a
|
339
|
+
* <a href="http://twitter.com/wordcram">tweet</a>,
|
340
|
+
* or say hello at wordcram at gmail.
|
341
|
+
* Otherwise, it'll be deleted in a future release, probably 0.6.
|
342
|
+
*
|
343
|
+
* @param html the String(s) of HTML
|
344
|
+
* @return The WordCram, for further setup or drawing.
|
345
|
+
*/
|
346
|
+
@Deprecated
|
347
|
+
public WordCram fromHtmlString(String... html) {
|
348
|
+
return fromText(new Html(PApplet.join(html, "")));
|
349
|
+
}
|
350
|
+
|
351
|
+
/**
|
352
|
+
* Makes a WordCram from a text file, either on the filesystem
|
353
|
+
* or the network. Just before the WordCram is drawn, it'll
|
354
|
+
* load the file, and count and sort its words.
|
355
|
+
*
|
356
|
+
* @param textFilePathOrUrl the path of the text file
|
357
|
+
* @return The WordCram, for further setup or drawing.
|
358
|
+
*/
|
359
|
+
public WordCram fromTextFile(String textFilePathOrUrl) {
|
360
|
+
return fromText(new TextFile(textFilePathOrUrl, parent));
|
361
|
+
}
|
362
|
+
|
363
|
+
/**
|
364
|
+
* Makes a WordCram from a String of text. It takes one
|
365
|
+
* String, or any number of Strings, or an array of Strings,
|
366
|
+
* so you can easily use it with <a
|
367
|
+
* href="http://processing.org/reference/loadStrings_.html"
|
368
|
+
* target="blank">loadStrings()</a>.
|
369
|
+
*
|
370
|
+
* @param text the String of text to get the words from
|
371
|
+
* @return The WordCram, for further setup or drawing.
|
372
|
+
*/
|
373
|
+
//example fromTextString(loadStrings("my.txt"))
|
374
|
+
//example fromTextString("one", "two", "three")
|
375
|
+
//example fromTextString("Hello there!")
|
376
|
+
public WordCram fromTextString(String... text) {
|
377
|
+
return fromText(new Text(PApplet.join(text, " ")));
|
378
|
+
}
|
379
|
+
|
380
|
+
/**
|
381
|
+
* Makes a WordCram from any TextSource.
|
382
|
+
*
|
383
|
+
* <p> It only caches the TextSource - it won't load the text
|
384
|
+
* from it until {@link #drawAll()} or {@link #drawNext()} is
|
385
|
+
* called.
|
386
|
+
*
|
387
|
+
* @param textSource the TextSource to get the text from.
|
388
|
+
* @return The WordCram, for further setup or drawing.
|
389
|
+
*/
|
390
|
+
public WordCram fromText(TextSource textSource) {
|
391
|
+
this.textSources.add(textSource);
|
392
|
+
return this;
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* Makes a WordCram from your own custom Word array. The
|
397
|
+
* Words can be ordered and weighted arbitrarily - WordCram
|
398
|
+
* will sort them by weight, and then divide their weights by
|
399
|
+
* the weight of the heaviest Word, so the heaviest Word will
|
400
|
+
* end up with a weight of 1.0.
|
401
|
+
*
|
402
|
+
* <p>Note: WordCram won't do any text analysis on the words;
|
403
|
+
* stop-words will have no effect, etc. These words are
|
404
|
+
* supposed to be ready to go.
|
405
|
+
*
|
406
|
+
* @param words
|
407
|
+
* @return The WordCram, for further setup or drawing.
|
408
|
+
*/
|
409
|
+
public WordCram fromWords(Word[] words) {
|
410
|
+
return fromWords(new WordArray(words));
|
411
|
+
}
|
412
|
+
|
413
|
+
public WordCram fromWords(WordSource wordSource) {
|
414
|
+
this.wordSource = wordSource;
|
415
|
+
return this;
|
416
|
+
}
|
417
|
+
|
418
|
+
//----------------------------------------------
|
419
|
+
|
420
|
+
/**
|
421
|
+
* This WordCram will get a
|
422
|
+
* <a href="http://processing.org/reference/PFont.html" target="blank">PFont</a>
|
423
|
+
* for each fontName, via
|
424
|
+
* <a href="http://processing.org/reference/createFont_.html" target="blank">createFont</a>,
|
425
|
+
* and will render words in one of those PFonts.
|
426
|
+
*
|
427
|
+
* @param fontNames
|
428
|
+
* @return The WordCram, for further setup or drawing.
|
429
|
+
*/
|
430
|
+
public WordCram withFonts(String... fontNames) {
|
431
|
+
PFont[] fonts = new PFont[fontNames.length];
|
432
|
+
for (int i = 0; i < fontNames.length; i++) {
|
433
|
+
fonts[i] = parent.createFont(fontNames[i], 1);
|
434
|
+
}
|
435
|
+
|
436
|
+
return withFonts(fonts);
|
437
|
+
}
|
438
|
+
|
439
|
+
/**
|
440
|
+
* Make the WordCram render all words in the font that matches
|
441
|
+
* the given name, via Processing's
|
442
|
+
* <a href="http://processing.org/reference/createFont_.html" target="blank">createFont</a>.
|
443
|
+
*
|
444
|
+
* @param fontName the font name to pass to createFont.
|
445
|
+
* @return The WordCram, for further setup or drawing.
|
446
|
+
*/
|
447
|
+
public WordCram withFont(String fontName) {
|
448
|
+
PFont font = parent.createFont(fontName, 1);
|
449
|
+
return withFont(font);
|
450
|
+
}
|
451
|
+
|
452
|
+
/**
|
453
|
+
* This WordCram will render words in one of the given
|
454
|
+
* <a href="http://processing.org/reference/PFont.html" target="blank">PFonts</a>.
|
455
|
+
*
|
456
|
+
* @param fonts
|
457
|
+
* @return The WordCram, for further setup or drawing.
|
458
|
+
*/
|
459
|
+
public WordCram withFonts(PFont... fonts) {
|
460
|
+
return withFonter(Fonters.pickFrom(fonts));
|
461
|
+
}
|
462
|
+
|
463
|
+
/**
|
464
|
+
* Make the WordCram render all words in the given
|
465
|
+
* <a href="http://processing.org/reference/PFont.html" target="blank">PFont</a>.
|
466
|
+
*
|
467
|
+
* @param font the PFont to render the words in.
|
468
|
+
* @return The WordCram, for further setup or drawing.
|
469
|
+
*/
|
470
|
+
public WordCram withFont(PFont font) {
|
471
|
+
return withFonter(Fonters.pickFrom(font));
|
472
|
+
}
|
473
|
+
|
474
|
+
/**
|
475
|
+
* Use the given WordFonter to pick fonts for each word.
|
476
|
+
* You can make your own, or use a pre-fab one from {@link Fonters}.
|
477
|
+
*
|
478
|
+
* @see WordFonter
|
479
|
+
* @see Fonters
|
480
|
+
* @param fonter the WordFonter to use.
|
481
|
+
* @return The WordCram, for further setup or drawing.
|
482
|
+
*/
|
483
|
+
/*=
|
484
|
+
* Here is a bit of a play-ground for now, to see how
|
485
|
+
* this might work. See docgen.rb.
|
486
|
+
* example withFonter({your WordFonter})
|
487
|
+
* example withFonter(Fonters.alwaysUse("Comic Sans"))
|
488
|
+
* example withFonter(new WordFonter() { ... (how to doc-gen this?)
|
489
|
+
=*/
|
490
|
+
public WordCram withFonter(WordFonter fonter) {
|
491
|
+
this.fonter = fonter;
|
492
|
+
return this;
|
493
|
+
}
|
494
|
+
|
495
|
+
/**
|
496
|
+
* Make the WordCram size words by their weight, where the
|
497
|
+
* "heaviest" word will be sized at <code>maxSize</code>.
|
498
|
+
*
|
499
|
+
* <p>Specifically, it makes the WordCram use {@link
|
500
|
+
* Sizers#byWeight(int, int)}.
|
501
|
+
*
|
502
|
+
* @param minSize the size to draw a Word of weight 0
|
503
|
+
* @param maxSize the size to draw a Word of weight 1
|
504
|
+
* @return The WordCram, for further setup or drawing.
|
505
|
+
*/
|
506
|
+
/*=example sizedByWeight(int minSize, int maxSize)=*/
|
507
|
+
public WordCram sizedByWeight(int minSize, int maxSize) {
|
508
|
+
return withSizer(Sizers.byWeight(minSize, maxSize));
|
509
|
+
}
|
510
|
+
|
511
|
+
/**
|
512
|
+
* Make the WordCram size words by their rank. The first
|
513
|
+
* word will be sized at <code>maxSize</code>.
|
514
|
+
*
|
515
|
+
* <p>Specifically, it makes the WordCram use {@link
|
516
|
+
* Sizers#byRank(int, int)}.
|
517
|
+
*
|
518
|
+
* @param minSize the size to draw the last Word
|
519
|
+
* @param maxSize the size to draw the first Word
|
520
|
+
* @return The WordCram, for further setup or drawing.
|
521
|
+
*/
|
522
|
+
/*=example sizedByRank(int minSize, int maxSize)=*/
|
523
|
+
public WordCram sizedByRank(int minSize, int maxSize) {
|
524
|
+
return withSizer(Sizers.byRank(minSize, maxSize));
|
525
|
+
}
|
526
|
+
|
527
|
+
/**
|
528
|
+
* Use the given WordSizer to pick fonts for each word.
|
529
|
+
* You can make your own, or use a pre-fab one from {@link Sizers}.
|
530
|
+
*
|
531
|
+
* @see WordSizer
|
532
|
+
* @see Sizers
|
533
|
+
* @param sizer the WordSizer to use.
|
534
|
+
* @return The WordCram, for further setup or drawing.
|
535
|
+
*/
|
536
|
+
public WordCram withSizer(WordSizer sizer) {
|
537
|
+
this.sizer = sizer;
|
538
|
+
return this;
|
539
|
+
}
|
540
|
+
|
541
|
+
/**
|
542
|
+
* Render words by randomly choosing from the given colors.
|
543
|
+
* Uses {@link Colorers#pickFrom(int...)}.
|
544
|
+
*
|
545
|
+
* <p> Note: if you want all your words to be, say, red,
|
546
|
+
* <i>don't</i> do this:
|
547
|
+
*
|
548
|
+
* <pre>
|
549
|
+
* ...withColors(255, 0, 0)... // Not what you want!
|
550
|
+
* </pre>
|
551
|
+
*
|
552
|
+
* You'll just see a blank WordCram. Since <a
|
553
|
+
* href="http://processing.org/reference/color_datatype.html"
|
554
|
+
* target="blank">Processing stores colors as integers</a>,
|
555
|
+
* WordCram will see each integer as a different color, and
|
556
|
+
* it'll color about 1/3 of your words with the color
|
557
|
+
* represented by the integer 255, and the other 2/3 with the
|
558
|
+
* color represented by the integer 0. The punchline is,
|
559
|
+
* Processing stores opacity (or alpha) in the highest bits
|
560
|
+
* (the ones used for storing really big numbers, from
|
561
|
+
* 2<sup>24</sup> to 2<sup>32</sup>), so your colors 0 and 255
|
562
|
+
* have, effectively, 0 opacity -- they're completely
|
563
|
+
* transparent. Oops.
|
564
|
+
*
|
565
|
+
* <p> Use this instead, and you'll get what you're after:
|
566
|
+
*
|
567
|
+
* <pre>
|
568
|
+
* ...withColors(color(255, 0, 0))... // Much better!
|
569
|
+
* </pre>
|
570
|
+
*
|
571
|
+
* @param colors the colors to randomly choose from.
|
572
|
+
* @return The WordCram, for further setup or drawing.
|
573
|
+
*/
|
574
|
+
public WordCram withColors(int... colors) {
|
575
|
+
return withColorer(Colorers.pickFrom(colors));
|
576
|
+
}
|
577
|
+
|
578
|
+
/**
|
579
|
+
* Renders all words in the given color.
|
580
|
+
* @see #withColors(int...)
|
581
|
+
* @param color the color for each word.
|
582
|
+
* @return The WordCram, for further setup or drawing.
|
583
|
+
*/
|
584
|
+
public WordCram withColor(int color) {
|
585
|
+
return withColors(color);
|
586
|
+
}
|
587
|
+
|
588
|
+
/**
|
589
|
+
* Use the given WordColorer to pick colors for each word.
|
590
|
+
* You can make your own, or use a pre-fab one from {@link Colorers}.
|
591
|
+
*
|
592
|
+
* @see WordColorer
|
593
|
+
* @see Colorers
|
594
|
+
* @param colorer the WordColorer to use.
|
595
|
+
* @return The WordCram, for further setup or drawing.
|
596
|
+
*/
|
597
|
+
public WordCram withColorer(WordColorer colorer) {
|
598
|
+
this.colorer = colorer;
|
599
|
+
return this;
|
600
|
+
}
|
601
|
+
|
602
|
+
// TODO need more overloads!
|
603
|
+
|
604
|
+
/**
|
605
|
+
* Make the WordCram rotate each word at one of the given angles.
|
606
|
+
* @param anglesInRadians The list of possible rotation angles, in radians
|
607
|
+
* @return The WordCram, for further setup or drawing.
|
608
|
+
*/
|
609
|
+
public WordCram angledAt(float... anglesInRadians) {
|
610
|
+
return withAngler(Anglers.pickFrom(anglesInRadians));
|
611
|
+
}
|
612
|
+
|
613
|
+
/**
|
614
|
+
* Make the WordCram rotate words randomly, between the min and max angles.
|
615
|
+
* @param minAngleInRadians The minimum rotation angle, in radians
|
616
|
+
* @param maxAngleInRadians The maximum rotation angle, in radians
|
617
|
+
* @return The WordCram, for further setup or drawing.
|
618
|
+
*/
|
619
|
+
public WordCram angledBetween(float minAngleInRadians, float maxAngleInRadians) {
|
620
|
+
return withAngler(Anglers.randomBetween(minAngleInRadians, maxAngleInRadians));
|
621
|
+
}
|
622
|
+
|
623
|
+
/**
|
624
|
+
* Use the given WordAngler to pick angles for each word.
|
625
|
+
* You can make your own, or use a pre-fab one from {@link Anglers}.
|
626
|
+
*
|
627
|
+
* @see WordAngler
|
628
|
+
* @see Anglers
|
629
|
+
* @param angler the WordAngler to use.
|
630
|
+
* @return The WordCram, for further setup or drawing.
|
631
|
+
*/
|
632
|
+
public WordCram withAngler(WordAngler angler) {
|
633
|
+
this.angler = angler;
|
634
|
+
return this;
|
635
|
+
}
|
636
|
+
|
637
|
+
/**
|
638
|
+
* Use the given WordPlacer to pick locations for each word.
|
639
|
+
* You can make your own, or use a pre-fab one from {@link Placers}.
|
640
|
+
*
|
641
|
+
* @see WordPlacer
|
642
|
+
* @see Placers
|
643
|
+
* @see PlottingWordPlacer
|
644
|
+
* @param placer the WordPlacer to use.
|
645
|
+
* @return The WordCram, for further setup or drawing.
|
646
|
+
*/
|
647
|
+
public WordCram withPlacer(WordPlacer placer) {
|
648
|
+
this.placer = placer;
|
649
|
+
return this;
|
650
|
+
}
|
651
|
+
|
652
|
+
/**
|
653
|
+
* Use the given WordNudger to pick angles for each word.
|
654
|
+
* You can make your own, or use a pre-fab one.
|
655
|
+
*
|
656
|
+
* @see WordNudger
|
657
|
+
* @see SpiralWordNudger
|
658
|
+
* @see RandomWordNudger
|
659
|
+
* @see PlottingWordNudger
|
660
|
+
* @param nudger the WordNudger to use.
|
661
|
+
* @return The WordCram, for further setup or drawing.
|
662
|
+
*/
|
663
|
+
public WordCram withNudger(WordNudger nudger) {
|
664
|
+
this.nudger = nudger;
|
665
|
+
return this;
|
666
|
+
}
|
667
|
+
|
668
|
+
/**
|
669
|
+
* How many attempts should be used to place a word. Higher
|
670
|
+
* values ensure that more words get placed, but will make
|
671
|
+
* algorithm slower.
|
672
|
+
* @param maxAttempts
|
673
|
+
* @return The WordCram, for further setup or drawing.
|
674
|
+
*/
|
675
|
+
public WordCram maxAttemptsToPlaceWord(int maxAttempts) {
|
676
|
+
renderOptions.maxAttemptsToPlaceWord = maxAttempts;
|
677
|
+
return this;
|
678
|
+
}
|
679
|
+
|
680
|
+
/**
|
681
|
+
* The maximum number of Words WordCram should try to draw.
|
682
|
+
* This might be useful if you have a whole bunch of words,
|
683
|
+
* and need an artificial way to cut down the list (for
|
684
|
+
* speed). By default, it's unlimited.
|
685
|
+
* @param maxWords can be any value from 0 to Integer.MAX_VALUE. Values < 0 are treated as unlimited.
|
686
|
+
* @return The WordCram, for further setup or drawing.
|
687
|
+
*/
|
688
|
+
public WordCram maxNumberOfWordsToDraw(int maxWords) {
|
689
|
+
renderOptions.maxNumberOfWordsToDraw = maxWords;
|
690
|
+
return this;
|
691
|
+
}
|
692
|
+
|
693
|
+
/**
|
694
|
+
* The smallest-sized Shape the WordCram should try to draw.
|
695
|
+
* By default, it's 7.
|
696
|
+
* @param minShapeSize the size of the smallest Shape.
|
697
|
+
* @return The WordCram, for further setup or drawing.
|
698
|
+
*/
|
699
|
+
public WordCram minShapeSize(int minShapeSize) {
|
700
|
+
renderOptions.minShapeSize = minShapeSize;
|
701
|
+
return this;
|
702
|
+
}
|
703
|
+
|
704
|
+
/**
|
705
|
+
* Use a custom canvas instead of the applet's default one.
|
706
|
+
* This may be needed if rendering in background or in other
|
707
|
+
* dimensions than the applet size is needed.
|
708
|
+
* @deprecated for more consistent naming. Use {@link #toCanvas(PGraphics canvas)} instead.
|
709
|
+
* @param canvas the canvas to draw to
|
710
|
+
* @return The WordCram, for further setup or drawing.
|
711
|
+
*/
|
712
|
+
public WordCram withCustomCanvas(PGraphics canvas) {
|
713
|
+
return toCanvas(canvas);
|
714
|
+
}
|
715
|
+
|
716
|
+
/**
|
717
|
+
* Use a custom canvas instead of the applet's default one.
|
718
|
+
* This may be needed if rendering in background or in other
|
719
|
+
* dimensions than the applet size is needed.
|
720
|
+
* @param canvas the canvas to draw to
|
721
|
+
* @return The WordCram, for further setup or drawing.
|
722
|
+
*/
|
723
|
+
public WordCram toCanvas(PGraphics canvas) {
|
724
|
+
this.renderer = new ProcessingWordRenderer(canvas);
|
725
|
+
return this;
|
726
|
+
}
|
727
|
+
|
728
|
+
public WordCram toSvg(String filename, int width, int height) throws java.io.FileNotFoundException {
|
729
|
+
this.renderer = new SvgWordRenderer(parent.sketchPath(filename), width, height);
|
730
|
+
return this;
|
731
|
+
}
|
732
|
+
|
733
|
+
|
734
|
+
/**
|
735
|
+
* Add padding around each word, so they stand out from each other more.
|
736
|
+
* If you call this multiple times, the last value will be used.
|
737
|
+
*
|
738
|
+
* WordCram uses a tree of java.awt.Rectangle objects to detect whether two
|
739
|
+
* words overlap. What this method actually does is call
|
740
|
+
* <code>Rectangle.grow(padding)</code> on the leaves of that tree.
|
741
|
+
*
|
742
|
+
* @param padding The number of pixels to grow each rectangle by. Defaults to zero.
|
743
|
+
* @return The WordCram, for further setup or drawing.
|
744
|
+
*/
|
745
|
+
public WordCram withWordPadding(int padding) {
|
746
|
+
renderOptions.wordPadding = padding;
|
747
|
+
return this;
|
748
|
+
}
|
749
|
+
|
750
|
+
/**
|
751
|
+
* Render a heatmap of the locations where your WordPlacer
|
752
|
+
* places words. This is pretty accurate: it renders all your words
|
753
|
+
* according to your sizer, fonter, angler, and placer, without
|
754
|
+
* nudging them, to an in-memory buffer. Then it splits your sketch
|
755
|
+
* into 10x10 pixel squares, and counts how many words overlap each
|
756
|
+
* square, and renders a heatmap: black for 0 words, green for 1,
|
757
|
+
* and red for more than 8. Rendering too many words at the same
|
758
|
+
* spot will make your WordCram run slower, and skip more words,
|
759
|
+
* so learning where your hotspots are can be helpful.
|
760
|
+
*
|
761
|
+
* This is very experimental, and could be changed or removed in a
|
762
|
+
* future release.
|
763
|
+
*/
|
764
|
+
public void testPlacer() {
|
765
|
+
initComponents();
|
766
|
+
WordShaper shaper = new WordShaper(renderOptions.rightToLeft);
|
767
|
+
PlacerHeatMap heatMap = new PlacerHeatMap(words, fonter, sizer, angler, placer, nudger, shaper);
|
768
|
+
heatMap.draw(parent);
|
769
|
+
}
|
770
|
+
|
771
|
+
private WordCramEngine getWordCramEngine() {
|
772
|
+
if (wordCramEngine == null) {
|
773
|
+
initComponents();
|
774
|
+
WordShaper shaper = new WordShaper(renderOptions.rightToLeft);
|
775
|
+
wordCramEngine = new WordCramEngine(renderer,
|
776
|
+
words,
|
777
|
+
fonter,
|
778
|
+
sizer,
|
779
|
+
colorer,
|
780
|
+
angler,
|
781
|
+
placer,
|
782
|
+
nudger,
|
783
|
+
shaper,
|
784
|
+
new BBTreeBuilder(),
|
785
|
+
renderOptions,
|
786
|
+
observer);
|
787
|
+
}
|
788
|
+
return wordCramEngine;
|
789
|
+
}
|
790
|
+
|
791
|
+
private void initComponents() {
|
792
|
+
|
793
|
+
if (words == null && wordSource != null) {
|
794
|
+
words = wordSource.getWords();
|
795
|
+
}
|
796
|
+
|
797
|
+
if (words == null && !textSources.isEmpty()) {
|
798
|
+
String text = joinTextSources();
|
799
|
+
|
800
|
+
text = textCase == TextCase.Lower ? text.toLowerCase()
|
801
|
+
: textCase == TextCase.Upper ? text.toUpperCase()
|
802
|
+
: text;
|
803
|
+
|
804
|
+
words = new WordCounter().withExtraStopWords(extraStopWords).shouldExcludeNumbers(excludeNumbers).count(text, renderOptions);
|
805
|
+
observer.wordsCounted(words);
|
806
|
+
if (words.length == 0) {
|
807
|
+
warnScripterAboutEmptyWordArray();
|
808
|
+
}
|
809
|
+
}
|
810
|
+
words = new WordSorterAndScaler().sortAndScale(words);
|
811
|
+
|
812
|
+
if (fonter == null) fonter = Fonters.alwaysUse(parent.createFont("sans", 1));
|
813
|
+
if (sizer == null) sizer = Sizers.byWeight(5, 70);
|
814
|
+
if (colorer == null) colorer = Colorers.alwaysUse(parent.color(0));
|
815
|
+
if (angler == null) angler = Anglers.mostlyHoriz();
|
816
|
+
if (placer == null) placer = Placers.horizLine();
|
817
|
+
if (nudger == null) nudger = new SpiralWordNudger();
|
818
|
+
}
|
819
|
+
|
820
|
+
private String joinTextSources() {
|
821
|
+
StringBuilder buffer = new StringBuilder();
|
822
|
+
textSources.stream().map((textSource) -> {
|
823
|
+
buffer.append(textSource.getText());
|
824
|
+
return textSource;
|
825
|
+
}).forEachOrdered((_item) -> {
|
826
|
+
buffer.append("\n");
|
827
|
+
});
|
828
|
+
return buffer.toString();
|
829
|
+
}
|
830
|
+
|
831
|
+
private void warnScripterAboutEmptyWordArray() {
|
832
|
+
System.out.println();
|
833
|
+
System.out.println("cue.language can't find any non-stop words in your text. This could be because your file encoding is wrong, or because all your words are single characters, among other things.");
|
834
|
+
System.out.println("Since cue.language can't find any words in your text, WordCram won't display any, but your Processing sketch will continue as normal.");
|
835
|
+
System.out.println("See https://github.com/danbernier/WordCram/issues/8 for more information.");
|
836
|
+
}
|
837
|
+
|
838
|
+
|
839
|
+
/**
|
840
|
+
* If you're drawing the words one-at-a-time using {@link
|
841
|
+
* #drawNext()}, this will tell you whether the WordCram has
|
842
|
+
* any words left to draw.
|
843
|
+
* @return true if the WordCram has any words left to draw; false otherwise.
|
844
|
+
* @see #drawNext()
|
845
|
+
*/
|
846
|
+
public boolean hasMore() {
|
847
|
+
return getWordCramEngine().hasMore();
|
848
|
+
}
|
849
|
+
|
850
|
+
/**
|
851
|
+
* If the WordCram has any more words to draw, draw the next
|
852
|
+
* one.
|
853
|
+
* @see #hasMore()
|
854
|
+
* @see #drawAll()
|
855
|
+
*/
|
856
|
+
public void drawNext() {
|
857
|
+
getWordCramEngine().drawNext();
|
858
|
+
}
|
859
|
+
|
860
|
+
/**
|
861
|
+
* Just like it sounds: draw all the words. Once the WordCram
|
862
|
+
* has everything set, call this and wait just a bit.
|
863
|
+
* @see #drawNext()
|
864
|
+
*/
|
865
|
+
public void drawAll() {
|
866
|
+
getWordCramEngine().drawAll();
|
867
|
+
}
|
868
|
+
|
869
|
+
|
870
|
+
/**
|
871
|
+
* Get the Words that WordCram is drawing. This can be useful
|
872
|
+
* if you want to inspect exactly how the words were weighted,
|
873
|
+
* or see how they were colored, fonted, sized, angled, or
|
874
|
+
* placed, or why they were skipped.
|
875
|
+
* @return
|
876
|
+
*/
|
877
|
+
public Word[] getWords() {
|
878
|
+
Word[] wordsCopy = new Word[words.length];
|
879
|
+
System.arraycopy(words, 0, wordsCopy, 0, words.length);
|
880
|
+
return wordsCopy;
|
881
|
+
}
|
882
|
+
|
883
|
+
/**
|
884
|
+
* Get the Word at the given (x,y) coordinates.
|
885
|
+
*
|
886
|
+
* <p>This can be called while the WordCram is rendering, or
|
887
|
+
* after it's done. If a Word is too small to render, or
|
888
|
+
* hasn't been placed yet, it will never be returned by this
|
889
|
+
* method.
|
890
|
+
*
|
891
|
+
* @param x the X coordinate
|
892
|
+
* @param y the Y coordinate
|
893
|
+
* @return the Word that covers those coordinates, or null if there isn't one
|
894
|
+
*/
|
895
|
+
public Word getWordAt(float x, float y) {
|
896
|
+
return getWordCramEngine().getWordAt(x, y);
|
897
|
+
}
|
898
|
+
|
899
|
+
/**
|
900
|
+
* Returns an array of words that could not be placed.
|
901
|
+
* @return An array of the skipped words
|
902
|
+
*/
|
903
|
+
public Word[] getSkippedWords() {
|
904
|
+
return getWordCramEngine().getSkippedWords();
|
905
|
+
}
|
906
|
+
|
907
|
+
/**
|
908
|
+
* How far through the words are we? Useful for when drawing
|
909
|
+
* to a custom PGraphics.
|
910
|
+
* @return The current point of progress through the list, as a float between 0 and 1.
|
911
|
+
*/
|
912
|
+
public float getProgress() {
|
913
|
+
return getWordCramEngine().getProgress();
|
914
|
+
}
|
915
|
+
|
916
|
+
public WordCram withObserver(Observer observer) {
|
917
|
+
this.observer = observer;
|
918
|
+
return this;
|
919
|
+
}
|
920
|
+
}
|