.de sz
.br
.ev 1
.ne 10
.ll -4
.in +4
.ls 1
.ce 0
..
.de zs
.br
.ll +4
.in -4
.ev
..
.he 'PICTURES''Page %'
.fo 'Aaron Sloman'- % -'December 1977'
.sp 4
.ce 2
PROCESSING TURTLE PICTURES
========== ====== ========
.mh
Can the Turtle See What It Has Drawn?
--- --- ------ --- ---- -- --- ------
.hm
.pg
Having spent some time playing with the turtle package, you may have come
to the conclusion that the computer was not doing anything really
intelligent. It was merely following your drawing instructions blindly.
In particular, the computer has no way of looking at what it has drawn, to
see whether it is a well-known shape, like a square, or a group of letters
making up a name.
.pg
That's not quite accurate. The computer has ways of doing these things,
in the sense that there are basic facilities which you can use to write
programs which will enable the computer to examine pictures, analyse the shapes
present, and perhaps describe what it sees.
.pg
The exercises described below are intended to introduce you to the problems of writing
such programs. Before working through them you will find it useful to work
through the DATABASE demo.
The SEELINES demo gives a brief account of a library program
which does some of the things described below. You may find it useful to
play with that either before or after reading the DATABASE demo.
.pg
You can think of the problems in terms of a variety of
sub-tasks the program will have to be able to perform.
Some of these sub-tasks
correspond roughly to different kinds of structures
that can be seen in a picture, and different kinds of things that can
be depicted in a picture.
.pg
The division of picture-interpretation into a number of sub-tasks is a bit like
the division of language-understanding into sub-tasks. Understanding
language involves being able to perceive and recognize letters,
or phonemes, syllables, words, phrases, clauses, sentences, types of
speech-acts, objects or events or processes referred to, and so on.
.pg
Similarly, if you use the turtle to draw a picture of some blocks, then
someone interpreting the picture has to be able to see the dots, groups of
dots making lines, junctions where lines meet, groups of lines forming closed
regions, and so on.
She also needs to be able to think about surfaces of objects, edges where
two surfaces meet, corners where edges meet, familiar types of blocks, like
cubes and wedges, relations between blocks like "next to", "behind",
"above", "supports", and so on.
When the picture is a work of art, there may be a great deal more to
it, like the "point" of the picture.
But for the moment let's concentrate on some of the simpler sub-tasks.
.mh
Sub-tasks for a program that can see
--------- --- - ------- ---- --- ---
.hm
.sz
.ti -3
1. Finding the dots in the picture
.ti -3
2. Recognising collinear groups of dots
.ti -3
3. Interpreting dots as lines with orientations (e.g. horizontal, vertical)
.ti -3
4. Finding significant types of junctions between lines (ELLs, TEEs, etc)
.ti -3
5. Grouping lines into closed regions.
.ti -3
6. Linking together those regions which represent surfaces of the same body.
.ti -3
7. Describing orientations of the surfaces in space (vertical, horizontal,etc)
.ti -3
8. Describing the edges where surfaces meet (concave, convex edges).
.ti -3
9. Noticing which surfaces or objects are occluded.
.ti -4
10. Recognising known shapes of surfaces (square, triangular, etc).
.ti -4
11. Recognizing known shapes of objects (cubes, prisms, pyramids, people,
etc)
.ti -4
12. Recognising relations between such objects... perhaps interpreting their
actions, etc.
.ti -4
13. Noticing inconsistencies, ambiguities, oddities. For example, detecting
the impossibility of the "Penrose triangle", or the ambiguity in the "Necker Cube".
.ti -4
14. Making predictions about aspects of the scene not directly depicted (e.g.
inferring that there are invisible surfaces on the far side of
a block).
.ti -4
15. Interpreting a series of pictures as telling a coherent story about
some process or sequence of events.
.ti -4
16. Using the picture in planning and executing actions - e.g. using a map,
or a picture of the layout of a room, to plan movements, or to form a
strategy for solving a problem (getting to the bananas suspended from
the ceiling....).
.ti -4
17. Recognising groups of objects which are in some sense similar, e.g. rows of
pyramids, a stack of cubes, a pile of wedges.
.ti -4
18. Describing the scene depicted in good English, or producing some
other representation of it, such as a picture showing it from a
different viewpoint, or perhaps making a three-dimensional model.
.ti -4
19. Learning new concepts if the picture presents things not previously
known about. I.e. remembering "significant" shapes, so that they can be
recognised if they turn up again later.
.zs
.pg
These sub-tasks are not all independent of one another. In particular,
it turns out that if you try to treat them as independent when
writing programs, the programs do not perform as well as
when the sub-modules are designed to co-operate with each
other. For instance, if you have already begun to interpret a
picture as representing a block, this may help you decide where to look
for lines, so that you'll more easily see lines that are obscure. But
seeing some lines might be a necessary condition for generating the
block hypothesis in the first place. So the line-finding and
block recognising aspects of a computer vision system may sometimes
need to co-operate with each other.
.pg
This interlocking program organisation is sometimes referred to as
"HETERARCHY", and is contrasted with "HIERARCHY". A "hierarchic"
system might have a structure represented as follows:
.ls 2
.tp 3
.in +4
[find-dots] --> [find-lines] --> [find-junctions] -->
[find-bodies] --> [describe-bodies] -->
[recognise-bodies] --> etc.
.in -4
.ls 1
.pg
This organisation assumes that finding lines, for example, can be
done completely before any "higher-level" entities are recognised.
This is analogous to assuming that in hearing speech we can
recognise all the words prior to understanding what is being
said, or that in reading handwriting you recognise all the
letters before you recognise the words or the meaning.
We shall challenge such assumptions during the course, but
for your initial programming exercises it is easiest to assume
that sub-tasks can be broken down and tackled separately,
since the assumption works quite well with simple problems.
.pg
The tasks listed above form only a subset of the things you might expect a program to be
able to do with a picture. Thinking about human abilities will lead you
to want to add things to this list. One important omission concerns the ability
to deal with pictures which are in some sense "messy" or misleading because
they contain lots of spurious fragments, or perhaps because some bits have
been blanked out, or smudged, or otherwise corrupted. People are very
good at dealing with various kinds of "messy" or "noisy" data, for
example when they read my handwriting, or when they watch television when
reception is bad.
.pg
There is no reason why programs should not be able to do these things too,
and some current work in computer perception is concerned with enabling
programs to cope with noisy pictures.
.pg
We shall not aim to develop a system which can achieve all of the
above tasks.
For now, we'll concentrate on a relatively simple subset, namely
finding dots and lines, grouping lines into two-dimensional shapes, and
trying to describe and recognise the shapes.
It is a good idea to start with simple tasks, so we'll restrict
the program to recognising squares, rectangles and triangles. Later, you
could consider extending the program so as to deal with more complex pictures,
for instance pictures in which some of the objects overlap others.
.mh
Examining a picture point
--------- - ------- -----
.hm
.pg
One of the things we'll need to be able to do is move the turtle round the
picture looking for dots. Later, we consider how to do the moving. For
now concentrate on the task of deciding whether the current position
contains a dot, or not.
.pg
Recall that the current position
of the turtle is always represented by the values of the two variables,
XPOSITION and YPOSITION
and that the contents of the picture at location X Y can be
found by applying PICTURE to X and Y.
.pg
So to find the contents of the picture at the current position we can type:
 	: PICTURE(XPOSITION, YPOSITION)
.br
It is rather tedious to have to type this wherever we need to know the
current picture contents, so lets have a function to do it.
.sp 1
.tp 3
 	: FUNCTION COLHERE;
 	:	PICTURE(XPOSITION, YPOSITION)
 	: END;
.pg
The command COLHERE(); will cause that function to be executed. It
will leave a result on the stack, namely the current "colour" of the picture.
At locations where the picture is empty, the result will be a
space, represented by the value of the variable SPACE. There will be a
dot at locations where the result is the same as the value of the variable
PAINT, assuming the paint has not been changed since the picture was
drawn.
.pg
So we can now define two new functions, which test whether there is a space
present, or whether there is "paint" in the current position.
.sp 1
.tp 7
 	: FUNCTION SPACEHERE;
 	:   COLHERE() = SPACE
 	: END;
 	:
 	: FUNCTION PAINTHERE;
 	:  COLHERE() = PA.....
 	: END;
.fi
.pg
What should go in place of the dots? These functions produce either
the result TRUE or the result FALSE. So they can be used in
"conditional" commands, of the form:
 	: IF PAINTHERE() THEN............CLOSE;
.pg
Strictly, you could do with only one of the two, provided that all picture
locations have either spaces or the current paint, not some other colour.
In that case you could use NOT, as in
 	: IF NOT( SPACEHERE() ) THEN ........CLOSE;
.pg
Suppose we wished to use one or both of these functions in our scan of the
picture, and we wanted to make a list of all the points containing dots.
How would we represent a point? A possible way is to make a list containing
the two co-ordinates, thus:
 	: [% XPOSITION,  *********  %]
.sp 1
What should go in place of the asterisks?
.pg
Once again, instead of having to type that whenever we need to make a
record of the current position, we can define a function which, when it
is executed, makes the required two-element list.
.sp 1
.tp 3
 	: FUNCTION HERE;
 	:   [% ........ , ******** %]
 	: END;
.sp 1
Fill in the missing bits. You then have a function which you can think
of as producing a "point", which can be stored, or assigned to a variable, to
be examined later by other functions. E.g.:
 	: VARS P;
 	: HERE() -> P;

creates a list representing the current co-ordinates, and assigns it to be
the value of the variable P. Similarly you can print out the current position
with the command:
 	: HERE() =>

Try it.
.pg
How can a program which goes round the picture looking for paint keep a
record of the points where it finds paint? In other words, how can
we store all the results of the function HERE, when it has been
executed with the turtle in lots of different locations?
Suppose we decide to make a list of all such points. There are two main
approaches to this. One is to leave all the points on the stack, wherever
they are created by the scanning function, and to put the command
to run the function between decorated list brackets.
The other approach is to have a list, perhaps called POINTLIST,
and keep adding things to this list. If the scanning function was called
SCAN, for example, the first approach would involve using a command
something like:
 	: [% SCAN() %] -> POINTLIST;
 
You may like to explore that approach. We'll use a different approach
in the examples.
.pg
Suppose we declare POINTLIST and initially make it the "empty" list
[], thus:
.tp 2
 	: VARS POINTLIST;
 	: [] -> POINTLIST;

How can we define a function which will add the current point to this
list? We already have the function HERE, which will create the point,
so all we need is a function which uses it , and adds the result on to the
beginning of POINTLIST. Something like the following would do:
.sp 1
.tp 3
 	: FUNCTION RECORDPT;
 	:   [%  .....()  %] <> POINTLIST  -> POINTLIST
 	: END;
.fi

What should go in place of the dots?
.pg
We can now define a function which will look at the current location,
and if paint is present, it will make a note of the location and
add it to POINTLIST, thus:
.sp 1
.tp 3
 	: FUNCTION TESTHERE;
 	:   IF   ******
 	:   THEN  .....
 	:   CLOSE
 	: END;

Fill in the missing bits, using the functions PAINTHERE and RECORDPTS.
.pg
Type these functions into the computer (preferably using the editor),
then try them out. Draw some simple pictures, e.g. a short line,
then move the turtle around (using JUMPTO, JUMP and JUMPBY),
and execute the functions, printing out results. See how POINTLIST
is changed by TESTHERE.
.pg
You may think of ways of altering the
definitions of the functions.
For instance, RECORDPOINT might be altered to store the
paint as well as the co-ordinates, if different paint is used in
different parts of the picture.
.mh
Scanning the picture
-------- --- -------
.hm
.pg
We now know what to do at each point if we are looking for dots in the picture.
But how do we arrange for this to be done at each point?
.pg
We want to define a function, perhaps called SCAN, which will move the
turtle over all the points of the picture, and at every point it will
execute the function TESTHERE defined above, so that eventually POINTLIST
will have a record of all the places in the picture where there are
dots. Later, we'll think about improving it so that it does something
more intelligent as it scans the picture.
.pg
Notice this design strategy: instead of pondering the whole complex
problem all at once, it is often useful, sometimes even essential,
to start off by thinking about very simplified versions. It is usually
easier to extend or re-design an oversimple program, than to start
designing one which is not oversimplified.
.pg
How should SCAN be defined?
There are always several possible ways of doing things.
What follows is one suggestion. You may be able to think of
something different, or even better.
.pg
Our problem is to ensure that the turtle moves to every point in the
picture. We can do this by making it scan every row, moving from
left to right. So we'll need to define a function called SCANROW
to do this. Further, we'll need to have a function which moves the
turtle to the beginning of the next row to be scanned. Let's call it
STARTROW. Then SCAN can be defined so as to repeatedly
call these two functions, until every row has been scanned.
These functions will need to know when to stop! So it will be useful
if SCAN starts by finding out how big the picture is.
Suppose it uses the variables XMAX and YMAX to hold the width
and the height of the picture, respectively.
We then have the following rough outline for a definition.

.tp 5
 	Plan for function SCAN.
 	  Find width and height of PICTURE and assign to XMAX and YMAX
 	  Repeat YMAX times:
 	    Use SCANROW to scan a row
 	    Use STARTROW to get to beginning of next row.

With that rough outline, we can begin to work on details.
.mh
Finding the dimensions of PICTURE
------- --- ---------- -- -------
.hm
When you type something like NEWPICTURE(25,20); this creates
a picture which is represented in the computer as an "array" of two
dimensions, the first going from 1 to 25, the second from 1 to 20.
If you want a program to discover the bounds of the picture, you can
use the function FNPROPS, by applying it to PICTURE.
It will return a list containing the bounds. Try the following:

 	: NEWPICTURE(25,30);
 	: FNPROPS(PICTURE) =>
 	: NEWPICTURE(35,27);
 	: FNPROPS(PICTURE) =>

(Make sure the turtle is compiled first.) The last command should have
printed out a list of four elements,namely [1 35 1 27].
.pg
So SCAN can find the width and height of the picture by examining the
second and fourth elements of the list. One way to proceed would be to
define a function to return the second element of a list (i.e. the HD
of the TL of the list), and a function to return the fourth element.
However, it would be more useful to have a general purpose function
called ELEMENT, say, which takes a number N and a list L as arguments
and returns the N'th element of L.
.pg
Try defining such a function. Ask for help, or go back to the LISTs1
demo, if you get stuck.
.pg
So the first three lines of SCAN might go:

.tp 3
 	:  VARS XMAX YMAX;
 	:  ELEMENT(2,FNPROPS(PICTURE)) ->XMAX;
 	:  ELEMENT(4,FNPROPS(PICTURE)) ->YMAX;

and the other functions used in SCAN can simply assume that XMAX and YMAX have the
appropriate values.
.mh
Getting the turtle ready to start scanning
------- --- ------ ----- -- ----- --------
.hm
The next bit of "initialisation" required is to ensure that the turtle
is in the correct place to start scanning, and facing in the required
direction. If we are scanning from left to right, starting in the
bottom left hand corner of the picture, we can set things up
with:
.tp 2
 	:  JUMPTO(1,1);
 	:  0 -> HEADING;

You might like to think about how this could be varied if the turtle
were to start in the top left corner, or the top right corner.
You'd need to use XMAX or YMAX, or both.
.mh
Scanning a row of the picture
-------- - --- -- --- -------
.hm
Our plan for SCAN assumes that SCANROW will apply the
function TESTHERE (or possibly some more elaborate function)
to every point along a row of the picture. Let's assume that before
SCANROW is executed the turtle has been moved to the beginning
of the row, and is facing to the right.
(This will be the job of STARTROW.)
.pg
We could define SCANROW so that it always uses the non-local variable
XMAX to decide how far to scan, but it is preferable to
give it a formal paramater, say DIST, a number representing
the distance to be scanned. Then inside the function SCAN,
the command SCANROW(XMAX) will ensure that DIST gets
the right value. So we have a plan:

 	Plan for SCANROW
 	  argument: DIST - a number
 	  do the following DIST times:
 		........... and jump one step.

Fill in the missing bits, then type in the function definition,
and try it out.
If you haven't learnt how to use the REPEAT command, ask for help.
.pg
That version of SCANROW applies the function TESTHERE
at each place before it does a jump to the next place. You could,
instead, define a more general version that has an extra formal parameter,
specifying the function to be applied. The shape
of the definition would
then be:

.tp 5
 	: FUNCTION SCANROW (DIST, FUNC);
 	:   REPEAT DIST TIMES
 	:	FUNC();
 	:	.......
 	:   CLOSE
 	: END;

This version of SCANROW has what is called a "functional
argument", namely an argument which is itself a function.
This would require the definition of SCAN to be
altered so that instead of the command
 	: SCANROW(XMAX);
.br
it contains the command
 	: SCANROW(XMAX,TESTHERE);
.br
I.e. it should specify that TESTHERE is to be the value of FUNC.
Try doing it this way. Better still, make SCAN itself
take a functional argument, so that instead of being restricted
to using TESTHERE at every point, it will use any
function it is given. Notice how the use of functional arguments
enables us to produce more general programs. (APPLIST and
MAPLIST are standard POP11 functions which take
functional arguments. See the LISTSUMMARY demo for
details.)
.pg
If SCAN is to take a functional argument, we need to alter its
plan, along the following lines:
 	Plan for function SCAN
 	  Argument:	FUNC  --a function to examine the
 					current picture point.
 	  Find width and height of picture, and assign to XMAX and YMAX
 	  Repeat YMAX times:
 		Use SCANROW(XMAX,FUNC) to scan a row
 		Use STARTROW to get to beginning of next row

Then to execute this you can give the command
 	: SCAN(TESTHERE);
.br
which will cause the function TESTHERE to be applied at
every picture point.
.pg
We still need to think about what happens at the end of each call of
SCANROW. The turtle needs to be moved back to the beginning of
the next row. The simplest way to do this is to use the function JUMPTO.
The row the turtle is on is represented by the value of YPOSITION,
which is 1 for the bottom row of the picture.
So the next row is represented by YPOSITION + 1, if you want the
next row to be the one above the current row. The function STARTROW
can then include the command
 	: JUMPTO(1, YPOSITION+1);
.br
where the first 1 represents the value of XPOSITION at the beginning
of a row.
.pg
Try defining all these functions. Test them by drawing several
simple pictures and trying the functions on them. E.g. for each
picture apply SCAN to TESTHERE, and afterwards examine
POINTLIST, to make sure everything is working.
Some problems to think about:
 	Could you have used JUMP or JUMPBY in STARTROW?
 	Should STARTROW be called before or after SCANROW in SCAN?
 	Should STARTROW do anything about HEADING?

If you can't solve these simply by thinking about them, try experimenting.
.mh
Finding lines
------- -----
.hm
You should now have a version of the function SCAN which will scan
any picture you draw, looking for dots, and record all the points
in the list POINTLIST. This list is a very simple "database",
a store of information, which could be used to answer questions about what is
in the picture. For instance, you could write procedures to search through
POINTLIST for points which are in the same row and for points which
are in the same column, and for points which are in the same diagonal,
and group those points into lines.
.pg
But that would be rather pointless (sorry!), since you might as well go back
and search the original picture. If POINTLIST is very long (i.e if
there are lots of points in the picture) then constantly searching down the
list for neighbours of a given point would take much more time than looking
in the picture at the eight neighbours of the point.
(The picture is an "analogical" representation with useful addressing
properties.)
.pg
We can make the program behave more sensibly when it is scanning, by
using a better structured database. Instead of putting all the points into
a single list, let us make a list for each row, column or diagonal containing
points.
But how could we know in advance how many lists to create? Should we
declare a whole lot of variables for them, e.g.

 	: VARS COL1 COL2 COL3....ROW1 ROW2 ROW3...DIAG1 DIAG2 DIAG3....;

.br
and assign [] to them, then alter TESTHERE so that as it
finds a point it stores it in the appropriate lists? (How many lists would
each point go into? )
.pg
There is a much better way. The POP11 library contains a "database"
package which you can use. There is a brief introduction to it in
the DATABASE demo.
.sp
Before reading on, work through the DATABASE demo if you
have not already done so.
In particular, you will need to be able to define the function
ADDTOLIST referred to there.
.pg
Suppose we store a pattern in the database for every row and column
containing points. For instance, if there are points on column 3,
and L is a list of all those points, we want the database to contain
the entry:
 	: [COLUMN 3 ^L]
where the third element uses the value of L. Similarly if row 5
has points, we want the database to include an entry like
 	: [ROW 5 ^L]
where L is a list of all the points.
.pg
How can we achieve this for every row and column containing points?
There are several sub-problems:
.sz
(a) Given a point, how do we decide which row and which column it
belongs to?
.br
(b) When we know the number of the appropriate row or column how
do we get the point added to the appropriate list in the database?
.br
(c) How do we make this happen at all places in the picture where
there is paint?
Can we alter the function RECORDPOINT so that when it is
called by TESTHERE, inside SCAN, it will do what is
needed?
.zs
.mh
Assigning points to rows and columns
--------- ------ -- ---- --- -------
.hm
Problem (a) is probably the easiest. We know that the function
HERE, defined previously, will produce a point record, which is
a list of two numbers, the x-coordinate and the y-coordinate. The
first number is the column number corresponding to the point, and
the second number is the row number. So we can easily define a
function which takes a point as argument and returns its column
number, thus:
.tp 3
 	: FUNCTION COLUMNOF (POINT);
 	:  HD(POINT);
 	: END;

What about a function called ROWOF, which produces the row
number of a point? Define it.
.pg
The next task, (b), is easy using the function ADDTOLIST,
or something like it. Here is a command which will make sure that
a point is recorded in the appropriate column:
 	: ADDTOLIST("COLUMN", COLUMNOF(POINT), POINT);

How would the corresponding command to store the point in its row
go?

Construct a new definition for the function RECORDPOINT
so that it takes a point as argument, and stores it in the
appropriate way. Then construct a new definition for TESTHERE,
so that it looks to see if there is paint at the current location in
the picture, and, if so, uses RECORDPOINT, applied to the
result of HERE(), to store the point at appropriate places in
the database.
.pg
Try out your functions by drawing a simple picture, then jumping
to various points in the picture, executing RECORDPOINT
and TESTHERE, and then examining the contents of the database.
.pg
Then see if using SCAN, as before, gets everything in the picture
recorded appropriately. To get a good idea of what is going on it may
be useful to trace ADDTOLIST. This could produce a lot of
printing, so make sure the picture doesn't have too much in it.
.mh
Diagonal lines
-------- -----
.hm
The program written so far may be fine for finding lines in pictures
made of squares and rectangles, where all the lines are vertical and
horizontal, but what about diamonds and triangles, and other shapes,
like letters, where there may be diagonal lines? You may like to
try modifying the function RECORDPOINT so that it builds
up entries of the form
 	: [LEFTDIAG ^N ^L]
 	: [RIGHTDIAG ^N ^L]
.br
where N is a number indicating which diagonal, and L is a list
of the points in that diagonal. You'll need two functions which,
when applied to a point, produce numbers corresponding to the
diagonals they are in. E.g. LEFTDIAGOF when applied to a
point should produce the number of the diagonal sloping up to the
left through that point, and RIGHTDIAGOF should produce the
number corresponding to the diagonal sloping up to the right.
.pg
But how should numbers be assigned to diagonal lines? A useful trick
is to notice that if you take a group of points all on the same
line sloping up to the right, then as you go up the line you keep
increasing the x-coordinate and the y-coordinate by the same amount.
So the difference between X and Y is the same for all points on
that diagonal. So the function to give a number for that diagonal
can just take any point on it and, say, subtract its y-coordinate from
its x-coordinate. Try to see if this procedure avoids giving the same
number to two different diagonals, which would be very confusing.
.pg
A similar trick deals with diagonals sloping up to the left.
As you start at the top left, moving down to the right, you are
gradually decreasing the y value of points and increasing the x value.
The two changes are equal in magnitude, but opposite in sign, so
that the sum of X and Y must be the same for all points on the
same line sloping down to the right, or up to the left. This can
be used to define a function which, when applied to a point, produces
a number for the left-diagonal containing it.
.pg
Try defining these functions, and modify RECORDPOINT so
that it uses them to store points in lists correspoinding to
diagonals. Then test all this again, using SCAN, on a simple
picture with a couple of diagonal lines.
.pg
If ADDTOLIST is still traced you'll notice that all the
points in any one row, column or diagonal are found in an order
which corresponds to their order in the line. This is a
result of the fact that we scan the picture in an orderly fashion.
This is very useful, since it means that by examining the lists of
points stored in the database, we can find out where lines begin and
end without having to re-order the points.
.pg
Notice that we are making our system gradually more and more
complex by making alterations to individual functions. If a system
is well designed, you should be able to redefine a part of
it and still find that the whole thing works, though perhaps in a
new way. This is sometimes called "modular" or "structured" programming.
.mh
Should lines be found by "tracking"?
------ ----- -- ----- -- -----------
.hm
It is worth noticing that there are many other ways we could
have designed a program to find lines of points in the picture.
For example, we could have had a function to search the picture for all
the horizontal lines, another to search for all the vertical lines,
and two more for each of the two lots of diagonal lines. Instead,
we have a single function which can be thought of as finding
the lines in all these different orientations in parallel.
Another possibility would have been to scan the picture until
a painted location is found, and then to try tracking from
there along whatever line the point belongs to. (This is called
"line-following"). This would have raised a lot of nasty
problems. If two lines pass through the same point, how could
we choose which one to follow first? How could we record all the
lines we have not yet tracked, and make sure we come back to
them later to track them to see where they end? When we reach dead
ends should we continue scanning the picture to see if there
are more points from which to start tracking? Then how can we
ensure that we don't track the same line more than once? Would
marking picture points with a different paint as we track along
them help?
.pg
You might like to explore these problems, and perhaps design a
program which finds lines by tracking along neighbouring points.
.pg
One of the problems with the "parallel" line-finding method
described previously is that every point gets stored in several
line lists no matter whether there is really a line of dots
or not. So the database ends up with a very large number of lines
recorded in it, but very many of them have only one point,
or perhaps two isolated points in them. Try defining a function
which will remove all such entries from the database. The
simplest strategy is to remove all the lists of length 1. But you
could do better by looking at the lists to make sure that they don't
contain isolated points. Given a list of points in the same row
column or diagonal, how do you decide which of them are
isolated points?
.pg
Could procedures of the sorts described here be used in a program
to play noughts and crosses (tic-tac-toe)? You'd need to modify the
program so that it can look for lines of "O"s and lines of "X"s.
How could you make it ignore all the diagonals except the two
long ones in a 3 by 3 picture?

A useful exercise would be to write a program which can examine a
noughts and crosses board (a 3 by 3 turtle picture),
and answer questions like (a) has either side won?
(b) Does one of the sides have a winning move (i.e.
are there two of the same marks in a row column or
diagonal with a vacant square completing the
line)?
.pg
What about a program which could tell whether anybody had won, or
was about to win a game of GO-MOKU? (This is a game of noughts and
crosses on a 19 by 19 board, and the first to get five in a
line wins.)
.mh
Lines and junctions
----- --- ---------
.hm
There are many things you could do with the "line-finding"
procedures described so far. Try one or more of the following:
.sz
.ti -3
1. Write a program which looks at a turtle picture and
for each line it finds (with three or more consecutive points?)
it prints out information of the form:
 	Line from .... to .... with orientation ....
.br
where the blanks are filled with the two end points and one of
the four words "vertical", "horizontal", "leftdiagonal",
"rightdiagonal". Perhaps it could also print out the
length of the line?
.ti -3
2. Write a program which deletes all the information stored
by SCAN and instead replaces it with patterns of the form
 	[LINE FROM ^END1 TO ^END2 ORIENTATION ^ORIENT]
.ti -3
3. Design a program to find all the points which are ends of two
or more lines (i.e. junctions where lines meet). For each such
point make a list of the lines ending at that point and then
store a pattern of the form
 	[JUNCTION ^POINT ^LINELIST]
.br
How should the lines in LINELIST be represented? You could
use PRESENT to retrieve patterns of the forms mentioned in
2 and use each pattern to represent a line. Could you make
sure that the lines in LINELIST are ordered in some useful way,
e.g. clockwise from the point of view of the junction?
.ti -3
4. Now make a program which prints out for each junction all the
lines which meet at that junction.
.ti -3
5. Instead of merely printing out and storing information about
each line and junction, make your program count the number of
lines and junctions. Could it use this to decide whether it has
a picture of a square or a triangle? How could your program
tell the difference between a picture of a square and a picture
of a rectangle?
How could it tell the difference between a picture of two squares
and a picture of an eight-sided figure?  How do you tell the
difference?  How could it distinguish a square from a diamond?
.ti -3
6. Modify your program so as to recognise and store information
about "free" ends of lines, in patterns of the form
 	[FREEEND ^POINT ^LINE]
.br
Extend it so that when given a picture of any one of the
following:
.sz
square, diamond, rectangle, triangle, single line, group of lines,
.zs
it prints out a description of what is in the picture, using
those terms.
.ti -3
7. What if the end of one line lies somewhere on another, forming
a "TEE" junction, as in a "T"? To make your program recognise this
you may need to write a function to tell whether a point lies on a line.
One way is to mark every point on the picture with a list of the lines
which go through that point. How?
.ti -3
8. Try making your program recognise "X" junctions, where two lines
cross. A possible way of recognising all sorts of junctions is to
take the list of all points on a line and a list of all the points on
another line, and see whether the two lists have a non-empty
intersection, i.e. a common subset. (For exercises on this sort of thing
see the SETSETC demo).
Try extending your program so that besides the descriptions given
previously, it can also recognise and describe a "TEE" and a
"CROSS".
.ti -3
9. Ensure that if the picture is composed of two or more disconnected
parts (e.g. two separate squares, or a square and a triangle, or a tee a cross
and a square) it can find them all and describe them correctly.
.ti -4
10. Design a program which can deal with pictures of squares and triangles
which occlude other squares and triangles. It will need to be able to
recognise an incomplete square as forming part of a square, not a triangle.
.ti -4
11. Consider a turtle picture of a cube. It will have nine
lines, seven junctions and three closed regions. Try designing
a program which will find the three regions.
It will have to use the information about junctions, and look for
"closed chains" of lines. Should it also find the chain consisting
of the outer boundary of the picture? (You could say there are four
regions in the picture, counting the background, and that the outer
boundary of the picture is the boundary of that region.)
For each region you could make a list of the lines bounding that region,
and a list of the corners of that region, and store a pattern of the
form
 	[REGION ^LINELIST ^JUNCTIONLIST]
.ti -4
12. Could you design a program to produce descriptions in English
of a picture?
.ti -4
13. What about a program to take the information in the database and
use it to draw a new picture, which is a copy of the original?
What kinds of database information would make this possible?
Can you imagine ways in which the information stored in the
database might lead the program to draw a copy which is not
exactly like the original? Could this be an explanation of some
of the mistakes people make when they copy pictures?
.zs
.pg
There are many possible extensions of these exercises. You could easily
generate a project from them. Some of the tasks are quite difficult.
