home back

A Groovy survival kit

JMathAnim scripts are written in a language called Groovy. Don't panic: you don't need to "learn programming" to make great animations. You need about a dozen ideas, and this chapter contains all of them. Think of it as the phrasebook you take on a trip abroad: enough to order coffee, buy train tickets, and animate a pentagon.

Two pieces of good news before we start:

  1. A script is just a list of instructions executed from top to bottom, like a recipe. No hidden magic.
  2. The editor writes half of the code for you. Whenever there is an easier, point-and-click way to do something, we will tell you. Keep an eye out for the "Let the editor do it" boxes.

editorBasic

Anything after // on a line is ignored by JMathAnim. Use it generously; the person most likely to read your script in 6 months is you!

// This defines a unit circle (ignore me, I'm just a comment)
def c = Shape.circle()

Variables: giving names to your objects

If you plan to do anything with an object (move it, color it, animate it), you need a way to refer to it later. That's what a variable is: a name tag. In Groovy you create one with def:

def c = Shape.circle()   // "c" is now our circle
def teacher = "Emmy"     // variables can hold text too
def sides = 5            // ...or numbers
def f={x -> x*x}         // ...or even functions!

Names are case-sensitive (circle1 and Circle1 are different things), can't contain spaces, and can't start with a digit. Choose descriptive names: hypotenuse beats h2x every single time.

Numbers, PI and DEGREES

Numbers work as you would expect, with +, -, *, / and parentheses. Decimals use a point: 0.5 (you can even write it as .5, and you will see that a lot in this manual).

Two constants you will use constantly:

  • PI is π. A full turn is 2*PI.
  • Angles in JMathAnim are measured in radians. If radians make your students cry, just write 45*DEGREES and the conversion is done for you.
def sq = Shape.square().rotate(45*DEGREES)  // no tears were shed

For anything fancier there is the Math toolbox: Math.sin(x), Math.cos(x), Math.sqrt(x), Math.random() (a random number between 0 and 1), etc.

Text (strings) and the special r"..." strings

Text goes between quotes, single or double, your choice:

def color1 = "steelblue"
def color2 = 'tomato'

LaTeX code is a special case: it is full of \ and $ symbols, which Groovy normally tries to interpret. To avoid a fencing match of backslashes, JMathAnim adds raw strings: put an r before the quotes and everything inside is taken literally:

def t = LatexMathObject.make(r"$e^{i\pi}+1=0$")   // clean and readable

Triple quotes r'''...''' allow multi-line LaTeX code, handy for long formulas.

Let the editor do it: you rarely need to type LaTeX blind. Place the cursor inside a LaTeX string and open Tools → LaTeX Editor... (Ctrl+Shift+L): you get a live preview while you type, automatically or pressing alt+C, and the Insert Code button (alt+I ) puts the corrected string back into your script, both in Groovy and DSL syntax.

Calling methods

Objects know how to do things, and you ask them with a dot: object.method(arguments).

def c = Shape.circle()
c.fillColor("tomato")   // "hey circle, fill yourself with tomato"
c.scale(0.5)            // "now shrink to half size"
c.shift(1, 0)           // "and move one unit to the right"

Most methods politely return the same object, so you can chain calls with dots and read them like a sentence:

def c = Shape.circle().fillColor("tomato").scale(0.5).shift(1, 0)

You can split a long chain across lines (start the new line with the dot) to keep things readable:

def c = Shape.circle()
    .fillColor("tomato")
    .scale(0.5)
    .shift(1, 0)

Both forms are identical. Choose whichever your eyes prefer.

Getters without the get: a Groovy shortcut

Many methods just read a property of an object, and by Java tradition their names start with get: getCenter(), getWidth(), getMathView()... Groovy lets you drop the get and the parentheses and access them as if they were plain properties (just lowercase the first letter after get):

def view = camera.getMathView()   // the classic way (perfectly valid)
def view = camera.mathView        // the Groovy shortcut (exactly the same thing)

// It also works in chains:
def corner = obj.getBoundingBox().getUpperLeft()
def corner = obj.boundingBox.upperLeft            // same result, fewer parentheses

def w = circle.getWidth()
def w = circle.width                              // you get the idea

Both spellings are 100% interchangeable, so use whichever you find more readable. The examples in this manual mostly use the explicit getSomething() form (it makes clear that a method is being called), but don't be surprised if you meet the short form in other people's scripts.

Lists

Square brackets create a list. You will meet them when a command expects coordinates or several values:

def coords = [1, 2]        // a list with 2 numbers, often used as a point (1,2)
def levels = [0.5, 1, 1.5] // three levels for a contour plot

Ranges are a lovely shortcut for consecutive numbers: 0..5 means 0, 1, 2, 3, 4, 5 (and 0..<5 stops at 4). You will occasionally see *0..5 in this manual, which "unpacks" the range as if you had typed 0, 1, 2, 3, 4, 5 by hand. Groovy magic.

Maps and the DSL format

A map is a list of labeled values, written as name: value pairs. This is exactly the syntax used by the JMathAnim DSL commands you have seen in the editor chapter:

def s = shape(
    type: "circle",                          // parameter "type", value "circle"
    style: [fillColor: "tomato"],            // a map inside a map!
    transform: [scale: 0.5, shift: [1, 0]],
)

The nice thing about maps: parameters have names, so the code documents itself, order doesn't matter, and you only write the ones you need. JMathAnim will set reasonable default values for the omitted ones.

Let the editor do it: you don't need to memorize any DSL parameter. Press Alt+S (or browse the snippets sidebar) to insert a ready-made DSL block, and press Ctrl+Space inside the block to see and add the available parameters. You can also autocomplete values for certain parameters (for example, it will show the available options in the type parameter of the DSL shape block). The DSL cheatsheet lists everything in one place.

Operator shortcuts

JMathAnim teaches Groovy a few extra tricks so that common operations read like plain math. None of these are required (there is always a longhand method that does the same), but they make scripts shorter and clearer. Here is the whole toolbox.

Lists as vectors. Anywhere a Vec is expected you can write a plain list of 2 or 3 numbers, and you can convert one explicitly with as Vec:

def v = [1, 2] as Vec        // same as Vec.to(1, 2)
def w = [1, 2, 3] as Vec     // 3D vector (yes, someday, maybe, a 3D version of JMathAnim will raise...)

Vector arithmetic. Vec objects (and lists standing in for them) understand the usual operators. Multiplication and division by a Vec act component by component:

def a = Vec.to(1, 2)
def b = a + [1, 0]     // add a list as if it were a vector -> (2, 2)
def c = a - b          // vector difference, the same as b.to(a)
def d = 3 * a          // scale by a number (a * 3 also works) -> (3, 6)
def e = a / 2          // -> (0.5, 1)
def f = -a             // opposite vector -> (-1, -2)
def g = a * b          // component-wise product (1*2,2*2)
def x = a[0]           // read a component: a[0]=a.x, a[1]=a.y, a[2]=a.z
def (x,y)=a           // equivalent to def x=a.x and def y=a.y
def list = a as List   // turn it back into a list [x, y, z]

Moving and scaling objects with +, -, *, /. Applied to a MathObject, these return a transformed copy and leave the original untouched (unlike .shift() / .scale(), which modify the object in place):

def v=Vec.to(1,.5)
def c = Shape.circle()
def right = c + v   // a copy shifted 1 units to the right and .5 units up; c is unchanged
def left  = c - [2, 0]   // a copy shifted to the left
def big   = c * 2        // a copy scaled by 2 (2 * c works too)
def small = c / 2        // a copy scaled by 1/2

Adding to the scene or a group with <<. The << operator is a compact add. It accepts a single object or a list, and returns the container so you can chain:

scene << circle                 // same as scene.add(circle)
scene << [circle, square, dot]  // add several at once, same as scene.add(circle, square, dot)
group << triangle               // add to a MathObjectGroup, same as group.add(triangle)

Indexing with []. Square brackets reach inside compound objects. On a group they return the n-th element; on a shape they return its n-th path point (and a range returns a list of them):

def first = group[0]        // the first element of a MathObjectGroup, same as group.get(0)
def p = shape[2]            // the third JMPathPoint of a Shape, same as shape.get(2)
def some = shape[1..3]      // a list of JMPathPoints 1, 2 and 3

Converting to a Shape with as Shape. Turns a shape-like object into a plain Shape; on a multi-shape object (like a LatexMathObject) it merges every subpath into a single connected Shape:

def s = someShape as Shape          // as a plain Shape
def merged = formula as Shape       // all glyphs merged into one path

Repeating things. Loops.

Half the fun of animating with code is doing something 50 times without copy-pasting 50 lines. Two idioms cover almost every need.

"Do this N times":

30.times {
    def sq = Shape.square()
        .moveTo(Vec.random())
        .fillColor("random")
        .scale(0.2)
    scene.add(sq)
}

Everything between { and } is repeated 30 times. Inside the braces there is a free bonus variable called it that counts the repetitions, starting at 0:

5.times {
    scene.add(Shape.regularPolygon(3 + it).shift(1.2 * it, 0))
    // it = 0, 1, 2, 3, 4, triangle, square, pentagon, hexagon, heptagon
}
camera.adjustToAllObjects()//This ensures everything is visible on screen

"For each n from a to b": when you want the counter to have a proper name, or to iterate over a range:

for (n in 3..8) {
    scene.add(
        Shape.regularPolygon(n)
    .fillColor("random")
    .fillAlpha(.3) //30% opacity in fill
    )
}
camera.adjustToAllObjects()

Passing functions as parameters

Sometimes JMathAnim asks you for a function, for example to plot a graph. You write it between braces, with the variables, an arrow, and the formula:

def fg = FunctionGraph.make({ x -> Math.sin(2*x) }, -2, 2)   // f(x) = sin(2x)
def cp = ContourPlot.make({ x, y -> x*x + y*y }, 0.5, 1, 2)  // f(x,y) = x²+y²

Read { x -> Math.sin(2*x) } as "the function that takes x and returns sin(2x)". That's what is called a closure in Groovy: a formula you can hand over as an ingredient.

When things go wrong (they will, and it's fine, breath!)

Programming is 20% writing and 80% wondering why line 7 is angry at you. Typical beginner tripwires:

  • Case matters: fillcolor is not fillColor. Methods and class names uses the camel case convention, capitalizing the first letter of a word. For example, LatexMathObject, MathObjectGroup, or methods .setLayout or .adjustToAllObjects(). Note that the first word of a method is not capitalized. Anyway, when in doubt, Ctrl+Space autocompletes the correct spelling. It is a good practice to use camel case in your variable names to avoid confusions.
  • Every ( needs its ), every " its closing ", every { its }. The editor highlights matching pairs to help you count.
  • Using a variable before creating it: the recipe is read top to bottom, so def your circle before animating it.

When a script fails, look at the log window below the preview: the error message includes the line number, and during execution the editor highlights the line being run in green, which makes it easy to see how far the script got. In this example we mispelled sq and tried to animate sr instead, which is an undefined variable.

editorError

Cheat table: let the editor type for you

You want to... The editor way
Create an object or animation without remembering syntax Snippets: Alt+S or the sidebar
See which parameters a DSL block accepts Ctrl+Space inside the block
Pick a color visually Tools → Color Editor... (Ctrl+Shift+K)
Write/fix a LaTeX formula with live preview Tools → LaTeX Editor... (Ctrl+Shift+L)
Color parts of a formula by clicking on them Tools → LaTeX Style Editor... (Ctrl+Shift+E)
Animate one formula into another by clicking glyph pairs Tools → Transform LaTeX... (Ctrl+Shift+T)
Run a quick preview F5
Render the final HD video Shift+F5
Open the folder with the generated video Ctrl+Shift+M

And that's the whole survival kit. With variables, dots, loops and Alt+S you can already follow every chapter of this manual. Whenever you meet a strange symbol in an example, come back here; it's almost certainly one of the friends above.

home back