ShapeScript

Control Flow

Loops

To repeat an instruction (or sequence of instructions) you can use a for loop. The simplest form of the for loop takes a numeric range, and a block of instructions inside braces. The following loop creates a circle of 5 points (you might use this inside a path):

for 1 to 5 {
    point 0 1
    rotate 2 / 5
}

The range 1 to 5 is inclusive of both the start and end values. A range of 0 to 5 would therefore loop 6 times and not 5 as you might expect.

The loop range does not have to be a literal value, you can use a previously defined symbol or expression instead:

define count 5

for 1 to count {
    point 0 1
    rotate 2 / count
}

Loop Index

If you have used similar loops in other programming languages, you might be wondering why we don’t need to use an index variable of some kind to keep track of the loop iteration?

Symbols defined inside the { ... } block will not persist between loops (see scope for details), but changes to the world transform will, which is why the rotate command doesn’t need to reference the index - its effect is cumulative.

If you do need to reference the index inside your loop for some reason, you can define a loop index symbol like this:

for i in 1 to count {
    point 0 i
}

This defines a symbol called i with the value of the current loop iteration. The i symbol only exists within the loop body itself and can’t be referenced after the loop has ended.

Note: The index symbol does not need to be called i, it can be any valid symbol name that you choose.

If you want to loop in increments greater or less than 1, you can use the optional step property:

for i in 1 to 5 step 2 {
    print i // prints 1, 3, 5 
}

for i in 0 to 1 step 0.2 {
    print i // prints 0, 0.2, 0.4, 0.6, 1
}

If not specified, the step value defaults to 1.

Looping Backwards

If the end value of a loop is less than the start value, the loop body will normally be skipped, but if you do wish to loop backwards you can achieve this by using a negative step value:

for i in 5 to 1 step -1 {
    print i // prints 5, 4, 3, 2, 1
}

Looping Over Values

As well as looping over a numeric range, you can also loop over a tuple of values:

define values 1 5 7 9

for i in values {
    print i // prints 1 5 7 9
}

The values can be non-numeric, or even a mix of different types:

define values "Mambo" "No." 5

for i in values {
    print i // prints Mambo No. 5
}

Note: To use a list of values directly in the loop definition, they must be placed in parentheses:

for i in ("parentheses" "are" "required") {
    print i
}

If-Else

Sometimes instead of repeating an action multiple times you need to perform it just once, but conditionally, based on some programmatic criteria. For example, your scene might have multiple configurations that you can switch between by setting constants at the top of the file.

To execute code conditionally, you can use an if statement:

define showCube true

if showCube {
    cube   
}

The showCube constant here is a boolean that can have the value true or false. The condition for an if statement must always be a boolean expression. The body of the if statement will only be executed if the condition is true.

To perform an alternative action for when the condition is false, you can add an else clause:

if showCube {
    cube   
} else {
    sphere   
}

You can chain multiple conditional statements using the else if construct:

if showCube {
    cube   
} else if showSphere {
    sphere   
} else if showCone {
    cone
} else {
    print "Nothing to see here!"   
}

Switch-Case

To select between multiple mutually-exclusive cases based on some value, you can use an else if chain, but it can be a bit cumbersome:

if value == 1 {
    // Do something
} else if value == 2 {
    // Do something else
} else if value == 3 {
    // Do something different
} else {
    // And so on ...
}

Instead, you can use a switch statement. This takes an input and performs one of several cases depending on its value:

switch value {
case 1
    // Do something
case 2
    // Do something else
case 3
    // Do something different
else
    // And so on ...
}

Note: If you are familiar with the C family of languages you’ll probably recognize this, but you should note that there are a few differences between the ShapeScript and C switch semantics:

Although there is no way to fall through from one case to another, you can group cases together using tuples to avoid repeating the case body:

switch value {
case 1 2
    print("Value was 1 or 2")
case 3
    print("Value was 3")
}

Conditional Defines

Something you might want to do with an if or switch statement is to conditionally define a constant value, for example:

define highlighted true

if highlighted {
    define cubeColor red
} else {
    define cubeColor white  
}

cube cubeColor

Unfortunately this won’t work, due to the scope rules. The cubeColor symbol is only defined inside the if statement blocks themselves, and can’t be accessed outside. So how can you set the value of cubeColor conditionally?

The solution is to move the if statement inside the define itself, like this:

define highlighted true

define cubeColor {
    if highlighted {
        red
    } else {
        white  
    }
}

cube { color cubeColor }

Index | Next: Blocks