{"id":15405685,"url":"https://github.com/zverok/magic_cloud","last_synced_at":"2025-04-09T13:05:34.858Z","repository":{"id":28004741,"uuid":"31499147","full_name":"zverok/magic_cloud","owner":"zverok","description":"Simple pretty word cloud for Ruby","archived":false,"fork":false,"pushed_at":"2020-06-29T13:23:43.000Z","size":707,"stargazers_count":85,"open_issues_count":6,"forks_count":26,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-04-24T18:10:00.715Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zverok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-01T14:40:40.000Z","updated_at":"2023-07-16T10:22:39.000Z","dependencies_parsed_at":"2022-08-20T13:10:38.293Z","dependency_job_id":null,"html_url":"https://github.com/zverok/magic_cloud","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zverok%2Fmagic_cloud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zverok%2Fmagic_cloud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zverok%2Fmagic_cloud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zverok%2Fmagic_cloud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zverok","download_url":"https://codeload.github.com/zverok/magic_cloud/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248045230,"owners_count":21038553,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-01T16:18:14.500Z","updated_at":"2025-04-09T13:05:34.837Z","avatar_url":"https://github.com/zverok.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"MagicCloud - simple pretty word cloud for Ruby\n==============================================\n\n[![Gem Version](https://badge.fury.io/rb/magic_cloud.svg)](http://badge.fury.io/rb/magic_cloud)\n\n**MagicCloud** is simple, pure-ruby library for making pretty\n[Wordle](http://www.wordle.net/)-like clouds. It uses RMagick as graphic\nbackend.\n\nUsage\n-----\n\n```ruby\nwords = [\n  ['test', 50],\n  ['me', 40],\n  ['tenderly', 30],\n  # ....\n]\ncloud = MagicCloud::Cloud.new(words, rotate: :free, scale: :log)\n\n# To save to file, if not redering it for a page\nimg = cloud.draw(960, 600) #default height/width\nimg.write('test.png')\n```\n\nOr from command-line:\n\n```\n./bin/magic_cloud --textfile samples/cat-in-the-hat.txt -f test.png --rotate free --scale log\n```\n\nResulting in:\n\n\u003cimg src=\"https://raw.github.com/zverok/magic_cloud/master/samples/cat.png\" alt=\"Sample word cloud\"/\u003e\n\nInstallation\n------------\n\n```\ngem install magic_cloud\n```\n\nrmagick is requirement, and it needs compilation, so you may expect\nproblems in non-compiler-friendly environment (Windows).\n\nOrigins\n-------\n\nAt first, it was a straightforward port of [d3.layout.cloud.js](https://github.com/jasondavies/d3-cloud)\nby Jason Davies, which, I assume, is an implementation of Wordle algorithm.\n\nThen there was major refactoring, to make code correspond to Ruby\nstandards (and be understandable to poor dumb me).\n\nThen collision algorithm was rewritten from scratch.\n\nAnd now we are here.\n\nReferences:\n* https://github.com/jasondavies/d3-cloud\n* http://stackoverflow.com/questions/342687/algorithm-to-implement-a-word-cloud-like-wordle\n* http://static.mrfeinberg.com/bv_ch03.pdf\n\nPerformance\n-----------\n\nIt's reasonable for me. On my small Thinkpad E330, some 50-words cloud\nimage, size 700×500, are typically generated in \u003c3sec. It's not that cool,\nyet not too long for you to fell asleep.\n\nThe time of cloud making depends on words count, size of image\n(it's faster to find place for all words on larger image) and used rotation\nalgorithm (vertical+horizontal words only is significantly faster - and,\non my opinion, better looking - than \"cool\" free-rotated-words cloud). It\neven depends on font - dense font like Impact takes more time to lay\nout than sparse Tahoma.\n\nMajor performance eater is perfect collision detection, which Wordle-like\ncloud needs. MagicCloud for now uses really dumb algorithm with some\nnot-so-dumb optimizations. You can look into\n`lib/magic_cloud/collision_board.rb` - everything can be optimized is\nthere; especially in `CollisionBoard#collides?` method.\n\nI assume, for example, that naive rewriting of code in there as a C\nextension would help significantly.\n\nAnother possible way is adding some smart tricks, which eliminate as much\nof pixel-by-pixel comparisons as possible (some of already made are\ncriss-cross intersection check, and memoizing of last crossed sprite).\n\nMemory effectiviness\n--------------------\n\nBasically: it's not.\n\nPlain Ruby arrays are used to represent collision bitmasks (each array\nmember stand for 1 bit), so, for example, 700×500 pixel cloud will requre\ncollision board size `700*500` (i.e. 350k array items only for board, and\nslightly less for all sprites).\n\nIt should be wise to use some packing (considering each Ruby Fixmnum can\nrepresent not one, but whole 32 bits). Unfortunately, all bit array\nlibraries I've tried are causing major slowdown of cloud computation.\nWith, say, 50 words we'll have literally millions of operation\n`bitmask#[]` and `bitmask#[]=`, so, even methods\nlike `Fixnum#\u0026` and `Fixnum#|` (typically used for bit array representation)\nare causing significant overload.\n\nConfiguration\n-------------\n\n```ruby\ncloud = MagicCloud.new(words, palette: palette, rotate: rotate)\n```\n\n* `:palette` (default is `:color20`):\n  * `:category10`, `:category20`, ... - from [D3.js](https://github.com/d3/d3-scale/blob/master/README.md#category-scales)\n  * `[array, of, colors]` - each color should be hex color, or any other RMagick color string (See \"Color names at http://www.imagemagick.org/RMagick/doc/imusage.html)\n  * any lambda, accepting `(word, index)` and returning color string\n  * any object, responding to `color(word, index)` - so, you can make color\n    depend on tag text, not only on its number in tags list\n* `:rotate` - rotation algorithm:\n  * `:square` (only horizontal and vertical words) - it's default\n  * `:none` - all words are horizontal (looks boooring)\n  * `:free` - any word rotation angle, looks cool, but not very readable\n    and slower to layout\n  * `[array, of, angles]` - each of possible angle should be number 0..360\n  * any lambda, accepting `(word, index)` and returning 0..360\n  * any object, responding to `rotate(word, index)` and returning 0..360\n* `:scale` - how word sizes would be scaled to fit into (FONT_MIN..FONT_MAX) range:\n  * `:no` - no scaling, all word sizes are treated as is;\n  * `:linear` - linear scaling (default);\n  * `:log` - logarithmic scaling;\n  * `:sqrt` - square root scaling;\n* `:font_family` (Impact is default, ex. Arial, Helvetica, Futura).\n* `:font` - Full path to custom font file. Overwrite `:font_family`.\n\nCurrent state\n-------------\n\nThis library is extracted from a real-life project. It should be\npretty stable (apart from bugs introduced during extraction and gemification).\n\nWhat it really lacks for now, is thorough (or any) testing, and\nsome more configuration options.\n\nAlso, while core algorithms (collision_board.rb, layouter.rb) are pretty\naccurately written and documented, \"wrapping code\" (options parsing and\nso on) are a bit more chaotic - it's subject to refactor and cleanup.\n\nAll feedback, usage examples, bug reports and feature requests are appreciated!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzverok%2Fmagic_cloud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzverok%2Fmagic_cloud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzverok%2Fmagic_cloud/lists"}