A neat library and DSL for creating graphics using SVG
Add to shards.yml
dependencies: celestine: github: redcodefinal/celestine
Documentation is cool, you can view the docs for Celestine at:
SVG Stuff It Can Do
- rect as rectangle
- g as group
- animateTransform as animate_transform_rotate
- animateMotion as animate_motion
- mpath via animate_motion
- Filters :( gonna need to design a whole system just for those
- Full SVG support for all elements and attributes
First, all drawing is done through
Celestine.draw this returns a string SVG element, or works on an
IO. You can easily embed this into webpages for dynamic server side drawing of assets.
Celestine.draw takes a block which takes a
ctx in the examples), the basis of all DSL calls in Celestine.
Celestine.draw do |ctx| end
You can create objects to be drawn with one of two methods.
Celestine.draw do |ctx| # use this context's DSL methods ctx.rectangle do |r| r.x = 10 r.y = 100 r.fill = "black" r # You must return the drawable item at the end of the DSL method. end # Create the object and add it manually r = Celestine::Rectangle.new r.x = 10 r.y = 100 r.fill = "black" ctx << r end
All context methods such as
circle take a block that takes their respective types, and needs to have an object of that type returned.
A short list of the Celestine types that can be used by
Celestine::Meta::Context and their DSL methods
- Celestine::Rectangle -> rectangle
- Celestine::Circle -> circle
- Celestine::Ellipse -> ellipse
- Celestine::Path -> path
- Celestine::Marker -> marker
- Celestine::Mask -> mask
- Celestine::Text -> text
- Celestine::Group -> group
- Celestine::Image -> image
- Celestine::Use -> use
- Celestine::Filter -> filter
Celestine::Use can be used to save space in an SVG, and reuse certain elements without the need to copy the entire object into your SVG document.
The only caveat is that the
use SVG element cannot change attributes it both doesn't own itself, or ones that have been set by it's ancestor, except in the case of
height. For example, the
use element cannot change the
radius of a
circle even if the
radius attribute had never been set.
Celestine::Use also requires that the drawable it is copying has an
id set. If not
Celestine::Use will still let you reference an ID that doesn't exist if you run
use with a string
You can do this only with
ctx in the examples).
Celestine::Marker cannot define drawables.
Celestine.draw do |ctx| # Only `Celestine::Meta::Context` is allowed to "define" objects. ctx.rectangle(define: true) do |r| # Set r's x, y, width, height and other attributes here r.id = "our-rect" # YOU MUST SET AN ID TO BE ABLE TO REUSE A COMPONENT, OR THERE IS NO WAY TO REFERENCE IT. r end ctx.use do |use| use.x = 100 use.y = 3000 use.width = 99 use.height = 99 use.fill = "black" # Cannot use this because `Celestine::Use` cannot change attributes specific to a drawable # only the attributes its shares with the type it's using. # use.radius_x = 1000 use end end
Most drawables can use the
transform method to translate, rotate, scale, and skew drawables.
Celestine.draw do |ctx| ctx.rectangle do |r| # Set r's x, y, width, height and other attributes here r.transform do |t| t.rotate(60, 0, 0) # Rotate by 60 degrees, at origin 0, 0 t.translate(50, 60) # Move by 50, 60 t.scale(100, 100) # Scale up 100x t.skew_x(10) t.skew_y(10) t.matrix(1.0, 1.0, 1.0, 1.0, 1.0, 1.0) t end r end end
You can use masks to make complicated shapes.
Celestine.draw do |ctx| our_mask = ctx.mask do |mask| mask.id = "our-mask" # YOU MUST SET A UNIQUE ID FOR THE MASK! mask.rectangle do |r| # Works just like a group # Set r's x, y, width, height and other attributes here r.fill = "black" # Will make everything under it invisibile and transparent r.fill = "white" # Will make everything under it visible r end mask end ctx.circle do |c| # Set c's x, y, radius and other attributes here c.set_mask our_mask # Set with the mask object directly c.set_mask "our-mask" # Set via string id c end end
You can animate most simple SVG attributes using
animate and the
Celestine.draw do |ctx| ctx.circle do |c| # IF ANIMATING AN ATTRIBUTE DO NOT SET IT IN ANY WAY, IT WILL OVERRIDE THE ANIMATION, BLAME SVG NOT ME # EX: # Using `c.radius = 100` will ruin the animation. c.animate do |a| a.attribute = "r" # Choose it directly a.attribute = Celestine::Circle::Attrs::RADIUS # Choose it using the predefined constants a.from = 100 a.to = 200 a.duration = 10 a.repeat_count = "indefinite" a end c end end
You can also animate more complicated attributes and transition using the
values array. SVG attempts to interpolate these values when possible.
If you use
values and don't also use the
key_times array to designate the timing changes, it will evenly space the animation values in the alotted time.
Celestine.draw do |ctx| ctx.circle do |c| c.animate do |a| a.attribute = Celestine::Circle::Attrs::Fill a.values << "red" # Will start the color of the fill at red a.values << "blue" # Then interpolate to blue a.values << "red" # Then interpolate back to red. a.duration = 10 a.repeat_count = "indefinite" a end c.animate do |a| a.attribute = Celestine::Circle::Attrs::Stroke a.values << "red" # Will start the color of the fill at red a.values << "blue" # Then interpolate to blue a.values << "red" # Then interpolate back to red. a.key_times << 0.0 # Change to red to start. You should almost always use 0.0 as your first key_times value a.key_times << 0.25 # Interpolate to blue at 25% of the duration a.key_times << 0.75 # Interpolate to red at 75% of the duration a_key_times << 1.0 # End the animation a.duration = 10 a.repeat_count = "indefinite" a end c end end
You can animate movement along paths using
Celestine.draw do |ctx| ctx.circle do |c| c.animate_motion do |a| # Make the path the animateMotion element will follow. a.mpath do |path| path.a_move(200, 200) path.r_line(20, 20) path.r_line(-20, 20) path.r_line(-20, -20) path.r_line(20, -20) path end a.duration = 10 a.repeat_count = "indefinite" a end c end end
You can animate transform elements using
Only rotate is supported right now.
Celestine.draw do |ctx| ctx.circle do |c| c.animate_transform_rotate do |a| a.use_from = true # Need to set these to allow `from` to render in the SVG element a.use_to = true # Need to set these to allow `to` to render in the SVG element a.from_angle = 0 a.to_angle = 360 a.from_origin_x = a.from_origin_y = a.to_origin_x = a.to_origin_y = 0 a.duration = 10 a.repeat_count = "indefinite" a end c end end
You can do some cool filtering using the
Celestine.draw do |ctx| our_filter = ctx.filter do |f| f.id = "our-filter" # ALWAYS SET ID FOR A FILTER OR IT CAN'T BE USED! f.blur do |b| b.standard_deviation = 5 b end f end ctx.circle do |c| # Either c.set_filter("our-filter") # or c.set_filter(our_filter) end c end end
- Celestine::Filter::Blend -> blend
- Celestine::Filter::Blur -> blur
- Celestine::Filter::ColorMatrix -> color_matrix
- Celestine::Filter::ComponentTransfer -> component_transfer
- Celestine::Filter::Composite -> composite
- Celestine::Filter::DisplacementMap -> displacement_map
- Celestine::Filter::Flood -> flood
- Celestine::Filter::Merge -> merge
- Celestine::Filter::Morphology -> morphology
- Celestine::Filter::Offset -> offset
- Celestine::Filter::SpecularLighting -> specular_lighting
- Celestine::Filter::Tile -> tile
- Celestine::Filter::Turbulence -> turbulence
If you'd like to see how the logo was made, check out make_logo
Here are some more intricate examples. procedural_art
Real world examples made with Celestine. All of these are flat SVG files with no JS inside, using only functions built into Celestine.
HMU via issues or make a pull request or something I don't know.
- Fork it (https://github.com/redcodefinal/celestine/fork)
- Create your feature branch (
git checkout -b my-new-feature)
- Commit your changes (
git commit -am 'Add some feature')
- Push to the branch (
git push origin my-new-feature)
- Create a new Pull Request
- Ian Rash - creator and maintainer