- Flexible Any mask, any color, any angle, adjustable density. You can specify the initial position of some words. Or you can pin some words and adjust others, etc.
- Fast 100% in Julia and efficient implementation based on Quadtree & gradient optimization (see Stuffing.jl). The advantage is more obvious when generating large images.
- Exact Words with the same weight have the exact same size. The algorithm will never scale the word to fit the blank.
showexample(:juliadoc) to see how to generate the banner
import Pkg; Pkg.add("WordCloud")
using WordCloud words = "天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章" words = [string(c) for c in words] weights = rand(length(words)) .^ 2 .* 100 .+ 30 wc = wordcloud(words, weights) generate!(wc) paint(wc, "qianziwen.svg")
Run the command
showexample(:qianziwen) to get the result.
More Complex Usage
using WordCloud wc = wordcloud( processtext(open(pkgdir(WordCloud)*"/res/alice.txt"), stopwords=WordCloud.stopwords_en ∪ ["said"]), mask = loadmask(pkgdir(WordCloud)*"/res/alice_mask.png", color="#faeef8"), colors = :Set1_5, angles = (0, 90), density = 0.55) |> generate! paint(wc, "alice.png", ratio=0.5, background=outline(wc.mask, color="purple", linewidth=1))
Unlike most other implementations, WordCloud.jl is programmed based on local grayscale gradient optimization. It’s a non-greedy algorithm in which words can be further moved after they are positioned. This means shrinking words is unnecessary, thus the word size can be kept unchanged during the adjustment. In addition, it allows words to be assigned to any initial position whether or not there will be an overlap. This enables the program to achieve the maximum flexibility.
First, raster masks of words are represented as
AbstractStackQtree forms a pyramid structure. It consists of layers of different pixel density of the original mask. Each layer is generated by reducing the pixel density of the layer below it. In this way, the
AbstractStackQtree can be seen as a set of hierarchical bounding boxes. The value of each pixel of each layer (the node of
AbstractStackQtree) can be
Second, we use a top-down method (
collision_randbfs) to detect collision between two
AbstractStackQtrees. On the level 𝑙 and coordinates (𝑎,𝑏), if one tree's node is
FULL and another's is NOT
EMPTY, then these two trees collide at (𝑙,𝑎,𝑏). However, to detect collisions between many trees/objects, pairwise detection would be time-consuming. So, we first locate the objects in hierarchical sub-areas (implemented as linked-quadtree), and then detect the collision between objects within each sub-area and between the objects in the sub-areas and those in their ancestral areas (see
At last, each word in collision pair is moved according to the gray gradient (calculated by
whitesum) near the collision point (𝑙,𝑎,𝑏), that is, moving the word away from
EMPTY region. This will enlarge space between the two words. Note that, at least one of the eight pixels around the collision point is
MIX, otherwise the collision would occur at the layer 𝑙−1. After moving the words, the
AbstractStackQtrees should be rebuilt for the next round of collision detection.
- 排序 & 预放置
mv wqy-microhei.ttc ~/.fonts
add /path/to/ffmpeg-4.2.1/lib to ENV["LD_LIBRARY_PATH"]
add /path/to/ffmpeg-4.2.1/bin to ENV["PATH"]