[cairo-commit] [cairo-www] src/animationrotation.mdwn

Carl Worth cworth at freedesktop.org
Mon Nov 26 02:05:30 PST 2007


 src/animationrotation.mdwn |  229 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 229 insertions(+)

New commits:
commit 631ca727bb2ab4725f99821154342e8acaaa7954
Author: Donn <donn.ingle at gmail.com>
Date:   Mon Nov 26 02:05:30 2007 -0800

    new sample

diff --git a/src/animationrotation.mdwn b/src/animationrotation.mdwn
index 0eb0298..d774710 100644
--- a/src/animationrotation.mdwn
+++ b/src/animationrotation.mdwn
@@ -154,3 +154,232 @@ I tried to comment it thoroughly, so it will 'splain itself.
         gtk.main( )
         
     run( MyStuff )
+
+## Here's a longer version with many things moving
+
+I spent some time (and got some help from the list - thanks) working on a better version to demonstrate how to move many things around. This one also shows how you can use context.save() to create what I call "bubbles" of private space on the screen where different rules can apply for a short time. By putting bubbles within other bubbles you can create parent->child relationships. The sample has a red square that is a parent to a green one. What the red does, the green does too.
+
+This also shows primitive mouse hit detection (prints to the console, so hold onto your seats because the budget was just blown baby!) on the green square. 
+
+I hope this help someone out there.
+
+
+
+    ##    cairo demos Copyright  (C)  2007 Donn.C.Ingle
+    ##
+    ##    Contact: donn.ingle at gmail.com - I hope this email lasts.
+    ##
+    ##    This program is free software; you can redistribute it and/or modify
+    ##    it under the terms of the GNU General Public License as published by
+    ##    the Free Software Foundation; either version 2 of the License, or
+    ##     ( at your option )  any later version.
+    ##
+    ##    This program is distributed in the hope that it will be useful,
+    ##    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    ##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    ##    GNU General Public License for more details.
+    ##
+    ##    You should have received a copy of the GNU General Public License
+    ##    along with this program; if not, write to the Free Software
+    ##    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    import pygtk
+    import gtk, gobject, cairo
+    from gtk import gdk
+
+    class Screen( gtk.DrawingArea ):
+        """ This class is a Drawing Area"""
+        def __init__( self, w, h, speed ):
+            super( Screen, self ).__init__( )
+            ## Old fashioned way to connect expose. I don't savvy the gobject stuff.
+            self.connect ( "expose_event", self.do_expose_event )
+            ## We want to know where the mouse is:
+            self.connect ( "motion_notify_event", self._mouseMoved )
+            ## More GTK voodoo : unmask events
+            self.add_events ( gdk.BUTTON_PRESS_MASK |   gdk.BUTTON_RELEASE_MASK |   gdk.POINTER_MOTION_MASK )        
+            ## This is what gives the animation life!
+            gobject.timeout_add( speed, self.tick ) # Go call tick every 'speed' whatsits.
+            self.width, self.height = w, h
+            self.set_size_request ( w, h )
+            self.x, self.y = 11110,11111110 #unlikely first coord to prevent false hits.
+            
+        def tick ( self ):
+            """This invalidates the screen, causing the expose event to fire."""
+            self.alloc = self.get_allocation ( )
+            rect = gtk.gdk.Rectangle ( self.alloc.x, self.alloc.y, self.alloc.width, self.alloc.height )
+            self.window.invalidate_rect ( rect, True )        
+            return True # Causes timeout to tick again.
+        
+        ## When expose event fires, this is run
+        def do_expose_event( self, widget, event ):
+            self.cr = self.window.cairo_create( )
+            ## Call our draw function to do stuff.
+            self.draw( )
+            
+        def _mouseMoved ( self, widget, event ):
+            self.x = event.x
+            self.y = event.y
+            
+    class BunchOfStuff ( object ):
+        """Stores a bunch of data"""
+        def __init__ ( self, x=0, y=0, rx=0, ry=0, rot=0, sx=1, sy=1 ):
+            self.x = x
+            self.y = y
+            self.rx = rx
+            self.ry = ry
+            self.rot = rot
+            self.sx = sx
+            self.sy  = sy
+            
+    class MyStuff ( Screen ):
+        """This class is also a Drawing Area, coming from Screen."""
+        def __init__ ( self, w, h, speed):
+            Screen.__init__( self, w, h, speed )
+            
+            ## Setup three sets of data for the three objects to be drawn
+            self.red = BunchOfStuff ( x=50, y=-10, rx=50, ry=25 )
+            self.green = BunchOfStuff ( x=-10, y=10 )
+            self.blue = BunchOfStuff ( x=-70,y=30, sx=1, sy=1 )
+            
+            self.sign = +1 # to flip the blue animation's sign
+            
+        def setToCenter ( self ):
+            """Shift 0,0 to be in the center of page."""
+            matrix = cairo.Matrix ( 1, 0, 0, 1, self.width/2, self.height/2 )
+            self.cr.transform ( matrix ) # Make it so...
+        
+        def doMatrixVoodoo ( self, bos ):
+            """Do all the matrix mumbo to get stuff to the right place on the screen."""
+            ThingMatrix =cairo.Matrix ( 1, 0, 0, 1, 0, 0 )
+            ## Next, move the drawing to it's x,y
+            cairo.Matrix.translate ( ThingMatrix, bos.x, bos.y )
+            self.cr.transform ( ThingMatrix ) # Changes the context to reflect that
+            ## Now, change the matrix again to:
+            if bos.rx != 0 and bos.ry != 0 : # Only do this if there's a special reason
+                cairo.Matrix.translate( ThingMatrix, bos.rx, bos.ry ) # move it all to point of rotation
+            cairo.Matrix.rotate( ThingMatrix, bos.rot ) # Do the rotation
+            if bos.rx != 0 and bos.ry != 0 :        
+                cairo.Matrix.translate( ThingMatrix, -bos.rx, -bos.ry ) # move it back again
+            cairo.Matrix.scale( ThingMatrix, bos.sx, bos.sy ) # Now scale it all
+            self.cr.transform ( ThingMatrix ) # and commit it to the context
+
+        def draw( self ):
+            cr = self.cr # Shabby shortcut.
+            
+            #---------TOP LEVEL - THE "PAGE"
+            self.cr.identity_matrix  ( ) # VITAL LINE :: I'm not sure what it's doing.
+            self.setToCenter ( )
+            
+            #----------FIRST LEVEL
+            cr.save ( ) # Creates a 'bubble' of private coordinates. Save # 1
+           
+            ## RED - draw the red object
+            self.doMatrixVoodoo ( self.red )        
+            self.drawCairoStuff ( self.red  )
+            
+            #---------- SECOND LEVEL - RELATIVE TO FIRST
+            cr.save ( ) #save 2
+            
+            ## GREEN - draw the green one
+            self.doMatrixVoodoo ( self.green )
+            self.drawCairoStuff ( self.green, col= ( 0,1,0 )  )
+            
+            ## Demonstrate how to detect a mouse hit on this shape:
+            ## Draw the hit shape :: It *should* be drawn exactly over the green rectangle.
+            self.drawHitShape ( )
+            cr.save ( ) # Start a bubble
+            cr.identity_matrix ( ) # Reset the matrix within it.
+            hit = cr.in_fill ( self.x, self.y ) # Use Cairo's built-in hit test
+            cr.new_path ( ) # stops the hit shape from being drawn
+            cr.restore ( ) # Close the bubble like this never happened.
+            
+            
+            cr.restore ( ) #restore 2 :: "pop" the bubble.
+            
+            ## We are in level one's influence now
+            
+            cr.restore ( ) #restore 1
+            ## Back on PAGE's influence now
+            
+            #-------- THIRD LEVEL  -- RELATIVE TO PAGE
+            cr.save ( ) # Creates a 'bubble' of private coordinates.
+            ## Draw the blue object
+            self.doMatrixVoodoo ( self.blue ) # within the bubble, this will not effect the PAGE
+            self.drawCairoStuff ( self.blue, col= ( 0,0,1 )  )
+            cr.restore ( )
+            
+            ## Back on the PAGE level again.
+            
+            #indicate center
+            self.drawCrosshair ( )         
+            self.guageScale ( )
+            
+            ## Let's animate the red object 
+            ## ( which *also* moves the green because it's a 'child' 
+            ## of the red by way of being in the same "bubble" )
+            self.red.rot += 0.01
+            ## Now animate the blue
+            self.blue.sx += self.sign *  0.1
+            if  self.blue.sx < 0 or self.blue.sx > 4: 
+                self.sign *= -1
+            self.blue.sy = self.blue.sx
+            
+            ## Print to the console -- low-tech special effects :)
+            if hit: print "HIT!", self.x, self.y
+            
+        def guageScale ( self ):
+            """Draw some axis so we can see where stuff is."""
+            c = self.cr
+            m = 0
+            for x in range ( 10,210,10 ):
+                m += 1
+                w = 10 + ( m % 2 * 10 )
+                if x == 100: w = 50
+                c.rectangle ( x,-w/2,1,w )
+                c.rectangle ( -x, -w/2, 1, w )
+                c.rectangle ( -w/2, x, w, 1 )
+                c.rectangle ( -w/2, -x , w, 1 )
+            c.set_source_rgb ( 0,0,0 )
+            c.fill ( )
+            
+        def drawCairoStuff ( self, bos, col= ( 1,0,0 ) ):
+            """This draws the squares we see. Pass it a BagOfStuff (bos) and a colour."""
+            cr = self.cr
+            ## Thrillingly, we draw a rectangle.
+            ## It's drawn such that 0,0 is in it's center.
+            cr.rectangle( -25, -25, 50, 50 )
+            cr.set_source_rgb( col[0],col[1],col[2] ) 
+            cr.fill( )
+            ## Now draw an axis
+            self.guageScale ( )
+            ## Now a visual indicator of the point of rotation
+            cr.set_source_rgb( 1,1,1 )
+            cr.rectangle ( bos.rx - 2, bos.ry - 2, 4, 4 )
+            cr.fill ( )
+            
+        ## Same as the rectangle we see. No fill.
+        def drawHitShape ( self ):
+            """Draws a shape that we'll use to test hits."""
+            self.cr.rectangle( -25, -25, 50, 50 ) # Same as the shape of the squares
+            
+        def drawCrosshair ( self ):
+            """Another visual aid."""
+            ctx = self.cr
+            ctx.set_source_rgb ( 0, 0, 0 )
+            ctx.move_to ( 0,10 )
+            ctx.line_to ( 0, -10 )
+            ctx.move_to ( -10, 0 )
+            ctx.line_to ( 10, 0 )
+            ctx.stroke ( )
+            
+            
+    def run( Widget, w, h, speed ):
+        window = gtk.Window( )
+        window.connect( "delete-event", gtk.main_quit )
+        widget = Widget( w, h, speed )
+        widget.show( )
+        window.add( widget )
+        window.present( )
+        gtk.main( )
+        
+    run( MyStuff, 400, 400, speed = 20 )


More information about the cairo-commit mailing list