home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume18 / planet / part02 < prev    next >
Internet Message Format  |  1991-04-10  |  55KB

  1. From: allen@viewlogic.com (Dave Allen)
  2. Newsgroups: comp.sources.misc
  3. Subject: REPOST: v18i002:  planet - planet generation simulator, Part02/04
  4. Message-ID: <1991Apr9.041718.8033@sparky.IMD.Sterling.COM>
  5. Date: 9 Apr 91 04:17:18 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: f4e3061e d6b1e950 3b22b131 feaa3b0c
  8.  
  9. Submitted-by: Dave Allen <allen@viewlogic.com>
  10. Posting-number: Volume 18, Issue 2
  11. Archive-name: planet/part02
  12. Supersedes: tec: Volume 10, Issue 77-78
  13.  
  14. #! /bin/sh
  15. # into a shell via "sh file" or similar.  To overwrite existing files,
  16. # type "sh file -c".
  17. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  18. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  19. # Contents:  ./doc/params.clim ./src/fileio.c ./src/tec2.c ./src/tec3.c
  20. # Wrapped by kent@sparky on Mon Apr  8 22:39:14 1991
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. echo If this archive is complete, you will see the following message:
  23. echo '          "shar: End of archive 2 (of 4)."'
  24. if test -f './doc/params.clim' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'./doc/params.clim'\"
  26. else
  27.   echo shar: Extracting \"'./doc/params.clim'\" \(9407 characters\)
  28.   sed "s/^X//" >'./doc/params.clim' <<'END_OF_FILE'
  29. XIf you give clim a filename as a command line argument, it will read 
  30. Xparameters from that file.  If you hand it '-' as an argument, it will
  31. Xread them from stdin.  For example, you could type "clim - < foo", or
  32. X"clim foo".  The '-' option is handy for pipes.
  33. X
  34. XThe parameter file is optional; all of the parameters you can change
  35. Xhave defaults.  Thus, the parameter file need contain only the parameters
  36. Xyou want to change.  A parameter file looks like LISP; for example,
  37. Xto change the XSIZE parameter, the file would have the one line
  38. X
  39. X(XSIZE 40)
  40. X
  41. XParameters can also be vectors; for example
  42. X
  43. X(RAINCUT (40 60 110 160 180))
  44. X
  45. XPrinting and sizing parameters:
  46. X
  47. XXSIZE     - Horizontal size of arrays.  Default 60.
  48. XYSIZE     - Vertical size of arrays.  Default 30.
  49. XBSIZE     - Number of seasons in animation and computation.  Default 4.  Note
  50. X            that the storage arrays are statically sized with the constant
  51. X            MAXB (in const.h); to increase BSIZE, you must increase MAXB and
  52. X            recompile.
  53. XPRINTMODE - Default 0.  The value 4 produces PostScript output; any other
  54. X            value produces short summary output.
  55. XPRINTEMP  - Default 0.  Any nonzero value produces one text map for each
  56. X            season, where the entries represent scaled temperature by a single
  57. X            hex digit (0..F).  A key is also printed, containing the Farenheit
  58. X            temperature equivalents for each digit.
  59. XPRINTPR   - Default 0.  Any nonzero value produces one text map for each
  60. X            season, where the entries are 1 for a region of low pressure,
  61. X            2 for a region of high pressure, and 3 for the heat equator
  62. X            (a zone of low pressure).
  63. XPRINTWIND - Default 0.  Any nonzero value produces one text map for each
  64. X            season, where the entries are a single hex digit interpreted as
  65. X            a bitmap.  Northbound winds are indicated by bit 0, southbound
  66. X            by bit 1, eastbound by bit 2 and westbound by bit 3.  For example,
  67. X            5 indicates NE winds.
  68. XPRINTRAIN - Default 0.  Any nonzero value produces one text map for each
  69. X            season, where the entries range from 0..F.  Zero indicates no
  70. X            rainfall, and F indicates a lot.
  71. XPRINTCLIM - Default 0.  Any nonzero value produces one text map for each
  72. X            season, where each entry is in the range 0..A.  Here is a key:
  73. X            0  Ocean         7  Savannah
  74. X            3  Icebergs      8  Deciduous forest
  75. X            4  Tundra        9  Jungle
  76. X            5  Steppe        A  Swamp
  77. X            6  Desert
  78. X
  79. XSo to produce a text file showing the climate and rainfall, you could use:
  80. X
  81. X(PRINTCLIM 1) (PRINTRAIN 1)
  82. X
  83. X
  84. XThe rest of the parameters deal with the computational models.
  85. X
  86. XParameters affecting temperature
  87. X
  88. XTILT      - Default 23.0.  This is the tilt of the planet with respect to
  89. X            its plane of orbit, in degrees.  Smaller numbers produce less 
  90. X            seasonality; numbers above 45 violate some of the assumptions
  91. X            of the models used.
  92. XECCENT    - Default 0.0.  The eccentricity of the planet's orbit; this
  93. X            parameter affects seasonality as well.  Numbers above 0.5 are
  94. X            probably unrealistic.
  95. XECCPHASE  - Default 0.0.  This parameter describes the phase offset of the
  96. X            eccentricity with respect to the tilt, in radians.  You can
  97. X            produce climates with complicated seasonality by varying this.
  98. XLCONST    - Default 275.0.  The basic temperature for land squares, assuming
  99. X            no tilt, eccentricity, or nearby ocean.
  100. XLCOS      - Default 45.0.  The amount by which land temperatures should vary
  101. X            from north pole to equator.  Land temperature, ignoring ocean
  102. X            effects, varies from LCONST - LCOS/2 at the poles to LCONST +
  103. X            LCOS/2 at the equator.
  104. XLTILT     - Default 1.0.  The fraction of the tilt parameter that should be
  105. X            applied to temperature adjustment for land.  Typically, land
  106. X            temperatures vary more from season to season than the ocean
  107. X            temperatures do, so LTILT should be higher than OTILT.
  108. XLSMOOTH   - Default 0.6.  One equation governs the effect of land on ocean
  109. X            temperatures and vice versa.  The equation involves LSMOOTH,
  110. X            LDIV, OSMOOTH and ODIV.  Given the land and sea temperatures, and
  111. X            the number of land squares in a 11 x 5 box around the square,
  112. X            the final temperature is a weighted sum of the two temperatures.
  113. X            The weights are related to LSMOOTH and OSMOOTH, and the importance
  114. X            of nearby land is diminished by increasing LDIV or ODIV.
  115. XLDIV      - Default 180.0.  See above.
  116. XOCONST    - Default 275.0.  Same as LCONST, only for the ocean.
  117. XOCOS      - Default 30.0.   Same as LCOS, only for the ocean.
  118. XOTILT     - Default 0.2.    See LTILT.
  119. XOSMOOTH   - Default 0.2.    See LSMOOTH.
  120. XODIV      - Default 250.0.  See LSMOOTH.
  121. X
  122. XParameters affecting pressure
  123. X
  124. XOLTHRESH  - Default 1.  Ocean pressure zones essentially ignore land masses
  125. X            whose radius is equal to or less than this number, like islands.
  126. XOOTHRESH  - Default 5.  Ocean pressure zones must be at least this many 
  127. X            squares away from the nearest (non-ignored) land.
  128. XOLMIN     - Default 40.  If the unscaled temperature of an ocean square is
  129. X            greater than OLMIN and less than OLMAX, then that square is a
  130. X            low pressure zone.
  131. XOLMAX     - Default 65.  See above.
  132. XOHMIN     - Default 130.  If the unscaled temperature of an ocean square is
  133. X            greater than OHMIN and less than OHMAX, then that square is a
  134. X            high pressure zone.
  135. XOHMAX     - Default 180.  See above.
  136. XLOTHRESH  - Default 3.  Land pressure zones essentially ignore ocean bodies
  137. X            whose radius is less than or equal to this number, like lakes.
  138. XLLTHRESH  - Default 7.  Land pressure zones must be at least this many 
  139. X            squares away from the nearest (non-ignored) ocean.
  140. XLLMIN     - Default 220.  If the unscaled temperature of a land square is
  141. X            greater than LLMIN and less than LLMAX, then that square is a
  142. X            low pressure zone.
  143. XLLMAX     - Default 255.  See above.
  144. XLHMIN     - Default 0.  If the unscaled temperature of a land square is
  145. X            greater than LHMIN and less than LHMAX, then that square is a
  146. X            high pressure zone.
  147. XLHMAX     - Default 20.  See above.
  148. X
  149. XParameters affecting wind
  150. X
  151. XBARSEP    - Default 16.  Winds are determined from pressure; a smooth
  152. X            pressure map ranging from 0..255 is built by interpolating between
  153. X            highs and lows.  Wind lines are contour lines on this map, and
  154. X            BARSEP indicates the pressure difference between lines on the map.
  155. X
  156. XParameters affecting rainfall
  157. X
  158. XMAXFETCH  - Default 5.  Fetch is the term that describes how many squares a
  159. X            given wind line travels over water.  A high fetch indicates a
  160. X            moist wind.  This number is the maximum depth for the tree walking
  161. X            algorithm which finds fetch; the effect of wind in one square
  162. X            can travel at most this number of squares before stopping.
  163. XRAINCONST - Default 32.  This is the base amount of rainfall in each square.
  164. XLANDEL    - Default -10.  This is the amount by which rainfall is increased
  165. X            in every land or mountain square; that is, rainfall goes down.
  166. XMOUNTDEL  - Default 32.  For each unit of fetch which is stopped by a mountain,
  167. X            rainfall in the mountain square increases by this amount.
  168. XFETCHDEL  - Default 4.  The amount of rainfall in a square is increased by
  169. X            this number for each unit of fetch in the square.
  170. XHEQDEL    - Default 32.  The amount of rainfall in a square is increased by
  171. X            this amount if the square is on the heat equator.
  172. XNRHEQDEL  - Default 24.  The amount of rainfall in a square is increased by
  173. X            this amount if the square is next to a square on the heat equator.
  174. XFLANKDEL  - Default -24.  The amount of rainfall in a square is increased by
  175. X            this amount if the square is on the "flank" of a circular wind
  176. X            pattern.  This happens when the wind blows south.
  177. XNRFDEL    - Default -3.  The amount of rainfall in a square is increased by
  178. X            this amount for each adjacent square which is on a "flank".
  179. X
  180. XParameters affecting climate determination
  181. X
  182. XICEBERGK  - Default 263.  If an ocean square is below this temperature
  183. X            (measured in deg Kelvin) all year round, then the ocean square
  184. X            is icebergs.
  185. XTEMPCUT   - Default is the vector (0 40 90 120).  The climate array found
  186. X            in climate.c/climkey is 4 x 5; the first index is based on
  187. X            average annual temperature.  The temperature is relative, based
  188. X            on the range 0..255; this vector determines the cutoff points.
  189. X            For example, with the default vector, a scaled temperature of 20
  190. X            falls into the first "bin" and 121 falls into the fourth.
  191. XRAINCUT   - Default is the vector (40 60 110 160 180).  The second index of
  192. X            the climate array is based on average annual rainfall, scaled into
  193. X            the range 0..255.  This vector determines the cutoff points.  For
  194. X            example, rainfall of 35 falls into the first "bin".
  195. XMTDELTA   - Default 20.  This is the amount, in degrees Farenheit, by which
  196. X            temperature in the mountains is decreased before the climate 
  197. X            lookup is performed.
  198. END_OF_FILE
  199.   if test 9407 -ne `wc -c <'./doc/params.clim'`; then
  200.     echo shar: \"'./doc/params.clim'\" unpacked with wrong size!
  201.   fi
  202.   # end of './doc/params.clim'
  203. fi
  204. if test -f './src/fileio.c' -a "${1}" != "-c" ; then 
  205.   echo shar: Will not clobber existing file \"'./src/fileio.c'\"
  206. else
  207.   echo shar: Extracting \"'./src/fileio.c'\" \(12090 characters\)
  208.   sed "s/^X//" >'./src/fileio.c' <<'END_OF_FILE'
  209. X/* This program is Copyright (c) 1991 David Allen.  It may be freely
  210. X   distributed as long as you leave my name and copyright notice on it.
  211. X   I'd really like your comments and feedback; send e-mail to
  212. X   allen@viewlogic.com, or send us-mail to David Allen, 10 O'Moore
  213. X   Avenue, Maynard, MA 01754. */
  214. X
  215. X/* This file contains all of the file input and output routines; putmat()
  216. X   is the output routine.  The function getparam() is called to start the
  217. X   parameter file input; the routines to read in the parameters follow. */
  218. X
  219. X#include "const.h"
  220. X#include "clim.h"
  221. X#include <stdio.h>
  222. X
  223. X/* The input is handled by one function, gettoken(), which returns one
  224. X   of the following codes each time it is called. */
  225. X#define TOK_EOF 0
  226. X#define TOK_OPEN 1
  227. X#define TOK_CLOSE 2
  228. X#define TOK_WORD 3
  229. X
  230. X
  231. X/* These parameters are needed by every source file; they are defined
  232. X   in main.c and described in params.doc */
  233. X
  234. Xextern int XSIZE, YSIZE, BSIZE, ZCOAST, PRINTMODE;
  235. X
  236. X/* The file pointer is used to read the parameters from a file.  Linecount
  237. X   is incremented each time a newline is read from the file, so error
  238. X   messages can refer to a line number.  Getbuf is the buffer each word
  239. X   is read into.  Getfname is a private copy of the filename used, so it
  240. X   can be reread if needed.   The two lookup tables are used by putmat();
  241. X   scalelut is set up by init().  Change is set by any of the parameter
  242. X   input routines which change a parameter. */
  243. Xstatic FILE *fp;
  244. Xstatic int linecount = 1;
  245. Xstatic char getbuf [256];
  246. Xchar scalelut[256], shortlut[] = "0123456789ABCDEF";
  247. Xextern int change[];
  248. X
  249. X
  250. Xusermessage (s) char *s; { fprintf (stderr, "%s\n", s); }
  251. X
  252. X
  253. Xfileinit () { int i; for (i=0; i<256; i++) scalelut[i] = shortlut[i>>4]; }
  254. X
  255. X
  256. Xputmat (s, buf, mode, cra, lra)
  257. X   char *s; int buf, mode; unsigned char cra[MAXX][MAXY], lra[MAXX][MAXY]; {
  258. X   register int i, j, k;
  259. X
  260. X   if (mode == PRINTMODE_CLIM) { psprint (cra, lra, 1); return (0); }
  261. X   else if (PRINTMODE == PRINTMODE_GREY) {
  262. X      psprint (cra, lra, 0); return (0); }
  263. X
  264. X   if (buf > -1) printf ("(%s %d (\n", s, buf);
  265. X   else printf ("(%s (\n", s);
  266. X
  267. X   if (PRINTMODE == PRINTMODE_LONG)
  268. X      for (j=0, k=0; j<YSIZE; j++) for (i=0; i<XSIZE; i++) {
  269. X         printf ("%4d", cra[i][j]); if (++k == 15) { printf ("\n"); k = 0; } }
  270. X   else if (mode == PRINTMODE_SHORT) for (j=0; j<YSIZE; j++) {
  271. X      for (i=0; i<XSIZE; i++) printf ("%c", shortlut[cra[i][j]]);
  272. X      if (j < YSIZE-1) printf ("\n"); }
  273. X   else if (mode == PRINTMODE_SCALE) for (j=0; j<YSIZE; j++) {
  274. X      for (i=0; i<XSIZE; i++) printf ("%c", scalelut[cra[i][j]]);
  275. X      if (j < YSIZE-1) printf ("\n"); }
  276. X   printf ("))\n"); return (0); }
  277. X
  278. X
  279. Xgetparams (s) char *s; {
  280. X   /* This function is called either by init() or picktype(), in main.c.
  281. X   In either case, the argument is a string which is supposed to be either
  282. X   "-", in which case stdin is opened, or a filename.  If the file doesn't
  283. X   exist, the program exits via panic().  Once the file is open, the loop
  284. X   expects to find paren-delimited pairs of (name value).  Value could be
  285. X   a vector, like (name (val1 val2)).  After a name is found, params(),
  286. X   above, is called to recognize the parameter.  If there is a syntax
  287. X   error or an unrecognized parameter, the program exits via panic().
  288. X   Each computation source file has its own parameter function; if all of
  289. X   them fail the parameter name is unknown. */
  290. X
  291. X   int x;
  292. X
  293. X   if (!strcmp (s, "-")) fp = stdin;
  294. X   else if (!(fp = fopen (s, "r"))) panic ("Can't find input file");
  295. X   while ((x = gettoken (getbuf)) != TOK_EOF) {
  296. X      if (x != TOK_OPEN) geterr ("expected open paren");
  297. X      if (gettoken (getbuf) != TOK_WORD) geterr ("expected param name");
  298. X      if (!mainpar  (getbuf)) geterr ("unknown parameter name");
  299. X      if (gettoken  (getbuf) != TOK_CLOSE) geterr ("expected close paren"); }
  300. X   fclose (fp); }
  301. X
  302. X
  303. Xgettoken (s) char *s; {
  304. X   /* This is the only function which actually reads from the file.  It
  305. X   maintains a one-character private unget buffer.  It ignores initial
  306. X   whitespace, but counts newlines in order to maintain an accurate linecount.
  307. X   Open or close parentheses count as tokens, and so does any amount of text
  308. X   with no white space or parens.  The return code indicates whether an
  309. X   open or close paren, end of file, or a word was found.  If a word was
  310. X   found, it is copied into the string pointed to by s.  When a word is found,
  311. X   the character which terminated it (whitespace, EOF, or paren) is put into
  312. X   the unget buffer to be read the next time gettoken() is called. */
  313. X
  314. X   static char buf = 0; char c;
  315. X
  316. X   white: if (buf) { c = buf; buf = 0; } else c = getc (fp);
  317. X   switch (c) {
  318. X      case '\n': linecount++;
  319. X      case '\t':
  320. X      case ' ' : goto white;
  321. X      case EOF : return (TOK_EOF);
  322. X      case '(' : return (TOK_OPEN);
  323. X      case ')' : return (TOK_CLOSE); }
  324. X   text: if ((c==' ')||(c=='\t')||(c=='\n')||(c==')')||(c=='(')||(c==EOF)) {
  325. X      buf = c; *s = 0; return (TOK_WORD); }
  326. X   else { *s++ = c; c = getc (fp); goto text; } }
  327. X
  328. X
  329. Xgeterr (s) char *s; {
  330. X   /* Use panic() to print a nicely formatted message, containing the input
  331. X   file line number, describing why the program just spat up. */
  332. X   sprintf (getbuf, "Par error: %s on line %d", s, linecount);
  333. X   fclose (fp);
  334. X   panic (getbuf); }
  335. X
  336. X
  337. Xgetlng (x, chg) int *x, chg; { int n;
  338. X   /* Called after reading a name associated with a single int, this
  339. X   function just reads a int from the input file and uses atoi() to
  340. X   convert it to a int, stored at the address passed in.  Note that any
  341. X   text, including a string or a real, will be read in; no type checking
  342. X   is performed.  If the value is different from the previous value of the
  343. X   parameter, change[chg] is set to one. */
  344. X
  345. X   if (gettoken (getbuf) != TOK_WORD) geterr ("expected int");
  346. X   n = atoi (getbuf); if (n != *x) change[chg] = 1; *x = n; }
  347. X
  348. X
  349. Xgetdbl (x, chg) double *x; int chg; { double n;
  350. X   /* This function reads in a double format number, like getlng above.  If
  351. X   the value is different from the previous value, change[chg] is set to 1. */
  352. X
  353. X   if (gettoken (getbuf) != TOK_WORD) geterr ("expected double");
  354. X   n = atof (getbuf); if (n != *x) change[chg] = 1; *x = n; }
  355. X
  356. X
  357. Xgetmat (dim1, dim2, chg, m)
  358. X   int *dim1, *dim2, chg; unsigned char m[MAXX][MAXY]; {
  359. X   /* This function expects to find a vector of strings, where each string
  360. X   contains only 0-9, A-Z.  The characters are converted into the numbers
  361. X   0..36 and stored in the character array whose address is passed into the
  362. X   function.  If one of the strings is too long, or if there are too many
  363. X   strings, the function spits up.  Before exiting, the function sets the
  364. X   array limits passed in; dim1 is set to the length of the longest string,
  365. X   while dim2 is set to the number of strings.  If any of the characters,
  366. X   or either dimension, is changed, then change[chg] is set to 1. */
  367. X
  368. X   int x, i = 0, j = 0, imax = -1; char c;
  369. X
  370. X   if (gettoken (getbuf) != TOK_OPEN) geterr ("expected open paren");
  371. X   while ((x = gettoken (getbuf)) == TOK_WORD) {
  372. X      if (j == *dim2) geterr ("matrix too high");
  373. X      for (i=0; getbuf[i]; i++) {
  374. X         if ((c = getbuf[i] - '0') > 9) c = 10 + getbuf[i] - 'A';
  375. X         if (c != m[i][j]) change[chg] = 1; m[i][j] = c; }
  376. X      if (i > *dim1) geterr ("string too long");
  377. X      if (i > imax) imax = i; j++; }
  378. X   if (x != TOK_CLOSE) geterr ("expected close paren");
  379. X   if ((*dim1 != imax) || (*dim2 != j)) change[chg] = 1;
  380. X   *dim1 = imax; *dim2 = j; }
  381. X
  382. X
  383. Xgetlvec (dim, v, chg) int *dim, *v, chg; {
  384. X   /* This function expects to find a list of ints, starting with an open
  385. X   paren and ending with a close paren.  The parameter dim should contain the
  386. X   compiled-in array limit; if the input file contains more than this number
  387. X   of entries, the function spits up.  No type checking is done; atoi() is
  388. X   used to convert any text to an int.  On exit, the dimension is set to
  389. X   the right value.  If either the dimension, or any of the elements, have
  390. X   changed from the previous values, change[chg] is set to one. */
  391. X
  392. X   int x, i = 0, n;
  393. X
  394. X   if (gettoken (getbuf) != TOK_OPEN) geterr ("expected open paren");
  395. X   while ((x = gettoken (getbuf)) == TOK_WORD) {
  396. X      if (i == *dim) geterr ("vector too long");
  397. X      n = atoi (getbuf);
  398. X      if (n != v[i]) change[chg] = 1; v[i++] = n; }
  399. X   if (x != TOK_CLOSE) geterr ("expected close paren");
  400. X   if (i != *dim) change[chg] = 1; *dim = i; }
  401. X
  402. X
  403. Xgetdim (x, chg) int *x, chg; { int y;
  404. X   /* Called right after reading a name associated with an array bound,
  405. X   this function reads one int from the input file; if the value is
  406. X   greater than the default value assigned above, the user is trying
  407. X   to exceed a compiled-in limit.  That's an error. */
  408. X
  409. X   if (gettoken (getbuf) != TOK_WORD) geterr ("expected int");
  410. X   y = atoi (getbuf);
  411. X   if (y > *x) geterr ("dimension exceeds reserved space");
  412. X   *x = y; change[chg] = 1; }
  413. X
  414. X
  415. Xgetdvec (dim, v, chg) int *dim; double *v; int chg; {
  416. X   /* Called right after reading a name associated with a one-dimensional
  417. X   array of doubles, this function expects to find a list of doubles
  418. X   starting with an open paren and ended by a closing paren.  The parameter
  419. X   dim contains the compiled-in array limit; if the input file contains
  420. X   more than this number of entries, the function complains.  No type checking
  421. X   is done; atof() is used to convert any text to a double.  On exit, the
  422. X   dimension is set to the correct value. */
  423. X
  424. X   int x, i = 0;
  425. X
  426. X   if (gettoken (getbuf) != TOK_OPEN)
  427. X      geterr ("expected open paren");
  428. X   while ((x = gettoken (getbuf)) == TOK_WORD) {
  429. X      if (i == *dim) geterr ("vector is too long");
  430. X      v[i++] = atof (getbuf); }
  431. X   if (x != TOK_CLOSE) geterr ("expected close paren");
  432. X   *dim = i; change[chg] = 1; } 
  433. X
  434. X
  435. X#define D ((double) 648 / XSIZE)
  436. X#define XX(x) (72 + ((x) * D))
  437. X#define YY(y) (72 + ((y) * D))
  438. X
  439. Xstatic char *psdef[] = {
  440. X   "/b { setgray moveto d 0 rlineto 0 d rlineto d neg 0 rlineto fill",
  441. X   "/v { moveto d 0 rlineto stroke",
  442. X   "/h { moveto 0 d rlineto stroke", 
  443. X   "/x { moveto d d rlineto d neg 0 rmoveto d d neg rlineto stroke",
  444. X   "/p { moveto e 0 rmoveto 0 d rlineto e neg dup rmoveto d 0 rlineto stroke",
  445. X   0 };
  446. Xstatic double climlut[] = { 0.00, 0.00, 0.00, 0.95, 0.95, 0.85, 0.70, 0.55,
  447. X   0.40, 0.70, 0.70 };
  448. Xextern double greyscale ();
  449. X
  450. X
  451. Xpsprint (cra, lra, doclim)
  452. X   unsigned char cra[MAXX][MAXY], lra[MAXX][MAXY]; int doclim; {
  453. X   register int i, j, k; double z;
  454. X   static int firstpage = 1;
  455. X
  456. X   if (firstpage) {
  457. X      printf ("%%!PS-Adobe-1.0\n/d %4.2f def /e %4.2f def\n", D, D/2);
  458. X      firstpage = 0;
  459. X      for (i=0; psdef[i]; i++) printf ("%s } bind def\n", psdef[i]); }
  460. X   printf ("%6.2f %6.2f moveto\n",         XX(-0.25),      YY(-0.25));
  461. X   printf ("%6.2f %6.2f lineto\n",         XX(YSIZE+0.25), YY(-0.25));
  462. X   printf ("%6.2f %6.2f lineto\n",         XX(YSIZE+0.25), YY(XSIZE+0.25));
  463. X   printf ("%6.2f %6.2f lineto\n",         XX(-0.25),      YY(XSIZE+0.25));
  464. X   printf ("%6.2f %6.2f lineto\nstroke\n", XX(-0.25),      YY(-0.25));
  465. X
  466. X   if (doclim) {
  467. X      for (j=0; j<YSIZE;j++) for (i=0; i<XSIZE; i++) {
  468. X         z = climlut[cra[i][j]];
  469. X         if (z > 0.0) printf ("%6.2f %6.2f %5.4f b\n", XX(j), YY(i), z); }
  470. X      printf ("0 setgray\n");
  471. X      for (j=0; j<YSIZE;j++) for (i=0; i<XSIZE; i++) {
  472. X         if (cra[i][j] == C_JUNGLE)
  473. X            printf ("%6.2f %6.2f p\n", XX(j), YY(i));
  474. X         if (cra[i][j] == C_SWAMP)
  475. X            printf ("%6.2f %6.2f x\n", XX(j), YY(i)); } }
  476. X
  477. X   else for (j=0; j<YSIZE;j++) for (i=0; i<XSIZE; i++) {
  478. X      z = greyscale (cra[i][j]);
  479. X      if (z > 0.0) printf ("%6.2f %6.2f %5.4f b\n", XX(j), YY(i), z); }
  480. X
  481. X   printf ("0 setgray\n");
  482. X   if (lra) for (j=0; j<YSIZE;j++) for (i=0; i<XSIZE; i++) {
  483. X      if (lra[i][j] & LINE_0V) printf ("%6.2f %6.2f v\n", XX(j), YY(i));
  484. X      if (lra[i][j] & LINE_0H) printf ("%6.2f %6.2f h\n", XX(j), YY(i)); }
  485. X
  486. X   printf ("1 setgray\n");
  487. X   if (lra) for (j=0; j<YSIZE;j++) for (i=0; i<XSIZE; i++) {
  488. X      if (lra[i][j] & LINE_1V) printf ("%6.2f %6.2f v\n", XX(j), YY(i));
  489. X      if (lra[i][j] & LINE_1H) printf ("%6.2f %6.2f h\n", XX(j), YY(i)); }
  490. X
  491. X   printf ("\nshowpage\n"); }
  492. END_OF_FILE
  493.   if test 12090 -ne `wc -c <'./src/fileio.c'`; then
  494.     echo shar: \"'./src/fileio.c'\" unpacked with wrong size!
  495.   fi
  496.   # end of './src/fileio.c'
  497. fi
  498. if test -f './src/tec2.c' -a "${1}" != "-c" ; then 
  499.   echo shar: Will not clobber existing file \"'./src/tec2.c'\"
  500. else
  501.   echo shar: Extracting \"'./src/tec2.c'\" \(14644 characters\)
  502.   sed "s/^X//" >'./src/tec2.c' <<'END_OF_FILE'
  503. X/* This program is Copyright (c) 1991 David Allen.  It may be freely
  504. X   distributed as long as you leave my name and copyright notice on it.
  505. X   I'd really like your comments and feedback; send e-mail to
  506. X   allen@viewlogic.com, or send us-mail to David Allen, 10 O'Moore Ave,
  507. X   Maynard, MA 01754. */
  508. X
  509. X/* This file contains the function trysplit(), which is called from
  510. X   onestep() in tec1.c, and its subfunctions.  One of its subfunctions,
  511. X   segment(), is also called from init() in tec1.c.   Trysplit is the
  512. X   function in charge of splitting one plate into smaller plates. */
  513. X
  514. X
  515. X#include "const.h"
  516. X#include "tec.h"
  517. X
  518. X#define PI 3.14159
  519. X#define TWOPI 6.28318
  520. X#define TWOMILLIPI 0.00628318
  521. X
  522. X/* RIFTMARK is the temporary indicator placed in the arrays to indicate
  523. X   the squares a rift has just appeared.  The function stoprift() puts
  524. X   them in, and trysplit() takes them out before anybody can see them. */
  525. X#define RIFTMARK -1
  526. X
  527. X/* These are all defined in tec1.c */
  528. Xextern char m[2][MAXX][MAXY], r[MAXX][MAXY], kid[MAXFRAG];
  529. Xextern unsigned char t[2][MAXX][MAXY];
  530. Xextern int karea[MAXFRAG], tarea[MAXPLATE], ids[MAXPLATE], step;
  531. Xextern struct plate p [MAXPLATE];
  532. X
  533. Xtrysplit (src) int src; {
  534. X   /* Trysplit is called at most once per step in only 40% of the steps.
  535. X   It first draws a rift on one of the plates, then it segments the result
  536. X   into some number of new plates and some splinters.  If exactly two new
  537. X   non-splinter plates are found, new plate structures are allocated, new
  538. X   dx and dy values are computed, and the old plate is freed.  If anything
  539. X   goes wrong, the rift is erased from the array, returning the array to its
  540. X   previous state.  The functions newrift, segment and newplates do most
  541. X   of the work. */
  542. X
  543. X   register int i, j, a; int count, old, frag, reg;
  544. X
  545. X   if (newrift (src, &old)) if (segment (src, old, &frag, ®)) if (reg > 1) {
  546. X
  547. X         /* Set tarea[i] to areas of the final segmented regions */
  548. X         for (i=0; i<MAXPLATE; i++) tarea[i] = 0;
  549. X         for (i=1; i<=frag; i++) tarea[kid[i]] += karea[i];
  550. X
  551. X         /* Give up unless exactly two regions are large enough */
  552. X         for (i=1, count=0; i<=reg; i++) if (tarea[i] > MAXSPLINTER) count++; 
  553. X         if (count == 2) {
  554. X
  555. X            /* Compute new dx,dy; update m with the ids of the new plates */
  556. X            newplates (src, old);
  557. X            for (i=0, count=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++) {
  558. X               if (a = r[i][j]) m[src][i][j] = ids[kid[a]];
  559. X               if (m[src][i][j] == RIFTMARK) {
  560. X                  m[src][i][j] = 0; t[src][i][j] = 0; } }
  561. X            pfree (old); return (0); } }
  562. X
  563. X   /* If execution reaches here, the split operation failed; remove rift */
  564. X   if (old) for (i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++)
  565. X      if (m[src][i][j] == RIFTMARK) {
  566. X         m[src][i][j] = old; p[old].area++; }
  567. X   return (0); }
  568. X
  569. X
  570. Xnewrift (src, old) int src, *old; {
  571. X   /* This function randomly picks a center for a new rift, and draws in
  572. X   a curving line until the line hits either the coast or another plate.
  573. X   If another plate is hit, the rift is invalid and the function returns 0.
  574. X   To find a center, the function generates random x,y values until it
  575. X   finds one that is at least RIFTDIST squares from any ocean square.  If a
  576. X   center is found, a random angle is generated; the rift will pass through
  577. X   the center at that angle.  Next, halfrift() is called twice.  Each call
  578. X   generates the rift leaving the center in one direction.  If everything
  579. X   works out, the function returns the id of the plate the rift is on. */
  580. X
  581. X   int x, y, lx, rx, ty, by, i, j, tries = 0, which; double tt;
  582. X
  583. X   /* Generate a random x, y value */
  584. X   getctr: if (tries > MAXCTRTRY) { *old = 0; return (0); }
  585. X   x = rnd (XSIZE); y = rnd (YSIZE);
  586. X
  587. X   /* If the location is ocean, try again */
  588. X   if (!m[src][x][y]) { tries++; goto getctr; }
  589. X
  590. X   /* Set lx,rx,ty,by to the coordinate values of a box 2*RIFTDIST on a side */
  591. X   /* centered on the center.  Clip the values to make sure they are on the */
  592. X   /* array.  Loop through the box; if a point is ocean, try another center. */
  593. X   lx = (x < RIFTDIST) ? 0 : x - RIFTDIST;
  594. X   rx = (x > XSIZE - RIFTDIST - 1) ? XSIZE - 1 : x + RIFTDIST;
  595. X   ty = (y < RIFTDIST) ? 0 : y - RIFTDIST;
  596. X   by = (y > YSIZE - RIFTDIST - 1) ? YSIZE - 1 : y + RIFTDIST;
  597. X   for (i=lx; i<rx; i++) for (j=ty; j<by; j++)
  598. X      if (!m[src][i][j]) { tries++; goto getctr; }
  599. X
  600. X   /* Found a good center, on plate `which'.  Put a rift indicator in the */
  601. X   /* center.  Generate a random angle t, which is really an integer in the */
  602. X   /* range 0-499 multiplied by 2 PI / 1000.  Call halfrift once for each */
  603. X   /* half of the rift; t is the initial angle for the first call, and */
  604. X   /* t + PI is the initial angle for the second call.  If halfrift() */
  605. X   /* returns zero, abort and return 0; otherwise, return the plate id. */
  606. X   which = m[src][x][y]; m[src][x][y] = RIFTMARK;
  607. X   tt = rnd (500) * TWOMILLIPI;
  608. X   if (!halfrift (src, x, y, which, tt)) { *old = which; return (0); }
  609. X   tt += PI; if (tt > TWOPI) tt -= TWOPI;
  610. X   if (!halfrift (src, x, y, which, tt)) { *old = which; return (0); }
  611. X   *old = which; return (1); }
  612. X
  613. X
  614. Xhalfrift (src, cx, cy, which, tt) int src, cx, cy, which; double tt; {
  615. X   /* Draw a rift from cx,cy on plate `which' at angle t.  At the beginning,
  616. X   digitize the angle using Bresenham's algorithm; once in a while thereafter,
  617. X   modify the angle randomly and digitize it again.  For each square travelled,
  618. X   call stoprift() to see if the rift has left the plate. */
  619. X
  620. X   int ddx, ddy, rdx, rdy, draw, i, a; double dx, dy, adx, ady;
  621. X
  622. X   checkmouse ();
  623. X   /* For-loop against SIZE to guard against infinite loops */
  624. X   for (i=0; i<XSIZE; i++) {
  625. X
  626. X      /* If first square or 1/6 chance at each step, digitize */
  627. X      if (!i || !rnd (BENDEVERY)) {
  628. X
  629. X         /* If not first step, modify angle a little */
  630. X         if (i) tt = tt + (rnd (BENDBY<<1) * TWOMILLIPI) - (BENDBY*TWOMILLIPI);
  631. X         if (tt > TWOPI) tt -= TWOPI; if (tt < 0) tt += TWOPI;
  632. X
  633. X         /* Compute dx and dy, scaled so that larger is exactly +1.0 or -1.0 */
  634. X         dy = sin (tt); dx = cos (tt); adx = ABS(dx); ady = ABS(dy);
  635. X         if (adx > ady) { dy = dy / adx; dx = (dx < 0) ? -1.0: 1.0; }
  636. X         else { dx = dx / ady; dy = (dy < 0) ? -1.0: 1.0; }
  637. X
  638. X         /* Convert to integer value and initialize remainder */
  639. X         /* for each coordinate to half value */
  640. X         ddx = REALSCALE * dx; ddy = REALSCALE * dy;
  641. X         rdx = ddx >> 1; rdy = ddy >> 1; }
  642. X
  643. X      /* Main part of loop, draws one square along line.  The basic idea */
  644. X      /* of Bresenham's algorithm is that if the slope of the line is less */
  645. X      /* than 45 degrees, each time you step one square in X and maybe step */
  646. X      /* one square in Y.  If the slope is greater than 45, step one square */
  647. X      /* in Y and maybe one square in X.  Here, if the slope is less than 45 */
  648. X      /* then ddx == REALSCALE (or -REALSCALE) and the first call to */
  649. X      /* stoprift() is guaranteed.  If stoprift returns <0, all is ok; */
  650. X      /* if zero, the rift ran into the ocean, so stop now; if positive, the */
  651. X      /* rift ran into another plate, which is a perverse condition and the */
  652. X      /* rift must be abandoned.  */
  653. X      rdx += ddx; rdy += ddy;
  654. X      if (rdx >=  REALSCALE) { cx++; rdx -= REALSCALE; draw = 1; }
  655. X      if (rdx <= -REALSCALE) { cx--; rdx += REALSCALE; draw = 1; }
  656. X      if (draw == 1) {
  657. X         a = stoprift (src, cx, cy, which); if (a >= 0) return (!a); }
  658. X      if (rdy >=  REALSCALE) { cy++; rdy -= REALSCALE; draw = 2; }
  659. X      if (rdy <= -REALSCALE) { cy--; rdy += REALSCALE; draw = 2; }
  660. X      if (draw == 2) {
  661. X         a = stoprift (src, cx, cy, which); if (a >= 0) return (!a); } }
  662. X   return (1); }
  663. X
  664. X
  665. Xstoprift (src, x, y, which) int src, x, y, which; {
  666. X   /* This function is called once for each square the rift enters.  It
  667. X   puts a rift marker into m[src] and decides whether the rift can go on.
  668. X   It looks at all four adjacent squares.  If one of them contains ocean
  669. X   or another plate, return immediately so that the rift stops (if ocean)
  670. X   or aborts (if another plate).  If none of them do, then check to make
  671. X   sure the point is within underscan boundaries.  If so, return ok. */
  672. X
  673. X   register int w, a;
  674. X
  675. X   w = which; p[w].area--; m[src][x][y] = RIFTMARK;
  676. X   a = m[src][x][y+1]; if ((a != w) && (a!= RIFTMARK)) return (a != 0);
  677. X   a = m[src][x][y-1]; if ((a != w) && (a!= RIFTMARK)) return (a != 0);
  678. X   a = m[src][x+1][y]; if ((a != w) && (a!= RIFTMARK)) return (a != 0);
  679. X   a = m[src][x-1][y]; if ((a != w) && (a!= RIFTMARK)) return (a != 0);
  680. X   if ((x < UNDERSCAN) || (x > XSIZE - UNDERSCAN)) return (1);
  681. X   if ((y < UNDERSCAN) || (y > YSIZE - UNDERSCAN)) return (1);
  682. X   return (-1); }
  683. X
  684. X
  685. Xsegment (src, match, frag, reg) int src, match, *frag, *reg; {
  686. X   /* This routine implements a standard binary-blob segmentation.  It looks
  687. X   at the array m[src]; match is the value of the blob, and everything else
  688. X   is background.  The result is placed into array r and vectors kid and karea.
  689. X   One 8-connected region can be made up of many fragments; each fragment is
  690. X   assigned a unique index.  Array r contains the frag indices k, while kid[k]
  691. X   is the region frag k belongs to and karea[k] is the area of frag k.
  692. X   Variables frag and reg are set on output to the number of fragments and
  693. X   regions found during the segmentation.  The private vector kk provides one
  694. X   level of indirection for merging fragments; fragment k is merged with
  695. X   fragment kk[k] where kk[k] is the smallest frag index in the region. */
  696. X
  697. X   register int i, j, k, k1, k2, k3, l;
  698. X   char kk [MAXFRAG];
  699. X
  700. X   /* Initialize all frag areas to zero and every frag to merge with itself */
  701. X   for (k=0; k<MAXFRAG; k++) { kk[k] = k; karea[k] = 0; }
  702. X   checkmouse ();
  703. X
  704. X   /* Look at every point in the array */
  705. X   for (k=0, i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++) {
  706. X
  707. X      /* If too many fragments, give up */
  708. X      if (k == MAXFRAG) return (0);
  709. X
  710. X      /* If this square isn't part of the blob, try the next square */
  711. X      if (m[src][i][j] != match) { r[i][j] = 0; goto bottom; }
  712. X
  713. X      /* It is part of the blob.  Set k1 to the frag id of the square to */
  714. X      /* its left, and set k2 to the frag id of the square above it.  Note */
  715. X      /* that because of the for-loop direction, both of these squares have */
  716. X      /* already been processed. */
  717. X      k1 = i ? kk [r [i-1] [j]] : 0; k2 = j ? kk [r [i] [j-1]] : 0;
  718. X
  719. X      /* If k1 and k2 are both background, start a new fragment */
  720. X      if (!k1 && !k2) { r[i][j] = ++k; karea[k]++; goto bottom; }
  721. X
  722. X      /* If k1 and k2 are part of the same frag, add this square to it */
  723. X      if (k1 && (k1 == k2)) { r[i][j] = k1; karea[k1]++; goto bottom; }
  724. X
  725. X      /* If k1 and k2 belong to different frags, merge them by finding */
  726. X      /* all the frags merged with max(k1,k2) and merging them instead */
  727. X      /* with min(k1,k2).  Add k to that fragment as well. */
  728. X      if (k1 && k2) {
  729. X         if (k2 < k1) { k3 = k1; k1 = k2; k2 = k3; }
  730. X         for (l=1; l<=k; l++) if (kk[l] == k2) kk[l] = k1;
  731. X         r[i][j] = k1; karea[k1]++; goto bottom; }
  732. X
  733. X      /* Default case is that one of k1,k2 is a fragment and the other is */
  734. X      /* background.  Add k to the fragment. */
  735. X      k3 = (k1) ? k1 : k2; r[i][j] = k3; karea[k3]++;
  736. X   bottom: continue; }
  737. X
  738. X   /* Set up vector kid to map from fragments to regions by using i to count */
  739. X   /* unique groups of fragments.  A unique group of fragments is */
  740. X   /* characterized by kk[k] == k; otherwise, frag k is merged with some */
  741. X   /* other fragment. */
  742. X   for (i=0, j=1; j<=k; j++) {
  743. X      if (j == kk[j]) kid[j] = ++i;
  744. X      else kid[j] = kid [kk [j]]; }
  745. X
  746. X   /* Make sure the id of the background is zero; set up return values */
  747. X   kid[0] = 0; *frag = k; *reg = i; return (1); }
  748. X
  749. X
  750. X
  751. Xnewplates (src, old) int src, old; {
  752. X   /* Compute new dx and dy values for plates right after fragmentation.  This
  753. X   function looks at the rift markers in m[src]; variable old is the index of
  754. X   the plate from which the new plates were created.  For each plate adjacent
  755. X   to the rift, this function subtracts the number of plate squares to the left
  756. X   of the rift from the number to the right; this gives some indication of
  757. X   whether the plate should move left or right, and how fast.  The same is done
  758. X   for squares above and below the rift.  The results are put into dx[] and
  759. X   dy[].  At this point some unscaled movement vector is available for both of
  760. X   the new plates.  The vectors are then scaled by the relative sizes of the
  761. X   plates.  The idea is that if one plate is much larger than the other, the
  762. X   small one should move faster.  New plate structures are allocated for the
  763. X   new plates, and the computed dx and dy values are put in them. */
  764. X
  765. X   int dx[MAXPLATE], dy[MAXPLATE];
  766. X   register int i, j, a; int totarea=0, maxmag=0; double scale, b;
  767. X
  768. X   for (i=1; i<MAXPLATE; i++) { dx[i] = 0; dy[i] = 0; ids[i] = 0; }
  769. X   checkmouse ();
  770. X
  771. X   /* For every point in the array, set a to the region id (kid is the */
  772. X   /* lookup table and r contains frag indices); if a is nonzero and */
  773. X   /* the rift is adjacent, adjust counters appropriately */
  774. X   for (i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++) if (a = kid[r[i][j]]) {
  775. X      if ((i-1 > -1)    && (m[src][i-1][j] == RIFTMARK)) (dx[a])++;
  776. X      if ((i+1 < XSIZE) && (m[src][i+1][j] == RIFTMARK)) (dx[a])--;
  777. X      if ((j-1 > -1)    && (m[src][i][j-1] == RIFTMARK)) (dy[a])++;
  778. X      if ((j+1 < XSIZE) && (m[src][i][j+1] == RIFTMARK)) (dy[a])--; }
  779. X
  780. X   /* For those regions larger than splinters (tarea is set up in trysplit), */
  781. X   /* allocate a new plate structure and initialize its area; compute the */
  782. X   /* magnitude of the dx dy vector and remember the maximum magnitude; also */
  783. X   /* record the total area of new regions */
  784. X   for (i=1; i<MAXPLATE; i++) if (tarea[i] > MAXSPLINTER) {
  785. X      ids[i] = palloc (); p[ids[i]].area = tarea[i];
  786. X      totarea += tarea[i];
  787. X      a =sqrt ((double) ((dx[i]*dx[i]) + (dy[i]*dy[i])));
  788. X      if (a > maxmag) maxmag = a; }
  789. X
  790. X   /* Generate a random speed and predivide so that all speeds computed */
  791. X   /* below are less than the random speed. */
  792. X   scale = (double) (rnd (SPEEDRNG) + SPEEDBASE) / (maxmag * totarea);
  793. X
  794. X   /* Compute the dx and dy for each new plate; note that the speed the */
  795. X   /* plate was moving at before splitting is given by p[old].odx,ody */
  796. X   /* but those must be multiplied by MR to get the actual values */
  797. X   for (i=1; i<MAXPLATE; i++) if (ids[i]) {
  798. X      b = scale * (totarea - tarea[i]);
  799. X      p[ids[i]].odx = p[old].odx * MR [p[old].age] + dx[i] * b;
  800. X      p[ids[i]].ody = p[old].ody * MR [p[old].age] + dy[i] * b; } }
  801. END_OF_FILE
  802.   if test 14644 -ne `wc -c <'./src/tec2.c'`; then
  803.     echo shar: \"'./src/tec2.c'\" unpacked with wrong size!
  804.   fi
  805.   # end of './src/tec2.c'
  806. fi
  807. if test -f './src/tec3.c' -a "${1}" != "-c" ; then 
  808.   echo shar: Will not clobber existing file \"'./src/tec3.c'\"
  809. else
  810.   echo shar: Extracting \"'./src/tec3.c'\" \(14070 characters\)
  811.   sed "s/^X//" >'./src/tec3.c' <<'END_OF_FILE'
  812. X/* This program is Copyright (c) 1991 David Allen.  It may be freely
  813. X   distributed as long as you leave my name and copyright notice on it.
  814. X   I'd really like your comments and feedback; send e-mail to
  815. X   allen@viewlogic.com, or send us-mail to David Allen, 10 O'Moore Ave,
  816. X   Maynard, MA 01754. */
  817. X
  818. X#include "const.h"
  819. X#include "tec.h"
  820. X
  821. X/* These are all defined in tec1.c */
  822. Xextern char m[2][MAXX][MAXY], r[MAXX][MAXY], e[MAXX][MAXY], kid[MAXFRAG],
  823. X   mm[MAXPLATE][MAXPLATE];
  824. Xextern unsigned char t[2][MAXX][MAXY];
  825. Xextern int step, phead, tarea[MAXPLATE], karea[MAXFRAG];
  826. Xextern struct plate p [MAXPLATE];
  827. X
  828. X
  829. Xmakefrac (src, match) int src, match; {
  830. X   /* This function uses a very simple fractal technique to draw a blob.  Four
  831. X   one-dimensional fractals are created and stored in array x, then the
  832. X   fractals are superimposed on a square array, summed and thresholded to
  833. X   produce a binary blob.  Squares in the blob are set to the value in `match'.
  834. X   A one-dimensional fractal of length n is computed like this.  First,
  835. X   set x [n/2] to some height and set the endpoints (x[0] and x[n]) to 0.
  836. X   Then do log-n iterations.  The first iteration computes 2 more values:
  837. X   x[n/4] = average of x[0] and x[n/2], plus some random number, and
  838. X   x[3n/4] = average of x[n/2] and x[n], plus some random number.  The second
  839. X   iteration computes 4 more values (x[n/8], x[3n/8], ...) and so on.  The
  840. X   random number gets smaller by a factor of two each time also.
  841. X   Anyway, you wind up with a number sequence that looks like the cross-section
  842. X   of a mountain.  If you sum two fractals, one horizontal and one vertical,
  843. X   you get a 3-d mountain; but it looks too symmetric.  If you sum four,
  844. X   including the two 45 degree diagonals, you get much better results. */
  845. X   register int xy, dist, n, inc, i, j, k; char x[4][65];
  846. X   int xofs, yofs, xmin, ymin, xmax, ymax, bloblevel, hist[256];
  847. X   /* Compute offsets to put center of blob in center of the world, and
  848. X      compute array limits to clip blob to world size */
  849. X   xofs = (XSIZE - 64) >> 1; yofs = (YSIZE - 64) >> 1;
  850. X   if (xofs < 0) { xmin = -xofs; xmax = 64 + xofs; }
  851. X   else { xmin = 0; xmax = 64; }
  852. X   if (yofs < 0) { ymin = -yofs; ymax = 64 + yofs; }
  853. X   else { ymin = 0; ymax = 64; }
  854. X   for (xy=0; xy<4; xy++) {
  855. X      /* Initialize loop values and fractal endpoints */
  856. X      x [xy] [0] = 0; x [xy] [64] = 0; dist = 32;
  857. X      x [xy] [32] = 24 + rnd (16); n = 2; inc = 16;
  858. X      /* Loop log-n times, each time halving distance and doubling iterations */
  859. X      for (i=0; i<5; i++, dist>>=1, n<<=1, inc>>=1)
  860. X         for (j=0, k=0; j<n; j++, k+=dist)
  861. X            x[xy][k+inc] = ((x[xy][k]+x[xy][k+dist])>>1) + rnd (dist) - inc; }
  862. X   /* Superimpose fractals into the output array.  x[0] is horizontal, x[1] */
  863. X   /* vertical, x[2] diagonal from top left to bottom right, x[3] diagonal */
  864. X   /* from TR to BL.  While superimposing, create a histogram of the values.*/
  865. X   for (i=0; i<256; i++) hist[i] = 0;
  866. X   for (i=xmin; i<xmax; i++) for (j=ymin; j<ymax; j++) {
  867. X      k = x[0][i] + x[1][j] + x[2][(i - j + 64) >> 1] + x[3][(i + j) >> 1];
  868. X      if (k < 0) k = 0; if (k > 255) k = 255;
  869. X      hist[k]++; m[src][i+xofs][j+yofs] = k; }
  870. X
  871. X   /* Pick a threshhold to get as close to the goal number of squares as */
  872. X   /* possible, then go back through the array and adjust it */
  873. X   bloblevel = XSIZE * YSIZE * (100 - HYDROPCT) / 100;
  874. X   for (k=255, i=0; k>=0; k--) if ((i += hist[k]) > bloblevel) break;
  875. X   for (i=xmin; i<xmax; i++) for (j=ymin; j<ymax; j++)
  876. X      m[src][i+xofs][j+yofs] = (m[src][i+xofs][j+yofs] > k) ? match : 0; }
  877. Xsinglefy (src, match) int src, match; {
  878. X   /* This is a subfunction of init() which is called twice to improve the
  879. X   fractal blob.  It calls segment() and then interprets the result.  If
  880. X   only one region was found, no improvement is needed; otherwise, the
  881. X   area of each region must be computed by summing the areas of all its
  882. X   fragments, and the index of the largest region is returned. */
  883. X   int i, reg, frag, besti, besta;
  884. X   segment (src, match, &frag, ®);
  885. X   if (reg == 1) return (-1); /* No improvement needed */
  886. X   /* Initialize the areas to zero, then sum frag areas */
  887. X   for (i=1; i<=reg; i++) tarea[i] = 0;
  888. X   for (i=1; i<=frag; i++) tarea [kid[i]] += karea [i];
  889. X   /* Pick largest area of all regions and return it */
  890. X   for (i=1, besta=0, besti=0; i<=reg; i++)
  891. X      if (besta < tarea[i]) { besti = i; besta = tarea[i]; }
  892. X   return (besti); }
  893. Xnewdxy () {
  894. X   /* For each plate, compute how many squares it should move this step.
  895. X   Multiply the plate's basic movement vector odx,ody by the age modifier
  896. X   MR[], then add the remainders rx,ry from the last move to get some large
  897. X   integers.  Then turn the large integers into small ones by dividing by
  898. X   REALSCALE and putting the remainders back into rx,ry.  This function also
  899. X   increases the age of each plate, but doesn't let the age of any plate
  900. X   go above MAXLIFE.  This is done to make sure that MR[] does not need to
  901. X   be a long vector. */
  902. X   register int i, a;
  903. X   /* If there is only a single supercontinent, anchor it */
  904. X   for (i=phead, a=0; i; i=p[i].next, a++);
  905. X   if (a == 1) if (p[phead].odx || p[phead].ody) {
  906. X      p[phead].odx = 0; p[phead].ody = 0; }
  907. X
  908. X   for (i=phead; i; i=p[i].next) {
  909. X      a = (p[i].odx * MR[p[i].age]) + p[i].rx;
  910. X      p[i].dx = a / REALSCALE; p[i].rx = a % REALSCALE;
  911. X      a = (p[i].ody * MR[p[i].age]) + p[i].ry;
  912. X      p[i].dy = a / REALSCALE; p[i].ry = a % REALSCALE;
  913. X      if (p[i].age < MAXLIFE-1) (p[i].age)++; } }
  914. Xmove (src, dest) int src, dest; {
  915. X   /* This function moves all the plates that are drifting.  The amount to
  916. X   move by is determined in newdxy().  The function simply steps through
  917. X   every square in the array; if there's a plate in a square, its new location
  918. X   is found and the topography is moved there.  Overlaps between plates are
  919. X   detected and recorded so that merge() can resolve the collision; mountain
  920. X   growing is performed.  If two land squares wind up on top of each other,
  921. X   folded mountains are produced.  If a land square winds up where ocean was
  922. X   previously, that square is the leading edge of a continent and grows a
  923. X   mountain by subsuming the ocean basin. */
  924. X   register int i, j; int a, b, c, x, y;
  925. X   /* Clear out the merge matrix and the destination arrays */
  926. X   for (i=1; i<MAXPLATE; i++) for (j=1; j<MAXPLATE; j++) mm[i][j] = 0;
  927. X   for (i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++) {
  928. X      m[dest][i][j] = 0; t[dest][i][j] = 0; }
  929. X   checkmouse ();
  930. X   /* Look at every square which belongs to a plate */
  931. X   for (i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++) if ((a = m[src][i][j]) > 0) {
  932. X      /* Add the plate's dx,dy to the position to get the square's new */
  933. X      /* location; if it is off the map, throw it away */
  934. X      x = p[a].dx + i; y = p[a].dy + j;
  935. X      if ((x >= XSIZE) || (x < 0) || (y >= YSIZE) || (y < 0)) p[a].area--;
  936. X      else { /* It IS on the map */
  937. X
  938. X         /* If the destination is occupied, remove the other guy but */
  939. X         /* remember that the two plates overlapped; set the new height */
  940. X         /* to the larger height plus half the smaller. */
  941. X         if (c = m[dest][x][y]) {
  942. X            (mm[a][c])++; (p[c].area)--;
  943. X            b = t[src][i][j]; c = t[dest][x][y];
  944. X            t[dest][x][y] = (b > c) ? b + (c>>1) : c + (b>>1); }
  945. X
  946. X         /* The destination isn't occupied.  Just copy the height. */
  947. X         else t[dest][x][y] = t[src][i][j];
  948. X
  949. X         /* If this square is over ocean, increase its height. */
  950. X         if (t[src][x][y] < ZCOAST) t[dest][x][y] += ZSUBSUME;
  951. X
  952. X         /* Plate A now owns this square */
  953. X         m[dest][x][y] = a; } } }
  954. Xmerge (dest) int dest; {
  955. X   /* Since move has set up the merge matrix, most of the work is done.  This
  956. X   function calls bump once for each pair of plates which are rubbing; note
  957. X   that a and b below loop through the lower diagonal part of the matrix.
  958. X   One subtle feature is that a plate can bump with several other plates in
  959. X   a step; suppose that the plate is merged with the first plate it bumped.
  960. X   The loop will try to bump the vanished plate with the other plates, which
  961. X   would be wrong.  To avoid this, the lookup table lut is used to provide
  962. X   a level of indirection.  When a plate is merged with another, its lut
  963. X   entry is changed to indicate that future merges with the vanished plate
  964. X   should be applied to the plate it has just been merged with. */
  965. X   char lut[MAXPLATE]; int a, aa, b, bb, i;
  966. X   for (a=1; a<MAXPLATE; a++) lut[a] = a;
  967. X   for (a=2; a<MAXPLATE; a++) for (b=1; b<a; b++) if (mm[a][b] || mm[b][a]) {
  968. X      aa = lut [a]; bb = lut[b];
  969. X      if (aa != bb) if (bump (dest, aa, bb)) {
  970. X         lut[aa] = bb;
  971. X         for (i=1; i<MAXPLATE; i++) if (lut[i] == aa) lut[i] = bb; } } }
  972. Xbump (dest, a, b) int dest, a, b; {
  973. X   /* Plates a and b have been moved on top of each other by some amount;
  974. X   alter their movement rates for a slow collision, possibly merging them.
  975. X   The collision "strength" is a ratio of the area overlap (provided by
  976. X   move ()) to the total area of the plates involved.  A fraction of each
  977. X   plate's current movement vector is subtracted from the movement vector
  978. X   of the other plate.  If the two vectors are now within some tolerance
  979. X   of each other, they are almost at rest so merge them with each other. */
  980. X   double maa, mab, ta, tb, rat, area; register int i, j, x;
  981. X   checkmouse();
  982. X   /* Find a ratio describing how strong the collision is */
  983. X   x = mm[a][b] + mm[b][a]; area = p[a].area + p[b].area;
  984. X   rat = x / (MAXBUMP + (area / 20)); if (rat > 1.0) rat = 1.0;
  985. X   /* Do some math to update the move vectors.  This looks complicated */
  986. X   /* because a plate's actual movement vector must be multiplied by */
  987. X   /* MR[age], and because I have rewritten the equations to maximize */
  988. X   /* use of common factors.  Trust me, it's just inelastic collision. */
  989. X   maa = p[a].area * MR[p[a].age]; mab = p[b].area * MR[p[b].age];
  990. X   ta = MR[p[a].age] * area;
  991. X   p[a].odx = (p[a].odx * maa + p[b].odx * mab * rat) / ta;
  992. X   p[a].ody = (p[a].ody * maa + p[b].ody * mab * rat) / ta;
  993. X   tb = MR[p[b].age] * area;
  994. X   p[b].odx = (p[b].odx * mab + p[a].odx * maa * rat) / tb;
  995. X   p[b].ody = (p[b].ody * mab + p[a].ody * maa * rat) / tb;
  996. X   /* For each axis, compute the remaining relative velocity.  If it is */
  997. X   /* too large, return without merging the plates */
  998. X   if (ABS (p[a].odx*MR[p[a].age] - p[b].odx*MR[p[b].age]) > BUMPTOL) return(0);
  999. X   if (ABS (p[a].ody*MR[p[a].age] - p[b].ody*MR[p[b].age]) > BUMPTOL) return(0);
  1000. X   /* The relative velocity is small enough, so merge the plates.  Replace */
  1001. X   /* all references to a with b, free a, and tell merge() a was freed. */
  1002. X   for (i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++)
  1003. X      if (m[dest][i][j] == a) m[dest][i][j] = b;
  1004. X   p[b].area += p[a].area; pfree (a);
  1005. X   return (a); }
  1006. Xerode (dest) int dest; {
  1007. X   /* This function takes the topography in t[dest] and smooths it, lowering
  1008. X   mountains and raising lowlands and continental margins.  It does this by
  1009. X   stepping across the entire array and doing a computation once for each
  1010. X   pair of 8-connected pixels.  The computation is done by onerode(), below.
  1011. X   The computation result for a pair is a small delta for each square, which
  1012. X   is summed in the array e.  When the computation is finished, the delta
  1013. X   is applied; if this pushes an ocean square high enough, it is added to
  1014. X   an adjacent plate if one can be found.  Also, if a land square is eroded
  1015. X   low enough, it is turned into ocean and removed from its plate. */
  1016. X   register int i, j, x, z, xx;
  1017. X   /* Zero out the array for the deltas first */
  1018. X   for (i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++) e[i][j] = 0;
  1019. X   checkmouse();
  1020. X
  1021. X   /* Step across the entire array; each pixel is adjacent to 8 others, and */
  1022. X   /* it turns out that if four pairs are considered for each pixel, each */
  1023. X   /* pair is considered exactly once.  This is important for even erosion */
  1024. X   for (i=1; i<XSIZE; i++) for (j=1; j<YSIZE; j++) {
  1025. X      onerode (dest, i, j,  0, -1);
  1026. X      onerode (dest, i, j, -1, -1);
  1027. X      onerode (dest, i, j, -1,  0);
  1028. X      if (i < XSIZE-1) onerode (dest, i, j, -1, 1); }
  1029. X   /* Now go back across the array, applying the delta values from e[][] */
  1030. X   for (i=0; i<XSIZE; i++) for (j=0; j<YSIZE; j++) {
  1031. X      z = t[dest][i][j] + e[i][j]; if (z < 0) z = 0; if (z > 255) z = 255;
  1032. X      /* If the square just rose above shelf level, look at the four */
  1033. X      /* adjacent squares.  If one is a plate, add the square to that plate */
  1034. X      if ((z >= ZSHELF) && (t[dest][i][j] < ZSHELF)) { xx = 0;
  1035. X         if (i > 1)       if (x = m[dest][i-1][j]) xx = x;
  1036. X         if (i < XSIZE-1) if (x = m[dest][i-1][j]) xx = x;
  1037. X         if (j > 1)       if (x = m[dest][i][j-1]) xx = x;
  1038. X         if (j < YSIZE-1) if (x = m[dest][i][j+1]) xx = x;
  1039. X         if (xx) { p[xx].area++; m[dest][i][j] = xx; } }
  1040. X      /* Add the increment to the old value; if the square is lower than */
  1041. X      /* shelf level but belongs to a plate, remove it from the plate */
  1042. X      t[dest][i][j] = z;
  1043. X      if ((z < ZSHELF) && (x = m[dest][i][j])) {
  1044. X         p[x].area--; m[dest][i][j] = 0; } } }
  1045. X
  1046. X
  1047. Xonerode (dest, i, j, di, dj) int dest, i, j, di, dj; {
  1048. X   /* This function is called once per pair of squares in the array.  The
  1049. X   amount each square loses to an adjacent but lower square in each step is
  1050. X   one-eighth the difference in altitude.  This is coded as a shift right 3
  1051. X   bits, but since -1 >> 3 is still -1, the code must be repeated to avoid
  1052. X   shifting negative numbers.  Also, since erosion is reduced below the
  1053. X   waterline, if an altitude is lower than ZERODE, ZERODE is used instead. */
  1054. X
  1055. X   register int z, t1, t2;
  1056. X
  1057. X   t1 = t[dest][i][j]; t2 = t[dest][i+di][j+dj]; z = 0;
  1058. X   if ((t1 > t2) && (t1 > ZERODE)) {
  1059. X      if (t2 < ZERODE) t2 = ZERODE;
  1060. X      z = ((t1 - t2 + ERODERND) >> 3); }
  1061. X   else if ((t2 > t1) && (t2 > ZERODE)) {
  1062. X      if (t1 < ZERODE) t1 = ZERODE;
  1063. X      z = -((t2 - t1 + ERODERND) >> 3); }
  1064. X   if (z) { e[i][j] -= z; e[i+di][j+dj] += z; } }
  1065. END_OF_FILE
  1066.   if test 14070 -ne `wc -c <'./src/tec3.c'`; then
  1067.     echo shar: \"'./src/tec3.c'\" unpacked with wrong size!
  1068.   fi
  1069.   # end of './src/tec3.c'
  1070. fi
  1071. echo shar: End of archive 2 \(of 4\).
  1072. cp /dev/null ark2isdone
  1073. MISSING=""
  1074. for I in 1 2 3 4 ; do
  1075.     if test ! -f ark${I}isdone ; then
  1076.     MISSING="${MISSING} ${I}"
  1077.     fi
  1078. done
  1079. if test "${MISSING}" = "" ; then
  1080.     echo You have unpacked all 4 archives.
  1081.     rm -f ark[1-9]isdone
  1082. else
  1083.     echo You still must unpack the following archives:
  1084.     echo "        " ${MISSING}
  1085. fi
  1086. exit 0
  1087. exit 0 # Just in case...
  1088. -- 
  1089. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1090. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1091. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1092. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1093.