home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / gopher / NeXT / Tree0.5 / treeobj / TreeView.m < prev    next >
Encoding:
Text File  |  1992-05-16  |  14.1 KB  |  593 lines

  1. /* TreeView.m - Copyright 1992  Steve Ludtke  All Rights Reserved      */
  2. /* This object draws lines in 3d with perspective. It has 2 modes if   */
  3. /* dtype==0, a grid is drawn around the current position at z=0 and    */
  4. /* a line is drawn from the "ground" to the Root Branch. Movement      */
  5. /* between trees occurs above the ground. If dtype==1, there is no     */
  6. /* "ground" or grid and motion occurs on a straight line.           */
  7.  
  8. #import "TreeView.h"
  9. #import <appkit/View.h>
  10. #import <appkit/Window.h>
  11. #import <appkit/Control.h>
  12. #import <appkit/Application.h>
  13. #import <strings.h>
  14. #import <dpsclient/psops.h>
  15. #import <libc.h>
  16. #import <math.h>
  17.  
  18. #define SQR(x) ((x)*(x))
  19. @implementation TreeView
  20.  
  21. /* initialize */
  22. - initFrame:(NXRect *)myrect
  23. {
  24.     [super initFrame:myrect];
  25.     bgColor = NX_WHITE;
  26.     fgColor = NX_BLACK;
  27.  
  28.     xsca = NX_WIDTH(&bounds) / 2.0;    /* scaling for display in view */
  29.     zsca = NX_HEIGHT(&bounds) / 2.0;
  30.     xof = NX_MIDX(&bounds);
  31.     zof = NX_MIDY(&bounds);
  32.  
  33.     dtype = 0;
  34.  
  35.     myx = myy = myz = 0.0;
  36.     [self setView:0.0 alt:M_PI / 2.0 aov:0.8];
  37.     return self;
  38. }
  39.  
  40. /* get useful pointers */
  41. - start:(Root *) Ptop :Pobject :(char *)path
  42. {
  43.     top = Ptop;
  44.     object = Pobject;
  45.     return self;
  46. }
  47.  
  48. /* one time step, use for keyboard motion */
  49. - step:(Branch *) myloc
  50. {
  51.     static float        tx, ty, t;
  52.  
  53.     myx += dx;            /* move viewpoint by dx,dy,dz */
  54.     myy += dy;
  55.     myz += dz;
  56.  
  57.  /* rotate about fixed target by dc radians */
  58.     if (dc != 0.0) {
  59.     tx = myx - myloc->x;
  60.     ty = myy - myloc->y;
  61.     myx = tx * cos(dc) - ty * sin(dc) + myloc->x;
  62.     myy = tx * sin(dc) + ty * cos(dc) + myloc->y;
  63.     }
  64.  /* move towards/away from target */
  65.     if (dr != 0.0) {
  66.     tx = myx - myloc->x;
  67.     ty = myy - myloc->y;
  68.     t = sqrt(SQR(tx) + SQR(ty));
  69.     myx += tx * dr / t;
  70.     myy += ty * dr / t;
  71.     }
  72.  /* refresh if we moved */
  73.     if (dx != 0.0 || dy != 0.0 || dz != 0.0 || dr != 0.0 || dc != 0.0) {
  74.     [self refresh:myloc :80];
  75.     }
  76.     return self;
  77. }
  78.  
  79. /* These methods recieve keystrokes */
  80. - (BOOL)acceptsFirstResponder
  81. {
  82.     return YES;
  83. }
  84.  
  85. /* parse keyboard input */
  86. - keyDown:(NXEvent *)event
  87. {
  88.     char                c;
  89.  
  90.     c = event->data.key.charCode;
  91.     switch (c) {
  92.     case 'i':
  93.     dz +=.1;
  94.     break;
  95.     case 'm':
  96.     dz -=.1;
  97.     break;
  98.     case 'j':
  99.     dc = -.1;
  100.     break;
  101.     case 'k':
  102.     dc =.1;
  103.     break;
  104.     case 'z':
  105.     dr =.1;
  106.     break;
  107.     case 'a':
  108.     dr = -.1;
  109.     break;
  110.     case '1':
  111.     dtype = 0;        /* enter planar mode */
  112.     break;
  113.     case '3':
  114.     dtype = 1;        /* enter full 3d mode */
  115.     break;
  116.     }
  117.     return (self);
  118. }
  119.  
  120. - keyUp:(NXEvent *)event
  121. {
  122.     char                c;
  123.  
  124.     c = event->data.key.charCode;
  125.     switch (c) {
  126.     case 'm':
  127.     case 'i':
  128.     dz = 0.0;
  129.     break;
  130.     case 'j':
  131.     case 'k':
  132.     dc = 0.0;
  133.     break;
  134.     case 'a':
  135.     case 'z':
  136.     dr = 0.0;
  137.     break;
  138.     }
  139.     return (self);
  140. }
  141.  
  142.  
  143. /* look at a particular point */
  144. - lookAt:(float)x y:(float)y z:(float)z
  145. {
  146.     static float        r;
  147.  
  148.     r = sqrt(SQR(x - myx) + SQR(y - myy));
  149.     chi = -atan2(x - myx, y - myy);
  150.     theta = atan2(z - myz, r);
  151.     [self setView:chi alt:theta aov:0.8];
  152. /*#ifdef DEBUG
  153. printf("lookAt:%f,%f   %f,%f,%f\n",theta*57.0,chi*57.0,x-myx,y-my,z-mz);
  154. #endif */
  155.  
  156.     return (self);
  157. }
  158.  
  159.  
  160. /* Build a 3-d transformation matrix without         */
  161. /* perspective (added when drawn). aov == angle of view     */
  162. /* az == azimuthal angle   alt == altitude (angle)       */
  163. - setView:(float)az alt:(float)alt aov:(float)aov
  164. {
  165.  
  166.     xfm[0] = cos(az);
  167.     xfm[1] = sin(az);
  168.     xfm[2] = 0.0;
  169.     xfm[3] = -cos(alt) * sin(az);
  170.     xfm[4] = cos(alt) * cos(az);
  171.     xfm[5] = sin(alt);
  172.     xfm[6] = sin(alt) * sin(az);
  173.     xfm[7] = -sin(alt) * cos(az);
  174.     xfm[8] = cos(alt);
  175.     yas = tan(aov);
  176.  
  177.     return self;
  178. }
  179.  
  180. /* The "meat" of the TreeView */
  181. - drawSelf:(NXRect *)rects :(int)rectCount
  182. {
  183.     float               x, y, z, tmx, tmy, tmz, xs, ys, zs, xe, ye, ze, x1, y1, z1;
  184.     Root               *cur;
  185.  
  186. /*#ifdef DEBUG
  187. printf("Display alt:%f az:%f\n",Dalt*57.296,Daz*57.296);
  188. #endif */
  189.  
  190. /* bounding box for DPSDoUserPath */
  191.     bbox[0] = bbox[1] = 50.0;
  192.     bbox[2] = bbox[3] = 51.0;
  193.  
  194.  
  195. /* clear the view */
  196.     PSsetlinewidth(0.0);
  197.     PSsetgray(NX_BLACK);
  198.     NXRectFill(&bounds);
  199.     PSsetgray(fgColor = NX_WHITE);
  200.  
  201.     cur = top;
  202.     pathc = 0;
  203.  
  204. /* if we're in 3d mode, draw the grid and the sun */
  205.  /* draw the sun */
  206.     tmx = 1500000.0;
  207.     tmy = 32000.0;
  208.     tmz = 200000.0;
  209.     y = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  210.     x = (xfm[0] * tmx + xfm[1] * tmy);
  211.     z = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  212.     if (y > 0.0) {
  213.     x = x / (y * yas) * zsca + xof;
  214.     z = z / (y * yas) * zsca + zof;
  215.     PSsetgray(fgColor =.6667);
  216.     PSarc(x, z, 20.0, 0, 360.0);
  217.     PSfill();
  218.     PSstroke();
  219.     }
  220.  /* draw the grid */
  221.     PSsetgray(fgColor =.5);
  222.  /* draw 2d grid */
  223.     if (dtype == 0) {
  224.     /* this insures that the grid moves correctly */
  225.     xs = floor(myx / 30.0) * 30.0 - 400.0 - myx;
  226.     ys = floor(myy / 30.0) * 30.0 - 400.0 - myy;
  227.     xe = xs + 800.0;
  228.     ye = ys + 800.0;
  229.     tmz = -myz;
  230.  
  231.     for (tmx = xs; tmx <= xe; tmx += 30.0) {
  232.         tmy = ys;
  233.         y = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  234.         x = (xfm[0] * tmx + xfm[1] * tmy);
  235.         z = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  236.  
  237.         tmy = ye;
  238.         y1 = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  239.         x1 = (xfm[0] * tmx + xfm[1] * tmy);
  240.         z1 = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  241.         [self addline:x :y :z :x1 :y1 :z1];
  242.     }
  243.  
  244.     for (tmy = ys; tmy <= ye; tmy += 30.0) {
  245.         tmx = xs;
  246.         y = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  247.         x = (xfm[0] * tmx + xfm[1] * tmy);
  248.         z = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  249.  
  250.         tmx = xe;
  251.         y1 = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  252.         x1 = (xfm[0] * tmx + xfm[1] * tmy);
  253.         z1 = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  254.         [self addline:x :y :z :x1 :y1 :z1];
  255.     }
  256.     } else {
  257.     /* 3d grid - Too slow, need to convert to use DPSuserpath... */
  258. /*
  259.         xs = floor(myx / 100.0) * 100.0 - 400.0 - myx;
  260.         ys = floor(myy / 100.0) * 100.0 - 400.0 - myy;
  261.         zs = floor(myz / 100.0) * 100.0 - 400.0 - myz;
  262.         xe = xs + 800.0;
  263.         ye = ys + 800.0;
  264.         ze = zs + 800.0;
  265.     
  266.         for (tmx = xs; tmx <= xe; tmx += 100.0) {
  267.         for (tmy=ys; tmy<=ye; tmy +=100.0) {
  268.             for (tmz=zs; tmz<=ze; tmz+=100.0) {
  269.                     y = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  270.                     x = (xfm[0] * tmx + xfm[1] * tmy);
  271.                     z = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  272.             
  273.                     [self add_dot:x :y :z];
  274.             }
  275.         }
  276.         }
  277.     */
  278.     }
  279.     if (pathc != 0)
  280.     DPSDoUserPath(path, pathc * 2, dps_float, com, pathc, bbox, dps_ustroke);
  281.     pathc = 0;
  282.     PSsetgray(fgColor = NX_WHITE);
  283.  
  284.     while (cur != NULL) {
  285.     if (cur->drawme) {
  286.     /* normal 3d display */
  287.         tmx = cur->branch.x - myx;
  288.         tmy = cur->branch.y - myy;
  289.         if (dtype == 0)
  290.         tmz = -myz;
  291.         else
  292.         tmz = cur->branch.z - myz;
  293.  
  294.     /* do the x-form, perspective is added later */
  295.         y = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  296.         x = (xfm[0] * tmx + xfm[1] * tmy);
  297.         z = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  298.     /* recursive tree branching */
  299.         [self Draw:&cur->branch:x :y :z :0];
  300. /*#ifdef DEBUG
  301. printf("Display:%s %f,%f,%f\n",cur->name,x,y,z);
  302. #endif */
  303.     }
  304.     /* repeat for all Roots */
  305.     cur = cur->next;
  306.     }
  307.  
  308.     if (pathc != 0)
  309.     DPSDoUserPath(path, pathc * 2, dps_float, com, pathc, bbox, dps_ustroke);
  310.     return self;
  311. }
  312.  
  313. /* this is the recursive routine that draws the branches */
  314. - Draw:(Branch *) topb :(float)x :(float)y :(float)z :(int)f
  315. {
  316.     float               cx, cy, cz;
  317.     float               tmx, tmy, tmz;
  318.     Branch             *cur;
  319.  
  320. /*#ifdef DEBUG
  321. printf("Draw: %s\n",topb->dname);
  322. #endif*/
  323.  
  324.     cur = topb;
  325.     while (cur != NULL) {
  326.     tmx = cur->x - myx;
  327.     tmy = cur->y - myy;
  328.     tmz = cur->z - myz;
  329.     cy = xfm[3] * tmx + xfm[4] * tmy + xfm[5] * tmz;
  330.     cx = (xfm[0] * tmx + xfm[1] * tmy);
  331.     cz = (xfm[6] * tmx + xfm[7] * tmy + xfm[8] * tmz);
  332.     [self addline:x :y :z :cx :cy :cz];
  333.     if (f)
  334.         return (self);
  335.     if (cur->sub != NULL)
  336.     /* this is a "tree" link, so keep going recursively */
  337.         [self Draw:cur->sub:cx :cy :cz :0];
  338.     else if (cur->link != NULL) {
  339.     /* this is a "web" link, so we need to stop the recursion */
  340.         [self Draw:cur->link:cx :cy :cz :1];
  341.     }
  342.     cur = cur->next;
  343.     }
  344.  
  345.     return (self);
  346. }
  347.  
  348. /* 2d routine to add lines to the view */
  349. - addline:(float)x1 :(float)y1 :(float)x2 :(float)y2
  350. {
  351.     static float        lastx, lasty;
  352.  
  353.     if (x1 != lastx || y1 != lasty) {
  354.     com[pathc] = dps_moveto;
  355.     path[pathc * 2] = x1;
  356.     path[pathc * 2 + 1] = y1;
  357.     pathc++;
  358.     if (x1 > bbox[2])
  359.         bbox[2] = x1;
  360.     if (y1 > bbox[3])
  361.         bbox[3] = y1;
  362.     if (x1 < bbox[0])
  363.         bbox[0] = x1;
  364.     if (y1 < bbox[1])
  365.         bbox[1] = y1;
  366.     }
  367.     com[pathc] = dps_lineto;
  368.     path[pathc * 2] = x2;
  369.     path[pathc * 2 + 1] = y2;
  370.     pathc++;
  371.     if (x2 > bbox[2])
  372.     bbox[2] = x2;
  373.     if (y2 > bbox[3])
  374.     bbox[3] = y2;
  375.     if (x2 < bbox[0])
  376.     bbox[0] = x2;
  377.     if (y2 < bbox[1])
  378.     bbox[1] = y2;
  379.  
  380.     if (pathc >= 1500) {
  381.     DPSDoUserPath(path, pathc * 2, dps_float, com, pathc, bbox, dps_ustroke);
  382.     bbox[0] = bbox[1] = 50.0;
  383.     bbox[2] = bbox[3] = 51.0;
  384.     pathc = 0;
  385.     }
  386.     lastx = x2;
  387.     lasty = y2;
  388.     return (self);
  389. }
  390.  
  391. /* draw a small circle (slow) */
  392. - add_dot:(float)x :(float)y :(float)z
  393. {
  394.     x = x / (y * yas) * zsca + xof;
  395.     z = z / (y * yas) * zsca + zof;
  396.  
  397.     PSarc(x, z, 2.0, 0.0, 360.0);
  398.     PSstroke();
  399.     return self;
  400. }
  401.  
  402. /* 3d routine to add lines to the view. Handles 3d clipping and perspecive */
  403. - addline:(float)x1 :(float)y1 :(float)z1 :(float)x2 :(float)y2 :(float)z2
  404. {
  405.     static float        lastx, lasty;
  406.     float               t;
  407.  
  408. /* 3d clipping (for points "behind" the screen) */
  409.     if (y1 < 0.0 && y2 < 0.0)
  410.     return self;
  411.     if (y2 < 0.0) {
  412.     t = x2;
  413.     x2 = x1;
  414.     x1 = t;
  415.     t = y2;
  416.     y2 = y1;
  417.     y1 = t;
  418.     t = z2;
  419.     z2 = z1;
  420.     z1 = t;
  421.     }
  422.     if (y1 < 0.0) {
  423.     x1 = x2 - (y2 -.5) * (x2 - x1) / (y2 - y1);
  424.     z1 = z2 - (y2 -.5) * (z2 - z1) / (y2 - y1);
  425.     y1 = 0.5;
  426.     }
  427. /* perspective */
  428.     x1 = x1 / (y1 * yas) * zsca + xof;
  429.     x2 = x2 / (y2 * yas) * zsca + xof;
  430.     z1 = z1 / (y1 * yas) * zsca + zof;
  431.     z2 = z2 / (y2 * yas) * zsca + zof;
  432.  
  433. /* add the line to the list to draw (x1,z1) - (x2,z2) */
  434.     if (x1 != lastx || z1 != lasty) {
  435.     com[pathc] = dps_moveto;
  436.     path[pathc * 2] = x1;
  437.     path[pathc * 2 + 1] = z1;
  438.     pathc++;
  439.     if (x1 > bbox[2])
  440.         bbox[2] = x1;
  441.     if (z1 > bbox[3])
  442.         bbox[3] = z1;
  443.     if (x1 < bbox[0])
  444.         bbox[0] = x1;
  445.     if (z1 < bbox[1])
  446.         bbox[1] = z1;
  447.     }
  448.     com[pathc] = dps_lineto;
  449.     path[pathc * 2] = x2;
  450.     path[pathc * 2 + 1] = z2;
  451.     pathc++;
  452.     if (x2 > bbox[2])
  453.     bbox[2] = x2;
  454.     if (z2 > bbox[3])
  455.     bbox[3] = z2;
  456.     if (x2 < bbox[0])
  457.     bbox[0] = x2;
  458.     if (z2 < bbox[1])
  459.     bbox[1] = z2;
  460.  
  461. /* if we have too many lines, draw them and empty the buffer */
  462.     if (pathc >= 1500) {
  463.     DPSDoUserPath(path, pathc * 2, dps_float, com, pathc, bbox, dps_ustroke);
  464.     bbox[0] = bbox[1] = 50.0;
  465.     bbox[2] = bbox[3] = 51.0;
  466.     pathc = 0;
  467.     }
  468.     lastx = x2;
  469.     lasty = z2;
  470.     return (self);
  471. }
  472.  
  473. /* handles movement between branches */
  474. - refresh:(Branch *) myloc :(int)speed;
  475. {
  476.     static Branch      *last = NULL;
  477.     float               xs, ys, zs;
  478.     float               xls, yls, zls;
  479.     float               f, sp1, sp2;
  480.  
  481. /*#ifdef DEBUG
  482. printf("refresh: %f %f %f\n",myloc->x,myloc->y,myloc->z);
  483. #endif */
  484.     if (myloc == NULL) {
  485.     last = NULL;
  486.     myloc = &top->branch;
  487.     speed = 0;
  488.     }
  489.     if (last == NULL) {
  490.     last = myloc;
  491.     myx = myloc->x - 2.0;
  492.     myy = myloc->y - 2.0;
  493.     myz = myloc->z;
  494.     }
  495. /* turn the speed into something usable */
  496.     sp1 = 30.0;
  497.     sp2 = 51.2;
  498.     if (speed <= 80) { sp1 = 20.0; sp2 = 97.0; }
  499.     if (speed <= 60) { sp1 = 15.0; sp2 = 182.0; }
  500.     if (speed <= 40) { sp1 = 10.0; sp2 = 273.0; }
  501.     if (speed <= 20) { sp1 = 5.0; sp2 = 400.0; }
  502.     if (speed == 0) { sp1 = 1.0; sp2 = 30000.0; }
  503.  
  504. /* we moved !!! */
  505.     if (last != myloc) {
  506.     /* new branch, same tree, just move up and down */
  507.     if (last->root == myloc->root) {
  508.         if (dtype == 0)
  509.         zs = (myloc->z - last->z) / sp1;
  510.         else
  511.         zs = (myloc->z - myz) / sp1;
  512.         zls = (myloc->z - last->z) / sp1;
  513.         xls = (myloc->x - last->x) / sp1;
  514.         yls = (myloc->y - last->y) / sp1;
  515.         for (f = sp1; f >= 0.0; f -= 1.0) {
  516.         myz += zs;
  517.         [self lookAt:myloc->x - xls * f y:myloc->y - yls * f z:myloc->z - zls * f];
  518.         [self display];
  519.         usleep(20000);
  520.         }
  521.     } else {        /* new branch AND tree, fly over there ... */
  522.     /* rise into the sky */
  523.         xls = chi;
  524.         yls = theta;
  525.         zls = myz;
  526.         if (dtype == 0)
  527.         myz = 40.0;
  528.         [self lookAt:myloc->x y:myloc->y z:myloc->z];
  529.         sp1 *= 2.0;
  530.         xls = (xls - chi) / sp1;
  531.         yls = (yls - theta) / sp1;
  532.         zls = (40.0 - zls) / sp1;
  533.         xs = chi;
  534.         ys = theta;
  535.         for (f = sp1; f >= 0.0; f -= 1.0) {
  536.         chi = xs - xls * f;
  537.         theta = ys - yls * f;
  538.         if (dtype == 0)
  539.             myz = 40.0 - zls * f;
  540.         [self setView:chi alt:theta aov:0.8];
  541.         [self display];
  542.         usleep(20000);
  543.         }
  544.  
  545.     /* zoom over to the new tree */
  546.         xs = (myloc->x - 3.0 - last->x);
  547.         ys = (myloc->y - 3.0 - last->y);
  548.         if (dtype == 0)
  549.         zs = (myloc->z - 40.0);
  550.         else
  551.         zs = (myloc->z - last->z);
  552.         xls = sqrt(SQR(xs) + SQR(ys)) / sp2;
  553.         if (xls > (float)(3 * speed))
  554.         xls /= 4.0;
  555.         for (f = xls; f >= 0.0; f -= 1.0) {
  556.         myx = myloc->x - 2.0 - xs / xls * f;
  557.         myy = myloc->y - 2.0 - ys / xls * f;
  558.         myz = myloc->z - zs / xls * f;
  559.  
  560.         [self lookAt:myloc->x y:myloc->y z:myloc->z];
  561.         [self display];
  562.         usleep(20000);
  563.         }
  564.         myx = myloc->x - 2.0;
  565.         myy = myloc->y - 2.0;
  566.         myz = myloc->z;
  567.     }
  568.     }
  569. /* make sure we're looking at the right place */
  570.     [self lookAt:myloc->x y:myloc->y z:myloc->z];
  571.     [self display];
  572.  
  573.     last = myloc;
  574.  
  575.     return self;
  576. }
  577.  
  578. /* preferences are currently keyboard selected */
  579. - preferences:sender
  580. {
  581.     return self;
  582. }
  583.  
  584. /* return a help string for display */
  585. - (char *)help:window :browser
  586. {
  587.     return ("TreeView - Steve Ludtke  May 1992\n\nThis is the original view of gopherspace. This view has 2 modes. In planar mode, a grid is displayed around the observer at z=0 and lines are drawn from the ground to the 1st node.\
  588. In 3d mode the grid and the trunk of the trees is not drawn. This is better for Coord's that span all 3 dimensions. Select the view and press 1 for planar\
  589.  mode and 3 for 3d mode. Movement also occurs somewhat differently in the two modes. You may use the i,j,k,m,a, and z keys to move around. The sun is in the east.\n");
  590. }
  591.  
  592. @end
  593.