Cartoon animation program


A cartoon can be thought of as a series of drawings. To simulate movement, the drawings can be slightly different from each other.


 


Remember drawing simple cartoons using a pad of paper? Simply flipping through the pages made the drawings come to life.


 


This was tedious work: a computer can help.


 


Just after first IBM PC came out in Aug 81, I wrote a cartoon animation program in C++ and assembly code. The concept was very simple: just use the mouse (I had to write my own mouse driver for a RS-232 serial mouse and had to hijack the COM1 port for my DOS program) to let the user draw some lines on the screen. Then the user could save those lines as a cartoon frame, and then draw another frame. The program could then calculate multiple frames in between the user created frames, creating smooth animation.


 


That early 80’s version of cartoon still runs on XP (although it writes directly to the video memory so it requires full screen mode.


It doesn’t run on Vista (I think you can download a DOS compatible window to make it work.)


 


A zip file of the 80’s version is available here. Unzip the contents to a folder (check out the date stamps!), then run cartoon.exe and hit Shift-A.


It includes some stored samples, like flying birds, alphabet, basketball. Because this was written before Windows, it won’t work with your mouse. You can see the main menu bar at the top. The only thing you can do with this program is run the stored samples by hitting Shift-A. Q will Quit. Try typing other chars to invoke various commands.


If you get it to run on Vista, please let me know the details.


 


About 10 years ago, I wrote another version of Cartoon in Foxpro and it was modified and published as a Solution Sample.


 


Start Visual Foxpro, Task Pane, Solution Samples. In the “Search for sample” text, box, type in Animation. “Display line animation in a form”


You can run the form, or click on the button on the right of the task pane that opens  the form in the Form Designer, so you can see the source code.


 


You can also open the form in the Class Browser, then export the code into a single file using the “View Class Code” button.


 


Foxpro excerpt of the inbetween algorithm (notice how cursors (in-memory data tables) are used)


 


      SELECT (lcTable)


      DO WHILE !EOF(“shadow”)


            mr = recno()


            mr2 = recno(“shadow”)


            FOR nb = 0 TO nBetween


                  THISFORMSET.frmAnimation.cls


                  GO mr


                  IF mr2 < RECCOUNT(“shadow”)


                        GO mr2 IN shadow


                  ENDIF


                  nFrames1 = &lcTable..frameno


                  nFrames2 = shadow.frameno


                  SCAN WHILE &lcTable..frameno = nFrames1


                        nx1 = &lcTable..x1 + nb * (shadow.x1 – &lcTable..x1) / nBetween


                        ny1 = &lcTable..y1 + nb * (shadow.y1 – &lcTable..y1) / nBetween


                        nx2 = &lcTable..x2 + nb * (shadow.x2 – &lcTable..x2) / nBetween


                        ny2 = &lcTable..y2 + nb * (shadow.y2 – &lcTable..y2) / nBetween


                        THISFORMSET.frmAnimation.line(nx1,ny1,nx2,ny2)


                        IF !EOF(“shadow”)


                              SKIP IN shadow


                              IF shadow.frameno # nFrames2


                                    SKIP -1 IN shadow


                              ENDIF


                        ENDIF


                  ENDSCAN


                  SELECT shadow


                  IF !EOF()


                        SKIP


                        LOCATE REST FOR shadow.Frameno # nFrames2


                  ENDIF


                  SELECT (lcTable)


                  wait wind “” time .05


            ENDFOR


      ENDDO


      USE IN shadow


      SCAN REST


            THISFORMSET.frmAnimation.line(x1,y1,x2,y2)


      ENDSCAN


      THISFORMSET.frmAnimation.frameno = nFrames1 + 1


 


 


 


Below is a more up to date example using WPF.  The equivalent animation code is in tmr_tick().


Try running it, hitting the Demo button.


 


Start Visual Studio 2008.


Choose File->New Project->Visual Basic->WPF Application.


(This also works with Temporary projects)


Open Window1.xaml.vb. Paste in the code below, then hit F5 to run.


 


I had my wife and kids playing with it for quite a while!


 


Try adding a frame or MouseWheel while a cartoon is playing.


 


Try right click and then draw: it changes the way you draw.


 


Experiment with using a touchpad and a mouse for drawing.


 


Try using variable frame rates. Add a feature to save/restore the current cartoon.


 


Try drawing your name, or the letters of the alphabet.


 


My original had features like copy/paste from frames, color fill


 


See also:


My toys over the years


Why was the original IBM PC 4.77 Megahertz?


 


<Cartoon Code>


Class Window1


    Private WithEvents btnNewFrame As Button


    Private WithEvents btnErase As Button


    Private WithEvents btnPlay As Button


    Private WithEvents btnDemo As Button


    Private WithEvents btnReset As Button


    Private txtStatus As TextBlock


    Private _AnimControl As AnimControl


    Sub Load() Handles MyBase.Loaded


        Me.Width = 800


        Me.Height = 600


        Dim xaml = _


        <DockPanel


            xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation


            xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml LastChildFill=True>


            <StackPanel Background=Transparent Orientation=Vertical DockPanel.Dock=Top>


                <TextBlock>Draw to create lines for a cartoon frame. Add a new frame, hit play</TextBlock>


                <TextBlock>Rebirth of Calvin’s cartoon program circa 1982


                    <Hyperlink>http://blogs.msdn.com/calvin_hsia</Hyperlink>


                </TextBlock>


            </StackPanel>


            <Border DockPanel.Dock=Bottom Height=25>


                <StackPanel Orientation=Horizontal>


                    <Button Name=btnNewFrame ToolTip=Add current drawing to cartoon, so you can create a new one>_New Frame</Button>


                    <Button Name=btnErase>_Erase</Button>


                    <Button Name=btnPlay ToolTip=Animate the current frames or stop animation>_Play</Button>


                    <Button Name=btnDemo>_Demo</Button>


                    <Button Name=btnReset ToolTip=Erase all frames>_Reset</Button>


                    <TextBox Name=txtBetween Text={Binding Path=txtBetween.text}></TextBox>


                    <TextBlock Name=txtStatus></TextBlock>


                </StackPanel>


            </Border>


            <UserControl Name=MyCtrl/>


        </DockPanel>


 


        Dim dPanel = CType(System.Windows.Markup.XamlReader.Load(xaml.CreateReader), DockPanel)


        Dim MyCtrl = CType(dPanel.FindName(“MyCtrl”), UserControl)


        _AnimControl = New AnimControl(Me)


        MyCtrl.Content = _AnimControl


        btnPlay = CType(dPanel.FindName(“btnPlay”), Button)


        btnNewFrame = CType(dPanel.FindName(“btnNewFrame”), Button)


        btnPlay = CType(dPanel.FindName(“btnPlay”), Button)


        btnDemo = CType(dPanel.FindName(“btnDemo”), Button)


        btnErase = CType(dPanel.FindName(“btnErase”), Button)


        btnReset = CType(dPanel.FindName(“btnReset”), Button)


        txtStatus = CType(dPanel.FindName(“txtStatus”), TextBlock)


        Me.Content = dPanel


    End Sub


    Sub btnNewFrame_Click() Handles btnNewFrame.Click


        _AnimControl.NewFrame()


        RefreshStatus()


    End Sub


    Sub btnPlay_Click() Handles btnPlay.Click


        btnNewFrame_Click()   ‘ save any currently drawn changes first


        _AnimControl.Play()


    End Sub


    Sub btnDemo_Click() Handles btnDemo.Click


        _AnimControl.Demo()


    End Sub


    Sub btnErase_Click() Handles btnErase.Click


        _AnimControl.EraseBtn()


    End Sub


    Sub btnReset_Click() Handles btnReset.Click


        _AnimControl.Reset()


    End Sub


    Friend Sub RefreshStatus()


        Me.txtStatus.Text = String.Format(“Frame count = {0} CurLineCnt = {1} CurFrame= {2} Between = {3}”, _


                                          _AnimControl._UserFrameList.Count, _AnimControl._CurLineList.Count, _


                                          _AnimControl._ndxUserFrame, _AnimControl._nBetween)


    End Sub


End Class


 


Public Class AnimControl


    Inherits FrameworkElement


    Private WithEvents _timer As New System.Windows.Threading.DispatcherTimer


    Private _Window1 As Window1


    Friend _nBetween As Integer = 10 ‘ # of frames being calc’d between user frames


    Friend _ndxUserFrame As Integer   ‘ index into user created frames.


    Private _ndxBetween As Integer  ‘ from 0 to nBetween


    Friend _nBetweenDyn As Integer = 0 ‘ # to add to _nBetween for next animation: adjustable by mousewheel


    Private _ptCurrent As Point?


    Private _ptOld As Point?


    Private _fPenDown As Boolean


    Private _oPen = New Pen(Brushes.Black, 2)


    Private _PenModeDrag As Boolean = True ‘ Click to create line segs, or continuous drag to create multiple segs


    ‘ lines to draw for current image: could be while composing, or playing. Could be real frame or calc’d frame


    Friend _CurLineList As New List(Of cFrameLine)


 


    ‘Frames stored by user


    Friend _UserFrameList As New List(Of cCartoonFrame)   


    Sub New(ByVal w As Window1)


        _Window1 = w


    End Sub


    Sub Reset()


        Me._timer.IsEnabled = False ‘stop playback, if any


        Me._UserFrameList.Clear() ‘ erase all user data


        Me._nBetweenDyn = 0


        EraseBtn()


    End Sub


    Sub EraseBtn() ‘ erase current frame


        _CurLineList.Clear()


        Me._ptOld = Nothing


        Me._fPenDown = False


        Me.InvalidateVisual()


    End Sub


    Sub Demo()


        Reset()


        Me._CurLineList.Add(New cFrameLine(New Point(10, 10), New Point(10, 300)))


        Me._CurLineList.Add(New cFrameLine(New Point(10, 300), New Point(300, 300)))


        Me._CurLineList.Add(New cFrameLine(New Point(300, 300), New Point(300, 10)))


        Me._CurLineList.Add(New cFrameLine(New Point(300, 10), New Point(10, 10)))


        Me._UserFrameList.Add(New cCartoonFrame(Me._CurLineList, 30))


        Me._CurLineList.Clear() ‘ reset for next frame


        Me._CurLineList.Add(New cFrameLine(New Point(10, 10), New Point(300, 10)))


        Me._CurLineList.Add(New cFrameLine(New Point(300, 10), New Point(300, 300)))


        Me._CurLineList.Add(New cFrameLine(New Point(300, 300), New Point(10, 300)))


        Me._CurLineList.Add(New cFrameLine(New Point(10, 300), New Point(10, 10)))


        Me._UserFrameList.Add(New cCartoonFrame(Me._CurLineList, 50))


        Me._CurLineList.Clear() ‘ reset for next frame


        Me._CurLineList.Add(New cFrameLine(New Point(10, 10), New Point(300, 10)))


        Me._UserFrameList.Add(New cCartoonFrame(Me._CurLineList, 10))


        Play()


    End Sub


    Sub NewFrame()


        If _CurLineList.Count > 0 Then


            Dim curFrame = New cCartoonFrame(_CurLineList, 10)


            _UserFrameList.Add(curFrame)


            EraseBtn()


        End If


    End Sub


 


    Friend Sub Play()


        If _UserFrameList.Count < 2 Then


            MsgBox(“Need at least 2 frames to animate”)


            Return


        End If


        If _timer.IsEnabled Then ‘ if we’re already playing, stop


            _timer.IsEnabled = False


        Else


            _timer.Interval = New TimeSpan(0, 0, 0, 0, 50) ‘ days,hrs,mins,secs,msecs


            _timer.IsEnabled = True


        End If


        Me._fPenDown = False


        Me._ndxUserFrame = 0


        Me._ndxBetween = 1 ‘ 1st is drawn now, next by timer tick


        Me._CurLineList.Clear()


        Me._CurLineList.AddRange(Me._UserFrameList(0)._Lines) ‘get the 1st frame


        Me.InvalidateVisual() ‘ show it


    End Sub


    Sub tmr_tick() Handles _timer.Tick ‘ let’s do the animating


        If _ndxUserFrame = Me._UserFrameList.Count – 1 Then     ‘ we’ve reached the end: let’s restart


            Me._ndxUserFrame = 0


            Me._ndxBetween = 0


        End If


        Me._CurLineList.Clear()


        Dim frmLeft = Me._UserFrameList(Me._ndxUserFrame) ‘ the frame on the left


        Dim frmRight = Me._UserFrameList(Me._ndxUserFrame + 1) ‘ the frame on the right


        _nBetween = Math.Max(0, frmLeft._nBetween + Me._nBetweenDyn) ‘ recorded value plus mousewheel adjustment


        Dim nLinesToDraw = Math.Max(frmLeft._Lines.Count, frmRight._Lines.Count) – 1


        For ndx = 0 To nLinesToDraw ‘ calc the lines to draw


            Dim lineLeft = frmLeft._Lines(Math.Min(ndx, frmLeft._Lines.Count – 1))


            Dim lineRight = frmRight._Lines(Math.Min(ndx, frmRight._Lines.Count – 1))


            Dim pt0 As New Point With _


                {.X = lineLeft.pt0.X + Me._ndxBetween * (lineRight.pt0.X – lineLeft.pt0.X) / (_nBetween + 1), _


                 .Y = lineLeft.pt0.Y + Me._ndxBetween * (lineRight.pt0.Y – lineLeft.pt0.Y) / (_nBetween + 1)}


            Dim pt1 As New Point With _


                {.X = lineLeft.pt1.X + Me._ndxBetween * (lineRight.pt1.X – lineLeft.pt1.X) / (_nBetween + 1), _


                 .Y = lineLeft.pt1.Y + Me._ndxBetween * (lineRight.pt1.Y – lineLeft.pt1.Y) / (_nBetween + 1)}


            Dim newLine = New cFrameLine(pt0, pt1)


            Me._CurLineList.Add(newLine)


        Next


        If Me._ndxBetween > Me._nBetween Then ‘ we’ve reached the right


            Me._ndxUserFrame += 1 ‘ advance to next user frame


            Me._ndxBetween = 0 ‘ we don’t want to redraw frmRight when it becomes frmLeft


        End If


        Me._ndxBetween += 1 ‘ advance to next frame


        Me.InvalidateVisual()


    End Sub


    Protected Overrides Sub OnRender(ByVal drawingContext As System.Windows.Media.DrawingContext)


        drawingContext.DrawRectangle(Brushes.AliceBlue, New Pen(Brushes.Purple, 1), New Rect(0, 0, Me.RenderSize.Width, Me.RenderSize.Height))


        For Each fr In Me._CurLineList ‘ draw the lines in the current frame


            drawingContext.DrawLine(_oPen, fr.pt0, fr.pt1)


        Next


        If Me._fPenDown Then


            If Me._ptOld.HasValue Then


                drawingContext.DrawLine(_oPen, Me._ptOld, Me._ptCurrent)


            Else


                drawingContext.DrawLine(_oPen, Me._ptOld, Me._ptCurrent)


            End If


        End If


        _Window1.RefreshStatus()


    End Sub


    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Input.MouseButtonEventArgs)


        If e.RightButton = MouseButtonState.Pressed Then


            Me._PenModeDrag = Not Me._PenModeDrag ‘ toggle modes on right click


        Else


            If Me._PenModeDrag Then


                Me._ptOld = e.GetPosition(Me)


            Else


                If e.RightButton = MouseButtonState.Pressed Then


                    Me._fPenDown = False


                    Me._ptOld = Nothing


                Else


                    Me._fPenDown = True


                    Me._ptCurrent = e.GetPosition(Me) ‘ get cur pos rel to self


                    If Not Me._ptOld.HasValue Then


                        Me._ptOld = Me._ptCurrent ‘ same


                    End If


                    Me.InvalidateVisual()


                End If


            End If


        End If


    End Sub


 


    Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Input.MouseEventArgs)


        If Me._PenModeDrag Then


            If e.LeftButton = MouseButtonState.Pressed Then


                If Me._ptOld.HasValue Then


                    Me._ptCurrent = e.GetPosition(Me)


                    Dim newFrameLine = New cFrameLine(Me._ptOld, Me._ptCurrent)


                    Me._CurLineList.Add(newFrameLine)


                    Me._ptOld = Me._ptCurrent


                    Me.InvalidateVisual()


                End If


            End If


        Else


            If Me._fPenDown Then


                If Me._ptOld.HasValue Then


                    Me._ptCurrent = e.GetPosition(Me)


                End If


                Me.InvalidateVisual()


            End If


        End If


    End Sub


    Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Input.MouseButtonEventArgs)


        If Me._fPenDown Then


            Me._ptCurrent = e.GetPosition(Me) ‘ get cur pos rel to self


            Dim newFrameLine = New cFrameLine(Me._ptOld, Me._ptCurrent)


            Me._CurLineList.Add(newFrameLine)


            Me._ptOld = Me._ptCurrent


            Me._fPenDown = False


            Me.InvalidateVisual()


        End If


    End Sub


    Protected Overrides Sub OnMouseWheel(ByVal e As System.Windows.Input.MouseWheelEventArgs)


        If Me._timer.IsEnabled Then ‘ only when playing


            Me._nBetweenDyn += If(e.Delta > 0, 2, -2)


        End If


    End Sub


    <DebuggerDisplay(“{ToString()}”)> _


    Public Class cCartoonFrame ‘ User created cartoon frame


        Public ReadOnly _Lines As New List(Of cFrameLine) ‘ a frame is a list of lines


        ‘ # of frames to gen between real user frames


        Public ReadOnly _nBetween As Integer


        Sub New(ByVal lst As List(Of cFrameLine), ByVal nBetween As Integer)


            _Lines.AddRange(lst)


            _nBetween = nBetween


        End Sub


        Public Overrides Function ToString() As String


            Return String.Format(“LineCount = {0}”, _Lines.Count)


        End Function


    End Class


    ‘A line to be animated. Could belong to a real or gen’d user frame, or while user is actively drawing


    <DebuggerDisplay(“{ToString()}”)> _


    Public Class cFrameLine ‘ User created cartoon line. It’s just 2 points.


        Friend ReadOnly pt0 As Point


        Friend ReadOnly pt1 As Point


        Sub New(ByVal pt0 As Point, ByVal pt1 As Point)


            Me.pt0 = pt0


            Me.pt1 = pt1


        End Sub


        Public Overrides Function ToString() As String


            Return String.Format(“Line = ({0}) – ({1})”, pt0.ToString, pt1.ToString)


        End Function


    End Class


End Class


 


</Cartoon Code>


 

Comments (16)

  1. Steven Black says:

    Lovin’ the concept that 39-lines of VFP code become 271-lines of code using WPF.

  2. Rick Bean says:

    I just installed VS 2008 along with SP1, and when I followed your directions, I ended up with 46 errors. Obviously there are some things assumed that I’m missing, any hints?

  3. Calvin_Hsia says:

    Are you sure you had VB->WPF application? Did you paste into the Window1.Xaml.VB file? what are the errors? Did you remove the double spacing? (to remove, see http://blogs.msdn.com/calvin_hsia/archive/2005/08/07/448889.aspx)

  4. Rick Bean says:

    Well, color me emarassed. Removing the double spacing does indeed makes a world of difference. Thanks to VFP’s rr to r replacement, that was quite trivial. Amazing to compare the VFP version and see how it’s become more complicated to accomplish the same effects. Thanks for reminding me of the Solution Sample.

    I’m just going to have to play in VS a bit more, but my daytime VFP job keeps me very busy!

    Thanks again for a proper hint!

  5. Alan Bourke says:

    Calvin – can you remember what model of mouse it was that you handled in the drawing prg, and was it a bus mouse or serial mouse ?

  6. Mimi says:

    Well…………….am i suppose to cut and paste somehwere? where should i paste?

  7. Calvin_Hsia says:

    Mimi: the post above says to start Visual Studio, etc. to paste. Be sure to remove any double spacing

  8. Calvin_Hsia says:

    Alan: yes: the mouse I used was a 1200 baud serial optical mouse from VisiOn

  9. When I wrote my cartoon animation program almost 30 years ago (see Cartoon animation program ) I needed

  10. Russell Campbell says:

    Calvin, are you aware of the Phun program at http://www.phunland.com/wiki/Home . . . make sure you watch the video on the home page.  You can get lost in this program and kids of all ages love it.

  11. In the last post, Area fill algorithm: crayons and coloring book , I showed a program that emulates a

  12. rolly says:

    Hi,

    May I know if you have a code in Visual Foxpro 6 to show only a portion of my screen into a video projector.

    Thank you

    Rolly