/******************************************************************************\ * * * IFS Lab, Copyright (C) 1992 by N. Zeldes. All rights reserved. * * * * Version 1.0, April 9, 1992 * * * * An interactive program for exploring the IFS Code method of Fractal Image * * representation and reconstruction. * * * * Requires the Fox/Dawson freeware req.library in libs: directory. * * * * Compile under Aztec C 5.0, with -ff option (Fast floating point) * * and -safmnpsu options (code optimization). * * Link with +cd option (forces sprite and image data to load in CHIP RAM). * * Link with IFF files (from CBM IFF Disk, Fish disk #185), and with the glue * * module for the Fox/Dawson requester library, thus: * * * * cc -ff -safmnpsu -i IFF -i req IFSLab.c * * ln +cd IFSLab.o IFF/putpict.o IFF/ilbmw.o IFF/packer.o * * IFF/iffw.o IFF/readpict.o IFF/ilbmr.o IFF/unpacker.o IFF/iffr.o * * IFF/remalloc.o req/myreqglue.o mf.lib c.lib * * * \******************************************************************************/ #include #include /* exec/memory.h required to use AllocMem() */ #include /* graphics/text.h required for font TextAttr structure */ #include #include /* Floating point math: sqrt, fabs, trig functions */ #include /* Required by myatan2() */ #include /* needed for definition of INTERLACE */ #include /* stdlib.h needed for rand() */ #include /* time.h needed for time() */ #include /* needed for memset */ #include /* Header file from IFF Disk (Note that */ /* ilbm.h #includes compiler.h and iff.h) */ #include /* From IFF Disk */ #include /* From IFF Disk; For ChipAlloc(), RemFree() */ #include /* For IFF file I/O stuff */ #include /* For the Process structure definition. */ #include /* Fox/Dawson file requester header file */ #include "IFSLab.h" /* PowerWindows-generated structures and defines */ #define INTUITION_REV 31L #define GRAPHICS_REV 31L #define OUTLINE 0 /* parameters for SetMode() */ #define COLLAGE 1 #define FREEHAND 0 /* drawmode values */ #define LINES 1 #define ERASE 2 #define FILL 3 #define VECTOR 4 #define CODES 0 /* parameters for AboutText() */ #define IFS_THEORY 1 #define HELP1 2 #define HELP2 3 #define AUTHOR 4 #define DEMOTEXT 5 #define WIDTH Window->Width #define HEIGHT Window->Height #define GZZWIDTH Window->GZZWidth #define GZZHEIGHT Window->GZZHeight #define MAX_N 19 /* max permitted piece index */ struct Screen *Screen = NULL, *ImageScreen; struct RastPort *scrp; /* Pointer to Screen RastPort */ struct Window *Window = NULL, *NumWindow; struct RastPort *r; /* Pointer to RastPort of main editing window */ struct Window *ImageWindow; /* Window for rendering Image */ struct RastPort *Textrp; /* Rastport of Text Window */ struct IntuitionBase *IntuitionBase = NULL; struct GfxBase *GfxBase = NULL; void *MathBase = NULL; void *MathTransBase = NULL; struct ReqLib *ReqBase = NULL; /* Fox/Dawson Requester Library */ int drawmode = FREEHAND; /* Current mode - FREEHAND,LINES,ERASE,FILL (outline) or VECTOR (Collage) */ int exists_outline = 0, exists_image = 0, exists_numwindow = 0; int renderwidth = 640, renderheight = 400; /* initialize to default */ int renderdepth = 1; /* initialize to default */ long mx, my; /* Current Mouse coordinates in editing */ struct pixel { long x; long y; }; struct piece { double a, b, c, d, e, f; double s1, s2, r1, r2; double dens; double p; double det; UBYTE *piecemap; struct pixel boxo, boxx, boxy, boxz; }; struct piece *pieceptr[MAX_N + 1]; /* Array of pointers to piece structures */ int N = -1; /* Index of highest existing Piece Initialized to zero pieces */ int selpiece; /* Index of currently selected Piece */ char curcor; /* Currently dragged Box corner ('o','x','y','z') or none ('0') */ struct pixel ghboxo, ghboxx, ghboxy, ghboxz; /* Current corners of ghost Box */ struct pixel tmpboxo, tmpboxx, tmpboxy, tmpboxz; struct piece *AllocPiece(); double myatan2(); /* My version of atan2() which is missing in Manx mf.lib */ char *fname, *GetFileName(); struct Library *OpenLibrary(); void *AllocMem(), *AllocRaster(); UBYTE *outlinebufptr=NULL; /* ptr to buffer holding outline in collage mode */ long i; /* handy as a loop index */ /*** IFF error messages (external) */ char MsgOkay[] = { "(IFF_OKAY) A good IFF file" }; char MsgEndMark[] = { "(END_MARK) IFF Loader Error" }; char MsgDone[] = { "(IFF_DONE) Well Done!" }; char MsgDos[] = { "DOS Error - Aborting Load!" }; char MsgNot[] = { "Not an IFF file - Aborting Load!" }; char MsgNoFile[] = { "No such file found - Aborting Load!" }; char MsgClientError[] = { "(CLIENT_ERROR) IFF Checker bug"}; char MsgForm[] = { "(BAD_FORM) IFF Loader Error" }; char MsgShort[] = { "(SHORT_CHUNK) IFF Loader Error" }; char MsgBad[] = { "A mangled IFF file - Aborting Load!" }; /* MUST GET THESE IN RIGHT ORDER!!*/ char *IFFPMessages[-LAST_ERROR+1] = { /*IFF_OKAY*/ MsgOkay, /*END_MARK*/ MsgEndMark, /*IFF_DONE*/ MsgDone, /*DOS_ERROR*/ MsgDos, /*NOT_IFF*/ MsgNot, /*NO_FILE*/ MsgNoFile, /*CLIENT_ERROR*/ MsgClientError, /*BAD_FORM*/ MsgForm, /*SHORT_CHUNK*/ MsgShort, /*BAD_IFF*/ MsgBad }; struct FileRequester MyFileReqStruct; /* File Requester stuff */ char filename[FCHARS+1]; char directoryname[DSIZE+1]; char pathname[FCHARS+DSIZE+2+4]; /* +4 to permit space for appending '.ifs' */ /*==========================================================================*/ main() { struct Message * GetMsg(); void ReplyMsg(); struct IntuiMessage *message; ULONG class; USHORT code, MenuNumber, menuid, itemid, subitemid; struct MenuItem *ItemAddress(), *ItemAddr; long labs(); int w; /* temporary int variable */ int hitgadget; /* GadgetID # of last gadget hit by mouse */ struct Gadget *hitgadstruct; /* Gadget Structure of last hit gadget */ int numgadgpending; /* GadgetID of unserviced coeff gadget, or -1 if none */ struct piece tmppiece; /* will be used to hold temporary trans' coeffs */ long mx0,my0, mx1,my1; /* coords of endpoints of ghost line in Lines mode */ int pendown = 0; /* 0 if mouse SELECT button is currently pressed in window */ SHORT WindowBdrArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* will be filled later to corners of inner GZZ Window */ struct Gadget *gad; /* used as a temporary variable */ /**** Initialize stuff, set up edit window in custom screen etc. ****/ OpenLibraries(); OpenDisplay(); /* Open Outline Sketch window in custom screen */ SetAPen(r, 2L); WindowBdrArray[2] = WindowBdrArray[4] = GZZWIDTH - 1; WindowBdrArray[5] = WindowBdrArray[7] = GZZHEIGHT - 1; /*** Allocate memory for Outline buffer bitmap */ if ((outlinebufptr = (UBYTE *)AllocMem((long)RASSIZE(WIDTH,HEIGHT), MEMF_CHIP)) == NULL) { ShowError("Can't allocate Outline Buffer Memory!"); CloseAll(); } /**** Main Loop ****/ while(1) { mx = (long)Window->GZZMouseX; my = (long)Window->GZZMouseY; if (pendown == 1) { /*** Do Mouse editing to board per drawmode and mouse coords */ switch (drawmode) { case FREEHAND: Draw(r, mx, my); break; case LINES: SetDrMd(r, JAM1|COMPLEMENT); /* COMPLEMENT used for ghost line */ Move(r, mx0, my0); Draw(r, mx1,my1); /* Undraw previous ghost line */ Move(r, mx0, my0); Draw(r, mx1 = mx, my1 = my); /* Draw a new ghost line */ SetDrMd(r, JAM1); break; case ERASE: RectFill(r, mx - 2, my - 2, mx + 2, my + 2); break; case FILL: break; case VECTOR: /* Change the Ghost Vector Box */ if (exists_numwindow || curcor == '0') break; /* Check for attempt to size box to a point */ if ( (mx==pieceptr[selpiece]->boxo.x && my==pieceptr[selpiece]->boxo.y) && ( (curcor == 'z') || (curcor == 'x' && pieceptr[selpiece]->boxy.x == pieceptr[selpiece]->boxo.x && pieceptr[selpiece]->boxy.y == pieceptr[selpiece]->boxo.y) || (curcor == 'y' && pieceptr[selpiece]->boxx.x == pieceptr[selpiece]->boxo.x && pieceptr[selpiece]->boxx.y == pieceptr[selpiece]->boxo.y) ) ) mx = (mx == 0) ? 1 : mx - 1; /* If attempted, tweak mx to avoid */ ComputeNewBox(); /* Compute New Ghost Box from mouse coords */ ToggleGhostBox(); /* Undraw old ghost box */ ghboxo = tmpboxo; /* Update ghost box variables */ ghboxx = tmpboxx; ghboxy = tmpboxy; ghboxz = tmpboxz; ToggleGhostBox(); /* Draw new ghost box */ break; } /* End Switch */ } /* end if pendown == 1 */ /* Get an IDCMP event, if any, from Window */ if (message = (struct IntuiMessage *)GetMsg(Window->UserPort)) { class = message->Class; code = message->Code; ReplyMsg(message); } else /* no message */ class = code = NULL; /* so event routines below will be skipped */ /*** Go case by case over possible events from port */ if (class == MOUSEBUTTONS) { /*** Respond to user mousebutton clicks */ if (code == SELECTDOWN) { pendown = 1; if (drawmode != ERASE) exists_outline = 1; switch (drawmode) { case FREEHAND: case ERASE: Move(r, mx, my); break; case LINES: mx0 = mx1 = mx; /* Attach both ends of line to pixel */ my0 = my1 = my; break; case FILL: ModifyMousePtr(Window, 1); /* Bring up 'ZZ' pointer */ SetAPen(r, 2L); Move(r, 0L, 0L); PolyDraw(r,5L,WindowBdrArray); /* temporary border to prevent */ Flood(r, 0L, mx, my); /* messes on flood fill to edge */ SetAPen(r, 0L); Move(r, 0L, 0L); PolyDraw(r,5L,WindowBdrArray); ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ break; case VECTOR: if (exists_numwindow) break; /*** Decide which corner, if any, the user clicked on */ curcor = '0'; w = selpiece; /* Just to keep source lines shorter... */ if (labs(mx-pieceptr[w]->boxo.x)<5&&labs(my-pieceptr[w]->boxo.y)<5) curcor = 'o'; if (labs(mx-pieceptr[w]->boxx.x)<5&&labs(my-pieceptr[w]->boxx.y)<5) curcor = 'x'; if (labs(mx-pieceptr[w]->boxy.x)<5&&labs(my-pieceptr[w]->boxy.y)<5) curcor = 'y'; if (labs(mx-pieceptr[w]->boxz.x)<5&&labs(my-pieceptr[w]->boxz.y)<5) curcor = 'z'; if (curcor != '0') { /* Set up ghost box for starters */ /*** Update ghost box variables by calling ComputeNewBox() */ ComputeNewBox(); ghboxo = tmpboxo; /* Structure Assignments */ ghboxx = tmpboxx; ghboxy = tmpboxy; ghboxz = tmpboxz; ToggleGhostBox(); /* Draw initial ghost box */ } break; } /* End switch */ } /* End if SELECTDOWN */ else if (code == SELECTUP) { pendown = 0; switch (drawmode) { case FREEHAND: case ERASE: case FILL: break; case LINES: SetAPen(r, 2L); /* Draw final line (Jam onto last Ghost line) */ Move(r,mx0, my0); Draw(r, mx1, my1); break; case VECTOR: if (exists_numwindow || curcor == '0') break; ToggleGhostBox(); /* Undraw Ghost Box */ /*** Compute all coeffs from ghbox corners, put into tmppiece */ tmppiece = *pieceptr[selpiece]; /* Structure assignment */ tmppiece.s1 = sqrt((double)((ghboxx.x-ghboxo.x)*(ghboxx.x-ghboxo.x) + (ghboxx.y-ghboxo.y)*(ghboxx.y-ghboxo.y)))/GZZWIDTH; tmppiece.s2 = sqrt((double)((ghboxy.x-ghboxo.x)*(ghboxy.x-ghboxo.x) + (ghboxy.y-ghboxo.y)*(ghboxy.y-ghboxo.y)))/GZZHEIGHT; tmppiece.r1 = myatan2((double)(ghboxx.y-ghboxo.y), (double)(ghboxx.x-ghboxo.x)); tmppiece.r2 = myatan2((double)(ghboxo.x-ghboxy.x), (double)(ghboxy.y-ghboxo.y)); tmppiece.a = tmppiece.s1*cos(tmppiece.r1); tmppiece.b = -tmppiece.s2*sin(tmppiece.r2); tmppiece.c = tmppiece.s1*sin(tmppiece.r1); tmppiece.d = tmppiece.s2*cos(tmppiece.r2); tmppiece.e = (double)(ghboxo.x)/GZZWIDTH; tmppiece.f = (double)(ghboxo.y)/GZZWIDTH; /*** Erase Plane 3 before transformation delay */ SetWrMsk(r,0xFFF8); /* Write-protect planes 0,1,2 */ SetRast(r, 0L); SetWrMsk(r,0xFFFF); /* Transform the selected piece per user drag of vector box */ TransformPiece(outlinebufptr, pieceptr[selpiece]->piecemap, &tmppiece); /*** Erase Plane 2, then Blit Selected Piece from piecemap to it */ SetWrMsk(r,0xFFF4); /* Write-protect planes 0,1,3 */ SetRast(r, 0L); SetAPen(r, 4L); BltTemplate((char*)pieceptr[selpiece]->piecemap, (long)Window->BorderLeft,(long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT); /* Rastport WrMask will be restored in DrawBox() called below */ /*** Recompute probabilities and update piece structures */ tmppiece.boxo = ghboxo; /* structure assignment */ tmppiece.boxx = ghboxx; /* structure assignment */ tmppiece.boxy = ghboxy; /* structure assignment */ tmppiece.boxz = ghboxz; /* structure assignment */ *pieceptr[selpiece] = tmppiece; /* structure assignment */ pieceptr[selpiece]->det = fabs(tmppiece.a * tmppiece.d - tmppiece.b * tmppiece.c); if (pieceptr[selpiece]->det == 0.0) pieceptr[selpiece]->det = 0.01; ComputeProbs(); /* Adjust all piece probabilities */ DrawBox(); /* Redraw Vector Box for modified selected piece */ curcor = '0'; break; } /* End switch */ } /* End else if SELECTUP */ } /* End if MOUSEBUTTONS */ else if (class == GADGETUP) { /*** Respond to Gadget clicked in Window */ hitgadget = ((struct Gadget *)(message->IAddress))->GadgetID; /* ID# of just-selected gadget structure */ if (hitgadget <= 3) /* a draw mode gadget in Outline mode */ HiliteGadget(hitgadget); switch(hitgadget) { case 0: drawmode = FREEHAND; SetAPen(r,2L); break; case 1: drawmode = LINES; SetAPen(r,2L); break; case 2: drawmode = ERASE; SetAPen(r, 0L); SetOPen(r, 0L); break; case 3: drawmode = FILL; SetOPen(r, 2L); /* (A Pen defined before doing the flood fill) */ break; case 4: /* CLROUT (Clear Outline) gadget */ SetRast(r, 0L); exists_outline = 0; if(drawmode == ERASE || drawmode == FILL) { drawmode = FREEHAND; SetAPen(r,2L); HiliteGadget(0); } break; case 5: /* DONE gadget */ SetMode(COLLAGE); AddDefPiece(); DrawCollage(); break; case 6: /* ADD gadget */ AddDefPiece(); DrawCollage(); if (exists_numwindow) UpdateNumStrings(); break; case 7: /* DUP gadget */ DupSelPiece(); DrawCollage(); if (exists_numwindow) UpdateNumStrings(); break; case 8: /* SELECT gadget */ if (--selpiece < 0) selpiece = N; DrawCollage(); if (exists_numwindow) UpdateNumStrings(); break; case 9: /* DELETE gadget */ DeleteSelPiece(); DrawCollage(); ComputeProbs(); if (exists_numwindow) UpdateNumStrings(); break; case 10: /* CLRIFS gadget */ ClearIFS(); AddDefPiece(); DrawCollage(); if (exists_numwindow) UpdateNumStrings(); break; case 11: /* NUMBERS Gadget */ if (!exists_numwindow) { UpdateNumStrings(); OpenNumWindow(); numgadgpending = -1; } break; } /* end switch hitgadget */ } /* End else if GADGETUP */ else if (class == MENUPICK) { /*** Respond to menu selections */ MenuNumber = code; /* (First) item selection returned by IDCMP */ while (MenuNumber != MENUNULL) { /*** service multiply chosen items */ /* (if any) */ ItemAddr = ItemAddress(&MenuList1, (long)MenuNumber); menuid = MENUNUM(MenuNumber); /* Extract menu, item from IDCMP code */ itemid = ITEMNUM(MenuNumber); subitemid = SUBNUM(MenuNumber); if (menuid == 0) { /* PROJECT Menu */ switch (itemid) { case 0: /* NEW */ if (SetMode(OUTLINE) == 1) { SetRast(r, 0L); exists_outline = 0; if (exists_image) CloseImageScreen(); } break; case 1: /* EDIT OUTLINE */ SetMode(OUTLINE); break; case 2: /* LOAD FILE (with subitems) */ if(ReqBase == NULL) { /* req.library missing - no file I/O */ ShowError("req.library not found!"); break; } fname = GetFileName("File to Load:"); if (*fname != '\0') { switch (subitemid) { case 0: /* Load Outline */ if (SetMode(OUTLINE) == 1) LoadILBM(fname, 0); break; case 1: /* Load IFS */ SetMode(COLLAGE); if (LoadIFS(fname) == NULL) { /* If load failed */ ClearIFS(); AddDefPiece(); DrawCollage(); } break; case 2: /* Load Image */ LoadILBM(fname, 1); break; } /* end switch subitemid */ } /* end if *fname etc */ break; case 3: /* SAVE FILE (with subitems) */ if(ReqBase == NULL) { /* req.library missing - no file I/O */ ShowError("req.library not found!"); break; } switch (subitemid) { case 0: /* Save IFS */ SaveIFS(); break; case 1: /* Save Image */ SaveILBM(); break; } /* end switch subitemid */ break; case 4: /* OPTIMIZE */ if (drawmode != VECTOR) ShowError("No IFS to Optimize!"); else Optimize(1); break; case 5: /* QUIT */ CloseAll(); break; } /* end switch itemid */ } /* end if menuid == 0) */ else if (menuid == 1) { /* DISPLAY Menu */ switch (itemid) { case 0: /* SHOW CODES */ if (drawmode != VECTOR) ShowError("No IFS!"); else AboutText(CODES); break; case 1: /* SHOW IMAGE */ if (exists_image) { MoveScreen(ImageScreen, 0L, (long)(-1*ImageScreen->TopEdge)); ScreenToFront(ImageScreen); } else ShowError("No Image!"); break; } /* end switch itemid */ } /* end else if menuid == 1) */ else if (menuid == 2) { /* RENDER Menu */ switch (itemid) { case 0: /* RESOLUTION (with subitems) */ renderwidth = (subitemid > 0) ? 640 : 320; renderheight = (subitemid > 1) ? 400 : 200; break; case 1: /* GRAY LEVELS (with subitems) */ renderdepth = subitemid + 1; break; case 2: /* START */ while(message = (struct IntuiMessage *)GetMsg(Window->UserPort)) ReplyMsg(message); /* Clean out IDCMP port before closing it */ ModifyIDCMP(Window, NULL); /* Disable IDCMP during rendering */ ClearMenuStrip(Window); /* remove main window menus during render*/ RenderImage(0); /* render the attractor in Image Screen */ SetMenuStrip(Window, &MenuList1); ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK); break; } /* end switch itemid */ } /* end else if menuid == 2) */ else if (menuid == 3) { /* ABOUT Menu */ switch (itemid) { case 0: /* IFS THEORY */ AboutText(IFS_THEORY); break; case 1: /* HELP */ AboutText(HELP1); AboutText(HELP2); break; case 2: /* AUTHOR */ AboutText(AUTHOR); break; case 3: /* DEMO */ DoDemo(); break; } /* end switch itemid */ } /* end else if menuid == 3) */ MenuNumber = ItemAddr->NextSelect; /* Loop back for next multiply selected item, if any */ } /* end while MenuNumber != MENUNULL */ } /* end else if MENUPICK */ /*** Get & serve an IDCMP event, if any, from Coefficients Window */ if (exists_numwindow) { if (message = (struct IntuiMessage *)GetMsg(NumWindow->UserPort)) { ReplyMsg(message); /* Note that all events in this window are GADGETUP, GADGETDOWN */ /* or CLOSEWINDOW */ if (message->Class == CLOSEWINDOW) { CloseWindow(NumWindow); exists_numwindow = 0; numgadgpending = -1; SetMenuStrip(Window, &MenuList1); } else if (message->Class == GADGETDOWN) { /* ENTER or string gadget */ if (numgadgpending != -1) RecomputeCoeffs(numgadgpending); hitgadstruct = (struct Gadget *)(message->IAddress); /* pointer to just-hit gadget */ numgadgpending = hitgadstruct->GadgetID; /* ID# of just-selected gadget structure */ /**** Refresh all string gadgets except just-hit one */ for(gad = &NumAcoeffGadg; gad != NULL; gad = gad->NextGadget) if (gad->GadgetID != numgadgpending) RefreshGList(gad, NumWindow, NULL, 1L); if (numgadgpending == 12) numgadgpending = -1; /* It was only the ENTER Gadget */ } else if (message->Class == GADGETUP) { hitgadget = ((struct Gadget *)(message->IAddress))->GadgetID; /* ID# of just-selected gadget structure */ switch (hitgadget) { case 12: /* ENTER */ tmppiece = *pieceptr[selpiece]; /* Structure assignment */ /* Copy all coeffs from string gadgets to tmppiece */ sscanf((char*)NumNumR2coeffGadgSIBuff, "%lf", &tmppiece.r2); sscanf((char*)NumNumR1coeffGadgSIBuff, "%lf", &tmppiece.r1); sscanf((char*)NumNumS2coeffGadgSIBuff, "%lf", &tmppiece.s2); sscanf((char*)NumNumS1coeffGadgSIBuff, "%lf", &tmppiece.s1); sscanf((char*)NumNumPcoeffGadgSIBuff, "%lf", &tmppiece.p); sscanf((char*)NumNumDenscoeffGadgSIBuff, "%lf", &tmppiece.dens); sscanf((char*)NumNumFcoeffGadgSIBuff, "%lf", &tmppiece.f); sscanf((char*)NumNumEcoeffGadgSIBuff, "%lf", &tmppiece.e); sscanf((char*)NumNumDcoeffGadgSIBuff, "%lf", &tmppiece.d); sscanf((char*)NumNumCcoeffGadgSIBuff, "%lf", &tmppiece.c); sscanf((char*)NumNumBcoeffGadgSIBuff, "%lf", &tmppiece.b); sscanf((char*)NumNumAcoeffGadgSIBuff, "%lf", &tmppiece.a); tmppiece.r1 = tmppiece.r1*0.017453293; /* convert degs to radians*/ tmppiece.r2 = tmppiece.r2*0.017453293; /*** Erase Plane 3 before transformation delay */ SetWrMsk(r,0xFFF8); /* Write-protect planes 0,1,2 */ SetRast(r, 0L); SetWrMsk(r, 0xFFFF); /* Transform the selected piece per user-entered coefficients */ TransformPiece(outlinebufptr, pieceptr[selpiece]->piecemap, &tmppiece); /*** Erase Plane 2, then Blit Selected Piece from piecemap to it */ SetWrMsk(r,0xFFF4); /* Write-protect planes 0,1,3 */ SetRast(r, 0L); SetAPen(r, 4L); BltTemplate((char*)pieceptr[selpiece]->piecemap, (long)Window->BorderLeft,(long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT); /* Rastport WrMask will be restored in DrawBox() called below */ /*** Compute new Box corners from new coeffs */ tmppiece.boxo.x = tmppiece.e * GZZWIDTH; tmppiece.boxo.y = tmppiece.f * GZZWIDTH; /* sic! */ tmppiece.boxx.x = (tmppiece.a + tmppiece.e) * GZZWIDTH; tmppiece.boxx.y = (tmppiece.c + tmppiece.f) * GZZWIDTH; tmppiece.boxy.x = (tmppiece.b*GZZHEIGHT/GZZWIDTH + tmppiece.e) * GZZWIDTH; tmppiece.boxy.y = (tmppiece.d*GZZHEIGHT/GZZWIDTH + tmppiece.f) * GZZWIDTH; tmppiece.boxz.x = tmppiece.boxy.x + tmppiece.boxx.x - tmppiece.boxo.x; tmppiece.boxz.y = tmppiece.boxy.y + tmppiece.boxx.y - tmppiece.boxo.y; *pieceptr[selpiece] = tmppiece; /* structure assignment */ /*** Recompute probabilities and update piece structures */ pieceptr[selpiece]->det = fabs(tmppiece.a * tmppiece.d - tmppiece.b * tmppiece.c); if (pieceptr[selpiece]->det == 0.0) pieceptr[selpiece]->det = 0.01; ComputeProbs(); /* Adjust all piece probabilities */ DrawBox(); /* Redraw Vector Box for modified selected piece */ break; default: /* GADGETUP on any coefficient string gadget */ numgadgpending = -1; RecomputeCoeffs(hitgadget); RefreshGadgets(&NumGadgetList3, NumWindow, NULL); break; } /* end switch hitgadget */ } /* end else if ... GADGETUP */ } /* end if message etc of coeffs window */ } /* end if exists_numwindow */ /*** Get & serve an IDCMP event, if any, from Image Window */ /* Note that all events in this window are MOUSEBUTTONS */ if (exists_image) { if (message = (struct IntuiMessage *)GetMsg(ImageWindow->UserPort)) { ReplyMsg(message); if (message->Code == SELECTUP) { /* User clicked - push Image to back*/ ScreenToBack(ImageScreen); MoveScreen(ImageScreen, 0L, (long)(-1*ImageScreen->TopEdge)); ScreenToFront(Screen); ActivateWindow(Window); } } } /* End if exists_image */ } /* End while of main loop */ } /* End main() */ /*=========================================================================*/ char * GetFileName(prompt) /* Returns pointer to a filename selected from */ char *prompt; /* a file requester. Prompt appears at top of requester */ /* window. If user selected CANCEL,returns a nullstring */ /* Places the requester in a custom 640x200 screen. */ /* Uses the Fox/Dawson File Requester; you must link */ /* with glue module myreqglue.o. You need to include */ /* reqbase.h and also libraries/dosextens.h (for the */ /* Process structure). */ { struct Screen *ReqScreen; struct Window *ReqWindow; struct NewScreen ns; struct Screen *OpenScreen(); struct NewWindow nw; struct Window *OpenWindow(); struct Task *FindTask(); struct Process *myprocess; APTR oldwindowptr; /* Link the buffers to the FileRequester struct (all declared as globals) */ MyFileReqStruct.File = filename; MyFileReqStruct.Dir = directoryname; MyFileReqStruct.PathName = pathname; /*** Open a window in a custom Hires screen for requester display */ ns.LeftEdge = 0; /*** Initialize a NewScreen structure */ ns.TopEdge = 0; ns.Width = 640; ns.Height = 200; ns.Depth = 2; ns.DetailPen = 0; /* Colour of text in screen title bar */ ns.BlockPen = 1; /* Colour of screen Title Bar */ ns.ViewModes = HIRES; ns.Type = CUSTOMSCREEN; ns.Font = &TOPAZ80; ns.DefaultTitle = NULL; ns.Gadgets = NULL; ns.CustomBitMap = NULL; nw.LeftEdge = 0; /*** Initialize a NewWindow structure */ nw.TopEdge = 0; nw.Width = 640; nw.Height = 200; nw.DetailPen = 0; /* Menu title text color */ nw.BlockPen = 1; /* Menu item box and title bar background */ nw.IDCMPFlags = NULL; nw.Flags = ACTIVATE|NOCAREREFRESH; nw.FirstGadget = NULL; nw.CheckMark = NULL; nw.Title = (UBYTE*)" "; nw.Screen = NULL; /* Will set it to ReqScreen after the screen is opened */ nw.BitMap = NULL; nw.MinWidth = 0; nw.MinHeight = 0; nw.MaxWidth = 0; nw.MaxHeight = 0; nw.Type = CUSTOMSCREEN; /*** Open a Hires screen to place the requester in */ if ((ReqScreen = (struct Screen *) OpenScreen(&ns)) == NULL) { ShowError("Couldn't open Requester screen!!!"); pathname[0] = '\0'; /* make returned buffer a nullstring */ return(pathname); } /*** Open the Window to place the requester in */ nw.Screen = ReqScreen; if ((ReqWindow = (struct Window *) OpenWindow(&nw)) == NULL) { CloseScreen(ReqScreen); ShowError("Couldn't open Requester window!!!"); pathname[0] = '\0'; /* make returned buffer a nullstring */ return(pathname); } /* Set pointer to tell req.library requesters where to appear */ myprocess = (struct Process *)FindTask((char *)0); oldwindowptr = myprocess->pr_WindowPtr; myprocess->pr_WindowPtr = (APTR)ReqWindow; /*** Set up file requester structure fields to customize requester */ MyFileReqStruct.Title = prompt; /* Text at top of requester */ MyFileReqStruct.dirnamescolor = 3; /* colours of requester elements */ MyFileReqStruct.devicenamescolor = 3; MyFileReqStruct.WindowLeftEdge = 0; MyFileReqStruct.WindowTopEdge = 0; MyFileReqStruct.Flags = FRQCACHINGM | /* Directory Caching */ FRQABSOLUTEXYM | /* Fix requester position in screen */ FRQNOHALFCACHEM| /* Don't cache half-read directories */ FRQNODRAGM; /* No drag bar on requester */ if (FileRequester(&MyFileReqStruct) == NULL) /* Call the File Requester */ pathname[0] = '\0'; /* If user selected no file, return a nullstring*/ /* restore pr_WindowPtr before closing the window! */ myprocess->pr_WindowPtr = oldwindowptr; CloseWindow(ReqWindow); /*** Close requester screen stuff and return */ CloseScreen(ReqScreen); return(pathname); /* Will be the pointer to a string which now contains the */ /* full file name selected, or a NULL string if the user */ /* selected CANCEL or no filename */ } /*===========================================================================*/ OpenLibraries() /* Open needed libraries */ { IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",INTUITION_REV); if (IntuitionBase == NULL) { printf("Can't open Intuition library!!!!!!\n"); CloseAll(); } GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",GRAPHICS_REV); if (GfxBase == NULL) { printf("Can't open graphics library!!!!!!\n"); CloseAll(); } MathBase = (void *)OpenLibrary("mathffp.library",0L); if (MathBase == NULL) { printf("Can't open mathffp library!!!!!!\n"); CloseAll(); } MathTransBase = (void *)OpenLibrary("mathtrans.library",0L); if (MathTransBase == NULL) { printf("Can't open mathtrans library!!!!!!\n"); CloseAll(); } ReqBase = (struct ReqLib *)OpenLibrary("req.library", 0L); if (ReqBase == NULL) { printf("Couldn't open req.library!!!!!!\n"); printf("IFSlab requires req.library in libs: to be able to do file I/O\n"); } } /*=========================================================================*/ OpenDisplay() /* Open Outline Sketch window in custom screen */ { /* NEWSCREENSTRUCTURE, PALETTE and NewWindowStructure1 are in IFSLab.h */ /* and, along with pointers Window, Screen, have been declared externally */ struct Screen *OpenScreen(); struct Window *OpenWindow(); struct ViewPort *vp; /*** Open the screen */ if ((Screen = (struct Screen *) OpenScreen(&NEWSCREENSTRUCTURE)) == NULL) { printf("\nCouldn't open screen!!!!\n"); CloseAll(); } /*** Link Custom colours defined in PALETTE to screen's ViewPort */ vp = &(Screen->ViewPort);/* ptr to viewport structure assoc'd with screen */ LoadRGB4(vp,PALETTE,16L); /* Change to custom colors defined above */ /*** Open the Window */ NewWindowStructure1.Screen = Screen; if ((Window = (struct Window *) OpenWindow(&NewWindowStructure1)) == NULL) { printf("\nCouldn't open window!!!!\n"); CloseAll(); } scrp = &(Screen->RastPort); /* Screen RastPort,used to draw in outer window*/ SetDrMd(scrp, JAM1); SetAPen(scrp, 1L); r = (Window->RPort); /* Window's rastport, used for drawing in GZZ bitmap */ SetDrMd(r, JAM1); HiliteGadget(0); SetMenuStrip(Window,&MenuList1); OffMenu(Window, NOITEM<<5 | 2); /* disable render menu and its items */ } /*===========================================================================*/ OpenNumWindow() /* Open Coefficients Window and disable menus */ { /* Assumes window is not open already */ NumNewWindowStructure3.Screen = Screen; if ((NumWindow = (struct Window *)OpenWindow(&NumNewWindowStructure3)) == NULL) { ShowError("Error - Couldn't open coeffs window"); return; } ClearMenuStrip(Window); /* remove main window menus while Coeffs window on */ SetWindowTitles(NumWindow, -1L, (char *)" Collage Editor"); exists_numwindow = 1; } /*===========================================================================*/ CloseAll() /* Close everything neatly and exit program */ { if (exists_image) CloseImageScreen(); for (i = 0; i <= N; i++) /* Dealocate Pieces' struct and piecemap memory */ FreePiece(pieceptr[i]); if (outlinebufptr) FreeMem(outlinebufptr, (long)RASSIZE(WIDTH, HEIGHT)); if (Window) { ClearMenuStrip(Window); CloseWindow(Window); } if (exists_numwindow) CloseWindow(NumWindow); if (Screen) CloseScreen(Screen); if (IntuitionBase) CloseLibrary(IntuitionBase); if (GfxBase) CloseLibrary(GfxBase); if (MathBase) CloseLibrary(MathBase); if (MathTransBase) CloseLibrary(MathTransBase); if (ReqBase) { PurgeFiles(&MyFileReqStruct); /* function in req.library */ CloseLibrary(ReqBase); } exit(0); } /*===========================================================================*/ int SetMode(newmode) /* Sets up and enters requested mode */ int newmode; /* newmode can be OUTLINE or COLLAGE */ { /* returns 1 normally; returns 0 if user chose CANCEL in */ /* "IFS will be LOST" requester */ BOOL AutoRequest(); static struct IntuiText Cancel = { 3,0, /* FrontPen, BackPen */ JAM1, /* DrawMode */ 6,3, /* LeftEdge, TopEdge */ &TOPAZ80, /* ITextFont */ (UBYTE*)"Cancel", /* IText */ NULL /* NextText */ }; static struct IntuiText Doit, ReqBodyText; Doit = ReqBodyText = Cancel; /* structure assignment */ ReqBodyText.IText = (UBYTE *)" IFS will be LOST!"; ReqBodyText.TopEdge = 8; Doit.IText = (UBYTE *)"Do it!"; switch (newmode) { case OUTLINE: if (drawmode != VECTOR) /* already in desired mode! */ return(1); if (AutoRequest(Window, &ReqBodyText, &Doit, &Cancel, NULL, NULL, 180L, 56L) == FALSE) { /* Set IDCMP flags right in case AutoRequest() messed 'em up */ ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK); return(0); } ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK); /* After AutoReq. */ ClearIFS(); RemoveGList(Window, &CollageGadgetList2, 6L); AddGList(Window, &GadgetList1, 0L, 6L, NULL); /* change gadgets */ SetDrMd(scrp, JAM1); SetAPen(scrp, 0L); RectFill(scrp, 280L, 12L, 316L, 117L); /* Blot out old gadget images */ RefreshGadgets(&GadgetList1, Window, NULL); OffMenu(Window, NOITEM<<5 | 2); /* disable render menu and its items */ SetWindowTitles(Window, -1L, " Outline Editor"); drawmode = FREEHAND; HiliteGadget(0); SetAPen(r, 2L); break; case COLLAGE: if (drawmode == VECTOR) /* already in desired mode! */ return(1); if (exists_outline == 0) PutDefOutline(); CopyMem((char*)Window->RPort->BitMap->Planes[1], (char*)outlinebufptr, (long)RASSIZE(WIDTH,HEIGHT)); RemoveGList(Window, &GadgetList1, 6L); AddGList(Window, &CollageGadgetList2, 0L, 6L, NULL); SetDrMd(scrp, JAM1); SetAPen(scrp, 0L); RectFill(scrp, 280L, 12L, 316L, 117L); /* Blot out old gadget images */ RefreshGadgets(&CollageGadgetList2, Window, NULL); OnMenu(Window, NOITEM<<5 | 2); /* Enable render menu and its items */ SetWindowTitles(Window, -1L, " Collage Editor"); drawmode = VECTOR; curcor = '0'; break; } /* end switch newmode */ return(1); } /*===========================================================================*/ DupSelPiece() /* Generate a copy of Selected Piece, make it highest */ { /* numbered and Selected. Increment N. */ if (N >= MAX_N) { ShowError("Too many transformations"); return; } /*** Allocate and fill piece structure, make it Selected */ if ((pieceptr[++N] = AllocPiece()) == NULL) { /* Allocate Piece */ N--; /* if couldn't allocate piece */ if (N == -1) { /* No memory for even one Piece - Quit program */ ShowError("Re-run with more free memory!"); CloseAll(); } return; } pieceptr[N]->a = pieceptr[selpiece]->a; pieceptr[N]->b = pieceptr[selpiece]->b; pieceptr[N]->c = pieceptr[selpiece]->c; pieceptr[N]->d = pieceptr[selpiece]->d; pieceptr[N]->e = pieceptr[selpiece]->e; pieceptr[N]->f = pieceptr[selpiece]->f; pieceptr[N]->s1 = pieceptr[selpiece]->s1; pieceptr[N]->s2 = pieceptr[selpiece]->s2; pieceptr[N]->r1 = pieceptr[selpiece]->r1; pieceptr[N]->r2 = pieceptr[selpiece]->r2; pieceptr[N]->dens = 1.0; pieceptr[N]->det = pieceptr[selpiece]->det; pieceptr[N]->boxo = pieceptr[selpiece]->boxo; /* Structure assignment! */ pieceptr[N]->boxx = pieceptr[selpiece]->boxx; /* Structure assignment! */ pieceptr[N]->boxy = pieceptr[selpiece]->boxy; /* Structure assignment! */ pieceptr[N]->boxz = pieceptr[selpiece]->boxz; /* Structure assignment! */ ComputeProbs(); /* Adjust all Piece probabilities */ /* Copy selected piece's piecemap to the new piece's piecemap */ CopyMem((char*)pieceptr[selpiece]->piecemap, (char*)pieceptr[N]->piecemap, (long)RASSIZE(WIDTH,GZZHEIGHT)); selpiece = N; /* make this Piece Selected */ } /*===========================================================================*/ AddDefPiece() /* Generate a default Piece of current Outline, make */ { /* it highest numbered and Selected. Increment N. */ if (N >= MAX_N) { ShowError("Too many transformations"); return; } /*** Allocate and fill piece structure, make it Selected */ if ((pieceptr[++N] = AllocPiece()) == NULL) { /* Allocate Piece */ N--; /* if couldn't allocate piece */ if (N == -1) { /* No memory for even one Piece - Quit program */ ShowError("Re-run with more free memory!"); CloseAll(); } return; } pieceptr[N]->a = 0.5; pieceptr[N]->b = 0.0; pieceptr[N]->c = 0.0; pieceptr[N]->d = 0.5; pieceptr[N]->e = 0.25; pieceptr[N]->f = 0.16938406; /* = 0.25*GZZHEIGHT/GZZWIDTH */ pieceptr[N]->s1 = 0.5; pieceptr[N]->s2 = 0.5; pieceptr[N]->r1 = 0.0; pieceptr[N]->r2 = 0.0; pieceptr[N]->dens = 1.0; pieceptr[N]->det = 0.25; pieceptr[N]->boxo.x = 0.25 * GZZWIDTH; pieceptr[N]->boxo.y = 0.25 * GZZHEIGHT; pieceptr[N]->boxx.x = 0.75 * GZZWIDTH; pieceptr[N]->boxx.y = 0.25 * GZZHEIGHT; pieceptr[N]->boxy.x = 0.25 * GZZWIDTH; pieceptr[N]->boxy.y = 0.75 * GZZHEIGHT; pieceptr[N]->boxz.x = 0.75 * GZZWIDTH; pieceptr[N]->boxz.y = 0.75 * GZZHEIGHT; ComputeProbs(); /* Adjust all Piece probabilities */ /* Transform Outline by this piece's transformation into its piecemap */ TransformPiece(outlinebufptr, pieceptr[N]->piecemap, pieceptr[N]); selpiece = N; /* make this Piece Selected */ } /*===========================================================================*/ ComputeProbs() /* Recompute all piece probabilities */ { double sum = 0; for (i = 0; i <= N; i++) sum = sum + pieceptr[i]->dens * pieceptr[i]->det; for (i = 0; i <= N; i++) pieceptr[i]->p = (pieceptr[i]->dens * pieceptr[i]->det)/sum; } /*===========================================================================*/ DrawBox() /* Erase bitplane 3, and draw Vector Box of Selected Piece in it */ { SetWrMsk(r,0xFFF8); /* Write-protect planes 0,1,2 */ SetRast(r, 0L); /* Erase plane 3 */ SetAPen(r, 8L); /*** Draw Vector Box */ Move(r, pieceptr[selpiece]->boxo.x, pieceptr[selpiece]->boxo.y); Draw(r, pieceptr[selpiece]->boxx.x, pieceptr[selpiece]->boxx.y); Draw(r, pieceptr[selpiece]->boxz.x, pieceptr[selpiece]->boxz.y); Draw(r, pieceptr[selpiece]->boxy.x, pieceptr[selpiece]->boxy.y); Draw(r, pieceptr[selpiece]->boxo.x, pieceptr[selpiece]->boxo.y); SetDrMd(r, JAM2|INVERSVID); /*** Draw Corner "gadgets" */ SetBPen(r, 0L); Move(r, pieceptr[selpiece]->boxo.x - 3L, pieceptr[selpiece]->boxo.y + 3L); Text(r, "O", 1L); Move(r, pieceptr[selpiece]->boxx.x - 3L, pieceptr[selpiece]->boxx.y + 3L); Text(r, "x", 1L); Move(r, pieceptr[selpiece]->boxz.x - 3L, pieceptr[selpiece]->boxz.y + 3L); Text(r, " ", 1L); Move(r, pieceptr[selpiece]->boxy.x - 3L, pieceptr[selpiece]->boxy.y + 3L); Text(r, "y", 1L); SetDrMd(r, JAM1); SetWrMsk(r,0xFFFF); } /*===========================================================================*/ ToggleGhostBox() /* Draw or undraw current ghost box, as given in */ { /* ghbox? variables, in COMPLEMENT mode */ SetDrMd(r, JAM1|COMPLEMENT); Move(r, ghboxo.x, ghboxo.y); Draw(r, ghboxx.x, ghboxx.y); Draw(r, ghboxz.x, ghboxz.y); Draw(r, ghboxy.x, ghboxy.y); Draw(r, ghboxo.x, ghboxo.y); SetDrMd(r, JAM1); } /*===========================================================================*/ ComputeNewBox() /* Compute New Ghost Box from mouse coords, put in tmpbox */ { /* variables. */ long dx, dy; /* increments of mouse position from corner's previous pos */ double s, a; /* Scaling and Rotation components of change in vector Z */ double cosa, sina; /* cosine and sine of angle a */ double Qx, Qy; double tmp; switch (curcor) { case 'o': dx = mx - pieceptr[selpiece]->boxo.x; dy = my - pieceptr[selpiece]->boxo.y; tmpboxo.x = mx; tmpboxo.y = my; tmpboxx.x = pieceptr[selpiece]->boxx.x + dx; tmpboxx.y = pieceptr[selpiece]->boxx.y + dy; tmpboxy.x = pieceptr[selpiece]->boxy.x + dx; tmpboxy.y = pieceptr[selpiece]->boxy.y + dy; tmpboxz.x = pieceptr[selpiece]->boxz.x + dx; tmpboxz.y = pieceptr[selpiece]->boxz.y + dy; break; case 'x': dx = mx - pieceptr[selpiece]->boxx.x; dy = my - pieceptr[selpiece]->boxx.y; tmpboxo = pieceptr[selpiece]->boxo; /* Structure Assignment */ tmpboxx.x = mx; tmpboxx.y = my; tmpboxy = pieceptr[selpiece]->boxy; /* Structure Assignment */ tmpboxz.x = pieceptr[selpiece]->boxz.x + dx; tmpboxz.y = pieceptr[selpiece]->boxz.y + dy; break; case 'y': dx = mx - pieceptr[selpiece]->boxy.x; dy = my - pieceptr[selpiece]->boxy.y; tmpboxo = pieceptr[selpiece]->boxo; /* Structure Assignment */ tmpboxx = pieceptr[selpiece]->boxx; /* Structure Assignment */ tmpboxy.x = mx; tmpboxy.y = my; tmpboxz.x = pieceptr[selpiece]->boxz.x + dx; tmpboxz.y = pieceptr[selpiece]->boxz.y + dy; break; case 'z': s = sqrt((double)((mx-pieceptr[selpiece]->boxo.x)* (mx-pieceptr[selpiece]->boxo.x)+(my-pieceptr[selpiece]->boxo.y)* (my-pieceptr[selpiece]->boxo.y))/ (double)((pieceptr[selpiece]->boxz.x-pieceptr[selpiece]->boxo.x)* (pieceptr[selpiece]->boxz.x-pieceptr[selpiece]->boxo.x)+ (pieceptr[selpiece]->boxz.y-pieceptr[selpiece]->boxo.y)* (pieceptr[selpiece]->boxz.y-pieceptr[selpiece]->boxo.y))); a = myatan2((double)(my - pieceptr[selpiece]->boxo.y), (double)(mx - pieceptr[selpiece]->boxo.x)) - myatan2((double) (pieceptr[selpiece]->boxz.y - pieceptr[selpiece]->boxo.y), (double)(pieceptr[selpiece]->boxz.x - pieceptr[selpiece]->boxo.x)); cosa = cos(a); sina = sin(a); Qx = pieceptr[selpiece]->boxo.x + s * (pieceptr[selpiece]->boxo.y * sina - pieceptr[selpiece]->boxo.x * cosa); Qy = pieceptr[selpiece]->boxo.y - s * (pieceptr[selpiece]->boxo.y * cosa + pieceptr[selpiece]->boxo.x * sina); tmpboxo = pieceptr[selpiece]->boxo; /* Structure Assignment */ /* Below, the conditional assignments assure rounding on the */ /* double-to-int conversion rather than truncation */ tmp = s * (pieceptr[selpiece]->boxx.x * cosa - pieceptr[selpiece]->boxx.y * sina) + Qx; tmpboxx.x = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp; tmp = s * (pieceptr[selpiece]->boxx.x * sina + pieceptr[selpiece]->boxx.y * cosa) + Qy; tmpboxx.y = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp; tmp = s * (pieceptr[selpiece]->boxy.x * cosa - pieceptr[selpiece]->boxy.y * sina) + Qx; tmpboxy.x = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp; tmp = s * (pieceptr[selpiece]->boxy.x * sina + pieceptr[selpiece]->boxy.y * cosa) + Qy; tmpboxy.y = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp; tmpboxz.x = tmpboxy.x + tmpboxx.x - tmpboxo.x; tmpboxz.y = tmpboxx.y + tmpboxy.y - tmpboxo.y; break; } /* End switch curcor */ } /*===========================================================================*/ DeleteSelPiece() /* Delete the Selected Piece. If it was the only piece, */ { /* replace it with a Default Piece. Rearrange Piece list */ FreePiece(pieceptr[selpiece]); /* free memory associated with the piece */ for (i = selpiece; i < N; i++) /* close gap in pointer array */ pieceptr[i] = pieceptr[i+1]; if (selpiece == N) selpiece--; N--; if (N == -1) /* if deleted last piece, place a default piece */ AddDefPiece(); } /*==========================================================================*/ ClearIFS() /* Clear IFS variables, memory, and Collage image */ { /* Clear all GZZ bitplanes except Outline plane */ SetWrMsk(r,0xFFFD); /* Write-protect plane 1, Outline */ SetRast(r, 0L); SetWrMsk(r,0xFFFF); for(i = 0; i <= N; i++) FreePiece(pieceptr[i]); N = -1; } /*==========================================================================*/ HiliteGadget(gadgetid) /* frame gadget of given ID# in Red */ /* Implements in software Gadget Mutual Exclude */ /* Because window is GZZ and gadgets are in its border, this routine */ /* Draws the frame directly on the Screen,not in Window. It is quite */ /* non-generic because gadget coordinates are coded in numerically */ int gadgetid; { long i; /*** Define frames as border structures ***/ static SHORT Frame_data[10] = { 0,0, 36,0, 36,15, 0,15, 0,0 }; /* Gadget select frame data */ static struct Border OnFrame_Bdr = { /* Red (Selected) Gadget border */ 0,0, /* LeftEdge, TopEdge */ 4,0,JAM1, /* FrontPen, BackPen, DrawMode */ 5, /* Count */ Frame_data, /* XY */ NULL /* Next Border */ }; static struct Border OffFrame_Bdr = { /* Lt Green (deselected) Gadg border */ 0,0, /* LeftEdge, TopEdge */ 3,0,JAM1, /* FrontPen, BackPen, DrawMode */ 5, /* Count */ Frame_data, /* XY */ NULL /* Next Border */ }; /*** Unframe all gadgets, then frame selected one */ for (i = 0; i <= 3; i++) DrawBorder(scrp, &OffFrame_Bdr, 280L, i*18L+12L); DrawBorder(scrp, &OnFrame_Bdr, 280L, gadgetid*18L+12L); } /*==========================================================================*/ DrawCollage() /* Draw the current IFS as a collage image with */ { /* vector Box of Selected Piece (wipe out old box)*/ /*** Erase all bitplanes except Outline plane */ SetWrMsk(r,0xFFFD); /* Write-protect plane 1, Outline */ SetRast(r, 0L); /* Blit Deselected pieces from piecemaps to plane 0 */ SetWrMsk(r,0x0001); /* Write protect all planes except plane 0 */ SetAPen(r, 1L); for (i = 0; i <= N; i++) if (i != selpiece) /* skip Selected piece */ BltTemplate((char*)pieceptr[i]->piecemap,(long)Window->BorderLeft, (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT); /* Blit Selected Piece from piecemap to plane 2 */ SetWrMsk(r,0x0004); /* Write protect all planes except plane 2 */ SetAPen(r, 4L); BltTemplate((char*)pieceptr[selpiece]->piecemap,(long)Window->BorderLeft, (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT); DrawBox(); /* Draw Vector Box of Selected Piece */ /* Note that Rastport's WrMask is restored within DrawBox() */ } /*==========================================================================*/ double myatan2(y,x) /* atan2() as defined in Aztec C but missing from mf.lib */ double x, y; /* returns arctangent of y/x in range -pi to pi. */ { /* If x=y=0, returns 0, sets errno=EDOM. */ /* Requires #include of errno.h and math.h */ #define PI 3.141592654 if (x == 0.0) { if (y == 0.0) { errno = EDOM; return(0.0); } else x = 1.0e-10; } if (y >= 0.0) { if (x >= 0.0) return(atan(y/x)); /* Quadrant I */ else return(PI - atan(-y/x)); /* Quadrant II */ } else { if (x >= 0.0) return(-atan(-y/x)); /* Quadrant IV */ else return(atan(y/x) - PI); /* Quadrant III */ } } /*==========================================================================*/ ModifyMousePtr(Wind, n) /* Change to custom mouse pointer in Window if n=1, or restore default */ /* arrow and free CHIP RAM if n=-1. Use in only one window at a time! */ struct Window *Wind; int n; { static short *ChipPtr; static struct ViewPort *vprt; static long color17, color18, color19; long GetRGB4(); /*** Define Custom mouse pointer sprite data */ #define ROWS 14 /* Number of Pixel rows in pointer */ static USHORT Mouseptr[] = { 0x0000, 0x0000, /* ++++++++++++++++++ */ 0x00f0, 0x00f0, /* + 3333 + */ 0x03f8, 0x03f8, /* + 3333333 + */ 0x07fc, 0x079c, /* + 333311333 + */ 0x0ffe, 0x0e0e, /* + 33311111333 + */ 0x0ffe, 0x0c06, /* + 33111111133 + */ 0xafff, 0x0c73, /* +1 1 331113331133+ */ 0xafff, 0x0cfb, /* +1 1 331133333133+ */ 0xafff, 0x0ccb, /* +1 1 331133113133+ */ 0xcfff, 0x0cdb, /* +11 331133133133+ */ 0xdfff, 0x18c3, /* +11 3311133111133+ */ 0xffff, 0x1ce7, /* +1113331133311333+ */ 0xfffe, 0x07fe, /* +111113333333333 + */ 0x7ffe, 0x003c, /* + 11111111133331 + */ 0x1fff, 0x0000, /* + 1111111111111+ */ /* ++++++++++++++++++ */ 0x0000, 0x0000 }; if (n == 1 && ChipPtr == NULL) { /*** Get colors of default pointer in Window's screen */ vprt = &(Wind->WScreen->ViewPort); /* ptr to viewport of screen */ color17 = GetRGB4(vprt->ColorMap, 17L); color18 = GetRGB4(vprt->ColorMap, 18L); color19 = GetRGB4(vprt->ColorMap, 19L); /*** Copy sprite data into chip RAM to be accessible to graphic chips */ ChipPtr =(short*)AllocMem((long)((ROWS+2)*4),MEMF_CHIP); /* Alloc block */ CopyMem((char*)Mouseptr,(char*)ChipPtr,(long)((ROWS+2)*4)); /* Copy data*/ /*** Attach custom pointer to window */ SetPointer(Wind, ChipPtr, (long)ROWS, 16L, -1L, -1L); /*** Change custom pointer colors */ SetRGB4(vprt, 17L, 15L, 12L, 10L); SetRGB4(vprt, 18L, 13L, 2L, 2L); SetRGB4(vprt, 19L, 15L, 0L, 0L); } else if (n == -1 && ChipPtr != NULL) { ClearPointer(Wind); /* Reset default pointer and its original colors */ SetRGB4(vprt, 17L, color17>>8, (color17>>4L)&15, color17&15); SetRGB4(vprt, 18L, color18>>8, (color18>>4L)&15, color18&15); SetRGB4(vprt, 19L, color19>>8, (color19>>4L)&15, color19&15); FreeMem(ChipPtr, (long)((ROWS+2)*4)); /* release memory of pointer data */ ChipPtr = NULL; } else DisplayBeep(NULL); /* Illegal call (called twice in a row with same n */ } /*===========================================================================*/ UpdateNumStrings() /* Update values of Coeff window string gadgets from */ { /* selected piece structure. Redraw gadgets if the window is open */ double degr1, degr2; degr1 = pieceptr[selpiece]->r1 / 0.017453293; /* convert radians to degrees*/ degr2 = pieceptr[selpiece]->r2 / 0.017453293; sprintf((char*)NumNumT2coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->f); sprintf((char*)NumNumT1coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->e); sprintf((char*)NumNumR2coeffGadgSIBuff, "%5.lf", degr2); sprintf((char*)NumNumR1coeffGadgSIBuff, "%5.lf", degr1); sprintf((char*)NumNumS2coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->s2); sprintf((char*)NumNumS1coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->s1); sprintf((char*)NumNumPcoeffGadgSIBuff, "%5.3lf", pieceptr[selpiece]->p); sprintf((char*)NumNumDenscoeffGadgSIBuff,"%4.1lf", pieceptr[selpiece]->dens); sprintf((char*)NumNumFcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->f); sprintf((char*)NumNumEcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->e); sprintf((char*)NumNumDcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->d); sprintf((char*)NumNumCcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->c); sprintf((char*)NumNumBcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->b); sprintf((char*)NumNumAcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->a); if (exists_numwindow) RefreshGadgets(&NumGadgetList3, NumWindow, NULL); } /*===========================================================================*/ RecomputeCoeffs(gadgetid) /* Re-Calculate 13 coefficients based on */ int gadgetid; /* change in value of the one in the string gadget with */ { /* the given gadgetid; put new values in string gadgets */ /* Does range checking on entered coefficients; If user */ /* entered an illegal value, will restore previous */ /* value and inform user before returning. */ double a, b, c, d, e, f, s1, s2, r1, r2, t1, t2, dens, p; double sum, p0, det, olddet, olddens; /* Put current coefficient values into temporary variables */ sscanf((char*)NumNumT2coeffGadgSIBuff, "%lf", &t2); sscanf((char*)NumNumT1coeffGadgSIBuff, "%lf", &t1); sscanf((char*)NumNumR2coeffGadgSIBuff, "%lf", &r2); sscanf((char*)NumNumR1coeffGadgSIBuff, "%lf", &r1); sscanf((char*)NumNumS2coeffGadgSIBuff, "%lf", &s2); sscanf((char*)NumNumS1coeffGadgSIBuff, "%lf", &s1); sscanf((char*)NumNumPcoeffGadgSIBuff, "%lf", &p); sscanf((char*)NumNumDenscoeffGadgSIBuff, "%lf", &dens); sscanf((char*)NumNumFcoeffGadgSIBuff, "%lf", &f); sscanf((char*)NumNumEcoeffGadgSIBuff, "%lf", &e); sscanf((char*)NumNumDcoeffGadgSIBuff, "%lf", &d); sscanf((char*)NumNumCcoeffGadgSIBuff, "%lf", &c); sscanf((char*)NumNumBcoeffGadgSIBuff, "%lf", &b); sscanf((char*)NumNumAcoeffGadgSIBuff, "%lf", &a); while (r1 > 180. || r1 < -180.) /* Get angles to range -180 => +180 */ r1 = (r1 > 0.) ? r1 - 360. : r1 + 360.; while (r2 > 180. || r2 < -180.) r2 = (r2 > 0.) ? r2 - 360. : r2 + 360.; r1 = r1*0.017453293; /* convert degrees to radians*/ r2 = r2*0.017453293; /*** Check that all coefficients are in legal ranges */ if (a < -1. || a > 1. || b < -1. || b > 1. || c < -1. || c > 1. || d < -1. || d > 1. || e < -0.25 || e > 1. || f < -0.25 || f > 1. || s1 < -1. || s1 > 1. || s2 < -1. || s2 > 1. || dens <= 0. || t1 < -0.25 || t1 > 1. || t2 < -0.25 || t2 > 1. || p <= 0. || p > 1.) { ShowError("Coefficient out of Range!"); a = pieceptr[selpiece]->a; /*** Undo the error */ b = pieceptr[selpiece]->b; c = pieceptr[selpiece]->c; d = pieceptr[selpiece]->d; e = pieceptr[selpiece]->e; f = pieceptr[selpiece]->f; p = pieceptr[selpiece]->p; dens = pieceptr[selpiece]->dens; s1 = pieceptr[selpiece]->s1; s2 = pieceptr[selpiece]->s2; r1 = pieceptr[selpiece]->r1; r2 = pieceptr[selpiece]->r2; t1 = pieceptr[selpiece]->e; t2 = pieceptr[selpiece]->f; } det = fabs(a * d - b * c); if (det == 0.0) det = 0.01; sum = -(pieceptr[selpiece]->dens * pieceptr[selpiece]->det) + dens * det; for (i = 0; i <= N; i++) sum = sum + pieceptr[i]->dens * pieceptr[i]->det; if (gadgetid <= 26) { /* id's 21 thru 26, matrix coeffs a - f */ s1 = sqrt(a*a + c*c); s2 = sqrt(b*b + d*d); r1 = myatan2(c, a); r2 = myatan2(-b, d); t1 = e; t2 = f; p = (dens * det)/sum; } if (gadgetid == 27) { /* density coefficient */ p = (dens * det)/sum; } if (gadgetid == 28) { /* probability coefficient */ olddens = dens; p0 = det / (sum - olddens*det + det); /* What p SHOULD be with dens == 1 */ dens = p / p0; p = (dens * det)/(sum - olddens*det + dens*det); } if (gadgetid >= 29) { /* id's 29 thru 34, geometrical coeffs s1 thru t2 */ olddet = fabs(a * d - b * c); if (olddet == 0.0) olddet = 0.01; a = s1 * cos(r1); b = -s2 * sin(r2); c = s1 * sin(r1); d = s2 * cos(r2); e = t1; f = t2; det = fabs(a * d - b * c); /*** recompute det and probability */ if (det == 0.0) det = 0.01; sum = sum - olddet*dens + det*dens; p = (dens * det)/sum; } r1 = r1 / 0.017453293; /* convert radians to degrees */ r2 = r2 / 0.017453293; /* Put resultant coefficient values into their string gadgets */ sprintf((char*)NumNumT2coeffGadgSIBuff, "%5.2lf", f); sprintf((char*)NumNumT1coeffGadgSIBuff, "%5.2lf", e); sprintf((char*)NumNumR2coeffGadgSIBuff, "%5.lf", r2); sprintf((char*)NumNumR1coeffGadgSIBuff, "%5.lf", r1); sprintf((char*)NumNumS2coeffGadgSIBuff, "%5.2lf", s2); sprintf((char*)NumNumS1coeffGadgSIBuff, "%5.2lf", s1); sprintf((char*)NumNumPcoeffGadgSIBuff, "%5.3lf", p); sprintf((char*)NumNumDenscoeffGadgSIBuff, "%4.1lf", dens); sprintf((char*)NumNumFcoeffGadgSIBuff, "%5.2lf", f); sprintf((char*)NumNumEcoeffGadgSIBuff, "%5.2lf", e); sprintf((char*)NumNumDcoeffGadgSIBuff, "%5.2lf", d); sprintf((char*)NumNumCcoeffGadgSIBuff, "%5.2lf", c); sprintf((char*)NumNumBcoeffGadgSIBuff, "%5.2lf", b); sprintf((char*)NumNumAcoeffGadgSIBuff, "%5.2lf", a); return; } /*==========================================================================*/ ShowError(text) /* Show error message in a Window, return on user */ /* clicking in the OK gadget. Text string must not */ /* exceed Window's width minus two characters. */ /* Window is used and not a requester so it is in front */ /* of coeffs window & GZZ Border of main Window */ char *text; /* The message to be displayed */ { struct Message * GetMsg(); void ReplyMsg(); struct IntuiMessage *message; int gadgetid; struct Window *ErrWindow, *OpenWindow(); #define TEXTCOLOR 3 /* Text color for message and "OK" */ #define BGCOLOR 14 /* Color for Window and Gadget background */ #define GADGBDRCOLOR 3 /* Color for border of "OK" Gadget */ #define BDRCOLOR 3 /* Color for border of Error-window */ /*** Initialize Error-window structures */ static struct IntuiText OKGadgetMsg = { TEXTCOLOR, BGCOLOR, /* FrontPen, BackPen */ JAM1, /* DrawMode */ 4,3, /* LeftEdge, TopEdge */ NULL, /* ITextFont */ (UBYTE*)"OK", /* IText */ NULL /* NextText */ }; static SHORT OKGadgetCoord[18] = { 0,0, 23,0, 23,12, 0,12, 0,0, 24,0, 24,12, -1,12, -1,0 }; /* NOTE: Second line above gives 2-pixel-wide border for hires screen */ static struct Border OKGadgetBdr = { 0,0, /* LeftEdge, TopEdge */ GADGBDRCOLOR, BGCOLOR, JAM1, /* FrontPen, BackPen, DrawMode */ 9, /* Count */ &OKGadgetCoord[0], /* XY */ NULL /* NextBorder */ }; static struct Gadget OKGadget = { NULL, /* NextGadget */ -40,35,24,13, /* LeftEdge, TopEdge, Width, Height */ GADGHCOMP | GRELRIGHT, /* Flags */ ENDGADGET | RELVERIFY, /* Activation */ BOOLGADGET, /* GadgetType */ (APTR)&OKGadgetBdr, /* GadgetRender */ NULL, /* SelectRender */ &OKGadgetMsg, /* GadgetText */ 0, /* MutualExclude */ NULL, /* SpecialInfo */ 99, /* GadgetID */ NULL /* UserData */ }; static SHORT BdrCoords[10] = { 2,2, 0,2, 0,57, 2,57, 2,2 }; /* 0's to be replaced later */ static struct Border Bdr = { 0, 0, /* LeftEdge, TopEdge */ BDRCOLOR, BGCOLOR, JAM1, /* FrontPen, BackPen, DrawMode */ 5, /* Count */ &BdrCoords[0], /* XY */ NULL /* NextBorder */ }; static struct IntuiText ErrText = { TEXTCOLOR, BGCOLOR, JAM1, /* FrontPen, BackPen, DrawMode */ 8, 18, /* LeftEdge, TopEdge */ NULL, /* ITextFont */ NULL, /* IText -- Will be filled later */ NULL, /* NextText */ }; static struct Image ErrWinBgnd = { /* Just a filled rectangle */ 0, 0, /* LeftEdge, TopEdge */ 0, 60, 0, /* Width,Height, Depth */ NULL, /* ImageData */ 0x0, 0xE, /* PlanePick, PlaneOnOff */ NULL /* NextImage */ }; static struct NewWindow ErrNewWindow = { 0,60, /* window XY origin relative to TopLeft of screen */ 0,60, /* window width and height */ TEXTCOLOR,BDRCOLOR, /* detail and block pens */ GADGETUP, /* IDCMP flags */ ACTIVATE|SIMPLE_REFRESH|BORDERLESS, /* other window flags */ &OKGadget, /* first gadget in gadget list */ NULL, /* custom CHECKMARK imagery */ NULL, /* window title */ NULL, /* custom screen pointer */ NULL, /* custom bitmap */ 5,5, /* minimum width and height */ -1,-1, /* maximum width and height */ CUSTOMSCREEN /* destination screen type */ }; /*** Compute and fill in text and window-dependent stuff */ ErrNewWindow.Screen = Screen; ErrNewWindow.Width = strlen(text) * 8 + 16; if (ErrNewWindow.Width < 44) ErrNewWindow.Width = 44; ErrWinBgnd.Width = ErrNewWindow.Width; ErrNewWindow.LeftEdge = (320 - ErrNewWindow.Width)/2; if (ErrNewWindow.LeftEdge < 0) ErrNewWindow.LeftEdge = 0; BdrCoords[2] = BdrCoords[4] = ErrNewWindow.Width - 3; ErrText.IText = (UBYTE*)text; if (Window->WScreen->Width = 320) /* If Lo-res -- remove vert. double line */ OKGadgetBdr.Count = 5; else OKGadgetBdr.Count = 9; /*** Display the Error-Window */ if ((ErrWindow = OpenWindow(&ErrNewWindow)) == NULL) { printf("\nCouldn't open Error Message window!!!\n"); CloseAll(); } DrawImage(ErrWindow->RPort, &ErrWinBgnd, 0L, 0L); PrintIText(ErrWindow->RPort, &ErrText, 0L, 0L); DrawBorder(ErrWindow->RPort, &Bdr, 0L, 0L); RefreshGadgets(&OKGadget, ErrWindow, NULL); /* Because DrawImage() obscured the OK Gadget image */ /*** Wait until user clicks the Error-window's "OK" Gadget */ gadgetid = 9999; while (gadgetid != 99) { if (message = (struct IntuiMessage *)GetMsg(ErrWindow->UserPort)) { gadgetid = ((struct Gadget *)(message->IAddress))->GadgetID; ReplyMsg(message); } } CloseWindow(ErrWindow); } /*===========================================================================*/ OpenImageScreen(width, height, depth) /* Open a Custom screen & window for */ /* the Image; Set exists_image flag */ int width, height, depth; { struct NewScreen ns; struct Screen *OpenScreen(); struct NewWindow nw; struct Window *OpenWindow(); long numcols; /* # of colors in screen */ UWORD *colortableptr[5]; /* Array of Pointers to screen colortables */ /* Note: element 0 is not used! */ static UWORD colortable1[] = /*** Colors for 1-bitplane screen */ {0x000, 0x068, 0x0AF, 0x5CF}; static UWORD colortable2[] = /*** Colors for 2-bitplane screen */ {0x000, 0x057, 0x07B, 0xAF}; static UWORD colortable3[] = /*** Colors for 3-bitplane screen */ {0x000, 0x034, 0x046, 0x068, 0x06A, 0x08C, 0x09E, 0x0AF}; static UWORD colortable4[] = /*** Colors for 4-bitplane screen */ {0x000, 0x011, 0x012, 0x023, 0x034, 0x045, 0x046, 0x057, 0x068, 0x069, 0x06A, 0x07B, 0x08C, 0x08D, 0x09E, 0x0AF}; colortableptr[1] = colortable1; colortableptr[2] = colortable2; colortableptr[3] = colortable3; colortableptr[4] = colortable4; ns.LeftEdge = 0; /*** Initialize NewScreen structure */ ns.TopEdge = 0; ns.Width = width; ns.Height = height; ns.Depth = (depth == 1) ? 2 : depth; ns.DetailPen = 3; /* Colour of text in screen title bar */ ns.BlockPen = 1; /* Colour of screen Title Bar */ ns.ViewModes = ((width == 640) ? HIRES:0) | ((height == 400) ? INTERLACE:0); ns.Type = CUSTOMSCREEN; ns.Font = &TOPAZ80; ns.DefaultTitle = (UBYTE*)" Click in screen to stop rendering"; ns.Gadgets = NULL; ns.CustomBitMap = NULL; switch (depth) { /* Customize Image Title and Border colors to depth */ case (1): ns.DetailPen = 3; ns.BlockPen = 1; break; case (2): ns.DetailPen = 3; ns.BlockPen = 1; break; case (3): ns.DetailPen = 7; ns.BlockPen = 3; break; case (4): ns.DetailPen = 15; ns.BlockPen = 7; break; } nw.LeftEdge = 0; /*** Initialize NewWindow structure */ nw.TopEdge = 0; nw.Width = width; nw.Height = height; nw.DetailPen = ns.DetailPen; /* Title text color */ nw.BlockPen = ns.BlockPen; /* Title bar background */ nw.IDCMPFlags = MOUSEBUTTONS; nw.Flags = ACTIVATE|GIMMEZEROZERO|SIMPLE_REFRESH|BACKDROP; nw.FirstGadget = NULL; nw.CheckMark = NULL; nw.Title = (UBYTE*)" "; /* Never seen - stays behind screen title bar */ nw.Screen = NULL; /* Will set it to ImageScreen after the screen is opened */ nw.BitMap = NULL; nw.MinWidth = 0; nw.MinHeight = 0; nw.MaxWidth = 0; nw.MaxHeight = 0; nw.Type = CUSTOMSCREEN; /*** Open the screen */ if ((ImageScreen = (struct Screen *) OpenScreen(&ns)) == NULL) { ShowError("Couldn't open Image screen!"); return; } /* Link Custom colours defined above to screen's ViewPort */ numcols = 1 << ns.Depth; LoadRGB4(&(ImageScreen->ViewPort), colortableptr[depth], numcols); /*** Open the Window */ nw.Screen = ImageScreen; if ((ImageWindow = (struct Window *) OpenWindow(&nw)) == NULL) { CloseScreen(ImageScreen); ShowError("Couldn't open Image window!"); return; } SetWindowTitles(ImageWindow,-1L, (char *)" Click in screen to stop rendering"); exists_image = 1; } /*===========================================================================*/ CloseImageScreen() /* Close image screen & Window, reset exists_image flag */ { struct Message * GetMsg(); void ReplyMsg(); struct IntuiMessage *message; /* Empty ImageWindow IDCMP queue if any */ while (message = (struct IntuiMessage *)GetMsg(ImageWindow->UserPort)) ReplyMsg(message); CloseWindow(ImageWindow); CloseScreen(ImageScreen); exists_image = 0; } /*===========================================================================*/ DoDemo() /* Demonstrate IFS process, then NEW and return */ { if (SetMode(OUTLINE) == 1) { SetRast(r, 0L); exists_outline = 0; } else /* User clicked CANCEL in IFS WILL BE LOST requester - Abort demo */ return; ModifyIDCMP(Window, NULL); /* Disable Editor Window IDCMP during Demo */ ClearMenuStrip(Window); /* remove main window menus during demo */ AboutText(DEMOTEXT); /* Show explanation of demo in text screen */ ModifyMousePtr(Window, 1); /* Bring up 'ZZ' pointer */ /* Draw outline of Sierpinski Triangle */ SetDrMd(r, JAM1); for (i = 0; i < 100000; i++) ; /* Delay */ Move(r, 138L, 24L); Draw(r, 24L, 172L); for (i = 0; i < 100000; i++) ; /* Delay */ Draw(r, 238L, 131L); for (i = 0; i < 100000; i++) ; /* Delay */ Draw(r, 138L, 24L); for (i = 0; i < 100000; i++) ; /* Delay */ Move(r, 82L, 98L); Draw(r, 188L, 77L); for (i = 0; i < 100000; i++) ; /* Delay */ Draw(r, 130L, 152L); for (i = 0; i < 100000; i++) ; /* Delay */ Draw(r, 82L, 98L); exists_outline = 1; for (i = 0; i < 150000; i++) /* Delay before entering Collage Editor */ ; /*** Place 3 Pieces to form Collage */ SetMode(COLLAGE); for (i = 0; i < 100000; i++) ; /* Delay */ ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ AddDefPiece(); DrawCollage(); pieceptr[0]->e = 0.25; pieceptr[0]->f = 0.045; pieceptr[0]->dens = 1.0; ComputeBoxCorners(pieceptr[0]); ComputeProbs(); for (i = 0; i < 100000; i++) ; /* Delay */ TransformPiece(outlinebufptr, pieceptr[0]->piecemap, pieceptr[0]); DrawCollage(); for (i = 0; i < 100000; i++) ; /* Delay */ AddDefPiece(); DrawCollage(); pieceptr[1]->e = 0.045; pieceptr[1]->f = 0.315; pieceptr[1]->dens = 0.4; ComputeBoxCorners(pieceptr[1]); ComputeProbs(); for (i = 0; i < 100000; i++) ; /* Delay */ TransformPiece(outlinebufptr, pieceptr[1]->piecemap, pieceptr[1]); DrawCollage(); for (i = 0; i < 100000; i++) ; /* Delay */ AddDefPiece(); DrawCollage(); pieceptr[2]->e = 0.43; pieceptr[2]->f = 0.24; pieceptr[2]->dens = 0.7; ComputeBoxCorners(pieceptr[2]); ComputeProbs(); for (i = 0; i < 100000; i++) ; /* Delay */ TransformPiece(outlinebufptr, pieceptr[2]->piecemap, pieceptr[2]); DrawCollage(); for (i = 0; i < 300000; i++) /* Delay before rendering */ ; RenderImage(0); /* Render, until user clicks in image screen */ /*** After rendering ended, do a NEW (without IFS WILL BE LOST requester) */ ClearIFS(); RemoveGList(Window, &CollageGadgetList2, 6L); AddGList(Window, &GadgetList1, 0L, 6L, NULL); /* change gadgets */ SetDrMd(scrp, JAM1); SetAPen(scrp, 0L); RectFill(scrp, 280L, 12L, 316L, 117L); /* Blot out old gadget images */ RefreshGadgets(&GadgetList1, Window, NULL); OffMenu(Window, NOITEM<<5 | 2); /* disable render menu and its items */ SetWindowTitles(Window, -1L, " Outline Editor"); drawmode = FREEHAND; HiliteGadget(0); SetAPen(r, 2L); SetRast(r, 0L); exists_outline = 0; CloseImageScreen(); ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK); /* Restore IDCMP */ SetMenuStrip(Window, &MenuList1); /* Restore menu strip */ } /*===========================================================================*/ ComputeBoxCorners(pcptr) /* Compute Vector Box corners from Piece coeffs */ struct piece *pcptr; { pcptr->boxo.x = pcptr->e * GZZWIDTH; pcptr->boxo.y = pcptr->f * GZZWIDTH; /* sic! */ pcptr->boxx.x = (pcptr->a + pcptr->e) * GZZWIDTH; pcptr->boxx.y = (pcptr->c + pcptr->f) * GZZWIDTH; pcptr->boxy.x = (pcptr->b*GZZHEIGHT/GZZWIDTH + pcptr->e) * GZZWIDTH; pcptr->boxy.y = (pcptr->d*GZZHEIGHT/GZZWIDTH + pcptr->f) * GZZWIDTH; pcptr->boxz.x = pcptr->boxy.x + pcptr->boxx.x - pcptr->boxo.x; pcptr->boxz.y = pcptr->boxy.y + pcptr->boxx.y - pcptr->boxo.y; } /*===========================================================================*/ LoadILBM(fnam, dest) /* Load an Image from IFF file; If dest = 1, */ char *fnam; /* put image and colormap in Image Screen; */ int dest; /* if dest = 0, put plane 0 of image in Outline */ { struct BitMap picbitmap = {0}; /* Empty BitMap structure to "Fill" */ ILBMFrame iFrame; /* ILBM Frame to be used by Reader Routines */ LONG file; /* File handle [we use AmigaDOS Open(), not Manx open()] */ LONG iffp; char *blitbuffer; /* Needed for the Blit operation only */ struct BitMap tmpbitmap; /* used for 2-step blit to Outline */ #define MIN(a,b) ((a)<(b)?(a):(b)) /* Open the File to read */ if ((file = Open(fnam, MODE_OLDFILE)) == NULL) { ShowError("Can't open file!"); return; } ModifyMousePtr(Window, 1); /* Bring up 'ZZ' pointer */ /* Use EA IFF routine ReadPicture() to Read image from ILBM file into */ /* memory, modify picbitmap accordingly */ iffp = ReadPicture(file,&picbitmap,&iFrame,ChipAlloc); Close(file); if (iffp != IFF_DONE) { /* React to error message from EA routine, if any */ ShowError(IFFPMessages[-iffp]); if (picbitmap.Planes[0]) /* free planes allocated by ReadPicture, if any */ RemFree(picbitmap.Planes[0]); ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ return; } /*** Now image is in memory; copy it to the proper destination bitmap */ switch (dest) { case 0: /*** Copy plane 0 of loaded image to Outline (plane 1 in Window) */ SetRast(r, 0L); /* Erase old outline, set drawmode to FREEHAND */ if(drawmode == ERASE || drawmode == FILL) { drawmode = FREEHAND; SetAPen(r,2L); HiliteGadget(0); } /* NOTE: Must use 2 steps because must blit (possibly part of) loaded */ /* image of arbitrary size, adjusting for GZZ borders, and from plane 0 */ /* to plane 1. Neither BltBitMap nor BltTemplate can do this in 1 step */ /* Step 1: Blit plane 0 of loaded image to temp bitmap */ /* (uses outline buffer as its single bitplane) */ InitBitMap(&tmpbitmap, 1L, WIDTH, HEIGHT); tmpbitmap.Planes[0] = (PLANEPTR)outlinebufptr; blitbuffer = (char *)AllocMem(80L,MEMF_CHIP); /* Allocate temp buffer */ BltBitMap(&picbitmap, 0L, 0L, &tmpbitmap, 0L, 0L, MIN(WIDTH, picbitmap.BytesPerRow*8L), MIN(HEIGHT, picbitmap.Rows), 192L, 0x1, blitbuffer); /* 192L is minterm for Direct Copy; 0x1 is mask, will copy plane 0 */ FreeMem(blitbuffer, 80L); /* Step 2: Blit the temporary bitplane from Outline Buffer to plane 1 */ /* of GZZ Window */ SetAPen(r, 2L); SetDrMd(r, JAM1); SetWrMsk(r,0x0002); /* Write protect all planes except plane 1 */ BltTemplate((char*)outlinebufptr, 0L, WIDTH/8L, Window->RPort, 0L, 0L, MIN(GZZWIDTH, picbitmap.BytesPerRow*8L), MIN(GZZHEIGHT, picbitmap.Rows)); SetWrMsk(r,0xFFFF); exists_outline = 1; break; case 1: /*** Set up a (new) Image Screen and blit loaded image into it */ if (exists_image) CloseImageScreen(); OpenImageScreen(iFrame.bmHdr.pageWidth, iFrame.bmHdr.pageHeight, picbitmap.Depth); /* Link colours read in from file to screen's ViewPort */ LoadRGB4(&(ImageScreen->ViewPort), iFrame.colorMap, 1<RPort->BitMap, 2L, 11L, MIN(ImageWindow->Width-4L,picbitmap.BytesPerRow*8L-2L), MIN(ImageWindow->Height-13L,picbitmap.Rows-11L), 192L, 0xFF, blitbuffer); /* 192L is minterm for Direct Copy; 0xFF is mask, will copy all planes */ /* 2L, 11L, 4L, 13L cause The blit to skip the window borders */ FreeMem(blitbuffer, 80L); exists_image = 1; break; } /* End switch dest */ if (picbitmap.Planes[0]) /* free planes allocated by ReadPicture, if any */ RemFree(picbitmap.Planes[0]); ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ } /*==========================================================================*/ AboutText(whichtext) /* Opens the text screen and displays in it */ int whichtext; /* the selected About text or IFS code table */ /* according to whichtext. Closes on click in screen */ /* whichtext can be: CODES, IFS_THEORY, HELP1, HELP2, AUTHOR, DEMOTEXT*/ { struct Screen *TextScreen; struct Window *TextWindow; struct NewScreen ns; struct Screen *OpenScreen(); struct NewWindow nw; struct Window *OpenWindow(); struct Message * GetMsg(); struct IntuiMessage *message; static struct IntuiText RedText = { 3, 0, JAM1, /* FrontPen, BackPen, DrawMode */ 0, 0, /* LeftEdge, TopEdge */ &TOPAZ80, /* ITextFont */ (UBYTE*)"Click Mouse to Continue", NULL /* IText, NextText */ }; char codelinebuf[78]; /* Buffer for line of codes in CODES display */ static struct IntuiText CodesText = { /* Codes line for display */ 1, 0, JAM1, /* FrontPen, BackPen, DrawMode */ 0, 0, /* LeftEdge, TopEdge */ &TOPAZ80, /* ITextFont */ NULL, NULL /* IText, NextText */ }; /*** Open a window in a custom Hires screen for text display */ ns.LeftEdge = 0; /*** Initialize NewScreen structure */ ns.TopEdge = 0; ns.Width = 640; ns.Height = 200; ns.Depth = 2; ns.DetailPen = 0; /* Colour of text in screen title bar */ ns.BlockPen = 1; /* Colour of screen Title Bar */ ns.ViewModes = HIRES; ns.Type = CUSTOMSCREEN; ns.Font = &TOPAZ80; ns.DefaultTitle = NULL; ns.Gadgets = NULL; ns.CustomBitMap = NULL; nw.LeftEdge = 0; /*** Initialize NewWindow structure */ nw.TopEdge = 0; nw.Width = 640; nw.Height = 200; nw.DetailPen = 0; /* Menu title text color */ nw.BlockPen = 1; /* Menu item box and title bar background */ nw.IDCMPFlags = MOUSEBUTTONS; nw.Flags = ACTIVATE|SIMPLE_REFRESH; nw.FirstGadget = NULL; nw.CheckMark = NULL; nw.Title = (UBYTE*)" "; nw.Screen = NULL; /* Will set it to TextScreen after the screen is opened */ nw.BitMap = NULL; nw.MinWidth = 0; nw.MinHeight = 0; nw.MaxWidth = 0; nw.MaxHeight = 0; nw.Type = CUSTOMSCREEN; /*** Open the screen */ if ((TextScreen = (struct Screen *) OpenScreen(&ns)) == NULL) { ShowError("Couldn't open Text screen!!!"); return; } /*** Open the Window */ nw.Screen = TextScreen; if ((TextWindow = (struct Window *) OpenWindow(&nw)) == NULL) { CloseScreen(TextScreen); ShowError("Couldn't open Text window!!!"); return; } Textrp = (TextWindow->RPort); /* Window's rastport, used by p() */ p(NULL); /* reset screen print routine to top of screen */ p(" "); /* skip 2 lines to clear text window title bar */ p(" "); switch (whichtext) { /**** Print appropreiate text into window */ case IFS_THEORY: SetWindowTitles(TextWindow, " IFS Theory Background", -1L); p(" IFS is a method developed by Michael F. Barnsley that allows one to en\ code a"); p(" Fractal as a small set of numbers, the coefficients of an Iterated Funct\ ion"); p(" System (IFS) Code, and later to reconstruct it from those numbers. An IF\ S Code"); p(" is a set of Contractive Affine Transformations, which are transformation\ s of"); p(" the plane that combine a Linear Transformation (consisting of combinatio\ ns of"); p(" stretching and rotation) and a Translation of the origin. Each such affi\ ne"); p(" transformation can be represented by six real numbers. In addition, with\ each"); p(" transformation is associated a probability value."); p(" "); p(" To derive the IFS Code for a given Fractal image, one must find a set \ of"); p(" transformations of the image - reduced, deformed copies of it - that whe\ n"); p(" taken together cover the original image. To reconstruct the image, one \ starts"); p(" with any point in the plane, picks at random one of the transformations"); p(" defined above, applies it to the point, and draws the resulting point. T\ o this"); p(" new point one applies the same procedure, and so on. The probabilities d\ efine"); p(" how often each transformation will be picked. The 'Collage Theorem' assu\ res"); p(" that this process will in fact reconstruct the original Fractal."); p(" "); p(" For more detailed information on the theory and use of IFS, refer to:"); p(" - Barnsley and Sloan: A Better Way to Compress Images, BYTE, Jan. 88\ ."); p(" - Peitgen and Saupe, eds.: The Science of Fractal Images, Chapter 5."); break; case HELP1: SetWindowTitles(TextWindow, " A Summary of Program Operation", -1L); p(" This program requires two steps to generate an IFS Code for a Fractal:"); p(" "); p(" a. OUTLINE EDITOR: This step allows you to draw the approximate Outline \ of"); p(" the planned Fractal. The user interface is a simple Paint program, with"); p(" Freestyle, Lines, Erase (XXX), Fill and Clear gadgets. Note that the le\ ss"); p(" pixels you draw, the faster things will move later, in the Collage Edit\ or"); p(" phase. When your Outline is finished, click DONE to enter the Collage E\ ditor."); p(" "); p(" b. COLLAGE EDITOR: Here you create the Collage, by defining the affine"); p(" transformations ('Pieces') to cover the Outline with. When you enter th\ is"); p(" mode, you see your Outline, and one Default transformation - a half-siz\ ed"); p(" copy of this Outline, drawn in red. The box around it is the image of t\ he"); p(" screen boundary under the transformation. You can modify the Piece by"); p(" dragging any corner of this box with the mouse. The corner marked 'O' w\ ill"); p(" translate the Origin; 'X' and 'Y' will stretch and rotate these axes; T\ he"); p(" last corner will enlarge and rotate the Piece without deforming it. Cli\ ck"); p(" ADD to add a new Default Piece, or DUP to add a duplicate of the curren\ t"); p(" Piece. The Piece drawn in red is the 'Selected' one; all editing action\ s"); p(" apply to it. Clicking SEL repeatedly will make one Piece after another \ the"); p(" Selected one, allowing you to modify (or delete, by clicking DEL) previ\ ously"); p(" defined Pieces."); break; case HELP2: SetWindowTitles(TextWindow, " A Summary of Program Operation (Continued)", -1L); p(" You can click the NUM gadget to get a numeric display of the Selected"); p(" transformation's coefficients (a-f, p), as well as the associated Proba\ bility"); p(" Density (probability per unit area) and Scalings, Rotations and Transla\ tions."); p(" You can modify any one of these values in their string gadgets, and cli\ ck"); p(" ENTER to make the changes take effect. To quit the numeric window, clic\ k its"); p(" Close gadget."); p(" "); p(" When you've covered the Outline fully with Pieces, use the Render men\ u"); p(" to generate the Attractor of the IFS in the resolution and gray level c\ ount"); p(" of your choice."); p(" "); p(" The Optimize menu item will modify the current IFS so that its Attrac\ tor"); p(" will fill the screen; it will also generate an exact Collage for it, by\ using"); p(" the Attractor itself as an Outline."); p(" "); p(" File I/O menuitems allow you to save the IFS codes or the Image to d\ isk,"); p(" to load IFS Codes from files saved by IFSLab and by most other IFS prog\ rams,"); p(" and to load any Non-HAM IFF image file into the Image screen or as the"); p(" Outline."); break; case AUTHOR: SetWindowTitles(TextWindow, " Author Information", -1L); p(" "); p(" "); p(" "); p(" "); p(" IFS Lab written by Nathan Zeldes"); p(" "); p(" "); p(" Copyright (C) 1992 by N. Zeldes. All Rights Reserved. "); p(" "); p(" Thanks to C.W. Fox and Bruce Dawson for the File Requester"); p(" "); p(" "); p(" "); p(" "); p(" "); p(" "); break; case DEMOTEXT: SetWindowTitles(TextWindow, " A Demonstration of IFS in Action", -1L); p(" "); p(" This demo will define and render the Sierpinski Triangle, a well known"); p(" fractal."); p(" "); p(" The program will first enter the Outline Editor and draw an Outline of\ the"); p(" Sierpinski triangle. After a brief delay it will switch to the Collage E\ ditor"); p(" and show how the Outline can be covered by three half-sized copies of it\ self."); p(" When it has built this Collage, the program will initiate rendering of t\ he"); p(" fractal. Note that this demo intentionally assigns unequal probabilities\ to"); p(" the three transformations, which causes the uneven 'shading' effect in t\ he"); p(" rendered image."); p(" "); p(" To start the demo, click the left mouse button. Do no more until the"); p(" rendering begins. When you've had your fill of the beauty of the emergin\ g"); p(" image, click twice in the Image screen to exit the demo."); break; case CODES: /* Display Codes Table */ SetWindowTitles(TextWindow, " IFS Code Coefficients", -1L); p(" A B C D E F P Dens S1 S2\ R1 R2 "); if (N < MAX_N) p(" ------ ------ ------ ------ ----- ----- ----- ------- ----- -----\ ----- ----- "); /* The underline is omitted to allow display of 20 pieces */ for(i = 0; i <= N; i++) { /* Put lines of coefficients in text window */ sprintf(codelinebuf, " % 6.3lf % 6.3lf % 6.3lf % 6.3lf %5.3lf %5.3lf\ %5.3lf %7.3lf% 5.3lf %5.3lf % 4.0lf % 4.0lf", pieceptr[i]->a, pieceptr[i]->b, pieceptr[i]->c, pieceptr[i]->d, pieceptr[i]->e, pieceptr[i]->f, pieceptr[i]->p, pieceptr[i]->dens, pieceptr[i]->s1, pieceptr[i]->s2, pieceptr[i]->r1/0.017453293, pieceptr[i]->r2/0.017453293); CodesText.FrontPen = (i == selpiece) ? 3 : 1; /* Selected piece in red */ CodesText.IText = (UBYTE*)codelinebuf; CodesText.TopEdge = (N < MAX_N) ? 8L * i + 32L : 8L * i + 24L; PrintIText(Textrp, &CodesText, 0L, 0L); } break; } /* End switch */ PrintIText(Textrp, &RedText, 450L, 190L); /* 'Click Mouse...' text */ /*** Wait with text displayed, poll IDCMP until you get a SELECTDOWN */ while (!(message = (struct IntuiMessage *)GetMsg(TextWindow->UserPort))) ; /* here any event must be a SELECTDOWN due to IDCMP flag definition */ ReplyMsg(message); while (!(message = (struct IntuiMessage *)GetMsg(TextWindow->UserPort))) ; /* get rid of SELECTUP message following the SELECTDOWN */ ReplyMsg(message); CloseWindow(TextWindow); /* Close text display and return */ CloseScreen(TextScreen); } /*===========================================================================*/ PutDefOutline() /* Place default Outline in Outline Bitmap */ { ModifyMousePtr(Window, 1); /* Bring up 'ZZ' pointer */ SetDrMd(r, JAM1); SetAPen(r, 2L); SetOPen(r, 2L); SetWrMsk(r,0x0002); /* Write protect all planes except plane 1 */ SetRast(r, 0L); DrawCircle(r, 138L, 93L, 22L); /* Face outline */ DrawCircle(r, 138L, 93L, 13L); /* To become mouth outline */ SetAPen(r, 0L); SetOPen(r, 0L); RectFill(r, 124L, 80L, 152L, 100L); /* Blot out top part of mouth circle */ SetAPen(r, 2L); SetOPen(r, 2L); DrawCircle(r, 129L, 85L, 3L); /* Left Eye */ DrawCircle(r, 147L, 85L, 3L); /* Right Eye */ exists_outline = 1; SetWrMsk(r,0xFFFF); ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ } /*===========================================================================*/ TransformPiece(source, dest, pc) /* Apply transformation of Piece */ /* To image in source bitplane, put result in dest piecemap. */ /* Return 1 if done OK, return 0 if a pixel overflowed plane boudary. */ /* Any overflow pixels are not put into the destination piecemap. */ /* Skips title bar and borders of source bitplane properly. */ /* Replaces doubles with scaled-up integers for speed. */ UBYTE *source; /* Pointer to 1st byte in a single Window-sized Bitplane */ UBYTE *dest; /* Pointer to first byte in target piecemap */ struct piece *pc; /* pointer to piece structure containing trans' coeffs */ { #define LEFTBDRBITS 4 /* Width in BITS of left border of Window */ #define TOPBDRBYTES 440 /* bytes within Top Border of Window */ #define RIGHTBDRBYTES 5 /* bytes within right Border (must be integral) */ #define MAPROWBYTES 35 /* Number of bytes in a net row of piecemap */ #define MAPROWBYTESPLUS 40 /* Number of bytes in a gross row of piecemap */ #define PIECEMAPWIDTH GZZWIDTH+Window->BorderLeft UBYTE *srcbyte; /* address of current byte in source bitplane */ int bitpos; /* bit position in byte (leftmost bit = 0) */ int rowbyte; /* number of current source byte within row */ int srcx, srcy, destx, desty; /* Source and destination pixel X-Y coords */ long ai, bi, ci, di, ei, fi; /* Scaled integer transformation coefficients */ long lbdri; /* Scaled LEFTBDRBITS */ int overflowflag = 0; ModifyMousePtr(Window, 1); /* Bring up 'ZZ' pointer */ /* Erase piecemap */ BltClear((char*)dest, (long)RASSIZE(WIDTH,GZZHEIGHT), 1L); /*** Convert coefficients to scaled integers */ ai = (long)(pc->a * 1000000); bi = (long)(pc->b * 1000000); ci = (long)(pc->c * 1000000); di = (long)(pc->d * 1000000); ei = (long)(pc->e * 1000000 * GZZWIDTH); fi = (long)(pc->f * 1000000 * GZZWIDTH); lbdri = (long)(LEFTBDRBITS * 1000000); srcbyte = source + TOPBDRBYTES; /* Initialize srcbyte to after Top Border */ for (srcy = 0; srcy < GZZHEIGHT; srcy++) { /* Loop on source plane rows */ /* Loop on bytes in source row - up to right border only */ for (rowbyte = 0; rowbyte < MAPROWBYTES; rowbyte++) { if (*srcbyte != 0) { /* If Source byte not empty */ for(bitpos = 0; bitpos < 8; bitpos++) { /* loop on bits in byte */ if ((*srcbyte & (128>>bitpos)) != 0) { /* current src pixel is '1' */ srcx = rowbyte * 8 + bitpos - LEFTBDRBITS; /* do transformation */ destx = (ai * srcx + bi * srcy + ei + lbdri) / 1000000; desty = (ci * srcx + di * srcy + fi) / 1000000; if(destx < LEFTBDRBITS || destx >= PIECEMAPWIDTH || desty < 0 || desty >= GZZHEIGHT) /* Don't write transformed */ overflowflag = 1; /* pixel to dest - it is outside bitplane! */ else /* write '1' pixel to destination. Expression to left of |= */ /* is destination byte address; 128>>bitpos is the bitmask. */ *( dest + desty*MAPROWBYTESPLUS + destx/8 ) |= 128>>(destx % 8); } /* End if current source pixel is '1' */ } /* End loop on bits in byte */ } /* End If source byte not empty */ srcbyte++; } /* End loop on bytes in source row */ srcbyte += RIGHTBDRBYTES; /* skip right border */ } /* End loop on source rows */ ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ return(!overflowflag); } /*===========================================================================*/ SaveIFS() /* Save IFS coefficients, if any, to a disk file */ { FILE *fopen(), *fp; char *fnam, *p; char *GetFileName(); int strcmp(); char *strcat(); long k; if (drawmode != VECTOR) { ShowError("No IFS to Save!"); return; } /*** Open disk file,deleting prev rev if any; if error, exit function */ fnam = GetFileName("Save to File:"); if (*fnam == '\0') { return; /* do nothing - user selected CANCEL or no filename */ } k = 0; /* Set k to 1 if filename ends with .ifs */ for (p = fnam; *p != '\0'; p++) if (strcmp(p, ".ifs") == 0) k = 1; if (k == 0) /* Add .ifs to filename if needed */ strcat(fnam, ".ifs"); if ((fp = fopen(fnam, "w")) == NULL) { ShowError(" Cannot open file!!!"); return; } /*** Save the current IFS codes to a disk file */ fprintf(fp, " A B C D E F P Dens S1\ S2 R1 R2\n"); for(i = 0; i <= N; i++) fprintf(fp, " % 6.3lf % 6.3lf % 6.3lf % 6.3lf %5.3lf %5.3lf\ %5.3lf %7.3lf% 5.3lf %5.3lf % 4.0lf % 4.0lf\n", pieceptr[i]->a, pieceptr[i]->b, pieceptr[i]->c, pieceptr[i]->d, pieceptr[i]->e, pieceptr[i]->f, pieceptr[i]->p, pieceptr[i]->dens, pieceptr[i]->s1, pieceptr[i]->s2, pieceptr[i]->r1/0.017453293, pieceptr[i]->r2/0.017453293); fclose(fp); /* close disk file */ } /*===========================================================================*/ int LoadIFS(fnam) /* Load IFS Code variables from disk file */ char *fnam; /* Returns 1 if done OK, 0 if failed to load */ /* Skips lines of wrong form (e.g. text headers) */ /* and ignores excess info on a line (e.g. Geom. Coeffs) */ /* Calls Optimize() to create a Collage and assure */ { /* Attractor will not overflow window */ FILE *fopen(), *fp; int c, eofread, scan; char linebuf[161]; /* Input lines will be truncated after 160 chars */ double mindens; long xmin, xmax, ymin, ymax; /* GZZ PIXEL coords of boundary rect of old (as-loaded) attractor*/ int j; /*** Open disk file for reading */ if ((fp = fopen(fnam,"r")) == NULL) { ShowError(" Cannot open file!!!"); return(0); } ClearIFS(); /* Erase old IFS (if any), set N to -1 */ /*** Load the IFS codes line by line */ eofread = 0; while (N < MAX_N && !eofread) { /* Loop on lines in file */ if ((pieceptr[++N] = AllocPiece()) == NULL) { /* Allocate Piece */ N--; /* if couldn't allocate piece */ ShowError("Couldn't allocate memory!"); if (N == -1) { /* No memory for even one Piece - Quit program */ ShowError("Re-run with more free memory!"); CloseAll(); } ShowError(" Aborting Load! "); fclose(fp); return(0); } /*** Read in a single line from file into memory, then sscanf it */ /* (Don't use fscanf from file because it ignores end of line */ for (i = 0; i < 160 && (c = getc(fp)) != EOF && c != '\n'; i++) linebuf[i] = c; linebuf[i] = '\0'; if (c != EOF && c != '\n') /* Line has been truncated after 160 chars */ while ((c = getc(fp)) != '\n' && c != EOF) ; /* Remove any other stuff on the file line */ if (c == EOF) eofread = 1; /* Now a null-terminated line is in linebuf & the file is positioned at */ /* start of next file line or past EOF;eofread is set if EOF was reached */ scan = sscanf(linebuf, "%lf %lf %lf %lf %lf %lf %lf", &pieceptr[N]->a, &pieceptr[N]->b, &pieceptr[N]->c, &pieceptr[N]->d, &pieceptr[N]->e, &pieceptr[N]->f, &pieceptr[N]->p); if (scan == 7) { /* converted successfully 7 coeffs of one IFS */ /*** Check that coefficients a - d and p are in legal ranges */ if (pieceptr[N]->a < -1. || pieceptr[N]->a > 1. || pieceptr[N]->b < -1.|| pieceptr[N]->b > 1. || pieceptr[N]->c < -1. || pieceptr[N]->c > 1. || pieceptr[N]->d < -1. || pieceptr[N]->d > 1. || pieceptr[N]->p <= 0. || pieceptr[N]->p > 1.) { ShowError("Coefficient out of Range!"); ShowError(" Aborting Load! "); fclose(fp); return(0); } /*** compute the geometrical coeffs from matrix coeffs read from file */ pieceptr[N]->s1 = sqrt(pieceptr[N]->a*pieceptr[N]->a + pieceptr[N]->c*pieceptr[N]->c); pieceptr[N]->s2 = sqrt(pieceptr[N]->b*pieceptr[N]->b + pieceptr[N]->d*pieceptr[N]->d); pieceptr[N]->r1 = myatan2(pieceptr[N]->c, pieceptr[N]->a); pieceptr[N]->r2 = myatan2(-pieceptr[N]->b, pieceptr[N]->d); pieceptr[N]->det = fabs(pieceptr[N]->a * pieceptr[N]->d - pieceptr[N]->b * pieceptr[N]->c); if (pieceptr[N]->det == 0.0) pieceptr[N]->det = 0.01; pieceptr[N]->dens = pieceptr[N]->p / pieceptr[N]->det; /*** Compute new Box corners from new coeffs */ pieceptr[N]->boxo.x = pieceptr[N]->e * GZZWIDTH; pieceptr[N]->boxo.y = pieceptr[N]->f * GZZWIDTH; /* sic! */ pieceptr[N]->boxx.x = (pieceptr[N]->a + pieceptr[N]->e) * GZZWIDTH; pieceptr[N]->boxx.y = (pieceptr[N]->c + pieceptr[N]->f) * GZZWIDTH; pieceptr[N]->boxy.x = (pieceptr[N]->b * GZZHEIGHT/GZZWIDTH + pieceptr[N]->e) * GZZWIDTH; pieceptr[N]->boxy.y = (pieceptr[N]->d * GZZHEIGHT/GZZWIDTH + pieceptr[N]->f) * GZZWIDTH; pieceptr[N]->boxz.x = pieceptr[N]->boxy.x + pieceptr[N]->boxx.x - pieceptr[N]->boxo.x; pieceptr[N]->boxz.y = pieceptr[N]->boxy.y + pieceptr[N]->boxx.y - pieceptr[N]->boxo.y; } else /* scanf failed to match 7 doubles on line - skip it! */ FreePiece(pieceptr[N--]); } /* End while loop on lines */ selpiece = N; if (N == MAX_N && !eofread && getc(fp) != EOF) /* Exited loop at N==MAX_N */ ShowError("File too long; IFS may be truncated"); if (N == -1) { /* Not a single transformation was found! */ ShowError("No IFS found in File - Aborting Load!"); fclose(fp); return(0); } /*** If probabilities read from file were cumulative, convert to non-cum */ if (pieceptr[N]->p == 1.000) { for (i = N; i > 0; i--) { pieceptr[i]->p = pieceptr[i]->p - pieceptr[i-1]->p; pieceptr[i]->dens = pieceptr[i]->p / pieceptr[i]->det; } } /*** Normalize Piece densities so smallest one is 1.000 */ mindens = 1000000.; for (i = 0; i <= N; i++) if (pieceptr[i]->dens < mindens) mindens = pieceptr[i]->dens; for (i = 0; i <= N; i++) pieceptr[i]->dens /= mindens; ComputeProbs(); /* Adjust all probabilities in case rounding errors in */ /* file made their sum != 1.000 */ fclose(fp); /* close disk file */ /* Transform loaded IFS to fit in window if it exceeds it, */ /* and generate Collage */ Optimize(0); return(1); } /*===========================================================================*/ int Optimize(mode) /* Creates an Outline from current Attractor, creates */ int mode; /* a Collage from it and displays it. If mode==1, also */ /* modifies the IFS so its attractor fills the window. */ /* If mode==0, does this only if current Attractor would */ /* overflow the window. */ /* Returns 1 if successful and 0 if it failed (because */ { /* not in Collage Editor) and no modifications were done.*/ long xmin, xmax, ymin, ymax; /* GZZ PIXEL coords of old boundary rect */ int px1, py1; /* GZZ PIXEL coords of Topleft corner of new boundary rect */ double x0,y0; /* REAL plane coords of Topleft corner of old boundary rect */ double x1,y1; /* REAL plane coords of Topleft corner of new boundary rect */ double m; /* Magnification to apply to attractor so it fills window */ double attrw, attrh; /* PIXEL Width & Height of old boundary rect */ struct piece tmppiece; /* will be used to hold temporary trans' coeffs */ int j; if (drawmode != VECTOR) { /* Abort if not in Collage Editor! */ ShowError("No IFS!"); return(0); } ModifyMousePtr(Window, 1); /* Bring up 'ZZ' pointer */ SetRast(r, 0L); /* Erase Old Outline and Collage */ FindBoundary(&xmin, &xmax, &ymin, &ymax); /* Identify old boundary rect */ /*** If mode is 1, or mode is 0 but attractor exceeds window, modify IFS ***/ if (mode==1 || xmin<0 || xmax >= GZZWIDTH || ymin<0 || ymax >= GZZHEIGHT) { /*** Compute required magnification and relocation of attractor */ attrw = (double)((xmax - xmin) + 1); attrh = (double)((ymax - ymin) + 1); if ((double)attrw/(double)attrh >= (double)GZZWIDTH/(double)GZZHEIGHT) { m = 0.95 * (double)GZZWIDTH / attrw; px1 = 0.025 * (double)GZZWIDTH; py1 = 0.025 * (double)GZZHEIGHT + (0.95 * (double)GZZHEIGHT - m * attrh) / 2.0; } else { m = 0.95 * (double)GZZHEIGHT / attrh; px1 = 0.025 * (double)GZZWIDTH + (0.95 * (double)GZZWIDTH - m * attrw) / 2.0; py1 = 0.025 * (double)GZZHEIGHT; } x0 = (double)xmin / (double)GZZWIDTH; y0 = (double)ymin / (double)GZZWIDTH; x1 = (double)px1 / (double)GZZWIDTH; y1 = (double)py1 / (double)GZZWIDTH; /*** Modify all piece coefficients to make new attractor fill window */ for (i = 0; i <= N; i++) { /* Loop on pieces */ pieceptr[i]->e = m * pieceptr[i]->e + (pieceptr[i]->a - 1) * (m * x0 - x1) + pieceptr[i]->b * (m * y0 - y1); pieceptr[i]->f = m * pieceptr[i]->f + pieceptr[i]->c * (m * x0 - x1) + (pieceptr[i]->d - 1) * (m * y0 - y1); /*** Recompute new Box corners from new coeffs */ pieceptr[i]->boxo.x = pieceptr[i]->e * GZZWIDTH; pieceptr[i]->boxo.y = pieceptr[i]->f * GZZWIDTH; /* sic! */ pieceptr[i]->boxx.x = (pieceptr[i]->a + pieceptr[i]->e) * GZZWIDTH; pieceptr[i]->boxx.y = (pieceptr[i]->c + pieceptr[i]->f) * GZZWIDTH; pieceptr[i]->boxy.x = (pieceptr[i]->b * GZZHEIGHT/GZZWIDTH + pieceptr[i]->e) * GZZWIDTH; pieceptr[i]->boxy.y = (pieceptr[i]->d * GZZHEIGHT/GZZWIDTH + pieceptr[i]->f) * GZZWIDTH; pieceptr[i]->boxz.x = pieceptr[i]->boxy.x + pieceptr[i]->boxx.x - pieceptr[i]->boxo.x; pieceptr[i]->boxz.y = pieceptr[i]->boxy.y + pieceptr[i]->boxx.y - pieceptr[i]->boxo.y; } /* End loop on pieces */ } /* End if mode==1... */ RenderImage(1); /* Render new attractor in OUTLINE Bitplane */ CopyMem((char*)Window->RPort->BitMap->Planes[1], (char*)outlinebufptr, (long)RASSIZE(WIDTH,HEIGHT)); ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ /*** Transform the new Outline (=attractor) into all pieces' piecemaps */ /*** Draw each piece into the collage as you go */ selpiece = N; for (i = 0; i <= N-1; i++) { /* Loop on deselected pieces */ TransformPiece(outlinebufptr, pieceptr[i]->piecemap, pieceptr[i]); /* Blit this piece from its piecemap to plane 0 */ SetWrMsk(r,0x0001); /* Write protect all planes except plane 0 */ SetAPen(r, 1L); BltTemplate((char*)pieceptr[i]->piecemap,(long)Window->BorderLeft, (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT); } /* Now transform Selected Piece & Blit it from piecemap to plane 2 */ TransformPiece(outlinebufptr, pieceptr[i]->piecemap, pieceptr[i]); SetWrMsk(r,0x0004); /* Write protect all planes except plane 2 */ SetAPen(r, 4L); BltTemplate((char*)pieceptr[selpiece]->piecemap,(long)Window->BorderLeft, (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT); DrawBox(); /* Draw Vector Box of Selected Piece */ /* Note that Rastport's WrMask is restored within DrawBox() */ return(1); } /*===========================================================================*/ RenderImage(z) /* If z == 0, Render the Attractor in Image Screen at current */ int z; /* resolution & depth settings; stop and return on click in Image */ /* screen. If z == 1, render it in Outline bitplane. */ /* Probabilities and coefficients are scaled to integers so can use */ /* faster integer math */ { struct Message * GetMsg(); struct IntuiMessage *message; int randprob; /* Random probability value */ double sum; /* cum probability */ int k; long unitwidth, unitheight; /* width, height in image screen pixels of unit square in the plane */ long iter; /* Iteration counter */ struct RastPort *imagerp; /* Pointer to rastport of Image window */ long pix; /* Color (Pen #) of a pixel */ long ReadPixel(); long ai[MAX_N+1], bi[MAX_N+1], ci[MAX_N+1], di[MAX_N+1], ei[MAX_N+1], fi[MAX_N+1]; /* Scaled integer coefficients */ long xi, yi, tmpxi; /* Scaled integer pixel coordinates */ int cumprobi[MAX_N+2]; /* Scaled integer Cum Probabilities */ /* cumprobi array has MAX_N+2 elements because using MAX_N+1 caused a */ /* Crash for unclear reasons ?!?! */ /*** Prepare stuff for iteration loop ***/ if (z == 0) { /* Rendering is in Image Screen */ /*** Open Image Window in its screen (Closing any previous image screen) */ if (exists_image) CloseImageScreen(); OpenImageScreen(renderwidth, renderheight, renderdepth); /* OpenImageScreen() also opens ImageWindow */ imagerp = ImageWindow->RPort; SetAPen(imagerp,2L); /* Will be modified in loop for Greyscale rendering */ SetDrMd(imagerp, JAM1); SetWindowTitles(ImageWindow, -1L, (char *)" Click in screen to stop rendering"); unitwidth = GZZWIDTH*(renderwidth/320); /* GZZWIDTH is of Collage window!*/ unitheight = GZZWIDTH * (renderheight/200); } else { /* Rendering in Outline plane */ SetWrMsk(r, 0x0002); /* Write protect all planes except plane 1 */ SetAPen(r, 2L); SetDrMd(r, JAM1); unitwidth = unitheight = GZZWIDTH; } srand((unsigned int)(time(NULL)%10000L)); /* seed random gen from sys time*/ sum = 0; for (k = 0; k <= N; k++) { /* Scale Coefficients as longs */ ai[k] = (long)(pieceptr[k]->a * 1000000); bi[k] = (long)(pieceptr[k]->b * 1000000); ci[k] = (long)(pieceptr[k]->c * 1000000); di[k] = (long)(pieceptr[k]->d * 1000000); ei[k] = (long)(pieceptr[k]->e * 1000000 * unitwidth); fi[k] = (long)(pieceptr[k]->f * 1000000 * unitwidth); sum = sum + pieceptr[k]->p; /* Compute scaled Cum Probabilities */ cumprobi[k] = (int)(sum * (double)RAND_MAX); } xi = unitwidth / 3; /* Initial coordinates */ yi = unitwidth / 3; /*** Iterate until user clicks in Image window or until 1000 points ***/ /*** (depending on z) ***/ for (iter = 0; 1; iter++) { /* endless loop */ /*** Select a Piece at random */ randprob = rand(); /* rand() returns a random # (between 0 - RAND_MAX) */ for (k = 0; cumprobi[k] < randprob; k++) { /* the following IF is to trap problem case when randprob = RAND_MAX */ /* exactly and cum probability is RAND_MAX minus slight delta due to */ /* rounding errors */ if (k > N) { k--; break; } } /* Apply transformation #k to previous (x,y) */ tmpxi = (ai[k] * xi + bi[k] * yi + ei[k]) / 1000000; yi = (ci[k] * xi + di[k] * yi + fi[k]) / 1000000; xi = tmpxi; if (iter > 19) { /* Render the pixel (except 1st 20 points) */ if (z == 0) { /*** draw in Image Screen, quit on mouseclick in it */ if (renderdepth > 1) { /* Grayscale rendering */ pix = ReadPixel(imagerp, xi, yi * unitheight / unitwidth); if (++pix < (1<UserPort)) { ReplyMsg(message); if (message->Code == SELECTUP) { SetWindowTitles(ImageWindow, (char *)" Click in screen to push it back", -1L); ShowTitle(ImageScreen, FALSE); /* Hide screen depth gadgets! */ break; /* From for iter */ } } } else { /* z == 1, draw in outline plane, quit after 1000 iters */ WritePixel(r, xi, yi); if (iter > 999) break; /* From for iter */ } } /* End if iter > 19 */ } /* End for iter */ if (z == 0) { /*** Wait for user click in Image screen to push it to back */ while (!(message = (struct IntuiMessage*)GetMsg(ImageWindow->UserPort))) ; /* Wait for a message */ ReplyMsg(message); SetWindowTitles(ImageWindow, (char *)" ", (char *)" Click in screen to push it back"); ShowTitle(ImageScreen, TRUE); /* OK to have depth gadgets now */ ScreenToBack(ImageScreen); MoveScreen(ImageScreen, 0L, (long)(-1*ImageScreen->TopEdge)); ScreenToFront(Screen); ActivateWindow(Window); } else /* z == 1, unprotect Outline window planes */ SetWrMsk(r, 0xFFFF); } /*===========================================================================*/ SaveILBM() /* Saves the current Image to an IFF disk file */ { LONG file; /* File handle [we use AmigaDOS Open(), not Manx open()] */ UBYTE *savebuffer; char *fnam; if (exists_image == 0) { ShowError("No Image to save!"); return; } fnam = GetFileName("Save to File:"); /* Get the filename */ if (*fnam == '\0') /* User selected CANCEL or gave no filename */ return; if ((file = Open(fnam, MODE_NEWFILE)) == NULL) { ShowError("Can't open file!"); return; } if ((savebuffer = (UBYTE *)AllocMem(8000L, MEMF_CHIP|MEMF_PUBLIC)) == NULL) { ShowError("Can't allocate Save Buffer Memory!"); return; } /* Use EA IFF routine PutPict() to save the image to file */ ModifyMousePtr(Window, 1); /* Bring up 'ZZ' pointer */ SetWindowTitles(ImageWindow,-1L, (char *)""); ShowTitle(ImageScreen, FALSE); /* Hide screen depth gadgets! */ ActivateWindow(ImageWindow); /* To make title change take effect */ ActivateWindow(Window); PutPict(file, ImageWindow->RPort->BitMap, ImageScreen->Width, ImageScreen->Height, (WORD *)(&(ImageScreen->ViewPort))->ColorMap->ColorTable, savebuffer,8000L); SetWindowTitles(ImageWindow,-1L, (char *)" Click in screen to push it back"); ShowTitle(ImageScreen, TRUE); /* OK to have depth gadgets now */ ActivateWindow(ImageWindow); /* To make title change take effect */ ActivateWindow(Window); FreeMem(savebuffer, 8000L); Close(file); ModifyMousePtr(Window, -1); /* Remove 'ZZ' pointer */ } /*===========================================================================*/ struct piece *AllocPiece() /* Allocate memory for a piece structure and */ { /* its piecemap. Return pointer to the piece structure */ /* Or NULL if can't allocate memory */ struct piece *tmppieceptr; UBYTE *mapptr; /* Allocate memory for piece structure */ if ((tmppieceptr = (struct piece *)AllocMem((long)sizeof(struct piece), NULL)) == NULL) { ShowError("Can't allocate Memory!"); return(NULL); } /* Allocate piecemap CHIP RAM */ if ((mapptr = (UBYTE*)AllocRaster((long)WIDTH, (long)GZZHEIGHT)) == NULL) { FreeMem((void*)tmppieceptr, (long)sizeof(struct piece)); ShowError("Can't allocate Memory!"); return(NULL); } /* Clear piecemap */ BltClear((char*)mapptr, (long)RASSIZE(WIDTH,GZZHEIGHT), 1L); tmppieceptr->piecemap = mapptr; /* Link bitplane to piece structure */ return(tmppieceptr); } /*===========================================================================*/ FreePiece(piecepntr) /* Frees the memory used for a piece structure */ struct piece *piecepntr; /* and its piecemap. */ { /* Deallocate CHIP RAM of piecemap bitplane */ FreeRaster((void*)(piecepntr->piecemap), (long)WIDTH, (long)GZZHEIGHT); /* Deallocate memory of piece structure */ FreeMem((void*)piecepntr, (long)sizeof(struct piece)); } /*===========================================================================*/ p(text) /* screen text output routine, similar to BASIC's PRINT */ /* puts text in consecutive lines in rastport pointed at by the */ /* external pointer Textrp. Line position is maintained between */ /* calls, but is reset to top if text==NULL. No overflow check */ char *text; /* is done -- text should fit in rastport area! */ { static struct IntuiText Textline = { 1, 0, JAM1, /* FrontPen, BackPen, DrawMode */ 0, NULL, /* LeftEdge, TopEdge */ NULL, /* ITextFont */ NULL, NULL /* IText, NextText */ }; static int line = 0; /* vert. position of line to be printed next (0 - 24) */ if (text == NULL) /* reset to top line of rastport */ line = 0; else { Textline.TopEdge = 8 * line; /* Put text into line on screen */ Textline.IText = (UBYTE*)text; PrintIText(Textrp, &Textline, 0L, 0L); line++; /* increment line for next call */ } } /*===========================================================================*/ FindBoundary(xminp, xmaxp, yminp, ymaxp) /* Iterates IFS without drawing */ /* and returns PIXEL coords of boundary rectangle of attractor */ /* in variables pointed at by its arguments */ /* Uses float math, not scaled int, to avoid overflow on very */ /* large coeffs of loaded IFS files from other programs */ long *xminp, *xmaxp, *yminp, *ymaxp; /* Pointers! */ { int randprob; /* Random probability value */ double sum; /* cum probability */ int k; long iter; /* Iteration counter */ double xd, yd, tmpxd; /* Real pixel coordinates */ double xdmin, ydmin, xdmax, ydmax; /* Real boundary rect coords */ int cumprobi[MAX_N+2]; /* Scaled integer Cum Probabilities */ /* cumprobi array has MAX_N+2 elements because using MAX_N+1 caused a */ /* Crash for unclear reasons in RenderImage() ?!?! */ /*** Prepare stuff for iteration loop ***/ srand((unsigned int)(time(NULL)%10000L)); /* seed random gen from sys time*/ sum = 0; for (k = 0; k <= N; k++) { /* Scale Coefficients as longs */ sum = sum + pieceptr[k]->p; /* Compute scaled Cum Probabilities */ cumprobi[k] = (int)(sum * (double)RAND_MAX); } xdmin = 1000000000.; /* Initialize boundary rectangle coords */ xdmax = -1000000000.; ydmin = 1000000000.; ydmax = -1000000000.; xd = 0.3; /* Initial coordinates */ yd = 0.3; /*** Iterate 1000 times without drawing ***/ for (iter = 0; iter < 1000; iter++) { /*** Select a Piece at random */ randprob = rand(); /* rand() returns a random # (between 0 - RAND_MAX) */ for (k = 0; cumprobi[k] < randprob; k++) { /* the following IF is to trap problem case when randprob = RAND_MAX */ /* exactly and cum probability is RAND_MAX minus slight delta due to */ /* rounding errors */ if (k > N) { k--; break; } } /* Apply transformation #k to previous (x,y) */ tmpxd = pieceptr[k]->a * xd + pieceptr[k]->b * yd + pieceptr[k]->e; yd = pieceptr[k]->c * xd + pieceptr[k]->d * yd + pieceptr[k]->f; xd = tmpxd; if (iter > 19) { /* Adjust rectangle (except 1st 20 points) */ if (xd < xdmin) xdmin = xd; if (xd > xdmax) xdmax = xd; if (yd < ydmin) ydmin = yd; if (yd > ydmax) ydmax = yd; } } /* End for iter */ /*** Convert real coords to pixel coords for returned values */ *xminp = (long)(xdmin * GZZWIDTH); *xmaxp = (long)(xdmax * GZZWIDTH); *yminp = (long)(ydmin * GZZWIDTH); *ymaxp = (long)(ydmax * GZZWIDTH); } /*===========================================================================*/