• Keine Ergebnisse gefunden

Underlined Label

4. The Graphics Editor

4.3. Generating code

When the user performs one of the various graphics editor operations, the editor modifies the code of the current program and invokes the incremental execution module to do the changes.

This section will discuss the forms that these modifications take.

Figure 4-11: A bounding box being scaled

Figure 4-12: The same, in a skewed coordinate system

Inmany cases it is possible for the editor to modify the code in either of two ways: it could change the original invocation of an object, or it could add extra statements to the end of the invoking routine. For example, assume the program originally contained code that generated a circle with radius 50 translated to (100,100)

(with (translate [100 100) (object a_circle (circle 50»

)

(draw a_circle)

and, through the editor, the user moved the circle to (200,200). The editor could either change the original code to

(with (translate [200 200) (object a_circle (circle 50»

)

(draw a_circle)

oritcould append the statement

(transform a_circle (translate [200 200))

to the end of the current routine. Which should it chose?

The first has the advantage that it keeps the program much cleaner. Objects are frequently moved around many times before the picture assumes its final state, so programs would become littered with dozens of extra statements at the end. These clutter up the program text, obscure the program flow, and make the program take longer to execute.

Unfortunately, the first solution will not work if the subobject being moved was defined within a loop or if the object being edited is a variant. In these situations the definition is un-touchable; the editor must use the second solution. Since it has to do modifications this way when editing objects that are variants, the editor just does it this way all the time.

Using the second solution has two additional advantages. The actual modification of the program text is much easier to do this way since appending to the end of a routine takes less text manipulation than changing the middle of a routine. More important, appending to the end means that the incremental execution module can extend execution rather than redoing it, so the results of the change take place more quickly. The only disadvantage is the cluttering up of the program text; however, this can be solved by manually invoking a source code optimizer upon the program when it gets too messy. The optimizer replaces trailing transform, raise, lower, delete, and recall statements by the appropriate changes to the invocations whenever possible.

The following descriptions of the modifications made by various commands only describes the changes as they actually take place; next chapter contains a section on the optimizer that describes how it transforms the text to remove the trailing editing statements. In all the examples it is assumed that there is only one object in the selection and that the variable obj contains a reference to it. Ifthere are multiple objects in the selection the process must be iterated for each one.

The three types of editing are all treated slightly differently. The simplest change is choosing to change the definition of an object; this doesn't actually modify the text, it just changes the editor's focus in the program to the particular object definition. Ifthe user chooses to edit the instances as different objects, the editor prompts the user for the name of the new object and appends arecall statement

(recallobj «new name> <parameters to original call»)

at the end of the current routine. Before executing the recall statement, it creates the definition for the new object. Ifthe selection is being edited as a variant, the definition is

(defineobject <new name> (variant <old name»

(method

) )

and if the selection is being edited as an independent object the definition is a copy of the original definition with the new name. The new definition is parsed, and then the recall state-ment is executed.

Transforms are done by appending a transform statement to the program:

(transform obj <new transformation»

This is the same for all flavors of transformation. The value of the transformation for an anchored transform is just a concatenation of a rotation or scaling with a translation.

When Stack is chosen, the editor appends either a raise or a lower statement for obj to the program.

The Copy operation requires the editor to come up with a name for the new object instance.

This new name is then assigned the result of calling newon the original object with the ap-propriate transformation and the result is drawn:

(with <transformation of new object>

(object <new name> (new obj»

)

(draw <new name»

Delete just appends a delete statement for obj.

Transform All is the one operation that cannot be done by appending statements since it operates on paths as well as objects. It instead wraps the entire body of the routine in a with statement

(with <transformation>

<original routine body>

)

and reexecutes the routine. Since this involves modification, not appending, it is impossible to do a Transform All on an object that is a variant of another object.

Add acts differently depending on whether the object to add already exists or is a totally new object. Ifthe latter, the editor prompts for the new name and creates a new skeleton defmition for the object:

(defineobject <new name>

(method

) )

In either case the editor creates a new name for the instance and appends statements to the program to instantiate and draw the object to be added

(with <transformation of new object>

(object <new name> «name of object> <parameters»)

)

(draw <new name»

and executes the statements. Ifany control points were specified as parameters, the new code is wrapped with using statements for the objects involved. Finally, if a new type of object is being added, the editor shifts its focus to the new object so that the user can add subobjects to the newly defmed object.