[alt.sources] xmb, fast mandelbrot for x-window, part01/02

dm@rainbow.oulu.fi (Hannu Helminen) (05/23/91)

This is another program for generating Mandelbrot and Julia
sets for BSD unix and X window system. I wrote it for fun
but I think it might interest somebody, because it is quite
fast (even on workstations) and it looks quite nice, at least
on color displays.

This version (call it, say, 1.00) is freely distributable.

Have fun!

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 2)."
# Contents:  ReadMe cmap.h ipc.c ipc.h makefile mb.c mb.h misc.c misc.h
#   pix.c pix.h soc.c soc.h xmb.n
# Wrapped by dm@stekt2 on Thu May 23 16:38:44 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f ReadMe -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"ReadMe\"
else
echo shar: Extracting \"ReadMe\" \(2512 characters\)
sed "s/^X//" >ReadMe <<'END_OF_ReadMe'
X
XThis is a Mandelbrot/Julia program for X-windows. I think
Xit might interest somebody because it is very fast and
Xit looks very nice, at least on color displays.
X
XI originally thought I would post this to comp.sources.x but
Xthe rules for posting were too strict ;)
XNo kidding, I am not going to construct an Imakefile nor
Xpatchlevel.h because I do not know how to.
X
XSecondly, this program will probably compile OK only on
XSun 3's, Sun sparcstations and IBM RS/6000's because those
Xare the only machines I have access to. I still do not
Xknow how to make programs portable ;)
X
XThis program is designed under BSD unix, and there is at least
Xone BSD-specific select() call that you may need to modify if
Xporting to sysV environment. And I really do not know why
Xthe includes have different names under differenct environments...
Xsys/termio.h? sys/termios.h? termios.h? what the heck, I'll
Xleave it up to you to figure it out ;)
X
XCompiling instructions:
XSun3/sparc:	make "CC=gcc"  OR
X                make
X
XIBM RS/6000: make "CC=bsdcc"
X
XIt should not be too difficult in other environments either.
X
XThis will produce two programs, xmb and xnetmb;
Xthey are essentially the same program but xnetmb can
Xdistribute the calculations to other machines using
Xunix-style stream sockets. See the manual page for more details.
X
XThe speed of this program is for three reasons:
X1) fixed point math when possible (first 16-bit, then 32-bit and
X   doubles only as the last possibility)
X2) contour crawling algorithm... follows the edges of different ares
X   and thus reduces the number of pixels that must be calculated.
X3) distributed calculation in an network, xnetmb only.
X
XThe first of these had to be done in somewhat unportable manner.
XIf your system has problems with signed bit-shifts
Xor 2's complement math, you may have to #undef USE_INTEGER_MATH
Xfrom mb.h.
X
XOne of the options is undocumented, namely "-guru fast" ;)
XThat is because it will increase the net traffic of xnetmb by over
X100%, and it is not meant to be used lightly.
XNormally xnetmb will use only about 100 K of net traffic for an 1000x1000
Xpicture by 32767 color, pretty little, eh? ;)
X
XI might as well advertise the "mandel" program which is a similar
Xmandelbrot program for Amiga... it too uses fixed-point math and
Xcontour crawling. But do not contact me to get it!! It can probably
Xbe found in abcfd20, but I do not remenber its name.. mandel.lzh?
X
XAuthor: Hannu Helminen, Finland
XEmail: dm@stekt.oulu.fi
X(or dm@otitsun.oulu.fi or dm@rainbow.oulu.fi)
X
X
END_OF_ReadMe
if test 2512 -ne `wc -c <ReadMe`; then
    echo shar: \"ReadMe\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cmap.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"cmap.h\"
else
echo shar: Extracting \"cmap.h\" \(320 characters\)
sed "s/^X//" >cmap.h <<'END_OF_cmap.h'
X
X#define CMAP_TOGGLE 0
X#define CMAP_OPEN 1
X#define CMAP_CLOSE 2
X
X/* commands HandleCEvent may return to main(): */
X#define DO_REDRAW 1
X#define DO_PREVIOUS 2
X#define DO_RESTART 3
X#define DO_QUIT 4
X
Xvoid ToggleCWin();
Xint HandleCEvent();
Xvoid AllocColors();
Xvoid FreeColors();
Xvoid SetNColors();
Xunsigned long ToPixel();
X
END_OF_cmap.h
if test 320 -ne `wc -c <cmap.h`; then
    echo shar: \"cmap.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f ipc.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"ipc.c\"
else
echo shar: Extracting \"ipc.c\" \(10643 characters\)
sed "s/^X//" >ipc.c <<'END_OF_ipc.c'
X
X/* This module does all the inter-process communication. Hope I got 
X * it right.
X
X * callable functions of this module:
X
X * void CreateServer(int portnum);
X * void Client_run(char *server, int portnum);
X * int CalcRequest(double x, y, dx, dy, cx, cy, 
X                   int iters, flags, xoff, yoff);
X * void Invalidate(void);
X * void FlushRequests(void);
X * void CheckClients(int timeout); 
X * void CloseDown(void);
X * int PendingRequests(void);
X
X */
X#include <sys/types.h>
X#ifdef _AIX
X#include <sys/select.h>
X#endif
X#include <sys/types.h>
X#include <sys/time.h>
X#include <rpc/types.h>
X#include <rpc/xdr.h>
X#include <netinet/in.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <stdio.h>
X#include "ipc.h"
X#include "soc.h"
X
X#define MAXHOST 8
X
X#define PORTSTART IPPORT_USERRESERVED+926
X
X#define SHORT_SIZE 2
X#define LONG_SIZE 4
X#define PACK_SIZE (6*8+4*4)
X
X#define READY 0
X#define BUSY 1
X#define DEAD 2 /* Not yet impelented; for time-out. */
X#define OBSOLETE 3 /* client is busy, but result has become obsolete */
X
Xextern int errno;
X
Xstatic int soc1, soc2;
X
Xstruct clientinfo {
X  int soc;
X  int status;
X  int x, y, w, h;
X  } client[MAXHOST];
X
Xstatic int clients= 0;
X
X/* Queue of waiting calcrequests */
Xstruct waitqueue {
X  double x, y, dx, dy, cx, cy;
X  int w, h, iter, flags, xoff, yoff;
X  } *queue;
X
Xstatic int qlen= 0, qpos= 0;
X
X
Xvoid CreateServer(portnum)
Xint portnum;
X{
Xif ((soc1= establish((u_short)portnum)) < 0) {
X  perror("establish");
X  exit(1);
X  }
X/* The socket should be marked non-blocking. */
Xif (fcntl(soc1, F_SETFL, O_NDELAY) <0) {
X  perror("could not mark socket as non-blocking");
X  exit(1);
X  }
X}
X
X
Xvoid Client_run(server, portnum)
Xchar *server;
Xint portnum;
X{
Xint p= 0;
X
Xwhile (p < MAXHOST && ((soc1= establish((u_short)(PORTSTART+p))) < 0)) 
X  p++;
X
Xif (soc1 < 0) {
X  perror("establish() failed");
X  exit(1);
X  }
X
Xif ((soc2= call_socket(server, portnum)) < 0) {
X  perror("call_socket() failed");
X  exit(1);
X  }
X
X/* This socket shall block */
Xif (fcntl(soc2, F_SETFL, 0) <0) {
X  perror("could not mark socket as blocking");
X  exit(1);
X  }
X/* Now the server knows we exist (no other handshaking is done),
X * so loop waiting for incoming command (either QUIT or CALC)
X */
X
Xfor (;;) {
X  u_short cmd;
X  char xdrbuf[PACK_SIZE];
X  XDR xdrs;
X  int i;
X
X  double x, y, dx, dy, cx, cy;
X  int w, h, iter, flags;
X  u_short *dest;
X  u_char *buf, *ptr;
X  u_long len, nlen;
X
X  if (read_data(soc2, (char *)&cmd, SHORT_SIZE) < 0) {
X    /* an EOF condition... */
X    shutdown(soc1, 2);
X    close(soc1);
X    shutdown(soc2, 2);
X    close(soc2);
X    exit(0);
X    }
X
X  cmd= ntohs(cmd);
X  switch(cmd) {
X    case QUIT:
X      shutdown(soc1, 2);
X      close(soc1);
X      shutdown(soc2, 2);
X      close(soc2);
X      exit(0);
X    case CALC:
X      xdrmem_create(&xdrs, xdrbuf, PACK_SIZE, XDR_DECODE);
X      if (read_data(soc2, (char *)xdrbuf, PACK_SIZE) < 0) {
X        /* EOF in the middle of read... */
X        shutdown(soc1, 2);
X        close(soc1);
X        shutdown(soc2, 2);
X        close(soc2);
X        exit(0);
X        }
X
X      if (!(xdr_double(&xdrs, &x)  && xdr_double(&xdrs, &y) &&
X          xdr_double(&xdrs, &dx) && xdr_double(&xdrs, &dy) &&
X          xdr_double(&xdrs, &cx) && xdr_double(&xdrs, &cy) &&
X          xdr_int(&xdrs, &w) && xdr_int(&xdrs, &h) &&
X          xdr_int(&xdrs, &iter) && xdr_int(&xdrs, &flags) )) {
X        fprintf(stderr, "client xdr failed\n");
X        exit(1);
X        }
X
X      xdr_destroy(&xdrs);
X      len= iterate(x, y, dx, dy, cx, cy,
X              w, h, iter, flags, &dest);
X      ptr= buf= (u_char *)malloc(SHORT_SIZE * len);
X      for (i= 0; i < len; i++) {
X        u_short tmp= htons(dest[i]);
X        *ptr++= (u_char)(tmp >> 8);
X        *ptr++= (u_char)(tmp & 0xff);
X        }
X      nlen= htonl(len);
X      if (write_data(soc2, (char *)&nlen, LONG_SIZE) < 0 ||
X          write_data(soc2, (char *)buf, (int)(SHORT_SIZE * len)) < 0) {
X        shutdown(soc1, 2);
X        close(soc1);
X        shutdown(soc2, 2);
X        close(soc2);
X        exit(0);
X        }
X      free(dest);
X      free(buf);
X      break;
X    default:
X      fprintf(stderr, "client received unknown command #%d\n",
X        cmd);
X    } /* switch */
X  } /* for */
X} /* client_run */
X
X
Xvoid Recruit()
X{
Xint soc;
Xif ((soc= get_connection(soc1)) < 0) {
X  if (errno == EINTR || errno == EWOULDBLOCK)
X    return;
X  perror("accept");
X  exit(1);
X  }
X/* This socket should block when necessary */
Xif (fcntl(soc, F_SETFL, 0) <0) {
X  perror("could not mark socket as blocking");
X  exit(1);
X  }
Xif (clients == MAXHOST) {
X  /* If too many hosts, send QUIT command immediately. */
X  u_short cmd;
X  cmd= htons(QUIT);
X  write_data(soc, (char *)&cmd, SHORT_SIZE); /* if this fails, tough. */
X  shutdown(soc, 2);
X  close(soc);
X  return;
X  }
X/* record new client */
Xclient[clients].soc= soc;
Xclient[clients].status= READY;
Xclients++;
X}
X
X
Xint CalcRequest(x, y, dx, dy, cx, cy, w, h, iter, flags, xoff, yoff)
Xdouble x, y, dx, dy, cx, cy;
Xint w, h, iter, flags, xoff, yoff;
X{
Xint cli;
XRecruit();
Xif (clients == 0)
X  return(0); /* Cannot satisfy request */
X
Xfor (cli= 0; cli < clients; cli++) {
X  if (client[cli].status == READY) {
X    /* Send request */
X    u_short cmd;
X    char xdrbuf[PACK_SIZE];
X    XDR xdrs;
X
X    /* Cant use &-operator on auto variables */
X    double x2= x, y2= y, dx2= dx, dy2= dy, cx2= cx, cy2= cy;
X    int w2= w, h2= h, iter2= iter, flags2= flags;
X
X    cmd= htons(CALC);
X    if (write_data(client[cli].soc, (char *)&cmd, SHORT_SIZE) < 0) {
X      shutdown(client[cli].soc, 2);
X      close(client[cli].soc);
X      client[cli].status = DEAD;
X      return(0);
X      }
X
X    xdrmem_create(&xdrs, xdrbuf, PACK_SIZE, XDR_ENCODE);
X
X    if (!(xdr_double(&xdrs, &x2)  && xdr_double(&xdrs, &y2) &&
X      xdr_double(&xdrs, &dx2) && xdr_double(&xdrs, &dy2) &&
X      xdr_double(&xdrs, &cx2) && xdr_double(&xdrs, &cy2) &&
X      xdr_int(&xdrs, &w2) && xdr_int(&xdrs, &h2) &&
X      xdr_int(&xdrs, &iter2) && xdr_int(&xdrs, &flags2) )) {
X      fprintf(stderr, "server xdr failed\n");
X      exit(1);
X      }
X
X    if (write_data(client[cli].soc, (char *)xdrbuf, PACK_SIZE) < 0) {
X      shutdown(client[cli].soc, 2);
X      close(client[cli].soc);
X      client[cli].status = DEAD;
X      return(0);
X      }
X    xdr_destroy(&xdrs);
X    client[cli].status= BUSY;
X    client[cli].x= xoff; client[cli].y= yoff;
X    client[cli].w= w; client[cli].h= h;
X    return(1); /* Request succeeded */
X
X    } /* if */
X  } /* for */
X/* Well, all clients were busy. Now we shall put the remaining
X * requests into a queue waiting for processing in the future.
X * (Formerly, we just blocked until a client became free.
X * ie. CheckClient(BLOCK);  and then try again.)
X */
X
Xif (qlen == 0)
X  queue= (struct waitqueue *)
X          malloc((qlen= 4) * sizeof(struct waitqueue));
Xelse if (qpos >= qlen)
X  queue= (struct waitqueue *)
X          realloc(queue, (qlen += 4) * sizeof(struct waitqueue));
Xqueue[qpos].x= x;   queue[qpos].y= y;
Xqueue[qpos].dx= dx; queue[qpos].dy= dy;
Xqueue[qpos].cx= cx; queue[qpos].cy= cy;
Xqueue[qpos].w= w;   queue[qpos].h= h;
Xqueue[qpos].iter= iter; queue[qpos].flags= flags;
Xqueue[qpos].xoff= xoff; queue[qpos].yoff= yoff;
Xqpos ++;
Xreturn(2); /* Succesfully queued */
X
X} /* CalcRequest() */
X
X
Xvoid CheckClients(timeout)
Xint timeout;
X{
Xint cli, i;
Xfd_set ready;
Xstruct timeval *to= NULL; /* NULL would mean block infinitely */
Xint width;
XRecruit();
X
Xif (timeout != BLOCK) {
X  to= (struct timeval *)malloc(sizeof(struct timeval));
X  to->tv_usec= 0;
X  if (timeout == NOBLOCK)
X    to->tv_sec= 0;
X  else
X    to->tv_sec= timeout;
X  }
Xwidth= getdtablesize();
XFD_ZERO(&ready);
Xfor (cli= 0; cli < clients; cli++)
X  if (client[cli].status == BUSY || client[cli].status == OBSOLETE) 
X    FD_SET(client[cli].soc, &ready);
Xif (select(width, &ready, (fd_set *)NULL, (fd_set *)NULL, to) < 0) {
X  perror("select");
X  exit(1);
X  }
Xfor (cli= 0; cli < clients; cli++) {
X  if (FD_ISSET(client[cli].soc, &ready))
X    {
X    void DrawImage();
X    u_short *dest;
X    u_char *buf, *ptr;
X    u_long len;
X
X    if (read_data(client[cli].soc, (char *)&len, LONG_SIZE) < 0) {
X      shutdown(client[cli].soc, 2);
X      close(client[cli].soc);
X      client[cli].status = DEAD;
X      return;
X      }
X    len= ntohl(len);
X    dest= (u_short *)malloc(sizeof(short) * len);
X    ptr= buf= (u_char *)malloc(SHORT_SIZE * len);
X
X    if (read_data(client[cli].soc, (char *)buf, (int)(SHORT_SIZE * len)) < 0) {
X      shutdown(client[cli].soc, 2);
X      close(client[cli].soc);
X      client[cli].status = DEAD;
X      return;
X      }
X
X    /* Should the result be drawn on screen? */
X    if (client[cli].status == OBSOLETE) {
X      /* No, this is obsolete */
X      free(buf);
X      free(dest);
X      client[cli].status= READY;
X      }
X    else {
X      /* Go ahead, draw it */
X      for (i= 0; i < len; i++) {
X        dest[i]= (*ptr << 8)+(*(ptr+1));
X        ptr += 2;
X        }
X      free(buf);
X
X      for (i= 0; i < len; i++)
X        dest[i]= ntohs(dest[i]);
X      DrawImage(client[cli].x, client[cli].y,
X             client[cli].w, client[cli].h, dest);
X      client[cli].status= READY;
X      } /* else */
X    } /* if FD_SET */
X  } /* for */
X
X/* Now, if there is an idle client, and if there are requests
X * waiting, handle the waiting requests. 
X */
Xwhile (qpos > 0) {
X  int c_free= 0;
X  for (cli= 0; cli < clients; cli++)
X    if (client[cli].status == READY)
X      c_free= 1;
X  if (c_free) {
X    qpos--;
X    CalcRequest(queue[qpos].x, queue[qpos].y,
X                queue[qpos].dx, queue[qpos].dy,
X                queue[qpos].cx, queue[qpos].cy,
X                queue[qpos].w, queue[qpos].h,
X                queue[qpos].iter, queue[qpos].flags,
X                queue[qpos].xoff, queue[qpos].yoff);
X    }
X  else
X    break;
X  } /* while */
X} /* CheckClients */
X
X
Xvoid Invalidate()
X{
Xint cli;
X
Xfor (cli= 0; cli < clients; cli++)
X  if (client[cli].status == BUSY)
X    client[cli].status= OBSOLETE;
Xqpos= 0;
X}
X
X
Xvoid FlushRequests()
X{
X/* If any of the clients is unreachable or down, this call will block
X * forever. This would then be the most suitable place for time-outs.
X */
Xint cli;
X
Xfor (cli= 0; cli < clients; cli++)
X  while (client[cli].status == BUSY  || client[cli].status == OBSOLETE)
X    CheckClients(BLOCK);
X}
X
X
Xvoid CloseDown()
X{
Xint cli;
X
XFlushRequests();
Xfor (cli= 0; cli < clients; cli++)
X  if (client[cli].status != DEAD) {
X    u_short cmd;
X    cmd= htons(QUIT);
X    write_data(client[cli].soc, (char *)&cmd, SHORT_SIZE);
X    shutdown(client[cli].soc, 2);
X    close(client[cli].soc);
X    }
Xshutdown(soc1, 2);
Xclose(soc1);
X}
X
X
Xint PendingRequests()
X{
Xint cli;
Xfor (cli= 0; cli < clients; cli++) {
X  if (client[cli].status == BUSY)
X    return(1);
X  }
Xreturn(0);
X}
X
END_OF_ipc.c
if test 10643 -ne `wc -c <ipc.c`; then
    echo shar: \"ipc.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f ipc.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"ipc.h\"
else
echo shar: Extracting \"ipc.h\" \(255 characters\)
sed "s/^X//" >ipc.h <<'END_OF_ipc.h'
X
X#define QUIT 0
X#define CALC 1
X
X#define NOBLOCK 0
X#define BLOCK -1
X
X#define DEF_PORT 5358
X
Xvoid CreateServer();
Xvoid Client_run();
Xint CalcRequest();
Xvoid Invalidate();
Xvoid FlushRequests();
Xvoid CheckClients(); 
Xvoid CloseDown();
Xint PendingRequests();
X
END_OF_ipc.h
if test 255 -ne `wc -c <ipc.h`; then
    echo shar: \"ipc.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"makefile\"
else
echo shar: Extracting \"makefile\" \(735 characters\)
sed "s/^X//" >makefile <<'END_OF_makefile'
X
XCC=cc
Xall: xnetmb xmb
Xclean:
X	rm mb.o pix.o xnet.o xnonet.o soc.o ipc.o cmap.o misc.o
Xmisc.o : misc.c misc.h
X	$(CC) -O misc.c -c
Xcmap.o : cmap.c cmap.h misc.h
X	$(CC) -O cmap.c -c
Xipc.o : ipc.c ipc.h soc.h
X	$(CC) -O ipc.c -c
Xmb.o : mb.c mb.h pix.h
X	$(CC) -O mb.c -c
Xsoc.o : soc.c soc.h
X	$(CC) -O soc.c -c
Xpix.o : pix.c mb.h
X	$(CC) -O pix.c -c
Xxnet.o : x.c mb.h ipc.h cmap.h misc.h pix.h
X	$(CC) -O x.c -c -DUSE_NETWORK
X	mv x.o xnet.o
Xxnonet.o : x.c mb.h cmap.h misc.h pix.h
X	$(CC) -O x.c -c
X	mv x.o xnonet.o
Xxnetmb : xnet.o pix.o mb.o ipc.o soc.o cmap.o misc.o
X	$(CC) -O xnet.o mb.o pix.o ipc.o soc.o cmap.o misc.o -o xnetmb -lX11 -lm
Xxmb : xnonet.o pix.o mb.o cmap.o misc.o
X	$(CC) -O xnonet.o mb.o pix.o cmap.o misc.o -o xmb -lX11 -lm
END_OF_makefile
if test 735 -ne `wc -c <makefile`; then
    echo shar: \"makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f mb.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mb.c\"
else
echo shar: Extracting \"mb.c\" \(8424 characters\)
sed "s/^X//" >mb.c <<'END_OF_mb.c'
X
X/* This module does the most essential part of every mandelbrot
X * program: it iterates points (pixels). I am trying to make this
X * part fast using 16-bit or 32-bit fixed point math whenever possible.
X * Unfortunately, this had to be done in somewhat unportable manner.
X * If your machine has problems with these, #undefine USE_INTEGER_MATH
X * from mb.h.
X */
X
X/* And right :) You guessed it; because it is me that is writing this
X * program, the contour crawling algorithm is used, too.
X */
X
X/* iterate() is the main callable function of this module:
X
Xint iterate(x0, y0, dx, dy, cx, cy, nx, ny, iter, flags, dest)
Xdouble x0, y0, dx, dy, cx, cy;
Xint nx, ny;
Xint iter;
Xint flags;
Xu_short **dest;
X
X * Where
X o x0+iy0 is the upper lefthand corner of the rectangular area,
X o dx and idy are deltas in two orthogonal directions,
X o cx+icy is the constant used in iterating Julia sets,
X o (nx, ny) is the size of the rectangle,
X o iter is the maximum number of iterations,
X o flags can be set to JULIA and
X
X o dest is an array of u_shorts that returns the result to the caller.
X o iterate() returns the number of usable u_shorts in the array.
X 
X * dest will be allocated by iterate(), but it is callers responsibility
X * to free it. dest has the following format:
X
X o 16 bits for color of the first pixel (as returned by first_point(),
X o 16 bits for the number of entries in the chain of directions and
X o 2 bits for each direction code (see mb.h), rounded to next 16 bits.
X * NOTE: to further reduce the bandwidth needed, if color has highest
X * (bit 15 set), the number of directions is zero (0), that is, the
X * area consists of one (1) pixel.
X */
X
X#include <sys/types.h>
X#include <math.h>
X#include <stdio.h>
X#include "mb.h"
X#include "pix.h"
X#define PACKSIZE 512
X
X#ifdef USE_INTEGER_MATH
X/* First, the 16-bit version */
X#define multi16(x,y) (((x)*(y)) >> BIT16)
X
Xint mb16(xl, yl, cx, cy, iter)
Xdouble xl, yl;
Xdouble cx, cy;
Xint iter;
X{
Xregister short x= xl * (1 << BIT16), y= yl * (1 << BIT16);
Xregister short x0= cx * (1 << BIT16), y0= cy * (1 << BIT16);
Xregister short x2, y2;
Xregister short tmp;
X
X/* It is painful for assembler programmer such as me to write
X * unefficient code such as this. And, worse still, this code
X * is not portable. (It counts on signed bit-shifts and 2's
X * complement math.
X */
Xwhile (iter--) {
X  x2= multi16(x,x);
X  y2= multi16(y,y);
X  tmp= x2 + y2;
X  if (x < -(2 << BIT16) || x > (2 << BIT16) ||
X      y < -(2 << BIT16) || y > (2 << BIT16) ||
X      tmp < 0 || tmp > ((4 << BIT16)-1) )
X    return(iter);
X  y= multi16(x,y);
X  tmp= y + y;
X  if ((tmp > 0 && y <= 0) || (tmp < 0 && y >= 0))
X    return(iter?iter-1:0);
X  y= tmp + y0;
X  if ((tmp > 0 && y0 > 0 && y <= 0) || (tmp < 0 && y0 < 0 && y >= 0))
X    return(iter?iter-1:0);
X  tmp= x2 - y2;
X  x= tmp + x0;
X  if ((tmp > 0 && x0 > 0 && x <= 0) || (tmp < 0 && x0 < 0 && x >= 0))
X    return(iter?iter-1:0);
X  }
Xreturn(0);
X}
X
X
X/* Then a 32-bit one with even more complicated multiplication */
Xlong multi32(x, y)
Xregister long x,y;
X{
Xregister short sign= 1;
Xregister unsigned short xl, yl, xh, yh;
X
Xif (x < 0) {sign= -1; x= -x;}
Xif (y < 0) {sign= -sign; y= -y;}
Xxl= x & 0xffff; xh= x >> 16;
Xyl= y & 0xffff; yh= y >> 16;
Xreturn(sign*(long)( ((xh*yh) << (32-BIT32)) + ((xl*yh) >> (BIT32-16)) +
X        ((xh*yl) >> (BIT32-16)) + ((xl*yl) >> (BIT32))) );
X}
X
Xint mb32(xl, yl, cx, cy, iter)
Xdouble xl, yl;
Xdouble cx, cy;
Xint iter;
X{
Xlong x= xl * (1 << BIT32), y= yl * (1 << BIT32);
Xlong x0= cx * (1 << BIT32), y0= cy * (1 << BIT32);
Xlong x2, y2;
Xlong tmp;
X
Xwhile (iter--) {
X  x2= multi32(x,x);
X  y2= multi32(y,y);
X  tmp= x2 + y2;
X  if (x < -(2 << BIT32) || x > (2 << BIT32) ||
X      y < -(2 << BIT32) || y > (2 << BIT32) ||
X      tmp < 0 || tmp > ((4 << BIT32)-1) )
X    return(iter);
X  y= multi32(x,y);
X  tmp= y + y;
X  if ((tmp > 0 && y <= 0) || (tmp < 0 && y >= 0))
X    return(iter?iter-1:0);
X  y= tmp + y0;
X  if ((tmp > 0 && y0 > 0 && y <= 0) || (tmp < 0 && y0 < 0 && y >= 0))
X    return(iter?iter-1:0);
X  tmp= x2 - y2;
X  x= tmp + x0;
X  if ((tmp > 0 && x0 > 0 && x <= 0 ) || (tmp < 0 && x0 < 0 && x >= 0))
X    return(iter?iter-1:0);
X  }
Xreturn(0);
X}
X#endif
X
X
X/* Then, the simplest version of them all. (Slowest too.) */
Xint mbf(x0, y0, cx, cy, iter)
Xdouble x0, y0;
Xdouble cx, cy;
Xint iter;
X{
Xdouble x= x0, y= y0;
Xdouble x2, y2;
X
Xx0= cx; y0= cy;
Xwhile (iter--) {
X  x2= x*x;
X  y2= y*y;
X  if (x2 + y2 >= 4)
X    return(iter);
X  y= 2 * x * y + y0;
X  x= x2 - y2 + x0;
X  }
Xreturn(0);
X}
X
Xint iterate(x0, y0, dx, dy, cx, cy, nx, ny, iter, flags, dest)
Xdouble x0, y0, dx, dy, cx, cy;
Xint nx, ny;
Xint iter;
Xint flags;
Xu_short **dest;
X{
Xint px, py;
Xint **pixel;
Xint (*f)();
Xlong index= 0;
Xlong colorindex;
Xlong len;
Xint bitindex= 7;
X
Xlong size= PACKSIZE;
X
X*dest= (u_short *)malloc(PACKSIZE * sizeof(u_short));
X
X/* Determine which iterating function to use... */
Xf= mbf; /* default to highest precision */
X#ifdef USE_INTEGER_MATH
Xif (flags & JULIA) {
X  if (fabs(dx) > LIMIT_J32 && fabs(dy) > LIMIT_J32)
X    f= mb32;
X  if (fabs(dx) > LIMIT_J16 && fabs(dy) > LIMIT_J16)
X    f= mb16;
X  }
Xelse {
X  if (fabs(dx) > LIMIT_M32 && fabs(dy) > LIMIT_M32)
X    f= mb32;
X  if (fabs(dx) > LIMIT_M16 && fabs(dy) > LIMIT_M16)
X    f= mb16;
X  }
X#endif
X
X/* alloc space for map of iterated pixels... */
Xpixel= (int **)malloc(ny * sizeof(int *));
Xfor (py= 0; py < ny; py++) {
X  pixel[py]= (int *)malloc(nx*sizeof(int));
X  for (px= 0; px < nx; px++)
X    pixel[py][px]= -1;
X  }
X
X/* Move pixel forward (relative to dir) */
X#define forward(dir) (ox= x, oy= y, qx= px,qy= py,\
X         (dir&1 ? (dir&2?(x-= dx, --px) : (x+= dx, ++px)) : \
X             dir&2?(y-= dy,++py) : (y+= dy,--py)))
X/* This nullifies the effect of previous forward() */
X#define stepback (x= ox, y= oy,px= qx,py= qy)
X/* And these are used to rotate dir 90 deg clockwise or anticlockwice */
X#define clockwise(dir) (dir==3 ? dir= 0 : ++dir)
X#define anticlockwise(dir) (dir==0 ? dir= 3 : --dir)
X
X
X/* First check if x and y are within limits; if not, return -1.
X * Then check if pixel already calculated; if so, return its value.
X * Otherwise calculate the pixel, store and return it.
X */
X#define getpixel(px,py) (px < 0 || py < 0 || px >= nx || py >= ny ? -1 : \
X      (pixel[py][px] >= 0 ? pixel[py][px] : \
X      (pixel[py][px]= (flags & JULIA ? \
X      f(x, y, cx, cy, iter) : f(x, y, x, y, iter)))))
X
Xinitialize(nx, ny); /* pixel recording module (pix.c) */
Xfor (;;) {
X  double x, y, ox, oy;
X  int qx, qy;
X  int startx, starty;
X  int prevpixel, newpixel;
X  int dir;
X  u_short color;
X
X/* This tells us where to start from. */
X  if (first_point(&px, &py) == 0)
X    break;
X
X  if (flags & FAST) {
X    if (index+2 >= size) *dest= (u_short *)
X      realloc(*dest, (size += PACKSIZE) * sizeof(u_short));
X    (*dest)[index++]= px;
X    (*dest)[index++]= py;
X    }
X
X  x= x0+dx*px; y= y0-dy*py; dir= RIGHT;
X  startx= px; starty= py;
X  color= prevpixel= getpixel(px,py);
X  colorindex= index;
X  index += 2; /* space for color & len */
X  if (index >= size) *dest= (u_short *)
X    realloc(*dest, (size += PACKSIZE) * sizeof(u_short));
X  len= 0;
X
X/* Another macro... the complexity of the crawling algorithm is hidden behind
X * the facade of simple-looking macros ;) (Just kidding)
X */
X#define record_dir(dir) { \
Xif (bitindex <0 ) {bitindex= 7; index++;} \
Xif (index >= size) *dest= (u_short *) \
X  realloc(*dest, (size += PACKSIZE) * sizeof(u_short)); \
Xif (bitindex == 7) (*dest)[index]= 0; \
X(*dest)[index] |= ((u_short)dir) << (2*bitindex); \
Xbitindex--; \
X}
X
X/* This is the actual crawling algorithm. See how inherintly simple it is.
X */
X  do {
X    forward(dir);
X    if ((newpixel= getpixel(px,py)) != prevpixel) {
X      stepback;
X      clockwise(dir);
X      }
X    else {
X      prevpixel= newpixel;
X      next_point(dir); /* every single pixel must be recorded */
X      record_dir(dir); /* ...twice */
X      len++;
X      anticlockwise(dir);
X      }
X    }
X  while (startx != px || starty != py || dir != RIGHT);
X
X  if (bitindex != 7) {
X    bitindex= 7;
X    index++;
X    }
X  if (len==0) {
X    (*dest)[colorindex]=  color | 0x8000;
X    index--;
X    }
X  else {
X    (*dest)[colorindex]= color & 0x7fff;
X    (*dest)[colorindex+1]= len;
X    }
X
X  } /* for */
X
Xfor (py= 0; py < ny; py++) {
X  free(pixel[py]);
X  }
Xfree(pixel);
Xdeinit(); /* free some memory (pix.c) */
X
Xif (flags & FAST) {
X  if (index >= size) *dest= (u_short *)
X    realloc(*dest, (size += PACKSIZE) * sizeof(u_short));
X  (*dest)[index++]= LASTPIX;
X  }
Xreturn(index);
X}
X
END_OF_mb.c
if test 8424 -ne `wc -c <mb.c`; then
    echo shar: \"mb.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f mb.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mb.h\"
else
echo shar: Extracting \"mb.h\" \(656 characters\)
sed "s/^X//" >mb.h <<'END_OF_mb.h'
X
X#define JULIA 0x01
X#define FAST 0x02
X
X/* what FAST mode is on, the stream of data includes pixel start
X * information. This will increase net traffic by about 100%,
X * so do not use it lightly.
X * However, server can decode the pictures much faster when
X * FAST flag is set.
X */
X#define USE_INTEGER_MATH
X
X#define LIMIT_J16 (8e-3)
X#define LIMIT_J32 (1.6e-8)
X
X#define LIMIT_M16 (2e-3)
X#define LIMIT_M32 (4e-9)
X
X#define BIT16 13
X#define BIT32 29
X
X#define NODIR -1
X#define UP 0
X#define RIGHT 1
X#define DOWN 2
X#define LEFT 3
X
X#define LASTPIX (0xffff)
X
X#define MAXHOST 16
X
Xint iterate();
Xvoid initialize();
Xvoid deinit();
Xint first_point();
Xvoid next_point();
X
END_OF_mb.h
if test 656 -ne `wc -c <mb.h`; then
    echo shar: \"mb.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f misc.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"misc.c\"
else
echo shar: Extracting \"misc.c\" \(2615 characters\)
sed "s/^X//" >misc.c <<'END_OF_misc.c'
X
X/* This module contains some miscellaneous routines, like
X * allocating/freeing fonts, allocating cursors, writing text.
X
X * int AllocFonts(Display *display, GC gc);
X * int WriteText(Display *display, Window window, GC gc,
X            char *text, int x, int y);
X * void FreeFonts(Display *display);
X * void AllocCursors(Display *display);
X * void WaitCursor(Display *display, Window window);
X * void NormalCursor(Display *display, Window window);
X * void ZoomCursor(Display *display, Window window);
X * void SlideCursor(Display *display, Window window);
X * void VertCursor(Display *display, Window window);
X
X */
X#include <X11/Xlib.h>
X#include <X11/cursorfont.h>
X
X#define FONT1 "-*-times-bold-r-*-*-12-*"
X#define FONT2 "-*-helvetica-bold-r-*-*-12-*"
X#define FONT3 "-*-courier-bold-r-*-*-12-*"
X
XCursor arrow, waitc, cross, updown, vert;
XXFontStruct *font= 0;
X
X
Xint AllocFonts(display, gc)
XDisplay *display;
XGC gc;
X{
Xif ( !(font= XLoadQueryFont(display, FONT1)) &&
X    !(font= XLoadQueryFont(display, FONT2)) &&
X     !(font= XLoadQueryFont(display, FONT3)))
X  return 0;
XXSetFont(display, gc, font->fid);
Xreturn 1;
X}
X
X
Xint WriteText(display, win, gc, text, x, y)
XDisplay *display;
XWindow win;
XGC gc;
Xchar *text;
Xint x, y;
X{
Xint len;
Xif (!font) 
X  /* Some plausible default */
X  return(8 * strlen(text));
X
Xlen= XTextWidth(font, text, strlen(text));
XXDrawImageString(display, win, gc, x, y+ font->ascent,
X            text, strlen(text));
Xreturn(len);
X}
X
X
Xvoid FreeFonts(display)
XDisplay *display;
X{
Xif (font) XFreeFont(display, font);
X}
X
X
Xvoid AllocCursors(display)
XDisplay *display;
X{
X/* Good cursor choices:
X * XC_crosshair, XC_diamond_cross, XC_fleur, XC_heart,
X * XC_pencil, XC_plus, XC_spider
X */
X
Xarrow= XCreateFontCursor(display, XC_top_left_arrow);
Xwaitc= XCreateFontCursor(display, XC_watch);
Xcross= XCreateFontCursor(display, XC_crosshair);
Xupdown= XCreateFontCursor(display, XC_sb_v_double_arrow);
Xvert= XCreateFontCursor(display, XC_xterm);
X}
X
X
Xvoid WaitCursor(display, win)
XDisplay *display;
XWindow win;
X{
Xif (!arrow || !waitc)
X  return;
XXDefineCursor(display, win, waitc);
X}
X
X
Xvoid NormalCursor(display, win)
XDisplay *display;
XWindow win;
X{
Xif (!arrow)
X  return;
XXDefineCursor(display, win, arrow);
X}
X
X
Xvoid ZoomCursor(display, win)
XDisplay *display;
XWindow win;
X{
Xif (!cross || !arrow)
X  return;
XXDefineCursor(display, win, cross);
X}
X
X
Xvoid SlideCursor(display, win)
XDisplay *display;
XWindow win;
X{
Xif (!updown || !arrow)
X  return;
XXDefineCursor(display, win, updown);
X}
X
X
Xvoid VertCursor(display, win)
XDisplay *display;
XWindow win;
X{
Xif (!vert || !arrow)
X  return;
XXDefineCursor(display, win, vert);
X}
X
X
END_OF_misc.c
if test 2615 -ne `wc -c <misc.c`; then
    echo shar: \"misc.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f misc.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"misc.h\"
else
echo shar: Extracting \"misc.h\" \(174 characters\)
sed "s/^X//" >misc.h <<'END_OF_misc.h'
X
Xint AllocFonts();
Xint WriteText();
Xvoid FreeFonts();
Xvoid AllocCursors();
Xvoid WaitCursor();
Xvoid NormalCursor();
Xvoid ZoomCursor();
Xvoid SlideCursor();
Xvoid VertCursor();
X
END_OF_misc.h
if test 174 -ne `wc -c <misc.h`; then
    echo shar: \"misc.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pix.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pix.c\"
else
echo shar: Extracting \"pix.c\" \(5973 characters\)
sed "s/^X//" >pix.c <<'END_OF_pix.c'
X
X/* This module keeps track of which pixels are calculated and which are
X * not. It then returns the next pixel to start from, which is
X * guaranteed to be in upper edge of an area.
X * The algorithm used here depends on that the chain of pixels relayed
X * to it 1) goes clockwise and 2) follows the boundary of the area 
X * (That is, no peaks to the inside) (That would be stupid anyways)
X */
X
X/* In case you are interested, the changes in the "calculated-or-not"
X * status (off-to-on and on-to-off) are stored in a column by column
X * basis in a dynamically allocated structure.
X */
X
X/* NOTE: This module is used both by the sender and the receiver, since
X * pixel position information are omitted; the receiver should
X * reconstruct it using this package.
X * (unless FAST-flag is used)
X
X * void initialize(int nx, int ny);
X * void deinit(void);
X * int first_point(int *nx, int *ny);
X * void next_point(int dir);
X
X */
X
X#include <stdio.h>
X#include "mb.h"
X
X#undef DEBUG
X
X#define DEFSIZE 8
Xtypedef struct {
X  int *ch;
X  int size;
X  } pixline;
Xstatic pixline *pix;
Xstatic int px, py;
Xstatic int xsize, ysize;
Xstatic int firstdir, prevdir;
X#define NONE -1
X#define FIRSTP 0
X#define NEXTP 1
Xstatic int prevcall;
Xstatic int firstx, lastx;
X
X#define NOP 0
X#define ON 1
X#define OFF 2
X/* The need for on-to-off or off-to-on -transitions is determined 
X * entirely by previous and current direction. */
Xstatic int dir_code[4][4]= {
X  {NOP, ON,     ON,  NOP},
X  {NOP, ON,     ON,  ON|OFF},
X  {OFF, NOP,    NOP, OFF},
X  {OFF, ON|OFF, NOP, OFF}};
X
X
X/* dynamically allocate array */
Xvoid initialize(nx, ny)
Xint nx, ny;
X{
Xint x;
Xxsize= nx; ysize= ny;
Xpix= (pixline *)malloc(xsize * sizeof(pixline));
Xfor (x= 0; x < xsize; x++) {
X  pix[x].ch= (int *)malloc(DEFSIZE * sizeof(int));
X  pix[x].size= DEFSIZE;
X  }
Xfor (x= 0; x < xsize; x++) {
X  pix[x].ch[0]= 0; /* first on-to-off transition */
X  pix[x].ch[1]= -1; /* end of pixline */
X  }
Xprevcall= NONE;
X}
X
X
X/* free it as needed */
Xvoid deinit()
X{
Xint x;
Xfor (x= 0; x < xsize; x++) 
X  free(pix[x].ch);
Xfree(pix);
X}
X
X
X/* return next starting point for the crawling algorithm (or 0) */ 
Xint first_point(nx, ny)
Xint *nx, *ny;
X{
Xint x, y1, y2;
Xvoid next_point();
X
X/* There are at least two special cases to be considered.
X * 1) If first_point() is called two times in a row (that is, no
X * next_point() in between) that means that the previous area
X * consisted only of one single point. This is easily fixed by
X * feeding next_point() some false input.
X */
Xif (prevcall == FIRSTP) {
X  prevdir= RIGHT;
X  next_point(LEFT);
X  }
X/* 2) Assume next_point() has been called. The first direction in the
X * chain had to be wrapped to the end, because it's predecessor
X * of all pixels has to be known. Now handle that first dir.
X */
Xelse if (prevcall == NEXTP)
X  next_point(firstdir);
X
Xprevcall= FIRSTP; /* Now it is us that was the previously called one */
X
X/* first remove redundancy from the pix array (that is, if in the
X * same pixel position is both on-to-off and off-to-on -transition,
X * discard both. */
X#ifdef DEBUG
Xprintf ("--------------------------------------\ncombining...\n");
X#endif
Xfor (x= firstx; x <= lastx; x++) {
X#ifdef DEBUG
Xif (pix[x].ch[0] != 0 || pix[x].ch[1] != -1) {
X  int y;
X  printf ("Line %d (len %d):",x, pix[x].size);
X  for (y= 0; ; y++) {
X    printf ("%d ", pix[x].ch[y]);
X    if (pix[x].ch[y] < 0)
X      break;
X    }
X  printf ("\n");
X  }
X#endif
X  for (y1= y2= 0; ; y1++, y2++) {
X    while (pix[x].ch[y1] >= 0 &&
X           pix[x].ch[y1] == pix[x].ch[y1+1])
X             y1 += 2;
X    pix[x].ch[y2]= pix[x].ch[y1];
X    if (pix[x].ch[y1] < 0) break;
X    }
X#ifdef DEBUG
Xif (pix[x].ch[0] != 0 || pix[x].ch[1] != -1) {
X  int y;
X  printf ("Line %d (len %d):",x, pix[x].size);
X  for (y= 0; ; y++) {
X    printf ("%d ", pix[x].ch[y]);
X    if (pix[x].ch[y] < 0)
X      break;
X    }
X  printf ("\n");
X  }
X#endif
X  }
X
Xpx= 0; py= pix[0].ch[0];
Xfor (x= 1; x < xsize; x++)
X  if (pix[x].ch[0] < py) {
X    py= pix[x].ch[0];
X    px= x;
X    }
X*nx= px; *ny= py;
Xfirstx= xsize; lastx= 0;
Xprevdir= NODIR;
Xif (py >= ysize) return(0);
Xreturn(1);
X}
X
X
Xvoid next_point(dir)
Xint dir;
X{
Xint y;
Xint code;
Xif (prevdir == NODIR) {
X  /* This is the first dir in the chain. Because we do not
X   * know the previous dir, wrap this one to the end of the chain,
X   * since then we do know it. */
X  code= NOP;
X  firstdir= dir;
X  }
Xelse
X  code= dir_code[prevdir][dir];
X
X#ifdef DEBUG
Xprintf ("prev= %d dir= %d -> %d\n", prevdir, dir, code);
Xprintf ("Line %d (len %d):",px, pix[px].size);
Xfor (y= 0; ; y++) {
X  printf ("%d ", pix[px].ch[y]);
X  if (pix[px].ch[y] < 0)
X    break;
X  }
Xprintf ("\n");
X#endif
X
X/* Optimize a little.. keep track of which lines need to be rearranged
X */
Xif (px < firstx) firstx= px;
Xif (px > lastx) lastx= px;
X
X/* Store on-to-off and off-to-on transitions in an uniform manner
X * (The only exception is that on-to-off is stored one pixel below)
X */
Xif (code & ON) {
X  int tmp1= py, tmp2;
X  y= 0;
X  while (pix[px].ch[y] < tmp1 && pix[px].ch[y] >= 0)
X    y++;
X  while (tmp1 >= 0) {
X    tmp2= pix[px].ch[y];
X    pix[px].ch[y]= tmp1;
X    tmp1= tmp2;
X    y++;
X    }
X  if (y >= pix[px].size)
X    pix[px].ch= (int *)realloc(pix[px].ch,
X            (pix[px].size += DEFSIZE) * sizeof(int));
X  pix[px].ch[y]= tmp1; /* == -1 */
X  }
X
Xif (code & OFF) {
X  int tmp1= py+1, tmp2;
X  y= 0;
X  while (pix[px].ch[y] < tmp1 && pix[px].ch[y] >= 0)
X    y++;
X  while (tmp1 >= 0) {
X    tmp2= pix[px].ch[y];
X    pix[px].ch[y]= tmp1;
X    tmp1= tmp2;
X    y++;
X    }
X  if (y >= pix[px].size)
X    pix[px].ch= (int *)realloc(pix[px].ch,
X            (pix[px].size += DEFSIZE) * sizeof(int));
X  pix[px].ch[y]= tmp1; /* == -1 */
X  }
X
X#ifdef DEBUG
Xprintf ("Line %d (len %d):",px, pix[px].size);
Xfor (y= 0; ; y++) {
X  printf ("%d ", pix[px].ch[y]);
X  if (pix[px].ch[y] < 0)
X    break;
X  }
Xprintf ("\n\n");
X#endif
X
Xswitch (dir) {
X  case UP:    py--; break;
X  case DOWN:  py++; break;
X  case LEFT:  px--; break;
X  case RIGHT: px++; break;
X  }
Xprevdir= dir;
Xprevcall= NEXTP;
X}
X
END_OF_pix.c
if test 5973 -ne `wc -c <pix.c`; then
    echo shar: \"pix.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pix.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pix.h\"
else
echo shar: Extracting \"pix.h\" \(74 characters\)
sed "s/^X//" >pix.h <<'END_OF_pix.h'
X
Xvoid initialize();
Xvoid deinit();
Xint first_point();
Xvoid next_point();
X
END_OF_pix.h
if test 74 -ne `wc -c <pix.h`; then
    echo shar: \"pix.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f soc.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"soc.c\"
else
echo shar: Extracting \"soc.c\" \(3918 characters\)
sed "s/^X//" >soc.c <<'END_OF_soc.c'
X
X/* Socket operations.
X * int establish(int portnum)
X * int get_connection(int s)
X * int call_socket(char *hostname, int portnum)
X * int read_data(int s, char *buf, int n)
X * int write_data(int s, char *buf, int n)
X */
X
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/param.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <stdio.h>
X#include "soc.h"
X
Xextern int errno;
X
X/* code to establish a socket; originally from bzs@bu-cs.bu.edu
X */
X
Xint establish(portnum)
Xu_short portnum;
X{ char   myname[MAXHOSTNAMELEN+1];
X  int    s;
X  struct sockaddr_in sa;
X  struct hostent *hp;
X
X  bzero((char *)&sa,sizeof(struct sockaddr_in));      /* clear our address */
X  gethostname(myname,MAXHOSTNAMELEN);            /* who are we? */
X  hp= gethostbyname(myname);                  /* get our address info */
X  if (hp == NULL)                             /* we don't exist !? */
X    return(-1);
X  sa.sin_family= hp->h_addrtype;              /* this is our host address */
X  sa.sin_port= htons(portnum);                /* this is our port number */
X  if ((s= socket(AF_INET,SOCK_STREAM,0)) < 0) /* create socket */
X    return(-1);
X  if (bind(s,(struct sockaddr *)&sa,sizeof sa) < 0) {
X    close(s);
X    return(-1);                               /* bind address to socket */
X    }
X  listen(s, 3);                               /* max # of queued connects */
X  return(s);
X}
X
X
Xint get_connection(s)
Xint s;                    /* socket created with establish() */
X{ struct sockaddr_in isa; /* address of socket */
X  int i;                  /* size of address */
X  int t;                  /* socket of connection */
X
X  i = sizeof(isa);                   /* find socket's address */
X  getsockname(s,(struct sockaddr *)&isa,&i);            /* for accept() */
X
X  if ((t = accept(s,(struct sockaddr *)&isa,&i)) < 0) /* accept connection if ... */
X    return(-1);
X  return(t);
X}
X
X
Xint call_socket(hostname, portnum)
Xchar *hostname;
X{ struct sockaddr_in sa;
X  struct hostent     *hp;
X  int s;
X
X  if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host's */
X    errno= ECONNREFUSED;                       /* address? */
X    return(-1);                                /* no */
X}
X
X  bzero((char *)&sa,sizeof(sa));
X  bcopy(hp->h_addr,(char *)&sa.sin_addr,hp->h_length); /* set address */
X  sa.sin_family= hp->h_addrtype;
X  sa.sin_port= htons((u_short)portnum);
X
X  if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) < 0)   /* get socket */
X    return(-1);
X  if (connect(s,(struct sockaddr *)&sa,sizeof sa) < 0)    /* connect */
X    return(-1);
X  return(s);
X}
X
X
Xint read_data(s, buf, n)
Xint  s;                /* connected socket */
Xchar *buf;             /* pointer to the buffer */
Xint  n;                /* number of characters (bytes) we want */
X{ int bcount,          /* counts bytes read */
X      br;              /* bytes read this pass */
X
X  bcount= 0;
X  br= 0;
X  while (bcount < n) {             /* loop until full buffer */
X    if ((br= read(s, buf, n-bcount)) > 0) {
X      bcount += br;                /* increment byte counter */
X      buf += br;                   /* move buffer ptr for next read */
X    }
X    if (br <= 0)                    /* signal an error to the caller */
X      return(-1);
X  }
X  return(bcount);
X}
X
X
Xint write_data(s, buf, n)
Xint  s;                /* connected socket */
Xchar *buf;             /* pointer to the buffer */
Xint  n;                /* number of characters (bytes) */
X{ int bcount,          /* counts bytes written */
X      br;              /* bytes written this pass */
X
X  bcount= 0;
X  br= 0;
X  while (bcount < n) {             /* loop until full buffer */
X    if ((br= write(s, buf, n-bcount)) > 0) {
X      bcount += br;                /* increment byte counter */
X      buf += br;                   /* move buffer ptr for next write */
X    }
X    if (br <= 0)                    /* signal an error to the caller */
X      return(-1);
X  }
X  return(bcount);
X}
X
END_OF_soc.c
if test 3918 -ne `wc -c <soc.c`; then
    echo shar: \"soc.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f soc.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"soc.h\"
else
echo shar: Extracting \"soc.h\" \(95 characters\)
sed "s/^X//" >soc.h <<'END_OF_soc.h'
X
Xint establish();
Xint get_connection();
Xint call_socket();
Xint read_data();
Xint write_data();
X
END_OF_soc.h
if test 95 -ne `wc -c <soc.h`; then
    echo shar: \"soc.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f xmb.n -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"xmb.n\"
else
echo shar: Extracting \"xmb.n\" \(6731 characters\)
sed "s/^X//" >xmb.n <<'END_OF_xmb.n'
X.TH XMB N "20 May 1990" "X Version 11"
X.SH NAME
Xxmb, xnetmb \- fast mandelbrot generator for X-windows
X.SH SYNOPSIS
X.B xmb
X[
X.B \-display
X.I display
X]
X[
X.B \-geometry
X.I geometry
X]
X[
X.B \-zebra
X]
X.if n .ti +5
X.if t .ti +.5i
X[
X.B \-nice
X.I value
X]
X[
X.B \-iters
X.I i
X]
X[
X.B \-colors
X.I number
X]
X.if n .ti +5
X.if t .ti +.5i
X[
X.B \-z
X.I x+yi
X]
X[
X.B \-j
X.I cx+cyi
X]
X[
X.B \-x
X.I scale
X]
X.LP
X.B xnetmb
X[
X.B \-display
X.I display
X]
X[
X.B \-geometry
X.I geometry
X]
X.if n .ti +5
X.if t .ti +.5i
X[
X.B \-zebra
X]
X[
X.B \-nice
X.I value
X]
X[
X.B \-iters
X.I i
X]
X.if n .ti +5
X.if t .ti +.5i
X[
X.B \-colors
X.I number
X]
X[
X.B \-z
X.I x+yi
X]
X[
X.B \-j
X.I cx+cyi
X]
X.if n .ti +5
X.if t .ti +.5i
X[
X.B \-x
X.I scale
X]
X[
X.B \-server
X.I name
X]
X[
X.B \-port
X.I port
X]
X.if n .ti +5
X.if t .ti +.5i
X[
X.B \-machines
X.I list of rsh machines
X]
X
X.SH DESCRIPTION
X.LP
X.IR xmb 
Xand
X.IR xnetmb 
Xare fast and nice programs for generating Mandelbrot and Julia sets.
X.\"If you do not know what these sets are, tough.
X.IR xmb 
Xand
X.IR xnetmb
Xuse a few tricks to gain extra speed,
Xnamely
Xfixed-point math,
Xcontour-crawling algorithm,
Xand in the case of
X.IR xnetmb,
Xdistributed processing in a number of machines in a network.
X
X.IR xmb
Xand
X.IR xnetmb
Xare almost identical,
Xso the following explanation applies to both.
XWhen started,
X.IR xmb
Xwill open a window and display the Mandelbrot set
Xin its initial form.
XOne can zoom into the set
Xby pressing the left mouse button
Xand dragging it over the set.
XWhen middle mouse button is pressed,
X.IR xmb
Xwill calculate a Julia set,
Xusing the point under the pointer
Xas the magical constant
X.IR c.
XAnother press of the middle mouse button
Xwill return the Mandelbrot image.
X
XThe right mouse button will open and close a
X.IR control
X.IR window,
Xwhich contains some
X.IR gadgets,
Xa colormap
X.IR slider
Xand a 
X.IR spectrum
Xshowing the current colormap.
X
XThe sliders can be moved by pressing the left mouse button
Xand dragging.
XThe leftmost three sliders control 
Xthe single color
Xthat represents the interior of the set
Xwhile the remaining three
Xcontrol the continuous colormap
Xthat is used when colouring
Xthe exterior of the set.
X
XThe changes to the colormap do not take effect until
Xthe middle mouse button or the
X.IR Apply
Xgadget is pressed.
XThis in turn may damage the colors of the actual
Ximage, in which case the
X.IR Redraw
Xgadget may be used to redraw it.
XThe
X.IR Previous
Xgadget causes
X.IR xmb
Xto redraw the previous image, while
X.IR Restart
Xcauses it to draw the initial Mandelbrot set.
XThe
X.IR Quit
Xgadget, of course, quits the program.
X
XThe spectrum not only shows the current colormap,
Xit can also be used to shift the mapping
Xbetween iterations and colors.
XThis is achieved by pressing the left mouse button
Xover the spectrum,
Xmoving it to another position
Xand releasing the button.
XFor example, if there is an area that is coloured in red
Xand you would like to see it blue,
Xpress the mouse button when it is over red on the spectrum
Xand release it when it is over blue.
X(This should be done only once,
Xconsequent changes accumulate.)
XThen you may wish to press the
X.IR Redraw
Xgadget to see the new colors take effect.
X
X
X.SH OPTIONS
X.LP
XAll options may be abbreviated.
X.TP 1i
X.BI \-display " name"
Xspecifies the connection to X server, see
X.BR X (1).
X.TP
X.BI \-geometry " geometry"
Xdefines the initial window geometry, see
X.BR X (1).
X.TP
X.BI \-zebra
Xcauses
X.IR xmb
Xto go into zebra-stripe mode. Useful mostly on black-and-white displays.
X.TP
X.BI \-nice " value"
Xdefines how nice the program will be, see
X.BR nice (1).
XIt is nice to use this option, especially with
X.IR xnetmb
Xclients.
X.TP
X.BI \-iters " i"
Xspecifies the number of iterations. Default is as huge as 500.
X.TP
X.BI \-colors " number"
Xspecifies the number of color cells
X.IR xmb
Xwill try to allocate.
XBy default, it tries to allocate all colors available.
X.TP
X.BI \-z " x+yi"
Xspecifies the point in complex plane that will be shown
Xat the center of the window.
XDefault is 0+0i.
X.TP
X.BI \-j " x+yi"
Xspecifies the constant
X.IR c
Xand causes
X.IR xmb
Xto draw the corresponding Julia set.
X.TP
X.BI \-x " scale"
Xdefines the scale of the picture.
X.I scale
Xspecifies the average distance in the complex plane
Xthat is shown inside the window.
XDefault is 4.
XThe options
X.I z,
X.I j
Xand
X.I x
Xare useful to display an interesting set whose coordinates
Xare known in advance.
X.TP
X.BI \-server " name"
Xcauses
X.IR xnetmb
Xto start client operation.
XFirst it connects itself to specified
X.I port
X(see below) of server
X.I name.
XThen it waits for incoming calculation requests,
Xcalculates them, and sends the results back to server.
X.IR xnetmb
Xonly.
X.TP
X.BI \-port " port"
Xspecifies the
X.I port
X.IR xnetmb
Xwill establish the server at.
XDefault port is 5358.
XDo not override the default unless necessary.
XSpecifying a port number below 5000 may be dangerous.
XBeware of other servers using fixed port numbers.
X.IR xnetmb
Xonly.
X.TP
X.BI \-machines " list of rsh machines"
Xwill cause
X.IR xnetmb
Xto start server operation.
XFirst it establishes a specified port
X(see above).
XIf there are any machines in the
X.I list,
X.IR xnetmb
Xwill rsh to each of them and try to start a client,
Xsee
X.BR rsh (1C) .
XIf no clients can be found,
X.IR xnetmb
Xwill do the calculations itself.
X.IR xnetmb
Xonly.
X
XNOTE: You must be able to use rsh to the
Xmachines specified in the list.
XIt is also possible to start the clients
Xmanually.
XNote also that no client can connect to the server
Xunless the -m option is given (even with an empty list).
X
X.SH EXAMPLES
X.IP
X.B
Xexample% xmb -g 200x200+0+0 -z -i 1000
X.LP
Xwill open a small window at the top left corner
Xof the screen
Xand fill it with zebra-striped mandelbrot set.
X.IP
X.B
Xexample% xmb -d foobar -c 100
X.LP
Xwill display the set on display foobar but use only 100 colors.
X.IP
X.B
Xserver% xnetmb -m client
X.LP
Xwill start a server on machine
X.B server
Xand try to start a client on machine
X.B client.
X.IP
X.B
Xserver% xnetmb -m -p 6535
X.IP
Xclient1% xnetmb -s server -p 6535
X.IP
Xclient2% xnetmb -s server -p 6535
X.LP
Xwill start server on machine
X.B server
Xand two clients on machines
X.B client1
Xand
X.B client2
Xusing an alternate port number 6535.
X
X.SH SEE ALSO
X.BR nice (1),
X.BR rsh (1),
X.BR X (1)
X
X.SH BUGS
X.IR xmb
Xwill show an infinite number of Mandelbrot and Julia sets,
Xin addition to the real one that is centered at 0+0i.
XThese ghosts are due to fixed-point calculations and
Xare normally invisible, unless an abnormally
Xlarge
X.I scale
Xis specified
X
XThere are some problems with the networking of
X.IR xnetmb.
XIf you run
X.IR xnetmb
Xwith the -m -option twice in succession,
Xit may fail with error message
X"establish: Address already in use".
XWaiting a few seconds or specifying another port
Xnumber will probably correct the situation.
X
X.SH AUTHOR
X	Hannu Helminen -- dm@stekt.oulu.fi
X
END_OF_xmb.n
if test 6731 -ne `wc -c <xmb.n`; then
    echo shar: \"xmb.n\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0