Reference:Declare and Local Directives

From POV-Wiki
Jump to navigation Jump to search

Identifiers may be declared and later referenced to make scene files more readable and to parameterize scenes so that changing a single declaration changes many values. There are several built-in identifiers which POV-Ray declares for you. See the sections: Built-in Variables and Built-in Vector Identifiers for details.

Declaring identifiers

An identifier is declared as follows.

DECLARATION:
  #declare [deprecated] IDENTIFIER = RVALUE |
  #local [deprecated] IDENTIFIER = RVALUE
RVALUE:
  FLOAT; | VECTOR; | COLOR; | STRING | OBJECT | TEXTURE |
  PIGMENT | NORMAL | FINISH | INTERIOR | MEDIA | DENSITY |
  COLOR_MAP | PIGMENT_MAP | SLOPE_MAP | NORMAL_MAP |
  DENSITY_MAP | CAMERA | LIGHT_SOURCE | FOG | RAINBOW |
  SKY_SPHERE | TRANSFORM

Note: See the section on Deprecation Support for more information

Where IDENTIFIER is the name of the identifier that is at least one character long and RVALUE is any of the listed items.

The syntax for each is in the corresponding section of this language reference.

Note: In versions prior to 3.6.2, identifier names were limited to 40 characters. There has been a Change removing that restriction.

Here are some examples.

#declare Rows = 5;
#declare Count = Count+1;
#local  Here = <1,2,3>;
#declare White = rgb <1,1,1>;
#declare Cyan = color blue 1.0 green 1.0;
#declare Font_Name = "ariel.ttf"
#declare Rod = cylinder {-5*x,5*x,1}
#declare Ring = torus {5,1}
#local  Checks = pigment { checker White, Cyan }

object { Rod scale y*5 } // not "cylinder { Rod }"
object {
  Ring
  pigment { Checks scale 0.5 }
  transform Skew
  }

Note: There should be a semi-colon after the expression in all float,vector and color identifier declarations. This semi-colon is introduced in POV-Ray v3.1. If omitted, it generates a warning and some macros may not work properly. Semicolons after other declarations are optional.

Declarations, like most language directives, can appear almost anywhere in the file, even within other statements. For example:

#declare Here=<1,2,3>;
#declare Count=0;               // initialize Count
  union {
    object { Rod translate Here*Count }
    #declare Count=Count+1;     // re-declare inside union
    object { Rod translate Here*Count }
    #declare Count=Count+1;     // re-declare inside union
    object { Rod translate Here*Count }
    }

As this example shows, you can re-declare an identifier and may use previously declared values in that re-declaration.

Note: Object identifiers use the generic wrapper statement object{ ... }. You do not need to know what kind of object it is.

Declarations may be nested inside each other within limits. In the example in the previous section you could declare the entire union as a object. However for technical reasons there are instances where you may not use any language directive inside the declaration of floats, vectors or color expressions. Although these limits have been loosened somewhat since POV-Ray v3.1, they still exist.

Identifiers declared within #macro ... #end blocks are not created at the time the macro is defined. They are only created at the time the macro is actually invoked. Like all other items inside such a #macro definition, they are ignored when the macro is defined.

To help simplify passing multiple values out of a macro New tuple-style syntax extensions have been added to the #declare and #local directives:

#declare ( ID1, ID2, ...) = ( EXPR1, EXPR2, ... );

//This statement is fully equivalent to:
#declare ID1 = EXPR1;
#declare ID1 = EXPR2;

Note: This equivalence holds true even for constructs like #declare (A,B) = (B,A); which does not swap the contents of A and B as you might expect. The terminating semicolon is optional.

In addition, a similar syntax extension has been added for easier assignment of vector components to individual variables:

// Is this valid? ie: < and > if so just remove this line
#declare < ID1, ID2, ... > = VECTOR_EXPR;

//This statement is fully equivalent to:
#local TEMP = VECTOR_EXPR;
#declare ID1 = TEMP.x;
#declare IDY = TEMP.y;

Note: The new syntax does not actually define the local variable TEMP. The terminating semicolon is mandatory.

The #local statement syntax has also been extended accordingly. The deprecated keyword can be used with these new syntax variants by placing it right before the respective identifier:

#declare (A, deprecated B) = (47, 11);

Extended #declare and #local tuple syntax to also support assignment from arrays, and to support assignment to mismatching number of identifiers. Using ...

#declare { IDENTIFIER_LIST } = ARRAY_EXPRESSION

... will assign the first elements of the array expression (either an array identifier or an array declaration with initializer) to the given identifiers. The IDENTIFIER_LIST may now contain empty elements, in which case the corresponding element from the right-hand side will be skipped accordingly.

Extended tuple assignment syntax to allow for omission of right-hand side elements; extend array initializer syntax to allow for omission of elements.

Left-hand side elements in tuple assignments can now be prepended with the optional keyword. If an element is flagged this way, or left empty, the corresponding right-hand side element may be an uninitialized identifier or left empty. In this case, the respective left-hand side element remains unchanged (it is NOT undefined).

An array initializer may now be prepended with the optional keyword ...

#declare A = array[10] optional { ... }

... in this case, each individual element of the initializer may be an uninitialized identifier or left empty.

Note: Due to parser limitations, uninitialized identifiers do not encompass uninitialized array elements. This limitation also applies to optional macro parameters.

declare vs. local

Identifiers may be declared either global using #declare or local using the #local directive.

Those created by the #declare directive are permanent in duration and global in scope. Once created, they are available throughout the scene and they are not released until all parsing is complete or until they are specifically released using #undef. See Destroying Identifiers.

Those created by the #local directive are temporary in duration and local in scope. They temporarily override any identifiers with the same name. See Identifier Name Collisions.

If #local is used inside a #macro then the identifier is local to that macro. When the macro is invoked and the #local directive is parsed, the identifier is created. It persists until the #end directive of the macro is reached. At the #end directive, the identifier is destroyed. Subsequent invocations of the macro create totally new identifiers.

Use of #local within an include file but not in a macro, also creates a temporary identifier that is local to that include file. When the include file is included and the #local directive is parsed, the identifier is created. It persists until the end of the include file is reached. At the end of file the identifier is destroyed. Subsequent inclusions of the file create totally new identifiers.

Use of #local in the main scene file (not in an include file and not in a macro) is identical to #declare. For clarity sake you should not use #local in a main file except in a macro.

There is currently no way to create permanent, yet local identifiers in POV-Ray.

Local identifiers may be specifically released early using #undef but in general there is no need to do so. See Destroying Identifiers.

Identifier Name Collisions

Local identifiers may have the same names as previously declared identifiers. In this instance, the most recent, most local identifier takes precedence. Upon entering an include file or invoking a macro, a new symbol table is created. When referencing identifiers, the most recently created symbol table is searched first, then the next most recent and so on back to the global table of the main scene file. As each macro or include file is exited, its table and identifiers are destroyed. Parameters passed by value reside in the same symbol table as the one used for identifiers local to the macro.

The rules for duplicate identifiers may seem complicated when multiple-nested includes and macros are involved, but in actual practice the results are generally what you intended.

Consider this example: You have a main scene file called myscene.pov and it contains

#declare A = 123;
#declare B = rgb<1,2,3>;
#declare C = 0;
#include "myinc.inc"

Inside the include file you invoke a macro called MyMacro(J,K,L). It is not important where MyMacro is defined as long as it is defined before it is invoked. In this example, it is important that the macro is invoked from within myinc.inc.

The identifiers A, B, and C are generally available at all levels. If either myinc.inc or MyMacro contain a line such as #declare C=C+1; then the value C is changed everywhere as you might expect.

Now suppose inside myinc.inc you do...

#local A = 546;

The main version of A is hidden and a new A is created. This new A is also available inside MyMacro because MyMacro is called from inside myinc.inc. Once you exit myinc.inc, the local A is destroyed and the original A with its value of 123 is now in effect. Once you have created the local A inside myinc.inc, there is no way to reference the original global A unless you #undef A or exit the include file. Using #undef always undefines the most local version of an identifier.

Similarly if MyMacro contained...

#local B = box{0,1}

then a new identifier B is created local to the macro only. The original value of B remains hidden but is restored when the macro is finished. The local B need not have the same type as the original.

The complication comes when trying to assign a new value to an identifier at one level that was declared local at an earlier level. Suppose inside myinc.inc you do...

#local D = 789;

If you are inside myinc.inc and you want to increment D by one, you might try to do...

#local D = D + 1;

but if you try to do that inside MyMacro you will create a new D which is local to MyMacro and not the D which is external to MyMacro but local to myinc.inc. Therefore you've said "create a MyMacro D from the value of myinc.inc's D plus one". That's probably not what you wanted. Instead you should do...

#declare D = D + 1;

You might think this creates a new D that is global but it actually increments the myinc.inc version of D. Confusing isn't it? Here are the rules:

  1. When referencing an identifier, you always get the most recent, most local version. By "referencing" we mean using the value of the identifier in a POV-Ray statement or using it on the right of an equals sign in either a #declare or #local.
  2. When declaring an identifier using the #local keyword, the identifier which is created or has a new value assigned, is ALWAYS created at the current nesting level of macros or include files.
  3. When declaring a NEW, NON-EXISTANT identifier using #declare, it is created as fully global. It is put in the symbol table of the main scene file.
  4. When ASSIGNING A VALUE TO AN EXISTING identifier using #declare, it assigns it to the most recent, most local version at the time.

In summary, #local always means "the current level", and #declare means "global" for new identifiers and "most recent" for existing identifiers.

New in version 3.8: To circumvent the pitfalls of identifier name collisions, two pseudo-dictionaries are provided to specifically refer to identifiers at either the global or the most local scope. See also the Array section for more information.

Destroying Identifiers with undef

Identifiers created with #declare will generally persist until parsing is complete. Identifiers created with #local will persist until the end of the macro or include file in which they were created. You may however un-define an identifier using the #undef directive. For example:

#undef MyValue

If multiple local nested versions of the identifier exist, the most local most recent version is deleted and any identically named identifiers which were created at higher levels will still exist.

See also The #ifdef and #ifndef Directives.

Deprecation Support

The ability to add a deprecated flag to a #declare has been added. This is to aid in migrating scenes away from old constructs (e.g. old textures). The usage is illustrated below:

#declare deprecated Col_Glass_Old=color rgbf <0.8, 0.9, 0.85, 0.85>;
#declare deprecated once Col_Glass_Old=... etc
#declare deprecated "Some message" Col_Glass_Old=... etc

A deprecated identifier generates no message at the time it is declared, a warning is only issued if it is used.

If the optional once keyword is present it must immediately follow the deprecated keyword and indicates that the warning should only be displayed once per parse.

If the optional message string is present, it will be used as the warning to be displayed if the identifier is used. Otherwise, a message in the form "Identifier Col_Glass_Old was declared deprecated." is used.

An identifier is considered used if it is referenced anywhere (even if in another #declare).