Getting Started with Macros – Part 3

In my previous two installments of this series, I’ve looked at the basics of macros and how to use variables. Today, I’ll be rounding off this introductory series by having a look at program flow. The macros we have looked at so far have just been a row of instructions, which are executed one by one, but it is also possible to make branching logic and loops. For example, you can do different things based on what the user chooses, or you can run the same block of code multiple times. For example, if you’ve had a look at my Large Exports annual, it contains the code required for exporting a small section of the map, and then it just loops over the same code enough times to export the entire map as smaller chunks.

If you’re used to other programming languages, you’ll quickly notice that the CC3+ macro language don’t have built-in conditional/loop blocks like more modern computer languages, but we can manually implement their functionality.

To make conditional statements, CC3+ comes with three main checks, IFP (if positive), IFN (if negative) and IFZ (if zero). In addition, we have a couple more, like IFDEF (if defined) and IFERR (if error) which can be used to check values. Along with these, we also need to introduce the concept of labels.

Labels are simply a named place in the code, and can be added anywhere in the macro. They are just any single word, prefixed by a colon (:). Once you have a label, then you can use the conditional commands above to jump to that label, if the condition is true.

Let us showcase these two features by making a small calculator that adds up all the numbers the user provides it, and it will continue adding numbers until the user enters a zero, at which point it shows a dialog with the result.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
MACRO CALC
GV vSum 0
:PROMPT
GV vAdd ^DNumber to add:
IFERR END
IFZ vADD END
GV vSUM vSum+vAdd
GOTO PROMPT
:END
MSGBOX Calculator;The Sum is;vSum;
ENDM

Let us have a quick look at how this macro operates.

Line 2 starts by setting the final sum variable to 0. This isn’t required if you just run the macro once, but since variables stay defined after the macro is done, it would have brought along the previous value into the macro if we didn’t reset it at the beginning.

Line 3 starts out with defining a label. Since this macro will be looping around the request to the user to supply more numbers, we need a place to start the loop. This line doesn’t actually do anything by itself, except being the label we can jump to.

Line 4 ask the user for a number to add, and puts the value provided into the vAdd variable.

Then on line 5 we use the IFERR command to check for an error. If the previous line returned an error, which it will do it the user either wrote something that isn’t a number (GV do request a number) or the user just hits enter instead of providing a value, this will trigger and jump to the specified label. In this case, the END label which leads to the final dialog being shown with the sum so far.

Line 6 is another check, but here we check if the user typed the number 0 (IFZ – If Zero), which we also interprets as a sign to end the calculation and jump to the END label.

Line 7 simply adds the latest value provided to the running sum.

Line 8 does an unconditional jump back to the beginning to ask the user for a new value to add. We’ve already checked if the macro should end on lines 5 and 6, so if we get here, we always want to ask for another number.

Line 9 is another label, which we target from lines 5 and 6.

And finally, line 10 simply display a message box with some text and the final sum.

 

One question that might come to mind if you’re used to dealing with if-blocks in other computer language is; how to do something like if (x < 5) ? The CC3+ commands doesn’t seem to be able to target a specific number?

Solving this is quite simple, you just have to rewrite it a bit. To check if x is less than 5, you can do IFN x-5. If x is less than 5, then x-5 will be negative, and thus, IFN will be true. If x is greater or equal than 5, the result is no longer negative, and IFN will be false.


So far, I’ve only used command line prompts to gather values, but if you only need simple yes/no type of responses, there is also a graphical dialog you can call, ASKBOX.  The usage is similar to the MSGBOX, but you can use IFERR on the next line to check if the user clicked the No button.

ASKBOX Continue?;The sum so far is;vSum;Do you wish to continue adding numbers?;
IFERR END

 

As you can see, setting up conditional behavior and loops in macros isn’t that hard. You may need to think a bit differently compared to using blocks in other programming languages, but the main idea is to use the conditional checks and jump to labels as appropriate. If you want to do a loop that repeats a set amount of times, just set up a dedicated counter variable for it.

Let us finish this article with an example that shows a loop with a counter. This macro asks the user for a center point and radius, and draw a nice spiral using arcs. Since there are some calculations here, I’ve used some short variable names to make these a bit more readable, X and Y are the coordinates for the center point, R is the increment between lines int he spiral, and C is the count. Longer variable names might be easier to understand, but the CC3+ macro editor window is a bit small, so short names makes things easier to see without scrolling. Note that since we don’t set any properties, the spiral will be drawn with the current line style, color, width and fill, whatever they may be.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
MACRO SPIRAL
GP vCenter ^DPick location
GD vRadius ^DSpecify radius
GETX X vCenter
GETY Y vCenter
GV R vRadius/10
GV C 1
:START
ARCB X-R*C,Y X+R*C,Y X+(C-1)/2,Y+R*C
ARCB X+R*C,Y X-R*(C+1),Y X+C/2,Y-R*(C+0.33)
GV C C+1
IFN C-10 START
ENDM

Lines 2 & 3 asks the user to provide location and size

Lines 4 & 5 retrieves the X and Y coordinates as individual values from the center point specified by the user while line 6 calculates the distance between each line (1/10th of the entire radius)

Line 7 starts the loop counter at 1, note that this value will be used in the calculations (If not, I might have elected to count down from 10 instead)

Line 8 sets the label for the start of the loop.

Line 9 & 10 draws the actual arcs. Each of them takes 3 arguments, the start point, the ending point, and the bulge point. These are calculated from the center point and multiplied by the loop counter (C) to increase them for each round. I elected to draw both the top and bottom part of the spiral in each loop, as it makes the calculations easier.

Line 11 increments the loop counter by 1, and line 12 loops back to the START label, as long as the loop variable is less than 10. If not, the macro ends here.

 

This ends this small introduction series. We have now covered all the basics about using macros, from having them execute basic CC3+ commands, to storing values in variables, and now using conditional statements to make branching paths and loops. My examples here have been pretty small, but using these techniques you can make pretty complex macros. To learn more about the commands you can use in macros, you can find the command and macro reference in the CC3+ help files, or if you own the Tome of Ultimate Mapping, it comes with an in-depth list of all the commands, both included in the book itself, but also provided as a stand-alone spreadsheet which can be searched, filtered and sorted.

 

If you have questions regarding the content of this article, please use the ProFantasy forums. It can take a long time before comments on the blog gets noticed, especially for older articles. The forums on the other hand, I frequent daily.

 

Comments are closed.