At the moment, the way 2D works in Gibber is unfortunately very different from how the rest of Gibber works 1. For 2D drawing, there is a single object use, Canvas
. The Canvas
object allows you to create shapes, fill them, draw lines.. all the normal 2D drawing activities. It is basically a mirror of HTMLs canvas context, but with some sugar added to make programming with it a little more expressive.
One aspect of 2D drawing that is relatively unique to Gibber is that it can be combined with the use of OpenGL shaders. These shaders can quickly add a great deal of visual complexity to simple shapes and patterns, and can be manipulated by audio using Gibber's mapping abstractions. These shaders can only be used if you are using a WebGL enabled browser on a computer with a graphics card that also supports WebGL. Note that using shaders significantly increases CPU usage, as Gibber will constantly be converting your drawing to a texture that can be transferred to the GPU of your computer.
A weaving in Gibber by yaxu 2. © Alex McLean (http://yaxu.org/), GPL 3.0
Let's get a square drawn on the screen:
canvas = Canvas()
canvas.square( 0,0,400 ) // x, y and size
canvas.fill( 'red' )
By default Gibber will create a canvas that fills the entire window and sits in the background of Column
objects. Our second line of code tells Gibber to create a square path using a provided position and size. This actually doesn't draw the square; to do that we need to use the stroke
or fill
method of the canvas object. If we pass a color (this can be any type of CSS color, for example 'red', 'rgb(255,0,0)', or '#ff00000') that color will be used to fill the square; otherwise the last used fill color will be employed.
When working with background canvas objects, it can quickly become difficult to read superimposed code. The keystrokes Ctrl-Shift-Alt-+
and Ctrl-Shift-Alt--
allow you to change the background opacity of code to make it more legible.
Alternatively, we can also create a new Column
object and use this to display our canvas.
column = Column()
canvas = Canvas( column )
canvas.square( 0,0,400 ) // x, y and size
canvas.fill( 'blue' )
canvas.stroke( 'red', 8 ) // color and line width
You can clear the canvas at any time with a call to canvas.clear()
.
We've already seen that the canvas can easily draw a square. There are three other shapes with built-in convenience methods:
Let's draw few of these. Note that we can easily chain method calls:
a = Canvas()
a.rectangle( 0,0,200,400 ).fill('red')
a.circle( a.center.x, a.center.y, 300 ).stroke('green', 4 )
a.polygon( a.center.x, a.center.y, 150, 7).fill( 'blue' )
As shown above, the Canvas
object has a center
property that we can use for positioning.
We can rotate the canvas in between drawing operations to create either a series of rotations or an animation of rotation. We can also call the fade()
method in between drawing operations to fade the canvas out. Using the two of these together can create interesting effects.
a = Canvas()
count = 10
for( var i = 0; i < count; i++ ) {
a.restore()
a.fade( .1 )
a.save()
a.rotate( .1 * i )
a.polygon( a.center.x, a.center.y, 250, 5).fill( a.randomColor() )
}
There are a couple of points to note. First, we save
and restore
the canvas context before rotation. This allows us to easily return the canvas to the state it was at before it was rotated. In this case, we want to apply the fade to the unrotated canvas, so we call restore
immediately before our call to fade
(try removing the call to restore and see what happens). Second, we use the randomColor
method to generate a random color for every polygon we create.
The canvas has a draw
method that you can override that is called at 30 frames per second. Before calling the draw
method, the state of the canvas context is saved; after calling draw
the state is restored. This means you can rotate or translate as you see fit within draw without having to worry about managing state in between frames of animation.
a = Canvas()
var rotation = 0
a.draw = function() {
a.fade(.1)
rotation += .05
a.rotate( rotation )
a.polygon( a.center.x, a.center.y, 250, 3).stroke( a.randomColor(), 10 )
}
The Mouse Trails tutorial 3 is a simple example of using the draw
method for animation while polling the mouse position.
Using shaders is discussed more here (not written yet ), but for now I'd point out that it is fun and simple to combine shaders with 2D drawing in Gibber. Here's a simple example using the Kaleidoscope
and Dots
shader, that also tracks the Mouse
position.
Mouse.on() // turn on mouse tracking
a = Canvas()
a.draw = function() {
a.fade( .05 )
a.polygon(
Mouse.x * a.width,
Mouse.y * a.height,
300, rndi(5,10)
)
}
b = Dots()
b.scale = Mouse.X // continuous mapping
c = Kaleidoscope()
c.sides = Mouse.Y // continuous mapping
See the tutorial on mappings in Gibber if you're not familiar with the continuous mapping syntax.
At some point in the future I may change to more of an object-oriented approach for 2D in Gibber... but the ability to draw directly to the canvas will always be available.