[comp.windows.news] Graphic display of binary search tree manipulation in NeWS

callahan@gondor.cs.psu.edu (Paul B. Callahan) (11/02/88)

The follow shar file contains the code for NeWS window program that allows
the user to "edit" binary search trees.  It is contained in the files
treeedit.c, and treeedit.cps.  The executable csh file cctreeedit will
compile it into treeedit if your system is configured in the same way
as the one I used.

The editor is easy to use.

        The default initial tree is just a single node. 

	The left mouse button causes a tree rotation on the edge
	going into a node, if it is clicked directly on the node.
        If it is clicked far away, it allows gradual movement of 
	the current node.

	The middle mouse button is used to select the node it is
	clicked on as the current node.

	The right button causes insertion of a node in a place 
	determined by the x coordinate of the mouse location.

	Finally, the screen button DELETE deletes the current node.

	When the window is zapped, the c program outputs an RPN
	expression for the tree to stdout.

	This sort of expression can also be input as the current
	tree using the -i option.

Have fun, and reply with any feedback or suggestions.

             -----------------------cut here----------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	cctreeedit
#	treeedit.c
#	treeedit.cps
# This archive created: Mon Oct 31 21:05:54 1988
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'cctreeedit'
then
	echo shar: "will not over-write existing file 'cctreeedit'"
else
cat << \SHAR_EOF > 'cctreeedit'
cps treeedit.cps
cc -o treeedit treeedit.c /usr/NeWS/lib/libcps.a -I/usr/NeWS/include
SHAR_EOF
chmod +x 'cctreeedit'
fi
if test -f 'treeedit.c'
then
	echo shar: "will not over-write existing file 'treeedit.c'"
else
cat << \SHAR_EOF > 'treeedit.c'
#include <stdio.h>
#include "treeedit.h" 
#define MaxNodes 1000
#define MaxCol 1
#define MaxRow 1

struct bst {
  struct bst *left;
  struct bst *right;
  int rank;
  int depth;
};
typedef struct bst * item;

item stack[MaxNodes];
int StackPtr;

initstack() {StackPtr=0;}

push(node)
item node;
{ 
 if (StackPtr>=MaxNodes) {
     printf("Stack Overflow\n");
     exit(1); 
 }
 else stack[StackPtr++]=node;
}

pop(node)
item *node;
{
 if (StackPtr==0) {
     printf("Stack Underflow\n");
     exit(1); 
 }
 else *node=stack[--StackPtr]; 
}
 
construct() {
   struct bst *node,*nodel,*noder;
   char c;
  
   while ((c=getchar())!='\n') {
      node=(struct bst *) malloc(sizeof(struct bst));
      node->left=NULL;
      node->right=NULL;
      if (c=='.') {
         push(node);
      }
      else if (c=='l') {
         pop(&nodel);
         node->left=nodel;
         push(node);
      } 
      else if (c=='r') {
         pop(&noder);
         node->right=noder;
         push(node);
      }
      else {
         pop(&noder);
         node->right=noder;
         pop(&nodel);
         node->left=nodel;
         push(node);
      }
   }
}

findxy(root,rank,depth,maxdepth) 
struct bst *root;
int *rank,depth,*maxdepth;
{
   if (root!=NULL) {
      if (depth>*maxdepth) *maxdepth=depth;
      findxy(root->left,rank,depth+1,maxdepth);
      root->rank=*rank;
      root->depth=depth; 
      (*rank)++;
      findxy(root->right,rank,depth+1,maxdepth);
   }
}

draw(id,root) 
struct bst *root;
int id;
{  
   cps_drawnode(id,root->rank,root->depth);
   if (root->left!=NULL) {
      cps_drawedge(id,
              root->rank,root->depth,
              root->left->rank,root->left->depth);
      draw(id,root->left);
   }
   if (root->right!=NULL) {
      cps_drawedge(id,
              root->rank,root->depth,
              root->right->rank,root->right->depth);
      draw(id,root->right);
   }
}

postout(root)
struct bst *root;
{
   int typenode;
   static char nodechar[]=".rlb";
  
   typenode=0; 
   if (root->left!=NULL) {
      postout(root->left);
      typenode+=2;
   }
   if (root->right!=NULL) {
      postout(root->right);
      typenode++;
   }
   printf("%c",nodechar[typenode]);
}

main(argc,argv) 
    char **argv;
{
   char c;
   struct bst *root;
   int maxdepth, rank;
   struct bst *rotabout,*newrot,*tempnode,*next,*parent;
   struct bst **insplace; 
   int id,cnum;
   int opt,done,x,rotx,tempx;
   float geomx;
   
   if (ps_open_PostScript()==0) {
      fprintf(stderr,"No NeWS is bad news\n");
      exit(1);
   }
   ps_flush_PostScript();
   opt=0;
   while (--argc>0) opt=((++argv)[0][0]=='-');
   if (!opt) {
      root=(struct bst *) malloc(sizeof(struct bst));
      root->left=NULL;
      root->right=NULL;
   }
   else if (argv[0][1]=='i') {
       done=0; 
       while (!done) {  
          initstack();
          construct();
          pop(&root);
          if (StackPtr>0) printf("Not a tree\n"); else done=1;
       } 
   }
   else {
       fprintf(stderr,"invalid option %c\n",argv[0]);
       exit(-1);
   }
   rotabout=root;
   rank=0; 
   maxdepth=0;
   findxy(root,&rank,0,&maxdepth);
   cps_initwindow(rank-1,maxdepth,rotabout->rank,rotabout->depth); 
   while (!psio_error(PostScriptInput)) {
          if (cps_damage(&id)) {
             draw(id,root);
             cps_Wcirc(id,rotabout->rank,rotabout->depth);
             cps_exit(id);
          }
          else if (cps_rightbutton(&id,&geomx)) {
             insplace=&root; 
             while ((*insplace)!=NULL) { 
                if ((float) ((*insplace)->rank) > geomx)
                     insplace=&((*insplace)->left);
                else insplace=&((*insplace)->right);
             }
             *insplace=(struct bst *) malloc(sizeof(struct bst));
             (*insplace)->left=NULL;
             (*insplace)->right=NULL;
             rank=0; 
             maxdepth=0;
             findxy(root,&rank,0,&maxdepth);
             cps_clearandrescale(id,rank-1,maxdepth);
             cps_exit(id); 
          }
          else if (cps_midbutton(&id,&geomx)) {
             x = (int) (geomx+0.5);
             rotx=rotabout->rank; 
             next=root;         
             while (next!=NULL) {
                tempnode=next;
                tempx=tempnode->rank;
                if ((tempx > x) && (tempx > rotx)) next=tempnode->left;
                else if ((tempx < x) && (tempx < rotx)) next=tempnode->right;
                else next=NULL;                
             }
             while (rotabout!=tempnode) {
                cps_Bcirc(id,rotabout->rank,rotabout->depth);
                pop(&rotabout);
                cps_Wcirc(id,rotabout->rank,rotabout->depth);
             }
             while (rotabout->rank!=x) { 
                cps_Bcirc(id,rotabout->rank,rotabout->depth);
                push(rotabout);
                if (rotabout->rank>x) rotabout=rotabout->left;
                else rotabout=rotabout->right;
                cps_Wcirc(id,rotabout->rank,rotabout->depth);
             }
             cps_exit(id);
          } 
          else if (cps_delbutton(&id)) {
             if ((root->left!=NULL) || (root->right!=NULL))
             switch((rotabout->left!=NULL)*2+(rotabout->right!=NULL)) {
             case 0: 
               pop(&tempnode);
               if (tempnode->left==rotabout) tempnode->left=NULL;
               else tempnode->right=NULL;
               break;
             case 1:
               if (root==rotabout) root=rotabout->right;
               else {
                  pop(&tempnode);
                  if (tempnode->left==rotabout) tempnode->left=rotabout->right;
                  else tempnode->right=rotabout->right;
               }   
               break; 
             case 2:
               if (root==rotabout) root=rotabout->left;
               else {
                  pop(&tempnode);
                  if (tempnode->left==rotabout) tempnode->left=rotabout->left;
                  else tempnode->right=rotabout->left;
               }
               break; 
             case 3:
               parent=rotabout;
               tempnode=parent->left;
               if (tempnode->right==NULL) parent->left=tempnode->left;
               else {
                  while (tempnode->right!=NULL) {
                     parent=tempnode; 
                     tempnode=parent->right;
                  }
                  parent->right=tempnode->left;
               } 
               break;
             }
             initstack();
             rotabout=root;
             maxdepth=0;
             rank=0; 
             findxy(root,&rank,0,&maxdepth);
             cps_clearandrescale(id,rank-1,maxdepth);
             cps_exit(id); 
          }
          else if (cps_get_comm(&id,&cnum)) {
             switch(cnum) {
             case 0:
                if (StackPtr>0) { 
                   cps_Bcirc(id,rotabout->rank,rotabout->depth);
                   pop(&rotabout);
                   cps_Wcirc(id,rotabout->rank,rotabout->depth);
                } 
                break;
             case 1:
                if ((rotabout->left)!=NULL) {
                   push(rotabout);
                   cps_Bcirc(id,rotabout->rank,rotabout->depth); 
                   rotabout=rotabout->left;
                   cps_Wcirc(id,rotabout->rank,rotabout->depth);
                }  
                break;
             case 2:
                if ((rotabout->right)!=NULL) {
                   push(rotabout);
                   cps_Bcirc(id,rotabout->rank,rotabout->depth);
                   rotabout=rotabout->right;
                   cps_Wcirc(id,rotabout->rank,rotabout->depth);
                }  
                break;
             case 3:
                if (StackPtr>0) {
                   pop(&newrot);
                   if (StackPtr==0) root=rotabout;
                   else {
                      pop(&tempnode);
                      push(tempnode);
                      if ((tempnode->right)==newrot) tempnode->right=rotabout;
                      else tempnode->left=rotabout;
                   }
                   if (newrot->left==rotabout) {
                      newrot->left=rotabout->right;
                      rotabout->right=newrot;
                   }
                   else {
                      newrot->right=rotabout->left;
                      rotabout->left=newrot;
                   }
                   rank=0; 
                   maxdepth=0;
                   findxy(root,&rank,0,&maxdepth);
                   cps_clearandrescale(id,rank-1,maxdepth);
                }
                break;  
             }
             cps_exit(id);
          }
          else if (cps_done() || psio_eof(PostScriptInput)) break; 
      }  
      postout(root);
      printf("\n");    
}
SHAR_EOF
fi
if test -f 'treeedit.cps'
then
	echo shar: "will not over-write existing file 'treeedit.cps'"
else
cat << \SHAR_EOF > 'treeedit.cps'
#define LMB_TAG 1
#define DAMAGE_TAG 2
#define DONE_TAG 3
#define MMB_TAG 4
#define RMB_TAG 5
#define DELB_TAG 6

cdef cps_initwindow(int sx,int sy,int xrot,int yrot)

systemdict /Item known not { (NeWS/liteitem.ps) run } if

/LMB /LeftMouseButton def
/MMB /MiddleMouseButton def
/RMB /RightMouseButton def

yrot xrot 
userdict begin /xrot exch def /yrot exch def end

/downeventinterest {/DownTransition ClientCanvas eventmgrinterest} def 

/startinput {
   /ButtonMgr[
      LMB {LMB_TAG getdirection} downeventinterest
      MMB {MMB_TAG getgeomx} downeventinterest
      RMB {RMB_TAG getgeomx} downeventinterest
    ]forkeventmgr store
} def

/repair {
   DAMAGE_TAG tagprint
   uniquecid dup typedprint
   [exch cidinterest] forkeventmgr
   waitprocess pop
} def

/makescale {
  /y exch def /x exch def 
  4 2 roll pop pop 
  y 0.5 add div exch
  x 1.5 add div exch scale 
  0.01 setlinewidth
  0 y 0.5 add translate
  1 -1 scale
  0.75 0.25 translate 
} def

/TreeWindow DefaultWindow
   dictbegin
       /ScaleX sx def
       /ScaleY sy def
       /DelButtonHeight 45 def
       /DelButtonWidth 60 def
       /FrameLabel (Tree) def
       /ButtonMgr null def
   dictend
   classbegin
       /CreateClientCanvas {
          /CreateClientCanvas super send
          /items 1 dict dup begin 
             /DelButton (Delete) {DELB_TAG screenbutton} FrameCanvas
                DelButtonWidth 0 /new ButtonItem send
                dup /ItemBorderColor 0 0 0 rgbcolor put
                def  
          end def 
          /itemmgr items forkitems def
       } def
       /RescaleCanvas {
          /ScaleY exch def
          /ScaleX exch def
          setcanvas 
          [1 0 0 1 0 0] setmatrix 
          0 exch translate
          initclip clippath pathbbox
          ScaleX ScaleY makescale  
          currentcanvas reshapecanvas 
       } def
       /ShapeClientCanvas { 
          gsave 
          /ShapeClientCanvas super send
          currentcanvas 
          FrameCanvas setcanvas
          FrameWidth 2 div
          DelButtonWidth 2 div sub
          BorderBottom 5 add /move 
          window /items get /DelButton get send
          setcanvas
          grestore
       } def
       /PaintClient { 1 fillcanvas
            repair 
            /paint items /DelButton get send
            } def
       /PaintIcon { 0.75 0.498 0.196 rgbcolor fillcanvas
                    repair } def
       /flipiconic {
            /flipiconic super send 
            Iconic? IconCanvas /Retained get and {/paint self send} if  
       } def
       /move {
            Iconic? {IconHeight sub} if 
            /move super send
       } def
       /DestroyClient {
           itemmgr killprocess 
           ButtonMgr killprocess
           DONE_TAG tagprint 
       } def 
       /ForkFrameEventMgr{
           /ForkFrameEventMgr super send
           startinput
       } def
       /ClientPath {
           4 copy
           rectpath
           DelButtonHeight sub
           0 DelButtonHeight translate 
           ScaleX ScaleY 
           makescale
       } def 
       /IconPath {
           4 copy
           rectpath
           ScaleX ScaleY 
           makescale
       } def 
   classend def

/window framebuffer /new TreeWindow send def
/reshapefromuser window send
/map window send

/getdirection {
   ClientCanvas setcanvas
   tagprint uniquecid dup typedprint
   exch 
   begin
      YLocation userdict /yrot get sub dup mul
      XLocation userdict /xrot get sub 3 div dup mul add 0.09 lt
      {3}
      {YLocation userdict /yrot get gt 
         {XLocation userdict /xrot get lt {1} {2} ifelse}
         {0} ifelse} ifelse 
   end
   typedprint 
   [exch cidinterest] forkeventmgr
   waitprocess pop
} def

/getgeomx {
   ClientCanvas setcanvas
   tagprint uniquecid dup typedprint
   exch 
   begin
      XLocation 
   end
   typedprint 
   [exch cidinterest] forkeventmgr
   waitprocess pop
} def

/screenbutton {
   window begin
   ClientCanvas setcanvas 
   tagprint uniquecid dup typedprint
   [exch cidinterest] forkeventmgr 
   waitprocess pop
   end
} def

cdef cps_get_comm(int id, int cnum) => LMB_TAG(id,cnum)
cdef cps_midbutton(int id, float geomx) => MMB_TAG(id,geomx)
cdef cps_rightbutton(int id, float geomx) => RMB_TAG(id,geomx)
cdef cps_delbutton(int id) => DELB_TAG(id)
cdef cps_damage(int id) => DAMAGE_TAG(id)
cdef cps_done() => DONE_TAG() 

cdef cps_exit(int id)
       id {exit} sendcidevent

cdef cps_clearandrescale(int id, int sx, int sy)
  id { 
    DelButtonHeight ClientCanvas sx sy /RescaleCanvas window send  
    0 IconCanvas sx sy /RescaleCanvas window send  
    ClientCanvas setcanvas 
    gsave 
       [1 0 0 1 0 0] setmatrix
       initclip clippath pathbbox
       0 DelButtonHeight translate
       DelButtonHeight sub
       rectpath 1 setgray fill
    grestore 
    repair
   pause} sendcidevent 

cdef cps_drawedge(int id, int x1, int y1, int x2, int y2)
  id { 
   x1 y1 moveto x2 y2 lineto 0 setgray stroke 
  pause} sendcidevent

cdef cps_drawnode(int id, int x,int y)
  id {
   gsave x y translate 3 1 scale 
   0 0 0.25 0 360 arc 0 setgray fill grestore
  pause} sendcidevent

cdef cps_Wcirc(int id,int x,int y)
  id {
   gsave x y translate 3 1 scale 
   0 0 0.1 0 360 arc 1 setgray fill grestore
   userdict begin
      /xrot x def  
      /yrot y def
   end
 pause} sendcidevent

cdef cps_Bcirc(int id,int x,int y)
  id { 
   gsave x y translate 3 1 scale 
   0 0 0.1 0 360 arc 0 setgray fill grestore
  pause} sendcidevent
SHAR_EOF
fi
exit 0
#	End of shell archive