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)
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.V5Automation.chm
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 Do ' 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 DoEvents Sleep 5 Loop While True End Sub
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 Next 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 Else 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.