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
}
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.
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
}
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
}
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!"
}
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 case
s 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:
else
instead of default
.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")
}
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 }