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.
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.
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
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
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.