 |
» |
|
|
 |
Since many primitives may fall within the pick aperture, many
primitives may cause a hit. If multiple hits can occur, there should
be some way of deciding which hit to report to the user during an
inq_pick_path. Display List's default behavior when checking for hits —
its default pick mode — is to stop
looking for hits as soon as it finds the first one. This makes hit
detection fast, but the first one to be noticed may or may not be
the one you want. This is what happened in the previous program
when you attempted to pick the door: the area covered by the door
was also covered by the wall. The wall surface was drawn before
the door; therefore, it was noticed earlier in the pick traversal.
Since the default pick mode is for Display List to stop looking
after noticing the first hit, the subsequent possibilities are not
noticed. Specifying Your Own Hit-Detection Mechanism |  |
The way Display List determines which hit to accept is determined
by the routine set_pick_mode: set_pick_mode(〈fildes〉,
〈check_hit〉);
where: 〈check_hit〉
is the name of a procedure which is to determine which hit to accept.
The syntax for this procedure is: 〈check_hit〉
(〈fildes〉, 〈result〉);
where: 〈fildes〉
is a pointer to the integer containing the
file descriptor. 〈result〉
is a pointer to an integer that contains the bitwise or
of the desired pick-control tokens. The legal values are: - PK_STOP
Stop the pick traversal of the current segment. - PK_CONTINUE
Continue the traversal of the segment with the next
element. - PK_ACCEPT
Accept the hit. The path to the current primitive
is remembered for subsequent retrieval via inq_pick_path. If pick traversal
is continued, subsequent hits can take precedence if they too include
the PK_ACCEPT
flag. - PK_NEVER_HIT
Do not call the check_hit procedure any more, even
if subsequent primitives are hit. This is normally used to complete
the traversal of a segment after a hit has been accepted. This may
be desirable to set the Starbase state (its attributes and matrix
stack) to what it would be after the segment was displayed.
By default, the first hit is accepted, and all others are
ignored.
As mentioned above, the first hit is accepted. Effectively,
this is implemented by having a display list "wake up" with the
following in effect: set_pick_mode(fildes, PK_FIRST); |
The pick mode PK_FIRST
(which is implemented as a function that always returns PK_ACCEPT | PK_CONTINUE | PK_NEVER_HIT)
defines which primitive, of all of the primitives hit, is returned
in the pick path. This procedure specified in set_pick_mode is called by
the pick_from_segment
procedure each time a potential hit occurs. User-Defined Hit-Checking Procedures |  |
If PK_FIRST
does not address an application's needs, the application programmer
can write a hit-checking procedure of his own. This procedure can
be called by set_pick_mode
in exactly the same way as the predefined mode. A "custom pick mode"
procedure can be used in the basic picking sequence simply by calling
set_pick_mode
with the desired hit-checking procedure: void 〈procedure_name〉(); . . . set_pick_mode(fildes, 〈procedure_name〉); |
The next step is to actually write the procedure. It is helpful
to understand the predefined hit-checking procedure before attempting
to write one. When pick_from_segment
detects a hit, it calls the active hit-checking procedure to determine
what to do with the hit. The procedure is called with two parameters:
the 〈fildes〉 (passed
by reference, not by value) that was passed
to pick_from_segment
and a return parameter, 〈pick_control〉,
which indicates how the pick loop in pick_from_segment
should react to the hit. The syntax for the hit-checking procedure declaration is: void custom_hit_check_procedure(fildes, pick_control) int *fildes, *pick_control; |
Of course, you can call your hit-checking procedure anything
you like; you don't have to call it custom_hit_check_procedure.  |  |  |  |  | NOTE: The fildes
parameter to your custom hit-check procedure is a pointer rather
than a simple variable in order to be usable with Fortran77
(as well as C and Pascal). |  |  |  |  |
When your hit-checking procedure is called, it can take any
number of actions. The path to the primitive that caused the procedure
to be called is accessible through inq_pick_path_depth
and inq_pick_path
calls. The procedure can get its bearings, so to speak, as to where
in the segment network the primitive is located. Using this location,
the type of primitive and possibly supplemental information defined
by the program, a decision can be made to accept or reject the hit. Your hit-checking procedure can control pick traversal based
on its hit decision. If you desire to accept
the first hit, the pick traversal loop can be instructed to do so.
If you desire to continue on and find the next hit, the pick traversal
loop can be instructed to do so. If you desire to select only hits
on selected kinds of primitives, the pick traversal loop can be
instructed to do so. The traversal loop can be made to abort traversal
altogether returning back up through pick_from_segment. Your hit-checking procedure controls the hit selection by
returning various combinations of predefined pick loop control values
in the pick_control
return parameter. The controls are in two categories: hit
acceptance and traversal control. Pick Loop Controls: Hit AcceptanceThere is only one token that deals with hit acceptance: if
it is included in the return parameter from your hit-checking procedure,
the hit is accepted. If it is not included, the hit is not accepted. - PK_ACCEPT
Record the path to the current primitive for subsequent
retrieval via inq_pick_path
(overwrites any previous recordings).
Pick Loop Controls: Traversal ControlThere are three traversal-control tokens defined. Exactly
one of the first two must be used in every case: - PK_STOP
Stop traversal and exit the pick loop. - PK_CONTINUE
Continue pick traversal and seek a subsequent hit. - PK_NEVER_HIT
Ignore any subsequent hits. Transforms, attributes,
etc., are applied, but primitives are ignored in a special fast
traversal routine. Only useful with PK_CONTINUE.
A control from each category can be selected by oring
the respective constants together. A few examples of possible pick_control return values
are described below. If the hit should not be accepted,
the pick_control
value would be: If the hit should be accepted/recorded, but the
pick loop should continue looking for other hits, the pick_control value would
be: (The "|"
is C syntax for a bitwise or.) If the hit should be accepted and pick loop should
stop, the pick_control
value would be: If the hit should be accepted and pick loop should
complete without checking any more hits, the pick_control value would
be: PK_ACCEPT | PK_NEVER_HIT | PK_CONTINUE
In addition to these controls, the check_hit procedure can do
things "off to the side" such as saving information in static data
structures to keep track of number of hits, how deep in the segment
it is, etc. Enough information is at hand now to actually write a hit-checking
procedure. The following few sections describe several different
variations on the theme. Example Hit-Checking Procedure: Quit After the First
HitThe following procedure accepts the first hit and quits. void FirstHitAndQuit(); /* sent to "set_pick_mode" */ . . . set_pick_mode(display, FirstHitAndQuit); . . . void FirstHitAndQuit(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { *PickControl = PK_ACCEPT | PK_STOP; } |
When pick_from_segment
finds a hit, FirstHitAndQuit
will be called. The procedure will return immediately and cause
pick_from_segment's
traversal loop to record the path to the hit primitive and then
quit traversal. Before pick_from_segment
exits, the path is copied to the device's pick path. The program
can then retrieve it with inq_pick_path. Example Hit-Checking Procedure: Accept the Last HitThe following hit-checking primitive retrieves the last hit. void LastHit(); /* sent to "set_pick_mode" */ . . . set_pick_mode(display, LastHit); . . . void LastHit(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { *PickControl = PK_ACCEPT | PK_CONTINUE; } |
At each hit, there is a possibility of a subsequent hit, so
the hit-checking must keep going until the entire segment tree has
been traversed. Thus, the PK_CONTINUE
at each hit. Also, there is no guarantee that there will be a subsequent
hit, so every one is accepted (every accepted hit takes precedence
over the previous one). Example Hit-Checking Procedure: Note All HitsThis hit-checking procedure records all
pick paths intersecting the pick aperture. These are stored in an
n×3 array for subsequent processing.  |
#define PathDepthMax 15 /* maximum depth of pick path */ #define PathMax 10 /* maximum number of pick paths */ typedef struct { int Depth; int Path[PathDepthMax][3]; } PickPathType; PickPathType Hits[PathMax]; /* array of pick paths */ int NumHits; /* number of hits in "Hits" */ void AllHits(); /* sent to "set_pick_mode" */ . . . set_pick_mode(display, AllHits); . . . void AllHits(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { int Depth; /* depth of current pick path */ if (NumHits > PathMax) { printf("Number of pick paths exceeds array size; terminating.\n"); *PickControl = PK_NEVER_HIT | PK_CONTINUE; } else { inq_pick_path_depth(*fildes, &Depth); if (Depth > PathDepthMax) { printf("Pick path depth exceeds array size; ignoring.\n"); *PickControl = PK_CONTINUE; } else { Hits[NumHits].Depth = Depth; inq_pick_path(*fildes, Hits[NumHits].Path); NumHits++|; *PickControl = PK_CONTINUE; } } } |
 |
At every hit, the pick path and its depth are stored into
the appropriate element of the array of structures Hits. *PickControl is set to PK_CONTINUE, deliberately
excluding PK_ACCEPT.
The inclusion of PK_CONTINUE
causes pick traversal to keep going, and the exclusion of PK_ACCEPT causes the 〈found〉
parameter of pick_from_segment
to always return FALSE.
If an error occurs — either too large of a pick depth or
too many different pick paths — an error message is printed,
and *PickControl
is set to PK_NEVER_HIT,
thus causing no more hits to be considered. Example Hit-Checking Procedure: Hits on Selected Primitive
TypesYou can define a hit-checking procedure to accept hits only
if they are on one of a desired set of primitive types. As an example,
the following procedure accepts hits only if the primitive is a
polygon2d. Of
course, you can accept or reject whatever set of primitives you
choose.  |
#define PathDepthMax 15 /* maximum depth of pick path */ #define Segment 0 /* \ These tokens make the */ #define ClosestLabel 1 /* > interpretation of the */ #define OffsetFromLabel 2 /* / "PickPath" array easier. */ . . . void LastHitAndType(); /* sent to "set_pick_mode" */ . . . set_pick_mode(display, LastHitAndType); . . . void LastHitAndType(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { int PickPathDepth; /* the depth of the pick path */ int PickPath[PathDepthMax][3]; /* ret. from "inq_pick_path" */ int ElementType; /* returned from "inq_ele_type" */ inq_pick_path_depth(*fildes, &PickPathDepth); if (PickPathDepth > PathDepthMax) { printf("Pick path depth exceeds array size; terminating.\n"); *PickControl = PK_NEVER_HIT | PK_CONTINUE; } else { inq_pick_path(*fildes, PickPath); open_segment(*fildes, PickPath[PickPathDepth - 1][Segment], AppendOff, DisplayOff); set_ele_ptr_relative_to_label(*fildes, PickPath[PickPathDepth - 1][ClosestLabel], PickPath[PickPathDepth - 1][OffsetFromLabel]); inq_ele_type(*fildes, &ElementType); close_segment(*fildes); if (ElementType == OP_POLYGON2D) *PickControl = PK_ACCEPT | PK_CONTINUE; else *PickControl = PK_CONTINUE; } } |
 |
Again, rudimentary error-checking is done, as in the previous
example. If the pick path depth is satisfactory, the pick path is
retrieved via inq_pick_path.
As mentioned previously, the pick path is an n×3
integer matrix whose: First column is the segment number, Second column is the closest preceding label, and Third column is the offset from that label to the
element in the path.
The first column of the last row is the segment number containing
the picked primitive; this segment is then opened. The last two
columns are use to set the element pointer to the picked element.
Then, the element type is interrogated by inq_ele_type. If the element
is not of an acceptable type (only polygon2d
is acceptable in this example), the hit is ignored, and the search
continues. If the element is of an acceptable
type, the hit is accepted, but still, the search continues: we are
looking for the last acceptable hit.  |  |  |  |  | NOTE: The display list state (specifically, the current element
and open segment) is changed by opening segments and moving the
element pointer. If this is important to your application, you can
interrogate the current segment with inq_open_segment_and_mode
and the current element position with inq_ele_ptr,
and restore the values after the display list manipulation. |  |  |  |  |
Example Program with Hit-Checking ProceduresThe following example program illustrates the use of the four
example hit-checking procedures discussed above. The program is
the house example, with the picking code added, as well as some
labels. Run the program several times, trying out each of the four
procedures (you'll need to specify the desired hit-checking procedure
name in the set_pick_mode
call).  |
#include <starbase.c.h> /* get Starbase definitions */ #include <dl.c.h> /* get Display List definitions */ #include <stdio.h> /* get standard I/O functions */ #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 AlwaysCurrent FALSE /* sent to "buffer_mode" */ #define Edges TRUE /* sent to "interior_style" */ #define NoFlags FALSE /* sent to "polygon2d" */ #define On TRUE /* sent to "seg_control" */ #define Off FALSE /* sent to "seg_control" */ #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 WallLabel 1 /* \\ */ #define DoorLabel 2 /* \\ */ #define DoorKnobLabel 3 /* \\ These are tokens for */ #define WindowLabel 4 /* / Display List labels. */ #define FrameLabel 5 /* / */ #define PanesLabel 6 /* / */ #define PathDepthMax 15 /* maximum depth of pick path */ #define PathMax 10 /* maximum number of pick paths */typedef struct { int Depth; int Path[PathDepthMax][3]; } PickPathType; PickPathType Hits[PathMax]; /* array of pick paths */ int NumHits; /* number of hits in "Hits" */ main() /* program "HitCheck.c" */ { int display, locator; /* file descriptors */ if ((display = gopen(getenv("SB_OUTDEV"), OUTDEV, NULL, INIT)) == -1) { fprintf(stderr, "%s %s\\n", "Error: gopen failed using environment", "variable SB_OUTDEV."); exit(-1); } if ((locator = gopen(getenv("SB_INDEV"), INDEV, getenv("SB_INDRIVER"), INIT)) == -1) { fprintf(stderr, "%s %s\\n", "Error: gopen failed using environment", "variables SB_INDEV and SB_INDRIVER."); gclose(display); exit(-1); } printf("\\033h\\033J"); fflush(stdout); /* clear the alpha screen */ buffer_mode(display, AlwaysCurrent); vdc_extent(display, 0.0, 0.0, 0.0, 1.25, 1.0, 0.0); view_window(display, 0.0, 0.0, 10.0, 8.0); interior_style(display, INT_HOLLOW, Edges); |
 |
 |
/*=== define and draw the house ============================*/ /*- Section 1: Define the house's structure */ open_segment(display, House, AppendOff, DisplayOff); call_segment(display, Wall); call_segment(display, Roof); close_segment(display); /*- Section 1.1: Define the wall's structure -*/ open_segment(display, Wall, AppendOff, DisplayOff); dl_label(display, WallLabel); call_segment(display, WallSurface); call_segment(display, Door); call_segment(display, Window); close_segment(display); /*- Section 1.1.1: Define the wall surface's structure -*/ open_segment(display, WallSurface, AppendOff, DisplayOff); rectangle(display, 2.0, 1.0, 8.0, 5.0); close_segment(display); /*- Section 1.1.2: Define the door's structure -*/ open_segment(display, Door, AppendOff, DisplayOff); dl_label(display, DoorLabel); rectangle(display, 2.5, 1.0, 4.2, 4.2); dl_label(display, DoorKnobLabel); ellipse(display, 0.06, 0.06, 2.65, 2.3); close_segment(display); /*- Section 1.1.3: Define the window's structure -*/ open_segment(display, Window, AppendOff, DisplayOff); dl_label(display, WindowLabel); call_segment(display, Frame); call_segment(display, Panes); close_segment(display); /*- Section 1.1.3.1: Define the window frame's structure -*/ open_segment(display, Frame, AppendOff, DisplayOff); dl_label(display, FrameLabel); rectangle(display, 5.0, 2.0, 7.0, 4.0); close_segment(display); /*- Section 1.1.3.2: Define the window panes' structure */ open_segment(display, Panes, AppendOff, DisplayOff); dl_label(display, PanesLabel); rectangle(display, 5.1, 2.1, 5.95, 2.95); rectangle(display, 6.05, 2.1, 6.9, 2.95); rectangle(display, 6.05, 3.05, 6.9, 3.9); rectangle(display, 5.1, 3.05, 5.95, 3.9); close_segment(display); /*- Section 1.2: Define the roof's structure -*/ open_segment(display, Roof, AppendOff, DisplayOff); call_segment(display, RoofSurface); call_segment(display, Chimney); close_segment(display); /*- Section 1.2.1: Define the roof surface's structure -*/ open_segment(display, RoofSurface, AppendOff, DisplayOff); { static float RoofStructure[3][2] = { 1.0, 5.0, 9.0, 5.0, 5.0, 7.0}; polygon2d(display, RoofStructure, 3, NoFlags); } close_segment(display); /*- Section 1.2.2: Define the chimney's structure */ open_segment(display, 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(display, ChimneyStructure, 4, NoFlags); } close_segment(display); refresh_segment(display, House); |
 |
 |
/*=== allow user to pick parts of the house ==============*/ #define Segment 0 /* \\ These tokens make the */ #define ClosestLabel 1 /* > interpretation of the */ #define OffsetFromLabel 2 /* / "PickPath" array easier. */ #define SemiPickAperture 0.01 /* the "radius" of the pick aperture */ #define SmallTrackingCross 3 /* sent to "echo_type" */ { int Valid; /* returned from "request_locator" */ float X = 1.0, Y = 1.0, Z; /* returned from "request_locator" */ int Found; /* returned from "pick_from_segment" */ int PickPathDepth; /* the depth of the pick path */ int PickPath[PathDepthMax][3]; /* ret. from "inq_pick_path" */ void FirstHitAndQuit(); /* sent to "set_pick_mode" */ void LastHit(); /* sent to "set_pick_mode" */ void AllHits(); /* sent to "set_pick_mode" */ void LastHitAndType(); /* sent to "set_pick_mode" */ int Hit, I, Done = FALSE; /* loop control variables */ track(locator, display, 1); echo_type(display, 0, SmallTrackingCross, 0.0, 0.0, 0.0); highlight_type(display, DASH); highlight_attributes(display, HL_STYLE); set_pick_mode(display, LastHit); do { request_locator(locator, 1, 5.0, &Valid, &X, &Y, &Z); if (!Valid) continue; set_pick_window(display, X - SemiPickAperture, Y - SemiPickAperture, X + SemiPickAperture, Y + SemiPickAperture); pick_from_segment(display, House, &Found); PickPathDepth = 0; if (Found) { inq_pick_path_depth(display, &PickPathDepth); inq_pick_path(display, PickPath); printf("\\033h\\033JPick path depth: %d\\n", PickPathDepth); for (I = 0; I < PickPathDepth; I++) printf(" %s: %4d; %s: %d; %s: %d.\\n", "Segment", PickPath[I][Segment], "Closest preceding label", PickPath[I][ClosestLabel], "Offset from that label", PickPath[I][OffsetFromLabel]); seg_control(display, PickPath[PickPathDepth - 1][Segment], HIGHLIGHT, On); clear_view_surface(display); refresh_segment(display, House); sleep(1); seg_control(display, PickPath[PickPathDepth - 1][Segment], HIGHLIGHT, Off); clear_view_surface(display); refresh_segment(display, House); Done = (PickPath[PickPathDepth - 1][Segment] == Chimney); } else { if (NumHits > 0) { printf("\\033h\\033JNumber of separate pick paths: %d\\n", NumHits); for (Hit = 0; Hit < NumHits; Hit++) { printf(" Pick path depth: %d\\n", Hits[Hit].Depth); for (I = 0; I < Hits[Hit].Depth; I++) printf(" %s: %4d; %s: %d; %s: %d.\\n", "Segment", Hits[Hit].Path[I][Segment], "Closest preceding label", Hits[Hit].Path[I][ClosestLabel], "Offset from that label", Hits[Hit].Path[I][OffsetFromLabel]); } NumHits = 0; Done = (Hits[NumHits].Path[Hits[NumHits].Depth - 1] [Segment] == Chimney); } else printf("\\033h\\033JNothing hit.\\n"); } } while (!Done); } gclose(locator); gclose(display);
|
 |
 |
}/************************************************************/ void FirstHitAndQuit(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { *PickControl = PK_ACCEPT | PK_STOP; } /************************************************************/ void LastHit(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { *PickControl = PK_ACCEPT | PK_CONTINUE; } /***********************************************************/ void AllHits(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { int Depth; /* depth of current pick path */ if (NumHits > PathMax) { printf("Number of pick paths exceeds array size; terminating.\\n"); *PickControl = PK_NEVER_HIT | PK_CONTINUE; } else { inq_pick_path_depth(*fildes, &Depth); if (Depth > PathDepthMax) { printf("Pick path depth exceeds array size; ignoring.\\n"); *PickControl = PK_CONTINUE; } else { Hits[NumHits].Depth = Depth; inq_pick_path(*fildes, Hits[NumHits].Path); NumHits++; *PickControl = PK_CONTINUE; } } }/**********************************************************/ void LastHitAndType(fildes, PickControl) int *fildes; /* file descriptor */ int *PickControl; /* control word */ { int PickPathDepth; /* the depth of the pick path */ int PickPath[PathDepthMax][3]; /* ret. from "inq_pick_path" */ int ElementType; /* returned from "inq_ele_type" */ inq_pick_path_depth(*fildes, &PickPathDepth); if (PickPathDepth > PathDepthMax) { printf("Pick path depth exceeds array size; ignoring.\\n"); *PickControl = PK_NEVER_HIT | PK_CONTINUE; } else { inq_pick_path(*fildes, PickPath); open_segment(*fildes, PickPath[PickPathDepth - 1][Segment], AppendOff, DisplayOff); set_ele_ptr_relative_to_label(*fildes, PickPath[PickPathDepth - 1][ClosestLabel], PickPath[PickPathDepth - 1][OffsetFromLabel]); inq_ele_type(*fildes, &ElementType); close_segment(*fildes); if (ElementType == OP_POLYGON2D) *PickControl = PK_ACCEPT | PK_CONTINUE; else *PickControl = PK_CONTINUE; } } |
 |
Notice that the program prints the pick path(s) for each hit-checking
procedure. The output for the various runs are as follows. In each
case, the printed output is in response to picking the doorknob. Hit-Checking Procedure: "FirstHitAndQuit"Pick path depth: 3 Segment: 1; Closest preceding label: 0; Offset from that label: 1. Segment: 11; Closest preceding label: 1; Offset from that label: 1. Segment: 111; Closest preceding label: 0; Offset from that label: 1. |
The pick path is as follows: Traversal started in segment 1 (House) and, at the first
element into it, called another segment. The "first element into
it" is understood from an offset of 1
from the null element, referred to here as "label 0." The next segment traversed is 11 (Wall). At the first element
after label 1
(WallLabel),
this segment calls another. The next (and final) segment traversed in this pick
path is segment 111
(WallSurface).
Since this is the last line of the pick path, the label and offset
do not locate where this segment calls another, but where the element
is that contains the picked primitive. Indeed, any location close
to the doorknob is within the rectangle
forming the wall's surface. And, since the first hit is being accepted,
and the wall rectangle is drawn before the doorknob, the wall rectangle
is returned as being the accepted primitive.
Hit-Checking Procedure: "LastHit"Pick path depth: 3 Segment: 1; Closest preceding label: 0; Offset from that label: 1. Segment: 11; Closest preceding label: 1; Offset from that label: 2. Segment: 112; Closest preceding label: 3; Offset from that label: 1. |
The pick path is as follows: Traversal started in segment 1 (House) and, at the first
element into it, called another segment. The "first element into
it" is understood from an offset of 1
from the null element, referred to here as "label 0." The next segment traversed is 11 (Wall). At the second
element after label 1
(WallLabel),
this segment calls another. The next (and final) segment traversed in this pick
path is segment 112
(Door). Since
this is the last line of the pick path, the label and offset do
not locate where this segment calls another, but where the element
is that contains the picked primitive. Since the last
hit is being accepted, and the doorknob is drawn before the wall
rectangle (which was also hit), the doorknob is returned as being
the accepted primitive. The doorknob is the first element after
label 3 (DoorKnobLabel).
Hit-Checking Procedure: "AllHits"Number of separate pick paths: 3 Pick path depth: 3 Segment: 1; Closest preceding label: 0;Offset from that label: 1. Segment: 11; Closest preceding label: 1;Offset from that label: 1. Segment: 111; Closest preceding label: 0;Offset from that label: 1. Pick path depth: 3 Segment: 1; Closest preceding label: 0;Offset from that label: 1. Segment: 11; Closest preceding label: 1;Offset from that label: 2. Segment: 112; Closest preceding label: 2;Offset from that label: 1. Pick path depth: 3 Segment: 1; Closest preceding label: 0;Offset from that label: 1. Segment: 11; Closest preceding label: 1; Offset from that label: Segment: 112; Closest preceding label: 3;Offset from that label: 1. |
Picking the doorknob results in three distinct possibilities
for hits: The wall surface, as described in
the "FirstHitAndQuit" section, above. The Door
rectangle: it is the primitive in the first element after label
2 (DoorLabel). The doorknob, as described in the "LastHit" section,
above.
Hit-Checking Procedure: "LastHitAndType"The LastHitAndType
procedure only accepts hits of type OP_POLYGON2D.
As we saw above, in the "AllHits" section, there are three possible
pick paths. However, the wall and the door were drawn with rectangle, and the doorknob
with ellipse
— none of the possible pick paths specified a polygon2d primitive. Therefore,
no hits were accepted, and the message to that effect was printed: Miscellaneous Picking Topics |  |
Pick Traversal and Starbase StatePick traversal, like display traversal, can change the Starbase
state as a result of traversing and executing elements of the segment
network. If the traversed segment network is "neutral", i.e., if all
"push" operations (either on the matrix stack or the Starbase state
stack) have corresponding "pop" operations throughout the segment
network and attributes are kept in some known state, the Starbase
state will be the same as it was before pick traversal. If the structure
is "positive" (more pushes than pops) or "negative" (more pops than
pushes) then the Starbase state will not be the same as the state
before traversal. Subsequent graphics operations could be adversely
affected if some attempt is not made to bring Starbase back to a
known state (e.g., flushing the transform stack, etc.). The current
position is undefined after a call to pick_from_segment. Even if the structure is neutral, improper use of set_disp_traversal_control,
set_pick_traversal_control,
or cond_return
can polarize the structure by causing traversal to miss processing
critical push or pop operations, and bring about an unknown state. Performance: Locking the DisplayThe performance of pick traversal can be improved on graphics
display devices with bit-mapped displays by giving the pick traversal
process exclusive access to the display during the traversal. The
traversal_lock
call sets a flag indicating whether the traversal process should
lock the display during traversal. The default is to not lock the
display during traversal. When the display is locked, all other
processes attempting communication with the display will be blocked
until traversal completes. As mentioned in Chapter 8 “Filters and Name Sets”, the pick filter has an inclusion set
and an exclusion set, like the invisibility- and highlighting filters,
and it works in the same way. By default, all graphic primitives
are pickable. If a graphic primitive's name set includes any of
the names in the pick filter's inclusion set, but doesn't include
any of the names in its exclusion set, it is unpickable. Primitives
that are eliminated by the pick filter are effectively invisible
to the picking functions. That is, no graphic primitive eliminated
by the pick filter will ever be returned by a pick_from_segment call. The
invisibility filter also affects what parts of the display list
are pickable. Invisible primitives cannot be picked. The use of name sets and the invisibility filter is similar
to PHIGS usage. However, the Display List pick filter works in the
opposite manner from the PHIGS pick filters. In the Starbase Display
List, by default all visible primitives are pickable, and primitives
that are specified by the pick filter are unpickable. In PHIGS,
by default all primitives are not pickable;
only primitives that are specified by the pick filter are pickable. In order to simplify the port of PHIGS programs, Display List
provides a function set_pick_sense,
which allows programs to use pick filters in the PHIGS sense if
desired. By default, the pick sense is 0, which means that the pickable
primitives are those that are visible and not
specified by the pick filter. If the pick sense is set to nonzero,
the sense is reversed, meaning that the only pickable primitives
are those that are visible and that are specified by the pick filter.
Programmers that prefer PHIGS definition of pick filters should
set the pick sense to 1. Other users can leave the pick sense at
its default 0. The devices HP 98705, HP 98765, and HP 98766 support batched
picking. Instead of sending one primitive at a time to the device
and subsequently performing an inquire on each primitive (via pick_from_segment), several
primitives may be batched, sent to the device and inquired. Refer to the reference page DL_CONTROL
for information on batched picking. This procedure provides the
means to limit the number of primitives batched and allows for software
control over the pick stack.
|