Here's the procedure: proc cloud_tags { cloudname {range 7} } {
upvar ${cloudname}_counts cloud_counts
upvar ${cloudname}_buckets cloud_buckets
set tag_sizes $range
set taglist {}
foreach tag [array names cloud_counts] {
lappend taglist [list $cloud_counts($tag) $tag]
}
set total_tags [llength $taglist]
if {$total_tags} {
# Start with the sorted list of tags and divide by the number of font
# sizes (buckets). Then proceed to put an even number of tags into each
# bucket. The only restriction is that tags of the same count can't
# span 2 buckets, so some buckets may have more tags than others.
# Because of this, the sorted list of remaining tags is divided by the
# remaining 'buckets' to evenly distribute the remainder of the tags and
# to fill as many 'buckets' as possible up to the largest font size.
set min_tags [expr {$total_tags / $tag_sizes}]
set bucket_count 1
set bucket_items 0
set tags_set 0
foreach tagdata [lsort -integer -index 0 $taglist] {
set tag_count [lindex $tagdata 0]
set tag [lindex $tagdata 1]
# If we've met the minimum number of tags for this class and the current
# tag does not equal the last tag, we can proceed to the next class.
if {$bucket_items >= $min_tags && $last_count != $tag_count && $bucket_count < $tag_sizes} {
incr bucket_count
set bucket_items 0
# Calculate a new minimum number of tags for the remaining buckets.
set remaining_tags [expr {$total_tags - $tags_set}]
set min_tags [expr {$remaining_tags / $bucket_count}]
}
# Set the tag to the current class.
set cloud_buckets($tag) $bucket_count
incr bucket_items
incr tags_set
set last_count $tag_count
}
}
}Simple test/demo. This reads a list of tags (keywords) from stdin, counts them, and then calls the above cloud_tags procedure and generates the HTML fragment Tag Cloud
proc add_tag { cloudname tag } {
upvar ${cloudname}_counts cloud_counts
if {[info exists cloud_counts($tag)]} {
incr cloud_counts($tag)
} else {
set cloud_counts($tag) 1
}
return $cloud_counts($tag)
}
while {[gets stdin tag] > 0} {
add_tag testcloud $tag
}
# only use sizes 1-7
cloud_tags testcloud 7
puts {<div style="float:left; text-align:center; margin:0px 20px 0px 20px;">}
foreach tag [lsort -dictionary [array names testcloud_counts]] {
puts "<span class=\"tsxl$testcloud_buckets($tag)\">$tag</span>"
}
puts {</div>}The page would need CSS classes with styles for the font sizes, such as:
.tsxl1 {font-size: 85%;}
.tsxl2 {font-size: 95%;}
.tsxl3 {font-size: 105%;}
.tsxl4 {font-size: 120%;}
.tsxl5 {font-size: 130%;}
.tsxl6 {font-size: 140%;}
.tsxl7 {font-size: 150%;}Perhaps somebody will find this useful.
