home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 13 / 13.iso / p / p024 / 12.img / ADS3.LIB / GRAVITY.C < prev    next >
Encoding:
Text File  |  1993-02-17  |  64.5 KB  |  1,806 lines

  1. /* Next available MSG number is 141 */
  2. /*
  3.  
  4.       GRAVITY.C
  5.       ¬⌐┼v (C) 1990-1992  Autodesk ñ╜Ñq
  6.  
  7.       Ñ╗│n┼ΘºK╢O¿╤▒z╢iªµÑ⌠ª≤Ñ╬│~╗▌¿D¬║½■¿⌐íB¡╫º∩ñ╬╡oªµ, ª²¼O░╚╜╨┐φ┤`ñU¡z
  8.       ¡∞½h :
  9.  
  10.       1)  ñWªC¬║¬⌐┼v│qºi░╚╗▌ÑX▓{ªb¿Cñ@Ñ≈½■¿⌐∙╪íC
  11.       2)  ¼█├÷¬║╗í⌐·ñσÑ≤ñ]Ñ▓╢╖⌐·╕ⁿ¬⌐┼v│qºiñ╬Ñ╗╢╡│\Ñi│qºiíC
  12.  
  13.       Ñ╗│n┼Θ╢╚┤ú¿╤º@¼░└│Ñ╬ñW¬║░╤ª╥, ª╙Ñ╝┴n⌐·⌐╬┴⌠ºtÑ⌠ª≤½O├╥; ╣∩⌐≤Ñ⌠ª≤»S«φ
  14.       Ñ╬│~ñº╛A║┘⌐╩, ÑHñ╬░╙╖~╛P░Γ⌐╥┴⌠ºtÑX¿π¬║½O├╥, ªbª╣ñ@╖ºñ⌐ÑHº_╗{íC
  15.  
  16.  
  17.  
  18.         N-Body Gravitational Interaction Simulator for AutoCAD
  19.  
  20.         A sample ADS application.
  21.  
  22.         Designed and implemented by John Walker in August of 1989.
  23.  
  24.            "Ubi materia, ibi geometria."
  25.                       -- Johannes Kepler
  26.  
  27.            "The orbit of  any  one  planet  depends  on  the
  28.            combined  motion  of  all  the  planets,  not  to
  29.            mention the action of all of these on each other.
  30.            But  to  consider simultaneously all these causes
  31.            of motion and to define these  motions  by  exact
  32.            laws   allowing   of   conventional   calculation
  33.            exceeds, unless I am mistaken, the force  of  the
  34.            entire human intellect."
  35.                       -- Sir Isaac Newton, Principia
  36.  
  37.  
  38.         The  physics  underlying  this simulation are explained in the
  39.         the  chapter  "A  Cosmic  Ballet",  pp.   229-238  of  A.   K.
  40.         Dewdney,  "The  Armchair Universe", W.  H.  Freeman: New York,
  41.         1988.
  42.  
  43.         The following commands are implemented in this file:
  44.  
  45.         DEMO        Creates  one  of  a series of standard demo models
  46.                     when executed in  a  new,  empty  drawing.   These
  47.                     models  may be modified with the MASS and MASSEDIT
  48.                     commands just like models entered manually.
  49.  
  50.         FRAME       Asks you to pick a mass entity.   Motion  is  then
  51.                     displayed in that mass's reference frame (in other
  52.                     words, that mass  appears  stationary  and  others
  53.                     move  around  it).  The default reference frame is
  54.                     "Inertial", an unaccelerated frame  at  rest  with
  55.                     respect  to  the  distant  stars.   Specifying  no
  56.                     entity to the  FRAME  command  re-establishes  the
  57.                     inertial frame.
  58.  
  59.         MASS        Creates  a  new  mass.   You're invited to enter a
  60.                     name for the mass, its position (specified by  any
  61.                     means  of  co-ordinate  specification), a velocity
  62.                     vector in units of  astronomical  units  per  year
  63.                     (which  can  be either entered explicitly or drawn
  64.                     by typing "V", then dragging the endpoint  of  the
  65.                     velocity  vector to the correct position), and its
  66.                     mass in units of Solar masses.
  67.  
  68.         MASSEDIT    Lets you modify the name, velocity, and mass of an
  69.                     existing mass.  Pick a single mass by pointing.  A
  70.                     dialogue is displayed with the properties  of  the
  71.                     mass.   Change  them  as you wish, then pick OK to
  72.                     update the properties of the mass in the database.
  73.                     If you pick Cancel, the mass is not modified.
  74.  
  75.         RESET       When  a  simulation  is  run,  it  halts after the
  76.                     specified number of steps or  simulated  time  has
  77.                     elapsed.    A   subsequent  RUN  command  normally
  78.                     resumes the simulation from the point at which the
  79.                     last  stopped.  RESET erases all motion paths from
  80.                     the screen and sets the  simulation  back  to  the
  81.                     start.    RUN   after   a  RESET  will  begin  the
  82.                     simulation from the initial state.
  83.  
  84.         RUN         Starts the simulation.  You're  asked  to  specify
  85.                     the length of the simulation as either a number of
  86.                     motion steps or by the number of simulated  years.
  87.                     If  you  enter  a positive number, it's taken as a
  88.                     step count.  Negative numbers (which  may  include
  89.                     decimal   fractions)  specify  simulated  time  in
  90.                     years.  The length in time of each simulation step
  91.                     varies  based  upon the velocity and separation of
  92.                     masses, so if objects approach  one  another  very
  93.                     closely  a  very  large  number  of  steps will be
  94.                     required to simulate a given time  interval.   The
  95.                     calculation time per step is essentially constant,
  96.                     so if you're investigating a system  with  unknown
  97.                     behaviour  you'll probably want to RUN for a given
  98.                     number of cycles, but when running a stable system
  99.                     such as the Solar System, you can simulate a given
  100.                     time span.  In  any  case,  you  can  terminate  a
  101.                     simulation with the Control C key.  RUN  can  also
  102.                     be  invoked as a function, (C:RUN <length>), where
  103.                     <length> specifies  the  simulation  length  by  a
  104.                     positive  or  negative  number as described above.
  105.                     When called  as  a  function,  C:RUN  returns  the
  106.                     simulation end time as its result.
  107.  
  108.         SETGRAV     The  Gravity  simulator has several variables that
  109.                     control its operation.  These variables are  saved
  110.                     with the drawing and may be inspected and modified
  111.                     with the  SETGRAV  command.   SETGRAV  displays  a
  112.                     dialogue  in  which  you  may  change  any  of the
  113.                     following variables:
  114.  
  115.                        Output to display only?
  116.                           This is set to Yes or  No  (True/False,  and
  117.                           1/0  are  also  accepted).   If  set  to the
  118.                           default value of Yes, motion paths are drawn
  119.                           using  temporary  vectors within the current
  120.                           viewport.  If the picture is  REDRAWn,  they
  121.                           disappear.   If  set to No, motion paths are
  122.                           added to the drawing as LINE entities.  This
  123.                           causes paths to appear in all viewports, and
  124.                           you can  ZOOM  on  sections  of  a  path  to
  125.                           examine   it   in   greater  detail.   Paths
  126.                           represented as  LINEs  are  saved  with  the
  127.                           drawing.  Generating LINE entities for paths
  128.                           is much slower than just drawing them on the
  129.                           screen,  so  choose  this mode only when you
  130.                           need it.
  131.  
  132.                        Display step number?
  133.                           If this mode is  "Yes"  (the  default),  the
  134.                           simulation  step  number is displayed in the
  135.                           coordinate status  line  as  the  simulation
  136.                           progresses.
  137.  
  138.                        Display time?
  139.                           If "Yes" (the default) the simulated time in
  140.                           years is displayed in the coordinate  status
  141.                           line.
  142.  
  143.                        Step size?
  144.                           This real number specifies the  factor  used
  145.                           to  determine  the  size  of the integration
  146.                           steps used in calculating the motion of  the
  147.                           masses.   The  time  step  is  calculated by
  148.                           dividing  the  distance  between   the   two
  149.                           closest   masses  by  the  highest  relative
  150.                           velocity  of  any  pair  of  masses.    This
  151.                           quantity,  measured  in years, is multiplied
  152.                           by the factor  given  by  this  variable  to
  153.                           obtain  the length of the step.  The default
  154.                           value  of  0.1  works  well  for  reasonably
  155.                           well-behaved  simulations.  If you find that
  156.                           a simulation is taking  too  long,  you  can
  157.                           speed it up by increasing the step size, but
  158.                           be aware that the results you see may not be
  159.                           physically  accurate.   Increasing  the step
  160.                           size magnifies the inaccuracies of  modeling
  161.                           a  continuous process such as gravitation by
  162.                           discrete  steps.   In   general,   you   can
  163.                           increase the step size as long as you obtain
  164.                           the same results.  When the  outcome  begins
  165.                           to vary, you've set the step size too large.
  166.  
  167.                        Minimum step?
  168.                           After  the  step  size  is   calculated   as
  169.                           described  above  it  is  compared  with the
  170.                           minimum step size and, if less, the  minimum
  171.                           is  used.   The  minimum step size is set to
  172.                           0.00001 years (about  5  minutes);  this  is
  173.                           sufficient  to unstick many simulations that
  174.                           involve a close encounter.  Amazingly,  even
  175.                           a  minimum  this  short  can  yield  grossly
  176.                           inaccurate results when solar masses execute
  177.                           hairpin turns about one another
  178.  
  179.         UPDATE      A  simulation  normally  proceeds from the initial
  180.                     positions, velocities, and masses of  the  objects
  181.                     specified  by  the  MASS  command.  The simulation
  182.                     keeps track of these values as it  progresses  but
  183.                     does  not automatically adjust the mass objects in
  184.                     the database.  If you want to  move  the  database
  185.                     objects  to  the  positions at the end of the most
  186.                     recent simulation (and adjust their velocities  to
  187.                     the  corresponding  instantaneous velocities), use
  188.                     the UPDATE command.  If you don't  do  an  UPDATE,
  189.                     the masses will remain in their original positions
  190.                     even if you  save  the  drawing  after  running  a
  191.                     simulation.
  192.  
  193. */
  194.  
  195. #include   <stdio.h>
  196. #include   <string.h>
  197. #ifndef MPW32WORK
  198. #include   <ctype.h>
  199. #endif
  200. #include   <math.h>
  201. #include   <assert.h>
  202.  
  203. #include   "adslib.h"
  204.  
  205. /*  Standard drawing object names  */
  206.  
  207. /* Utility frozen layer for information */
  208. #define FrozenLayer /*MSG1*/"FROZEN-SOLID"
  209. #define OrbitLayer  /*MSG2*/"ORBITS"  /* Layer for orbital path traces */
  210.  
  211. /*  Data types  */
  212.  
  213. typedef enum {False = 0, True = 1} Boolean;
  214.  
  215. #define HANDLEN  18                   /* String long enough to hold a handle */
  216.  
  217. /* Definitions  to  wrap  around  submission  of  AutoCAD commands to
  218.    prevent their being echoed.  */
  219.  
  220. #define Cmdecho  False                /* Make True for debug command output */
  221.  
  222. #define CommandB()  { struct resbuf rBc, rBb, rBu, rBh; \
  223.         ads_getvar(/*MSG0*/"CMDECHO", &rBc); \
  224.         ads_getvar(/*MSG0*/"BLIPMODE", &rBb); \
  225.         ads_getvar(/*MSG0*/"HIGHLIGHT", &rBh); \
  226.         rBu.restype = RTSHORT; \
  227.         rBu.resval.rint = (int) Cmdecho; \
  228.         ads_setvar(/*MSG0*/"CMDECHO", &rBu); \
  229.         rBu.resval.rint = (int) False; \
  230.         ads_setvar(/*MSG0*/"BLIPMODE", &rBu); \
  231.         ads_setvar(/*MSG0*/"HIGHLIGHT", &rBu)
  232.  
  233. #define CommandE()  ads_setvar(/*MSG0*/"CMDECHO", &rBc); \
  234.                     ads_setvar(/*MSG0*/"BLIPMODE", &rBb); \
  235.                     ads_setvar(/*MSG0*/"HIGHLIGHT", &rBh); }
  236.  
  237. /*  Definitions  that permit you to push and pop system variables with
  238.     minimal complexity.  These don't work  (and  will  cause  horrible
  239.     crashes  if  used)  with  string  variables,  but since all string
  240.     variables are read-only, they cannot be saved and restored in  any
  241.     case.  */
  242.  
  243. #define PushVar(var, cell, newval, newtype) { struct resbuf cell, cNeW; \
  244.         ads_getvar(var, &cell); cNeW.restype = cell.restype;             \
  245.         cNeW.resval.newtype = newval; ads_setvar(var, &cNeW)
  246.  
  247. #define PopVar(var, cell) ads_setvar(var, &cell); }
  248.  
  249. /* Set point variable from three co-ordinates */
  250.  
  251. #define Spoint(pt, x, y, z)  pt[X] = (x);  pt[Y] = (y);  pt[Z] = (z)
  252.  
  253. /* Copy point  */
  254.  
  255. #define Cpoint(d, s)   d[X] = s[X];  d[Y] = s[Y];  d[Z] = s[Z]
  256.  
  257. /* Generation parameters for objects */
  258.  
  259. #define SphereLong  12                /* Longitudinal tabulations on sphere */
  260. #define SphereLat   12                /* Latitudinal tabulations on sphere */
  261.  
  262. /* Utility definition to get an  array's  element  count  (at  compile
  263.    time).   For  example:
  264.  
  265.        int  arr[] = {1,2,3,4,5};
  266.        ...
  267.        printf("%d", ELEMENTS(arr));
  268.  
  269.    would print a five.  ELEMENTS("abc") can also be used to  tell  how
  270.    many  bytes are in a string constant INCLUDING THE TRAILING NULL. */
  271.  
  272. #define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))
  273.  
  274. /* Utility definitions */
  275.  
  276. #ifndef abs
  277. #define abs(x)      ((x)<0 ? -(x) : (x))
  278. #endif  /* abs */
  279. #ifdef min
  280. #undef  min
  281. #endif
  282. #define min(a,b)    ((a)<(b) ? (a) : (b))
  283. #ifdef max
  284. #undef  max
  285. #endif
  286. #define max(a,b)    ((a)>(b) ? (a) : (b))
  287.  
  288. /*  Many C implementations may lack a cube root function.  Rather than
  289.     count  on  a system cbrt() function, we define our own in terms of
  290.     functions more likely to be available.  */
  291.  
  292. #define cuberoot(x) (exp(log(x) / 3.0))
  293.  
  294. /* All Function Prototypes for gravity.c */
  295.  
  296. void            main    _((int argc, char *argv[]));
  297. static Boolean  funcload _((void));
  298. static char *   alloc   _((unsigned nbytes));
  299. static void     defmassblk _((void));
  300. static struct resbuf *
  301.                 entitem _((struct resbuf *rchain, int gcode));
  302. static void     entinfo _((ads_name en,char *h,ads_point p,ads_real *r,int *c));
  303. static void     partext _((void));
  304. static void     addvec  _((ads_real *ap, ads_real *bp, ads_real *cp));
  305. static void     subvec  _((ads_real *ap, ads_real *bp, ads_real *cp));
  306. static ads_real sqabsv  _((ads_real *ap));
  307. static void     sumvec  _((ads_real *ap,ads_real *bp,ads_real t, ads_real *cp));
  308. static void     varblockdef _((void));
  309. static void     savemodes _((void));
  310. static Boolean  boolvar _((char *varname, char *s));
  311. static void     varset  _((void));
  312. static void     setframe _((void));
  313. static Boolean  initacad _((Boolean reset));
  314. static void     addmass _((char *mn,ads_point pos,ads_point vel,ads_real mass));
  315. static void     mass    _((void));
  316. static void     demo    _((void));
  317. static void     massedit _((void));
  318. static void     reset   _((void));
  319. static void     frame   _((void));
  320. static void     run     _((void));
  321. static void     setgrav _((void));
  322. static void     update  _((void));
  323. #ifdef  HIGHC
  324. static void     abort   _((void));
  325. #endif
  326.  
  327.  
  328. /*  This  program  works in a somewhat unconventional system of units.
  329.     Length is measured in astronomical units (the mean  distance  from
  330.     the  Earth  to the Sun), mass in units of the mass of the Sun, and
  331.     time in years.  The following definitions derive the value of  the
  332.     gravitational  constant  in this system of units from its handbook
  333.     definition in SI units.  */
  334.  
  335. #define G_SI        6.6732e-11        /* (Newton Metre^2) / Kilogram^2 */
  336. #define AU          149504094917.0    /* Metres / Astronomical unit */
  337. #define M_SUN       1.989e30          /* Kilograms / Mass of Sun */
  338. #define YEAR        (365.0 * 24 * 60 * 60) /* Seconds / Year */
  339.  
  340. /*  From Newton's second law, F = ma,
  341.  
  342.            Newton = kg m / sec^2
  343.  
  344.     the fundamental units of the gravitational constant are:
  345.  
  346.            G = N m^2 / kg^2
  347.              = (kg m / sec^2) m^2 / kg^2
  348.              = kg m^3 / sec^2 kg^2
  349.              = m^3 / sec^2 kg
  350.  
  351.     The conversion factor, therefore,  between  the  SI  gravitational
  352.     constant and its equivalent in our units is:
  353.  
  354.            K = AU^3 / YEAR^2 M_SUN
  355.  
  356. */
  357.  
  358. #define GRAV_CONV   ((AU * AU * AU) / ((YEAR * YEAR) * M_SUN))
  359.  
  360. /*  And finally the  gravitational  constant  itself  is  obtained  by
  361.     dividing the SI definition by this conversion factor.  */
  362.  
  363. #define GRAVCON     (G_SI / GRAV_CONV)
  364.  
  365. /*  We also want to come up with approximate sizes for the  objects we
  366.     create.   The  actual  sizes  based  on the density of the objects
  367.     result in everything looking like geometrical points  so,  in  the
  368.     rich  tradition  of  celestial  maps, we enormously exaggerate the
  369.     sizes of objects to render them visible.  Our magic number is  one
  370.     tenth of the cube root of the mass of the object.  */
  371.  
  372. #define DENSCON     0.1
  373.  
  374. static Boolean functional;            /* C:command is returning result */
  375. static ads_real numsteps = 50;        /* Default number of steps to run */
  376. static double simtime = 0.0;          /* Simulated time */
  377. static long stepno = 0;               /* Step number */
  378. static char fhandle[HANDLEN];         /* Handle of reference frame entity */
  379. static int framei = -1;               /* Reference frame object index */
  380.  
  381. /*  Command definition and dispatch table.  */
  382.  
  383. struct {
  384.         char *cmdname;
  385.         void (*cmdfunc)();
  386. } cmdtab[] = {
  387. /*        Name         Function  */
  388. {/*MSG3*/"DEMO",       demo},         /* Create demo case */
  389. {/*MSG4*/"FRAME",      frame},        /* Set local reference frame */
  390. {/*MSG5*/"MASS",       mass},         /* Create new mass */
  391. {/*MSG6*/"MASSEDIT",   massedit},     /* Edit existing mass */
  392. {/*MSG7*/"RESET",      reset},        /* Erase orbital paths */
  393. {/*MSG8*/"RUN",        run},          /* Run simulation */
  394. {/*MSG9*/"SETGRAV",    setgrav},      /* Set mode variables */
  395. {/*MSG10*/"UPDATE",     update}       /* Update masses to last state */
  396. };
  397.  
  398. /*  Particle structure.  */
  399.  
  400. typedef struct {
  401.         char partname[32];            /* Particle name */
  402.         ads_point position;           /* Location in space */
  403.         ads_point velocity;           /* Velocity vector */
  404.         ads_point acceleration;       /* Acceleration vector */
  405.         ads_point lastpos;            /* Last plotted position */
  406.         ads_real mass;                /* Mass */
  407.         ads_real radius;              /* Radius */
  408.         int colour;                   /* Entity colour */
  409.         char partblock[HANDLEN];      /* Block defining particle */
  410. } particle;
  411.  
  412. static particle pt;                   /* Static particle structure */
  413. static particle *ptab = NULL;         /* Particle table */
  414. static int nptab = 0;                 /* Number of in-memory particles */
  415.  
  416. /*  Particle definition block attributes.  */
  417.  
  418. #define ParticleBlock  /*MSG11*/"PARTICLE"  /* Particle block name */
  419. struct {                              /* Attribute tag table */
  420.         char *attname;                /* Attribute tag name */
  421.         ads_real *attvar;             /* Variable address */
  422.         char *attprompt;              /* Prompt for attribute */
  423. } partatt[] = {
  424.         {/*MSG12*/"VELOCITY_X", &pt.velocity[X], /*MSG13*/"│t½╫ (X)"},
  425.         {/*MSG14*/"VELOCITY_Y", &pt.velocity[Y], /*MSG15*/"│t½╫ (Y)"},
  426.         {/*MSG16*/"VELOCITY_Z", &pt.velocity[Z], /*MSG17*/"│t½╫ (Z)"},
  427.         {/*MSG18*/"MASS",       &pt.mass,        /*MSG19*/"╜Φ╢q"}
  428.        };
  429.  
  430. /*  Modal  variables.   Default   initial   values   below   are   for
  431.     documentation  only.   The actual defaults are reset in initacad()
  432.     upon entry to the drawing editor, so that they will be placed with
  433.     the   default  values  in  the  modal  variable  block  if  it  is
  434.     subsequently created for a new drawing.  */
  435.  
  436. static Boolean drawonly = True,       /* DRAWONLY: Draw, but don't add entities
  437.                                                    if 1.  Make LINEs if 0. */
  438.                showstep = True,       /* SHOWSTEP: Show step in status line. */
  439.                showtime = True;       /* SHOWTIME: Show time in status line. */
  440. static ads_real stepsize = 0.1,       /* STEPSIZE: Motion step size factor. */
  441.                 stepmin = 0.00001;    /* STEPMIN:  Smallest step to use. */
  442.  
  443. /*  Modal attribute definition.  */
  444.  
  445. #define ModalBlock  /*MSG20*/"GRAVITY_MODES" /* Mode variable block name */
  446. struct {
  447.         char *attname;                /* Attribute tag name */
  448.         int attvari;                  /* Variable index */
  449.         char *attprompt;              /* Prompt for variable */
  450. } varatt[] = {
  451.         {/*MSG21*/"DRAWONLY", 1, /*MSG22*/"╢╚ñ⌐íu┼πÑ▄ív?           "},
  452.         {/*MSG23*/"SHOWSTEP", 3, /*MSG24*/"┼πÑ▄íu¿B╢i╝╞ív?         "},
  453.         {/*MSG25*/"SHOWTIME", 2, /*MSG26*/"┼πÑ▄íu«╔╢íív?           "},
  454.         {/*MSG27*/"STEPSIZE", 4, /*MSG28*/"¿B╢i╢q?                 "},
  455.         {/*MSG29*/"STEPMIN",  5, /*MSG30*/"íu¿B╢iívñU¡¡ (ª~)?      "}
  456.        };
  457.  
  458. /* MAIN -- the main routine */
  459.  
  460. void main(argc, argv)
  461.   int argc;
  462.   char *argv[];
  463. {
  464.     int stat, cindex, scode = RSRSLT;
  465.  
  466.     ads_init(argc, argv);             /* Initialise the application */
  467.  
  468.     /* Main dispatch loop. */
  469.  
  470.     while (True) {
  471.  
  472.         if ((stat = ads_link(scode)) < 0) {
  473.             printf(/*MSG31*/"GRAVITY: Ñ╤ ads_link() ╢╟ª^¬║ñú¿╬¬¼║A = %d\n",
  474.                    stat);
  475.             exit(1);
  476.         }
  477.  
  478.         scode = RSRSLT;               /* Default return code */
  479.  
  480.         switch (stat) {
  481.  
  482.         case RQXLOAD:                 /* Load functions.  Called at the start
  483.                                          of the drawing editor.  Re-initialise
  484.                                          the application here. */
  485.             scode = -(funcload() ? RSRSLT : RSERR);
  486.             break;
  487.  
  488.         case RQXUNLD:                 /* Application unload request. */
  489.             break;
  490.  
  491.         case RQSUBR:                  /* Evaluate external lisp function */
  492.             cindex = ads_getfuncode();
  493.             functional = False;
  494.             if (!initacad(False)) {
  495.                 ads_printf(/*MSG32*/"\n╡L¬k▒╥⌐líu└│Ñ╬╡{ªíívíC\n");
  496.             } else {
  497.  
  498.                 /* Execute the command from the command table with
  499.                    the index associated with this function. */
  500.  
  501.                 if (cindex > 0) {
  502.                     cindex--;
  503.                     assert(cindex < ELEMENTS(cmdtab));
  504.                     (*cmdtab[cindex].cmdfunc)();
  505.                 }
  506.             }
  507.             if (!functional)
  508.                 ads_retvoid();        /* Void result */
  509.             break;
  510.  
  511.         default:
  512.             break;
  513.         }
  514.     }
  515. }
  516.  
  517. /* FUNCLOAD  --  Load external functions into AutoLISP */
  518.  
  519. static Boolean funcload()
  520. {
  521.     char ccbuf[40];
  522.     int i;
  523.  
  524.     strcpy(ccbuf, /*MSG0*/"C:");
  525.     for (i = 0; i < ELEMENTS(cmdtab); i++) {
  526.         strcpy(ccbuf + 2, cmdtab[i].cmdname);
  527.         ads_defun(ccbuf, i + 1);
  528.     }
  529.  
  530.     return initacad(True);            /* Reset AutoCAD initialisation */
  531. }
  532.  
  533. /*  ALLOC  --  Allocate storage and fail if it can't be obtained.  */
  534.  
  535. static char *alloc(nbytes)
  536.   unsigned nbytes;
  537. {
  538.     char *cp;
  539.  
  540.     if ((cp = malloc(nbytes)) == NULL) {
  541.         ads_abort(/*MSG33*/"╢WÑX░O╛╨«e╢q");
  542.     }
  543.     return cp;
  544. }
  545.  
  546. /*  DEFMASSBLK  --  Create the mass definition block. */
  547.  
  548. static void defmassblk()
  549. {
  550.     ads_point centre, ucentre;
  551.     ads_real radius = 1;
  552.     ads_point ax, ax1;
  553.     struct resbuf rb1, rb2, ocolour;
  554.     ads_name e1, e2;
  555.     int i;
  556.     ads_name matbss;
  557.     ads_name ename;
  558.     ads_point atx;
  559.  
  560.     Spoint(ucentre, 4, 4, 0);
  561.     rb1.restype = rb2.restype = RTSHORT;
  562.     rb1.resval.rint = 1;              /* From UCS */
  563.     rb2.resval.rint = 0;              /* To world */
  564.     ads_trans(ucentre, &rb1, &rb2, False, centre);
  565.     CommandB();
  566.     ads_command(RTSTR, /*MSG0*/"_.UCS",
  567.                 RTSTR, /*MSG0*/"_X", RTREAL, 90.0, RTNONE);
  568.  
  569.     ads_getvar(/*MSG0*/"CECOLOR", &ocolour);
  570.     /* AutoCAD  currently returns  "human readable" colour strings
  571.        like "1 (red)" for the standard colours.  Trim  the  string
  572.        at  the  first space to guarantee we have a valid string to
  573.        restore the colour later.  */
  574.     if (strchr(ocolour.resval.rstring, ' ') != NULL)
  575.         *strchr(ocolour.resval.rstring, ' ') = EOS;
  576.  
  577.     ads_command(RTSTR, /*MSG0*/"_.COLOUR", RTSTR, /*MSG96*/"_BYBLOCK", RTNONE);
  578.     rb1.resval.rint = 0;              /* From world */
  579.     rb2.resval.rint = 1;              /* To new UCS */
  580.     ads_trans(centre, &rb1, &rb2, False, centre);
  581.     ax[X] = ax1[X] = centre[X];
  582.     ax[Y] = centre[Y] + radius;
  583.     ax[Z] = ax1[Z] = centre[Z];
  584.     ax1[Y] = centre[Y] - radius;
  585.     ads_command(RTSTR, /*MSG0*/"_.LINE", RT3DPOINT, ax, RT3DPOINT, ax1,
  586.                 RTSTR, "", RTNONE);
  587.     ads_entlast(e1);
  588.     ads_command(RTSTR, /*MSG0*/"_.Arc", RT3DPOINT, ax, RTSTR, /*MSG0*/"_E",
  589.                 RT3DPOINT, ax1, RTSTR, /*MSG0*/"_A", RTSTR, "<<180.0",
  590.                 RTNONE);
  591.     ads_entlast(e2);
  592.     PushVar(/*MSG0*/"SURFTAB1", stab1, SphereLong, rint);
  593.     PushVar(/*MSG0*/"SURFTAB2", stab2, SphereLat, rint);
  594.     ads_command(RTSTR, /*MSG0*/"_.REVSURF", RTLB, RTENAME, e2, RT3DPOINT, ax,
  595.                 RTLE, RTLB, RTENAME, e1, RT3DPOINT, centre, RTLE, RTSTR, "",
  596.                 RTSTR, "", RTNONE);
  597.     PopVar(/*MSG0*/"SURFTAB2", stab2);
  598.     PopVar(/*MSG0*/"SURFTAB1", stab1);
  599.     ads_command(RTSTR, /*MSG0*/"_.COLOUR",
  600.                 RTSTR, ocolour.resval.rstring, RTNONE);
  601.     free(ocolour.resval.rstring);
  602.     ads_entdel(e1);
  603.     ads_entdel(e2);
  604.     ads_entlast(e2);
  605.  
  606.     /* Create attributes */
  607.  
  608.     Spoint(atx, 4, 2.75, 0);
  609.     ads_ssadd(e2, NULL, matbss);
  610.  
  611.     PushVar(/*MSG0*/"AFLAGS", saflags, 1, rint);  /* Invisible */
  612.     ads_command(RTSTR, /*MSG0*/"_.ATTDEF", RTSTR, "",
  613.                 RTSTR, /*MSG34*/"PARTNAME",
  614.                 RTSTR, /*MSG35*/"▓╔ñlªW║┘", RTSTR, "", RT3DPOINT, atx,
  615.                 RTREAL, 0.2, RTREAL, 0.0, RTNONE);
  616.     ads_entlast(ename);
  617.     ads_ssadd(ename, matbss, matbss);
  618.  
  619.     for (i = 0; i < ELEMENTS(partatt); i++) {
  620.         atx[Y] -= 0.25;
  621.         ads_command(RTSTR, /*MSG0*/"_.ATTDEF",
  622.                     RTSTR, "", RTSTR, partatt[i].attname,
  623.                     RTSTR, partatt[i].attprompt, RTSTR, "0", RT3DPOINT, atx,
  624.                     RTREAL, 0.2, RTREAL, 0.0, RTNONE);
  625.         ads_entlast(ename);
  626.         ads_ssadd(ename, matbss, matbss);
  627.     }
  628.     PopVar(/*MSG0*/"AFLAGS", saflags);
  629.  
  630.     /* Collect sphere and attributes into a block. */
  631.  
  632.     ads_command(RTSTR, /*MSG0*/"_.BLOCK",
  633.                 RTSTR, ParticleBlock, RT3DPOINT, centre,
  634.                 RTPICKS, matbss, RTSTR, "", RTNONE);
  635.     ads_command(RTSTR, /*MSG0*/"_.UCS", RTSTR, /*MSG0*/"_Prev", RTNONE);
  636.     CommandE();
  637.     ads_ssfree(matbss);
  638. }
  639.  
  640. /*  ENTITEM  --  Search an entity buffer chain and return an item
  641.                  with the specified group code.  */
  642.  
  643. static struct resbuf *entitem(rchain, gcode)
  644.   struct resbuf *rchain;
  645.   int gcode;
  646. {
  647.     while ((rchain != NULL) && (rchain->restype != gcode))
  648.         rchain = rchain->rbnext;
  649.  
  650.     return rchain;
  651. }
  652.  
  653. /*  ENTINFO  --  Obtain information about a particle entity:
  654.  
  655.                        Handle
  656.                        Position
  657.                        Size (from scale factor of unit block)
  658.                        Colour
  659. */
  660.  
  661. static void entinfo(ename, h, p, r, c)
  662.   ads_name ename;
  663.   char *h;
  664.   ads_point p;
  665.   ads_real *r;
  666.   int *c;
  667. {
  668.     struct resbuf *rent, *rh;
  669.  
  670.     rent = ads_entget(ename);
  671.     if ((rh = entitem(rent, 5)) != NULL)
  672.         strcpy(h, rh->resval.rstring);
  673.     else
  674.         h[0] = EOS;
  675.     rh = entitem(rent, 10);
  676.     assert(rh != NULL);
  677.     Cpoint(p, rh->resval.rpoint);
  678.     rh = entitem(rent, 41);
  679.     assert(rh != NULL);
  680.     *r = rh->resval.rreal;
  681.     if ((rh = entitem(rent, 62)) != NULL) {
  682.         *c = rh->resval.rint;
  683.         if (*c == 0)                  /* Naked colour by block? */
  684.             *c = 7;                   /* Forbidden: make it white.  Q.C.D. */
  685.     } else {
  686.         /* This entity derives its colour from the layer.  Get
  687.            the colour from the layer table. */
  688.         *c = 7;
  689.         if ((rh = entitem(rent, 8)) != NULL) {
  690.             if ((rh = ads_tblsearch(/*MSG0*/"LAYER", rh->resval.rstring,
  691.                                     False)) != NULL) {
  692.                 struct resbuf *lh = entitem(rh, 62);
  693.                 if (lh != NULL) {
  694.                     int lc = abs(lh->resval.rint);
  695.                     if (lc > 0 && lc < 256) {
  696.                         *c = lc;
  697.                     }
  698.                 }
  699.                 ads_relrb(rh);
  700.             }
  701.         }
  702.     }
  703.     ads_relrb(rent);
  704. }
  705.  
  706. /*  PARTEXT  --  Extract particles present in drawing and build the
  707.                  in-memory particle table.  */
  708.  
  709. static void partext()
  710. {
  711.     long i, l;
  712.     struct resbuf rbet, rbb;
  713.     struct resbuf *rb, *rent, *vb;
  714.     ads_name ename, vname;
  715.  
  716.     if (ptab != NULL) {
  717.         free(ptab);
  718.     }
  719.     ptab = NULL;
  720.     nptab = 0;
  721.  
  722.     /* Build the SSGET entity buffer chain to filter for block
  723.        insertions of the named block on the named layer. */
  724.  
  725.     rbet.restype = 0;                 /* Entity type */
  726.     rbet.resval.rstring = /*MSG0*/"INSERT";
  727.     rbet.rbnext = &rbb;
  728.     rbb.restype = 2;                  /* Block name */
  729.     rbb.resval.rstring = ParticleBlock;
  730.     rbb.rbnext = NULL;
  731.  
  732.     if (ads_ssget(/*MSG0*/"X", NULL, NULL, &rbet, vname) != RTNORM) {
  733.         return;                       /* No definitions in database */
  734.     }
  735.  
  736.     /* We found one or more definitions.  Process  the  attributes
  737.        that  follow  each, plugging the values into material items
  738.        which get attached to the in-memory definition chain.  */
  739.  
  740.     if (ads_sslength(vname, &l) < 0)
  741.         l = 0;
  742.  
  743.     nptab = l;                        /* Save particle count */
  744.     ptab = (particle *) alloc(nptab * sizeof(particle));
  745.     for (i = 0; i < l; i++) {
  746.         ads_name pname;
  747.  
  748.         ads_ssname(vname, i, ename);
  749.         memcpy(pname, ename, sizeof ename);
  750.  
  751.         memset(&pt, 0, sizeof pt);
  752.         while (True) {
  753.             ads_entnext(ename, ename);
  754.             rent = ads_entget(ename);
  755.             if (rent == NULL) {
  756.                 ads_printf(/*MSG36*/"PARTEXT: ╡L¬k┼¬¿·íu─▌⌐╩ívíC\n");
  757.                 break;
  758.             }
  759.             rb = entitem(rent, 0);    /* Entity type */
  760.             if (rb != NULL) {
  761.                 if (strcmp(rb->resval.rstring, /*MSG0*/"ATTRIB") != 0)
  762.                     break;
  763.                 rb = entitem(rent, 2);  /* Attribute tag */
  764.                 vb = entitem(rent, 1);  /* Attribute value */
  765.                 if (rb != NULL && vb != NULL) {
  766.                     if (strcmp(rb->resval.rstring, /*MSG37*/"PARTNAME") == 0) {
  767.                         strcpy(pt.partname, vb->resval.rstring);
  768.                     } else {
  769.                         int j;
  770.  
  771.                         for (j = 0; j < ELEMENTS(partatt); j++) {
  772.                             if (strcmp(rb->resval.rstring,
  773.                                        partatt[j].attname) == 0) {
  774.                                 *partatt[j].attvar = atof(vb->resval.rstring);
  775.                                 break;
  776.                             }
  777.                         }
  778.                     }
  779.                 }
  780.             }
  781.             ads_relrb(rent);
  782.         }
  783.         entinfo(pname, pt.partblock, pt.position, &pt.radius, &pt.colour);
  784.         memcpy(&(ptab[(int) i]), &pt, sizeof(particle));
  785.     }
  786.     ads_ssfree(vname);                /* Release selection set */
  787. }
  788.  
  789. /*  ADDVEC  --  Add two vectors, a = b + c  */
  790.  
  791. static void addvec(ap, bp, cp)
  792.   ads_real *ap, *bp, *cp;
  793. {
  794.     ap[X] = bp[X] + cp[X];
  795.     ap[Y] = bp[Y] + cp[Y];
  796.     ap[Z] = bp[Z] + cp[Z];
  797. }
  798.  
  799. /*  SUBVEC  --  Subtract two vectors, a = b - c  */
  800.  
  801. static void subvec(ap, bp, cp)
  802.   ads_real *ap, *bp, *cp;
  803. {
  804.     ap[X] = bp[X] - cp[X];
  805.     ap[Y] = bp[Y] - cp[Y];
  806.     ap[Z] = bp[Z] - cp[Z];
  807. }
  808.  
  809. /*  SQABSV  --  Square of absolute value of a vector.  */
  810.  
  811. static ads_real sqabsv(ap)
  812.   ads_real *ap;
  813. {
  814.     return (ap[X] * ap[X] + ap[Y] * ap[Y] + ap[Z] * ap[Z]);
  815. }
  816.  
  817. /*  SUMVEC  --  Add a linear multiple to another vector, a = b + t * c.  */
  818.  
  819. static void sumvec(ap, bp, t, cp)
  820.   ads_real *ap, *bp, *cp;
  821.   ads_real t;
  822. {
  823.     ap[X] = bp[X] + t * cp[X];
  824.     ap[Y] = bp[Y] + t * cp[Y];
  825.     ap[Z] = bp[Z] + t * cp[Z];
  826. }
  827.  
  828. /*  VARBLOCKDEF  --  Create the block that carries the attributes that
  829.                      define the modal variables. */
  830.  
  831. static void varblockdef()
  832. {
  833.     int i;
  834.     ads_name varbss;
  835.     ads_name ename;
  836.     ads_point atx;
  837.  
  838.     atx[X] = 4.0;
  839.     atx[Y] = 4.0;
  840.     atx[Z] = 0.0;
  841.     ads_ssadd(NULL, NULL, varbss);
  842.  
  843.     CommandB();
  844.     PushVar(/*MSG0*/"AFLAGS", saflags, 1, rint);  /* Invisible */
  845.  
  846.     for (i = 0; i < ELEMENTS(varatt); i++) {
  847.         atx[Y] -= 0.25;
  848.         ads_command(RTSTR, /*MSG0*/"_.ATTDEF",
  849.                     RTSTR, "", RTSTR, varatt[i].attname,
  850.                     RTSTR, varatt[i].attprompt, RTSTR, "0", RT3DPOINT, atx,
  851.                     RTREAL, 0.2, RTREAL, 0.0, RTNONE);
  852.         ads_entlast(ename);
  853.         ads_ssadd(ename, varbss, varbss);
  854.     }
  855.     PopVar(/*MSG0*/"AFLAGS", saflags);
  856.  
  857.     ads_command(RTSTR, /*MSG0*/"_.BLOCK", RTSTR, ModalBlock, RTSTR, "4,4",
  858.                 RTPICKS, varbss, RTSTR, "", RTNONE);
  859.     CommandE();
  860.     ads_ssfree(varbss);
  861. }
  862.  
  863. /*  SAVEMODES  --  Save application modes as attributes of the mode
  864.                    block.  */
  865.  
  866. static void savemodes()
  867. {
  868.     int i;
  869.     struct resbuf rbb;
  870.     ads_name ename, vname;
  871.     long l;
  872.  
  873.     /* Build the SSGET entity buffer chain to filter for block
  874.        insertions of the named block on the named layer. */
  875.  
  876.     rbb.restype = 2;                  /* Block name */
  877.     rbb.resval.rstring = ModalBlock;
  878.     rbb.rbnext = NULL;
  879.  
  880.     if (ads_ssget(/*MSG0*/"X", NULL, NULL, &rbb, vname) == RTNORM) {
  881.  
  882.         /* Delete all definitions found. */
  883.  
  884.         if (ads_sslength(vname, &l) < 0)
  885.             l = 0;
  886.  
  887.         if (l > 0) {
  888.             CommandB();
  889.             ads_command(RTSTR, /*MSG0*/"_.ERASE",
  890.                         RTPICKS, vname, RTSTR, "", RTNONE);
  891.             CommandE();
  892.         }
  893.         ads_ssfree(vname);            /* Release selection set */
  894.     }
  895.  
  896.     /* Now insert the modal variable block, attaching the
  897.        mode variables to its attributes. */
  898.  
  899.     CommandB();
  900.     ads_command(RTSTR, /*MSG0*/"_.INSERT", RTSTR, ModalBlock,
  901.                 RTSTR, "0,0", RTSTR, "1", RTSTR, "1", RTSTR, "0",
  902.                 RTNONE);
  903.     for (i = 0; i < ELEMENTS(varatt); i++) {
  904.         char attval[20];
  905.  
  906. #define YorN(x) ((x) ? /*MSG38*/"Yes" : /*MSG39*/"No")
  907.         switch (varatt[i].attvari) {
  908.         case 1:
  909.             strcpy(attval, YorN(drawonly));
  910.             break;
  911.         case 2:
  912.             strcpy(attval, YorN(showtime));
  913.             break;
  914.         case 3:
  915.             strcpy(attval, YorN(showstep));
  916.             break;
  917.         case 4:
  918.             sprintf(attval, "%.12g", stepsize);
  919.             break;
  920.         case 5:
  921.             sprintf(attval, "%.12g", stepmin);
  922.             break;
  923.         }
  924. #undef YorN
  925.         ads_command(RTSTR, attval, RTNONE);
  926.     }
  927.     ads_entlast(ename);
  928.     ads_command(RTSTR, /*MSG0*/"_.CHPROP", RTENAME, ename, RTSTR, "",
  929.                 RTSTR, /*MSG0*/"_layer", RTSTR, FrozenLayer, RTSTR, "",
  930.                 RTNONE);
  931.     CommandE();
  932. }
  933.  
  934. /*  BOOLVAR  --  Determine Boolean value from attribute string.  We
  935.                  recognise numbers (nonzero means True) and the
  936.                  strings "True", "False", "Yes", and "No",
  937.                  in either upper or lower case.  */
  938.  
  939. static Boolean boolvar(varname, s)
  940.   char *varname, *s;
  941. {
  942.     char ch = *s;
  943.  
  944.     if (islower(ch))
  945.         ch = toupper(ch);
  946.     if (isdigit(ch)) {
  947.         return (atoi(s) != 0) ? True : False;
  948.     }
  949.  
  950.     switch (ch) {
  951.     case /*MSG40*/'Y':
  952.     case /*MSG41*/'T':
  953.         return True;
  954.     case /*MSG42*/'N':
  955.     case /*MSG43*/'F':
  956.         return False;
  957.     default:
  958.         ads_printf(/*MSG44*/"ѵÑIíu%sív¬║Ѽ¬L¡╚╡L«─: %s íC\n", varname, s);
  959.         break;
  960.     }
  961.     return False;
  962. }
  963.  
  964. /*  VARSET  --  Set modal variables from the block attributes.  */
  965.  
  966. static void varset()
  967. {
  968.     struct resbuf rbb;
  969.     ads_name vname;
  970.     long l;
  971.  
  972.     /* Build the SSGET entity buffer chain to filter for block
  973.        insertions of the named block on the named layer. */
  974.  
  975.     rbb.restype = 2;                  /* Block name */
  976.     rbb.resval.rstring = ModalBlock;
  977.     rbb.rbnext = NULL;
  978.  
  979.     if (ads_ssget(/*MSG0*/"X", NULL, NULL, &rbb, vname) == RTNORM) {
  980.  
  981.         if (ads_sslength(vname, &l) < 0)
  982.             l = 0;
  983.  
  984.         if (l > 0) {
  985.             ads_name ename;
  986.  
  987.             if (ads_ssname(vname, 0L, ename) == RTNORM) {
  988.                 while (ads_entnext(ename, ename) == RTNORM) {
  989.                     int i;
  990.                     struct resbuf *rb = ads_entget(ename),
  991.                                   *et, *at, *av;
  992.  
  993.                     if (rb == NULL)
  994.                         break;
  995.                     et = entitem(rb, 0);
  996.                     assert(et != NULL);
  997.                     if (strcmp(et->resval.rstring, /*MSG0*/"ATTRIB") == 0) {
  998.                         at = entitem(rb, 2);  /* Attribute tag */
  999.                         av = entitem(rb, 1);  /* Attribute value */
  1000.                         if (at == NULL || av == NULL)
  1001.                             break;
  1002.                         for (i = 0; i < ELEMENTS(varatt); i++) {
  1003.                             if (strcmp(at->resval.rstring,
  1004.                                        varatt[i].attname) == 0) {
  1005.                                 switch (varatt[i].attvari) {
  1006.                                 case 1:
  1007.                                     drawonly = boolvar(at->resval.rstring,
  1008.                                                        av->resval.rstring);
  1009.                                     break;
  1010.                                 case 2:
  1011.                                     showtime = boolvar(at->resval.rstring,
  1012.                                                        av->resval.rstring);
  1013.                                     break;
  1014.                                 case 3:
  1015.                                     showstep = boolvar(at->resval.rstring,
  1016.                                                        av->resval.rstring);
  1017.                                     break;
  1018.                                 case 4:
  1019.                                     stepsize = 0.1;    /* Default if error */
  1020.                                     sscanf(av->resval.rstring, "%lf",
  1021.                                            &stepsize);
  1022.                                     break;
  1023.                                 case 5:
  1024.                                     stepmin = 0.00001; /* Default if error */
  1025.                                     sscanf(av->resval.rstring, "%lf",
  1026.                                            &stepmin);
  1027.                                     break;
  1028.                                 }
  1029.                             }
  1030.                         }
  1031.                     } else if (strcmp(et->resval.rstring,
  1032.                                       /*MSG0*/"SEQEND") == 0) {
  1033.                         ads_relrb(rb);
  1034.                         break;
  1035.                     }
  1036.                     ads_relrb(rb);
  1037.                 }
  1038.             }
  1039.         }
  1040.         ads_ssfree(vname);            /* Release selection set */
  1041.     }
  1042. }
  1043.  
  1044. /*  SETFRAME  --  Set reference frame index from handle of
  1045.                   reference frame entity in effect.  */
  1046.  
  1047. static void setframe()
  1048. {
  1049.     int i;
  1050.  
  1051.     framei = -1;
  1052.     for (i = 0; i < nptab; i++) {
  1053.         if (strcmp(ptab[i].partblock, fhandle) == 0) {
  1054.             framei = i;
  1055.             break;
  1056.         }
  1057.     }
  1058. }
  1059.  
  1060. /*  INITACAD  --  Initialise the required modes in the AutoCAD
  1061.                   drawing.  */
  1062.  
  1063. static Boolean initacad(reset)
  1064.   Boolean reset;
  1065. {
  1066.     static Boolean initdone, initok;
  1067.  
  1068.     if (reset) {
  1069.         initdone = False;
  1070.         initok = True;
  1071.     } else {
  1072.         if (!initdone) {
  1073.             struct resbuf rb;
  1074.             struct resbuf *ep;
  1075.  
  1076.             initok = False;
  1077.  
  1078.             /* Reset the program modes to standard values upon
  1079.                entry to the drawing editor. */
  1080.  
  1081.             stepno = 0;               /* Step 0 */
  1082.             numsteps = 50;            /* Default number of steps */
  1083.             simtime = 0.0;            /* No elapsed time */
  1084.             stepsize = 0.1;           /* Default step size */
  1085.             stepmin = 0.00001;        /* Default minimum time: none */
  1086.             drawonly = True;          /* Draw using ads_grdraw() */
  1087.             showstep = True;          /* Show step number */
  1088.             showtime = True;          /* Show elapsed time */
  1089.             framei = -1;              /* No reference frame object */
  1090.             fhandle[0] = EOS;         /* Clear reference frame handle */
  1091.  
  1092.             /* Enable  handles  if  they aren't  already on.  We use
  1093.                handles to link  from  our  in-memory  table  to  the
  1094.                masses  in  the  AutoCAD  database,  and we also keep
  1095.                track of the reference frame entity  by  its  handle. */
  1096.  
  1097.             ads_getvar(/*MSG0*/"HANDLES", &rb);
  1098.             if (!rb.resval.rint) {
  1099.                 CommandB();
  1100.                 ads_command(RTSTR, /*MSG0*/"_.handles",
  1101.                             RTSTR, /*MSG0*/"_on", RTNONE);
  1102.                 CommandE();
  1103.                 ads_getvar(/*MSG0*/"HANDLES", &rb);
  1104.                 if (!rb.resval.rint) {
  1105.                     ads_printf(/*MSG45*/"╡L¬k▒╥Ñ╬íu╣╧╜XívíC\n");
  1106.                     initdone = True;
  1107.                     return (initok = False);
  1108.                 }
  1109.             }
  1110.  
  1111.             /* Create required drawing objects. */
  1112.  
  1113.             /* If there isn't already a "frozen solid" layer, create one. */
  1114.  
  1115.             if ((ep = ads_tblsearch(/*MSG0*/"LAYER",
  1116.                                     FrozenLayer, False)) == NULL) {
  1117.                 CommandB();
  1118.                 ads_command(RTSTR, /*MSG0*/"_.LAYER",
  1119.                             RTSTR, /*MSG0*/"_NEW", RTSTR, FrozenLayer,
  1120.                             RTSTR, /*MSG0*/"_FREEZE",
  1121.                             RTSTR, FrozenLayer, RTSTR, "", RTNONE);
  1122.                 ads_command(RTSTR, /*MSG0*/"_.LAYER",
  1123.                             RTSTR, /*MSG0*/"_NEW", RTSTR, OrbitLayer,
  1124.                             RTSTR, "", RTNONE);
  1125.                 CommandE();
  1126.                 defmassblk();         /* Define the particle block */
  1127.             } else {
  1128.                 ads_relrb(ep);
  1129.             }
  1130.  
  1131.             /* Create the block definition for our modal variables. */
  1132.  
  1133.             if ((ep = ads_tblsearch(/*MSG0*/"BLOCK",
  1134.                                     ModalBlock, False)) == NULL) {
  1135.                 varblockdef();
  1136.                 savemodes();
  1137.             } else {
  1138.                 ads_relrb(ep);
  1139.                 varset();             /* Load modals from block */
  1140.             }
  1141.  
  1142.             initdone = initok = True;
  1143.         }
  1144.     }
  1145.     return initok;
  1146. }
  1147.  
  1148. /*  ADDMASS  --  Insert mass block with attributes.  */
  1149.  
  1150. static void addmass(mname, pos, vel, mass)
  1151.   char *mname;
  1152.   ads_point pos, vel;
  1153.   ads_real mass;
  1154. {
  1155.     ads_real size;
  1156.     char velx[32], vely[32], velz[32], smas[32];
  1157.  
  1158.     size = cuberoot(mass) * DENSCON;  /* Set size by cube root of mass */
  1159.     CommandB();
  1160.     sprintf(velx, "%.12g", vel[X]);
  1161.     sprintf(vely, "%.12g", vel[Y]);
  1162.     sprintf(velz, "%.12g", vel[Z]);
  1163.     sprintf(smas, "%.12g", mass);
  1164.     ads_command(RTSTR, /*MSG0*/"_.INSERT", RTSTR, ParticleBlock, RTPOINT, pos,
  1165.                 RTREAL, size, RTREAL, size, RTREAL, 0.0,
  1166.                 RTSTR, mname,
  1167.                 RTSTR, velx, RTSTR, vely, RTSTR, velz,
  1168.                 RTSTR, smas, RTNONE);
  1169.     CommandE();
  1170. }
  1171.  
  1172. /*  MASS  --  Add mass to database.  */
  1173.  
  1174. static void mass()
  1175. {
  1176.     int k;
  1177.     ads_point pos, vel;
  1178.     ads_real mass;
  1179.     char pname[134];
  1180.  
  1181.     if (ads_getstring(True, /*MSG46*/"\n▓╔ñlªW║┘ (½÷ <Return> ╡▓º⌠): ",
  1182.                       pname) != RTNORM)
  1183.         return;
  1184.     ads_initget(1 + 8 + 16, NULL);
  1185.     if (ads_getpoint(NULL, /*MSG47*/"\nª∞╕m: ", pos) != RTNORM)
  1186.         return;
  1187.     ads_initget(1 + 8 + 16, /*MSG48*/"Vector");
  1188.     switch (ads_getpoint(NULL, /*MSG49*/"\n│t½╫ (⌐╬ V ªV╢q) í╨ AU/Year: ",
  1189.                          vel)) {
  1190.     case RTNORM:
  1191.         break;
  1192.     case RTKWORD:                     /* Vector keyword */
  1193.         if (ads_getpoint(pos, /*MSG50*/"\n│t½╫ªV╢q: ", vel) != RTNORM)
  1194.             return;
  1195.         for (k = X; k <= Z; k++)
  1196.             vel[k] -= pos[k];
  1197.         break;
  1198.     default:
  1199.         return;
  1200.     }
  1201.     if (ads_getreal(/*MSG51*/"\n╜Φ╢q: ", &mass) != RTNORM)
  1202.         return;
  1203.  
  1204.     addmass(pname, pos, vel, mass);
  1205. }
  1206.  
  1207. /*  DEMO  --  Load a canned demo world.  */
  1208.  
  1209. static void demo()
  1210. {
  1211.     int i, j;
  1212.  
  1213.     typedef struct {                  /* Demo mass table item */
  1214.        char *dmname;                  /* Mass name */
  1215.        ads_point dmpos;               /* Position */
  1216.        ads_point dmvel;               /* Velocity */
  1217.        ads_real dmmass;               /* Mass */
  1218.        int dmcolour;                  /* Colour */
  1219.     } dmass;
  1220.  
  1221.     typedef struct {
  1222.        char *dname;                   /* Name of demo case */
  1223.        ads_point dwind1,              /* Display window corners */
  1224.                  dwind2;
  1225.        ads_real dnstep;               /* Default number of steps */
  1226.        ads_real dssize;               /* Step size */
  1227.        ads_real dssmin;               /* Minimum step size */
  1228.        char *dframe;                  /* Reference frame */
  1229.        dmass *dmtab;                  /* Table of masses */
  1230.        int dmtabl;                    /* Number of masses in table */
  1231.     } demodesc;
  1232.  
  1233.     static dmass interlom[] = {
  1234.        {/*MSG52*/"┬⌠ñJ¬╠", {-167.5, 168, 0}, {2,-1.5,0}, 2, 2},
  1235.        {/*MSG53*/"¼P▓y 3", {100, 50, 0}, {-1, 0, 0}, 20, 3},
  1236.        {/*MSG54*/"¼P▓y 2", {0, 100, 0}, {2, 0, 0}, 10, 1},
  1237.        {/*MSG55*/"¼P▓y 1", {0, 0, 0}, {-3, 0, 0}, 10, 5}
  1238.       };
  1239.     static dmass solarsm[] = {
  1240.        {/*MSG56*/"ñ╙╢º", {0, 0, 0}, {0, 0, 0}, 1, 2},
  1241.        {/*MSG57*/"ñ⌠¼P", {0.387, 0, 0}, {0, 10.0965, 0}, 1.666667e-7, 7},
  1242.        {/*MSG58*/"¬≈¼P", {0.723, 0, 0}, {0, 7.38628, 0}, 2.44786e-6, 7},
  1243.        {/*MSG59*/"ªa▓y", {1, 0, 0}, {0, 6.21318531, 0}, 3.04044e-6, 5},
  1244.        {/*MSG60*/"ñδ▓y", {1,-0.00256952,0}, {0.215831,6.21318531,0},
  1245.         3.6944e-8, 7},
  1246.        {/*MSG61*/"ñ⌡¼P", {1.524, 0, 0}, {0, 5.0894, 0}, 3.22716e-7, 1},
  1247.        {/*MSG62*/"ñ∞¼P", {5.203, 0, 0}, {0, 2.75456, 0}, 0.000954782, 7},
  1248.        {/*MSG63*/"ñg¼P", {9.539, 0, 0}, {0, 2.03534, 0}, 0.00285837, 2},
  1249.        {/*MSG64*/"ñ╤ñ²¼P", {19.182, 0, 0}, {0, 1.43423, 0}, 4.38596e-5, 4},
  1250.        {/*MSG65*/"«ⁿñ²¼P", {30.058, 0, 0}, {0, 1.14527, 0}, 5.18135e-5, 4}
  1251.       };
  1252.     static dmass nemesis = {
  1253.        /*MSG66*/"¡▀ñ²¼P", {28.8879, 1.67301, 0}, {-65, 0, 0}, 8, 2
  1254.       };
  1255.     static demodesc dmt[] = {
  1256.        {/*MSG67*/"┬⌠ñJ¬╠", {-176.3, -74, 0}, {176, 180, 0},
  1257.         3000, 0.1, 0.00001, NULL,
  1258.         interlom, ELEMENTS(interlom)},
  1259.        {/*MSG68*/"ñ╙╢º¿t", {-3.35, -11.82, 0}, {31.9, 13.6, 0},
  1260.         -1, 100, 0, NULL,
  1261.         solarsm, ELEMENTS(solarsm)},
  1262.        {/*MSG69*/"¡▀ñ²¼P", {-1.59, -1.3, 0}, {2.67, 1.79, 0},
  1263.         -1, 100, 0, /*MSG70*/"ñ╙╢º",
  1264.         solarsm, ELEMENTS(solarsm)}
  1265.       };
  1266.  
  1267.     if (nptab > 0) {
  1268.         ads_printf(/*MSG71*/"\n\
  1269. ╢╚Ñi⌐≤íu¬┼╣╧ívññ½╪Ñ▀Ñ▄╜díC\n");
  1270.         return;
  1271.     }
  1272.  
  1273.     partext();
  1274.     ads_textscr();                    /* Flip to text screen */
  1275.     ads_printf(/*MSG72*/"«e│\134¬║▒í¬p:\n");
  1276.     for (i = 0; i < ELEMENTS(dmt); i++) {
  1277.         ads_printf("\n%d.  %s", i + 1, dmt[i].dname);
  1278.     }
  1279.     ads_printf("\n");
  1280.     ads_initget(2 + 4, NULL);
  1281.     switch (ads_getint(/*MSG73*/"\n¿║ñ@╢╡Ñ▄╜d? ", &i)) {
  1282.     case RTNORM:
  1283.         i--;
  1284.         if (i < ELEMENTS(dmt))
  1285.             break;
  1286.     default:
  1287.         return;
  1288.     }
  1289.  
  1290.     CommandB();
  1291.     ads_command(RTSTR, /*MSG0*/"_.ZOOM", RTSTR, /*MSG0*/"_Window",
  1292.                 RTPOINT, dmt[i].dwind1, RTPOINT, dmt[i].dwind2, RTNONE);
  1293.     CommandE();
  1294.     for (j = 0; j < dmt[i].dmtabl; j++) {
  1295.         CommandB();
  1296.         ads_command(RTSTR, /*MSG0*/"_.COLOUR",
  1297.                     RTSHORT, dmt[i].dmtab[j].dmcolour, RTNONE);
  1298.         CommandE();
  1299.         addmass(dmt[i].dmtab[j].dmname, dmt[i].dmtab[j].dmpos,
  1300.                 dmt[i].dmtab[j].dmvel, dmt[i].dmtab[j].dmmass);
  1301.     }
  1302.  
  1303.     /* Special case for Nemesis demo.  Load the standard Solar
  1304.        System definitions above, then jam the Nemesis mass
  1305.        on top of it.  Here comes trouble.... */
  1306.     if (i == 2) {
  1307.         CommandB();
  1308.         ads_command(RTSTR, /*MSG0*/"_.COLOUR",
  1309.                     RTSHORT, nemesis.dmcolour, RTNONE);
  1310.         CommandE();
  1311.         addmass(nemesis.dmname, nemesis.dmpos, nemesis.dmvel,
  1312.                 nemesis.dmmass);
  1313.     }
  1314.     /* We now return to our elegant program, already in progress. */
  1315.  
  1316.     numsteps = dmt[i].dnstep;
  1317.     stepsize = dmt[i].dssize;
  1318.     stepmin = dmt[i].dssmin;
  1319.     savemodes();
  1320.     CommandB();
  1321.     ads_command(RTSTR, /*MSG0*/"_.COLOUR", RTSTR, /*MSG127*/"_BYLAYER", RTNONE);
  1322.     CommandE();
  1323.     partext();
  1324.  
  1325.     /* If this demo requests a default reference frame, place it
  1326.        in effect. */
  1327.  
  1328.     if (dmt[i].dframe != NULL) {
  1329.         for (j = 0; j < nptab; j++) {
  1330.             if (strcmp(ptab[j].partname, dmt[i].dframe) == 0) {
  1331.                 strcpy(fhandle, ptab[j].partblock);
  1332.                 framei = j;
  1333.                 break;
  1334.             }
  1335.         }
  1336.         assert(j < nptab);
  1337.     }
  1338. }
  1339.  
  1340. /*  MASSEDIT  --  Edit properties of an existing mass.  */
  1341.  
  1342. static void massedit()
  1343. {
  1344.     int i = nptab + 1;
  1345.     ads_name ename;
  1346.     ads_point ppt;
  1347.  
  1348.     partext();                        /* Update in-memory mass table */
  1349.     if (ads_entsel(/*MSG74*/"┐∩╛▄╣w│╞╜s┐Φ¬║íu╜Φ╢qív: ", ename, ppt) == RTNORM) {
  1350.         struct resbuf *eb = ads_entget(ename), *ha;
  1351.  
  1352.         if ((ha = entitem(eb, 5)) != NULL) {
  1353.             for (i = 0; i < nptab; i++) {
  1354.                 if (strcmp(ptab[i].partblock, ha->resval.rstring) == 0) {
  1355.                     break;
  1356.                 }
  1357.             }
  1358.         }
  1359.     }
  1360.     if (i < nptab) {
  1361.         if (stepno > 0) {
  1362.             reset();
  1363.         }
  1364.         CommandB();
  1365.         ads_command(RTSTR, /*MSG0*/"_.DDATTE", RTENAME, ename, RTNONE);
  1366.         CommandE();
  1367.         partext();
  1368.     } else {
  1369.         ads_printf(/*MSG75*/"\nÑ╝┐∩¿∞íu╜Φ╢qívíC\n");
  1370.     }
  1371. }
  1372.  
  1373. /*  RESET  --  Reset simulation, erasing all orbital paths.  */
  1374.  
  1375. static void reset()
  1376. {
  1377.     struct resbuf rbb;
  1378.     ads_name vname;
  1379.     long l;
  1380.  
  1381.     /* Build the SSGET entity buffer chain to select all
  1382.        entities on the orbital path layer. */
  1383.  
  1384.     rbb.restype = 8;                  /* Layer name */
  1385.     rbb.resval.rstring = OrbitLayer;
  1386.     rbb.rbnext = NULL;
  1387.  
  1388.     if (ads_ssget(/*MSG0*/"X", NULL, NULL, &rbb, vname) == RTNORM) {
  1389.  
  1390.         /* We found one or more definitions.  Delete them. */
  1391.  
  1392.         if (ads_sslength(vname, &l) < 0)
  1393.             l = 0;
  1394.  
  1395.         if (l > 0) {
  1396.             CommandB();
  1397.             ads_command(RTSTR, /*MSG0*/"_.ERASE",
  1398.                         RTPICKS, vname, RTSTR, "", RTNONE);
  1399.             CommandE();
  1400.         }
  1401.         ads_ssfree(vname);            /* Release selection set */
  1402.     }
  1403.     if (drawonly) {
  1404.         ads_grtext(-3, NULL, 0);      /* Clear time display */
  1405.         ads_redraw(NULL, 0);
  1406.     }
  1407.     stepno = 0;                       /* Reset step number */
  1408. }
  1409.  
  1410. /*  FRAME  --  Set reference frame for motion display.  */
  1411.  
  1412. static void frame()
  1413. {
  1414.     ads_name ename;
  1415.     ads_point ppt;
  1416.  
  1417.     fhandle[0] = EOS;                 /* Assume inertial frame */
  1418.     if (ads_entsel(/*MSG76*/"\
  1419. ⌐╥░╤ª╥ñºframe¬║íu╜Φ╢qív⌐╬½÷ <Return> ¬φÑ▄▒─Ñ╬íu║D⌐╩ªíív: ",
  1420.                    ename, ppt) == RTNORM) {
  1421.         struct resbuf *eb = ads_entget(ename), *ha;
  1422.  
  1423.         if ((ha = entitem(eb, 5)) != NULL) {
  1424.             strcpy(fhandle, ha->resval.rstring);
  1425.         }
  1426.     }
  1427.     setframe();
  1428.     ads_printf(/*MSG77*/"\n░╤ª╥ frame: %s\n",
  1429.                framei < 0 ? /*MSG78*/"Inertial" :
  1430.                 ptab[framei].partname);
  1431. }
  1432.  
  1433. /*  RUN  --  Run simulation.  This can be called as an interactive
  1434.              command, RUN, or functionally as (C:RUN <steps/-time>).  */
  1435.  
  1436. static void run()
  1437. {
  1438.     int i, j, k, n = 0, lastcolour = -1;
  1439.     Boolean resume = (stepno > 0) ? True : False, timelimit;
  1440.     ads_real r, deltat, endtime = 0;
  1441.     char rpt[80];
  1442.     struct resbuf clayer, ocolour;
  1443.     struct resbuf *rb = ads_getargs();
  1444.  
  1445.     /* If an argument was supplied, set the simulation length
  1446.        from it rather than asking the user interactively. */
  1447.  
  1448.     if (rb != NULL) {
  1449.         Boolean argok = True;
  1450.  
  1451.         switch (rb->restype) {
  1452.         case RTREAL:
  1453.             numsteps = rb->resval.rreal;
  1454.             break;
  1455.         case RTSHORT:
  1456.             numsteps = rb->resval.rint;
  1457.             break;
  1458.         default:
  1459.             ads_fail(/*MSG79*/"RUN:íuñ▐╝╞½¼║AívñúÑ┐╜T");
  1460.             argok = False;
  1461.             break;
  1462.         }
  1463.         if (argok) {
  1464.             if (numsteps == 0) {
  1465.                 ads_fail(/*MSG80*/"RUN:íuñ▐╝╞ívñúÑi¼░ 0");
  1466.                 argok = False;
  1467.             } else {
  1468.                 if (rb->rbnext != NULL) {
  1469.                     ads_fail(/*MSG81*/"RUN:íuñ▐╝╞ív╣Lªh");
  1470.                     argok = False;
  1471.                 }
  1472.             }
  1473.         }
  1474.         if (!argok)
  1475.             return;
  1476.         functional = True;            /* Mark called as a function */
  1477.     } else {
  1478.  
  1479.         /* Ask user for length of simulation in years or number of
  1480.            integration steps to perform. */
  1481.  
  1482.         sprintf(rpt, /*MSG82*/"íu¿B╢iív⌐╬ (-╝╥└└ª~╝╞) <%.12g>: ",
  1483.                 numsteps);
  1484.         ads_initget(2, NULL);
  1485.         switch (ads_getreal(rpt, &r)) {
  1486.         case RTCAN:                   /* Control C */
  1487.             return;
  1488.         case RTNONE:                  /* Null input */
  1489.             break;
  1490.         case RTNORM:                  /* Number entered */
  1491.             numsteps = r;
  1492.             break;
  1493.         }
  1494.     }
  1495.  
  1496.     /* If we're starting a new simulation rather than resuming one
  1497.        already underway, reset  the  simulated  time  counter  and
  1498.        extract the current particle information from the database. */
  1499.  
  1500.     if (!resume) {
  1501.         simtime = 0.0;
  1502.         partext();
  1503.     }
  1504.  
  1505.     setframe();                       /* Activate reference frame */
  1506.     if (numsteps > 0) {
  1507.         timelimit = False;
  1508.         n = numsteps;
  1509.     } else {
  1510.         timelimit = True;
  1511.         endtime = simtime - numsteps;
  1512.     }
  1513.  
  1514.     /* Save original layer and colour */
  1515.  
  1516.     ads_getvar(/*MSG0*/"CLAYER", &clayer);
  1517.     ads_getvar(/*MSG0*/"CECOLOR", &ocolour);
  1518.     /* AutoCAD  currently returns  "human readable" colour strings
  1519.        like "1 (red)" for the standard colours.  Trim  the  string
  1520.        at  the  first space to guarantee we have a valid string to
  1521.        restore the colour later.  */
  1522.     if (strchr(ocolour.resval.rstring, ' ') != NULL)
  1523.         *strchr(ocolour.resval.rstring, ' ') = EOS;
  1524.  
  1525.     CommandB();
  1526.     ads_command(RTSTR, /*MSG0*/"_.UCS", RTSTR, /*MSG0*/"_World", RTNONE);
  1527.     if (!drawonly) {
  1528.         ads_command(RTSTR, /*MSG0*/"_.LAYER",
  1529.                     RTSTR, /*MSG0*/"_SET", RTSTR, OrbitLayer,
  1530.                     RTSTR, "", RTNONE);
  1531.     }
  1532.  
  1533.     /* Display frame of reference in mode line.  Do this  here  so
  1534.        it's  (a) outside the inner loop, and (b) after changing to
  1535.        the OrbitLayer (if !drawonly) wipes the mode line.  */
  1536.  
  1537.     sprintf(rpt, /*MSG84*/"░╤ª╥ frame: %s",
  1538.             framei < 0 ? /*MSG85*/"║D⌐╩ªí" :
  1539.              ptab[framei].partname);
  1540.     ads_grtext(-1, rpt, False);
  1541.  
  1542.     /* Initialise last plotted position for each particle. */
  1543.  
  1544.     for (i = 0; i < nptab; i++) {
  1545.         Cpoint(ptab[i].lastpos, ptab[i].position);
  1546.     }
  1547.  
  1548.     while ((nptab > 1) &&
  1549.            (timelimit ? (simtime < endtime) : (n-- > 0)) &&
  1550.            !ads_usrbrk()) {
  1551.         ads_real mindist = 1E100,
  1552.                  maxvel = -1;
  1553.  
  1554.         /* Update acceleration for all bodies. */
  1555.  
  1556.         for (i = 0; i < nptab; i++) {
  1557.             Spoint(ptab[i].acceleration, 0, 0, 0);
  1558.             for (j = 0; j < nptab; j++) {
  1559.                 if (i != j) {
  1560.                     ads_real vdist = ads_distance(ptab[i].position,
  1561.                                                   ptab[j].position),
  1562.                              /* F = G (m1 m2) / r^2 */
  1563.                              force = -GRAVCON *
  1564.                               ((ptab[i].mass * ptab[j].mass) /
  1565.                                (vdist * vdist)),
  1566.                              /* F = ma, hence a = F/m */
  1567.                              accel = force / ptab[i].mass;
  1568.  
  1569.                     mindist = min(mindist, vdist);
  1570.                     if (vdist == 0.0) {
  1571.                         ads_printf(/*MSG86*/"íu%sív╗Píu%sívñº╢í¬║íu╕I╝▓ív\n",
  1572.                                    ptab[i].partname, ptab[j].partname);
  1573.                     } else {
  1574.                         /* Update vector components of acceleration. */
  1575.                         for (k = X; k <= Z; k++) {
  1576.                             ptab[i].acceleration[k] +=
  1577.                                accel * (ptab[i].position[k] -
  1578.                                         ptab[j].position[k]) / vdist;
  1579.                         }
  1580.                     }
  1581.                 }
  1582.             }
  1583.         }
  1584.  
  1585.         /* Update velocity for all bodies. */
  1586.  
  1587.         for (i = 0; i < nptab; i++) {
  1588.             ads_point pacc;
  1589.             ads_real pvel;
  1590.  
  1591.             addvec(pacc, ptab[i].velocity, ptab[i].acceleration);
  1592.             pvel = sqabsv(pacc);
  1593.             maxvel = max(maxvel, pvel);
  1594.         }
  1595.         maxvel = sqrt(maxvel);
  1596.  
  1597.         deltat = stepsize * (mindist / maxvel);
  1598.         deltat = max(stepmin, deltat);
  1599.         for (i = 0; i < nptab; i++) {
  1600.             sumvec(ptab[i].velocity, ptab[i].velocity,
  1601.                    deltat, ptab[i].acceleration);
  1602.         }
  1603.  
  1604.         /* Update position for all bodies. */
  1605.  
  1606.         for (i = 0; i < nptab; i++) {
  1607.             sumvec(ptab[i].position, ptab[i].position,
  1608.                    deltat, ptab[i].velocity);
  1609.         }
  1610.  
  1611.         /* If we're viewing from one particle's frame of reference,
  1612.            translate the view to adjust  for  our  home  particle's
  1613.            most recent motion.  */
  1614.  
  1615.         if (framei >= 0) {
  1616.             ads_point delta;
  1617.  
  1618.             subvec(delta, ptab[framei].lastpos, ptab[framei].position);
  1619.             for (i = 0; i < nptab; i++) {
  1620.                 addvec(ptab[i].position, ptab[i].position, delta);
  1621.             }
  1622.         }
  1623.  
  1624.         /* Display motion since last update. */
  1625.  
  1626.         for (i = 0; i < nptab; i++) {
  1627.             if (drawonly) {
  1628.                 ads_grdraw(ptab[i].lastpos, ptab[i].position,
  1629.                            ptab[i].colour, False);
  1630.             } else {
  1631.                 if (lastcolour != ptab[i].colour) {
  1632.                     ads_command(RTSTR, /*MSG0*/"_.COLOUR",
  1633.                                 RTSHORT, ptab[i].colour,
  1634.                                 RTNONE);
  1635.                     lastcolour = ptab[i].colour;
  1636.                 }
  1637.                 ads_command(RTSTR, /*MSG0*/"_.LINE", RTPOINT, ptab[i].lastpos,
  1638.                             RTPOINT, ptab[i].position, RTSTR, "", RTNONE);
  1639.             }
  1640.             Cpoint(ptab[i].lastpos, ptab[i].position);
  1641.         }
  1642.  
  1643.         /* Update the step number, simulated time, and display
  1644.            the values on the status line if selected. */
  1645.  
  1646.         stepno++;
  1647.         simtime += deltat;
  1648.         rpt[0] = EOS;
  1649.         if (showtime) {
  1650.             sprintf(rpt, /*MSG87*/"ª~ %.2f", simtime);
  1651.         }
  1652.         if (showstep) {
  1653.             if (rpt[0] != EOS)
  1654.                 strcat(rpt, "  ");
  1655.             sprintf(rpt + strlen(rpt), /*MSG88*/"¿B╢i %ld", stepno);
  1656.         }
  1657.         if (rpt[0] != EOS)
  1658.             ads_grtext(-2, rpt, False);
  1659.     }
  1660.  
  1661.     /* Restore original layer, colour, and UCS. */
  1662.  
  1663.     if (!drawonly) {
  1664.         ads_command(RTSTR, /*MSG0*/"_.LAYER", RTSTR, /*MSG0*/"_SET",
  1665.                     RTSTR, clayer.resval.rstring, RTSTR, "", RTNONE);
  1666.         ads_command(RTSTR, /*MSG0*/"_.COLOUR",
  1667.                     RTSTR, ocolour.resval.rstring, RTNONE);
  1668.     }
  1669.     free(clayer.resval.rstring);
  1670.     free(ocolour.resval.rstring);
  1671.     ads_command(RTSTR, /*MSG0*/"_.UCS", RTSTR, /*MSG0*/"_Prev", RTNONE);
  1672.     CommandE();
  1673.  
  1674.     /* If called as a function, return the simulation end time.
  1675.        If called as a command, print the end time and step count. */
  1676.  
  1677.     if (functional)
  1678.         ads_retreal(simtime);
  1679.     else
  1680.         ads_printf(/*MSG89*/"\n\
  1681. ╡▓º⌠⌐≤íu¿B╢i %ldív,íu%.2f ª~ívíC\n", stepno, simtime);
  1682. }
  1683.  
  1684. /*  SETGRAV  --  Set modal variables.  */
  1685.  
  1686. static void setgrav()
  1687. {
  1688.     struct resbuf rbb;
  1689.     ads_name vname;
  1690.     long l;
  1691.  
  1692.     /* Build the SSGET entity buffer chain to filter for block
  1693.        insertions of the named block on the named layer. */
  1694.  
  1695.     rbb.restype = 2;                  /* Block name */
  1696.     rbb.resval.rstring = ModalBlock;
  1697.     rbb.rbnext = NULL;
  1698.  
  1699.     if (ads_ssget(/*MSG0*/"X", NULL, NULL, &rbb, vname) == RTNORM) {
  1700.  
  1701.         /* Delete all definitions found. */
  1702.  
  1703.         if (ads_sslength(vname, &l) < 0)
  1704.             l = 0;
  1705.  
  1706.         if (l > 0) {
  1707.             ads_name ename;
  1708.  
  1709.             if (ads_ssname(vname, 0L, ename) == RTNORM) {
  1710.                 CommandB();
  1711.                 ads_command(RTSTR, /*MSG0*/"_.DDATTE", RTENAME, ename, RTNONE);
  1712.                 CommandE();
  1713.                 varset();
  1714.             }
  1715.         }
  1716.         ads_ssfree(vname);            /* Release selection set */
  1717.     }
  1718. }
  1719.  
  1720. /*  UPDATE  --  Update masses to position them at the end of the current
  1721.                 point in the simulation.  */
  1722.  
  1723. static void update()
  1724. {
  1725.     int i;
  1726.  
  1727.     reset();                          /* Reset the simulation */
  1728.     for (i = 0; i < nptab; i++) {
  1729.         ads_name ename;
  1730.  
  1731.         if (ads_handent(ptab[i].partblock, ename) == RTNORM) {
  1732.             struct resbuf *rent, *rb, *vb;
  1733.  
  1734.             memcpy(&pt, &(ptab[i]), sizeof(particle));
  1735.             rent = ads_entget(ename);
  1736.             if (rent != NULL && ((vb = entitem(rent, 10)) != NULL)) {
  1737.                 Cpoint(vb->resval.rpoint, ptab[i].position);
  1738.                 if (ads_entmod(rent) != RTNORM) {
  1739.                     ads_printf(/*MSG90*/"\
  1740. UPDATE: ╡L¬kº≤╖síuª∞╕mívíC\n");
  1741.                 }
  1742.             }
  1743.             ads_relrb(rent);
  1744.  
  1745.             /* Update attributes */
  1746.  
  1747.             while (True) {
  1748.                 ads_entnext(ename, ename);
  1749.                 rent = ads_entget(ename);
  1750.                 if (rent == NULL) {
  1751.                     ads_printf(/*MSG91*/"UPDATE: ╡L¬k┼¬¿·íu─▌⌐╩ívíC\n");
  1752.                     break;
  1753.                 }
  1754.                 rb = entitem(rent, 0);  /* Entity type */
  1755.                 if (rb != NULL) {
  1756.                     if (strcmp(rb->resval.rstring, /*MSG0*/"ATTRIB") != 0)
  1757.                         break;
  1758.                     rb = entitem(rent, 2);  /* Attribute tag */
  1759.                     vb = entitem(rent, 1);  /* Attribute value */
  1760.                     if (rb != NULL && vb != NULL) {
  1761.                         int j;
  1762.  
  1763.                         for (j = 0; j < ELEMENTS(partatt); j++) {
  1764.                             if (strcmp(rb->resval.rstring,
  1765.                                        partatt[j].attname) == 0) {
  1766.                                 /* All right, we've found an attribute in
  1767.                                    the  table.   Edit  the new value into
  1768.                                    it, update the entity,  and  continue. */
  1769.                                 char *sp = vb->resval.rstring;
  1770.                                 char ebuf[32];
  1771.  
  1772.                                 sprintf(ebuf, "%.12g", *partatt[j].attvar);
  1773.                                 vb->resval.rstring = ebuf;
  1774.                                 if (ads_entmod(rent) != RTNORM) {
  1775.                                     ads_printf(
  1776.                                        /*MSG92*/"\
  1777. UPDATE:  ╡L¬kº≤╖síu%sív─▌⌐╩íC\n",
  1778.                                        partatt[j].attname);
  1779.                                 }
  1780.                                 /* Restore string buffer */
  1781.                                 vb->resval.rstring = sp;
  1782.                                 break;
  1783.                             }
  1784.                         }
  1785.                     }
  1786.                 }
  1787.                 ads_relrb(rent);
  1788.             }
  1789.         } else {
  1790.             ads_printf(/*MSG93*/"\n╡L¬k¿·▒oíu%sívíC\n", ptab[i].partname);
  1791.         }
  1792.     }
  1793. }
  1794.  
  1795. #ifdef  HIGHC
  1796.  
  1797. /*  Earlier versions of High C put abort() in the same module with exit();
  1798.     ADS defines its own exit(), so we have to define our own abort(): */
  1799.  
  1800. static void abort(void)
  1801. {
  1802.     ads_abort("");
  1803. }
  1804.  
  1805. #endif  /* HIGHC */
  1806.