RFC 493

GRAPHICS PROTOCOL

๐Ÿ“… published April 1973, from ietf

GRAPHICS PROTOCOL ๐Ÿ’–

okay bestie, let's get into RFC 493 ๐ŸŽ€ It's a Network Working Group doc, and the authors are J. Michener (MAC), I. Cotton (MITRE), K. Kelley (U. of Ill.), D. Liddle (Ownes Ill.), and E. Meyer (MAC). The NIC number is 15358, it references RFC 282 and RFC 258, and it obsoletes RFC 292 ๐Ÿซถ The header doesn't list a category or a date, so I'm not making one up ๐Ÿ™ˆ

Introduction โœจ

so this doc is basically the opinions and decisions from the second meeting of the Network Graphics Group, which happened at the Stanford Artificial Intelligence Laboratory in late November 1971 ๐Ÿ“ It describes part of a proposed Network Standard Graphics Protocol for sending graphics data over the ARPA network. The main focus is the stuff graphics info looks like when it goes from a source (like an application program in the "Serving Host") to a display package that outputs to a graphics console at the "Using Host." It's a sequence of 8-bit bytes, and it's called the graphics output byte stream ๐Ÿ’…

this doc is meant to be a starting point for discussion and experiments over the network ๐Ÿงš It does not cover the form or content of graphics input (data going from the Using Host to the Serving Host), and it doesn't cover how the connection between the hosts gets set up either. The committee will eventually make a proposal for the input side, and the Connection Committee (of the Network Graphics Group) handles the connection stuff ๐Ÿซถ

this RFC describes the commands in the protocol by what they'd do on the receiving end, aka the Using Host ๐Ÿฆ‹ Some kind of subroutine package would be super useful on the Serving Host for apps sending graphics data, but this RFC is not going to comment on that ๐Ÿคทโ€โ™€๏ธ

also, you might notice there's no way for the Using Host to report logical errors in the graphics output byte stream back to the Serving Host ๐Ÿ‘€ That kind of facility would have to be built into the graphics input byte stream, because it's tangled up with most of the problems around synchronizing independent hosts. Lowkey a whole mess ๐Ÿ’€

Background ๐ŸŽ€

bestie, go read RFC 282, "Graphics Meeting Report" by Mike Padlipsky. It gives you the framework around this whole network graphics conversation ๐Ÿ“š It's also worth checking out the model in RFC 285, "Network Graphics" by Donald Huff ๐Ÿ’œ

Levels and Ground Rules Pertaining Thereto ๐Ÿ’…

okay so the functions in the graphics protocol get sorted into a bunch of levels, partly based on how hard they are to implement ๐ŸŒˆ Any host that says it implements the functions of level N has to implement all the lower levels too. So sites are expected to build things up incrementally ๐Ÿฐ Implementations keep getting better as they add more and more functions, and each one should be able to tell a remote site that's requesting a graphics exchange which level it's at โœจ A nice side effect is that each site can decide its own priorities for putting programmers on graphics protocol work versus other projects ๐Ÿ’–

the other thing is that implementing level N doesn't need any knowledge of level N+1 ๐Ÿง  So a site can implement a level knowing (pretty firmly) that changes at higher levels won't mess with the level it already built. The Network Graphics Group could decide to redefine a level that's already been firmed up, and that's not the plan, but ngl the protocol is experimental and might have to change ๐Ÿ™ˆ

one more ground rule: a stream of commands and data that's valid at level K has to produce "identical" results on any interpreter at level K or higher ๐Ÿ”ฅ What they mean is that as long as the commands and data only use strictly defined operations, you should get similar pictures. The stuff that isn't strictly defined yet includes character size, where a character sits relative to the beam, how control characters in text output affect the terminal, and what happens when the beam moves or a line gets drawn outside the logical screen boundary ๐Ÿ‘‘ This rule is what keeps things upward compatible, so an app written with features from a low-numbered level still works at sites that have moved on to higher levels ๐Ÿฆ‹ And anything that's "left unspecified" in the detailed operations descriptions below has to be explicitly specified in any public description of an actual implementation โœจ

we're about to describe the framework that every level shares ๐Ÿซถ

Basic Data Forms ๐Ÿฆ‹

okay, the Network Standard Graphics Protocol expresses everything as a sequence of 8-bit bytes ๐Ÿ’– A command is a command byte followed by zero or more arguments. The same command byte always takes the same number of arguments in the same form, and each argument's length can be fixed or variable depending on the argument ๐ŸŽ€

there are two kinds of arguments. The simple one is a "value," which is an 8-bit integer ๐Ÿ“ The other is a "string," which is a count followed by that many 8-bit bytes โœจ If the count is between 0 and 127, it's sent in a single byte. If it's between 128 and 2**15-1 (where ** means exponentiation), it's sent in two bytes, with the high-order bit of the first byte set to one ๐ŸŒธ The first byte carries the seven high-order bits of the number, and the second byte carries the eight low-order bits ๐Ÿงš A string is the only argument type that can vary in length ๐Ÿ’… So whenever a command has optional arguments, they get represented inside a string ๐Ÿ’œ

coordinate data caused a LOT of discussion at the second Network Graphics Group meeting ๐Ÿ˜ญ They decided on a two-dimensional Logical Coordinate System, and each interpreter of the graphic command byte stream is responsible for mapping that onto physical device coordinates ๐ŸŒˆ The logical coordinate data is in twos-complement notation, it's fractional, each edge of the screen has unit length, and the origin is the center of the screen on the output device ๐Ÿ‘‘

the vertical and horizontal edges of the output device's screen line up with X = -1/2 or X = +1/2-e (and the same for Y), where e is a small positive number set by how precise the fractional data is ๐Ÿ”ฅ The points (-1/2, -1/2), (-1/2, 1/2-e), (1/2-e, -1/2), and (1/2-e, 1/2-e) are explicitly meant to be visible points at the corners of the logical screen โœจ For a non-square display surface, the implementer can make their own call ๐Ÿคทโ€โ™€๏ธ So the Logical Coordinate System has points with coordinates going from -1/2 up to a little less than +1/2 ๐Ÿฆ‹

coordinate commands come in different modes ๐Ÿซถ In absolute mode, a position is given by its coordinates in the Logical Coordinate System. In relative mode, you specify the difference between the position's coordinates and the current position's coordinates ๐Ÿ’– So a coordinate datum that's an argument for an absolute mode operation should fall in the range -1/2 to +1/2-e, and one for a relative mode operation should fall in the range -1+e to +1-e ๐ŸŽ€

people at the second Graphics Group Meeting were interested in eventually allowing a very large coordinate space, with many bits of precision in each fractional coordinate ๐Ÿ“ The plan is to let the length of each coordinate datum, in 8-bit bytes, be set as a mode โœจ At the meeting, they decided two bytes per coordinate would be enough for now ๐ŸŒธ So e in the discussion above is 2**(-15), which is one in the least significant bit of a 15-bit plus sign fractional coordinate ๐Ÿงš

text data gets sent as an argument to various commands so it can be displayed on the output device ๐Ÿ’… Network ASCII is used to represent characters ๐Ÿ’œ At the lowest levels of the protocol, there's only one character size, whatever is "normal" on the display device ๐ŸŒˆ If the device has no "normal" size, 72 characters per line would be the desirable choice ๐Ÿ‘‘ At higher levels, you can specify the size of each individual character ๐Ÿ”ฅ

also, at the lowest levels, control characters get passed along to the device so it can do the best it can with them ๐Ÿ˜ญ BUT the consensus from the graphics meeting was that at some reasonably low (but non-zero) level, carriage return, line feed, and backspace should be interpreted to do the right thing ๐Ÿ™ˆ โœจ

Okay so at the second Network Graphics Group meeting ๐Ÿ’–, everyone agreed there should be two sorts of picture subroutines ๐ŸŽ€. The big difference between them is how hard they are to implement ๐Ÿ› ๏ธ. The simpler one got called a subpicture, and the fancier one got called a subroutine. This author thinks those names don't carry enough meaning to keep the two straight, so they propose standardizing on "simple subroutine" and "full subpicture" instead ๐Ÿฆ‹.

The only parameter a simple subpicture can take is the "current beam position" โœจ. So if you call it more than once in a picture, the only thing that changes between calls is a translation based on where the beam was when you called it ๐ŸŒธ. Full subpictures, though? They take parameters that can cause scaling, rotation, reflection, or literally anything else we come up with ๐Ÿ‘‘.

It's planned that a subpicture definition only needs to be sent once (per network connection) ๐Ÿ’œ, and a "new picture" operation won't delete it ๐Ÿงš. That means a changing picture can be split into parts based on static versus changing info, and only the parts that change need to be resent to redraw it ๐Ÿซถ.

Traditionally, picture subroutines that depend only on the initial beam position have been limited to relative data mode drawing operations ๐Ÿ™ˆ. Since subpictures will probably be used to save static picture info, it's desirable to allow absolute data mode operations in simple subpictures ๐Ÿ’….

Next question: what does absolute data mean in a full subpicture that takes both position and scale parameters? Is absolute data really absolute here? ๐Ÿค” This author believes the answer is this: a full subpicture is really a picture in its own right, so it has its own logical coordinate system, and its absolute data lives inside that system ๐ŸŒˆ. So "shifting and scaling" a full subpicture really means "scale the subpicture in its own coordinate system and shift the result as a whole" ๐ŸŽ€.

So the main difference is that a full subpicture has its own logical coordinate system, while a simple subpicture uses the logical coordinate system of whoever calls it ๐Ÿ’–. That's exactly why full subpictures are harder to implement than simple ones ๐Ÿ˜ญ.

Another thing discussed at the meeting was a special data mode where a subpicture can display data at absolute positions on the screen, i.e., absolutely in the main (picture) program โœจ. To do this, the meeting proposed special data modes for three operations: move beam invisibly, draw line, and display dot ๐Ÿฆ‹. The idea was to bypass all rotation, scaling, and clipping functions tied to the current level of subpicture nesting until this mode gets cleared in a certain way ๐Ÿซง. This same effect can be done more directly and more efficiently with two commands: one to save and one to re-establish the logical coordinate system for the current subpicture ๐Ÿ‘‘. (Additionally, of course, the "save" operation would establish the initial, highest level, logical coordinate system.) ๐Ÿ’…


Simple Subpicture Calls ๐Ÿงš

Besides the <identifier> of the subpicture being called, a simple subpicture call can specify two optional parameters ๐ŸŽ€. The first is an <identifier> that's the "name" (in a sense described below) of this particular call ๐Ÿ’œ. The second is an absolute position on the calling page that the beam gets invisibly moved to before the subpicture is called ๐ŸŒˆ.

When (eventually) the viewer is allowed to interact by "picking" information displayed before them, and that information is part of a subpicture, the "name" of the subpicture call gets reported as part of the "graphic input" sent to the serving host ๐Ÿ‘€. If the picked information is within several levels of subpicture calls, the names of each call get reported in a way that shows their nesting ๐Ÿช†. (Note that just the name of the subpicture by itself isn't enough, since one subpicture may be displayed in several positions and the application may want to tell individual calls apart ๐Ÿ™ˆ.) If the identifier isn't specified, it defaults to the null string ๐Ÿซถ. If the position (for the invisible move) isn't specified, the current beam position is used ๐ŸŒธ.

Which of these two parameters are present is encoded by two bits in a code byte that comes right before the parameters ๐Ÿ”ฅ. If both are present, they're always in the same order. This order, and the bits assigned to the two parameters, are specified in the detailed description of the Simple Instance command (and in the BNF in Appendix 1) ๐Ÿ“–. Coming right before even the code byte, and right after the name of the subpicture being called, is a count of the data in the rest of the instance command โœจ. That's included so you don't have to decode the code byte just to figure out the total length of any one Simple Instance operation ๐Ÿ’–.


Windowing: Clipping, Blanking, or (?) ๐ŸชŸ

Since we're on the topic of coordinate systems and subpictures, it might be good to touch on this: who (which end of the connection) is responsible for what, when a picture is potentially going to display beyond the edge of the virtual screen? ๐Ÿ˜ฌ The consensus at the graphics meeting was that the interpreter of the graphics protocol (i.e., the using end) would not be held responsible for doing anything reasonable when a picture displays information beyond the edge of the screen (e.g., by relative moves and draws) ๐Ÿ™ƒ.

The interpreter must react properly to the next absolute data in the proper range, however ๐Ÿ’…. Various solutions to this situation in existing graphics systems include:

  • clipping a line to display as much as is proper,
  • blanking the whole of a line if any part is invisible, or
  • discarding high order bits of the current position register, so that no invisible positions can be represented ("wraparound").

In addition to edge effects at the highest level, problems arise with respect to (full) subpictures ๐ŸŒ€. It's nice to be able to select a rectangular portion of a subpicture to display as part of a subpicture call ๐ŸŽ€. (See: Newman, Display procedures, Communications of the ACM, Volume 14, Number 10, October 1971, pp651-660). In accordance with the consensus of the meeting, which was to make this capability optional, this author merely hopes to include in the protocol a method of encoding this information, since his site a) can handle some such windowing, and b) hopes to provide a service facility to perform this function ๐Ÿซถ.

Appendix 2 describes how to concatenate several levels of portions into a single rectangular test, as long as no rotations are involved ๐Ÿ“. It also outlines the problems related to rotations and portioning ๐ŸŒˆ.

Full Subpicture Calls โœจ

okay so now we get to the fun part ๐Ÿ’… we can look at everything you can put in a full subpicture call, on top of the name of the subpicture being called (which, obvi, is required ๐Ÿ™ˆ).

All of the data below is optional ๐Ÿซถ. A single code byte comes first, and one bit in that byte tells you whether a parameter is there (bit is one) or not (bit is zero). The parameters always show up in the same order, if they're present at all. You can see that order in the detailed description of the Full Instance command, and in the BNF in Appendix 1. Oh, and even before the code byte there's a <count> of the following bytes, including the code byte, so you always know the total length of any particular Full Instance operation ๐Ÿงš.

One parameter is an <identifier>, which lets you tell this particular call to a subpicture apart from every other call to that same subpicture ๐Ÿฆ‹. We already covered this one under Simple Subpicture Calls.

Another parameter is a translation. This one is given by the absolute coordinates of the center of the subpicture's image (on the calling page). If you don't give one, it defaults to the beam position at the time of the call.

A rotation can be specified by giving a 16-bit fraction in the range 0 to .1111111111111111 (binary), inclusive. That fraction tells you what part of a full circle (2pi) the rotation is. The default angle of rotation is zero.

(Actually, the rotation representation works the same way if you think of it as a two's complement fraction from -1/2 to just less than +1/2. The same bit configurations encode the same rotation, because sine and cosine are periodic. For example, binary zero always represents 0, and 010000...0 denotes pi/2 in both schemes. 100...00 denotes 1/2 in one scheme and -1/2 in the other. Those correspond to rotations of +pi and -pi, which are identical rotations. ๐ŸŒ€)

Also part of a full subpicture call is a rectangular portion of the called picture to be imaged on the calling picture. (See the previous section for a discussion of clipping ๐ŸŽ€.) This rectangle is specified by its center and by one half of its total size in x and y. The rectangle consists of all points whose x coordinate differs from that of the center by no more than the specified x-size, and whose y coordinate satisfies a similar condition. The defaults put the center at the origin and give both the x half width and the y half width the value of +1/2. So the default includes the whole logical coordinate system of the called page. It also includes some points where one of the coordinates is +1/2, which, strictly speaking, lie "outside" the coordinate system. How this inconsistency gets resolved is left unspecified ๐Ÿคทโ€โ™€๏ธ.

Finally, you have to specify the scaling to be applied when working out the image, and there are a bunch of ways to do it. One way is a uniform magnification applied to the subpicture. To reach a wide range of magnifications, the author's opinion is that some form of scientific notation (i.e., floating point) will have to be used. If there's already a network standard floating point notation (which the author isn't aware of), it should be used. Failing that, the suggestion is that this notation consist of an 8-bit (two's complement) exponent followed by a 16-bit (two's complement) fractional part ๐Ÿ”ฅ.

Another form of scaling is to specify separate magnifications in x and in y, applied to the subpicture before any rotation is performed. A third option is to specify a rectangular area in the calling picture's coordinate system to be filled with the image of the subpicture. Since the center of the image is already specified by the translation, this image information only consists of half-edge size data. If none of the three scaling methods is chosen (and an affine transformation, see below, isn't given explicitly), a uniform magnification of unity (i.e., no scaling) is used. Heads up: the three forms of scaling tend to contradict each other, so only one of them should be used in any one call. What happens if self-contradictory information is given in these fields is left unspecified ๐Ÿ’€.

Appendix 2 presents the math for transforming the subpicture's coordinate system into the calling picture's coordinate system. It shows that all the individual operations (scaling, rotating, and translating) can be represented as a single affine transformation, which consists of 6 values. It might be nice to let the serving program specify this transformation directly. So one possible parameter of a full subpicture call is six floating point numbers (in the form described under magnification, above) that get interpreted as an affine transformation. If the affine transformation has the following form:

   /_ |x  |y_/ = /_ x y_/ * / L11 L12  / + /_ T1 T2_/
                           /_ L21 L22 _/

then the values shall (arbitrarily) be sent in the following (columnar) order: L11, L21, L12, L22, T1, T2. This affine transformation should be invertible, meaning L11*L22 - L21*L12 should not be zero ๐Ÿง .


Viewporting ๐ŸŒธ

Another topic from the meeting, which got referred to the protocol committee for a decision, was the ability to place the "top level" picture in some rectangle of the virtual screen. The default rectangle might be the full screen. Alternatively, it might be left up to the viewer to specify the default, via interaction with the graphics system at the Using Host. Viewporting generally allows more than one "top level" picture to be viewed at once. You'd want to see several pictures on the same screen when multiple users are working together, or when one user is interacting with a group of applications (on separate serving hosts). The author thinks the coordinate transformations for this feature are simpler than the ones for "full subpictures", since no rotations are involved, and that it would be part of the same mechanism in implementation. Specifically, just another affine transformation (see Appendix 2) would be added to the levels caused by full subpicture calls. All you need to do is keep track of viewport identifiers and their associated rectangles. Since there's very little extra work, the proposal is to include this feature at some high level of the protocol โœจ.


Command Codes ๐Ÿ”ข

Each command in the graphics protocol gets assigned a non-negative value, which represents that command in the byte stream. The algorithm that matches values to commands is, it turns out, a super touchy subject ๐Ÿ˜ญ. There are five or ten different criteria for a "best" algorithm, and each criterion has a different emphasis. This proposal cuts the Gordian knot by ordering the commands approximately by level and then just numbering them. If several closely related commands show up at the same level, some effort goes into encoding variations of meaning in bit configurations. Even if a later consideration causes a change in ordering to be proposed, this committee feels the numbering should not be altered. Until this matter is firmly settled, though, it's strongly advised that any implementation take into account the possibility of reassignment of command codes ๐Ÿšจ.


Particular Proposal for Level 0 Protocol ๐ŸŽฏ

The proposal is to keep level 0 very simple. That way, implementation can be done quickly and experimenting with the protocol can begin. Another reason is that the least powerful host, and even programmable terminals, should be able to implement it. With that in mind, the "rule" was made that a command gets included only if its output is a function solely of the current command and the "beam position" current at the start of the command. Put differently, the interpreter for level 0 needs no internal storage for "modes" or pushdown stacks. With this restriction, the hope is that a very simple implementation will be possible for level 0. Someday, one could even build a hardware translator from level 0 code to one's own particular terminal's code ๐Ÿค–.

Note that in the opcode assignment for level 0, bits 4, 2, and 1 have special meaning for the move, line, and dot commands. Specifically, the 1 bit encodes absolute versus relative data mode, the 4 bit encodes whether any visible output occurs, and the 2 bit determines whether the visible output is a line or a dot ๐Ÿฆ‹.


Level 0: Command Summary ๐Ÿ“‹

Here's the list of commands (and their syntax) in level zero. Detailed descriptions follow in the next section. Commands dealing with protocol may be added by the Connection Committee. (They currently request opcodes in the range 128 to 255.) (As described in Basic Data Forms, above, <x coordinate>, <y coordinate>, <x delta> and <y delta> are two-byte coordinate values, <string> is a count followed by <count> many bytes, and <value> is an eight bit number.)

Decimal  Octal   Binary    Format
0        0       00000000  Null
1        1       00000001  Erase screen and reset beam
2        2       00000010  Move Absolute <x coordinate> <y coordinate>
3        3       00000011  Move Relative <x delta> <y delta>
4        4       00000100  Draw Absolute <x coordinate> <y coordinate>
5        5       00000101  Draw Relative <x delta> <y delta>
6        6       00000110  Dot Absolute <x coordinate> <y coordinate>
7        7       00000111  Dot relative <x delta> <y delta>
8        10      00001000  Text <string>
9        11      00001001  TextR <string>
10       12      00001010  End of Picture
11       13      00001011  Escape <value> <string>

Level 0: Command Descriptions ๐Ÿง

0 Null Statement ("NULL"). This statement has no arguments, and no effect either. ๐Ÿ˜Œ

1 Erase screen and reset beam to origin ("ERASE"). This command indicates that a new picture is about to be drawn. It should always be (eventually) paired with a following End of Picture command ๐Ÿงน.

2 Move beam invisibly to absolute position ("MOVEA") <x coordinate> <y coordinate>. Nothing is drawn. The beam is positioned at the specified absolute x,y position ๐Ÿ‘€.

3 Move beam invisibly by relative amount ("MOVER") <x coordinate> <y coordinate>. Nothing is drawn. The beam is shifted by the specified amount in x and y ๐Ÿšถโ€โ™€๏ธ.

4 Draw line to absolute position ("DRAWA") <x coordinate> <y coordinate>. A line is drawn from the current beam position to the specified absolute x,y position โœ๏ธ.

5 Draw line to relative position ("DRAWR") <x delta> <y delta>. A line is drawn from the current beam position to the position delta x and delta y away ๐Ÿ“.

6 Display a Dot at absolute position ("DOTA") <x coordinate> <y coordinate>. The beam is moved invisibly to absolute position x,y, and a dot is displayed there โญ.

7 Display a Dot at relative position ("DOTR") <x delta> <y delta>. The beam is moved invisibly by the specified amount in x and y, and a dot is displayed there ๐Ÿ’ซ.

8 Display text ("TEXT") <string>. At the current beam position, display some characters at the normal size for the device being operated. <string> consists of a <count> followed by <count> many characters. If there is no "normal size", choose the size so that seventy-two characters are displayed per line. The characters in

the characters in the string are coded in network ASCII, and all codes between 0 and 127 (decimal) inclusive are permitted โœจ (at level zero, what happens to control characters is left unspecified ๐Ÿ™ˆ). Where the beam ends up after this command is left unspecified, except that another Display Text command right after it will append its text to the previous string ๐Ÿฆ‹. (The TEXT command is discouraged, so use TextR instead ๐Ÿ’….) The position of the first character relative to the initial beam position is left unspecified too ๐ŸŒธ.

9 โœจ Display text and restore beam ("TEXTR") <string>.

Bestie, this one has main character energy ๐Ÿ’–. At the current beam position, it displays a string of characters at the normal size for the device being operated, and then the beam goes right back to where it was before the command ๐Ÿฆ‹. <string> is a <count> followed by count many characters ๐ŸŽ€. If there is no "normal size", pick a size so that 72 characters are displayed per line ๐Ÿ“ (that's a LOT of text per line, iykyk). The characters are coded in network ASCII, and all codes between 0 and 127 (decimal) inclusive are permitted. At level zero, what happens to control characters is left unspecified, so they're on their own ๐Ÿ’€. The position of the first character relative to the initial beam position is left unspecified ๐Ÿงš.

10 ๐ŸŒˆ End of Picture ("ENDPIC").

This command marks the end of a new picture ๐ŸŽฌ. It MUST be paired with a preceding ERASE command ๐Ÿ”ฅ.

11 ๐Ÿ’… Escape to device specifics ("ESCDEV") <value> <string>.

Okay so if <value> is the code the Protocol Committee assigned to the device being operated ๐Ÿซถ, then the eight-bit bytes in <string> get transmitted to the device without anyone examining them ๐Ÿ‘€. (<string> starts with a <count> telling you how many bytes there are.) Otherwise this command gets ignored ๐Ÿ™ˆ. If the device doesn't accept 8-bit information, the data has to be reformatted in some device-specific way ๐Ÿ”ง. For example, you could throw away the high-order bit for a seven-bit device, or gather 5 8-bit bytes into one 36-bit word and again discard the high-order bits, perhaps ๐Ÿ’–. The action of the bytes in the string SHOULD leave alone (or at least restore) any hardware beam position registers in the device which the interpreter might conceivably depend on ๐Ÿง .

This command really SHOULD NOT be used ๐Ÿ™ƒ, but it was included at level 0 so that specific applications can do mode setting and other device-specific manipulations โœจ. For example, ARDS terminals MAY optionally have several independently addressable output scopes ๐Ÿ–ฅ๏ธ. The selection mechanism only changes state when a particular sequence of ASCII characters reaches the terminal. So ESCDEV would be used to select which scope(s) is/are to be affected by following commands ๐Ÿฆ‹. (The current state is invisible to the graphics package at the Using Host.)

Further, suppose another make of terminal has a similar option, which responds to a different code sequence. That possibility is the motivation for conditionally ignoring the ESCDEV command based on the "<value>" specified ๐Ÿ’œ. Given that a particular application will only be used to output to either an ARDS or this second make (with the multiple scope option), the application could always send two ESCDEV commands: one applicable only to ARDS terminals, and the other applicable only to the second make ๐Ÿ‘‘.


LEVEL 1 ๐ŸŽ€

Set Line mode ("LINMOD") <value>.

This command sets the current line mode ๐Ÿ–Š๏ธ. The possible modes and the <value> that sets each are: solid (0), dashed (1), dotted (2), and others (3 or >). At the beginning of a new picture (i.e., after an Erase and Reset command), line mode is solid ๐Ÿ’–. If a site doesn't have a certain mode readily available, it MAY a) simulate it in software, b) substitute another in its place (dashed for dotted, or vice versa) c) ignore it entirely ๐Ÿ™ˆ. What is provided SHOULD be clearly indicated in any public document ๐Ÿ“„. It is strongly RECOMMENDED that at least solid and one other mode be provided ๐Ÿซถ.

Set intensity ("SETINT") <value>.

This command sets the intensity of lines, dots and characters displayed following the command โœจ. If <value> is 128 decimal, normal intensity SHOULD be set. If <value> is 255 decimal, brightest SHOULD be selected, and if it is 0, then the beam SHOULD be blanked ๐Ÿ”ฆ. Intermediate values SHOULD be mapped appropriately as the implementer sees fit. For instance, if brightest is the same as normal, all values from 128 through 255 SHOULD be mapped to normal ๐Ÿ’…. Information displayed between the start of a new picture (the ERASE command) and the first SETINT command appears at normal intensity ๐ŸŒธ.

Text out ("TEXTO") <string>.

Starting from the current beam position, this command displays the <string> (of network ASCII characters) formatted as if it were typed material, at the current intensity โŒจ๏ธ. <string> consists of a <count> followed by count many characters. Basically, text that runs past the right margin gets broken and repositioned at the left margin on the next line down ๐Ÿ“œ. Of the control characters, only carriage return, line feed, and backspace are REQUIRED to be interpreted properly ๐Ÿ”ฅ.

Subpicture header ("SUBHED") <identifier> <count> <header info>.

This command begins the definition of a subpicture named "<identifier>" ๐Ÿงš. The definition is terminated by a matching SUBEND command. It stays remembered until a new one is specified or until the graphics network connection breaks ๐Ÿ’œ. Note that <identifier> is a <string> made up solely of capital letters and numbers ๐Ÿ” .

Subpicture definitions MAY be nested, and that's equivalent to transmitting the two definitions separately ๐Ÿช†. In other words, all subpicture names are globals and are "known" to all other subpictures ๐ŸŒ. If a subpicture definition hasn't been received before its use in a picture, the empty subpicture SHOULD be displayed in its place until a definition is received โณ.

A subpicture definition need not be transmitted as part of a picture (i.e., within an ERASE and END command pair). Honestly, all subpicture definitions might come before the main picture ๐ŸŽ€.

Currently, the <count> will always be 1, which means only one byte of <header info> follows. At higher levels of the protocol, room for expansion may be required ๐ŸŒฑ. In the <header info>, the 80 hex bit will be set if this subpicture can be a simple subpicture, and the 40 hex bit will be set if the subpicture can be a full subpicture. (It's possible that one subpicture can be both ๐Ÿคฏ.)

Other information that may eventually be present in <header info> includes whether the current value of a certain mode or parameter SHOULD be saved on entry to, and restored on exit from, this subroutine whenever it's called ๐Ÿ”„. These modes and parameters include: line mode, intensity, character size, and data length ๐Ÿ“.

Subpicture end ("SUBEND").

This command ends the definition of a subpicture ๐Ÿ”š. Each SUBEND MUST match a preceding SUBHED command ๐Ÿ’….

Simple instance ("INSTS") <identifier> <simple instance tail>

This command indicates that the subpicture <identifier> is to be called (instanced) ๐Ÿ“ž. At this level, level 1, no subpicture may call another. If one does, what happens is left unspecified ๐Ÿ™ˆ. Also, this MUST be a call to a simple subpicture. Thus the 80 hex bit of the single byte of <header info> MUST have been set in the SUBHED command which started the definition of <identifier> ๐Ÿ”‘. If the subpicture <identifier> has never been defined, the empty subpicture SHOULD be displayed in its place ๐Ÿซฅ.

The <simple instance tail> begins with a count of the amount of information that follows, and this count MAY be zero ๐Ÿงฎ. If it's non-zero, the next byte is a code byte that gets interpreted to see what further information follows ๐Ÿงฉ. If the 80-hex bit is set, the next thing in the byte stream is an <identifier> (called "AS information") ๐Ÿท๏ธ. This <identifier> is the name of this particular instance of the subpicture, as described under Simple Subpicture Calls ๐ŸŽญ. If the 40-hex bit is set, then next in the byte stream (following the AS information, if present) is an x,y position (in the calling picture's coordinate scheme) where the subpicture will be centered ๐ŸŽฏ. (This is called AT information.)

If AT information isn't specified, the current beam position is used as a default ๐Ÿ“. If AS information isn't specified, it defaults to the <string> containing zero characters ๐Ÿซง. If neither the 40 hex nor the 80 hex bits are set, then neither the AT information not the AS information is present, and the code byte SHOULD be zero ๐Ÿ™ƒ. (Also, the length count had better be 1.)

Changes to levels 0 commands for level 1.

Okay, here's what changes ๐Ÿ‘€:

  • TEXT and TEXTR -- Carriage return, line feed and backspace characters SHOULD definitely be interpreted whenever they appear in <string> ๐Ÿ’–. The results of other control characters remain unspecified. The intensity of the characters SHALL be affected by the SETINT command โœจ.
  • ERASE -- Normal intensity and solid line mode MUST be established at the start of a new picture ๐ŸŒธ.
  • DRAWA and DRAWR -- Line mode and intensity SHALL be affected by the LINMOD and SETINT commands ๐ŸŽจ.
  • DOTA and DOTR -- Intensity SHALL be affected by the SETINT command ๐Ÿ”†.

LEVEL 2 ๐ŸŽ€

Mark ("MARK").

This command causes the current x,y beam position to be saved on a pushdown stack ๐Ÿ“Œ. This pushdown stack MUST be separate from the subpicture call pushdown stack ๐Ÿฅž.

Move to mark and pop ("MOVEMK").

This command sets the current beam position equal to the x,y position at the top of the "mark" pushdown stack ๐Ÿƒโ€โ™€๏ธ. If the stack is empty, the origin is used instead. Then the stack gets popped (unless it's already empty) ๐Ÿ’จ.

Draw to mark and pop ("DRAWMK").

If the "mark" pushdown stack isn't empty, this command draws a line (of the current line mode and intensity) from the current beam position to the x,y position at the top of the "mark" pushdown stack ๐Ÿ–๏ธ. It also sets the beam position to that value, and then the stack is popped ๐Ÿซถ. If the stack is empty, the line is drawn to the origin and the beam position is set there too ๐ŸŽฏ.

Changes to level 0 and 1 for level 2.

  • INTS -- arbitrary levels of simple subpictures MUST be supported ๐Ÿช†. (Note that recursive use of subpictures is not allowed: once recursion starts, it can never be stopped ๐ŸŒ€.) The pushdown stack for subpicture calls MUST be kept separate from the "mark" pushdown stack ๐Ÿ’œ.

Level 3 โœจ

okay bestie, this is where things get fancy ๐Ÿ’… The author's own little note says all rotational transformations should maybe be put at a higher level, like higher than viewport operations ๐ŸŒธ

Full Instance ("INSTF") <identifier> <full instance tail> ๐ŸŽ€

this command says the subpicture <identifier> gets called (instanced) in a "full" way, as described in an explanatory section ๐Ÿฆ‹ For one thing, the 40 hex bit of the single byte of <header info> must have been set in the SUBHED command that started the definition of <identifier>. If <identifier> has never been defined, the empty subpicture (aka nothing) should be displayed in its place ๐Ÿ™ˆ

The <full instance tail> is like the <simple instance tail> from the INSTS command, but it carries more info ๐Ÿ’œ Here's the list of what can be specified, plus the bit that marks whether each piece is there or not. Whatever pieces are present always show up in the byte stream in the order listed here. (All of them get explained more fully in Full Subpicture Calls, except the "AS information", which lives in Simple Subpicture Calls.)

Bit (hex)   Information
80          As information --"name" of this particular instance.
            Consists of an <identifier>.

40          Translation information -- Center of the subpicture's image
            on the calling page.  Consists of an <x coordinate> and a
            <y coordinate>.

20          Rotation -- Fractional part of 2pi to rotate the image
            counterclockwise.  Consists of a 16-bit unsigned fraction.

10          Portion Information -- Rectangular part of subpicture which
            is to be displayed.  Consists of <x coordinate>,
            <y coordinate>, <x delta>, and <y delta>.

8           Uniform Magnification -- Amount to scale the whole
            subpicture.  Consists of a floating point number (which
            should not be zero).

4           Separate x and y magnification -- Separate scales for the x
            and y axes of the subpicture.  Consists of two floating
            point numbers (neither of which should be zero).

2           Image Size -- How large a rectangle on the calling page is
            the image to occupy.  Consists of an <x delta> and a
            <y delta> (neither of which should be zero).

1           Affine transformation -- The map from the called to the
            calling coordinates system.  Consists of six floating point
            numbers.

Notes:

  1. At most one of the three bits: 8, 4, and 2, should be set.
  2. If the 1 bit is set, bits 2, 4, 8, 20, and 40, should not be set.
  3. If additional optional parameters are ever added to the full subpicture call, another code byte could follow all the above information. In that case, the <count> part of the <full instance tail> would include this second code byte and any additional bytes of information.

Escape to top level coordinate system ("ESCTOP") ๐Ÿฆ‹

okay so this one is the escape hatch ๐Ÿšช Until a RESLEV command is (subsequently) executed, all display commands (moves, draws, dots, and texts) shall operate as if they were issued by the top level (main) picture instead of the subpicture containing them. They get mapped to the screen using the map for the highest level โœจ Subpicture calls made while ESCTOP is in effect are NOT affected by it, though. The transformations still get calculated like the command isn't even there ๐Ÿค“ The calculated transformations just get ignored, and whatever the subpicture displays still looks like it's at the top level until a RESLEV nullifies ESCTOP mode. So a subpicture call made during ESCTOP acts as if a RESLEV ran right before the call, and an ESCTOP ran as the very first command of the subpicture. Similar stuff goes for returning from subpictures ๐Ÿ’…

Resume current level coordinate system ("RESLEV") ๐ŸŒˆ

this one restores the logical coordinate system for the subpicture that's currently executing, in case ESCTOP had turned it off (see ESCTOP) ๐Ÿ’–

Changes to levels 0, 1, and 2 for level 3

MARK: the saved beam position shall be in terms of the logical coordinate system, not the physical coordinate system ๐Ÿ“

TEXTR, TEXT, TEXTO: okay so a full subpicture is supposed to be transformed as a whole, like it's a picture in its own right. The author feels that means all beam movements related to characters should be affected too ๐Ÿซถ That includes character size, tab, carriage return and line feed. Specifically, carriage return should set the beam to the left margin, meaning the left edge of the logical coordinate system of the called subpicture. The author admits all this may be very hard to pull off ๐Ÿ˜ญ So what should happen is left unspecified for now, and comments from readers are particularly invited ๐ŸŽ€


Level 4 ๐Ÿ’œ

(maybe viewpoint operations can be included in level 3, the author says ๐ŸŒธ)

Declare Viewport ("SETVW") <viewport id> <x coordinate> <y coordinate> <x delta> <y delta> ๐ŸชŸ

this sets the viewport identified by <viewport id> to represent the given area of the logical screen โœจ The x and y data are NOT physical screen coordinates, since that would bring in device dependencies. This command completely supersedes any previous declaration of the same viewport. If info is already displayed inside the viewport, this command moves that displayed info to its new position on the screen ๐Ÿฆ‹

If the area specified goes past the limits of the graphics standard display screen, what happens is left unspecified ๐Ÿ™ƒ Viewports don't have to be disjoint, which just means two viewports can show display info at the same point on the screen.

If <x delta> or <y delta> are negative, the viewport named should be deleted. All info displayed by it shall no longer appear ๐Ÿ—‘๏ธ

Because this command affects the top level picture, the author feels it should not occur as part of a picture or in a subpicture declaration.

Add subpicture to viewport ("ADDSVW") <identifier> <viewport id> ๐ŸŽ€

the subpicture named <identifier> is displayed within the specified viewport, if it isn't already displayed there. (If it is, nothing is done.) The subpicture must be capable of being called via a full subpicture call. If the viewport has never been declared via a SETVW command, what happens is left unspecified ๐Ÿคทโ€โ™€๏ธ (Three possibilities are: nothing is displayed; the viewport defaults to the whole logical screen; or the human viewer is permitted by the Using Host to specify the viewport.) If the viewport is declared later, the subpicture shall be displayed in it. If the subpicture has never been declared, nothing is displayed for it. When and if it's declared later, the new definition is displayed in the viewport. More than one subpicture may be displayed in a single viewport at once ๐Ÿ’…

Because this command affects the top level picture, the author feels it should not occur as part of a picture or in a subpicture declaration.

Clear viewport ("CLVW") <viewport id> ๐Ÿงน

all subpictures that were added with the ADDSVW command to the specified viewport get removed from it. So the specified viewport contributes nothing to what the human viewer sees ๐Ÿ‘€ (After a CLVW, the area of the viewport may not be blank, because other, non-cleared viewports might overlap it.)

Because this command affects the top level picture, the author feels it should not occur as part of a picture or in a subpicture declaration.

Changes to levels 0, 1, 2, and 3 for level 4

ERASE: all viewports are cleared (like in the CLVW command), but their declarations are remembered ๐Ÿง 

ENDPIC: this command partially loses its purpose ๐Ÿ˜ข It no longer marks the end of all picture info to be presented to the user, since viewport operations may follow that amend or alter the picture. This job is partially taken over by the DELAY and NODELAY commands described below.


Level ? ๐ŸŒธ

Set Character Size ("SETCHS") <x delta> <y delta>.

until further notice, characters shall be displayed so that each one takes up approximately <x delta> and <y delta> in the appropriate coordinate direction in the current logical coordinate system ๐Ÿ“ Inter-character and inter-line spacing could be certain percentages (any ideas?) more than <x delta> and <y delta>, or they could be specified separately. Either way, only a "best effort" would be expected at a site ๐Ÿซถ

Character size is always set back to normal (as defined by level 0 character size being normal) by the ERASE command. <x delta> and <y delta> should be positive, EXCEPT that if <x delta> is equal to zero, then <y delta> being negative, zero, or positive corresponds to a character size of "smaller than normal", "normal", or "larger than normal" ๐Ÿ” How much smaller or larger than normal is left up to the site.

Changes to levels 0 and 1 for level ?

TEXTR, TEXT, and TEXTO: characters are to be displayed according to the current character size โœ๏ธ

ERASE: must establish normal character size, with normal being the one for level 0 ๐Ÿงผ


Level ?' ๐Ÿฆ‹

Set Data Length ("SETDLN") <value>.

until this mode is explicitly changed with another SETDLN, various data will consist of <value> number of bytes ๐Ÿ”ข <value> may be 1, 2, 3, or 4. The affected syntactic types (refer to Appendix 1) are: <coordinate>, <x coordinate>, <y coordinate>, <double coordinate>, <x delta>, <y delta>, <angle>, and the fractional part of a floating point number ๐Ÿ’œ When a network connection is first established, the data length is two โœจ


Level ?'' ๐ŸŒˆ

(these commands should probably be at the same level as viewport operations, if not earlier, the author says ๐Ÿ™ˆ)

Extensive Changes Follow ("DELAY").

this optional command is designed to eliminate futile effort on the part of the Using Host programs ๐Ÿ™Œ At some hosts and/or with some output devices (particularly storage tubes), a non-negligible amount of time may be needed to present an image to the human viewer. If extensive changes are going to be made, this command is used to stop the Using Host graphics package from updating the image after every change. A NODELAY command exits DELAY mode and causes the image to be prepared and presented to the viewer ๐ŸŽฌ

For example, the current picture may display four subpictures that are each going to be redefined. Without a DELAY command, the viewer would see successive stages of the change, each possibly involving a large amount of computation or transmission time ๐Ÿ˜ตโ€๐Ÿ’ซ

End of Extensive Changes ("NODELAY")

this optional command undoes the effect of the DELAY command ๐Ÿซง


Appendix 1: BNF for the Graphics Protocol Byte Stream ๐Ÿ“œ

Key to below:

Non-terminals are represented in < >. Terminals which are keywords standing for particular eight-bit values are in capitals. Terminals whose meaning should be clear to the reader are in lower case. Note that "empty_string" means "zero bytes", not "a <string> whose <count> is zero."

<graphics output byte stream> ::= empty_string
           | <picture> <graphics output byte stream>
           | <subpicture declaration> <graphics output byte stream>
           | <viewport operation> <graphics output byte stream>
           | <transmission control stt> <graphics output byte stream>
<picture> ::= <new picture sst> <program stt group> <end picture stt>

4. Appendix 1 Grammar, Continued โœจ

Okay so the grammar keeps going, and it's where all the pieces click together ๐Ÿงฉ Here's the rest, exactly as written, because every bracket matters.

<subpicture declaration> ::= <subpicture header stt> <program stt
                              group><subpicture end stt>
<viewport operation> ::= <declare viewport stt>
           | <add subpicture to viewport stt>
           | <clear viewport stt>
<transmission control stt> ::= <set data length stt>
           | <extensive changes follow stt>
           | <end of extensive changes stt>
<program stt group> ::= empty_string | <program stt <program stt group>
<program stt> ::= <picture control stt> | <display stt> |
              <transmission control stt>
<picture control stt> ::= <escape to device stt>
           | <escape to highest coordinate system stt>
           | <restore coordinate system stt>
           | <mark stt>
           | <null stt>
           | <line mode stt>
           | <set intensity stt>
           | <subpicture declaration>
           | <simple instance stt>
           | <full instance stt>
           | <set character size stt>
<display stt> ::= <move absolute stt>
           | <move relative stt>
           | <draw absolute stt>
           | <draw relative stt>
           | <dot absolute stt>
           | <dot relative stt>
           | <move to mark and pop stt>
           | <draw to mark and pop stt>
           | <text and restore beam stt>
           | <text stt>
           | <text out stt>
<new picture stt> ::= ERASE
<end picture stt> ::= ENDPIC
<subpicture header stt> ::= SUBHED <identifier> count> <header info>
<header info> ::= 80-hex | 40-hex | C0-hex
<subpicture end stt> ::= SUBEND
<set viewport stt> ::= SETVW <viewport id> <x coordinate>
                       <y coordinate> <x delta> <y delta>
<add subpicture to viewport stt> ::= AADSVW <identifier> <viewport id>
<clear viewport stt> ::= CLVW <viewport id>
<extensive changes follow stt> ::= DELAY
<end of extensive changes stt> ::= NODELAY
<escape to device stt> ::= ESCDEV <device code> <string>
<escape to highest coordinate system stt> ::= ESCTOP
<restore coordinate system stt> ::= RESLEV
<null stt> ::= NULL
<mark stt> ::= MARK
<line mode stt> ::= LINMOD <value>
<set character size stt> ::= SETCHS <x delta> <y delta>
<set data length stt> ::= SETDLN <value>
<move absolute stt> ::= MOVEA <x coordinate> <y coordinate>
<move relative stt> ::= MOVER <x delta> <y delta>
<draw absolute stt> ::= DRAWA <x coordinate> <y coordinate>
<draw relative stt> ::= DRAWR <x delta> <y delta>
<dot absolute stt> ::= DOTA <x coordinate> <y coordinate>
<dot relative stt> ::= DOTR <x delta> <y delta>
<move to mark and pop stt> ::= MOVEMK
<draw to mark and pop stt> ::= DRAWMK
<text and restore beam stt> ::= TEXTR <string>
<text stt> ::= TEXT <string>
<text out stt> ::= TEXTO <string>

<simple instance stt> ::= INST <identifier> <simple instance tail>
<full instance stt> ::= INSTF <identifier> <full instance tail>
<simple instance tail> ::= eight_bits_of_binary_0
                    | <count> <tail code> <as clause> <at clause>
<tail code> ::= bit_pattern_indicating_what_clauses_follow
<full instance tail> ::= eight_bits_of_binary_0
                    | <count> <tail code> <as clause> <at clause>
                    <rotation clause> <portion clause>
                    <uniform magnification clause>
                    <separate magnification clause> <image size
                    clause> <complete transformation clause>
<as clause> ::= empty_string | <identifier>
<at clause> ::= empty_string | <x coordinate> <y coordinate>
<rotation clause> ::= empty_string | <angle>
<portion clause> ::= empty_string | <x coordinate> <y coordinate>
                     <x delta> <y delta>
<uniform magnification clause> ::= empty_string |floating point number>
<separate magnification clause> ::= empty_string |
                    <floating point number> <floating point number>
<image size clause> ::= empty_string | <x delta> <y delta>
<complete transformation clause> ::= empty_string | six_<floating point
                    number>'s

<angle> ::= 16-bit_non-negative_fractional_part_of_a_circle
<x coordinate> ::= <coordinate>
<y coordinate> ::= <coordinate>
<x delta> ::= <double coordinate>
<y delta> ::= <double coordinate>
<coordinate> ::= signed,_two_s-complement,_fraction_in_range
                    -1/2_to_less_than_+1/2
<double coordinate> ::= signed,_two_s_complement,_fraction,
                    range _strictly_between_-1_and_+1
<floating point number> ::= network_standard_floating_point
                    number_if_any
                    | 8-bit_two_s_complement_exponent_part and a
                    16-bit_two_s_complement_fraction_part <count>
                    ::= 7-bit_non-negative_integer
                    | 15-bit_non-negative_integer_represented_in
                    "excess_2**15"_notation
<string> ::= <count> count_8-bit_bytes
<identifier> ::= <count> count_upper_case_letters_or_numbers
<viewport id> ::= <identifier>
<device code> ::= 8-bit_integer
<value> ::= 8-bit_integer

Appendix 2. Mathematical Formulae for Subpictures ๐Ÿงฎ

Transformations

Okay bestie, this appendix is the math behind subpictures, and it's giving linear algebra realness ๐Ÿค“ Positions in a logical coordinate system get written as a row vector with two elements, like /_ x y_/. Vectors and matrices are wrapped in these funny brackets: /_ _/ ๐Ÿ’…

A bunch of symbols show up in a full subpicture call to describe the transformation from one coordinate system to another. Here's the lineup:

Mx and My : magnifications in x and y to be applied before any
            rotation.
            They may be negative indicating reflection.
A: an angle of rotation in the range 0 to just less than 2pi.
/_ |cx |cy_/ : the center (in the calling picture) of the image of the
          subpicture.

|sx |sy : the half-sizes, in the x and y directions, of the
           image on the calling page in terms of the calling page's
           coordinate system.
           They may be negative to indicate reflection.
/_ x y_/ : a position on the called page.
/_ |x |y_/ : the position on the calling page corresponding to /_x y_/.
/_ Pcx Pcy_/ : The center of the portion of the called subpicture's
               coordinate system which is to be mapped to the calling
               page.
               This defaults to /_ 0 0_/ if not specified.
Psx and Psy : The half-sizes in x and y of the portion of the
              subpicture to be mapped. These both default to +1/2
              in not specified.

Quick recap in my own words ๐Ÿซถ

  • Mx and My: magnifications in x and y, applied before any rotation. Negative means reflection ๐Ÿชž
  • A: the rotation angle, from 0 up to just under 2pi ๐ŸŒ€
  • |cx |cy: the centre of the image, measured in the calling picture โœจ
  • |sx |sy: the half-sizes of the image on the calling page, in the calling page's coordinates. These can be negative too, for reflection ๐Ÿฆ‹
  • x y and |x |y: a position on the called page, and where it lands on the calling page ๐Ÿ“
  • Pcx Pcy and Psx Psy: the centre and half-sizes of the portion of the called subpicture being mapped. Pcx and Pcy default to /_ 0 0_/, and Psx and Psy default to +1/2 ๐ŸŽ€

(If a uniform magnification is specified, set Mx and My equal to it and proceed below as if they were specified.)

If magnifications are specified, the following holds:

/_ |x |y_/ = (/_ x y_/ - /_ Pcx Pcy_>) * / Mx/Psx  0    / *
                                        /_   0  My/Psy_/

          / cos 0 sin 0 / * / 1/2 0  / + /_ |cx |cy_/
         /_ -sin0 cos0_/  /_ 0  1/2_/

Or, put another way, here's the same thing in its expanded form:

1)
/_|x |y_/ = /_ x-Pcx y-Pcy_/ * / Mx cosA/2Psx Mx sinA/2Psx /
                             /_ -My sinA/2Psy My cosA/2Psy_/
            +/_ |cx |cy_/

The factor of 1/2 is there for a reason, babe ๐Ÿ™ˆ For example, (x-Pcx)/Psx goes from -1 to +1 for x values inside the portion (so |x-Pcx| <|Psx| ), but the image in the calling subpicture's coordinate system should only span -1/2 to +1/2. That's the whole trick โœจ

If the image size is specified instead of the magnification, we have the following:

/_ |x |y_/ = (/_ x y_/ - /_Pcx Pcy_/) * / 1/Psx  0    / *
                                       /_  0  1/Psy _/

          / cosA sinA    / * / |sx 0    / + /_ |cx |cy_/
         /_ -sinA cosA _/   /_ 0  |sy _/

Same idea, just written another way:

2)
/_|x |y_/ = /_x-Pcx y-Pcy_/ * /|sx cosA/Psx |sy sinA/Psx /
                            /_-|sx sinA/Psy |sy cosA/Psy_/

           + /_|cx |cy_/

Expanding the parenthesized quantities in equations 1) and 2) gives us the following ๐Ÿ’–

3a) /_|x |y_/ = /_x y_/ * /Mx cosA/2Psx   Mx  sinA/2Psx /
                         /_-My sinA/2Psy  My cosA/2Psy_/

           + /_|cx-PcxMxcosA/2Psx+PcyMysinA/2Psy
                      |cy-PcxMxsinA/2Psx-PcyMycosA/2Psy _/

and

3b) /_|x |y_/ = /_x y_/ * /|sx cosA/Psx |sy sinA/Psx  /
                        /_-|sx sinA/Psy |sy cosA/Psy_/

           + /_|cx-Pcx|sxcosA/Psx+Pcy|sxsinA/Psy
                      |cy-Pcy|sysinA/Psx-Pcy|sycosA/Psy_/

There are some super iconic substitutions you can make in 3a) and 3b) ๐Ÿ˜ญ For example, if A=0 (no rotation), then we have:

4a) /_|x |y_/ = /_x y_/ * /Mx/2Psx 0 / + /_|cx-PcxMx/2Psx
|cy-PcyMy/2Psy_/
                         /_ 0 My/2Psy_/

4b) /_|x |y_/ = /_x y_/ *  /|sx/Psx 0 / + /_|cx-Pcx|sx/Psx
|cy-Pcy|sy/Psy_/
                          /_ 0 |sy/Psy_/

Another one is if no portioning is done (Pcx=Pcy=0, Psx=Psy=1/2):

5a) /_|x |y_/ = /_ x y_/  * /Mx cosA Mx sinA   / + /_|cx |cy_/
                           /_-My sinA My sinA_/

5b) /_|x |y_/ = /_ x y_/ * /2|sx cosA 2|sy sinA   / + /_|cx |cy_/
                          /_-2|sx sinA 2|sx cosA_/

If in addition, 0=0, we have:

6a) /_|x |y_/ =  /_ xMx+|cx  yMy+|cy_/

6b) /_|x |y_/ = /_ x*2|sx+|cx y*2|sy+|cy_/

Of course, in every case, the transformation from /_x y_/ to /_|x |y_/ can be written in this form:

/_|x |y_/ = /_x y_/ * / 2 by 2 / + /_ translation _/
                     /_ matrix _/

In general, a transformation that combines a linear transformation with a translation is called an affine transformation ๐ŸŒธ

Transformations with Nested Levels

Here's where it gets lowkey brilliant ๐Ÿง  The combination of two affine transformations is itself an affine transformation. Like, if

/_|x |y_/ = /_x y_/ * / Mat1 / + /_ Tran1 _/
                     /_     _/

and

/_|x_ |y__/ = /_|x |y_/ * ( / Mat1 / * / Mat2 /)
                           /_    _/   /_    _/
            + (/_ Tran2 _/ + /_ Tran1 _/ * / Mat2 /)
                                          /_    _/

then you can see the combo is just another affine transformation ๐Ÿ’…

So if you have nested full subpicture calls, the data at any level only needs to be transformed once, by the single transformation that combines every step at each level of nesting. That's giving efficiency ๐Ÿš€

A new "grand combination" affine transformation should be computed whenever a full subpicture is called. You push down the current transformation first, then combine the current grand combination with the affine transformation for that particular subpicture call. Main character energy for sure ๐Ÿ‘‘โœจ

Portions with Nested Levels ๐ŸŽ€

okay bestie, so as long as no rotations are involved (or only rotations in multiples of pi/2 ๐ŸŒธ), multiple levels of portions are easy to implement ๐Ÿ’…. for the next two paragraphs, let's assume no rotations other than whole multiples of pi/2 are involved โœจ.

just like we keep track of a "grand combination" affine transformation, we can keep a grand combination of portions too ๐Ÿฆ‹. at each level, here's the move: save a copy of the current grand portion, then use the inverse of the single level affine transformation (the one specified in the subpicture call) to figure out which rectangle of the called page corresponds to the current grand portion (on the calling page) ๐Ÿงš.

then there are a bunch of ways this rectangle can relate to the rectangle specified (or defaulted) in the subpicture call ๐Ÿ’œ. they may be disjoint (in which case this subpicture need not be called at all ๐Ÿ’€). they may be equal (an easy case ๐Ÿ™ˆ). one may contain the other, or they may partially overlap. if there's any intersection at all, it'll be a rectangle, and that rectangle becomes the new grand combination portion ๐Ÿ‘‘.

now the tea โ˜•: with rotations other than multiples of pi/2, the inverse image of the rectangle is no longer in standard orientation (vertical and horizontal edges) ๐Ÿ˜ญ. so its intersection with the portion specified on the subpicture call may have 3, 4, 5, 6, 7, or 8 edges (if it's non-empty) ๐Ÿ”ฅ. deeper levels can get even messier if they involve rotations too ๐Ÿ’€. ngl there may be no conceptual difficulty (for some) in working with that, but it takes significantly more computation than the simple horizontal and vertical edge case ๐Ÿ“.

bestie, this protocol puts no recommendation on the table for rotations other than whole multiples of pi/2 with portions ๐Ÿซถ. it does suggest that nested portions be handled as described above in the more straightforward case ๐Ÿ’–.

[This RFC was put into machine readable form for entry] [into the online RFC archives by Helene Morin, Via Genie, 12/1999]
๐Ÿ“ฆ where this page comes from