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.
Wanna see it in action?
Then watch this video:Clock animation in CATIA V5 Drawing (VBA)
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.
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
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.