Clock animation in CATIA V5 product with VBA

In a post Clock animation in CATIA V5 drawing with VBA, I have already shown you, how to create a simple animation in a drawing. However, you are not limited only to a Drafting workbench, but with the help of VBA, you can force also your products to move. Let's create a clock animation in CATIA V5 product, but this time also with millisecond hand.

Wanna see it in action?

Then watch this video:

Clock animation in CATIA V5 product (VBA)

Main procedure

Resolution of built-in VBA Date type is around 1 second. This is good enough to properly update hour, minute and second clock hand, but to render a millisecond hand we need higher accuracy. Here comes in handy a Windows API function GetTickCount which retrieves the number of milliseconds that have elapsed since the system was started. The resolution of this function is typically in the range of 10 milliseconds to 16 milliseconds.

At the beginning of the module, we declare Windows API calls for Sleep and GetTickCount function and then we continue with a definition of "module-level" variables like the pi variable. We define a global Product variable for each of the clock hands as we reference them in several procedures of our module. Within the ClockMain procedure, we call a DrawFace routine to draw the face of a clock. The routine is called only one time because all elements except clock hands are static. The movement of the clock hands takes place inside the main endless loop at intervals of 20 milliseconds. Here we utilize GetTickCount function declared above. An important part of the main loop are calls to DoEvents and Sleep function, which keeps CATIA application responsive and less resource hungry.

And what the official documentation V5Automation.chm says about DoEvents function? See below:

The general purpose of the VBA function DoEvent is to allow the System to manage input events so that the UI becomes more responsive.

Besides this function is generally dangerous because it can generate unexpected reentrancy. CATIA infrastructure is not architectured to manage event treatment while the process is blocked into a VBA macro execution. Calling DoEvent while a macro is executed can so lead to unpredictable behavior.


Well, believe me, we have no other choice than to use it. Because of the infinite main loop, without DoEvents function you are not able to stop the script properly once you launch it.

Option Explicit

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long

Private pi As Double
Private firstDraw As Boolean

Private hourHand As Product
Private minuteHand As Product
Private secondHand As Product
Private msecHand As Product

Sub ClockMain()
    ' PI definition
    pi = 4 * Atn(1)
    Dim myProd As Product
    Set myProd = CATIA.ActiveDocument.Product
    ' get products
    Set secondHand = myProd.Products.Item(1)
    Set minuteHand = myProd.Products.Item(2)
    Set hourHand = myProd.Products.Item(3)
    Set msecHand = myProd.Products.Item(4)
    Dim startTime As Long
    startTime = GetTickCount
    Dim secondUpdate As Date
    secondUpdate = Now
    Dim msecBase As Long
    DrawFace myProd, 250

    ' main timer loop
        ' redraw every 20 millisecond
        If (GetTickCount - startTime) > 20 Then
            DrawWatch GetTickCount - msecBase
            startTime = GetTickCount
            ' every second reset milliseconds to properly update msec hand
            If DateDiff("s", secondUpdate, Now) Then
                secondUpdate = Now
                msecBase = GetTickCount
            End If
        End If
        ' keep application responsive
        Sleep 5
    Loop While True
End Sub

Drawing procedures

To draw the face of the clock we call a DrawFace procedure. This procedure is called only one time before the main timer loop. All elements of the clock face are just copies of an Element.CATPart (fifth item of the Clock assembly). Inside the loop of the DrawFace subroutine, we make copies of the Element.CATPart and with a little bit of geometry we move components to the right place.

Sub DrawFace(prod As Product, r As Double)
    Dim element As Product
    Set element = prod.Products.Item(5)
    Dim refElem As Product
    Set refElem = element.ReferenceProduct
    Dim i As Long
    Dim coords()
    For i = 1 To 12
        coords = Array(r * 0.9 * Cos(360 / 12 * i * pi / 180), r * 0.9 * Sin(360 / 12 * i * pi / 180))
        Dim copied
        Set copied = prod.Products.AddComponent(refElem)
        Dim pos(11)
        copied.Position.GetComponents pos
        pos(9) = coords(0)
        pos(10) = coords(1)
        pos(11) = -20
        copied.Position.SetComponents pos
        Rotate copied, 360 / 12 * i * pi / 180, False
End Sub

To get a proper position of clock hands we use VBA time functions. VBA represents date and time as a number (ddddd.tttttt). Integer portion represents the number of days since [January 0, 1900]. Any dates before this one are stored as negative numbers, all dates after are stored as positive values. The fractional portion of the date represents the fractional portion of a 24 hour day. For example, 6:00 AM is stored as 0.25, or 25% of a 24 hour day. To convert a 24 hour day to its analog counterpart (12-hour format) we use helper function GetHourFraction. It takes a date as a parameter, strip its integer (days) part and return its decimal portion converted to an analog format.

Function GetHourFraction(t As Date) As Double
    ' 12 hours boundary
    Const BOUNDARY As Double = 0.5
    Dim fraction As Double
    fraction = CDbl(t) - Int(t)
    If fraction <= BOUNDARY Then
        GetHourFraction = fraction / BOUNDARY
        GetHourFraction = (fraction - BOUNDARY) / BOUNDARY
    End If
End Function

Later we define a Rotate procedure. It takes an instance of a Product class + the angle as parameters and it sets the position (rotation) of the component in x and y direction. This method is then called multiple times from DrawWatch sub and takes care of rotation of every clock arm.

' rotate clock hands
Sub Rotate(child, myAngle As Double)
    Dim pos(11)
    child.Position.GetComponents pos
    ' set x-axis rotation
    pos(0) = Cos(myAngle)
    pos(1) = Sin(myAngle)
    ' set y-axis rotation
    pos(3) = -Sin(myAngle)
    pos(4) = Cos(myAngle)

    child.Position.SetComponents pos
End Sub

Sub DrawWatch(msec As Long)
    Dim currTime As Date
    currTime = Now
    Dim currSecond As Long
    currSecond = Second(currTime)
    Dim currMinute As Long
    currMinute = Minute(currTime)
    Dim currHour As Long
    currHour = Hour(currTime)
    Dim angleSecond As Double
    angleSecond = pi / 2 - 360 / 60 * currSecond * pi / 180
    Dim angleMinute As Double
    angleMinute = pi / 2 - 360 / 60 * currMinute * pi / 180
    Dim angleHour As Double
    angleHour = pi / 2 - 360 * GetHourFraction(currTime) * pi / 180
    Dim angleMillisecond As Double
    angleMillisecond = pi / 2 - 360 / 1000 * msec * pi / 180
    Rotate secondHand, angleSecond
    Rotate minuteHand, angleMinute
    Rotate hourHand, angleHour
    Rotate msecHand, angleMillisecond
End Sub

To test it, open and switch the Clock.CATProduct assembly into a Design Mode, import (or copy) the source code into a standard module and launch the ClockMain procedure.

The Clock.CATProduct with the source code can be downloaded here Clock in a product
Watch an application video here Clock animation in CATIA V5 product (VBA)

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>