If you want to create an animation in CATIA, most of the time you use a DMU Kinematics workbench. However, simple animations can be performed without any dedicated CATIA module, just with a plain VBA code. Moreover, you can use VBA across workbenches and Drafting workbench is not an exception. I am going to show you, how you can create a simple animation in a CATIA V5 drawing.
Timer procedure
We start with a definition of "module-level" variables and a "timer" procedure MyTimer which is running in a loop and in preset intervals regularly redraw our watch. An important part of this procedure is Windows API function Sleep. This function suspends the execution of the current thread until the time-out interval elapses. The function takes as a parameter a time interval (in milliseconds) for which execution is to be suspended.
Within the loop of the MyTimer procedure, we check if redrawing interval has already passed and if so, we call DrawWatch subroutine. The redrawing interval is set to half of a second and it is stored in an interval variable. Then we suspend execution for 20 milliseconds and call DoEvents function which keeps CATIA user interface more responsive. Theoretically, we can continue even without a call to a Sleep function, but then CATIA and DoEvents function become resources hungry and slow down the PC considerably because there's no pause in the processing.
And what 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, it does not sound very optimistic, but we can live with it :). Without DoEvents, CATIA, as well as VBA Editor, remain completely frozen and nonresponsive.
Remark
If you need a more advanced timer solution, you can use timer functions provided by Windows API. Functions SetTimer and KillTimer give you some advantage over our simple "DoEvents" timer, but if used inappropriately, CATIA will likely crash completely and you can lose your unsaved work.
Option Explicit
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private pi As Double
Private firstDraw As Boolean
Private hourHand As Line2D
Private minuteHand As Line2D
Private secondHand As Line2D
Sub MyTimer()
Dim myView As DrawingView
Set myView = CATIA.ActiveDocument.Sheets.ActiveSheet.Views.ActiveView
firstDraw = True
pi = 4 * Atn(1)
Dim interval As Double
interval = TimeValue("0:00:01") / 2
Dim startTime As Date
startTime = Now
DrawFace myView, 50
Do
If CDate(Now - startTime) > interval Then
Debug.Print GetHourFraction(Now)
DrawWatch myView, 50
startTime = Now
End If
Sleep 20
DoEvents
Loop While True
End Sub
Drawing procedures
To draw a face of our clock we call a DrawFace sub. This procedure is called only one time before the main timer loop. Elements of the face are not moving (constant in time) and there is no need to redraw them. To create individual elements of the clock face we use a Factory2D object of a drawing view and some basic geometry equations. To generate text we utilize DrawingTexts collection and text format is set by DrawingTextProperties object.
Sub DrawFace(myView As DrawingView, r As Double)
Dim fact2D As Factory2D
Set fact2D = myView.Factory2D
Dim i As Long
Dim coords()
fact2D.CreateClosedCircle 0, 0, r * 1.1
fact2D.CreateClosedCircle 0, 0, r * 1.15
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))
fact2D.CreateClosedCircle coords(0), coords(1), 2
Next
Dim digits()
digits = Array("3", "6", "9", "12")
' digits = Array("III", "XI", "IX", "XII")
Dim angledigits As Double
' get drawing text collection
Dim drwTexts As DrawingTexts
Set drwTexts = myView.Texts
For i = 1 To 4
angledigits = pi / 2 - 360 / 4 * i * pi / 180
coords = Array(r * 0.7 * Cos(angledigits), -r * 0.7 * Sin(angledigits))
' create letter
Dim digit As DrawingText
Set digit = drwTexts.Add(CStr(digits(i - 1)), coords(0), -coords(1))
' get DrawingTextProperties object of a letter
Dim textProp As DrawingTextProperties
Set textProp = digit.TextProperties
' set text properties of a letter
With textProp
' set letter mirror mode, do not let CATIA to perform auto flip of text
.AnchorPoint = catMiddleCenter
.Bold = True
.FontName = "Monospac821 BT"
.FontSize = 7
' update text properties
.Update
End With
Next
End Sub
You can even choose between arabic and roman numerals by commenting/uncommenting some line(s). I am sure you know which lines I am talking about ☺. When you launch this procedure, you should get the following result:
Clock face arabic numerals
Clock face roman numerals
The rest of the code consists of a procedure DrawWatch which is launched on every timer tick (called from inside of the main timer loop) and it draws/redraws the hands of our clock. Hands are just lines (Line2D object) with different length and thickness. On a first call of the method, we create all required elements (lines) and we set their thickness with the help of a Selection object. Once created, they are stored in module variables hourHand, minuteHand, secondHand, and their position is updated on every tick.
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
Sub DrawWatch(myView As DrawingView, r As Double)
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 coordsSecond()
coordsSecond = Array(r * Cos(angleSecond), r * Sin(angleSecond))
Dim coordsMinute()
coordsMinute = Array(r * 0.8 * Cos(angleMinute), r * 0.8 * Sin(angleMinute))
Dim coordsHour()
coordsHour = Array(r * 0.6 * Cos(angleHour), r * 0.6 * Sin(angleHour))
Dim fact2D As Factory2D
Set fact2D = myView.Factory2D
If secondHand Is Nothing Then
Set secondHand = fact2D.CreateLine(0, 0, coordsSecond(0), coordsSecond(1))
Set minuteHand = fact2D.CreateLine(0, 0, coordsMinute(0), coordsMinute(1))
Set hourHand = fact2D.CreateLine(0, 0, coordsHour(0), coordsHour(1))
Else
secondHand.SetData 0, 0, coordsSecond(0), coordsSecond(1)
minuteHand.SetData 0, 0, coordsMinute(0), coordsMinute(1)
hourHand.SetData 0, 0, coordsHour(0), coordsHour(1)
End If
If firstDraw Then
' get drawing doc selection
Dim sel As Selection
Set sel = CATIA.ActiveDocument.Selection
' Set sel = myView.Parent.Parent.Parent.Parent.Selection
' clear the selection
sel.Clear
sel.Add minuteHand
' change the visible width to the required value
Dim visProp As VisPropertySet
Set visProp = sel.VisProperties
visProp.SetVisibleWidth 4, 0
sel.Clear
sel.Add hourHand
Set visProp = sel.VisProperties
visProp.SetVisibleWidth 8, 0
sel.Clear
End If
firstDraw = False
End Sub
To test it, copy a source code into a standard module and launch MyTimer procedure.
I made changes to run on 64bit but how do you exit once it is running?
Hi Fred,
without seeing your changes it is hard to say where the issue is. Probably it is related to the DoEvents command.