Reference:Declare and Local Directives
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:
- 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
. - 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. - 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. - 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
).