home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD2.bin / bbs / misc / aspringies-1.0.lha / ASpringies / src / newmain.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-24  |  26.9 KB  |  981 lines

  1. /* newmain.c -- User interface event handling and graphics
  2.  * Copyright (C) 1991  Douglas M. DeCarlo
  3.  *
  4.  * Modifications for the Amiga port Copyright (C) 1994  Torsten Klein
  5.  *
  6.  * This file is part of ASpringies, a mass and spring simulation system for the Amiga
  7.  *
  8.  * ASpringies is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 1, or (at your option)
  11.  * any later version.
  12.  *
  13.  * ASpringies is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with ASpringies; see the file COPYING.  If not, write to
  20.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  *
  22.  * $Id: newmain.c,v 2.9 1994/08/19 11:52:22 Torsten_Klein Exp $  
  23.  */
  24.  
  25. #include <proto/timer.h>
  26. #include <math.h>
  27.  
  28. #include "main.h"
  29. #include "obj.h"
  30.  
  31. #define SetFL(obj, value)  (sprintf(temp, "%lf", (value)), \
  32.                 cut0s(temp),\
  33.                 setstring((obj), temp))
  34.  
  35. #define FOREachSelString(command) for (i = 0; i < num_spring; i++) \
  36.   if (springs[i].status & S_SELECTED) (command);
  37.  
  38. #define GetForceGrav(obj, which) \
  39.   mst.cur_grav_val[which] = GetFL(obj, cur_grav_min[which],cur_grav_max[which])
  40.  
  41. #define GetForceMisc(obj, which) \
  42.   mst.cur_misc_val[which] = GetFL(obj, cur_misc_min[which],cur_misc_max[which])
  43.  
  44. #define DRAWLINE(rp, x1, y1, x2, y2) \
  45. { Move(rp, x1, y1); Draw(rp, x2, y2); }
  46.  
  47. /* Mode values */
  48. #define M_EDIT        0L
  49. #define M_MASS        1L
  50. #define M_SPRING    2L
  51.  
  52. /* Behavior function modes */
  53. #define M_BF        3
  54.  
  55. /* Number of previous mouse state saves */
  56. #define MOUSE_PREV    4
  57.  
  58. #define NAIL_SIZE    12
  59.  
  60. /* Number of milliseconds that each drawing takes (to prevent skips) */
  61. #define MIN_DIFFT    3000
  62.  
  63. #define AnyButton 66  
  64. #define Button1 1
  65. #define Button2 2
  66. #define Button3 3
  67.  
  68. /* local prototypes 
  69.  */
  70. static void redraw_system(void);
  71. static void view_system(void);
  72. static void view_subsystem(int ulx, int uly, int wid, int ht);
  73. static void mouse_event(int, WORD, WORD, int, int);
  74. static void draw_edit_frame(int, int, int, int);
  75. static void handle_edit_message(struct IntuiMessage *);
  76. static void DoVanillaKey(struct IntuiMessage *);
  77. static void DoRestoreState(void);
  78. static void DoRestore(void);
  79. static boolean GetCK(APTR);
  80. static void draw_mass(boolean, int, int, double, boolean, boolean center);
  81. static boolean event_handler(void);
  82.  
  83. /* global vars
  84.  */
  85. int draw_wid, draw_ht;
  86.  
  87. t_state mst = {
  88.   1.0, 1.0,
  89.   1.0, 1.0,
  90.   FALSE, TRUE,
  91.   -1,
  92.   { 0, 0, 0, 0 },
  93.   { 10.0, 5.0, 10.0, 10000.0 },
  94.   { 0.0, 2.0, 0.0, 1.0 },
  95.   0.0, 0.0,
  96.   DEF_TSTEP, 1.0,
  97.   FALSE, FALSE,
  98.   20.0,
  99.   TRUE, TRUE, TRUE, TRUE
  100.   };
  101.  
  102. /* local vars
  103.  */
  104. typedef struct {
  105.   int x, y;
  106.   unsigned long t;
  107. } mouse_info;
  108.  
  109. static mouse_info mprev[MOUSE_PREV];
  110. static int moffset = 0;
  111.  
  112. static ULONG mode = M_EDIT;
  113. static int front = 0; back = 1;
  114. static BOOL erase = TRUE;
  115. static boolean quitting = FALSE, action = FALSE;
  116.  
  117. static t_state initst, sst;
  118.  
  119. static double cur_grav_max[BF_NUM] = { 10000000.0,  10000000.0, 
  120.                        10000000.0,  10000000.0   };
  121. static double cur_grav_min[BF_NUM] = {        0.0, -10000000.0, 
  122.                       -10000000.0, -10000000.0   };
  123. static double cur_misc_max[BF_NUM] = {      360.0,  10000000.0, 
  124.                        1000.0,      1000.0   };
  125. static double cur_misc_min[BF_NUM] = {     -360.0,         0.0,
  126.                          0.0,         -0.0   };
  127.  
  128. static char temp[20];        /* for SetSL */
  129.      
  130. /*****************************************************/
  131.  
  132. void new_main(void)
  133. {
  134.   initst = sst = mst;
  135.  
  136.   draw_wid = anim_wnd->Width;
  137.   draw_ht  = anim_wnd->Height;
  138.   SetDrMd(anim_rport[back], JAM1);
  139.   SetDrMd(anim_rport[front], JAM1);
  140.  
  141.   redisplay_widgets();
  142.   review_system();
  143.   init_objects();
  144.  
  145.   save_state(); sst = mst;    /* kludge */
  146.  
  147.   while (event_handler()); 
  148.  
  149.   delete_all();
  150. }
  151.  
  152. /*****************************************************/
  153.  
  154. static void cut0s(char *str)
  155. {
  156.   char *cp = &str[strlen(str)-1];
  157.  
  158.   while (*cp=='0' && *(cp-1)!='.') *(cp--) = '\0';
  159. }
  160.  
  161. /*****************************************************/
  162.  
  163. static double GetFL(APTR obj, double min, double max)
  164. {
  165.   char *contents;
  166.   double result;
  167.  
  168.   get(obj, MUIA_String_Contents, &contents);
  169.   sscanf(contents, "%lf", &result);
  170.  
  171.   if (result<min) result=min;
  172.   if (result>max) result=max;
  173.  
  174.   SetFL(obj, result); 
  175.   return(result);
  176. }
  177.  
  178. /*****************************************************/
  179.  
  180. static boolean GetCK(APTR obj) {
  181.   ULONG result;
  182.   
  183.   get(obj, MUIA_Selected, &result);
  184.   return((boolean)result);
  185. }
  186.  
  187. /*****************************************************/
  188.  
  189. void redisplay_widgets(void) 
  190. {
  191.   setcheckmark(CK_Action,       action);
  192.   setcheckmark(CK_DrawSpr,      mst.show_spring);
  193.   setcheckmark(CK_AdaptTStep,   mst.adaptive_step);
  194.   setcheckmark(CK_FixedMass,    mst.fix_mass);
  195.   setcheckmark(CK_WallTop,      mst.w_top);
  196.   setcheckmark(CK_WallLeft,     mst.w_left);
  197.   setcheckmark(CK_WallRight,    mst.w_right);
  198.   setcheckmark(CK_WallBottom,   mst.w_bottom);
  199.   setcheckmark(CK_GravActive,   mst.bf_mode[FR_GRAV]);
  200.   setcheckmark(CK_CenterActive, mst.bf_mode[FR_CMASS]);
  201.   setcheckmark(CK_PtAttrActive, mst.bf_mode[FR_PTATTRACT]);
  202.   setcheckmark(CK_WallFActive,  mst.bf_mode[FR_WALL]);
  203.  
  204.   setslider(SL_Grid, mst.grid_snap ? (ULONG)mst.cur_gsnap : 1L);
  205.  
  206.   SetFL(ST_Mass,       mst.cur_mass);
  207.   SetFL(ST_Elas,       mst.cur_rest);
  208.   SetFL(ST_Kspr,       mst.cur_ks);
  209.   SetFL(ST_Kdmp,       mst.cur_kd);
  210.   SetFL(ST_Visc,       mst.cur_visc);
  211.   SetFL(ST_Stic,       mst.cur_stick);
  212.   SetFL(ST_Step,       mst.cur_dt);
  213.   SetFL(ST_Prec,       mst.cur_prec);
  214.   SetFL(ST_GravGrav,   mst.cur_grav_val[FR_GRAV]);
  215.   SetFL(ST_GravDir,    mst.cur_misc_val[FR_GRAV]);
  216.   SetFL(ST_CenterMagn, mst.cur_grav_val[FR_CMASS]);
  217.   SetFL(ST_CenterDamp, mst.cur_misc_val[FR_CMASS]);
  218.   SetFL(ST_PtAttrMagn, mst.cur_grav_val[FR_PTATTRACT]);
  219.   SetFL(ST_PtAttrExpo, mst.cur_misc_val[FR_PTATTRACT]);
  220.   SetFL(ST_WallFMagn,  mst.cur_grav_val[FR_WALL]);
  221.   SetFL(ST_WallFExpo,  mst.cur_misc_val[FR_WALL]);
  222. }
  223.  
  224. /*****************************************************/
  225.  
  226. static boolean event_handler(void)
  227. {
  228.   static LONGBITS MUI_Signals;
  229.   static boolean quitting = FALSE;
  230.  
  231.   struct IntuiMessage *msg;
  232.   int i;
  233.  
  234.   do
  235.     switch (DoMethod(AP_ASpringies, MUIM_Application_Input, &MUI_Signals))
  236.       {
  237.       case MUIV_Application_ReturnID_Quit :
  238.     quitting = (BOOL)MUI_Request(AP_ASpringies, WI_ASpringies, 0,
  239.                      "Quit request", "Yes|*No",
  240.                      "Would you like to quit ASpringies?");
  241.     break;
  242.  
  243.       case ID_RA_Mode       : get(RA_Mode, MUIA_Radio_Active, &mode); break;
  244.       case ID_CK_DrawSpr    : mst.show_spring=GetCK(CK_DrawSpr); review_system();break;
  245.       case ID_CK_AdaptTStep : mst.adaptive_step=GetCK(CK_AdaptTStep); break;
  246.       case ID_CK_Action     : action=           GetCK(CK_Action);     break;
  247.       case ID_CK_FixedMass  : 
  248.     mst.fix_mass = GetCK(CK_FixedMass);
  249.     for (i = 0; i < num_mass; i++) 
  250.       if (masses[i].status & S_SELECTED) {
  251.         if (mst.fix_mass) masses[i].status |= S_FIXED;
  252.         else              masses[i].status &= ~(S_FIXED | S_TEMPFIXED);
  253.       }
  254.     review_system();
  255.     break;
  256.  
  257.       case ID_CK_WallTop    : mst.w_top    = GetCK(CK_WallTop);    review_system();
  258.     break;
  259.       case ID_CK_WallLeft   : mst.w_left   = GetCK(CK_WallLeft);   review_system();
  260.     break;
  261.       case ID_CK_WallRight  : mst.w_right  = GetCK(CK_WallRight);  review_system();
  262.     break;
  263.       case ID_CK_WallBottom : mst.w_bottom = GetCK(CK_WallBottom); review_system();
  264.     break;
  265.  
  266.       case ID_CK_GravActive  : mst.bf_mode[FR_GRAV]      =GetCK(CK_GravActive);  break;
  267.       case ID_CK_CenterActive: mst.bf_mode[FR_CMASS]     =GetCK(CK_CenterActive);break;
  268.       case ID_CK_PtAttrActive: mst.bf_mode[FR_PTATTRACT] =GetCK(CK_PtAttrActive);break;
  269.       case ID_CK_WallFActive : mst.bf_mode[FR_WALL]      =GetCK(CK_WallFActive); break;
  270.  
  271.       case ID_BT_SelAll    : select_all(); eval_selection(); review_system(); break;
  272.       case ID_BT_SetRLen   : set_sel_restlen(); break;
  273.       case ID_BT_SetCenter : set_center(); review_system(); break;
  274.  
  275.       case ID_SL_Grid : {
  276.     ULONG result;
  277.     get(SL_Grid, MUIA_Slider_Level, &result);
  278.     mst.grid_snap = (mst.cur_gsnap = result) != 1;
  279.       } 
  280.     break;
  281.  
  282.       case ID_ST_Mass : 
  283.     mst.cur_mass = GetFL(ST_Mass, 0.01, 10000000.0);
  284.     for (i = 0; i < num_mass; i++) 
  285.       if (masses[i].status & S_SELECTED) {
  286.         masses[i].mass = mst.cur_mass;
  287.         masses[i].radius = mass_radius(mst.cur_mass);
  288.       }
  289.     review_system();
  290.     break;
  291.       case ID_ST_Elas :
  292.     mst.cur_rest = GetFL(ST_Elas, 0.0, 1.0);
  293.     for (i = 0; i < num_mass; i++) {
  294.       if (masses[i].status & S_SELECTED)
  295.         masses[i].elastic = mst.cur_rest;
  296.     }
  297.     break;
  298.       case ID_ST_Kspr : 
  299.     mst.cur_ks = GetFL(ST_Kspr, 0.01, 10000000.0);
  300.     FOREachSelString(springs[i].ks = mst.cur_ks);
  301.     break;
  302.       case ID_ST_Kdmp : 
  303.     mst.cur_kd = GetFL(ST_Kdmp, 0.0, 10000000.0);
  304.     FOREachSelString(springs[i].kd = mst.cur_kd);
  305.     break;
  306.       case ID_ST_Visc : mst.cur_visc  = GetFL(ST_Visc, 0.0,    10000000.0); break;
  307.       case ID_ST_Stic : mst.cur_stick = GetFL(ST_Stic, 0.0,    10000000.0); break;
  308.       case ID_ST_Step : mst.cur_dt    = GetFL(ST_Step, 0.0001,        1.0); break;
  309.       case ID_ST_Prec : mst.cur_prec  = GetFL(ST_Prec, 0.0001,     1000.0); break;
  310.  
  311.       case ID_ST_GravGrav   : GetForceGrav(ST_GravGrav,   FR_GRAV);      break;
  312.       case ID_ST_GravDir    : GetForceMisc(ST_GravDir,    FR_GRAV);      break;
  313.       case ID_ST_CenterMagn : GetForceGrav(ST_CenterMagn, FR_CMASS);     break;
  314.       case ID_ST_CenterDamp : GetForceMisc(ST_CenterDamp, FR_CMASS);     break;
  315.       case ID_ST_PtAttrMagn : GetForceGrav(ST_PtAttrMagn, FR_PTATTRACT); break;
  316.       case ID_ST_PtAttrExpo : GetForceMisc(ST_PtAttrExpo, FR_PTATTRACT); break;
  317.       case ID_ST_WallFMagn  : GetForceGrav(ST_WallFMagn,  FR_WALL);      break;
  318.       case ID_ST_WallFExpo  : GetForceMisc(ST_WallFExpo,  FR_WALL);      break;
  319.  
  320.       case ID_ME_About      : DoInfo();                break;
  321.       case ID_ME_Help       : 
  322.     DoMethod(AP_ASpringies, MUIM_Application_ShowHelp, WI_ASpringies,
  323.          HELP_FILE, "Main", 0);
  324.     break;
  325.       case ID_ME_Load       : FileIO(F_LOAD);   break;
  326.       case ID_ME_Insert     : FileIO(F_INSERT); break;
  327.       case ID_ME_Save       : FileIO(F_SAVE);   break;
  328.       case ID_ME_SaveAs     : FileIO(F_SAVEAS); break;
  329.       case ID_ME_DupSel     : duplicate_selected(); review_system(); break;
  330.       case ID_ME_DeleteSel  : delete_selected();    review_system(); break;
  331.       case ID_ME_Restore    : 
  332.     if (MUI_Request(AP_ASpringies, WI_ASpringies, 0, "Restore request", "Yes|*No",
  333.             "Restore last Snapshot?"))
  334.       DoRestoreState();
  335.     break;
  336.       case ID_ME_Snapshot   : save_state(); sst = mst; break;
  337.       case ID_ME_Reset      : 
  338.     if (MUI_Request(AP_ASpringies, WI_ASpringies, 0, "Reset request", "Yes|*No",
  339.             "Reset ASpringies?"))
  340.       DoRestore();             
  341.     break;
  342.       case ID_ME_ScreenMode :    /* noch besser machen! */
  343.     CloseAnimDisplay(); OpenAnimDisplay(TRUE);
  344.     draw_wid = anim_wnd->Width;
  345.     draw_ht  = anim_wnd->Height;
  346.     SetDrMd(anim_rport[back], JAM1);
  347.     SetDrMd(anim_rport[front], JAM1);
  348.     review_system();
  349.     break;
  350.       case ID_KY_AnimToFront : 
  351.     ScreenToFront(anim_scr);
  352.     ActivateWindow(anim_wnd);
  353.     break;
  354.       } while (!MUI_Signals);
  355.  
  356.   if (quitting) 
  357.     return(FALSE);
  358.  
  359.   if (action && animate_obj()) {
  360.     static struct timeval tp, tpo;
  361.     static int totaldiff = 0;
  362.     static boolean started = FALSE;
  363.     static ULONG av_fps, fpscount;
  364.     int difft;
  365.  
  366.     if (!started) {
  367.       GetSysTime(&tp);
  368.       started = TRUE;
  369.     }
  370.     tpo = tp;
  371.  
  372.     GetSysTime(&tp);
  373.     difft = (tp.tv_secs - tpo.tv_secs) * 1000000 + (tp.tv_micro - tpo.tv_micro);
  374.  
  375.     if (difft < MIN_DIFFT)
  376.       difft = MIN_DIFFT;
  377.  
  378.     if (difft > 0) {
  379.       totaldiff += difft;
  380.  
  381.       fpscount++;      
  382.       av_fps += (ULONG)(1.e6/difft+.5);
  383.       if (fpscount == 10) {
  384.     set(TX_Fps, MUIA_Text_Contents, (sprintf(temp, "%2ld", av_fps/10), temp));
  385.     av_fps = fpscount = 0;
  386.       }
  387.       /* Do updates about 30 frames per second */
  388.       if ((totaldiff > MIN_DIFFT)) { 
  389.     review_system();      
  390.       } 
  391.     }
  392.   } 
  393.   else if (!action && MUI_Signals) {
  394.     Wait(MUI_Signals | (1 << anim_wnd->UserPort->mp_SigBit));
  395.   }
  396.  
  397.   while (msg= (struct IntuiMessage *)GetMsg(anim_wnd->UserPort)) 
  398.     handle_edit_message(msg);
  399.  
  400.   return(TRUE);
  401. }
  402.  
  403. /*****************************************************/
  404.  
  405. static void handle_edit_message(struct IntuiMessage *msg)
  406. {
  407.   int
  408.     shifted = msg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT),
  409.     alt     = msg->Qualifier & (IEQUALIFIER_LALT   | IEQUALIFIER_RALT);
  410.  
  411.   switch (msg->Class)     
  412.     {
  413.     case IDCMP_MOUSEBUTTONS: 
  414.       switch(msg->Code)
  415.     {
  416.     case SELECTDOWN:
  417.       msg->IDCMPWindow->Flags |= WFLG_REPORTMOUSE;
  418.       memset(mprev, 0, sizeof(mprev));
  419.       mouse_event(M_DOWN, msg->MouseX, msg->MouseY, 
  420.               alt ? Button2 : Button1, shifted);
  421.       break;
  422.  
  423.     case SELECTUP:
  424.       msg->IDCMPWindow->Flags &= ~WFLG_REPORTMOUSE;
  425.       mouse_event(M_UP,   msg->MouseX, msg->MouseY, 
  426.               alt ? Button2 : Button1, FALSE);
  427.       break;
  428.  
  429.     case MIDDLEDOWN: alt = TRUE;
  430.     case MENUDOWN:
  431.       msg->IDCMPWindow->Flags |= WFLG_REPORTMOUSE;
  432.       memset(mprev, 0, sizeof(mprev));  
  433.       mouse_event(M_DOWN, msg->MouseX, msg->MouseY, 
  434.               alt ? Button2 : Button3, shifted);
  435.       break;
  436.  
  437.     case MIDDLEUP: alt = TRUE;
  438.     case MENUUP:
  439.       msg->IDCMPWindow->Flags &= ~WFLG_REPORTMOUSE;
  440.       mouse_event(M_UP,   msg->MouseX, msg->MouseY, 
  441.               alt ? Button2 : Button3, FALSE);
  442.       break;
  443.     }
  444.     case IDCMP_MOUSEMOVE: 
  445.       mprev[moffset].x = msg->MouseX;
  446.       mprev[moffset].y = msg->MouseY;
  447.       mprev[moffset].t = (unsigned long)1000 * msg->Seconds + .001 * msg->Micros;
  448.       moffset = (moffset + 1) % MOUSE_PREV;
  449.       mouse_event(M_DRAG, msg->MouseX, msg->MouseY, AnyButton, FALSE);
  450.       break;    
  451.     case IDCMP_VANILLAKEY:
  452.       DoVanillaKey(msg);
  453.       break;
  454.     case IDCMP_RAWKEY:
  455.       break;
  456.     }
  457.   ReplyMsg((struct Message *)msg);
  458. }
  459.  
  460. /*****************************************************/
  461.  
  462.  
  463. static void DoVanillaKey(struct IntuiMessage *msg)
  464. {
  465.   switch(msg->Code) 
  466.     {
  467.     case IECODE_ASCII_DEL :  delete_selected(); review_system(); break;
  468.     case '0' : erase = FALSE; break;
  469.     case '1' : erase = TRUE; break;
  470.     case ' ' : 
  471.       DoMethod(WI_ASpringies, MUIM_Window_ScreenToFront);
  472.       DoMethod(WI_ASpringies, MUIM_Window_ToFront);
  473.       set(WI_ASpringies, MUIA_Window_Activate, TRUE);
  474.       break;
  475.     }
  476. }
  477.  
  478. /*****************************************************/
  479.  
  480. int mass_radius(double m)
  481. {
  482.   int rad;
  483.  
  484.   rad = (int)(2 * log(4.0 * m + 1.0));
  485.  
  486.   if (rad < 1) rad = 1;
  487.   if (rad > 64) rad = 64;
  488.  
  489.   return rad;
  490. }
  491.  
  492. /*****************************************************/
  493.  
  494. static void mouse_vel(int *mx, int *my)
  495. {
  496.   int i, totalx = 0, totaly = 0, scale = 0, dx, dy, dt;
  497.   int fudge = 256;
  498.  
  499.   for (i = 0; i < MOUSE_PREV - 1; i++) {
  500.     dx = mprev[(moffset + 2 + i) % MOUSE_PREV].x 
  501.       -  mprev[(moffset + 1 + i) % MOUSE_PREV].x;
  502.     dy = mprev[(moffset + 2 + i) % MOUSE_PREV].y 
  503.       -  mprev[(moffset + 1 + i) % MOUSE_PREV].y;
  504.     dt = mprev[(moffset + 2 + i) % MOUSE_PREV].t 
  505.       -  mprev[(moffset + 1 + i) % MOUSE_PREV].t;
  506.     
  507.     if (dt) {
  508.       scale += 64 * i * i;
  509.       totalx += 64 * i * i * fudge * dx / dt;
  510.       totaly += 64 * i * i * fudge * dy / dt;
  511.     }
  512.   }
  513.  
  514.   if (scale) {
  515.     totalx /= scale;
  516.     totaly /= scale;
  517.   }
  518.   
  519.   *mx = totalx;
  520.   *my = totaly;
  521. }
  522.  
  523. /*****************************************************/
  524.  
  525. static void view_subsystem(int ulx, int uly, int wid, int ht)
  526. {
  527.   if (ulx < 0) {
  528.     wid += ulx;
  529.     ulx = 0;
  530.   }
  531.   if (uly < 0) {
  532.     ht += uly;
  533.     uly = 0;
  534.   }
  535.   if (wid < 0 || ht < 0)
  536.     return;
  537.  
  538.   if (ulx + wid >= draw_wid)
  539.     wid -= (ulx + wid - draw_wid);
  540.   if (uly + ht >= draw_ht)
  541.     ht -= (uly + ht - draw_ht);
  542.  
  543.   if (wid < 0 || ht < 0)
  544.     return;
  545.  
  546.   if (!action)
  547.     ClipBlit(anim_rport[back], ulx, uly, anim_rport[front], ulx, uly,
  548.          wid, ht, 0xC0);
  549. }
  550.  
  551. /*****************************************************/
  552.  
  553. void review_system(void)
  554. {
  555.   /* Clear the old pixmap */
  556.   if (erase) SetRast(anim_rport[back], 0);
  557.  
  558.   redraw_system();
  559.   view_system();
  560.  
  561.   mouse_event(M_REDISPLAY, 0, 0, AnyButton, FALSE);
  562.  
  563.   if (mst.adaptive_step) 
  564.     SetFL(ST_Step, mst.cur_dt);
  565. }
  566.  
  567. /*****************************************************/
  568.  
  569. static void mouse_event(int type, WORD mx, WORD my, int mbutton, int shifted)
  570.  {
  571.   static int 
  572.     startx = 0, prevx = 0, starty = 0, prevy = 0, 
  573.     cur_button = 0, selection = -1;
  574.   static boolean 
  575.     active = FALSE, cur_shift = FALSE, static_spring = FALSE;
  576.  
  577.   /* Skip restarts when active or continuations when inactive */
  578.   if ((type != M_DOWN && !active) || (type == M_DOWN && active))
  579.     return;
  580.  
  581.   /* Do grid snapping operation */
  582.   if (mst.grid_snap && mode != M_EDIT ){
  583.     mx = ((mx + (int)mst.cur_gsnap/2)/(int)mst.cur_gsnap) * (int)mst.cur_gsnap;
  584.     my = ((my + (int)mst.cur_gsnap/2)/(int)mst.cur_gsnap) * (int)mst.cur_gsnap;
  585.   }
  586.  
  587.   switch (type) 
  588.     {
  589.     case M_REDISPLAY:
  590.       switch (mode)
  591.     {
  592.     case M_EDIT:
  593.       switch (cur_button) 
  594.         {
  595.         case Button1:
  596.           if (selection < 0) 
  597.         draw_edit_frame(startx, starty, prevx, prevy);
  598.           break;
  599.         case Button2:
  600.           break;
  601.         case Button3:
  602.           break;
  603.         }
  604.       break;
  605.     case M_MASS:
  606.       draw_mass(TRUE, prevx, prevy, mst.cur_mass, FALSE, mst.fix_mass);
  607.       break;
  608.     case M_SPRING:
  609.       if (static_spring) {
  610.         startx = COORD_X(masses[selection].x);
  611.         starty = COORD_Y(masses[selection].y);
  612.         DRAWLINE(anim_rport[front], startx, starty, prevx, prevy);
  613.       }
  614.       break;
  615.     }
  616.       break;
  617.  
  618.     case M_DOWN:
  619.       review_system();
  620.       startx = prevx = mx;
  621.       starty = prevy = my;
  622.       cur_button = mbutton;
  623.       active = TRUE;
  624.       cur_shift = shifted;
  625.  
  626.       switch (mode) 
  627.     {
  628.     case M_EDIT:
  629.       switch (cur_button) 
  630.         {
  631.         case Button1:
  632.           {
  633.         boolean is_mass = FALSE;
  634.         selection = nearest_object(COORD_X(mx), COORD_Y(my), &is_mass);
  635.         
  636.         /* If not shift clicking, unselect all currently selected items 
  637.          */
  638.         if (!shifted) {
  639.           unselect_all();
  640.           review_system();
  641.         }
  642.           }
  643.           break;
  644.         case Button2:
  645.         case Button3:
  646.           tempfixed_obj(TRUE);
  647.           break;
  648.         }
  649.       break;
  650.     case M_MASS:
  651.       draw_mass(TRUE, mx, my, mst.cur_mass, FALSE, mst.fix_mass);
  652.       break;
  653.     case M_SPRING:        /* start setting a new spring */
  654.       {
  655.         boolean is_mass = TRUE;
  656.  
  657.         /* static = doesn't affect the mass it's attached to
  658.          */
  659.         static_spring = (!action || cur_button == Button3);
  660.  
  661.         selection = nearest_object(COORD_X(mx), COORD_Y(my), &is_mass);
  662.         if (selection >= 0 && is_mass) {
  663.           startx = COORD_X(masses[selection].x);
  664.           starty = COORD_Y(masses[selection].y);
  665.           if (static_spring) {
  666.         DRAWLINE(anim_rport[front], startx, starty, prevx, prevy);
  667.           } else {
  668.         attach_fake_spring(selection);
  669.         move_fake_mass(COORD_X(mx), COORD_Y(my));
  670.         review_system();
  671.           }
  672.         } else {
  673.           active = FALSE;
  674.         }
  675.       }
  676.       break;
  677.     }
  678.       break;
  679.  
  680.     case M_DRAG:
  681.       switch (mode) 
  682.     {
  683.     case M_EDIT:
  684.       switch (cur_button) 
  685.         {
  686.         case Button1:
  687.           if (selection < 0) { /* redraw frame */
  688.         view_subsystem(MIN(startx, prevx), MIN(starty, prevy), 
  689.                    ABS(prevx - startx)+1, ABS(prevy - starty)+1);
  690.         prevx = mx;
  691.         prevy = my;
  692.         draw_edit_frame(startx, starty, prevx, prevy);
  693.           }
  694.           break;
  695.         case Button2:
  696.         case Button3:
  697.                 /* Move objects relative to mouse */
  698.           translate_selobj(COORD_DX(mx - prevx), COORD_DY(my - prevy));
  699.           review_system();
  700.           prevx = mx;
  701.           prevy = my;
  702.           break;
  703.         }    
  704.       break;
  705.     case M_MASS:        /* move dragged mass to new position */
  706.       {
  707.         int rad = mst.fix_mass ? NAIL_SIZE : mass_radius(mst.cur_mass); 
  708.         view_subsystem(prevx - rad, prevy - rad, rad * 2 + 1, rad * 2 + 1);
  709.       }
  710.       prevx = mx;
  711.       prevy = my;
  712.       draw_mass(TRUE, prevx, prevy, mst.cur_mass, FALSE, mst.fix_mass);
  713.       break;
  714.     case M_SPRING:        /* redraw spring being edited to new end position */
  715.       if (static_spring) {
  716.         view_subsystem(MIN(startx, prevx), MIN(starty, prevy), 
  717.                ABS(prevx - startx) + 1, ABS(prevy - starty) + 1);
  718.         prevx = mx;
  719.         prevy = my;
  720.         startx = COORD_X(masses[selection].x);
  721.         starty = COORD_Y(masses[selection].y);
  722.         DRAWLINE(action ? anim_rport[back] : anim_rport[front], startx, starty, prevx, prevy);
  723.       } else {
  724.         move_fake_mass(COORD_X(mx), COORD_Y(my));
  725.       }
  726.       break;
  727.     }
  728.       break;
  729.  
  730.     case M_UP:
  731.       active = FALSE;
  732.       switch (mode) {
  733.       case M_EDIT:
  734.     switch (cur_button) {
  735.     case Button1:
  736.       if (selection < 0) {    /* select objects in current frame*/
  737.         select_objects(MIN(COORD_X(startx), COORD_X(mx)), 
  738.                MIN(COORD_Y(starty), COORD_Y(my)),
  739.                MAX(COORD_X(startx), COORD_X(mx)), 
  740.                MAX(COORD_Y(starty), COORD_Y(my)), cur_shift);
  741.         eval_selection();
  742.         review_system();
  743.       } else {        /* select single object */
  744.         boolean is_mass = FALSE;
  745.         
  746.         if ((selection = nearest_object(COORD_X(mx), COORD_Y(my), 
  747.                         &is_mass)) >= 0) {
  748.           select_object(selection, is_mass, cur_shift);
  749.           eval_selection();
  750.           review_system();
  751.         }
  752.       }
  753.       break;
  754.     case Button2:
  755.       tempfixed_obj(FALSE);
  756.       review_system();
  757.       break;
  758.     case Button3:        /* throw objects */
  759.       {
  760.         int mvx, mvy;
  761.         
  762.         mouse_vel(&mvx, &mvy);
  763.  
  764.         changevel_selobj(COORD_DX(mvx), COORD_DY(mvy), FALSE);
  765.         tempfixed_obj(FALSE);
  766.         review_system();
  767.       }
  768.       break;
  769.     }
  770.     break;
  771.       case M_MASS:        /* create new mass */
  772.     {
  773.       int newm;
  774.  
  775.       newm = create_mass();
  776.  
  777.       masses[newm].x = COORD_X((double)mx);
  778.       masses[newm].y = COORD_Y((double)my);
  779.       masses[newm].mass = mst.cur_mass;
  780.       masses[newm].radius = mass_radius(mst.cur_mass);
  781.       masses[newm].elastic = mst.cur_rest;
  782.       if (mst.fix_mass) masses[newm].status |= S_FIXED;
  783.  
  784.       review_system();
  785.     }
  786.     break;
  787.  
  788.       case M_SPRING:        /* create new spring */
  789.     {
  790.       boolean is_mass = TRUE;
  791.       int start_sel = selection, news, endx, endy;
  792.  
  793.       if (!static_spring) 
  794.         kill_fake_spring();
  795.         
  796.       selection = nearest_object(COORD_X(mx), COORD_Y(my), &is_mass);
  797.       if ((static_spring || !action || cur_button == Button1)
  798.           && selection >= 0 && is_mass && selection != start_sel) {
  799.         startx = COORD_X(masses[start_sel].x);
  800.         starty = COORD_Y(masses[start_sel].y);
  801.         endx = COORD_X(masses[selection].x);
  802.         endy = COORD_Y(masses[selection].y);
  803.         
  804.         news = create_spring();
  805.         springs[news].m1 = start_sel;
  806.         springs[news].m2 = selection;
  807.         springs[news].ks = mst.cur_ks;
  808.         springs[news].kd = mst.cur_kd;
  809.         springs[news].restlen = sqrt(  (double)SQR(startx - endx)
  810.                      + (double)SQR(starty - endy));
  811.         add_massparent(start_sel, news);
  812.         add_massparent(selection, news);
  813.       }
  814.       review_system();
  815.     }
  816.     break;
  817.       }
  818.       break;
  819.     }
  820. }
  821.  
  822. /*****************************************************/
  823.  
  824. static void view_system(void)
  825. {
  826.   int old_back = back;
  827.  
  828.   ShowView(back);
  829.   back = front;
  830.   front = old_back;
  831. }
  832.  
  833. /*****************************************************/
  834.  
  835. static void redraw_system(void)
  836. {  
  837.   int     i, rad, num_spr;  
  838.   mass   *m1ptr, *m2ptr;
  839.   spring *sptr;
  840.  
  841.   num_spr = (mst.show_spring) ? num_spring : 1;
  842.  
  843.   SetDrPt(anim_rport[back], 0xffff);
  844.   if (mst.w_top)    DRAWLINE(anim_rport[back],1,          1,         draw_wid-2, 1        );
  845.   if (mst.w_left)   DRAWLINE(anim_rport[back],1,          1,         1,          draw_ht-2);
  846.   if (mst.w_right)  DRAWLINE(anim_rport[back],draw_wid-2, 1,         draw_wid-2, draw_ht-2);
  847.   if (mst.w_bottom) DRAWLINE(anim_rport[back],1,          draw_ht-2, draw_wid-2, draw_ht-2);
  848.   
  849.   {
  850.     static UWORD selspringpat = 0x0f0f;
  851.     
  852.     if (action)
  853.       selspringpat = (selspringpat >> 15) + (selspringpat << 1);
  854.  
  855.     for (i=0, sptr = springs; i<num_spr; i++, sptr++) {
  856.       m1ptr = masses + sptr->m1;
  857.       m2ptr = masses + sptr->m2;
  858.       if (sptr->status & S_ALIVE) {
  859.     if (sptr->status & S_SELECTED)
  860.       SetDrPt(anim_rport[back], selspringpat);
  861.     DRAWLINE(anim_rport[back], (int)COORD_X(m1ptr->x), (int)COORD_Y(m1ptr->y),
  862.          (int)COORD_X(m2ptr->x), (int)COORD_Y(m2ptr->y));
  863.     SetDrPt(anim_rport[back], 0xffff);
  864.       }
  865.     }
  866.   }
  867.  
  868.   for (i=0, m1ptr = masses; i<num_mass; i++, m1ptr++) 
  869.     if (m1ptr->status & S_ALIVE)
  870.       draw_mass(FALSE, (int)(COORD_X(m1ptr->x)), (int)(COORD_Y(m1ptr->y)),
  871.         m1ptr->mass, (boolean)(m1ptr->status & S_SELECTED),
  872.         (boolean)((m1ptr->status & S_FIXED) 
  873.               && !(m1ptr->status & S_TEMPFIXED)));      
  874.   
  875.  
  876.   if (mst.center_id != -1)
  877.     if (((m1ptr = &masses[mst.center_id])->status & S_FIXED)
  878.     && !(m1ptr->status & S_TEMPFIXED)) { 
  879.                 /* Draw center (fixed mass) */
  880.       int 
  881.     cx = (int)COORD_X(m1ptr->x),
  882.     cy = (int)COORD_Y(m1ptr->y);
  883.  
  884.       SetAPen(anim_rport[back], 1);
  885.       Move(anim_rport[back], cx-6, cy-12); 
  886.       Draw(anim_rport[back], cx+6, cy-12);
  887.       Draw(anim_rport[back], cx+6, cy+1);
  888.       Draw(anim_rport[back], cx-6, cy+1);
  889.       Draw(anim_rport[back], cx-6, cy-12);
  890.     }
  891.     else {
  892.       rad = m1ptr->radius;
  893.       
  894.       SetAPen(anim_rport[back], 0);
  895.       SetAfPt(anim_rport[back], 0,1);
  896.       RectFill(anim_rport[back],
  897.            (int)COORD_X(m1ptr->x) - rad + 1, (int)COORD_Y(m1ptr->y) - rad + 1,
  898.            (int)COORD_X(m1ptr->x) + rad - 1, (int)COORD_Y(m1ptr->y) + rad - 1);
  899.       SetAPen(anim_rport[back], 1);
  900.     }
  901. }
  902.  
  903. /*****************************************************/
  904.  
  905. static void draw_mass
  906.   (
  907.    boolean to_front_rp,
  908.    int mx,
  909.    int my,
  910.    double mass,
  911.    boolean selected,
  912.    boolean fixed
  913.    )
  914. {
  915.   static __chip UWORD
  916.     data_unsel[20]  = {7936,24768,32800,32800,32800,57568,32704,7936,1024,
  917.              1024,1024},
  918.     data_sel[20]    = {2560,8320,32800,0,32800,0,21824,2560,1024,0,1024},
  919.     selpattern[2]   = {0x5555, 0xaaaa},
  920.     unselpattern[2] = {0xffff, 0xffff};
  921.   static struct Image 
  922.     UnselNailImage = {0, 0, 11,11, 1, &data_unsel[0], 0x01, 0x00, NULL},
  923.     SelNailImage   = {0, 0, 11,11, 1, &data_sel[0],   0x01, 0x00, NULL};
  924.   struct RastPort
  925.     *rp = to_front_rp ? anim_rport[front] : anim_rport[back];
  926.   int
  927.     rad = fixed ? NAIL_SIZE : mass_radius(mass); 
  928.  
  929.   if (fixed) {
  930.     if (mx+5 >= 0 && mx-5 <= draw_wid && my >= 0 && my-11 <= draw_ht)
  931.       DrawImage(rp, selected ? &SelNailImage : &UnselNailImage, mx - 5, my - 11);
  932.   } else if (mx+rad >= 0 && mx-rad <= draw_wid && my+rad >= 0 && my-rad <= draw_ht) {
  933.     SetAfPt(rp, selected ? selpattern : unselpattern,1);
  934.     RectFill(rp, mx - rad, my - rad, mx+rad, my+rad);
  935.   }
  936. }
  937.  
  938. /*****************************************************/
  939.  
  940. static void draw_edit_frame(int x1, int y1, int x2, int y2)
  941. {
  942.   static UWORD pattern = 0x0f0f;
  943.  
  944.   if (action)
  945.     pattern = (pattern >> 15) + (pattern << 1);
  946.   SetDrPt(anim_rport[front], pattern);
  947.   Move(anim_rport[front], x1, y1);
  948.   Draw(anim_rport[front], x2, y1); Draw(anim_rport[front], x2, y2);
  949.   Draw(anim_rport[front], x1, y2); Draw(anim_rport[front], x1, y1);  
  950.   SetDrPt(anim_rport[front], 0xffff);
  951. }
  952.  
  953. /*****************************************************/
  954.  
  955. static void DoRestoreState(void)
  956. {
  957.   action = FALSE;
  958.   restore_state();
  959.   mst = sst;
  960.  
  961.   redisplay_widgets();
  962.   review_system();
  963.   mouse_event(M_REDISPLAY, 0, 0, AnyButton, FALSE);
  964. }
  965.  
  966. /*****************************************************/
  967.  
  968. static void DoRestore(void)
  969. {
  970.   action = FALSE;
  971.   delete_all();
  972.   mst = initst;
  973.   init_objects();
  974.   
  975.   redisplay_widgets();
  976.   review_system();
  977.   mouse_event(M_REDISPLAY, 0, 0, AnyButton, FALSE);
  978. }
  979.  
  980. /*****************************************************/
  981.