Hans Petter Langtangen 13 年之前
父節點
當前提交
b94a5961ca

+ 112 - 88
doc/src/sketcher/basics.do.txt

@@ -6,7 +6,7 @@ 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. This sketch
+plane. The sketch
 # #else
 
 ======= A First Glimpse of Pysketcher =======
@@ -19,34 +19,37 @@ 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 label (the $N$ and $Mg$ forces, and the $x$
+(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.
+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 and their functionality. 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 in object hierarchies. This
-topic is of key relevance in a wide range of other applications as
-well. In total, the drawing program to be presented constitutes an
-excellent example on the power of class programming.
+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 =====
 
@@ -74,24 +77,31 @@ FIGURE: [figs-sketcher/vehicle0_dim.png, width=600] Sketch of a simple figure. l
 
 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. Some graphics operations are done with
-a helper object called `drawing_tool` (imported from `pysketcher`).
-With the drawing area in place we can make the first `Circle` object
-in an intuitive fashion:
+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
-By using symbols for characteristic lengths in the drawing, rather than
-absolute lengths, it is easier
 to change dimensions later.
 
 To translate the geometric information about the `wheel1` object to
@@ -108,13 +118,14 @@ drawing_tool.savefig('tmp.png')  # store picture
 !ec
 
 The next wheel can be made by taking a copy of `wheel1` and
-translating the object a distance (to the right) described by the
-vector $(4,0)$:
+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 made in an intuitive way:
+
+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)
@@ -129,7 +140,7 @@ 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 codes goes like
+of its `wheels` and `body` groups. The code goes like
 !bc pycod
 wheels  = Composition({'wheel1': wheel1, 'wheel2': wheel2})
 body    = Composition({'under': under, 'over': over})
@@ -160,7 +171,8 @@ drawing_tool.savefig('tmp.png')
 !ec
 The `fig.draw()` call will visit
 all subgroups, their subgroups,
-and so in the hierarchical tree structure that we have collected,
+and so forth in the hierarchical tree structure of
+figure elements,
 and call `draw` for every object.
 
 === Changing Line Styles and Colors ===
@@ -179,14 +191,14 @@ drawing_tool.set_linewidth(4)
 At the object level the properties are specified in a similar
 way:
 !bc pycod
-wheel1.set_linestyle('solid')
-wheel1.set_linecolor('red')
+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
+!bc pycod
 # Set filling of all curves
 drawing_tool.set_filled_curves(color='blue', pattern='/')
 
@@ -201,7 +213,8 @@ wheel1.set_filled_curves('red')
 
 === The Figure Composition as an Object Hierarchy ===
 
-The composition of objects is hierarchical, as in a family, where
+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
@@ -229,6 +242,10 @@ This output is to be interpreted as follows:
   * `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')
@@ -292,7 +309,7 @@ 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 very handy for
+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
@@ -333,72 +350,80 @@ drawing_tool.erase()
 fig.draw()
 drawing_tool.display()
 !ec
-Without erasing the old position of the vehicle will remain in
+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.
 
-Let us make a velocity function and move the object according
-to that velocity in small steps of time:
+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))
 
-animate(fig, tp, action)
 !ec
-For small time steps `dt` the corresponding displacement is
-well approximated by `dt*v(t)` (we could integrate the velocity
-to obtain the exact position, but we would anyway need to
-calculate the displacement from time step to time step).
-The `animate` function takes as arguments some figure `fig`, a set of
-time points `tp`, and a user function `action`,
-and then a new figure is drawn for each time point and the user
-can through the provided `action` function modify desired parts
-of the figure. Here the `action` function will move the vehicle:
+Our action function for horizontal displacements `v(t)*dt` becomes
 !bc pycod
-def move_vehicle(t, fig):
+def move(t, fig):
     x_displacement = dt*v(t)
     fig['vehicle'].translate((x_displacement, 0))
 !ec
-Defining a set of time points for the frames in the animation
-and performing the animation is done by
+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_vehicle, pause_per_frame=0.2)
+animate(fig, tp, move, pause_per_frame=0.2)
 !ec
 The `pause_per_frame` adds a pause, here 0.2 seconds, between
-each frame.
+each frame in the animation.
 
-We can also make a movie file of 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 holds a string with the family of
-files constituting the frames in the movie, here
-`'tmp_frame*.png'`. Making a movie out of the individual
-frames can be done in many ways.
-A simple approach is to make an animated GIF file with help of
-`convert`, a program in the ImageMagick software suite:
+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 the page is loaded into a web browser (just insert
-`<img src="anim.gif">` in the HTML code to play the GIF animation).
+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, e.g.,
+The tool `ffmpeg` can alternatively be used to create an MPGE 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 interface to movie-making tools is provided by the
+An easy-to-use Python interface to movie-making tools is provided by the
 SciTools package:
 !bc pycod
 from scitools.std import movie
@@ -435,11 +460,9 @@ having run the present example in the file
 === Animation: Rolling the Wheels ===
 label{sketcher:vehicle1:anim}
 
-It is time to show rolling wheels. To this end, we make somewhat
-more complicated wheels with spokes as on a bicyle, formed by
-two crossing lines, see Figure ref{sketcher:fig:vehicle1}.
-The construction of the wheels will now involve a circle
-and two lines:
+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),
@@ -452,9 +475,9 @@ 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 show rotation. label{sketcher:fig:vehicle1}
+FIGURE: [figs-sketcher/vehicle1.png, width=400] Wheels with spokes to illustrate rolling. label{sketcher:fig:vehicle1}
 
-The `move_vehicle` function now needs to displace all the objects in the
+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
@@ -478,12 +501,13 @@ 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 += displacement[0]`.
-The complete `move_vehicle` function then becomes
+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 weels then becomes
 !bc pycod
 w_1 = w_1 + L   # start position
 
-def move_vehicle(t, fig):
+def move(t, fig):
     x_displacement = dt*v(t)
     fig['vehicle'].translate((x_displacement, 0))
 
@@ -504,17 +528,17 @@ The complete example is found in the file
 "`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,
+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 sketched in
-the drawing, as very simplistically shown in the example above.
+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.
 
 

+ 80 - 120
doc/src/sketcher/implementation.do.txt

@@ -35,7 +35,8 @@ This will be shown later.
 
 === Simple Geometric Objects ===
 
-One simple subclass is `Rectangle`:
+One simple subclass is `Rectangle`, specified by the coordinates of
+the lower left corner and its width and height:
 !bc pycod
 class Rectangle(Shape):
     def __init__(self, lower_left_corner, width, height):
@@ -46,23 +47,18 @@ class Rectangle(Shape):
              p[1] + height, p[1]]
         self.shapes = {'rectangle': Curve(x,y)}
 !ec
+
 Any subclass of `Shape` will have a constructor which takes
 geometric information about the shape of the object and
 creates a dictionary `self.shapes` with the shape built of
 simpler shapes. The most fundamental shape is `Curve`, which is
 just a collection of $(x,y)$ coordinates in two arrays `x` and `y`.
 Drawing the `Curve` object is a matter of plotting `y` versus `x`.
+For class `Rectangle` the `x` and `y` arrays contain the corner points
+of the rectangle in counterclockwise direction, starting and ending
+with in the lower left corner.
 
-The `Rectangle` class illustrates how the constructor takes information
-about the lower left corner, the width and the height, and
-creates coordinate arrays `x` and `y` consisting of the four corners,
-plus the first one repeated such that plotting `x` and `y` will
-form a closed four-sided rectangle. This construction procedure
-demands that the rectangle will always be aligned with the $x$ and
-$y$ axis. However, we may easily rotate the rectangle about
-any point once the object is constructed.
-
-Class `Line` constitutes a similar example:
+Class `Line` is also a simple class:
 !bc pycod
 class Line(Shape):
     def __init__(self, start, end):
@@ -71,7 +67,7 @@ class Line(Shape):
         self.shapes = {'line': Curve(x, y)}
 !ec
 Here we only need two points, the start and end point on the line.
-However, we may add some useful functionality, e.g., the ability
+However, we may want to add some useful functionality, e.g., the ability
 to give an $x$ coordinate and have the class calculate the
 corresponding $y$ coordinate:
 !bc pycod
@@ -82,12 +78,12 @@ corresponding $y$ coordinate:
         self.b = y[0] - self.a*x[0]
         return self.a*x + self.b
 !ec
-Unfortunately, this is too simplistic because vertical lines cannot
-be handled (infinite `self.a`). The source code of `Line` therefore
-provides a more general solution at the cost of significantly
-longer code with more tests.
+Unfortunately, this is too simplistic because vertical lines cannot be
+handled (infinite `self.a`). The true source code of `Line` therefore
+provides a more general solution at the cost of significantly longer
+code with more tests.
 
-A circle gives us somewhat increased complexity. Again we represent
+A circle implies a somewhat increased complexity. Again we represent
 the geometric object by a `Curve` object, but this time the `Curve`
 object needs to store a large number of points on the curve such
 that a plotting program produces a visually smooth curve.
@@ -102,9 +98,9 @@ y &= y_0 + R\sin (t),
 !et
 where $t\in [0, 2\pi]$. A discrete set of $t$ values in this
 interval gives the corresponding set of $(x,y)$ coordinates on
-the circle. The user must specify the resolution, i.e., the number
-of $t$ values, or equivalently, points on the circle. The circle's
-radius and center must of course also be specified.
+the circle. The user must specify the resolution as the number
+of $t$ values. The circle's radius and center must of course
+also be specified.
 
 We can write the `Circle` class as
 !bc pycod
@@ -134,19 +130,16 @@ a translation, scaling, or rotation of the circle.
 
 A part of a circle, an arc, is a frequent geometric object when
 drawing mechanical systems. The arc is constructed much like
-a circle, but $t$ runs in $[\theta_0, \theta_1]$. Giving
-$\theta_1$ and $\theta_2$ the slightly more descriptive names
+a circle, but $t$ runs in $[\theta_s, \theta_s + \theta_a]$. Giving
+$\theta_s$ and $\theta_a$ the slightly more descriptive names
 `start_angle` and `arc_angle`, the code looks like this:
 !bc pycod
 class Arc(Shape):
     def __init__(self, center, radius,
                  start_angle, arc_angle,
                  resolution=180):
-        self.center = center
-        self.radius = radius
-        self.start_angle = start_angle*pi/180  # radians
-        self.arc_angle = arc_angle*pi/180
-        self.resolution = resolution
+        self.start_angle = radians(start_angle)
+        self.arc_angle = radians(arc_angle)
 
         t = linspace(self.start_angle,
                      self.start_angle + self.arc_angle,
@@ -158,7 +151,7 @@ class Arc(Shape):
         self.shapes = {'arc': Curve(x, y)}
 !ec
 
-Having the `Arc` class, a `Circle` can alternatively befined as
+Having the `Arc` class, a `Circle` can alternatively be defined as
 a subclass specializing the arc to a circle:
 !bc pycod
 class Circle(Arc):
@@ -166,48 +159,24 @@ class Circle(Arc):
         Arc.__init__(self, center, radius, 0, 360, resolution)
 !ec
 
-A wall is about drawing a curve, displacing the curve vertically by
-some thickness, and then filling the space between the curves
-by some pattern. The input is the `x` and `y` coordinate arrays
-of the curve and a thickness parameter. The computed coordinates
-will be a polygon: going along the originally curve and then back again
-along the vertically displaced curve. The relevant code becomes
-!bc pycod
-class CurveWall(Shape):
-    def __init__(self, x, y, thickness):
-        # User's curve
-        x1 = asarray(x, float)
-        y1 = asarray(y, float)
-        # Displaced curve (according to thickness)
-        x2 = x1
-        y2 = y1 + thickness
-        # Combine x1,y1 with x2,y2 reversed
-        from numpy import concatenate
-        x = concatenate((x1, x2[-1::-1]))
-        y = concatenate((y1, y2[-1::-1]))
-        wall = Curve(x, y)
-        wall.set_filled_curves(color='white', pattern='/')
-        self.shapes = {'wall': wall}
-!ec
 
 === Class Curve ===
 
-Class `Curve` sits on the coordinates to be drawn, but how is
-that done? The constructor just stores the coordinates, while
-a method `draw` sends the coordinates to the plotting program
-to make a graph.
-Or more precisely, to avoid a lot of (e.g.) Matplotlib-specific
-plotting commands we have created a small layer with a
-simple programming interface to plotting programs. This makes it
-straightforward to change from Matplotlib to another plotting
-program. The programming interface is represented by the `drawing_tool`
-object and has a few functions:
+Class `Curve` sits on the coordinates to be drawn, but how is that
+done? The constructor of class `Curve` just stores the coordinates,
+while a method `draw` sends the coordinates to the plotting program to
+make a graph.  Or more precisely, to avoid a lot of (e.g.)
+Matplotlib-specific plotting commands in class `Curve` we have created
+a small layer with a simple programming interface to plotting
+programs. This makes it straightforward to change from Matplotlib to
+another plotting program. The programming interface is represented by
+the `drawing_tool` object and has a few functions:
 
   * `plot_curve` for sending a curve in terms of $x$ and $y$ coordinates
     to the plotting program,
   * `set_coordinate_system` for specifying the graphics area,
   * `erase` for deleting all elements of the graph,
-  * `set_grid` for turning on a grid (convenient while constructing the plot),
+  * `set_grid` for turning on a grid (convenient while constructing the figure),
   * `set_instruction_file` for creating a separate file with all
     plotting commands (Matplotlib commands in our case),
   * a series of `set_X` functions where `X` is some property like
@@ -217,7 +186,7 @@ This is basically all we need to communicate to a plotting program.
 
 Any class in the `Shape` hierarchy inherits `set_X` functions for
 setting properties of curves. This information is propagated to
-all other shape objects that make up the figure. Class
+all other shape objects in the `self.shapes` dictionary. Class
 `Curve` stores the line properties together with the coordinates
 of its curve and propagates this information to the plotting program.
 When saying `vehicle.set_linewidth(10)`, all objects that make
@@ -233,18 +202,10 @@ class Curve(Shape):
         self.x = asarray(x, dtype=float)
         self.y = asarray(y, dtype=float)
 
-        self.linestyle = None
-        self.linewidth = None
-        self.linecolor = None
-        self.fillcolor = None
-        self.fillpattern = None
-        self.arrow = None
-
     def draw(self):
         drawing_tool.plot_curve(
             self.x, self.y,
-            self.linestyle, self.linewidth, self.linecolor,
-            self.arrow, self.fillcolor, self.fillpattern)
+            self.linestyle, self.linewidth, self.linecolor, ...)
 
     def set_linewidth(self, width):
         self.linewidth = width
@@ -256,22 +217,24 @@ class Curve(Shape):
 
 === Compound Geometric Objects ===
 
-The simple classes `Line`, `Arc`, and `Circle` could define the geometric
-shape through just one `Curve` object. More complicated figure elements
-are built from instances of various subclasses of `Shape`. Classes used
+The simple classes `Line`, `Arc`, and `Circle` could can the geometric
+shape through just one `Curve` object. More complicated shapes are
+built from instances of various subclasses of `Shape`. Classes used
 for professional drawings soon get quite complex in composition and
-have a lot of geometric details, so here we prefer to make a very simple
-composition: the already drawn vehicle from
-Figure refref{sketcher:fig:vehicle0}.
-That is, instead of composing the drawing in a Python code we make a class
-`Vehicle0` for doing the same thing, and derive it from `Shape`.
+have a lot of geometric details, so here we prefer to make a very
+simple composition: the already drawn vehicle from Figure
+ref{sketcher:fig:vehicle0}.  That is, instead of composing the drawing
+in a Python program as shown above, we make a subclass `Vehicle0` in
+the `Shape` hierarchy for doing the same thing.
 
 The `Shape` hierarchy is found in the `pysketcher` package, so to use these
 classes or derive a new one, we need to import `pysketcher`. The constructor
 of class `Vehicle0` performs approximately the same statements as
 in the example program we developed for making the drawing in
-Figure refref{sketcher:fig:vehicle0}.
+Figure ref{sketcher:fig:vehicle0}.
 !bc pycod
+from pysketcher import *
+
 class Vehicle0(Shape):
     def __init__(self, w_1, R, L, H):
         wheel1 = Circle(center=(w_1, R), radius=R)
@@ -299,7 +262,9 @@ Any subclass of `Shape` *must* define the `shapes` attribute, otherwise
 the inherited `draw` method (and a lot of other methods too) will
 not work.
 
-The painting of the vehicle could be offered by a method:
+The painting of the vehicle, as shown in the right part of
+Figure ref{sketcher:fig:vehicle0:v2}, could in class `Vehicle0`
+be offered by a method:
 !bc pycod
     def colorful(self):
         wheels = self.shapes['vehicle']['wheels']
@@ -314,13 +279,13 @@ The painting of the vehicle could be offered by a method:
 !ec
 
 The usage of the class is simple: after having set up an appropriate
-coordinate system a s previously shown, we can do
+coordinate system as previously shown, we can do
 !bc pycod
 vehicle = Vehicle0(w_1, R, L, H)
 vehicle.draw()
 drawing_tool.display()
 !ec
-The color from Figure ref{sketcher:fig:vehicle0:v2} is realized by
+and go on the make a painted version by
 !bc pycod
 drawing_tool.erase()
 vehicle.colorful()
@@ -338,20 +303,21 @@ The `pysketcher` package contains a wide range of classes for various
 geometrical objects, particularly those that are frequently used in
 drawings of mechanical systems.
 
-======= Adding Functionality via Recursion =======
+===== Adding Functionality via Recursion =====
 
 The really powerful feature of our class hierarchy is that we can add
-much functionality to the superclass `Shape` and to the "bottom" classes
-`Curve`, and all other classes for all types of geometrical shapes
-immediately get the new functionality. To explain the idea we first have
-to look at the `draw` method, which all classes in the `Shape`
+much functionality to the superclass `Shape` and to the "bottom" class
+`Curve`, and then all other classes for various types of geometrical shapes
+immediately get the new functionality. To explain the idea we may
+look at the `draw` method, which all classes in the `Shape`
 hierarchy must have. The inner workings of the `draw` method explain
 the secrets of how a series of other useful operations on figures
 can be implemented.
 
 === Basic Principles of Recursion ===
 
-We work with two types of class hierarchies: one Python class hierarchy,
+Note that we work with two types of hierarchies in the
+present documentation: one Python *class hierarchy*,
 with `Shape` as superclass, and one *object hierarchy* of figure elements
 in a specific figure. A subclass of `Shape` stores its figure in the
 `self.shapes` dictionary. This dictionary represents the object hierarchy
@@ -379,7 +345,7 @@ whose `self.shapes` attributed has two elements: `wheels` and `body`.
 Since class `Composition` inherits the same `draw` method, this method will
 run through `self.shapes` and call `wheels.draw()` and `body.draw()`.
 Now, the `wheels` object is also a `Composition` with the same `draw`
-method, which will run through the `shapes` dictionary, now containing
+method, which will run through `self.shapes`, now containing
 the `wheel1` and and `wheel2` objects. The `wheel1` object is a `Circle`,
 so calling `wheel1.draw()` calls the `draw` method in class `Circle`,
 but this is the same `draw` method as shown above. This method will
@@ -405,9 +371,7 @@ structures like the figure structures we work with here. Even though the
 hierarchy of objects building up a figure are of different types, they
 all inherit the same `draw` method and therefore exhibit the same
 behavior with respect to drawing. Only the `Curve` object has a different
-`draw` method, which does not lead to more recursion. Without this
-different `draw` method in class `Curve`, the repeated `draw` calls would
-go on forever.
+`draw` method, which does not lead to more recursion.
 
 === Explaining Recursion ===
 
@@ -427,12 +391,13 @@ The `recurse` method is very similar to `draw`:
             self.shapes[shape].recurse(indent+2, shape)
 !ec
 The `indent` parameter governs how much the message from this
-`recurse` method is intended. We increase `indent` by 2 for every level
-in the hierarchy. This makes it easy to see on the printout how far
-down in the hierarchy we are.
+`recurse` method is intended. We increase `indent` by 2 for every
+level in the hierarchy, i.e., every row of objects in Figure
+ref{sketcher:fig:Vehicle0:hier2}. This indentation makes it easy to
+see on the printout how far down in the hierarchy we are.
 
-A typical message written by `recurse` when `name` is `body` and
-the `shapes` dictionary contains two entries, `over` and `under`,
+A typical message written by `recurse` when `name` is `'body'` and
+the `shapes` dictionary has the keys `'over'` and `'under'`,
 will be
 !bc dat
      Composition: body.shapes has entries 'over', 'under'
@@ -502,7 +467,6 @@ A recommended next step is to simulate the `recurse` method by hand and
 carefully check that what happens in the visits to `recurse` is
 consistent with the output listed below. Although tedious, this is
 a major exercise that guaranteed will help to demystify recursion.
-Also remember that it requires some efforts to understand recursion.
 
 A part of the printout of `v.recurse('vehicle')` looks like
 !bc dat
@@ -568,9 +532,7 @@ so that multiplication by a scalar number `factor` is
 a vectorized operation.
 
 An even more efficient implementation is
-to make use of in-place multiplication in the arrays, as this saves the creation
-of temporary arrays like `factor*self.x`, which is then assigned to
-`self.x`:
+to make use of in-place multiplication in the arrays,
 !bc cod
 class Curve:
     ...
@@ -578,19 +540,18 @@ class Curve:
         self.x *= factor
         self.y *= factor
 !ec
-
-In an instance of a subclass of `Shape`,
-the meaning of a method `scale` is
-to run through all objects in the dictionary `shapes` and ask
-each object to scale itself. This is the same delegation of actions
-to subclass instances as we do in the `draw` (or `recurse`) method. All
-all objects, except `Curve` instances, can share the same
-implementation of the `scale` method. Therefore, we place
-the `scale` method in the superclass `Shape` such that all
-subclasses inherit the method.
-Since `scale` and `draw` are so similar,
-we can easily implement the `scale` method in class `Shape` by
-copying and editing the `draw` method:
+as this saves the creation of temporary arrays like `factor*self.x`.
+
+In an instance of a subclass of `Shape`, the meaning of a method
+`scale` is to run through all objects in the dictionary `shapes` and
+ask each object to scale itself. This is the same delegation of
+actions to subclass instances as we do in the `draw` (or `recurse`)
+method. All objects, except `Curve` instances, can share the same
+implementation of the `scale` method. Therefore, we place the `scale`
+method in the superclass `Shape` such that all subclasses inherit the
+method.  Since `scale` and `draw` are so similar, we can easily
+implement the `scale` method in class `Shape` by copying and editing
+the `draw` method:
 !bc cod
 class Shape:
     ...
@@ -612,7 +573,7 @@ the $x$ direction and $v_1$ units in the $y$ direction using the formulas
 \begin{equation*}
 x_i\leftarrow x_i+v_0,\quad y_i\leftarrow y_i+v_1,\quad i=0,\ldots,n-1\thinspace . \end{equation*}
 !et
-The natural specification of the translation is in terms of a
+The natural specification of the translation is in terms of the
 vector $v=(v_0,v_1)$.
 The corresponding Python implementation in class `Curve` becomes
 !bc cod
@@ -666,9 +627,8 @@ is given in degrees and not in radians, becomes
         self.x = xnew
         self.y = ynew
 !ec
-The `rotate` method in class `Shape` is identical to the
-`draw`, `scale`, and `translate` methods except that we
-recurse into `self.rotate(angle, center)`.
+The `rotate` method in class `Shape` follows the principle of the
+`draw`, `scale`, and `translate` methods.
 
 We have already seen the `rotate` method in action when animating the
 rolling wheel at the end of Section ref{sketcher:vehicle1:anim}.

+ 2 - 0
doc/src/sketcher/make_primer.sh

@@ -20,6 +20,8 @@ doconce subst '\\noindent\nFilename: \\code\{(.+?)\}' 'Name of program file: \\c
 doconce replace "figs-sketcher/" "figs/" *.p.tex
 doconce replace "wheel_on_inclined_plane" "wheel_on_inclined_plane_cropped" *.p.tex
 
+doconce replace Section Chapter *.p.tex
+
 cp basics.p.tex pysketcher_basics.p.tex
 cp implementation.p.tex pysketcher_impl.p.tex
 cp exercises.p.tex pysketcher_ex.p.tex

+ 2 - 2
doc/src/sketcher/src-sketcher/vehicle0.py

@@ -60,11 +60,11 @@ import numpy
 tp = numpy.linspace(0, 2*R, 25)
 dt = tp[1] - tp[0]  # time step
 
-def move_vehicle(t, fig):
+def move(t, fig):
     x_displacement = dt*v(t)
     fig['vehicle'].translate((x_displacement, 0))
 
-files = animate(fig, tp, move_vehicle, moviefiles=True,
+files = animate(fig, tp, move, moviefiles=True,
                 pause_per_frame=0)
 
 os.system('convert -delay 20 %s anim.gif' % files)

+ 2 - 2
doc/src/sketcher/src-sketcher/vehicle1.py

@@ -51,7 +51,7 @@ import numpy
 tp = numpy.linspace(0, 2*R, 25)
 dt = tp[1] - tp[0]  # time step
 
-def move_vehicle(t, fig):
+def move(t, fig):
     x_displacement = dt*v(t)
     fig['vehicle'].translate((x_displacement, 0))
 
@@ -65,7 +65,7 @@ def move_vehicle(t, fig):
     w2 = fig['vehicle']['wheels']['wheel2']
     w2.rotate(degrees(angle), center=(w_1 + L, R))
 
-files = animate(fig, tp, move_vehicle, moviefiles=True,
+files = animate(fig, tp, move, moviefiles=True,
                 pause_per_frame=0)
 
 from scitools.std import movie