
# #ifdef PRIMER_BOOK
Implementing a drawing program provides a very good example on the
usefulness of object-oriented programming. In the following we shall
develop the simpler parts of a relatively small and compact drawing
program for making sketches of the type shown in Figure
ref{sketcher:fig:inclinedplane}.  This is a typical *principal sketch*
of a physics problem, here involving a rolling wheel on an inclined
plane. The sketch
# #else

======= A First Glimpse of Pysketcher =======

Formulation of physical problems makes heavy use of *principal sketches*
such as the one in Figure ref{sketcher:fig:inclinedplane}.
This particular sketch illustrates the classical mechanics problem
of a rolling wheel on an inclined plane.
The figure
# #endif
is made up many individual elements: a rectangle
filled with a pattern (the inclined plane), a hollow circle with color
(the wheel), arrows with labels (the $N$ and $Mg$ forces, and the $x$
axis), an angle with symbol $\theta$, and a dashed line indicating the
starting location of the wheel.

Drawing software and plotting programs can produce such figures quite
easily in principle, but the amount of details the user needs to
control with the mouse can be substantial. Software more tailored to
producing sketches of this type would work with more convenient
abstractions, such as circle, wall, angle, force arrow, axis, and so
forth. And as soon we start *programming* to construct the figure we
get a range of other powerful tools at disposal. For example, we can
easily translate and rotate parts of the figure and make an animation
that illustrates the physics of the problem.
Programming as a superior alternative to interactive drawing is
the mantra of this section.

FIGURE: [figs-sketcher/wheel_on_inclined_plane.png, width=600] Sketch of a physics problem. label{sketcher:fig:inclinedplane}

# #ifdef PRIMER_BOOK
Classes are very suitable for implementing the various components that
build up a sketch. In particular, we shall demonstrate that as soon as
some classes are established, more are easily added. Enhanced
functionality for all the classes is also easy to implement in common,
generic code that can immediately be shared by all present and future
classes.

The fundamental data structure involved in this case study is a
hierarchical tree, and much of the material on implementation issues
targets how to traverse tree structures with recursive function calls.
This topic is of key relevance in a wide range of other applications
as well.

===== Using the Object Collection =====

We start by demonstrating a convenient user interface for making
sketches of the type in Figure ref{sketcher:fig:inclinedplane}.
However, it is more appropriate to start with a significantly simpler
example as depicted in Figure ref{sketcher:fig:vehicle0}.  This toy
sketch consists of several elements: two circles, two rectangles, and
a "ground" element.

# #else

===== Basic Construction of Sketches =====

Before attacking real-life sketches as in Figure ref{sketcher:fig1}
we focus on the significantly simpler drawing shown in
in Figure ref{sketcher:fig:vehicle0}.  This toy sketch consists of
several elements: two circles, two rectangles, and a "ground" element.

# #endif

FIGURE: [figs-sketcher/vehicle0_dim.png, width=600] Sketch of a simple figure. label{sketcher:fig:vehicle0}

=== Basic Drawing ===

A typical program creating these five elements is shown next.
After importing the `pysketcher` package, the first task is always to
define a coordinate system:
!bc pycod
from pysketcher import *

drawing_tool.set_coordinate_system(
    xmin=0, xmax=10, ymin=-1, ymax=8)
!ec
Instead of working with lengths expressed by specific numbers it is
highly recommended to use variables to parameterize lengths as
this makes it easier to change dimensions later.
Here we introduce some key lengths for the radius of the wheels,
distance between the wheels, etc.:
!bc pycod
R = 1    # radius of wheel
L = 4    # distance between wheels
H = 2    # height of vehicle body
w_1 = 5  # position of front wheel
drawing_tool.set_coordinate_system(xmin=0, xmax=w_1 + 2*L + 3*R,
                                   ymin=-1, ymax=2*R + 3*H)
!ec
With the drawing area in place we can make the first `Circle` object
in an intuitive fashion:
!bc pycod
wheel1 = Circle(center=(w_1, R), radius=R)
!ec
to change dimensions later.

To translate the geometric information about the `wheel1` object to
instructions for the plotting engine (in this case Matplotlib), one calls the
`wheel1.draw()`. To display all drawn objects, one issues
`drawing_tool.display()`. The typical steps are hence:
!bc pycod
wheel1 = Circle(center=(w_1, R), radius=R)
wheel1.draw()

# Define other objects and call their draw() methods
drawing_tool.display()
drawing_tool.savefig('tmp.png')  # store picture
!ec

The next wheel can be made by taking a copy of `wheel1` and
translating the object to the right according to a
displacement vector $(L,0)$:
!bc pycod
wheel2 = wheel1.copy()
wheel2.translate((L,0))
!ec

The two rectangles are also made in an intuitive way:
!bc pycod
under = Rectangle(lower_left_corner=(w_1-2*R, 2*R),
                  width=2*R + L + 2*R, height=H)
over  = Rectangle(lower_left_corner=(w_1, 2*R + H),
                  width=2.5*R, height=1.25*H)
!ec

=== Groups of Objects ===

Instead of calling the `draw` method of every object, we can
group objects and call `draw`, or perform other operations, for
the whole group. For example, we may collect the two wheels
in a `wheels` group and the `over` and `under` rectangles
in a `body` group. The whole vehicle is a composition
of its `wheels` and `body` groups. The code goes like
!bc pycod
wheels  = Composition({'wheel1': wheel1, 'wheel2': wheel2})
body    = Composition({'under': under, 'over': over})

vehicle = Composition({'wheels': wheels, 'body': body})
!ec

The ground is illustrated by an object of type `Wall`,
mostly used to indicate walls in sketches of mechanical systems.
A `Wall` takes the `x` and `y` coordinates of some curve,
and a `thickness` parameter, and creates a "thick" curve filled
with a simple pattern. In this case the curve is just a flat
line so the construction is made of two points on the
ground line ($(w_1-L,0)$ and $(w_1+3L,0)$):
!bc pycod
ground = Wall(x=[w_1 - L, w_1 + 3*L], y=[0, 0], thickness=-0.3*R)
!ec
The negative thickness makes the pattern-filled rectangle appear below
the defined line, otherwise it appears above.

We may now collect all the objects in a "top" object that contains
the whole figure:
!bc pycod
fig = Composition({'vehicle': vehicle, 'ground': ground})
fig.draw()  # send all figures to plotting backend
drawing_tool.display()
drawing_tool.savefig('tmp.png')
!ec
The `fig.draw()` call will visit
all subgroups, their subgroups,
and so forth in the hierarchical tree structure of
figure elements,
and call `draw` for every object.

=== Changing Line Styles and Colors ===

Controlling the line style, line color, and line width is
fundamental when designing figures. The `pysketcher`
package allows the user to control such properties in
single objects, but also set global properties that are
used if the object has no particular specification of
the properties. Setting the global properties are done like
!bc pycod
drawing_tool.set_linestyle('dashed')
drawing_tool.set_linecolor('black')
drawing_tool.set_linewidth(4)
!ec
At the object level the properties are specified in a similar
way:
!bc pycod
wheels.set_linestyle('solid')
wheels.set_linecolor('red')
!ec
and so on.

Geometric figures can be specified as *filled*, either with a color or with a
special visual pattern:
!bc pycod
# Set filling of all curves
drawing_tool.set_filled_curves(color='blue', pattern='/')

# Turn off filling of all curves
drawing_tool.set_filled_curves(False)

# Fill the wheel with red color
wheel1.set_filled_curves('red')
!ec

# http://packages.python.org/ete2/ for visualizing tree structures!

=== The Figure Composition as an Object Hierarchy ===

The composition of objects making up the figure
is hierarchical, similar to a family, where
each object has a parent and a number of children. Do a
`print fig` to display the relations:
!bc dat
ground
    wall
vehicle
    body
        over
            rectangle
        under
            rectangle
    wheels
        wheel1
            arc
        wheel2
            arc
!ec
The indentation reflects how deep down in the hierarchy (family)
we are.
This output is to be interpreted as follows:

  * `fig` contains two objects, `ground` and `vehicle`
  * `ground` contains an object `wall`
  * `vehicle` contains two objects, `body` and `wheels`
  * `body` contains two objects, `over` and `under`
  * `wheels` contains two objects, `wheel1` and `wheel2`

In this listing there are also objects not defined by the
programmer: `rectangle` and `arc`. These are of type `Curve`
and automatically generated by the classes `Rectangle` and `Circle`.

More detailed information can be printed by
!bc pycod
print fig.show_hierarchy('std')
!ec
yielding the output
!bc dat
ground (Wall):
    wall (Curve): 4 coords fillcolor='white' fillpattern='/'
vehicle (Composition):
    body (Composition):
        over (Rectangle):
            rectangle (Curve): 5 coords
        under (Rectangle):
            rectangle (Curve): 5 coords
    wheels (Composition):
        wheel1 (Circle):
            arc (Curve): 181 coords
        wheel2 (Circle):
            arc (Curve): 181 coords
!ec
Here we can see the class type for each figure object, how many
coordinates that are involved in basic figures (`Curve` objects), and
special settings of the basic figure (fillcolor, line types, etc.).
For example, `wheel2` is a `Circle` object consisting of an `arc`,
which is a `Curve` object consisting of 181 coordinates (the
points needed to draw a smooth circle). The `Curve` objects are the
only objects that really holds specific coordinates to be drawn.
The other object types are just compositions used to group
parts of the complete figure.

One can also get a graphical overview of the hierarchy of figure objects
that build up a particular figure `fig`.
Just call `fig.graphviz_dot('fig')` to produce a file `fig.dot` in
the *dot format*. This file contains relations between parent and
child objects in the figure and can be turned into an image,
as in Figure ref{sketcher:fig:vehicle0:hier1}, by
running the `dot` program:
!bc sys
Terminal> dot -Tpng -o fig.png fig.dot
!ec

FIGURE: [figs-sketcher/vehicle0_hier1.png, width=500] Hierarchical relation between figure objects. label{sketcher:fig:vehicle0:hier1}


The call `fig.graphviz_dot('fig', classname=True)` makes a `fig.dot` file
where the class type of each object is also visible, see
Figure ref{sketcher:fig:vehicle0:hier2}. The ability to write out the
object hierarchy or view it graphically can be of great help when
working with complex figures that involve layers of subfigures.

FIGURE: [figs-sketcher/vehicle0_hier2.png, width=500] Hierarchical relation between figure objects, including their class names. label{sketcher:fig:vehicle0:hier2}

Any of the objects can in the program be reached through their names, e.g.,
!bc pycod
fig['vehicle']
fig['vehicle']['wheels']
fig['vehicle']['wheels']['wheel2']
fig['vehicle']['wheels']['wheel2']['arc']
fig['vehicle']['wheels']['wheel2']['arc'].x  # x coords
fig['vehicle']['wheels']['wheel2']['arc'].y  # y coords
fig['vehicle']['wheels']['wheel2']['arc'].linestyle
fig['vehicle']['wheels']['wheel2']['arc'].linetype
!ec
Grabbing a part of the figure this way is handy for
changing properties of that part, for example, colors, line styles
(see Figure ref{sketcher:fig:vehicle0:v2}):
!bc pycod
fig['vehicle']['wheels'].set_filled_curves('blue')
fig['vehicle']['wheels'].set_linewidth(6)
fig['vehicle']['wheels'].set_linecolor('black')

fig['vehicle']['body']['under'].set_filled_curves('red')

fig['vehicle']['body']['over'].set_filled_curves(pattern='/')
fig['vehicle']['body']['over'].set_linewidth(14)
fig['vehicle']['body']['over']['rectangle'].linewidth = 4
!ec
The last line accesses the `Curve` object directly, while the line above,
accesses the `Rectangle` object which will then set the linewidth of
its `Curve` object, and other objects if it had any.
The result of the actions above is shown in Figure ref{sketcher:fig:vehicle0:v2}.

FIGURE: [figs-sketcher/vehicle0.png, width=700] Left: Basic line-based drawing. Right: Thicker lines and filled parts. label{sketcher:fig:vehicle0:v2}

We can also change position of parts of the figure and thereby make
animations, as shown next.

=== Animation: Translating the Vehicle ===

Can we make our little vehicle roll? A first attempt will be to
fake rolling by just displacing all parts of the vehicle.
The relevant parts constitute the `fig['vehicle']` object.
This part of the figure can be translated, rotated, and scaled.
A translation along the ground means a translation in $x$ direction,
say a length $L$ to the right:
!bc pycod
fig['vehicle'].translate((L,0))
!ec
You need to erase, draw, and display to see the movement:
!bc pycod
drawing_tool.erase()
fig.draw()
drawing_tool.display()
!ec
Without erasing, the old drawing of the vehicle will remain in
the figure so you get two vehicles. Without `fig.draw()` the
new coordinates of the vehicle will not be communicated to
the drawing tool, and without calling display the updated
drawing will not be visible.

A figure that moves in time is conveniently realized by the
function `animate`:
!bc pycod
animate(fig, tp, action)
!ec
Here, `fig` is the entire figure, `tp` is an array of
time points, and `action` is a user-specified function that changes
`fig` at a specific time point. Typically, `action` will move
parts of `fig`.

In the present case we can define the movement through a velocity
function `v(t)` and displace the figure `v(t)*dt` for small time
intervals `dt`. A possible velocity function is
!bc pycod
def v(t):
    return -8*R*t*(1 - t/(2*R))

!ec
Our action function for horizontal displacements `v(t)*dt` becomes
!bc pycod
def move(t, fig):
    x_displacement = dt*v(t)
    fig['vehicle'].translate((x_displacement, 0))
!ec
Since our velocity is negative for $t\in [0,2R]$ the displacement is
to the left.

The `animate` function will for each time point `t` in `tp` erase
the drawing, call `action(t, fig)`, and show the new figure by
`fig.draw()` and `drawing_tool.display()`.
Here we choose a resolution of the animation corresponding to
25 time points in the time interval $[0,2R]$:
!bc pycod
import numpy
tp = numpy.linspace(0, 2*R, 25)
dt = tp[1] - tp[0]  # time step

animate(fig, tp, move, pause_per_frame=0.2)
!ec
The `pause_per_frame` adds a pause, here 0.2 seconds, between
each frame in the animation.

We can also ask `animate` to store each frame in a file:
!bc pycod
files = animate(fig, tp, move_vehicle, moviefiles=True,
                pause_per_frame=0.2)
!ec
The `files` variable, here `'tmp_frame*.png'`,
is a string expressing the family of frame files.
This string can be used directly in commands for making a movie
out of the files.
A simple approach is to generate an animated GIF file with help of
the `convert` program from the ImageMagick software suite:
!bc sys
Terminal> convert -delay 20 tmp_frame*.png anim.gif
Terminal> animate anim.gif  # play movie
!ec
The delay between frames governs the speed of the movie.
The `anim.gif` file can be embedded in a web page and shown as
a movie when the page is loaded into a web browser (just insert
`<img src="anim.gif">` in the HTML code).

The tool `ffmpeg` can alternatively be used to create an MPEG movie, e.g.,
!bc sys
Terminal> ffmpeg -i "tmp_frame_%04d.png" -b 800k -r 25 \
          -vcodec mpeg4 -y -qmin 2 -qmax 31 anim.mpeg
!ec
An easy-to-use Python interface to movie-making tools is provided by the
SciTools package:
!bc pycod
from scitools.std import movie

# HTML page showing individual frames
movie(files, encoder='html', output_file='anim.html')

# Standard GIF file
movie(files, encoder='convert', output_file='anim.gif')

# AVI format
movie('tmp_*.png', encoder='ffmpeg', fps=4,
      output_file='anim.avi') # requires ffmpeg package

# MPEG format
movie('tmp_*.png', encoder='ffmpeg', fps=4,
      output_file='anim2.mpeg', vodec='mpeg2video')
# or
movie(files, encoder='ppmtompeg', fps=24,
      output_file='anim.mpeg')  # requires the netpbm package
!ec
When difficulties with encoders and players arise, the simple
web page for showing a movie, here `anim.html` (generated by the
first `movie` command above), is a safe method that you always
can rely on.
You can try loading `anim.html` into a web browser, after first
having run the present example in the file
# #ifdef PRIMER_BOOK
`vehicle0.py`.
# #else
"`vehicle0.py`": "http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle0.py". Alternatively, you can view a ready-made "movie": "http://hplgit.github.com/pysketcher/doc/src/sketcher/movies-sketcher/anim.html_vehicle0/anim.html".
# #endif

=== Animation: Rolling the Wheels ===
label{sketcher:vehicle1:anim}

It is time to show rolling wheels. To this end, we add spokes to the
wheels, formed by two crossing lines, see Figure ref{sketcher:fig:vehicle1}.
The construction of the wheels will now involve a circle and two lines:
!bc pycod
wheel1 = Composition({
    'wheel': Circle(center=(w_1, R), radius=R),
    'cross': Composition({'cross1': Line((w_1,0),   (w_1,2*R)),
                          'cross2': Line((w_1-R,R), (w_1+R,R))})})
wheel2 = wheel1.copy()
wheel2.translate((L,0))
!ec
Observe that `wheel1.copy()` copies all the objects that make
up the first wheel, and `wheel2.translate` translates all
the copied objects.

FIGURE: [figs-sketcher/vehicle1.png, width=400] Wheels with spokes to illustrate rolling. label{sketcher:fig:vehicle1}

The `move` function now needs to displace all the objects in the
entire vehicle and also rotate the `cross1` and `cross2`
objects in both wheels.
The rotation angle follows from the fact that the arc length
of a rolling wheel equals the displacement of the center of
the wheel, leading to a rotation angle
!bc pycod
angle = - x_displacement/R
!ec
With `w_1` tracking the $x$ coordinate of the center
of the front wheel, we can rotate that wheel by
!bc pycod
w1 = fig['vehicle']['wheels']['wheel1']
from math import degrees
w1.rotate(degrees(angle), center=(w_1, R))
!ec
The `rotate` function takes two parameters: the rotation angle
(in degrees) and the center point of the rotation, which is the
center of the wheel in this case. The other wheel is rotated by
!bc pycod
w2 = fig['vehicle']['wheels']['wheel2']
w2.rotate(degrees(angle), center=(w_1 + L, R))
!ec
That is, the angle is the same, but the rotation point is different.
The update of the center point is done by `w_1 += x_displacement`.
The complete `move` function with translation of the entire
vehicle and rotation of the wheels then becomes
!bc pycod
w_1 = w_1 + L   # start position

def move(t, fig):
    x_displacement = dt*v(t)
    fig['vehicle'].translate((x_displacement, 0))

    # Rotate wheels
    global w_1
    w_1 += x_displacement
    # R*angle = -x_displacement
    angle = - x_displacement/R
    w1 = fig['vehicle']['wheels']['wheel1']
    w1.rotate(degrees(angle), center=(w_1, R))
    w2 = fig['vehicle']['wheels']['wheel2']
    w2.rotate(degrees(angle), center=(w_1 + L, R))
!ec
The complete example is found in the file
# #ifdef PRIMER_BOOK
`vehicle1.py`.
# #else
"`vehicle1.py`": "http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle1.py". You may run this file or watch a "ready-made movie": "http://hplgit.github.com/pysketcher/doc/src/sketcher/movies-sketcher/anim.html_vehicle1/anim.html".
# #endif

The advantages with making figures this way, through programming
rather than using interactive drawing programs, are numerous.  For
example, the objects are parameterized by variables so that various
dimensions can easily be changed.  Subparts of the figure, possible
involving a lot of figure objects, can change color, linetype, filling
or other properties through a *single* function call.  Subparts of the
figure can be rotated, translated, or scaled.  Subparts of the figure
can also be copied and moved to other parts of the drawing
area. However, the single most important feature is probably the
ability to make animations governed by mathematical formulas or data
coming from physics simulations of the problem, as very simplistically
shown in the example above.


