Thyme Additional Functions
Thyme Additional Functions
To import one of these functions into your scene, copy the contents of the code section and
paste it into the console.
Iteration
xFor
Written by Kilinich.
Performs a given function a given number of times.
Usage: Scene.my.xFor(n1, n2, code)
Example: Scene.my.xFor(5, 10, (i)=>{ print(i) }) prints the numbers 5-10 in the
console.
Parameters
Code
Scene.my.xFor := (n1, n2, code)=>{
n2 > n1 ? {
m := (n1 + n2) / 2;
Scene.my.xFor(n1, m, code);
Scene.my.xFor(m + 1, n2, code)
} : {code(n1)}
};
xWhile
Written by The Real Thing. A modified version of the original code is used here.
Performs a given function while a given condition is true.
Usage: Scene.my.xWhile(conditionFunc, mainFunc)
Example:
Scene.my.xWhile({
Scene.my.apples > 3
}, {
Scene.my.apples = Scene.my.apples - 1
});
Decreases the value of Scene.my.apples while Scene.my.apples is greater than 3.
Parameters
Code
Scene.my.xWhile = (conditionFunc, mainFunc)=>{
unfinished := math.toBool(conditionFunc);
iteration := {
unfinished ? {
mainFunc;
unfinished = math.toBool(conditionFunc)
} : {}
};
str := "iteration; iteration; iteration; iteration; iteration; iteration;
iteration; iteration";
exec := {
unfinished ? {
eval(str);
str = str + "; " + str;
exec
} : {}
};
exec;
// According to Steve Noonan, these two lines are to fix memory leaks when
using this function.
iteration = "";
exec = "";
};
Angle manipulation
cosh
Returns the hyperbolic cosine of a given angle in radians.
Usage: Scene.my.cosh(angle)
Example: Scene.my.cosh(math.log(2)) returns 1.25
Parameters
Code
Scene.my.cosh := (angle)=>{
(math.e ^ angle + math.e ^ (-angle)) / 2
};
deltaAngle
Written by Erikfassett.
Returns the shortest difference between two given angles in radians. The resulting angle can be
added to the first angle to get the second. This function assumes Algodoo’s range of angles [-π, π]
is being used, inputs must be within this range.
Usage: Scene.my.deltaAngle(angle1, angle2)
Example: Scene.my.deltaAngle(2.0, -2.0) returns 2.2831855
Parameters
Code
Scene.my.deltaAngle := (angle1, angle2)=>{
delta := angle2 - angle1;
delta > math.pi ? {
delta = delta - 2 * math.pi
} : {
delta < -math.pi ? {
delta = delta + 2 * math.pi
} : {}
};
delta;
};
sinh
Returns the hyperbolic sine of a given angle in radians.
Usage: Scene.my.sinh(angle)
Example: Scene.my.sinh(math.log(2)) returns 0.75
Parameters
Code
Scene.my.sinh := (angle)=>{
(math.e ^ angle - math.e ^ (-angle)) / 2
};
tanh
Returns the hyperbolic tangent of a given angle in radians.
Usage: Scene.my.tanh(angle)
Example: Scene.my.tanh(math.log(2)) returns 0.6
Parameters
Code
Scene.my.tanh := (angle)=>{
(math.e ^ (2 * angle) - 1) / (math.e ^ (2 * angle) + 1)
};
Geometry generation
generatePolygonVertices
Returns a list of vertex positions of a polygon with the given properties.
Usage: Scene.my.generatePolygonVertices(radius, sides)
Example: Scene.my.generatePolygonVertices(1, 3) returns
[[1, 0], [-0.50000006, 0.86602539], [-0.49999991, -0.86602545]]
(the vertices of a triangle with ‘radius’ 1)
Parameters
Code
Scene.my.generatePolygonVertices := (radius, sides)=>{
outputList := [];
for(sides, (i)=>{
angle = (2 * math.pi * i) / sides;
outputList = outputList ++ [radius * [math.cos(angle),
math.sin(angle)]]
});
outputList
};
Object manipulation
selectionFilter
Written by Lapse and Erikfassett.
When used as a command in the object console with multiple objects selected, it filters the
selection to only include the objects that match the condition. Not meant to be used in typical
scripts, meant to be used as quality of life to make it easier to select objects with specific
properties.
Usage: Scene.my.selectionFilter(condition, entityID)
Example: Scene.my.selectionFilter(color(1) > 0.5, entityID) filters the
selected objects to only include those with a green value of at least 0.5.
Parameters
Code
Scene.my.selectionFilter := (condition, entityID)=>{
selectionReset := {
Scene.temp.selected = [];
Scene.temp.selectedTime = System.time + 1.0
};
Scene.temp.selected == undefined || Scene.temp.selectedTime == undefined ?
{
selectionReset
} : {
System.time > Scene.temp.selectedTime ? {
selectionReset
} : {}
};
Scene.addGroup({
entityIDs := Scene.temp.selected = Scene.temp.selected ++ (condition ?
[entityID] : []);
name := "selected"
})
};
List manipulation
all
Returns true if all elements in the list meet a given condition.
Usage: Scene.my.all(list, predicate)
Example: Scene.my.all([1, 2, 3], (number)=>{number > 0}) returns true (all the
numbers in the list are greater than zero)
Parameters
Code
Scene.my.all := (list, predicate)=>{
listLength := string.length(list);
if_then_else(listLength == 0, {
true;
}, {
if_then_else(predicate(list(0)), {
Scene.my.all(list(1 .. (listLength - 1)), predicate);
}, {
false;
});
});
};
any
Returns true if any element in the list meets a given condition.
Usage: Scene.my.any(list, predicate)
Example: Scene.my.any([-3, 0], (number)=>{number > 0}) returns false (none of
the numbers in the list are greater than zero)
Parameters
Code
Scene.my.any := (list, predicate)=>{
listLength := string.length(list);
if_then_else(listLength == 0, {
false;
}, {
if_then_else(predicate(list(0)), {
true;
}, {
Scene.my.any(list(1 .. (listLength - 1)), predicate);
});
});
};
average
Returns the average of the given list of numbers.
Usage: Scene.my.average(list)
Example: Scene.my.average([3, 4, 5]) returns 4
Parameters
Code
Scene.my.average := (list)=>{
sum := 0;
listLength := string.length(list);
for(listLength, (i)=>{
sum = sum + list(i);
});
sum / listLength;
};
contains
Returns true if the given element is present in the list.
Usage: Scene.my.contains(list, element)
Example: Scene.my.contains([3, 4, 5], 3) returns true (the list [3, 4, 5] contains
3)
Parameters
element Bool, Float, String, The element to check the list for.
Bool/Float/String
List
Code
Scene.my.contains := (list, element)=>{
contains := false;
for(string.length(list), (i)=>{
if_then_else(list(i) == element, {
contains = true
}, {})
});
contains
};
containsFast
Written by Erikfassett.
Alternate version of the contains function built for speed but with variable type limitations.
Returns true if the given element is present in the list. Only works on lists of either numbers or
strings.
Usage: Scene.my.containsFast(list, element)
Example: Scene.my.containsFast([3, 4, 5], 3) returns true (the list [3, 4, 5]
contains 3)
Parameters
element Float, String The element to check the list for. Must match
type of list.
Code
Scene.my.containsFast := (list, element)=>{
string.length(set.insert(list, element)) == string.length(list)
};
filter
Returns a list containing all the elements of the given list that meet a given condition.
Usage: Scene.my.filter(list, predicate)
Example: Scene.my.filter(0 .. 10, (n)=>{n > 5}) returns [6, 7, 8, 9, 10] (the
integers between 0 and 10 inclusive that are greater than 5)
Parameters
Code
Scene.my.filter := (list, predicate)=>{
newList := [];
for(string.length(list), (i)=>{
if_then_else(predicate(list(i)), {
newList = newList ++ list[i];
}, {});
});
newList;
};
fold
Runs an accumulator function over a list, combining the elements of the list to return a single
value.
Usage: Scene.my.fold(initialValue, list, func)
Example: Scene.my.fold(0, [3, 4, 5], (total, number)=>{total + number})
returns 12 (the sum of the numbers in the list)
Parameters
Code
Scene.my.fold := (initialValue, list, func)=>{
accumulator := initialValue;
for(string.length(list), (i)=>{
accumulator = func(accumulator, list(i))
});
accumulator
};
indexOf
Returns the index of the first instance of a given element in the given list. Returns -1 if element is
not found.
Usage: Scene.my.indexOf(list, element)
Example: Scene.my.indexOf(["Hello", "World", "From", "Thyme"], "From")
returns 2
Parameters
Code
Scene.my.indexOf := (list, element)=>{
index := -1;
for(string.length(list), (i)=>{
if_then_else(index == -1 && list(i) == element, {
index = i
}, {})
});
index
};
insertAtIndex
Inserts the given element at the given index of a list, returning the new list.
Usage: Scene.my.insertAtIndex(list, index, element)
Example: Scene.my.insertAtIndex([3, 4, 5], 1, 6) returns [3, 6, 4, 5]
Parameters
map
Applies the given function to each element of the given list, returning a list of the results.
Usage: Scene.my.map(list, func)
Example: Scene.my.map(0 .. 5, (n)=>{n + 3}) returns [3, 4, 5, 6, 7, 8] (the
integers between 3 and 8 inclusive)
Parameters
Code
Scene.my.map := (list, func)=>{
newList := [];
for(string.length(list), (i)=>{
newList = newList ++ [func(list(i))];
});
newList;
};
max
Returns the maximum of the given list of numbers.
Usage: Scene.my.max(list)
Example: Scene.my.max([3, 4, 5]) returns 5
Parameters
Code
Scene.my.max := (list)=>{
max := -inf;
for(string.length(list), (i)=>{
if_then_else(list(i) > max, {
max = list(i);
}, {})
});
max;
};
mergeSort
Sorts the list using the merge sort algorithm, returning the sorted list.
Usage: Scene.my.mergeSort(list)
Example: Scene.my.mergeSort([3, 5, 4]) returns [3, 4, 5]
Dependencies:
● Scene.my.xWhile
Parameters
Code
Scene.my.mergeSort := (list)=>{
listLength := string.length(list);
if_then_else(listLength > 1, {
midpoint := listLength / 2;
leftList := list(0 .. (midpoint - 1));
rightList := list(midpoint .. (listLength - 1));
leftList = Scene.my.mergeSort(leftList);
rightList = Scene.my.mergeSort(rightlist);
Scene.my.merge(leftList, rightList);
}, {
list;
});
};
Scene.my.merge := (leftList, rightList)=>{
result := [];
newLeftList := leftList;
newRightList := rightList;
Scene.my.xWhile({
string.length(newLeftList) != 0 && string.length(newRightList) != 0
}, {
if_then_else(newLeftList(0) <= newRightList(0), {
result = result ++ [newLeftList(0)];
newLeftList = newLeftList(1 .. (string.length(newLeftList) - 1));
}, {
result = result ++ [newRightList(0)];
newRightList = newRightList(1 .. (string.length(newRightList) -
1));
});
});
Scene.my.xWhile({
string.length(newLeftList) != 0
}, {
result = result ++ [newLeftList(0)];
newLeftList = newLeftList(1 .. (string.length(newLeftList) - 1));
});
Scene.my.xWhile({
string.length(newRightList) != 0
}, {
result = result ++ [newRightList(0)];
newRightList = newRightList(1 .. (string.length(newRightList) - 1));
});
result;
};
min
Returns the minimum of the given list of numbers.
Usage: Scene.my.min(list)
Example: Scene.my.min([3, 4, 5]) returns 3
Parameters
Code
Scene.my.min := (list)=>{
min := +inf;
for(string.length(list), (i)=>{
if_then_else(list(i) < min, {
min = list(i);
})
});
min;
};
modifyAtIndex
Modifies the element at the given index from a list, returning the new list.
Usage: Scene.my.modifyAtIndex(list, index, newElement)
Example: Scene.my.modifyAtIndex([3, 4, 5], 1, 6) returns [3, 6, 5]
Parameters
Code
Scene.my.modifyAtIndex := (list, index, newElement)=>{
list(0 .. (index - 1)) ++ [newElement] ++ list((index + 1) ..
(string.length(list) - 1))
};
quickSort
Written by Brocolimanx with help from Erikfassett and YellowDude.
Sorts the list using the quick sort algorithm, returning the sorted list.
Usage: Scene.my.quickSort(list)
Example: Scene.my.quickSort([3, 5, 4]) returns [3, 4, 5]
Parameters
Code
Scene.my.quickSort = (arr)=>{
len := string.length(arr);
len <= 1 ? arr : {
average := 0;
for(len, (i)=>{
average = average + arr(i)
});
average = math.tofloat(average) / len;
smaller := [];
larger := [];
equal := [];
for(len, (i)=>{
arr(i) > average ? {
larger = larger ++ [arr(i)]
} : {};
arr(i) < average ? {
smaller = smaller ++ [arr(i)]
} : {};
arr(i) == average ? {
equal = equal ++ [arr(i)]
} : {}
});
Scene.my.quickSort(smaller) ++ equal ++ Scene.my.quickSort(larger)
}
};
removeAtIndex
Removes the element at the given index from a list, returning the new list.
Usage: Scene.my.removeAtIndex(list, index)
Example: Scene.my.removeAtIndex([3, 4, 5], 2) returns [3, 4]
Parameters
Code
Scene.my.removeAtIndex := (list, index)=>{
list(0 .. (index - 1)) ++ list((index + 1) .. (string.length(list) - 1))
};
removeFirstItem
Removes the first matching item from a list, returning the new list.
Usage: Scene.my.removeFirstItem(list, item)
Example: Scene.my.removeFirstItem([7, 6, 5, 6, 4], 6) returns [7, 5, 6, 4]
Parameters
removeItem
Written by Erikfassett.
Removes all elements that match the item from a list, returning the new list. Only works on lists of
floats/ints and strings, and the list cannot be a mix of numbers and strings, only one or the other.
Important: If the list has duplicate items, all duplicates will be collapsed into a single item even
when they’re not targeted.
Usage: Scene.my.removeItem(list, item)
Example: Scene.my.removeItem([7, 6, 5, 6, 4], 6) returns [7, 5, 4]
Parameters
Code
Scene.my.removeItem := (list, item)=>{
newList := set.merge([item], list);
newList(1 .. (string.length(newList) - 1))
};
removeItems
Written by Erikfassett.
Removes all elements from a list that match an item in a second list, returning the new list. Only
works on lists of floats/ints and strings, and the list cannot be a mix of numbers and strings, only
one or the other.
Important: If the list has duplicate items, all duplicates will be collapsed into a single item even
when they’re not targeted.
Usage: Scene.my.removeItems(list, items)
Example: Scene.my.removeItems([7, 6, 5, 6, 4], [5, 6]) returns [7, 4]
Parameters
Code
Scene.my.removeItems := (list, items)=>{
newList := set.merge(items, list);
newList(string.length(items) .. (string.length(newList) - 1))
};
reverse
Returns the reverse of the given list.
Usage: Scene.my.reverse(list)
Example: Scene.my.reverse([3, 4, 5]) returns [5, 4, 3]
Parameters
shuffle
Written by Lapse.
Shuffles the given list.
Usage: Scene.my.shuffle(list)
Example: Scene.my.shuffle([0, 1, 2]) returns [1, 0, 2]
Parameters
Code
Scene.my.shuffle := (list)=>{
input := list;
output := [];
for(string.length(list), (i)=>{
index := math.toInt(rand.uniform01 * string.length(input));
output = output ++ [input(index)];
input = input(0 .. index - 1) ++ input(index + 1 ..
string.length(input) - 1)
});
output
};
Other mathematical functions
abs
Returns the absolute value of a given number.
Usage: Scene.my.abs(number)
Example: Scene.my.abs(-4) returns 4
Parameters
Code
Scene.my.abs := (number)=>{
number < 0 ? -number : number;
};
round
Written by Erikfassett.
Rounds a number to a given number of decimal places.
Usage: Scene.my.round(number, digits)
Example: Scene.my.round(math.pi, 3) returns 3.142 (pi to 3 decimal places).
Parameters
sign
Written by Lapse.
Returns the sign of a given number, or zero if zero.
Usage: Scene.my.sign(number)
Example: Scene.my.sign(-5) returns -1 .
Parameters
Code
Scene.my.sign := (number)=>{
number > 0 ? 1 : number < 0 ? -1 : 0
};
Code
Scene.my.randFloat := (min, max)=>{
min + rand.uniform01 * (max - min);
};
randInt
Written by Erikfassett.
Returns a random integer between an inclusive lower bound and an exclusive upper bound.
Usage: Scene.my.randInt(min, max)
Example: Scene.my.randInt(5, 10) could return 6
Parameters
Code
Scene.my.randInt := (min, max)=>{
min + math.toInt(rand.uniform01 * (max - min));
};
String manipulation
addString
Written by YellowDude.
Adds a string into the given string.
Usage: Scene.my.addString(input, substring, index)
Example: Scene.my.addString("abcdeg", "f", 5) returns "abcdefg"
Parameters
Code
Scene.my.addString := (input, substring, index)=>{
listChar := string.str2list(input);
ret := [];
for(string.length(input), (i)=>{
i == index ? {
ret = ret ++ [substring]
} : {};
ret = ret ++ listChar([i])
});
string.list2str(ret)
};
containsString
Written by Lapse.
Checks if a string contains a substring.
Usage: Scene.my.containsString(input, substring)
Example: Scene.my.containsString("Hello", "ll") returns true
Parameters
Code
Scene.my.containsString := (input, substring)=>{
contains := false;
listString := string.str2list(input);
listTerm := string.str2list(substring);
termIndex := 0;
termLength := string.length(substring) - 1;
for(string.length(listString), (i)=>{
listString(i) == listTerm(termIndex) ? {
termIndex < termLength ? {
termIndex = termIndex + 1;
} : {
contains = true;
termIndex = 0;
}
} : {
listString(i) == listTerm(0) ? {
termIndex = 1;
} : {
termIndex = 0
}
}
});
contains
};
duplicateString
Duplicates string by the specified number of times.
Usage: Scene.my.duplicateString(str, times)
Example: Scene.my.duplicateString("string", 2) returns "stringstringstring"
Parameters
Code
Scene.my.duplicateString := (str, times)=>{
output := str;
for(times, (i)=>{
output = output + str
});
output
};
removeChar
Modified by Erikfassett and Lapse.
Removes all instances of a character from a string.
Usage: Scene.my.removeChar(str, char)
Example: Scene.my.removeChar("Hello", "l") returns "Heo"
Parameters
Code
Scene.my.removeChar := (str, char)=>{
string.list2str(string.split(str, char))
};
removeString
Written by Lapse.
Removes all instances of a substring from a string.
Usage: Scene.my.removeString(input, stringToRemove)
Example: Scene.my.removeString("Hello", "ll") returns "Heo"
Parameters
Code
Scene.my.removeString := (input, stringToRemove)=>{
listString := string.str2list(input);
listTerm := string.str2list(stringToRemove);
outputString := "";
termIndex := 0;
termLength := string.length(stringToRemove) - 1;
termTemp := "";
for(string.length(listString), (i)=>{
listString(i) == listTerm(termIndex) ? {
termIndex < termLength ? {
termIndex = termIndex + 1;
termTemp = termTemp + listString(i)
} : {
termIndex = 0;
termTemp = ""
}
} : {
outputString = outputString + termTemp;
listString(i) == listTerm(0) ? {
termIndex = 1;
termTemp = termTemp + listString(i)
} : {
outputString = outputString + listString(i);
termIndex = 0;
termTemp = ""
}
}
});
outputString
};
replaceChar
Replaces all instances of a character in a string with another character.
Usage: Scene.my.replaceChar(str, find, replace)
Example: Scene.my.replaceChar("Hello", "l", "n") returns "Henno"
Parameters
Code
Scene.my.replaceChar := (str, find, replace)=>{
findSplit := string.split(str, find);
splitLength := string.length(findSplit);
output := "";
for(splitLength - 1, (i)=>{
output = output + findSplit(i) + replace
});
output + findSplit(splitLength - 1)
};
replaceString
Written by Lapse.
Replaces all instances of a substring in a string with another substring.
Usage: Scene.my.replaceString(input, stringToRemove, stringReplace)
Example: Scene.my.replaceString("string", "str", "be") returns "being"
Parameters
Code
Scene.my.replaceString := (input, stringToRemove, stringReplace)=>{
listString := string.str2list(input);
listTerm := string.str2list(stringToRemove);
output := "";
termIndex := 0;
termLength := string.length(stringToRemove) - 1;
termTemp := "";
for(string.length(listString), (i)=>{
listString(i) == listTerm(termIndex) ? {
termIndex < termLength ? {
termIndex = termIndex + 1;
termTemp = termTemp + listString(i)
} : {
output = output + stringReplace;
termIndex = 0;
termTemp = ""
}
} : {
output = output + termTemp;
listString(i) == listTerm(0) ? {
termIndex = 1;
termTemp = termTemp + listString(i)
} : {
output = output + listString(i);
termIndex = 0;
termTemp = ""
}
}
});
output
};
toLower
Written by Lapse.
Converts UPPERCASE letters to lowercase.
Usage: Scene.my.toLower(input)
Example: Scene.my.toLower("LOWERCASE") returns "lowercase"
Parameters
Code
Scene.my.toLower := (input)=>{
characters := string.str2list(input);
output := "";
lower := string.str2list("abcdefghijklmnopqrstuvwxyz");
upper := "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for(string.length(input), (i)=>{
charTest := set.insert(string.str2list(upper), characters(i));
string.length(charTest) == string.length(upper) ? {
upperSplit := string.split(upper, characters(i));
output = output + lower(string.length(upperSplit(0)))
} : {
output = output + characters(i)
}
});
output
};
toOrdinal
Written by Erikfassett.
Converts integers into an ordinal number.
Usage: Scene.my.toOrdinal(input)
Example: Scene.my.toOrdinal(3) returns "3rd"
Parameters
Code
Scene.my.toOrdinal := (n)=>{
math.toString(n) + (n % 100 / 10 == 1 ? "th" : n % 10 == 1 ? "st" : n % 10
== 2 ? "nd" : n % 10 == 3 ? "rd" : "th")
};
toUpper
Written by Lapse.
Converts lowercase letters to UPPERCASE.
Usage: Scene.my.toUpper(input)
Example: Scene.my.toUpper("uppercase") returns "UPPERCASE"
Parameters
Vector manipulation
dotProduct
Returns the dot product (also known as the scalar product) of two two-dimensional vectors.
Usage: Scene.my.dotProduct(vector1, vector2)
Example: Scene.my.dotProduct([0, 3], [2, 1]) returns 3
Parameters
vector1 Float List The first vector to perform the calculation on.
Code
Scene.my.dotProduct := (vector1, vector2)=>{
vector1(0) * vector2(0) + vector1(1) * vector2(1);
};
normaliseVector
Returns the unit vector with the same direction as the given vector.
Usage: Scene.my.normaliseVector(vector)
Example: Scene.my.normaliseVector([3, 4]) returns [0.6, 0.8]
Parameters
Code
Scene.my.normaliseVector := (vector)=>{
vector / math.vec.len(vector);
};
vectorAngle
Returns the unsigned angle (in radians) between two two-dimensional vectors.
Usage: Scene.my.vectorAngle(vector1, vector2)
Example: Scene.my.vectorAngle([1, 0], [0, 1]) returns 1.5707964 (π / 2 or 90°)
Dependencies:
● Scene.my.dotProduct
Parameters
vector1 Float List The first vector to perform the calculation on.