Question: What is the best way to reference a surface or object in ZPL, so that the macro still works when surfaces/objects are inserted or deleted, and the numbering changes?

Many macros are 'quick and dirty', and so coding like this is perfectly reasonable:

a = THIC(10)
b = RADI(11)

In this case, the variable a is assigned the value of the thickness of surface 10, and b is assigned the value of the radius of curvature of the following surface. But what if you add or delete a surface before surface 10? The numbering in the editor will update, and the new surface 10 will not be the one you wanted. One solution is to ask every time the macro runs:

INPUT "What surface do you want to use", my_surface
a = THIC(my_surface)
b = RADI(my_surface + 1)

But this gets tedious if you run the macro several times without changing the surface numbering. Or, you could just use a variable:

my_surface = 10
a = THIC(my_surface)
b = RADI(my_surface + 1)


So you only have one line to edit in your macro when surfaces change. But that's still a bit tedious.

The best thing to do is to give the surface a unique comment, like "target" for example, and then use the SURC() function:

my_surface = SURC("target")
a = THIC(my_surface)
b = RADI(my_surface + 1)


SURC(A$) finds the first surface where the comment string is the same as A$ and returns its surface number. In the non-sequential component editor use OBJC() instead:

my_object = OBJC("target")

This simple structure ensures that your macro always finds the intended surface or object. Note that if no surface/object has the target string set as a comment, the functions will return the value -1. It's simple to add a test to bullet-proof your code:

my_object  = OBJC("target")
IF (my_object == -1)
     PRINT "Target object not found"
     END
ENDIF