Pixel Art Tips
This page has some tips for the Pixel Art assignment:
- The sketch (when you finish it) will have two recursive functions:
buildRandomFunctionconstructs an Array. If the first element of this Array is “avg” or “sin_pi” etc. (anything except “x” or “y”), then the second element is also an Array. For example, ["sin_pi", ["x"]], or ["sin_pi", ["sin_pi", ["x"]]].buildRandomFunctioncan callbuildRandomFunctionto create this second element.evaluateRandomFunctionis also recursive. Its argument is a Array (for example, ["x"] or ["sin_pi", ["x"]]), and it returns a number. If its argument is a list whose first element is e.g. “sin_pi” or "avg", then its second argument is also an Array, that needs to be turned into a number. If you had a function that took an Array as an argument and returned a Number, then you could write this function that takes an Array as an argument and returns a Number. This is a job for recursion!
- A recursive function needs to get closer to its base case (the values that will cause it not to call itself). The function function
badFactorial(n) { return n * badFactorial(n - 1); }will get an error “Maximum call stack size exceeded” because there is no base case. The functionfunction badFactorial(n) { if (n === 1) { return 1; } else { return n * badFactorial(n); } }will also get an error, because when it calls itself, it doesn’t get any closer to the base case. (Do you see why?) - One way to get closer to a base case is to if the argument is a number that counts down (is lower each time the function calls itself), like (good)
factorialorfibfrom Sunday. Another is to use an argument that counts up to some limit, that the base case tests for. A third is to if the function breaks its argument into smaller pieces, that the function applies itself recursively to. This is common when the argument is an array of arrays, or an object whose properties are objects, etc. - How does
buildRandomFunctioncall itself with arguments that are closer to its base case, so that it eventually reaches the base case instead of calling itself each time (likebadFactorial, above)? What is the base case, and what will bring the argument closer to this base case? - Don’t try to develop
buildRandomFunctionandevaluateRandomFunctionat the same time. (Don’t use whether the entire program works as a way to tell whether both of these functions work, before you have verfied that either of them works.) Instead, do this:- Call
buildRandomFunction, and useconsole.infoto print its results. This tests justbuildRandomFunctionwhether or notevaluateRandomFunctionis working. - Add an early return to
buildRandomFunctionthat returns the same value each time: for example, return ["x"], or return ["sin_pi", ["x"]]. You can use this to debug evaluateRandomFunction for just that value, without having to worry that evaluateRandomFunction will be called with a different value each time (this makes debugging difficult), or with a very complicated valu
- Call
buildRandomFunctionandevaluateRandomFunctiondon’t depend on anything in p5.js! (They don’t callrect()or any other p5.js functions, and they don’t use thewidthormouseXor variables or any other variables that are provided by p5.js.) This means that you can use JavaScript Tutor to trace through team.- While you are developing the program, try smaller numbers. Use smaller numbers for xSize and ySize so that your program starts more quickly, and you can see your changes more quickly. Use smaller numbers for minDepth and maxDepth so that the trees that
buildRandomFunctioncreates are not so deep, and they are easier to inspect – for example, withconsole.log. (This will also make evaluateRandomFunction faster.)
Here is an illustration of the kind of data that buildRandomFunction creates (returns), and that evaluateRandomFunction accepts (as its first argument).
![]()