Reference:Conditional Directives

From POV-Wiki
Revision as of 15:42, 10 March 2015 by Jholsenback (talk | contribs) (indexentry tag repair)
Jump to navigation Jump to search

POV-Ray allows a variety of language directives to implement conditional parsing of various sections of your scene file. This is especially useful in describing the motion for animations but it has other uses as well. Also available is a #while loop directive. You may nest conditional directives 200 levels deep.

The if...else...end Directives

The simplest conditional directive is the traditional #if directive. The syntax is:

IF_DIRECTIVE:
  #if ( Cond ) TOKENS... [ELSE_DIRECTIVE] #end
ELSE_DIRECTIVE:
  #else TOKENS... |
  #elseif ( Cond ) TOKENS... [ELSE_DIRECTIVE]

The TOKENS are any number of POV-Ray keyword, identifiers, or punctuation and ( Cond ) is a float expression that is interpreted as a boolean value. The parentheses and the #end directive are required, an optional #elseif clause is now supported.

A value of 0.0 is false (numbers <= 1e-10 are also considered zero) while other any non-zero value is true.

For example:

#if (Foo)
  #debug "Foo is true\n"
#elseif (Bar)
  #debug "Foo is false, but Bar is true\n"
#else
  #debug "Foo and Bar are both false\n"
#end

Note: Nesting directives in the following manner has been known to cause problems during the parse phase.

#if( #if(yes) yes #end ) #end

The ifdef and ifndef Directives

The #ifdef and #ifndef directive are similar to the #if directive however they are used to determine if an identifier has been previously declared.

IFDEF_DIRECTIVE:
  #ifdef ( IDENTIFIER ) TOKENS... [ELSE_DIRECTIVE] #end
IFNDEF_DIRECTIVE:
  #ifndef ( IDENTIFIER ) TOKENS... [ELSE_DIRECTIVE] #end
ELSE_DIRECTIVE:
  #else TOKENS... |
  #elseif ( Cond ) TOKENS... [ELSE_DIRECTIVE]

If the IDENTIFIER exists then the first group of tokens is parsed normally and the second set is skipped. If false, the first set is skipped and the second set is parsed. This is especially useful for replacing an undefined item with a default.

For example:

#ifdef (User_Thing)
  // This section is parsed if the
  // identifier "User_Thing" was
  // previously declared
  object{User_Thing} // invoke identifier
#else
  // This section is parsed if the
  // identifier "User_Thing" was not
  // previously declared
  box{<0,0,0>,<1,1,1>} // use a default
#end
// End of conditional part

The #ifndef directive works the opposite. The first group is parsed if the identifier is not defined. As with the #if directive, the #else clause is optional and the #end directive is required.

The #ifdef and #ifndef directives can be used to determine whether a specific element of an array has been assigned.

#declare MyArray=array[10]
//#declare MyArray[0]=7;
#ifdef(MyArray[0])
  #debug "first element is assigned\n"
#else 
  #debug "first element is not assigned\n"
#end

Note: Additionally, just like the #if directive, the #ifdef and #ifndef directives now also supports an #elseif clause.

The for Directive

A new #for loop construct is now available for simple loops incrementing Identifier from Start to End (inclusive) with the given Step size. The default Step size is +1.0.

The syntax is:

#for (Identifier, Start, End [, Step])
  //...
#end

Consider the following example:

#for (i,0,330,30)
  sphere { 0,1 translate <0,0,-10> rotate y*i }
#end

The Identifier (i in this case) starts at 0 and ends at 330 ... an optional step value of 30 was added.

Some additional notes:

  • If Step is negative, comparison will be automatically adjusted to match a countdown pattern.
  • Start, End and Step are evaluated only once.
  • The loop counter is a full-fledged local variable. Any local variable of the same name already defined before the loop will be overwritten without warning (note that in the main scene file, all local variables outside of macros are effectively global); inside the loop, any tampering with the variable is possible for effect, as long as it is defined as a local numeric variable at the end of each iteration.
  • After the loop has terminated, the variable will remain defined, typically holding the value End+Step.
  • The loop counter must not be an array element.

Note: See the end of the next section for more information about #break directive behavior.

The switch, case, range and break Directives

A more powerful conditional is the #switch directive. The syntax is as follows...

SWITCH_DIRECTIVE:
  #switch ( Switch_Value ) SWITCH_CLAUSE... [#else TOKENS...] #end
SWITCH_CLAUSE:
  #case( Case_Value ) TOKENS... [#break] |
  #range( Low_Value , High_Value ) TOKENS... [#break]

The TOKENS are any number of POV-Ray keyword, identifiers, or punctuation and ( Switch_Value ) is a float expression. The parentheses are required. The #end directive is required. The SWITCH_CLAUSE comes in two varieties. In the #case variety, the float Switch_Value is compared to the float Case_Value. If they are equal, the condition is true.

Note: Values whose difference is less than 1e-10 are considered equal in case of round off errors.

In the #range variety, Low_Value and High_Value are floats separated by a comma and enclosed in parentheses.

If Low_Value <= Switch_Value and Switch_Value <= High_Value then the condition is true.

In either variety, if the clause's condition is true, that clause's tokens are parsed normally and parsing continues until a #break, #else or #end directive is reached. If the condition is false, POV-Ray skips until another #case or #range is found.

There may be any number of #case or #range clauses in any order you want. If a clause evaluates true but no #break is specified, the parsing will fall through to the next #case or #range and that clause conditional is evaluated. Hitting #break while parsing a successful section causes an immediate jump to the #end without processing subsequent sections, even if a subsequent condition would also have been satisfied.

An optional #else clause may be the last clause. It is only executed if the clause before it was a false clause.

Additionally, within the #switch directive, the #else clause acts like a #range directive that encompasses any possible number. To avoid execution of the #else block, the preceding #case or #range directives must be ended with a #break statement.

Note: The above behavior applies only within the #switch directive.

For example:

#switch (VALUE)
  #case (TEST_1)
    // This section is parsed if VALUE=TEST_1
  #break //First case ends
  #case (TEST_2)
    // This section is parsed if VALUE=TEST_2
  #break //Second case ends
  #range (LOW_1,HIGH_1)
    // This section is parsed if (VALUE>=LOW_1)&(VALUE<=HIGH_1)
  #break //Third case ends
  #range (LOW_2,HIGH_2)
    // This section is parsed if (VALUE>=LOW_2)&(VALUE<=HIGH_2)
  #break //Fourth case ends
  #else
    // This section is parsed if no other case or
    // range is true.
#end

Note: As of version 3.7 the #break directive can now be used:

  • anywhere within a #case or #range block, to skip to the end of the #switch directive (previously, #break was only useful right before the next #case, #range or #else directive, to indicate that a slip-through was not desired).
  • anywhere within a loop block (both #while and #for), to terminate the loop.
  • anywhere within a #macro to preliminarily terminate the macro.

Example for the use in a loop:

#local R = seed(4711);
#for (I, 1, 100)
  #if (rand(R) < I/1000)
    #break // terminate loop early
  #end
  #debug concat(str(I,0,0), " iterations and counting\n")
#end

Where multiple #switch, loop and/or #macro blocks are nested, #break will leave only the innermost of these.

The while...end Directive

The #while directive is a looping feature that makes it easy to place multiple objects in a pattern or other uses.

WHILE_DIRECTIVE:
  #while ( Cond ) TOKENS... #end

The TOKENS are any number of POV-Ray keyword, identifiers, or punctuation marks which are the body of the loop. The #while directive is followed by a float expression that evaluates to a boolean value. A value of 0.0 is false and any non-zero value is true.

Note: Extremely small values of about 1e-10 are considered zero in case of round off errors.

The parentheses around the expression are required. If the condition is true parsing continues normally until an #end directive is reached. At the end, POV-Ray loops back to the #while directive and the condition is re-evaluated. Looping continues until the condition fails. When it fails, parsing continues after the #end directive.

Note: It is possible for the condition to fail the first time and the loop is totally skipped. It is up to the user to insure that something inside the loop changes so that it eventually terminates.

Here is a properly constructed loop example:

#declare Count=0;
#while (Count < 5)
  object { MyObject translate x*3*Count }
  #declare Count=Count+1;
#end

This example places five copies of MyObject in a row spaced three units apart in the x-direction.

Note: See the end of the previous section for more information about #break directive behavior.