[net.sources] view.c - A Mandlebrot set viewer for Apollos.

peterson@utah-cs.UUCP (John W Peterson) (10/07/85)

/* 
 * view.c - View mandlebrot sets on an Apollo display
 * 
 * Author:	J. W. Peterson, 
 *              based on "ibmcg4" by Bennett Todd @ Duke University.
 *
 * 		Computer Science Dept.
 * 		University of Utah
 * Date:	Fri Sep 20 1985
 * Copyright (c) 1985 J. W. Peterson
 * 
 */

/* View the mandlebrot set on a Color apollo (assumes 8 bit display) */

/*
 * view [name]
 * Display file name.pic created by "compute" on an Apollo color display
 * Works best with 8 bit color, but can produce interesting results on
 * bitmapped displays.
 *
 * This version is extended to you to use the mouse to find new regions
 * for "exploration".  The program will print out the coordinates
 * and the "scale" of the box defined by the last two mouse selections
 * (bottom left and top right).
 *
 * The "c" mouse key (whatever that is on your particular mouse)
 * exits the program.
 */


#include <stdio.h>

#include        "/sys/ins/base.ins.c"
#include        "/sys/ins/gpr.ins.c"
#include        "/sys/ins/pad.ins.c"
#include        "/sys/ins/streams.ins.c"

status_$t status;
stream_$id_t out_stream;
gpr_$pixel_value_t scanline[2000];
int hpix, vpix;

#define MAXNAMELEN 64 /* max filename length for MS-DOS */

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef EOF
#define EOF (-1)
#endif

#define ISSUFFIX(s1, s2) !strcmp((s1), (s2)+strlen(s2)-strlen(s1))

main(argc, argv)
int argc;
char **argv;
{
	char name[MAXNAMELEN];
        char ch;
	FILE *fp, *fopen();
	int i, j, n;
	double center_r,
		   center_i,
		   scale_r,
		   scale_i,
                   new_r, new_i, last_r, last_i;
	char colormax[4];
	int npix, count[256], temp_l;

	if (argc == 2)
		if (ISSUFFIX(".pic", argv[1]))
			strncpy(name, argv[1], MAXNAMELEN);
		else {
			strncpy(name, argv[1], MAXNAMELEN-4);
			strcat(name, ".pic");
		}
	else {
		printf("syntax: view [name]\n");
		printf("Name: ");
		gets(name);
	}
	if (!ISSUFFIX(".pic", name))
		strcat(name, ".pic");
	while ((fp = fopen(name, "r")) == NULL) {
		fprintf(stderr, "view: cannot open %s\n", name);
		printf("Name: ");
		gets(name);
		if (!ISSUFFIX(".pic", name))
			strcat(name, ".pic");
	}

	if (fscanf(fp, "Centered at %lf+%lfi, scale %lf+%lfi, %dx%d\n\032",
		&center_r, &center_i, &scale_r, &scale_i, &hpix, &vpix) != 6) {
		fprintf(stderr, "view: cannot parse header of %s\n", name);
		exit(1);
	}

	npix = hpix*vpix;

	printf("Reading %s centered at %lf+%lfi, scale %lf+%lfi, %dx%d\n",
			name, center_r, center_i, scale_r, scale_i, hpix, vpix);

	setup_scr(hpix,vpix);
	for (i=0; i<vpix; i++) {
		for (j=0; j<hpix; j++) {
			if ((n=getc(fp)) == EOF) {
				fprintf(stderr, "view: too few pixels!\n");
				exit(1);
			}

			putpoint(j, i, (n==255) ? 0 : n ); /* 255 = Black */
                        
		}
		flushline(i);
        }
 
        /* Allow the user to interactivly select points on the grid.  Code
         * will print out the center & scale of the box defined by the
         * last two points (lower left, upper right) selected. */

        last_r = 0.0; last_i = 0.0;
        do {
          get_cursor( &i, &j, &ch );
          new_r = (center_r - scale_r) + i * (2*scale_r/hpix);
          new_i = (center_i + scale_i) - j * (2*scale_i/vpix);

          printf("center=%lf+%lfi scale=%lf+%lfi\n",
                  (new_r + last_r)/2.0, (new_i + last_i)/2.0, 
                  (new_r-last_r)/2.0, (new_i-last_i)/2.0 );

          last_r = new_r; last_i = new_i;
       } while (ch != 'c');

}

/* Apollo dependant code (warning - status codes aren't checked) */

/* Initialize the display, by popping up a new window to draw the picture
 * in. */

setup_scr(hsize,vsize)
int hsize,vsize;
{
    gpr_$offset_t size;
    gpr_$color_vector_t colmap;
    gpr_$attribute_desc_t hidden_desc;
    unsigned int value;
    int i;
    name_$pname_t output_file;
    short len;
    gpr_$window_t dest_box;
    pad_$window_desc_t window_shape;
    static gpr_$bitmap_desc_t main_bitmap;
    short fontid;

    /* Open a separate window to display the results */

    window_shape.top = 0;
    window_shape.left = 0;
    window_shape.width = (short)hsize + 5;
    window_shape.height = (short)vsize + 5;

    pad_$create_window( "", (short) 0, pad_$transcript, (short) 1,
                        window_shape, out_stream, status );

    pad_$set_auto_close( out_stream, (short) 1, true, status );

    size.x_size = 1024;  /* GPR will shrink to fit */
    size.y_size = 1024;

  /* Initialize the graphics primitives package */

    gpr_$init(gpr_$direct,
              out_stream,               /* "unit" */
              size,                     /* size of the initial bitmap */
              7,                        /* identifier of the heightest
                                           numbered bitmap plane */
              main_bitmap,              /* resulting bitmap descriptor */
              status);

    gpr_$set_auto_refresh( true, status );  /* all yours, DM. */

    gpr_$acquire_display( status ) ;

  /* set the new bitmap descriptor to be the 'current bitmap' */
    gpr_$set_bitmap(main_bitmap,status);
    gpr_$clear( gpr_$black, status );

    gpr_$release_display( status );

/* Set up the color map.  I like the results this produces, however
 * there's an awful lot of room available for creative tweaking here.
 * (Note pixels between 255-16 and 254 may wrap around...)
 */

    for(i = 0; i < 256; i++) {
      colmap[i] = ((i * 4) % 128) + 127;
      colmap[i] |= i << 9;
      colmap[i] |= ((i * 2) & 0xFF) << 16 ;
    }

    colmap[0] = gpr_$black;		/* assure 0 is black */
    gpr_$acquire_display( status ) ;

    /* 16 is offset from DM window colors */
    gpr_$set_color_map( (int) 16, 255 - 16 , colmap, status );
    gpr_$release_display( status );

    init_cursor();

}

/*
 * Pixels are buffered on a Scanline basis - this increases the drawing
 * speed by an order of magnitude or so...
 */

putpoint(x, y, c)
int x,	y, c;
{
  scanline[x] = c + 16;  /* "+16" avoids DM colors, but can wrap around.. */
}

flushline(y)
int y;
{
    gpr_$window_t dest_box;

    dest_box.window_base.x_coord = 0;
    dest_box.window_base.y_coord = y;
    dest_box.window_size.x_size = hpix;
    dest_box.window_size.y_size = 1;

    gpr_$acquire_display(status);
    gpr_$write_pixels( scanline, dest_box, status); 
    gpr_$release_display( status );
}

/* Cursor handling code (translated from an old Pascal program...) */

/* Do all the grunge to initialize a cross-hair cursor */
init_cursor()
{
    static gpr_$bitmap_desc_t CursorBitmap;
    static gpr_$attribute_desc_t AttribBlock;
    static gpr_$keyset_t keyset;
  
    gpr_$position_t Pos;  /* Cursor offset */
    gpr_$offset_t Size;   /* Cursor bitmap size */
    gpr_$bitmap_desc_t Bitmap;  /* System bitmap */
  
    Size.x_size = 15;  Size.y_size = 15;
  
    gpr_$inq_bitmap(Bitmap, status);           /* save the original bitmap */
    gpr_$allocate_attribute_block(AttribBlock,status); 
  
 /*Now that we have a set of attributes, make a bitmap to store the cursor*/  

    gpr_$allocate_bitmap(Size, (gpr_$plane_t) 0,         /*Plane ID*/
                         AttribBlock,     /*Attribute block*/
                         CursorBitmap,
                         status);
  
  
    gpr_$set_bitmap(CursorBitmap,status);    /*Set graphics to cursor bitmap*/
    /*Draw cross-hair pattern*/
    gpr_$move((gpr_$coordinate_t) 0,  (gpr_$coordinate_t) 7, status);
    gpr_$line((gpr_$coordinate_t) 15, (gpr_$coordinate_t) 7, status);    
    gpr_$move((gpr_$coordinate_t) 7,  (gpr_$coordinate_t) 0, status);  
    gpr_$line((gpr_$coordinate_t) 7, (gpr_$coordinate_t) 15, status);
  
    gpr_$set_cursor_pattern(CursorBitmap, status); /*Assign bitmap to cursor*/
    Pos.x_coord =  7;
    Pos.y_coord =  7;
    gpr_$set_cursor_origin(Pos, status);	/*Center the origin*/
  
    gpr_$set_bitmap(Bitmap, status);    /*Reset the graphics to main bitmap*/
  
    { int i;				/* Kludge for pascal sets */
      for (i=0; i <= 7; i++)
      { keyset[i] = 0xFFFFFFFF; }}
  
    gpr_$enable_input( gpr_$buttons, keyset, status );
    gpr_$enable_input( gpr_$locator, keyset, status );
}

/* Read the cursor */

get_cursor( px, py, c )
int * px, * py;
char * c;
{
    gpr_$position_t pos;
    gpr_$event_t event;
    char eventdata;

    gpr_$acquire_display( status ); 
    do {
      gpr_$event_wait( event, eventdata, pos, status );
      gpr_$set_cursor_active( false, status );  /* avoid cursor smear */
      gpr_$set_cursor_position( pos, status );
      gpr_$set_cursor_active( true, status );
    } while ((event != gpr_$buttons) ||
             ((eventdata >= 'A') && (eventdata <= 'D'))); /* ignore 'up's */

    gpr_$release_display( status );
    *px = pos.x_coord;
    *py = pos.y_coord;
    *c = eventdata;
}