home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 5
/
DATAFILE_PDCD5.iso
/
utilities
/
m
/
makedrawf
/
Doc
/
ManualT
< prev
next >
Wrap
Text File
|
1997-04-29
|
77KB
|
1,948 lines
[[ Note: This text-only version of the manual was produced by hand from
the Impression version. It is therefore likely that there are lots of
infelicities in the formatting; in particular, font changes have been
lost. If a sentence doesn’t seem to make any sense, see whether it’s
any better if you pretend some of the words in it are in italics. :-) ]]
Contents
========
Introduction
Legal rubbish
Who am I?
How to read this manual
How to use mkdrawf
Errors
How to get mkdrawf
An introduction to drawfiles
Object type 0: font table
Object type 1: text
Object type 2: path
Object type 5: sprite
Object type 6: group
Object type 7: tagged
Object type 9: text area
Object type 10: text column
Object type 11: options
Object type 12: transformed text
Object type 13: transformed sprite
Object type 16: JPEG image
The drawfile description language I
Tokens
Numbers
Comments
Braces
Numbers
Comments
Braces
Points, dimensions and things
Colours
Keywords and special keywords
Token lists
The drawfile description language II
Global variables
Macros
Macro parameters and local variables
Arithmetic and things
String operations
Conversions
Conditionals
Iteration
File inclusion
Units
The drawfile description language III
The format of an object description
Bounding boxes
The Header pseudo-object
The FontTable object
The Text object
The Path object
The Sprite object
The Group object
The Tagged object
The TextArea object
The Options object
The XfText object
The XfSprite object
The JPEG object
Some sample drawfile descriptions
The Koch snowflake curve
A Lissajous figure
A typical text area
Circular arcs using Bezier curves
A silly spiral
Tagfiles
Technical issues
Memory management
Variables
Problems
Arbitrary restrictions
Differences from the previous version of mkdrawf
Points don’t have commas
Strings have quotation marks
No implicit hexadecimal
Revision history
Decoding drawfiles
Using decdrawf
The output from decdrawf
----------------------------------------------------------------------------
Introduction
============
This is the user manual for mkdrawf, a program for creating drawfiles.
Of course there are other programs for creating drawfiles, notably !Draw,
but mkdrawf is unusual in that it allows you complete control over what
goes into the drawfile. It takes as input a text file describing the objects
that should go into the drawfile; the description language it uses is quite
powerful, having variables, macros and some mathematical functions.
This manual describes the process of creating a drawfile using mkdrawf, and
includes several sample mkdrawf input files and the resulting output.
This manual also describes its much simpler companion program decdrawf,
which performs the inverse process: it converts drawfiles to mkdrawf
descriptions.
Before reading this manual you might want to read the mkdrawf tutorial.
You might also want to look at the manual for !Drawf, a Wimp front end
for mkdrawf and decdrawf.
Legal rubbish
-------------
1. These programs are not public domain; they are mine.
2. You are welcome to distribute them exactly unchanged (where “them”
includes all the source code and both versions (text and Impression) of
this documentation). You are also welcome to make changes to your own
copy of the programs.
3. However, you are not permitted to distribute modified versions without
my permission.
4. Furthermore, you are not permitted to charge money for copies of this
software; it is free software, and I intend that it should remain free.
And I certainly don’t want anyone other than me making money from it!
If you make improvements to the programs, I am very unlikely to object to
your distributing the improved version provided it is made clear what
changes you have made; the point is that I don’t want to be blamed for
other people’s bad programming or credited with their good programming,
and that I want the documentation for mkdrawf to be consistent with the
program. So if you improve mkdrawf, get in touch with me so that I can
give you an updated version of the documentation to go with your improved
version.
If you have somehow acquired an incomplete or modified version of these
programs, you may distribute it; but I’d prefer you to get in touch with
me and get a complete, up-to-date version of the programs. At the time of
writing, the current version is version 3.09.
Who am I?
---------
If you want to get in touch (to moan about problems, to suggest improvements,
to thank me for doing such a truly wonderful job, ...), you will need the
following information.
I have an e-mail address and a snail-mail address. Both are valid as of
December 1995, and both are likely to continue working for some time yet.
E-mail is preferred, but I’ll try to answer snail-mail too.
e-mail: gjm11@dpmms.cam.ac.uk
snail-mail:
Gareth McCaughan
Peterhouse
Cambridge CB2 1RD
I would be really happy to hear from you. If you have complaints or
suggestions, they may help me to make mkdrawf better; and if you have
neither, presumably that’s because mkdrawf is completely perfect for
your needs… I’d be very glad to be told!
How to read this manual
-----------------------
Of course it’s up to you; and it’s not a very long manual, so you could
probably read it in any order you like without causing yourself much trouble.
However, I would suggest the following procedure:
• Have a look at the “sample drawfile descriptions”, to give you some idea
about how mkdrawf works.
• If you don’t have much idea of just what goes into a drawfile, read “An
introduction to drawfiles”.
• If you’ve used the earlier version of mkdrawf, read “Differences
from the previous version...”.
• Read the three chapters on “The drawfile description language”, skipping
bits you don’t understand. The second contains the most obscurities; you
might well want to skim the third before reading the other two.
• Read “How to use mkdrawf”, and create a few files yourself, referring to
the DDL chapters.
At this point you’re on your own. You should be able to do anything you
want, referring to this manual as necessary.
If you find this manual heavy going, you might like to read the mkdrawf
tutorial.
----------------------------------------------------------------------------
How to use mkdrawf
==================
This is pretty easy. Just create a suitable input file, and type
mkdrawf <input-file-name> <output-file-name>
to make the output file. This will create the output file if it doesn’t
already exist, and will set its filetype to DrawFile.
The only other way of invoking mkdrawf is to type
mkdrawf −v
which displays the version number and date of your copy of mkdrawf. This
will be useful if I make further improvements. This version of the manual
describes version 3.09.
Alternatively, you can use the !Drawf application to give you a Wimp front
end to both mkdrawf and decdrawf.
Errors
------
mkdrawf produces three kinds of diagnostic: warnings, errors and fatal
errors. I hope the distinction between them is clear. If any errors or
fatal errors are produced, you should not rely on the resulting drawfile
containing anything sensible, though in most cases it will at any rate
be a valid drawfile. mkdrawf tries hard to carry on reading your input
file even if there are errors (because that may help you to spot several
problems at once); but you should not rely on anything after the first error
making sense. Just as with C, a single error can produce a cascade of
error messages.
All this is an improvement on versions 2.10 and earlier, which simply stopped
dead the first time they found anything they couldn’t parse.
The command-line option -e will make mkdrawf omit the filename in error and
warning messages. You are unlikely to need this.
----------------------------------------------------------------------------
How to get mkdrawf
==================
If you’re reading this manual, you probably already have a copy. But perhaps
you want to be sure you have the latest version, or you've accidentally
formatted your hard disc, or something. Or perhaps you stole the manual.
Anyway, you can get mkdrawf:
• from me, electronically or postally. If you want a copy sent to you by
post, please send me either a floppy disc and whatever sort of packaging
you think is suitable for the return journey, plus £1stg to cover postage
and make me bother to send it; or £2stg, for which you will get an HD disc
(or a DD disc if you request that), in what seems to me to be suitable
packaging. If you add another £4 you can also have a nicely printed
version of the documentation.
• from some FTP sites. I know mkdrawf is available at micros.hensa.ac.uk
(at least from academic sites in the UK), and I’m pretty sure it’s also
available at ftp.demon.co.uk. Please try any suitable FTP sites you can
think of before trying to get a copy from me!
----------------------------------------------------------------------------
An introduction to drawfiles
============================
This chapter describes briefly what sort of stuff goes into a drawfile.
If you already know all about that (for instance, if you’ve read the
relevant section of the PRMs), you can skip it. If not, read on...
A drawfile consists of a short header followed by a sequence of objects.
Each object consists, in turn, of a short header (similar but not identical
in structure to the header of the whole drawfile) followed by some more
interesting stuff. Exactly what that “more interesting stuff” is depends
on the object; in some cases, it can include other objects (and so
recursively...); see the sections on Group and Tagged objects.
There are 12 types of object; several are not understood by the version of
!Draw distributed with RISC OS 2, and one is so recent that no existing
version of !Draw understands it. There is no guarantee that yet more object
types will not be added in later versions of !Draw. Each type has an
associated number, by which objects of that type are identified in drawfiles.
The numbers are not consecutive; the apparent gaps are not mistakes.
Object type 0: font table
-------------------------
When you use !Draw you never see a font table object. That’s because it
doesn’t actually describe any particular part of the drawing. A drawfile
may contain text in several different fonts; internally these fonts are
referred to by magic numbers rather than by names, and the font table
establishes a correspondence between names and numbers. I don’t know for
certain why it’s done this way, but here are some possible reasons:
1. It saves a bit of space in the drawfile if one font is referred to
many times: only one byte needs to be repeated each time, rather than
a long font name.
2. It makes things easier for applications that have to render drawfiles:
they can call Font_FindFont once for each font in the table, and then
set up a table of font handles and use that.
3. It means that if you want to make wholesale changes to the fonts in a
drawfile (say, replacing Trinity with Homerton throughout), you only
have to change the font table.
(Reason number 3 here is irrelevant if you’re using !Draw, but if you’re
using mkdrawf or something like it it might be significant. My money is on
reason number 2 being the main one, though.)
There may only be one font table in a drawfile; if there are any text
objects, there must be exactly one, and it must appear before they do.
Actually !Draw doesn’t cope correctly if the font table is not the very
first object in the file.
Object type 1: text
-------------------
If you select the text tool in !Draw and type some text, you are creating
a text object. A text object specifies, as well as the text to be contained
in it:
• the colour in which the text is to appear;
• the background it’s likely to be on;
• the font it is to appear in;
• the size and aspect ratio of the font;
• where the text is to start (that is, its bottom-left corner).
The background colour is just a hint. It doesn’t actually cause anything
to be drawn in that colour, but it helps the anti-aliasing code in (for
instance) !Draw to improve the appearance of the text.
Object type 2: path
-------------------
This is the commonest object type. A path is made up of a number of
subpaths. Each subpath is drawn, and perhaps filled, separately (but
the same options for things like path width, fill colour and so on are
common to all the subpaths). A subpath consists of simple elements:
• move to a given place
• draw a line to a given place
• draw a Bezier curve to a given place, with given control points
• close the current subpath.
Each subpath starts with a move, and each move begins a new subpath.
It’s possible to specify the following things for a path object:
• the colour to use for filling it in (may be “none”)
• the rule used to determined what gets filled and what doesn’t
• the colour to use for drawing the outline (may be “none”)
• the width of the outline
• how to join up adjacent sections of path
• what to do at the ends of the path
• whether the path outline should be “dashed”, and what dash pattern to use
Object type 5: sprite
---------------------
A sprite object simply consists of an object header and a sprite. (The
object header determines exactly where the sprite will appear and how big
it will be.)
Object type 6: group
--------------------
A group object contains any number of other objects. (It’s possible to
work out from the object header where in the drawfile the list of
objects in the group ends.) The only other information in a group
object is a name; the name has no effect on the rendition of the object,
but it may be used for identification purposes by some applications.
Object type 7: tagged
---------------------
A tagged object consists of a 4-byte identifier, an object and some quantity
of extra data. A tagged object is rendered simply by rendering the object
it contains; the other stuff may be used by certain applications. (The PRM
says: “This allows specific programs to attach meaning to certain objects,
while keeping the image renderable”.)
Object type 9: text area
------------------------
A text area object consists of a number of columns and a quantity of text
(and a little extra data). The text contains various escape sequences which
control how it will be formatted. A text area object is rendered by
formatting the text in the columns (using the columns in sequence); users
of DTP packages may like to think of the columns as being linked text frames.
A complete list of the escape sequences and their meanings is contained
in the next chapter of this manual.
Object type 10: text column
---------------------------
Text column objects appear only inside text area objects. They describe
the columns into which the text will be placed.
Object type 11: options
-----------------------
An options object describes the following things, most of which are
specific to !Draw.
• The size of paper on which the objects are supposed to appear
• Some information about “paper limits”
• The set-up of the grid (shown? locked? what spacing? ...)
• The zoom ratio
• Whether the toolbox is to be shown or not
• What “entry mode” to use initially
• The size of !Draw’s undo buffer
If more than one options object appears, !Draw ignores all but the first.
The RISC OS 2 version of !Draw ignores options objects (and doesn’t save
them).
Object type 12: transformed text
--------------------------------
A transformed text object is a text object with bells on. The bells specify:
• what affine transformation to apply to the text
• whether to apply kerning to the text
• whether to render the text right-to-left
An affine transformation is specified by a 2×2 matrix and a vector.
More on this later.
Object type 13: transformed sprite
----------------------------------
A transformed sprite object bears the same relationship to a sprite object
as a transformed text object does to a text object, except that kerning and
right-to-left aren’t issues. In other words, a transformed sprite object
contains a transformation matrix (erm, and vector) and a sprite.
Object type 16: JPEG image
--------------------------
A JPEG image object consists of a small amount of header data and a JPEG image
in JFIF format. (JPEG is a standard method of compressing images; JFIF is the
name of the commonest file format for JPEG-compressed images. What is usually
called a “JPEG file” should strictly be called a JFIF file.) No current
version of !Draw understands this object type, but future versions might
well do.
----------------------------------------------------------------------------
The drawfile description language I
===================================
This chapter and the next two describe, in some detail, the language
understood by mkdrawf. To be more precise: this chapter is a brief
description of the syntax of the language, the next deals with variables
and macros and things, and the next describes what gets done with the
tokens produced when all the macro expansion and variable substitution
are finished. (So it’s that chapter that actually discusses drawfile
objects.)
Tokens
------
As mkdrawf reads an input file, it parses it into tokens. A token might be
a keyword like FontTable, or a number like 1.23456, or a colour like
r200g100b123, or whatever. Except in funny circumstances -- strings and
things -- tokens are delimited by whitespace and are not case-sensitive,
so you can write LINE or Line or Line or lInE or whatever you happen to
prefer.
Here is a list of the token types understood by mkdrawf, with an example
of each. Some of these are not explained until the next chapter.
• Number 123.456e2
• String "foo bar"
• Colour r123g0b255
• Keyword Width
• Special keyword Define
• Global variable $myvar1
• Local variable %length
• Macro name Snowflake
• Opening brace {
• Closing brace }
Internally, mkdrawf actually uses a couple of other keyword types, but
they aren’t things you can put into the file yourself.
Here’s a brief account of how mkdrawf decides what type each token it sees
has:
Opening and closing braces are easy. Anything that starts with a $ is
a global variable; anything that starts with a % is a local variable;
anything that starts with a " is a string. Numbers are things that can
be parsed successfully by the strtod function in the C library; colours
are things matching the scanf format r%dg%db%d. Anything else is one of
the other types. mkdrawf has a list of keywords and “specials”; anything
else must be a macro name. Simple!
Numbers
-------
You can enter a number either in a form that the C library function strtod()
can understand, or in the form 0x1234FEDC (meaning hexadecimal). Both get
dealt with in the program as “double”s, should this be relevant.
Comments
--------
If a token begins with a # then that token, and everything else up to the
end of the line, is ignored by mkdrawf. This is useful for making your
mkdrawf input files more comprehensible, and also for “commenting out”
sections that you don’t want to use but don’t want to throw away completely
either.
I suppose that strictly speaking this means there’s another type of token
(comment start), but that’s not how mkdrawf thinks about it internally.
Something that would be a token but begins with a # never makes it as far
as tokenhood; the token-maker just reads a new input line and tries again.
Braces
------
The language understood by mkdrawf is a structured one; its structure
reflects the structure of drawfiles. For instance, a Group object actually
contains other objects; so the description of a Group object in a mkdrawf
input file actually contains descriptions of other objects. Groupings of
this sort are done with braces. Since braces are tokens, they must have
whitespace on either side. (A brief note for the hitherto ignorant:
“whitespace” includes newlines as well as spaces and tabs!)
Whenever it’s stated that something is delimited by braces, this means
it’s delimited by matching braces. For instance, you can’t put unmatched
braces into a macro definition.
Points, dimensions and things
-----------------------------
A point (meaning a location in 2-dimensional space, rather than a pixel)
is represented by two numbers. By convention, the point 0 0 is at the
bottom-left corner of the image. The unit for these numbers, and indeed
for almost all dimensions, is by default the point; for our purposes a point
is exactly 1/72 of an inch. (Historically the printer’s point is a slightly
smaller unit than this; but the definition of the drawfile format says
that its points are exactly this size. I think this accords with usage
in PostScript and on the Apple Macintosh; it differs from that of TeX.)
It's possible to use different units; more on this later.
A bounding box is represented by two points, which should be the bottom-left
and top-right coordinates of the box.
In a drawfile, most dimensions are stored as integer multiples of 1/640 of
a point; so giving dimensions to 10000 significant figures is probably not
sensible.
Colours
-------
A colour is specified by giving its red, green and blue intensities. The
format is as described above; the numbers should be between 0 and 255
inclusive. There is also a special colour, called None or Transparent
(these are synonyms), which means just what you would expect it to.
As of version 2.33, wherever a colour is required you may instead give
three integers in the correct range. They will be used as the red, green
and blue intensities of the colour. This makes it possible to do things
like shading.
Keywords and special keywords
-----------------------------
Keywords are bits of drawfile object specification. Some of them name
object types; some name parameters of objects; some are “flags” (their
presence or absence determines something about an object); some are
possible parameter values, when those values form a finite set. Examples
(in order): Path, OutlineColour, Kerned, Mitred.
Special keywords do not directly describe drawfile objects. They are
dealt with earlier, by the macro-and-variable machinery described in the
next chapter. They are used in defining macros, doing arithmetic
calculations, and so on.
Token lists
-----------
I’m not sure whether I should count these as tokens. They are treated as
such in the program. A token list is, errm, a list of tokens; the expansion
text of a macro is a token list, and the value of a variable can be too.
The only thing you really need to know about these is that there’s an
implementation limit on the depth of nesting of token lists, namely 256
levels of nesting.
You get a token list into a variable by doing, say,
Set %womble { 1 2 3 4 }
which gives the local variable womble the token list 1 2 3 4 as its value.
(What’s a local variable? See the next chapter.)
Note: the body of a For loop is not actually a token list, although it
looks like one; nor are the brace-delimited bits of drawfile objects.
----------------------------------------------------------------------------
The drawfile description language II
====================================
The drawfile format, unlike (say) PostScript, has no “language” features
at all. A drawfile cannot use variables, looping constructs and so on.
mkdrawf allows you to use some (fairly weak) programming features of this
kind in a way that fits quite well with its underlying language; for
instance, you can give values to variables and define macros.
mkdrawf splits your input file into tokens, as discussed in the previous
chapter. However, most of the time it isn’t just reading tokens; it’s
expanding them too. This means that if it sees an expandable token
(I’ll explain this in a moment) it replaces it with its expansion, which
is usually another token or a whole sequence of tokens.
The tokens resulting from expansion are then expanded again, and so on;
thus, what eventually emerges is a stream of unexpandable (i.e., simple)
tokens. What happens to those is the subject of the next chapter.
Here’s a list of expandable token types, together with what happens to them
on expansion. There’s more detailed information in the next few sections.
• A variable (local or global) is replaced by its value.
• A macro, together with its parameters, is replaced by its text; while
that’s being processed, local variables are set up corresponding to
the parameter values given.
• A token list is replaced by the tokens in the list, in order.
• A For, together with its parameters, is replaced by several copies of
the loop body.
• An If vanishes, together with any conditional stuff in the “wrong”
arm of the If.
• A Define or Set vanishes, together with the thing being defined and
the value it’s being given; of course the defining or setting happens
too.
• An Include, together with the filename following it, is replaced by
the contents of that file. (This is slightly inaccurate: see below.)
• An operator (like Plus or Sin), together with 1 or 2 tokens following it,
is replaced by the result of applying that operator to the appropriate
values.
Global variables
----------------
A global variable is named by a token that starts with a dollar sign $.
When mkdrawf sees a global-variable token in its input stream it replaces
it with the current value associated with that variable. Of course this
isn’t quite true, since it must be possible to set the variable in the
first place; when mkdrawf sees the special keyword Set it doesn’t expand
the following token, but treats it as a variable name. It then sets the
variable’s value to be the token that follows, or (if that’s an open-brace
token) the token-list that follows.
The value of a variable can be any single token, or a token list. Note
that if you do
Set $foo { 1 2 3 }
the value of $foo is the list
1 2 3
rather than the list
{ 1 2 3 }.
When you set a variable to a token list, the tokens in the list are not
expanded as they are being read. (But of course they will be expanded later
when the variable name is read and expanded.) See the comments below on the
same feature for macro parameters if you want to know why.
Macros
------
A macro’s name can be any token that doesn’t already have some other
significance. So you can’t name a macro Width or 123 or $foo or %zog,
but you could call it Fred or 2a or ^^!"£@. A macro expands to a list of
tokens. You call a macro by giving its name followed by { }. (Why? See the
next section.) Macros are defined with the Define special keyword, which
behaves very much like the Set special keyword: give it the name of the
macro, followed by the text of the macro enclosed in braces.
Macro parameters and local variables
------------------------------------
There’s more, though: macros can take parameters, and can use local
variables. I’m lumping these together because they have the same syntax
and use the same internal machinery.
A parameter or local variable is named by a token that starts with a
percent sign %. It has a value local to the macro containing it; local,
in fact, to the macro invocation containing it. (The point is that you
can call a macro many times; it’s a different variable each time.)
Local variables get their values in two ways. The first is by the Set
special keyword, just as for global variables; the second is by being
used as parameters when the macro is called. To call a macro with
parameters, put some stuff between those braces. The stuff should look
like this:
Fred { %foo 123 %aardvark Width %%% %foo }
which means that macro Fred should be called, and three of its local
variables given values. The local variables are called %foo, %aardvark
and %%%; the values are, respectively, the number 123, the keyword Width,
and whatever the value of local variable %foo is when Fred is called.
(The procedure is: read a token without expansion, check it’s a local
variable name, then read a token with expansion and assign it to the
appropriate local variable for the macro about to be called.)
A local variable (or macro parameter) can have a token list as its value.
To do this with a macro parameter, you say (for instance):
Fred { %foo 123 %bar { Width 1 OutlineColour r0g0b0 } %baz "Hello" }
which means that %foo gets the value 123 and %baz the value "Hello",
as before, but %bar gets that list as its value. Within the macro body,
%bar will expand to Width 1 OutlineColour r0g0b0. Note again that whatever’s
inside the braces doesn’t get expanded until %bar is expanded.
This allows you to do something very like anonymous functions (!), and
if you need something variable in there you can always do
Fred { %list { Set %zog Plus %zog %foo } %foo $zoo }
since the value given to %foo will be expanded at invocation time.
The fact that a local variable is local to a macro invocation rather than
to a macro definition, as it were, makes it possible to do recursion. See
the snowflake curve example later.
Note that the following will not work:
Define Foo { FontTable }
Foo {
1 "Trinity.Medium"
2 "Homerton.Bold"
}
but the following will:
Define Foo { FontTable }
Foo { } {
1 "Trinity.Medium"
2 "Homerton.Bold"
}
And so will:
Set $Foo { FontTable } # or, Set $Foo FontTable
Foo {
1 "Trinity.Medium"
2 "Homerton.Bold"
}
Positional parameters
---------------------
Sometimes you need or want a less cumbersome way of getting parameters into
macros. Macro parameters of the form %number are special: %1 is the first token
after the macro’s parameter list, %2 is the next and so on. As many of these are
read as are needed. (This number may be different for different invocations of
the same macro, please note.)
This allows you, for instance, to produce your own functions with syntax
the same as that for the arithmetic functions described below.
Arithmetic and things
---------------------
You can do some (very limited) mathematical operations on numbers. This
is all done by the token-expansion machinery: for instance, expanding the
Plus special keyword reads two more tokens (with expansion), checks that
they’re numbers, and yields a numerical token whose value is the sum of
their values. The special keywords that do this sort of thing are:
Plus Minus Times Over Sqrt Sin Cos Tan Arcsin Arccos Arctan Arctan2 Floor;
it’s probably pretty obvious what most of these do, apart from Floor
which gives the greatest integer no greater than the number you give it
and Arctan2 which reads two numerical values x and y and returns the angle
between the positive x-axis and the vector (x,y).
I’m afraid that mathematical expressions look a bit ugly when written
like this; for instance, what you might like to write as “sin(2(x+3))”
becomes
Sin Times 2 Plus $x 3
which I agree is nasty. Those used to LISP will probably find it a bit
more manageable than most...
There is also Random, which reads no more tokens and expands to a random
number between 0 (inclusive) and 1 (exclusive). Unless you are very unlucky
you will get a different sequence of random numbers every time you run
mkdrawf.
String operations
-----------------
It’s possible to do some things with strings, too. Two things, in fact.
Firstly, you can append them to form new strings. Append 3 "foo" "bar " "baz"
expands to "foobar baz". Secondly, you can feed them to the OS_GSTrans SWI,
to get variable substitution and things. (Try making a text object including
the line Text GSTrans "<Sys$Time>".)
Conversions
-----------
It’s sometimes useful to be able to convert strings to numbers, and vice
versa. There are three operators for doing this. Str2Num and Num2Str do just
what you would expect them to (Str2Num will give an error and a result of
zero if given a string that wouldn’t be recognised as a number by the
token-parsing routines). Font "foo" expands to the number of the font whose
name is "foo", if there is any such in the font table. If there is a font
table and it doesn’t contain a font called foo then the expansion is 0 and an
error message is given; if no font table at all has been given, mkdrawf will
insert the font name into an internal table and remember to build a font
table for you when it finishes.
Conditionals
------------
Expanding the token IfExists does the following: First it reads one token,
without expansion. If this is the name of a variable that exists (i.e., has
had a value assigned to it with Set or as a macro parameter), the next bunch
of tokens will be read from whatever follows it, until an EndIf or Else
token is seen. If an Else token is seen first, everything from there to the
matching EndIf will be skipped. On the other hand, if what it reads is not
the name of an existing variable, everything up to the matching EndIf or
Else is skipped.
This is easier than it sounds; the behaviour is just the same as that of
an If in BASIC or C or whatever. Tokens are not expanded while skipping;
they are expanded while not skipping. This means that if you define a macro
with unbalanced If and Else and EndIf things in it, you can cause chaos.
So don’t.
As well as IfExists, there are two other sorts of If: IfLess reads two
tokens, with expansion; they must be numbers, and it does what you think
it does; and IfEqual reads two tokens, with expansion, and again does just
what you think it does. If there’s a need for other sorts of conditional,
I can put them in pretty easily.
Iteration
---------
Finally, and most nastily of all, there is a For construct that behaves
a little like the similarly-named things in other languages. Remember,
though, that this works by macro-expansion; it doesn’t “compile” to a
block of stuff followed by a test and a conditional jump. All the testing
and jumping happens in the macro-expansion mechanism.
The syntax is
For variable initial limit { stuff }.
The variable, which may be local or global, is initially given the value
initial. Then stuff is spliced into the token stream. When the end of the
stuff is reached, the variable has its value increased by 1. If that value
is now less than limit, then stuff is spliced into the token stream again,
and it all repeats; otherwise we’re finished. Note that this has more the
semantics of C’s do loop than of its while loop: the stuff is always read
at least once.
Relevant facts: the variable is of course not expanded; the stuff is not
expanded before it’s spliced into the token stream, so it can refer to
variable and have that expand differently each time round the loop; it
is legal to change the value of variable within the stuff, and this behaves
in the way you would expect.
File inclusion
--------------
This is not really a “language feature”, but it seems to belong here more
than it does anywhere else. If your mkdrawf code contains a line like
Include "$.Zog.Wombat", the contents of that file will effectively be
included at that point in the input file. You can nest these inclusions;
mkdrawf can cope with having up to 17 input files open at once (although
the Shared C Library or the operating system may impose other restrictions).
This allows you to have a “header file” containing frequently used variable
and macro definitions, or to have a mostly-unchanging file with a few
variable bits read from other files.
I advise you, for reasons of clarity, to put each Include on a line by
itself. If you don’t, the precise behaviour is as follows: The remainder
of the line on which the Include (with its associated string) finishes is
read, and then input switches to the named file until that is exhausted.
You might also want to know that Include doesn’t do anything if it’s read at
a time when tokens aren’t being expanded. The most notable such case is when
the a macro definition is being read. So you can, if you must, have a macro
called Include (although that would stop you using the Include feature from
then onwards). And you can define a macro that includes a file every time it
is expanded; but you can’t read the contents of a file into the definition of
a macro.
Units
-----
It’s possible, as of version 3.07, to use units other than points. If mkdrawf
sees Units 123, it interprets this as “From now on, treat all dimensions as
being given in units each of which is 123 times the size of a point”. To make
this friendlier, mkdrawf predefines a few variables for you: for instance,
$Inches is 72, so that Units $Inches will allow you to write all your
dimensions in inches. The complete list of such variables is, in ascending
order of size: $ScaledPoints $OSUnits $Points $Millimetres $Centimetres
$Inches. (A scaled point is 1/640 of a point, the smallest possible dimension
in most contexts in a drawfile; an OS unit is 1/180 of an inch. More
precisely, Draw assumes that an OS unit is 1/180 of an inch, although this is
probably not true of your screen in your favourite screen mode.) The change
in treatment of dimensions happens when the Units token is expanded, just as
file inclusion happens when the Include token is expanded; so you can put
unit changes into a macro.
There is also a Unit token; this expands to a number which is the number of
points in the current unit — thus Units Unit is guaranteed to do nothing.
This can be useful if you want to change units temporarily.
Warning: changing the Units affects all dimensions, including things you
might not think of like dash patterns and widths of paths.
----------------------------------------------------------------------------
The drawfile description language III
=====================================
This chapter (finally!) describes what happens to the “simple” tokens
that are spat out by the macro-expansion stuff. Here the correspondence
between your input file and the final output file is extremely close.
After expansion, your input file should look like a succession of object
descriptions. An object description describes a single drawfile object;
when one object contains another, its object description will contain the
object description for that other object.
The format of an object description
-----------------------------------
An object description consists of a keyword naming the object type,
followed by a left brace, followed by some stuff describing the object,
followed by a right brace. The details of the “stuff” depend, of course,
on just what sort of object it is; in broad terms, though, it’s very much
like what goes into the parameter list for a macro invocation, but instead
of local variable names you put appropriate keywords. I shall refer to
the keyword/value pairs as items.
In what follows, a “complete” (in the sense of showing all the possible
keywords and the sort of values they take) description for each object
type is given.
Bounding boxes
--------------
Almost any object can contain an item of the form BoundingBox 0 0 100 200;
this overrides the bounding box mkdrawf would otherwise have given it. This
is not usually a good thing to do, since mkdrawf calculates its bounding
boxes very sensibly. Sprite and transformed sprite objects need a bounding
box (see below); font table and options objects have no bounding boxes. You
also can’t specify explicitly the bounding box of a text area object, because
that is absolutely determined, with no room for disagreement, by its text
columns.
The Header pseudo-object
------------------------
There is one sort of object description that doesn’t actually describe
a real object. Rather, it describes what goes into the header at the start
of the drawfile. Here’s a “complete” Header object description.
Header {
Version 201 0
Creator "Aardvark"
BoundingBox 0 0 500 500
}
The Version item takes two integers, the major and minor version numbers.
Changes to the major number indicate incompatible changes in the file format,
so you shouldn’t give a major number other than 201 unless you have also
modified mkdrawf to make it produce the new format. The default is 201 0.
The Creator item takes a string, which must be at most 12 characters long.
If it’s less than that, it will be padded with spaces. It’s usual to make
it no more than 8 characters. The default is mkdrawf2 (this is the second
version of the mkdrawf program; the first was much simpler and lacked
variables, macros and things).
The BoundingBox item takes four numbers; their significance was explained
two chapters ago. You don’t have to give this; generally mkdrawf does a
good job of working out bounding boxes. You might want to change it for
special effects, though, but you should be warned that applications are
perfectly within their rights to misbehave horribly if given dishonest
bounding boxes!
The FontTable object
--------------------
Here’s a complete FontTable object description.
FontTable {
1 "Trinity.Medium.Italic"
2 "Helvetica.Medium.Exploding.Strawberry"
3 "Corpus.Oblique"
}
Font number 0 is always the bitmapped system font; you should not try to
redefine it. Also, font numbers are only 1 byte long. In other words, the
numbers you give must be integers between 1 and 255, inclusive.
Incidentally, I have lost my only copy of the
Helvetica.Medium.Exploding.Strawberry font; if you know where I might
find another, please let me know.
If you don’t put a FontTable object in your drawfile description,
mkdrawf will construct one for you. However, if you do have a FontTable
it must contain all the fonts you use in Text and XfText objects.
The Text object
---------------
Here’s a complete Text object description.
Text {
Colour r0g0b0 # black
Background r255g255b255 # white; remember this is just a hint
Style 1 # in other words, font number 1
Size 10 12 # x-size 10 points, y-size 12 points
StartAt 100 100 # start at (100,100)
Text "This is a text object"
}
If you find Style unpleasant in this context, let me know. It’s what the
description in the PRM uses. It might be relevant that the “style” here is
a 4-byte word; one byte is used for the font number, and the rest is
reserved. So perhaps one day it will mean something other than just “font”...
You can specify the placing of the text in another way besides StartAt: if
you replace it with an item CentreIn 100 100 500 300 (say), then the text
will be placed so that its bounding box has its centre at the centre of that
box. Alternatively you can give both StartAt point and HCentreIn box; this
computes the horizontal position from the box and the vertical position from
the StartAt. Since these are horribly verbose, you can also say
CentreAt 300 200 or HCentreOn 100 500 200; the former means what you think
it does, the latter takes two x-coordinates and a y-coordinate.
As of version 3.08, when a Text or XfText object (we’ll meet XfText objects
later) is made, mkdrawf sets the variables $_x0, $_y0, $_x1, $_y1 to the
bounding box of the object it has just created. This makes it possible to
tell mkdrawf to put two pieces of text so that the gap between them is of
some desired size. It’s sometimes useful to be able to get at the bounding
box of a piece of text before you typeset it; version 3.08 also introduces
the Virtual keyword which allows you to do this. A Text or XfText object
which is Virtual doesn’t actually get put into the drawfile. The bounding box
variables, however, do get set. Thus you can find out the vital statistics of
a text object before you actually commit yourself to putting it in a
particular place. One other thing about Text and XfText objects has changed
in 3.08: most attributes of a piece of text now default to whatever values
they had for the last Text/XfText object (even if it was virtual). This
should save typing.
The Path object
---------------
This one is rather complicated; glance at the description in the drawfile
summary earlier to see why! Anyway, here’s a complete Path object
description.
Path {
FillColour None
OutlineColour r0g0b255
Width 1 # in points. 0 means as thin as possible
Style {
Mitred # join style. Options: Mitred, Round, Bevelled
EndCap Round # Options: Butt, Round, Square, Triangular
StartCap Triangular
WindingRule NonZero # determines how filling is done. Alternative: EvenOdd
CapWidth 32 # for triangular caps only.
# 0..255, in 1/16ths of the line width
CapLength 64 # ditto
Dash {
1 3 2 5 3 # if you have no idea what this means, experiment!
Offset 2 } # ditto
}
# Now the path itself
Move 100 100
Line 200 100
Curve 250 100 200 300 400 400 # Bezier curve. Two control points, endpoint
Close # this subpath
Move 200 200
Line 300 200
Move 500 500 # this begins a new subpath too,
# even though no Close before it
Line 0 0
}
If the Style item specifies a dash pattern, it must come before any path
elements. This is a consequence of the way in which the information is
arranged in the drawfile: the dash pattern, if present, comes before the
path elements, and it’s of variable size.
There are also “relative motion” keywords RMove, RLine and RCurve. These
are just like the R-less versions, except that the numbers they mention are
relative to the last point visited: thus they behave exactly like the
similarly-named PostScript operators.
The Sprite object
-----------------
I’m afraid the way you specify a sprite is pretty nasty; this is more or
less inevitable, but it’s possibly nastier than it ought to be. A sprite
object description contains a BoundingBox item (like that in the drawfile
header), and a load of numbers. I strongly advise you to specify the numbers
in hexadecimal; don’t forget that if you do this the 0x on the front is not
optional!
The bounding box is not optional. It specifies where the sprite is to be
placed, and how big it is to be.
The numbers are simply the words that will be placed into the file, in
order. See the PRMs for details of the sprite format.
Here’s an example.
Sprite {
BoundingBox 100 100 200 200
0x000002D4 0x745F6369 0x6C706D65 0x00006465
several lines of hexadecimal rubbish deleted
0x00000077
}
As of version 2.20, there is another way of specifying a sprite: the
BoundingBox item is still needed, but instead of the load of numbers
you write FromFile followed by two strings. The first should be the name of a
sprite file, and the second the name of a sprite in that file. The resulting
behaviour should be obvious. I recommend that you use the old method where
possible, because this keeps all the information necessary to recreate the
drawfile in one place.
The Group object
----------------
Here’s a typical Group object description.
Group {
Name "ThisIsAGroup" # maximum 12 characters here
Text {
Size 10 10
StartAt 100 100
Style 2
Text "Foo"
}
Group { # a group may contain another group
Path { # All the path style options have sensible defaults
Move 100 100
Line 200 100
}
Path {
FillColour r200g200b200
Move 200 200
Line 300 300
Line 100 300
Close
}
} # a group doesn't need to be given a name
}
The Tagged object
-----------------
A tagged object, remember, consists of a 4-byte tag identifier, an object,
and some other (optional, word-aligned) data. Here’s what it looks like:
Tagged {
Identifier 0x12345678
Text {
blah blah blah
}
OtherData 0x9ABCDEF0
OtherData 0x11111111
OtherData 12345 # this is decimal 12345
}
Strictly speaking, you should register tag identifiers with Acorn. Unless
you’re planning to use them in a commercial product, there’s not much need
to bother, since they aren’t used much. Just pick a random number and hope
it doesn’t clash with anything.
The TextArea object
-------------------
Here’s a fairly complete text area object description. The only
incompleteness is in the fact that there should really be some control
sequences in the text.
TextArea {
Column 100 400 200 500
Column 220 400 320 500
Colour r0g0b0
Background r255g255b255
Text {
Really there should be some escape sequences at the start of this; too
bad. This text will be formatted in the 2 columns specified at the
start of the object description, or would be if the escape sequences
were there.
}
}
Note that the text is not quoted. The processing of text in a text area is
an exception to the way in which input is processed: it is just read
character by character, and terminated by the first line containing nothing
other than a closing brace and whitespace. The extra space at the starts of
lines in the example above isn’t a problem, because multiple whitespace
characters are treated as single spaces in text areas (unless there’s more
than one consecutive newline; see later).
This unusual processing has certain unfortunate consequences: you cannot get
anything into the text of a text area using macros or variables, and
comments don’t work. Also, anything following the open-brace token after
Text and on the same line is simply ignored.
All the columns must come before the text.
Silly implementation restriction (my fault, not Acorn’s): you may have
at most 16 columns. If you need more, it’s a trivial matter to change the
program to allow more.
Multiple spaces and tabs are treated as single spaces. A single newline is
treated as a single space (including absorbtion into multiple spaces);
extra newlines insert extra vertical space (for paragraphs). Newlines
occurring before any printing text are considered to be multiple newlines
for this purpose.
Here’s a list of escape sequences. Each starts with a backslash; the escape
sequences themselves are case-sensitive, but their arguments aren’t. Any
argument may be terminated with a /, and variable-length ones must be
(except that a newline will do instead). Dimensions should be given in
points.
\! <version number> This must appear at the start of the text, and may
not appear anywhere else. The version number must be
1, and must be followed by / or a newline.
\A<code> Determines alignment. <code> is L, R, C or D, indicating
left, right, centred or double (i.e., L and R). Default is L.
\B<colour> Set background colour hint for following text. <colour> might
be 200 3 150; range is 0..255, at least one space is needed
between successive numbers, and a / or newline is needed at
the end.
\C<colour> Set foreground colour for following text.
\D<number> Indicates number of columns. This must be given, before any
printing text, unless the number is 1.
\F<fontspec> Defines a font reference number. Typical use:
\F 1 Trinity.Medium 12
means number 1 is Trinity Medium, 12 point. Font numbers
are 1 or 2 digits, and are entirely separate from the
numbers used for text objects. You may specify a font width
as well: \F 1 <name> 12 10 means 12pt high, 10pt wide. Don’t
forget the slash or newline. (This last goes for \C, \D, and
most other escape sequences. I shall now stop mentioning it.)
\<number> Means “now use font number ...”.
\L<leading> Defines the leading; i.e., the vertical distance between
baselines of successive lines of text. The default is 10pt;
specify it in points.
\M<lt><spaces><rt> Margin sizes; must be positive; default is 1 (point).
\P<leading> Paragraph leading; i.e., the amount of extra space left from
each newline after the first in any consecutive sequence.
Default is 10pt.
\U<ulspec> Switches on underlining. \U 10 2 means 10 units above (or
is it below?) the baseline, 2 units thick. One unit is 1/256
of the font size. Ranges -128..127, 0..255 respectively.
Switch off either by giving a thickness of 0 or by using
“\U.”.
\V[-]<digit> Vertical move. In points.
\- Allow a line break here, inserting a hyphen if one happens
(and nothing otherwise).
\<newline> Force a line break here.
\; Everything from here to next newline is ignored.
Note: there is no such thing as a TextColumn object descriptor. Text
column objects only occur inside text area objects, and are produced
automatically by mkdrawf.
The Options object
------------------
Here’s an options object description illustrating just about everything.
Almost all of this is specific information for !Draw telling it how to
behave when it has the file loaded.
Options {
PaperSize 4 # meaning: A4
Limits {
Shown # not sure what this does. Omit to negate
Landscape # Omit for portrait
NonDefault # not sure what this does. Omit to negate
}
Grid {
Spacing 1.2345 # cm, unless "Inches" given; see below
Divisions 5
Isometric # omit for rectangular
AutoAdjust # Omit to turn this off
Shown # Omit if you don't want grid visible
Lock # Omit if you don't want grid lock on
Inches # Omit for centimetres
}
Zoom {
Ratio 3 2 # means 3:2. Both numbers should be in range 1..8
Lock # to powers of 2. !Draw seems to ignore it anyway
}
NoToolbox # omit if you want toolbox shown when file loaded
Mode Line # initial entry mode. Possibilities are
# Line,ClosedLine,Curve,ClosedCurve,Rectangle,
# Ellipse,Text,Select.
UndoSize 1234 # size of undo buffer, in bytes
}
The XfText object
-----------------
That is, transformed text. An XfText object description is just like a
Text object description, but it has up to three extra items. The most
important is one that looks like
Matrix 0.5 1 2 -0.1 30 0
which means that the text should be transformed by the 2x2 matrix given as
the first 4 numbers, and then translated by the vector given as the last 2.
To be more precise, the transformation done is
(x y) |--> (x y) / 0.5 1 \ + (30 0) .
| |
\ 2 -0.1 /
The other two items available are “flags”: Kerned and RightToLeft. You can
probably work out what they do. Incidentally, I think text in text objects
is never kerned, and text in text area objects is always kerned.
The position of the text in an XfText object may be specified using CentreIn
or CentreAt; as of version 3.09 you can even use HCentreIn or HCentreOn, but
the results aren't guaranteed to be sensible if the Matrix is non-trivial.
The XfSprite object
-------------------
An XfSprite object is just like a sprite object except that it can (and
should) also contain a transformation matrix, exactly like those used for
XfText objects. The sprite itself can (as for sprite objects) be given
either directly or in terms of a sprite file and a sprite name.
The placing and size of a transformed sprite are not determined by the
bounding box, but by the transformation matrix and the contents of the
sprite itself. However, mkdrawf doesn’t understand sprites (it just treats
a sprite as a lump of data), so it can’t make any sort of attempt at
computing a bounding box. So give it one.
For the record, an identity matrix with zero translation puts the
sprite’s bottom-left corner at (0,0) and its top right corner at (w.ex,h.ey)
where w,h are the width and height of the sprite in pixels and ex,ey are
the “eigenfactors” for the mode in which the sprite was defined.
The JPEG object
---------------
A JPEG object is in several ways rather like a transformed sprite object.
Here is a “complete” JPEG object description:
JPEG {
BoundingBox 100 100 300 200 # this is needed
Size 200 100 # and so is this; I'm not sure why
DPI 90 45 # in x,y directions. Default is 90,90
Matrix 2 0 0 1 100 0 # Default is identity, of course
FromFile "a_jpeg"
}
You must specify the Size; I’m not sure exactly what it means and how it
relates to the BoundingBox; I don’t have anything capable of displaying
drawfiles containing JPEG objects, so your guess is as good as mine. Instead
of giving a filename, you can in principle include the contents of the
JPEG file in the object description (as with sprites and transformed sprites),
but JPEGs are usually big enough that you really don’t want to do this. If
you do do this, you need to provide an item like
Length 12345
giving the exact length of the JPEG image in bytes; mkdrawf will not try to
guess from the number of integers you give, because the size of a JPEG image
need not be a whole number of words.
A reminder: no current version of !Draw understands JPEG image objects.
----------------------------------------------------------------------------
Some sample drawfile descriptions
=================================
Here are some examples of what can be done with mkdrawf. Only the input
files are given below, for obvious reasons; the results are in the “Examples”
directory.
The Koch snowflake curve
------------------------
The fractal everybody knows. Dead easy. This takes just under 8.4 seconds
on my machine, by the way.
# Snowflake curve
Set $r3/2 Over Sqrt 3 2
# Side { %x0 .. %y0 .. %x1 .. %y1 .. %n .. }
# this should be inserted into a Path
# The assumption is that we’re
# already "at" (x0,y0).
Define Side {
IfLess %n 1
Line %x1 %y1
Else
Set %dx Over Minus %x1 %x0 3
Set %dy Over Minus %y1 %y0 3
Set %xa Plus %x0 %dx Set %ya Plus %y0 %dy
Set %xb Minus %x1 %dx Set %yb Minus %y1 %dy
Set %xh Over Plus %x0 %x1 2 Set %yh Over Plus %y0 %y1 2
Set %xt Plus %xh Times %dy $r3/2
Set %yt Minus %yh Times %dx $r3/2
Set %m Minus %n 1
Side { %x0 %x0 %y0 %y0 %x1 %xa %y1 %ya %n %m }
Side { %x0 %xa %y0 %ya %x1 %xt %y1 %yt %n %m }
Side { %x0 %xt %y0 %yt %x1 %xb %y1 %yb %n %m }
Side { %x0 %xb %y0 %yb %x1 %x1 %y1 %y1 %n %m }
EndIf
}
Set $x0 100 Set $y0 100
Set $x1 400 Set $y1 100
Set $x2 250 Set $y2 Plus 100 Times 300 $r3/2
Path {
Move $x0 $y0
Side { %x0 $x0 %y0 $y0 %x1 $x1 %y1 $y1 %n 5 }
Side { %x0 $x1 %y0 $y1 %x1 $x2 %y1 $y2 %n 5 }
Side { %x0 $x2 %y0 $y2 %x1 $x0 %y1 $y0 %n 5 }
Close # not really needed unless you want to fill it
}
A Lissajous figure
------------------
You might have produced things slightly like this at school by connecting
a couple of signal generators to an oscilloscope. (Except I bet it didn’t
do the pretty chequered pattern.) The drawfile this produces could certainly
be made smaller by using Bezier curves instead of straight lines, but it’s
not very big anyway. mkdrawf takes about 3.1 seconds to process this file on
my machine.
Set $Pi 3.141592653589793
Set $Factor Over $Pi 400
Path {
Fillcolour r255g0b0
Style { WindingRule EvenOdd }
For $i 0 801 {
Set $j Times $i $Factor
Set $p Cos Times 9 $j
Set $q Sin Times 13 $j
IfEqual $i 0 Move Else Line EndIf
Plus 400 Times $p 300
Plus 400 Times $q 300
}
Close
}
A typical text area
-------------------
This drawfile contains nothing but a text area. I apologise for the inanity
of the text in it; this was typed in at random one evening while I was
testing an earlier version of the program, and I just wanted a certain
amount of text.
TextArea {
Column 100 400 200 500
Column 220 400 320 500
Colour r0g0b0
Text {
\! 1
\F 1 Trinity.Medium.Italic 12
\F 2 Trinity.Medium 12
\1
\AD
\D2
\L12
This is some text I'm putting in a text area.
I have no idea how it will look, nor indeed whether
it will work at all. For all I know \2 mkdrawf\1 will
just choke utterly on it, or corrupt my file, or
cause demons to fly out of the monitor.
This should be a new paragraph; it will still be in italics.
\2 Now we should be in roman type. (Isn't this fun, boys
and girls?)
Apparently the 1998 World Cup will be decided, in the event
of a draw, by a sudden-death playoff instead of by a penalty
shootout. How interesting.
}
}
Circular arcs using Bezier curves
---------------------------------
Here’s a useful macro you may want to use in your own mkdrawf code. It
produces (an approximation to) an arc of a circle using Bezier curves. I
don’t know whether the particular approximation it uses is the same as the
one used in !Draw, but it gives good results. This takes about 0.5 seconds
on my machine, by the way.
# %x0,%y0 is centre
# %r is radius
# %a0,%a1 are starting and ending angles in radians
# %n if set, indicates how many arcs to use
Define Arc {
Set %da Minus %a1 %a0
IfExists %n Else
Set %n Floor Plus .99 Over %da .75
EndIf
Set %da Over %da %n
Set %t Over Minus 1 Cos Over %da 2
Times .75 Sin Over %da 2
Move Plus %x0 Times %r Cos %a0
Plus %y0 Times %r Sin %a0
Set %aa %a0
Set %cc Cos %aa
Set %ss Sin %aa
For %k 0 %n {
Set %a %aa
Set %aa Plus %a %da
Set %c %cc Set %s %ss
Set %cc Cos %aa Set %ss Sin %aa
Curve Plus %x0 Times %r Minus %c Times %t %s
Plus %y0 Times %r Plus %s Times %t %c
Plus %x0 Times %r Plus %cc Times %t %ss
Plus %y0 Times %r Minus %ss Times %t %cc
Plus %x0 Times %r %cc
Plus %y0 Times %r %ss
}
}
Set $Pi 3.141592653589793
Path {
Arc { %x0 300 %y0 300 %r 200 %a0 0 %a1 Times 2 $Pi %n 2 }
# very inaccurate: only 2 arcs
Close
}
Path {
Arc { %x0 300 %y0 300 %r 200 %a0 0 %a1 Times 2 $Pi }
# I think this does 9 arcs
Close
}
A silly spiral
--------------
I first drew pictures like this on an old Hewlett-Packard desktop computer,
back in 1982 or so. I still think they rate quite highly for
prettiness-to-effort ratio. This is really an excuse for demonstrating
RLine. Oh, and it takes about 2s.
Set $2Pi 6.2831853071795864769
Set $Conv Over $2Pi 360
Define Spiral {
Set %theta 0
Set %r %dr
For %i 0 %n {
RLine Times %r Cos Times $Conv %theta
Times %r Sin Times $Conv %theta
Set %r Plus %r %dr
Set %theta Plus %theta %dt
IfLess 360 %theta
Set %theta Minus %theta 360
EndIf
}
}
Path {
Outlinecolour r0g0b0 Width 0
Move 250 250
Spiral { %dr .5 %dt 65 %n 500 }
}
----------------------------------------------------------------------------
Tagfiles
========
NOTE: This describes an optional feature of mkdrawf, which is not included in
the ready-compiled version of mkdrawf distributed with this manual. You’ll
have to recompile if you want to use it. This is because I expect most people
not to need it — it was added because one particular user asked for it. If
you don’t use it, why should it take up space in your executable? To make a
version of mkdrawf with tagfiles enabled, compile mkdrawf.c with the
preprocessor symbol TAGS defined.
Suppose you have a mkdrawf “script” which gets used repeatedly, but with
slight changes made to some of the strings. (If you don’t, you don’t need
tagfiles.) You could go through and change the file every time, but that
would be tedious. You could have the strings put into variables by an
Included file, and use different files on different occasions, but that would
be a waste of variables. The tagfile feature provides an alternative.
A tagfile consists of a number of lines, each containing a tag and an
associated value. There is a mkdrawf operator which takes a string, looks for a
matching tag and replaces it with the corresponding value. There are also
mkdrawf “specials” for opning and closing tagfiles. This is getting terribly
abstract: here’s a concrete example.
A tagfile:
One:un
Two:deux
# This is a comment; it will be ignored.
Saturday:samedi # ceci n'est pas une remarque
ThoughtPolice:Academie Francaise
A mkdrawf file:
TagOpen "tagfile"
...
Text { ...
Text Append 3 "The " TagLookup "ThoughtPolice:FBI" " will get you!"
}
...
TagClose
This will behave exactly as if you’d had instead a line saying
Text "The Academie Francaise will get you!".
If your tagfile contained a line
ThoughtPolice:Child Support Agency
instead of the one above, then the effect would be as if the Text item was
Text "The Child Support Agency will get you!".
If it didn’t contain any ThoughtPolice line at all, you’d get
Text "The FBI will get you!".
You are allowed to miss off the default (“:FBI” above) in a TagLookup; if the
tag is not found you will get an error message and the expansion of the
TagLookup will be the empty string.
You may only have one tagfile “open” at a time. (In fact it is only really
open while the TagOpen is doing its work; its contents are read into memory,
and TagClose means “free the memory used for tagfile data”.) Its lines are,
in effect, searched in order when looking for a tag.
The similarity between tagfiles and RISC OS message files is deliberate.
However, tagfiles don’t provide any of the fancy wildcarding features
provided by MessageTrans. They may do in the future.
If you put “-t foo” on the mkdrawf command line, the effect will be as if the
very first line of the file had been TagOpen "foo". This can be useful if you
want to run something with several different tagfiles without changing the
mkdrawf file itself; for instance, in makefiles.
----------------------------------------------------------------------------
Technical issues
================
If you’re interested in mkdrawf, you’re quite likely to be interested in
its internals. (Wimps use !Draw. Real Men use mkdrawf. Or something.) So
here is some information about how it works, with particular emphasis on
things that need improving. (After all, *you* might be able to make it
better...)
Memory management
-----------------
Oh dear.
This is a real mess, basically because it’s seldom trivial to know when
a piece of memory is finished with and can be thrown away. Every time a
string is created, a new block of memory is malloced; these blocks are
never freed. (Once upon a time this was true for numbers as well!) There
are some places in the program where we could free things, but there are
so many memory leaks that it is probably more sensible to wait until I
have the time and energy, and fix it all properly.
The real problem is that there is no guarantee that any particular token
will not get included in a token list. If that happens, we can’t afford
to throw it away. So we can’t, for instance, always free the memory used
by an old variable value. One solution to this problem would be to make
sure that we always copy tokens when we use them, but this would be terribly
time-inefficient. The best solution is probably to implement a
garbage-collector. Hmmm.
Fortunately, most drawfiles are quite small; and the amount of memory that
gets wasted at any one time is also small. There’s only likely to be a
practical problem here if you do complicated recursive things. At the
moment, that snowflake curve program needs about 280k (the mkdrawf program
itself being 94k), which I feel is rather excessive. I’ll work on it.
Variables
---------
... are stored in a hash table. The way in which all this works has changed
dramatically between version 2 and version 3; in version 2 there was a fairly
big global hash table and each macro invocation had its own local hash table,
whereas now there is a very big global hash table (which, by the way,
accounts for the increase in size since version 2) and local variables are
saved and restored as needed.
The hash scheme used is called the “method of coalescing lists”. I stole it
from Knuth, and stole his choices of hashing parameters too. In version 2 of
mkdrawf there were two sorts of local variable token, “resolved” and
“unresolved”. This distinction no longer exists; in effect, all variables are
now resolved.
Problems
--------
This is quite a complicated program, and I’m not a very reliable programmer.
There are bound to be a few bugs left, though I think I’d have spotted
anything really big. Fortunately, I am both a fast bug-fixer and a helpful
person; so if you spot any bugs and let me know, I can probably let you have
a fixed version of mkdrawf pretty quickly.
Similar remarks apply if you have features to suggest. Of course I don’t
guarantee to implement every (or any!) feature that is suggested to me, but
I am always open to suggestions.
Arbitrary restrictions
----------------------
The following aren’t bugs, but do represent limitations of which you should
be aware. I don’t guarantee robustness in the event of your violating
these...
• Token lists cannot be nested more than 256 deep; nor (independently)
can macros; nor (independently) can Fors; nor (independently) can Ifs.
• There must not be more than 2100 global things-with-names. That means
• keywords and specials (there are about 100 of these);
• global variables;
• (names of) local variables;
• macros.
Before version 3 of mkdrawf, there was a limit of 677 global things
and 61 local variables per macro.
• A text area may not have more than 16 columns.
• The drawfile produced by mkdrawf must be at most 4 megabytes long.
• A macro may not use more than 9 positional parameters.
• Include files cannot be nested more than 17 deep.
----------------------------------------------------------------------------
Differences from the previous version of mkdrawf
================================================
An earlier version of this program was distributed to a small number of
people. I have no idea whether it spread any further. The version described
in this manual is very considerably superior, and if you have been using
the old version you should change to the new one.
Unfortunately the two programs are not entirely compatible. They are
deliberately very similar, but there have had to be a few incompatible
changes. I think I’ve described them all in this chapter.
Changes since the transition to version 2 of mkdrawf are documented later,
in the section entitled “Revision history”.
Points don’t have commas
------------------------
In the older version, points were entered as (for instance) 200,100. In
this version the comma is replaced by whitespace. This change was made to
make it possible for points to be computed using arithmetic and similar
operations; in the older version of mkdrawf a point was a single token, but
now each coordinate can be produced separately.
The same applies to transformation matrices and things, of course.
As I’ve already mentioned, a similar change may befall colours in a later
version of mkdrawf.
Strings have quotation marks
----------------------------
In the older version, a text item might have contained a line like
Text This` is` a` zog
where the backquotes indicated that the spaces following them were to be
part of the string. On reflection, I decided that this was a dreadful idea,
especially as I didn’t want to have to make the textual form of each token
part of the token. (Suppose you wanted a text item whose text was “$foo” or
something.) So now that would be replaced by
Text "This is a zog"
which, to my mind, is cleaner.
The same difference affects other strings, such as group names and font names.
No implicit hexadecimal
-----------------------
In the older version, the OtherData in a tagged object were assumed to be given
in hexadecimal. Now if you want hex you must explicitly mark it as such with 0x.
----------------------------------------------------------------------------
Revision history
================
mkdrawf has been changed a number of times, and will doubtless change more
in the future. In case you’re curious about what has happened to it (or
want to see what’s changed since the last version you saw), here is a
brief account of the changes introduced with each new version. Not all
of these versions actually correspond to public releases of mkdrawf.
2.00 First “new” version of mkdrawf; many incompatible changes in
input format; programming features added; manual written.
2.10 Sprites and transformed sprites added.
2.20 Sprites and transformed sprites can be loaded from sprite files;
Include feature; much improved error handling; several minor code
changes.
2.30 Bounding boxes of paths computed using Draw_ProcessPath instead of
by computing bbox of points on path; optional tagfile features;
Append, GSTrans features; almost all objects can have explicit
BoundingBoxes; numerous bugfixes; code made rather cleaner;
decdrawf -s and -v.
2.35 Font, Str2Num, Num2Str, HCentreIn, CentreIn, Random features; implicit
font tables; colours as triples of integers; JPEG objects; several
minor bugfixes and code changes; tutorial written; !Drawf written,
and some minor changes made to mkdrawf to support it.
3.00 Complete rewrite of macro-expansion code, improving speed and
memory use and fixing a serious bug with nested macros. General
reorganisation of the source code (now in several files).
----------------------------------------------------------------------------
Decoding drawfiles
==================
With mkdrawf comes another program, called decdrawf, which turns drawfiles
into descriptions of the sort understood by mkdrawf. This is useful if
you’ve lost the mkdrawf source code for a drawfile, or if you want to sketch
something roughly and then tweak it so that all the points are in just the
right places.
Using decdrawf
--------------
... is trivial, even easier than using mkdrawf. Just type, say,
decdrawf foo
to get the contents of the drawfile named foo displayed to the screen.
If you want the output sent to a file, type
decdrawf foo > bar
and the output will be put in a file called bar.
As with mkdrawf, you can say
decdrawf -v
to get a version number (which will keep in step with the version number of
mkdrawf, even though one program may change without any change in the other).
More importantly, you can also request that decdrawf put sprites in a separate
sprite file rather than “inline” in hexadecimal, by putting -s <filename>
on the command line. If there are no sprites in the drawfile, the sprite file
will not be created.
Warning: A drawfile may contain several copies of one sprite, or (worse)
several different sprites with the same name. decdrawf will not output
multiple copies of duplicated sprites to a sprite file; where different
sprites have the same name, decdrawf will write them all to the sprite file
as they occur, but will emit warning messages indicating that this is
happening.
The output from decdrawf
------------------------
is a file suitable for feeding into mkdrawf. It doesn’t use any macros,
variables etc, of course. Here and there in the output are comments, which
are intended to make clearer the correspondence between the data in the
drawfile and its textual description, or (in, for instance, the case of the
Options object) to remind you what alterations you could make.
If you give the -s <filename> option to decdrawf, you will (if there actually
are any sprites in the drawfile) get two output files. If you change the
name of the sprite file, you will need to change the relevant FromFiles
before feeding the result back to mkdrawf. Also, when you use -s you are
not actually guaranteed that feeding the output into mkdrawf will produce
the right results; see the warning above.
If there are JPEG image objects in a drawfile fed to decdrawf it will by
default include them (in hexadecimal) in the decoded drawfile. This is
inefficient, and JPEG images are usually rather large; so, instead, put
-j <prefix> on the command line; this will make decdrawf put JPEG images in
separate files named prefix01 and so on. decdrawf makes no attempt to avoid
owerwriting existing files; one consequence of this is that you should be
careful of truncation problems if a drawfile contains several JPEG images.
As with mkdrawf, you can get decdrawf not to tell you the filename it’s
working on in warnings and errors by giving it the option -e.
You can make decdrawf produce output in terms of units other than points,
by putting -u <unit> on the command-line. Here unit is one of sp, os, pt,
mm, cm, in (meaning scaled points, OS units, points, millimetres, centimetres,
inches). The short names are used here to save typing; the long ones are
used by mkdrawf to avoid clashing with potentially useful short variable
names.