asobi 0.1.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.
data/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # asobi
2
+
3
+ [日本語版](README.ja.md)
4
+
5
+ asobi (Japanese for “play”) is a Ruby library for making SVG pictures and looping animations. A picture is a Ruby value. Here, eight copies of a petal form a flower, and the whole flower turns once every eight seconds.
6
+
7
+ ```ruby
8
+ require "asobi"
9
+
10
+ flower = petal.repeat(8).in_fan.colored(:crimson, :navy)
11
+ show flower.spin(every: 8)
12
+ ```
13
+
14
+ <img src="docs/first-sketch.svg" width="240" alt="Eight petals, alternating crimson and navy, arranged around a common centre.">
15
+
16
+ ## Run your first sketch
17
+
18
+ You'll need Ruby **3.2 or newer** and a browser. The library uses only Ruby's standard library. Run this command from the repository root.
19
+
20
+ ```sh
21
+ ruby exe/asobi
22
+ ```
23
+
24
+ asobi creates `play.rb` with the flower sketch above and opens a live preview in your browser. If `play.rb` already exists, it opens that sketch. Change `repeat(8)` to `repeat(12)` and save to add four petals. Change `every: 8` to `every: 2` to make the flower turn once every two seconds. Press Ctrl-C in the terminal to stop the preview.
25
+
26
+ To open another sketch, pass its path, for example `ruby exe/asobi examples/flower.rb`. Add `--no-open` to open the URL printed in the terminal yourself. The server looks for a free port starting at 4567. Use `--port 5000` to start the search at 5000.
27
+
28
+ ### Install the command for use in other folders
29
+
30
+ Build and install the gem from the repository root.
31
+
32
+ ```sh
33
+ gem build asobi.gemspec --output asobi.gem
34
+ gem install --local ./asobi.gem
35
+ ```
36
+
37
+ You can now run `asobi` in a sketch folder to start with `play.rb`, or open an existing sketch with `asobi sketch.rb`. A file that contains `require "asobi"` and calls `show` also runs with `ruby sketch.rb`.
38
+
39
+ ## Build pictures in Ruby
40
+
41
+ To try each example in this section, keep `require "asobi"` at the top of `play.rb` and replace the lines beneath it.
42
+
43
+ ### Arrange and combine shapes
44
+
45
+ `repeat` makes copies in the same spot. `in_fan` turns them around a shared centre. With `a + b`, the second picture sits on top, so a small circle added last becomes the centre of the flower.
46
+
47
+ ```ruby
48
+ petals = petal(40, 16).repeat(8).in_fan.colored(:crimson, :navy)
49
+ flower = petals + circle(5).colored(:gold)
50
+ show flower.beside(flower.scale(0.6), gap: 12)
51
+ ```
52
+
53
+ The smaller flower is a new picture. The original keeps its size. Lengths are relative to the other shapes in the sketch, and the preview fits the drawing to the screen. Angles are measured in degrees clockwise, so `rotate(90)` makes a quarter turn.
54
+
55
+ ### Turn data into a picture
56
+
57
+ With `to_art`, a number becomes a disc, and an array becomes a row of pictures in element order. Give it a shape with `as:` to use each number as the size of that shape. Here the Fibonacci sequence becomes a row of growing stars.
58
+
59
+ ```ruby
60
+ fib = [1, 1, 2, 3, 5, 8, 13, 21]
61
+ show fib.to_art(as: star).colored(:gold, :orange), on: :black
62
+ ```
63
+
64
+ ### Give your own class a visual form
65
+
66
+ Define `to_art` to return the picture for an instance. Include `Asobi::Art` when you also want to call methods such as `spin` on the instance itself.
67
+
68
+ ```ruby
69
+ class Flower
70
+ include Asobi::Art
71
+
72
+ def initialize(count, color)
73
+ @count = count
74
+ @color = color
75
+ end
76
+
77
+ def to_art
78
+ petal.repeat(@count).in_fan.colored(@color)
79
+ end
80
+ end
81
+
82
+ show Flower.new(7, :coral).spin(every: 4)
83
+ ```
84
+
85
+ ### Animate a group
86
+
87
+ The dots below grow to twice their size and shrink back over two seconds. Placing `one_by_one` after `pulse` offsets the start of each dot's motion, sending a pulse around the ring.
88
+
89
+ ```ruby
90
+ dots = circle(5).repeat(24).in_ring.colored(:gold, :orange)
91
+ show dots.pulse(every: 2, to: 2).one_by_one, on: :black
92
+ ```
93
+
94
+ | To do this | Try |
95
+ | --- | --- |
96
+ | Start with a shape | `circle`, `rect`, `star`, `text`, `path`, `wave`, `heart`, `petal` |
97
+ | Arrange copies | `in_row` for a line, `in_grid` for tiles, `in_ring` for a circle, `in_spiral` for a spiral |
98
+ | Add colour and texture | `colored(from: :gold, to: :crimson)` for a gradient, `glowing` for a halo, `shadowed` for a shadow, `clipped_by` for a cut-out |
99
+ | Add motion | `spin` to rotate, `sway` to rock, `orbit` to circle a point, `trail` for fading copies, `animate` for your own motion |
100
+ | Set up the preview | In `show`, use `on:` for the background colour, `paper:` for page dimensions, or `grain: true` for film grain |
101
+
102
+ ## Preview, export and record
103
+
104
+ When a saved sketch has an error, the live preview keeps the last picture that worked and displays the error underneath. Press **C** in the browser to toggle the source code. In browsers that support recording, **R** starts recording and **R** again downloads the clip as MP4 or WebM.
105
+
106
+ Use `save` to write a still picture as SVG or a playable animation as a self-contained HTML file. You can open the HTML file directly in a browser. The example writes both files to the current directory.
107
+
108
+ ```ruby
109
+ flower = petal.repeat(8).in_fan.colored(:crimson, :navy)
110
+ motion = flower.spin(every: 4)
111
+
112
+ flower.save("flower.svg")
113
+ motion.save("flower.html")
114
+ show motion
115
+ ```
116
+
117
+ To save a particular frame, use `motion.at(1).save("frame.svg")` for the picture one second into the loop.
118
+
119
+ ## Use asobi from an existing Ruby program
120
+
121
+ Load `asobi/pure` to keep shape names under `Asobi` and leave Ruby's built-in classes untouched. Save the following as `export.rb` in the repository root and run `ruby -Ilib export.rb`. It writes three navy stars to `stars.svg` in the same directory.
122
+
123
+ ```ruby
124
+ require "asobi/pure"
125
+
126
+ stars = Asobi.art([3, 1, 4], as: Asobi.star)
127
+ stars.colored(:navy).save("stars.svg")
128
+ ```
129
+
130
+ ## Examples and reference
131
+
132
+ Each file is a complete sketch that you can run with `ruby exe/asobi` followed by its path. [Browse all examples](examples/README.md)
133
+
134
+ - Turn a number sequence into a row of stars. [Number sequence](examples/sequence.rb)
135
+ - Layer glow, screen blending and trails. [Neon flower](examples/neon_flower.rb)
136
+ - Give a custom class an orbit and compose several instances into one picture. [Planets](examples/planets.rb)
137
+
138
+ The documentation site includes a step-by-step guide, a gallery, a reference with method defaults and examples, and a browser playground. To build it locally, run the following from the repository root.
139
+
140
+ ```sh
141
+ bundle install
142
+ bundle exec rake site
143
+ ```
144
+
145
+ Open `_site/index.html` in a browser for English or `_site/ja/index.html` for Japanese. Editing and running code in the playground requires an internet connection to load its Ruby WebAssembly runtime from a CDN.
146
+
147
+ ## Development
148
+
149
+ After `bundle install`, run `bundle exec rake test`. The suite checks the library and CLI, builds the site with test data, and runs the sketch examples from the gallery and both READMEs. For the design of pictures, motion and the `to_art` protocol, see the [design notes (Japanese)](DESIGN.md).
150
+
151
+ ## Bugs and feature ideas
152
+
153
+ Issues are welcome in English or Japanese. For a bug, a small sketch that shows the problem helps us reproduce it. For an idea, tell us what you'd like to draw or animate. [Open an issue](https://github.com/Hayato1031/asobi/issues/new/choose)
154
+
155
+ ## License
156
+
157
+ [MIT](LICENSE)
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-23.2 -23.2 46.4 46.4" width="512" height="512" stroke-linejoin="round" stroke-linecap="round"><rect x="-23.2" y="-23.2" width="46.4" height="46.4" fill="white"/><g><g fill="crimson"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g><g transform="matrix(0.707 0.707 -0.707 0.707 0 0)"><g fill="navy"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g></g><g transform="matrix(0 1 -1 0 0 0)"><g fill="crimson"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g></g><g transform="matrix(-0.707 0.707 -0.707 -0.707 0 0)"><g fill="navy"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g></g><g transform="matrix(-1 0 0 -1 0 0)"><g fill="crimson"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g></g><g transform="matrix(-0.707 -0.707 0.707 -0.707 0 0)"><g fill="navy"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g></g><g transform="matrix(0 -1 1 0 0 0)"><g fill="crimson"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g></g><g transform="matrix(0.707 -0.707 0.707 0.707 0 0)"><g fill="navy"><path d="M 0 0 C -7.385 -3 -5.908 -15 0 -20 C 5.908 -15 7.385 -3 0 0 Z"/></g></g></g></svg>
data/exe/asobi ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # asobi play.rb がなければ見本を作り、ライブ表示する
5
+ # asobi FILE.rb そのスケッチをライブ表示する
6
+ # asobi version
7
+ #
8
+ # Test seam: ASOBI_CREATE_ONLY=1 stops right after play.rb has been created
9
+ # (or found), before the live view starts.
10
+
11
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__) # a checkout; an installed gem is already on the path
12
+ require "optparse"
13
+ require "asobi"
14
+ require "asobi/live"
15
+
16
+ module Asobi
17
+ Messages.add(:cli_missing,
18
+ en: "asobi can't find %{file}. Check the name, like: asobi flower.rb (or run asobi alone to start with play.rb)",
19
+ ja: "%{file} が見つかりません。名前を確かめてください。例:asobi flower.rb(asobi だけで実行すると play.rb から始められます)")
20
+ Say.add(:cli_created,
21
+ en: "asobi: made %{file} for you. Change anything in it and save.",
22
+ ja: "asobi: %{file} を作りました。好きなところを書き換えて保存してください。")
23
+ Messages.add(:cli_bad_option,
24
+ en: "asobi doesn't know %{got}. See the options with, like: asobi --help",
25
+ ja: "%{got} は知りません。使えるオプションは次で見られます。例:asobi --help")
26
+ Say.add(:cli_help,
27
+ en: <<~TEXT,
28
+ asobi: play with pictures in Ruby
29
+
30
+ asobi make play.rb if it is missing, and show it live
31
+ asobi FILE.rb show that sketch live (save the file and the picture follows)
32
+ asobi version
33
+ asobi help
34
+
35
+ options:
36
+ --port N start looking for a free port at N (default 4567)
37
+ --no-open don't open the browser
38
+ TEXT
39
+ ja: <<~TEXT)
40
+ asobi: Ruby で絵と遊ぶ
41
+
42
+ asobi play.rb がなければ見本を作って、ライブ表示します
43
+ asobi FILE.rb そのスケッチをライブ表示します(保存すると絵が変わります)
44
+ asobi version
45
+ asobi help
46
+
47
+ オプション:
48
+ --port N ポート N から空きを探す(初期値 4567)
49
+ --no-open ブラウザを開かない
50
+ TEXT
51
+ Say.add(:cli_starter_comment,
52
+ en: "Change anything and save. The picture follows.",
53
+ ja: "どこでも書き換えて保存してください。絵がついてきます。")
54
+
55
+ module CLI
56
+ module_function
57
+
58
+ def starter
59
+ <<~RUBY
60
+ require "asobi"
61
+
62
+ # #{Say.text(:cli_starter_comment)}
63
+ flower = petal.repeat(8).in_fan.colored(:crimson, :navy)
64
+
65
+ show flower.spin(every: 8)
66
+ RUBY
67
+ end
68
+
69
+ def main(argv)
70
+ options = { port: Live::DEFAULT_PORT, open: true }
71
+ parser = OptionParser.new do |o|
72
+ o.on("--port N", Integer) { options[:port] = _1 }
73
+ o.on("--no-open") { options[:open] = false }
74
+ o.on("-h", "--help") { options[:help] = true }
75
+ o.on("-v", "--version") { options[:version] = true }
76
+ end
77
+ begin
78
+ args = parser.parse(argv)
79
+ rescue OptionParser::ParseError => e
80
+ warn Messages.text(:cli_bad_option, got: e.args.first || e.message)
81
+ return 1
82
+ end
83
+
84
+ return puts(Say.text(:cli_help)) || 0 if options[:help] || args.first == "help"
85
+ return puts(Asobi::VERSION) || 0 if options[:version] || args.first == "version"
86
+
87
+ file = args.first || "play.rb"
88
+ if args.empty? && !File.exist?(file)
89
+ File.write(file, starter, encoding: "UTF-8")
90
+ puts Say.text(:cli_created, file:)
91
+ elsif !File.file?(file)
92
+ warn Messages.text(:cli_missing, file:)
93
+ return 1
94
+ end
95
+ return 0 if ENV["ASOBI_CREATE_ONLY"] == "1"
96
+
97
+ Live.run(file, port: options[:port], open: options[:open])
98
+ 0
99
+ rescue IOError, SystemCallError => e
100
+ warn e.message
101
+ 1
102
+ end
103
+ end
104
+ end
105
+
106
+ exit Asobi::CLI.main(ARGV)