 |
» |
|
|
 |
As mentioned above, the phrase "segment-directed functions"
refers to those functions whose actions can relate to segments other
than the currently open one, and those functions that can act upon
many segments at once. The various operations are: Changing references. You can cause
all segments that refer to a particular segment name, to refer to
a different segment name. Renaming segments. You can rename either a single
segment, or simultaneously rename a segment and all references to
it. Deleting segments. You can delete a specified segment
from the display list, as well as all references to it in all other
segments, or delete all segments in the entire display list. Copy segments. You can copy a segment from elsewhere
in the display list (or from a different display list) into the
currently open segment.
These operations are described below in the context of our
house. After considering for a while, we want to make the following
changes: It seems unnecessarily complex to
have the Window
segment call the Frame
and Panes segments;
let's move the Starbase primitives to the Window segment itself, and
remove the Frame
and Panes segments. Instead of having the square window in the display
list, as we had before, we want an octagonal window.
Copying Display List Segments |  |
Suppose our house model was complex enough to have several
windows. If all the windows were identical, we could have a single
segment describing the window, and call that segment (with appropriate
transformations on the stack) to draw the many windows. If you want
a different window, just edit the window segment. However, in such
a case, a change to the window definition changes all
instances of the window. If we want only one window to be different,
we could copy the window segment into another segment, edit
the copy, and then call the new window where desired. This scenario is a good use of the copy_segment functionality,
but the house model we're dealing with has but a single window.
Thus, we'll use copy_segment
for another reason. We mentioned above that we wanted to consolidate
the Frame and
Panes segments
into the Window
segment, since there wasn't enough in either of the two smaller
segments. To do this, we can use the copy_segment
routine: copy_segment(〈fildes〉,
〈segno〉);
where: 〈segno〉
is the number specifying the segment to be copied into the currently
open segment. The copied segment is placed immediately after the
current element.
Segment-copying is illustrated by the following diagrams.
Assume the existence of "Segment A," with three elements, and "Segment
B," with two elements. The current element in Segment A is the second
user-defined element: Segment A looks like this after executing copy_segment(fildes, B);[20]: Segment B is left unchanged. In our example, we want to copy the Frame and Panes segments into the Window segment. However,
we don't want the call_segment
elements referring to those subordinate segments to also remain
in the Window
segment. If we do, the window will be drawn twice: once from the
Window segment,
and once from the subordinate segments called by Window. Therefore, we must
delete at least the call_segment
elements, and we might as well delete the two subordinate segments
as well. Deleting Display List Segments |  |
To delete elements from a segment, you can use the delete_eles routine, as described
above. To delete whole segments, use delete_segment: delete_segment(〈fildes〉,
〈segno〉);
where: 〈segno〉
is an integer specifying the segment to be deleted.
Or, to simultaneously delete a segment as well as all the
elements in the display list that refer to that segment, you can
call delete_segment_and_references.
The parameters are the same as for delete_segment.
And, since it is easier, let's use delete_segment_and_references
in our example. These operations need to be done: In order to see exactly what is going
on in the display list, let's print out the pertinent segments,
using the same subroutine as before. At this point, it's a "before"
picture of the Window
segment, and it's labelled to that effect. Set the element pointer to the null element. Copy the Frame
segment into the current segment. It goes immediately after the
current element, which, at this point, is the null element. After
the copy, the element pointer will be pointing to the last element
copied into the current element. Copy the Panes
segment into the current segment. It goes immediately after the
current element, which is now the last element element copied from
Frame. Close the Window
segment. The call_segment
elements (referring to Frame
and Panes) are
still in the Window
segment, but these are deleted at this point by calls to delete_segment_ and_references. Print an "after" picture of the Window segment, labelled
as such.
In code, this would look like: printf("Window segment before consolidation:\n"); PrintSegment(fildes, Window); open_segment(fildes, Window, AppendOff, DisplayOff); set_ele_ptr(fildes, 0); /* the null element */ copy_segment(fildes, Frame); /* get elements from "Frame" */ copy_segment(fildes, Panes); /* get elements from "Panes" */ close_segment(fildes); delete_segment_and_references(fildes, Frame); /* delete frame seg */ delete_segment_and_references(fildes, Panes); /* delete panes seg */ printf("\nWindow segment after consolidation:\n"); PrintSegment(fildes, Window); |
The non-graphical output of this section of the program looks
like this: Window segment before consolidation: ELEMENT 0 call_segment( fd, 1131) call_segment( fd, 1132) Window segment after consolidation: ELEMENT 0 rectangle( fd, 5, 2, 7, 4) rectangle( fd, 5.1, 2.1, 5.95, 2.95) rectangle( fd, 6.05, 2.1, 6.9, 2.95) rectangle( fd, 6.05, 3.05, 6.9, 3.9) rectangle( fd, 5.1, 3.05, 5.95, 3.9) |
As you can see, before consolidation, the Window segment called the
Frame segment
(1131) and the
Panes segment
(1132). After
consolidation, the Window
segment contains the former contents of those two subordinate segments. Changing Segment References |  |
To "change a segment reference" means to change all call_segment or execute_segment elements
that refer to Segment n, in the entire
display list, conditional or not, to refer to Segment m.
That is, it replaces all call_segment,
cond_call_segment,
execute_segment,
and cond_execute_segment
elements that call a particular segment with corresponding elements
that call a different segment. A typical use for this functionality is when the user of an
interactive graphics program says something like, "Make all of these
look like that one." All occurrences calling a particular segment
can be converted so they call the desired segment instead. The Display List routine that does this is change_segment_references;
its syntax is: change_segment_references(〈fildes〉,
〈old_segno〉, 〈new_segno〉);
where: 〈old_segno〉
is an integer specifying the current name of the segment —
that one that you no longer want to be called. 〈new_segno〉
is an integer specifying the new name to be referenced by the changed
"calling" elements.
The actions of change_segment_references
are illustrated below. Assume the existence of a segment 2, a segment
3, and a segment 7, which calls segment 3: After executing change_segment_references(fildes,
3, 2); the display list looks like this. The results
are the same regardless of which segment was open at the time of
the change_segment_references: In our example, we want the old window — the square
one — to be replaced by a new octagonal one. Thus, the
Wall segment,
which currently calls segment Window,
must be changed to call the octagonal window segment, named NewWindow. And at some point,
the octagonal window must be defined in terms of Starbase primitives.
Remember, a segment need not be defined before it is called: an
empty segment with the appropriate name will be created if a nonexistent
segment is referenced. Specifically, what we want to do is this: Print the Wall segment to show that
it originally calls the old, rectangular window (even though it
is now consolidated into a single segment). Draw the house. It indeed uses the rectangular window.
Pause until the user presses Return
to go on. This "pausing" sequence, which is called more than once
in this program, is implemented via a C macro called NewImage. Define the octagonal window segment (called NewWindow), using Starbase
primitives. For reasons of brevity, another macro was used here:
triangle. You
pass it 〈fildes〉, x1,
y1, x2,
y2, x3,
y3, and it draws
a triangle. Using change_segment_references,
modify the Wall
segment so that it calls NewWindow
instead of Window. Print the Wall
segment so you can see that the called segment number is indeed
different. Redraw the house. It now has an octagonal window.
In code, this looks like the following:  |
/*- draw the original house with consolidated window segment */ printf("\\nWall segment (original):\\n"); PrintSegment(fildes, Wall); refresh_segment(fildes, House); NewImage; /*- Section 1.1.4: Define the octagonal window's structure */ open_segment(fildes, NewWindow, AppendOff, DisplayOff); curve_resolution(fildes, STEP_SIZE, 45 deg, 45 deg, 45 deg, 45 deg); ellipse(fildes, 1.08, 1.08, 6.0, 3.0, 22.5 deg);/* window frame */ rectangle(fildes, 5.65, 2.65, 6.35, 3.35); /* central square */ rectangle(fildes, 5.05, 2.65, 5.60, 3.35); /* west rectangle */ triangle(fildes, 5.60, 3.40, 5.60, 3.95, 5.05, 3.40); /* NW triangle */ rectangle(fildes, 5.65, 3.40, 6.35, 3.95); /* north rectangle */ triangle(fildes, 6.40, 3.40, 6.95, 3.40, 6.40, 3.95); /* NE triangle */ rectangle(fildes, 6.40, 2.65, 6.95, 3.35); /* east rectangle */ triangle(fildes, 6.40, 2.60, 6.40, 2.05, 6.95, 2.60); /* SE triangle */ rectangle(fildes, 5.65, 2.05, 6.35, 2.60); /* south rectangle */ triangle(fildes, 5.60, 2.60, 5.05, 2.60, 5.60, 2.05); /* SW triangle */ close_segment(fildes); /*- change calls to square window to calls to octagonal window */ change_segment_references(fildes, Window, NewWindow); printf("\\nWall segment (after \\"change_segment_references\\"):\\n"); PrintSegment(fildes, Wall); refresh_segment(fildes, House); |
 |
The house, with its new octagonal window, now looks like this: The non-graphical output of this section of the program looks
like this: Wall segment (original): call_segment( fd, 111) call_segment( fd, 112) call_segment( fd, 113) Wall segment (after "change_segment_references"): call_segment( fd, 111) call_segment( fd, 112) call_segment( fd, 114) |
Note that the call_segment
element referencing segment 113 (Window)
has been changed to 114 (NewWindow). Renaming Segments |  |
Although the above approach using change_segment_references
works perfectly well, it has the effect of changing the segment
number referenced by the calling segment. In our example, the third
element of the Wall
segment is modified: 113 changes to 114. If this is undesirable
in your application, you can change window styles without changing
called segment numbers. In other words, you can: Cause the house to change windows
from square to octagonal, and Retain a call to Window
(as opposed to NewWindow)
in the Wall segment.
The routines that address this need are: rename_segment(〈fildes〉,
〈old_segno〉, 〈new_segno〉); rename_segment_and_references(〈fildes〉,〈old_segno〉,〈new_segno〉);
where: 〈old_segno〉
is an integer that specifies the segment whose name is to be changed. 〈new_segno〉
is an integer that specifies the new name of the segment specified
by 〈old_segno〉.
This action is illustrated by the following diagrams. There
are several different variations on this theme of renaming segments.
The differences arise because referenced segments may or may not
exist, and an affected segment may or may not be referred to from
elsewhere. These possibilities are shown below: Only the first segment exists.Before renaming, assume the display list looks like: After renaming with rename_segment(fildes,
3, 2); the display list looks like this: This is perhaps the most intuitive of all the possible scenarios
involving rename_segment. Both segments exist; references exist.Before renaming, assume the display list looks like: After renaming Segment 3 to Segment 2 via rename_segment(fildes, 3, 2);
the display list has this configuration: As before, the original Segment 2 is destroyed by renaming
another segment to its name. Segment 3, however, is still required
to exist because it is referenced from elsewhere. So, in the tradition
of Display List, the reference to a nonexistent segment causes an
empty segment by that name to be created. Both segments exist; no references to either one.Before renaming, assume the display list looks like: After renaming Segment 3 to Segment 2 via rename_segment(fildes, 3, 2);
the display list has this configuration: As you can see, the original Segment 2 is destroyed by renaming
another segment to its name. Only the second segment exists.Before renaming, assume the display list looks like: After renaming with rename_segment(fildes,
3, 2); the display list looks like this: What happened was this. When a reference to the nonexistent
Segment 3 was made, Display List noticed and created an empty Segment
3. Then it continues the renaming operation as if it were in the
category of "Both segments exist; no references to either one,"
as described above. In our house example, we wish to install the new octagonal
window without requiring segment Wall to call NewWindow instead of Window. The first thing to
do is to put things back the way they were: reinstall the old rectangular
window by calling change_segment_references: change_segment_references(fildes, NewWindow, Window); |
Now we can do the appropriate renaming: #define OldWindow 9999 . . . rename_segment(fildes, Window, OldWindow); rename_segment(fildes, NewWindow, Window); |
And finally, print and refresh the segment so we can see if
the operation was successful: printf("\\nWall segment (after \\"rename_segment\\"s):\\n"); PrintSegment(fildes, Wall); refresh_segment(fildes, House); |
The graphical output from this invocation of refresh_segment is the same
as above, but the segment Wall
is different. Here is what the program prints at this point: Wall segment (after "rename_segment"s): call_segment( fd, 111) call_segment( fd, 112) call_segment( fd, 113) |
A quick check of the original state of segment Wall will satisfy you that
while the child segment name is the same as before, the contents
of the segment referenced by Window
are now different. Here is the whole program. It draws three pictures: the first
is the original house with the rectangular window, and the last
two are the house with the octagonal window (shown a few pages ago).  |
#include <starbase.c.h> /* get Starbase definitions */ #include <dl.c.h> /* get Display List definitions */ #include <stdio.h> /* get standard I/O functions */ #include <math.h> /* link with library "-lm" */ #define AppendOff FALSE /* sent to "open_segment" */ #define AppendOn TRUE /* sent to "open_segment" */ #define DisplayOff FALSE /* sent to "open_segment" */ #define DisplayOn TRUE /* sent to "open_segment" */ #define AbbreviateArrays TRUE /* sent to "print_element" */ #define AlwaysCurrent FALSE /* sent to "buffer_mode" */ #define Edges TRUE /* sent to "interior_style" */ #define NoFlags FALSE /* sent to "polygon2d" */ #define House 1 /* \\ */ #define Wall 11 /* \\ */ #define WallSurface 111 /* \\ These mnemonic tokens */ #define Door 112 /* \\ are associated with */ #define Window 113 /* \\ segment numbers that */ #define Frame 1131 /* / indicate the house's */ #define Panes 1132 /* / hierarchical */ #define Roof 12 /* / structure. */ #define RoofSurface 121 /* / */ #define Chimney 122 /* / */ #define NewWindow 114 /* for the octagonal window */ #define OldWindow 9999 /* get it out of the way */ #define deg *M_PI/180 /* convert degrees to radians */ #define triangle(fd,x1,y1,x2,y2,x3,y3) move2d(fd,x1,y1), draw2d(fd,x2,y2); \\ draw2d(fd,x3,y3); draw2d(fd,x1,y1); #define NewImage make_picture_current(fildes); \\ fprintf(stderr, \\ "Press [Return] to go on. "); \\ getchar(); \\ clear_view_surface(fildes)main() /* program "SegHouse.c" */ { int fildes; /* file descriptor */ if ((fildes = gopen(getenv("SB_OUTDEV"), OUTDEV, NULL, INIT)) == -1) { fprintf(stderr, "%s %s\\n", "Error: gopen failed using environment", "variable SB_OUTDEV."); exit(-1); } buffer_mode(fildes, AlwaysCurrent); vdc_extent(fildes, 0.0, 0.0, 0.0, 1.25, 1.0, 0.0); view_window(fildes, 0.0, 0.0, 10.0, 8.0); interior_style(fildes, INT_HOLLOW, Edges); /*=== define the display list segments comprising the house =====*/ /*- Section 1: Define the house's structure -*/ open_segment(fildes, House, AppendOff, DisplayOff); call_segment(fildes, Wall); call_segment(fildes, Roof); close_segment(fildes); /*- Section 1.1: Define the wall's structure */ open_segment(fildes, Wall, AppendOff, DisplayOff); call_segment(fildes, WallSurface); call_segment(fildes, Door); call_segment(fildes, Window); close_segment(fildes); /*- Section 1.1.1: Define the wall surface's structure */ open_segment(fildes, WallSurface, AppendOff, DisplayOff); rectangle(fildes, 2.0, 1.0, 8.0, 5.0); close_segment(fildes); /*- Section 1.1.2: Define the door's structure */ open_segment(fildes, Door, AppendOff, DisplayOff); rectangle(fildes, 2.5, 1.0, 4.2, 4.2); ellipse(fildes, 0.06, 0.06, 2.65, 2.3); close_segment(fildes); /*- Section 1.1.3: Define the square window's structure -*/ open_segment(fildes, Window, AppendOff, DisplayOff); call_segment(fildes, Frame); call_segment(fildes, Panes); close_segment(fildes); /*- Section 1.1.3.1: Define the square window frame's structure -*/ open_segment(fildes, Frame, AppendOff, DisplayOff); rectangle(fildes, 5.0, 2.0, 7.0, 4.0); close_segment(fildes); /*- Section 1.1.3.2: Define the square window panes' structure */ open_segment(fildes, Panes, AppendOff, DisplayOff); rectangle(fildes, 5.1, 2.1, 5.95, 2.95); rectangle(fildes, 6.05, 2.1, 6.9, 2.95); rectangle(fildes, 6.05, 3.05, 6.9, 3.9); rectangle(fildes, 5.1, 3.05, 5.95, 3.9); close_segment(fildes); /*- Section 1.2: Define the roof's structure */ open_segment(fildes, Roof, AppendOff, DisplayOff); call_segment(fildes, RoofSurface); call_segment(fildes, Chimney); close_segment(fildes); /*- Section 1.2.1: Define the roof surface's structure */ open_segment(fildes, RoofSurface, AppendOff, DisplayOff); { static float RoofStructure[3][2] = { 1.0, 5.0, 9.0, 5.0, 5.0, 7.0}; polygon2d(fildes, RoofStructure, 3, NoFlags); } close_segment(fildes); /*- Section 1.2.2: Define the chimney's structure -*/ open_segment(fildes, Chimney, AppendOff, DisplayOff); { static float ChimneyStructure[4][2] = { 6.0, 6.5, 7.0, 6.0, 7.0, 7.5, 6.0, 7.5}; polygon2d(fildes, ChimneyStructure, 4, NoFlags); } close_segment(fildes); /*=== modify the house and re-draw it ===========================*/ /*- combine window segments into one */ printf("Window segment before consolidation:\\n"); PrintSegment(fildes, Window); open_segment(fildes, Window, AppendOff, DisplayOff); set_ele_ptr(fildes, 0); /* the null element */ copy_segment(fildes, Frame); /* get elements from "Frame" */ copy_segment(fildes, Panes); /* get elements from "Panes" */ close_segment(fildes); delete_segment_and_references(fildes, Frame); /* delete frame seg */ delete_segment_and_references(fildes, Panes); /* delete panes seg */ printf("\\nWindow segment after consolidation:\\n"); PrintSegment(fildes, Window); /*- draw the original house with consolidated window segment */ printf("\\nWall segment (original):\\n"); PrintSegment(fildes, Wall); refresh_segment(fildes, House); NewImage; /*- Section 1.1.4: Define the octagonal window's structure */ open_segment(fildes, NewWindow, AppendOff, DisplayOff); curve_resolution(fildes, STEP_SIZE, 45 deg, 45 deg, 45 deg, 45 deg); ellipse(fildes, 1.08, 1.08, 6.0, 3.0, 22.5 deg); /* window frame */ rectangle(fildes, 5.65, 2.65, 6.35, 3.35); /* central square */ rectangle(fildes, 5.05, 2.65, 5.60, 3.35); /* west rectangle */ triangle(fildes, 5.60, 3.40, 5.60, 3.95, 5.05, 3.40); /* NW triangle */ rectangle(fildes, 5.65, 3.40, 6.35, 3.95); /* north rectangle */ triangle(fildes, 6.40, 3.40, 6.95, 3.40, 6.40, 3.95); /* NE triangle */ rectangle(fildes, 6.40, 2.65, 6.95, 3.35); /* east rectangle */ triangle(fildes, 6.40, 2.60, 6.40, 2.05, 6.95, 2.60); /* SE triangle */ rectangle(fildes, 5.65, 2.05, 6.35, 2.60); /* south rectangle */ triangle(fildes, 5.60, 2.60, 5.05, 2.60, 5.60, 2.05); /* SW triangle */ close_segment(fildes); /*- change calls to square window to calls to octagonal window */ change_segment_references(fildes, Window, NewWindow); printf("\\nWall segment (after \\"change_segment_references\\"):\\n"); PrintSegment(fildes, Wall); refresh_segment(fildes, House); NewImage; /*- but we don't want the segment name to be changed... -*/ change_segment_references(fildes, NewWindow, Window); /* revert */ rename_segment(fildes, Window, OldWindow); rename_segment(fildes, NewWindow, Window); printf("\\nWall segment (after \\"rename_segment\\"s):\\n"); PrintSegment(fildes, Wall); refresh_segment(fildes, House); gclose(fildes); }/******************************************************************/ PrintSegment(fildes, SegmentNo) int fildes; /* file descriptor */ int SegmentNo; /* segment to print */ { int AtTop, AtBottom; /* booleans */ open_segment(fildes, SegmentNo, AppendOff, DisplayOff); set_ele_ptr(fildes, 0); do { print_element(fildes, AbbreviateArrays); inq_ele_ptr_at_bound(fildes, &AtTop, &AtBottom); set_ele_ptr_relative(fildes, 1); } while (! AtBottom); close_segment(fildes); } |
 |
|