Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
Starbase Display List Programmer's Manual: HP 9000 Series 700 Computers > Chapter 9 Picking

Hit Checking

» 

Technical documentation

» Feedback
Content starts here

 » Table of Contents

 » Glossary

 » Index

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 Acceptance

There 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 Control

There 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.

Multiple Controls

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:

    • PK_CONTINUE

  • If the hit should be accepted/recorded, but the pick loop should continue looking for other hits, the pick_control value would be:

    • PK_ACCEPT | PK_CONTINUE

    (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:

    • PK_ACCEPT | PK_STOP

  • 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 Hit

The 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 Hit

The 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 Hits

This 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 Types

You 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 Procedures

The 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:

Nothing hit.

Miscellaneous Picking Topics

Pick Traversal and Starbase State

Pick 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 Display

The 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.

Picking and Filters

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.

Pick sense

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.

Batched Picking

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.

Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 1995 Hewlett-Packard Development Company, L.P.