Introduction to JyScript

JyScript home page: download, installation instructions and tutorial

We'll start to make illustrations that respond to our requests for changes.

Drawing a box

PiScript code:

beginpage()

center()
scale(50)

newpath()
box(-1, -1, 2, 2)
setlinewidth(2)
fill(0, 0, 1)
stroke()

endpage()

In JyScript, the drawing commands go into a function.

def draw():
    beginpage()

    center()
    scale(50)

    newpath()
    box(-1, -1, 2, 2)
    setlinewidth(2)
    stroke([1, 0, 0])

    endpage()

Originally, the unit in the coordinate system is one pixel.

Now we ask a new window, which will contain our figure, to pop up by specifying the dimensions of the window and the drawing function.

openframe(200, 200, draw)

Finally, be sure to import JyModule.

from JyModule import *

Here's the whole thing:

from JyModule import *

def draw():
    beginpage()

    center()
    scale(50)

    newpath()
    box(-1, -1, 2, 2)
    setlinewidth(2)
    fill(0, 0, 1)
    stroke()

    endpage()

openframe(200, 200, draw)

If this program is in the file Box.py, it may be run by typing jython Box.py on the command line.

To create other figures, we simply rewrite the draw function.

Notice that there is no init or finish as there is in PiScript.

Moving a box

We'll add a button that moves the box to the right when pressed.

First, we'll add a variable x, which keeps track of where to draw the box.

x = -1
def draw():
    beginpage()

    center()
    scale(50)
    box(x, -1, 2, 2)
    fill(0, 0, 1)
    stroke()

    endpage()

We need to define a function that will be called when the button is pressed. Since we are changing the value of the variable, we need to declare it to be global. Also, we need to call refresh so that our changes appear.

def step():
    global x
    x += 0.1
    refresh()

Finally, we add the button, specifying a string to display on the button and a function to call when pressed.

addbutton("Move", step)

Here is the program in its entirety.

from JyModule import *

x = -1
def draw():
    beginpage()

    center()
    scale(50)
    box(x, -1, 2, 2)
    fill(0, 0, 1)
    stroke()

    endpage()

def step():
    global x
    x += 0.1
    refresh()

addbutton("Move", step)
openframe(200, 200, draw)

It is important to add any buttons before opening the frame.

Adding a reset button

After we press the button a few times, it disappears from our frame. We will therefore add a button to restore the original configuration.

def reset():
    global x
    x = -1
    refresh()
addbutton("Reset", reset)    

The entire program is here:

from JyModule import *

x = -1
def draw():
    beginpage()

    center()
    scale(50)
    box(x, -1, 2, 2)
    fill(0, 0, 1)
    stroke()

    endpage()

def step():
    global x
    x += 0.1
    refresh()
addbutton("Move", step)

def reset():
    global x
    x = -1
    refresh()
addbutton("Reset", reset)    
openframe(200, 200, draw)

Animations

If pressing that button is wearing you out, you may create an animation to move the box.

We do this by creating a timer that calls a function at regular intervals.

def step():
    global x
    x += 0.02
    refresh()

settimer(25, step)

The argument 25 sets the interval, in milliseconds, at which the timer calls the function.

Now we need to add some buttons to control the timer. The functions starttimer() and stoptimer(), well, start and stop the timer.

def begin():
    starttimer()
addbutton("Start", begin)

def pause():
    stoptimer()
addbutton("Stop", pause)

def reset():
    global x
    stoptimer()
    x = -1
    refresh()
addbutton("Reset", reset)    

The whole progam is like this:

from JyModule import *

x = -1
def draw():
    beginpage()

    center()
    scale(50)
    box(x, -1, 2, 2)
    fill(0, 0, 1)
    stroke()

    endpage()

def step():
    global x
    x += 0.02
    refresh()

settimer(25, step)

def begin():
    starttimer()
addbutton("Start", begin)

def pause():
    stoptimer()
addbutton("Stop", pause)

def reset():
    global x
    stoptimer()
    x = -1
    refresh()
addbutton("Reset", reset)    

openframe(200, 200, draw)

The next example shows how to use an animation to fade an element into the figure, a useful technique in, say, illustrating proofs.

First, we'll create a variable that controls the transparency of the blue square.

alpha = 0
def draw():
    beginpage()

    center()
    scale(50)

    setlinewidth(2)
    newpath()
    box(-2, -2, 4, 4)
    fill(1,0,0)
    stroke()

    gsave()
    setcomposite(alpha)
    newpath()
    circle(1, 1, 1.5)
    fill(0, 0, 1)
    stroke()
    grestore()

    endpage()

Now set the timer

def step():
    global alpha
    alpha += 0.01
    if alpha > 1:
        alpha = 1
        stoptimer()
    refresh()

settimer(25, step)

and add the buttons.

def begin():
    starttimer()
def pause():
    stoptimer()
def reset():
    global alpha
    stoptimer()
    alpha = 0
    refresh()

addbutton("Start", begin)
addbutton("Stop", pause)
addbutton("Reset", reset)

All together:

from JyModule import *

alpha = 0
def draw():
    beginpage()

    center()
    scale(50)

    setlinewidth(2)
    newpath()
    box(-2, -2, 4, 4)
    fill(1,0,0)
    stroke()

    gsave()
    setcomposite(alpha)
    newpath()
    circle(1, 1, 1.5)
    fill(0, 0, 1)
    stroke()
    grestore()

    endpage()

def step():
    global alpha
    alpha += 0.01
    if alpha > 1:
        alpha = 1
        stoptimer()
    refresh()

settimer(25, step)

def begin():
    starttimer()
def pause():
    stoptimer()
def reset():
    global alpha
    stoptimer()
    alpha = 0
    refresh()

addbutton("Start", begin)
addbutton("Stop", pause)
addbutton("Reset", reset)

openframe(300, 300, draw)

Moveablepoints

For a final kind of interactivity, we'll add points that may be moved by clicking and dragging.

We do this using a Moveablepoint. First, we create the point by specifying its coordinates and a function to be called when the point is moved. We also register this point using the addmoveable function.

def move(point, x, y):
    point.setpoint(x, -1)
    
movingpoint = Moveablepoint(-1, -1, move)
addmoveable(movingpoint)

We can use the point's coordinates in our draw method. Also, the point is drawn using the placemoveable function. The point's coordinates are interpreted relative to the current coordinate system when placemoveable is called.

def draw():
    beginpage()

    center()
    scale(50)
    newpath()
    box(movingpoint.x, -1, 2, 2)  # <-----
    setlinewidth(2)
    fill(0, 0, 1)
    stroke()

    newpath()
    placemoveable(movingpoint)    # <-----

    endpage()

The whole program looks like:

from JyModule import *

def draw():
    beginpage()

    center()
    scale(50)
    newpath()
    box(movingpoint.x, -1, 2, 2)
    setlinewidth(2)
    fill(0, 0, 1)
    stroke()

    newpath()
    placemoveable(movingpoint)

    endpage()

def move(point, x, y):
    point.setpoint(x, -1)
    
movingpoint = Moveablepoint(-1, -1, move)
addmoveable(movingpoint)

openframe(200, 200, draw)

This is a little more involved so here is a summary:

  • Create a function to be called when the point is moved.
  • Create the point, specifying its coordinates and the function.
  • Register the point with addmoveable
  • Draw the point with placemoveable
  • Use the point's coordinates in the drawing function.

We may have any number of Moveablepoints.

from JyModule import *

def draw():
    beginpage()

    center()
    scale(50)
    newpath()
    dx = upperright.x - lowerleft.x
    dy = upperright.y - lowerleft.y
    box(lowerleft.x, lowerleft.y, dx, dy)
    setlinewidth(2)
    fill(0, 0, 1)
    stroke()

    newpath()
    placemoveable(lowerleft)
    placemoveable(upperright)

    endpage()

def move(point, x, y):
    point.setpoint(x, y)
    
lowerleft = Moveablepoint(-1, -1, move)
upperright = Moveablepoint(1, 1, move)
addmoveable(upperright)
addmoveable(lowerleft)

openframe(200, 200, draw)

Moveablepoints have many attributes that may be set.

setstyle 'circle' or Moveablepoint.CIRCLE, 'square' or Moveablepoint.SQUARE, 'diamond' or Moveablepoint.DIAMOND
setfillcolor a color
setstrokecolor a color
setsize a float
setstrokesize a float
setfilled
True, 
False
setstroked
True, 
False
listentoclicksonly
True, 
False

Another example

from JyModule import *

x = 1
y = 1
def draw():
    beginpage()

    scale(10)

    newpath()
    placemoveable(point)

    newpath()
    string = 'Mouse at (%.1f, %.1f)' % (x, y)
    setfont(30)
    moveto(4, 9)
    show(string)

    endpage()

def move(point, px, py):
    global x, y
    x, y = px, py

point = Moveablepoint(1, 1, move)
point.setsize(400)
point.setfilled(False)
point.setstroked(False)

addmoveable(point)
openframe(400, 200, draw)

Multiple figures

Using these ideas, we may create dynamic illustrations with multiple figures.

from JyScript import *

t = -1
def draw(p):
    ...

def clock(p):
    ...

def step():
    global t
    t += 0.02
    if t > 1:
        t = 1
        stoptimer()
    refresh()

settimer(25, step)

def begin():
    starttimer()
def pause():
    stoptimer()
def reset():
    global t
    t = -1
    stoptimer()
    refresh()

addbutton("Start", begin)
addbutton("Stop", pause)
addbutton("Reset", reset)

openframe({'c': [200, 200, draw], 's': [200, 20, clock]})