diff --git a/src/util/cmdClient.c b/src/util/cmdClient.c new file mode 100644 index 000000000..83abec26f --- /dev/null +++ b/src/util/cmdClient.c @@ -0,0 +1,526 @@ +/**************************************************************************** + GTA PROJECT AT division + + Copyright, 1990, The Regents of the University of California + Los Alamos National Laboratory + + FILE PATH: cmdClient.c + ENVIRONMENT: SunOS, VxWorks + MAKE OPTIONS: + SCCS VERSION: $Id$ +*+/mod*********************************************************************** +* TITLE cmdClient.c - general purpose client for command-based servers +* +* DESCRIPTION +* Connects to a text-command-based server. +* +* Usage on SunOS: +* % cmdClient hostName portNum +* or +* execl("cmdClient", "cmdClient", "hostName", "portNum", +* (char *)0); +* +* Usage on VxWorks: +* > cmdClient "hostName",portNum +* +* BUGS +* o need to clean up structure to be more in line with cmdProto's structure +* o the stdout stream from this program contains, intermixed, the +* server's stdout and stderr streams +* o under VxWorks, if the server is on the same IOC, then the IOC itself +* must be added to the host table with hostAdd() +* o this program should use tasks.h to establish priorities, stack +* sizes, etc. +* o not all signals are caught +*- +* Modification History +* version date programmer comments +* ------- -------- ------------ ----------------------------------- +* 1.1 11/26/90 R. Cole initial version +* +*****************************************************************************/ +#include +#include +#include +#include + +#ifdef vxWorks +/*---------------------------------------------------------------------------- +* includes and defines for VxWorks compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +# include +# include +# include +# define MAXPRIO 160 +#else +/*---------------------------------------------------------------------------- +* includes and defines for Sun compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +# include +# define MAXPRIO 50 +#endif + + +/*/subhead CMDCL_CTX------------------------------------------------------- +* CMDCL_CTX - context information +* +* A cmdcl descriptor is the `master handle' which is used for +* handling business. +*----------------------------------------------------------------------------*/ +#define CmdclLock semTake(pglCmdclCtx->semLock) +#define CmdclUnlock semGive(pglCmdclCtx->semLock) +#define CmdclLockCheck semClear(pglCmdclCtx->semLock) +#define CmdclLockInitAndLock semInit(pglCmdclCtx->semLock) + +typedef struct { + TASK_ID id; /* ID of task */ + int status; /* status of task--initially ERROR */ + int stop; /* task requested to stop if != 0 */ + int stopped; /* task has stopped if != 0 */ + int serviceNeeded; /* task needs servicing */ + int serviceDone; /* task servicing completed */ + jmp_buf sigEnv; /* environment for longjmp at signal time */ +} CMDCL_TASK_INFO; + +typedef struct cmdclCtx { + SEM_ID semLock; + CMDCL_TASK_INFO cmdclTaskInfo; + CMDCL_TASK_INFO cmdclInTaskInfo; + int showStack; /* show stack stats on task terminate */ +} CMDCL_CTX; + +/*----------------------------------------------------------------------------- +* prototypes +*----------------------------------------------------------------------------*/ +int cmdcl(); +void cmdclCmdProcess(); +long cmdclInitAtStartup(); +long cmdclInTask(); +long cmdclTask(); +void cmdclTaskSigHandler(); +void cmdclTaskSigInit(); + +/*----------------------------------------------------------------------------- +* global definitions +*----------------------------------------------------------------------------*/ +CX_CMD glCmdclCxCmd; +CX_CMD *pglCmdclCxCmd=NULL; +CMDCL_CTX glCmdclCtx; +CMDCL_CTX *pglCmdclCtx=NULL; + +#ifndef vxWorks +main(argc, argv) +int argc; /* number of command line args */ +char *argv[]; /* command line args */ +{ + char *hostName; /* host name for server */ + int portNum; /* port number for server */ + + if (argc != 3) /* must be command and 2 args */ + goto mainUsage; + hostName = argv[1]; + if (sscanf(argv[2], "%d", &portNum) != 1) { + printf("error scanning port number\n"); + goto mainUsage; + } + +/*---------------------------------------------------------------------------- +* do some lwp initialization: +* o set allowable priorities between 1 and MAXPRIO +* o set up a cache of stacks for MAXTHREAD stacks +* +* A side effect is that this routine is turned into a lwp thread with a +* priority of MAXPRIO. +*---------------------------------------------------------------------------*/ + (void)pod_setmaxpri(MAXPRIO); + lwp_setstkcache(100000, 9); + + return cmdClient(hostName, portNum); + +mainUsage: + printf("Usage: %s serverHostName serverPortNumber\n", argv[0]); + return -1; +} +#endif + +/*+/subr********************************************************************** +* NAME cmdClient - shell callable interface for cmdClient +* +* DESCRIPTION +* This routine is the only part of cmdClient which is intended to be +* called directly from the shell. Several functions are performed here: +* o spawn the cmdclTask +* o spawn the cmdclInTask +* o wait until the cmdclInTask quits, then return to the shell. If +* other tasks belonging to cmdClient are being stopped, then this +* routine waits until they, too, are stopped before returning to +* the shell. +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o stack size and priority should come from tasks.h +* o there are lots of "holes" in detecting whether tasks exist, are +* suspended, etc. +* +*-*/ +int +cmdClient(hostName, portNum) +char *hostName; /* I host name for server */ +int portNum; /* I port number for server */ +{ + long stat; /* status return from calls */ + + pglCmdclCtx = &glCmdclCtx; + pglCmdclCxCmd = &glCmdclCxCmd; + stat = cmdclInitAtStartup(pglCmdclCtx, pglCmdclCxCmd); + assert(stat == OK); + +#ifdef vxWorks + assert(taskNameToId("cmdclTask") == ERROR); + assert(taskNameToId("cmdclInTask") == ERROR); +#endif + pglCmdclCtx->showStack = 0; + + pglCmdclCtx->cmdclTaskInfo.status = ERROR; + pglCmdclCtx->cmdclTaskInfo.stop = 0; + pglCmdclCtx->cmdclTaskInfo.stopped = 1; + + pglCmdclCtx->cmdclInTaskInfo.status = ERROR; + pglCmdclCtx->cmdclInTaskInfo.stop = 0; + pglCmdclCtx->cmdclInTaskInfo.stopped = 1; + + pglCmdclCtx->cmdclInTaskInfo.serviceNeeded = 0; + pglCmdclCtx->cmdclInTaskInfo.serviceDone = 1; + +/*----------------------------------------------------------------------------- +* cmdclTask +* spawn it +*----------------------------------------------------------------------------*/ + pglCmdclCtx->cmdclTaskInfo.id = taskSpawn("cmdclTask", MAXPRIO, + VX_STDIO | VX_FP_TASK, 50000, cmdclTask, + &pglCmdclCxCmd, hostName, portNum); + if (GenTaskNull(pglCmdclCtx->cmdclTaskInfo.id)) { + (void)fprintf(stdout, "error spawning cmdclTask\n"); + return ERROR; + } + pglCmdclCtx->cmdclTaskInfo.status = OK; + pglCmdclCtx->cmdclTaskInfo.stopped = 0; + +/*----------------------------------------------------------------------------- +* cmdclInTask +* spawn it +*----------------------------------------------------------------------------*/ + pglCmdclCtx->cmdclInTaskInfo.id = taskSpawn("cmdclInTask", MAXPRIO, + VX_STDIO | VX_FP_TASK, 50000, cmdclInTask, &pglCmdclCxCmd); + if (GenTaskNull(pglCmdclCtx->cmdclInTaskInfo.id)) { + (void)fprintf(stdout, "error spawning cmdclInTask\n"); + return ERROR; + } + pglCmdclCtx->cmdclInTaskInfo.status = OK; + pglCmdclCtx->cmdclInTaskInfo.stopped = 0; + +/*----------------------------------------------------------------------------- +* wait for cmdclInTask to exit and then return to the shell. If other +* "cmdClient tasks" are also exiting, wait until their wrapups are complete. +*----------------------------------------------------------------------------*/ + while (!pglCmdclCtx->cmdclInTaskInfo.stopped) + taskSleep(SELF, 1, 0); + if (pglCmdclCtx->cmdclTaskInfo.stop) { + while (!pglCmdclCtx->cmdclTaskInfo.stopped) + taskSleep(SELF, 1, 0); + } + + return OK; +} + +/*+/subr********************************************************************** +* NAME cmdclTask - main processing task for cmdClient +* +* DESCRIPTION +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +*-*/ +long +cmdclTask(ppCxCmd, hostName, portNum) +CX_CMD **ppCxCmd; +char *hostName; /* I host name for server */ +int portNum; /* I port number for server */ +{ + long stat; + CX_CMD *pCxCmd; + int serverSock=-1; /* socket connected to server */ + char serverBuf[80]; + FILE *myIn=NULL; + FILE *myOut=NULL; + char *findNl; + char message[80]; + + int i; + + pCxCmd = *ppCxCmd; + + CmdclLockInitAndLock; + CmdclUnlock; + + cmdclTaskSigInit(); + + if (setjmp(pglCmdclCtx->cmdclTaskInfo.sigEnv) != 0) + goto cmdclTaskWrapup; + +/*----------------------------------------------------------------------------- +* attempt to connect to the server. If the connection is successful, +* print the message returned by the server. Then open two streams to +* the socket, one for characters from keyboard to server, the other +* for characters from server to stdout. +*----------------------------------------------------------------------------*/ + if ((i=ezsConnectToServer(&serverSock, portNum, hostName, serverBuf)) < 0) { + if (i == -1) { + (void)sprintf(message, "connect to %s port:%d", hostName, portNum); + perror(message); + } + else + printf("%s\n", serverBuf); + serverSock = -1; + goto cmdclTaskWrapup; + } + printf("%s\n", serverBuf); + if (ezsFopenToFd(&myIn, &serverSock) < 0) { + perror("myIn"); + myIn = NULL; + goto cmdclTaskWrapup; + } + if (ezsFopenToFd(&myOut, &serverSock) < 0) { + perror("myOut"); + myOut = NULL; + goto cmdclTaskWrapup; + } + +/*---------------------------------------------------------------------------- +* "processing loop" +* do the interactions with the server, attempting as much as possible +* to show the messages as they come in from the server. +*---------------------------------------------------------------------------*/ + while (!pglCmdclCtx->cmdclTaskInfo.stop) { + if (pglCmdclCtx->cmdclInTaskInfo.serviceNeeded) { + fputs(pCxCmd->line, myOut); + fflush(myOut); + pglCmdclCtx->cmdclInTaskInfo.serviceNeeded = 0; + pglCmdclCtx->cmdclInTaskInfo.serviceDone = 1; + } + if (ezsCheckFpRead(myIn)) { + if (fgets(serverBuf, 80, myIn) != NULL) { + if (strncmp(serverBuf, "#p#r#", 5) == 0) { + if ((findNl = index(serverBuf, '\n')) != NULL) + *findNl = '\0'; + fputs(serverBuf+5, stdout); + } + else + fputs(serverBuf, stdout); + fflush(stdout); + } + else { + if (strncmp(pCxCmd->line, "quit", 4) == 0) + break; + else if (strncmp(pCxCmd->line, "close", 5) == 0) + break; + printf("server gone (?)\n"); + break; + } + } + taskSleep(SELF, 0, 100000); /* wait .1 sec */ + } + +cmdclTaskWrapup: + if (myIn != NULL) + fclose(myIn); + if (myOut != NULL) + fclose(myOut); + if (serverSock >= 0) + close(serverSock); + + while ((*ppCxCmd)->pPrev != NULL) + cmdCloseContext(ppCxCmd); + pCxCmd = *ppCxCmd; + + pglCmdclCtx->cmdclInTaskInfo.stop = 1; + while (pglCmdclCtx->cmdclInTaskInfo.stopped == 0) { + taskSleep(SELF, 1, 0); + } + +#ifdef vxWorks + if (pglCmdclCtx->showStack) + checkStack(pglCmdclCtx->cmdclTaskInfo.id); +#endif + + pglCmdclCtx->cmdclTaskInfo.stopped = 1; + pglCmdclCtx->cmdclTaskInfo.status = ERROR; + + return 0; +} + +/*+/subr********************************************************************** +* NAME cmdclTaskSig - signal handling and initialization +* +* DESCRIPTION +* These routines set up for the signals to be caught for cmdclTask +* and handle the signals when they occur. +* +* RETURNS +* void +* +* BUGS +* o not all signals are caught +* o under VxWorks, taskDeleteHookAdd isn't used +* o it's not clear how useful it is to catch the signals which originate +* from the keyboard +* +*-*/ +void +cmdclTaskSigHandler(signo) +int signo; +{ + printf("entered cmdclTaskSigHandler for signal:%d\n", signo); + signal(signo, SIG_DFL); + longjmp(pglCmdclCtx->cmdclTaskInfo.sigEnv, 1); +} + + +void +cmdclTaskSigInit() +{ + (void)signal(SIGTERM, cmdclTaskSigHandler); /* SunOS plain kill (not -9) */ + (void)signal(SIGQUIT, cmdclTaskSigHandler); /* SunOS ^\ */ + (void)signal(SIGINT, cmdclTaskSigHandler); /* SunOS ^C */ + (void)signal(SIGILL, cmdclTaskSigHandler); /* illegal instruction */ +#ifndef vxWorks + (void)signal(SIGABRT, cmdclTaskSigHandler); /* SunOS assert */ +#else + (void)signal(SIGUSR1, cmdclTaskSigHandler); /* VxWorks assert */ +#endif + (void)signal(SIGBUS, cmdclTaskSigHandler); /* bus error */ + (void)signal(SIGSEGV, cmdclTaskSigHandler); /* segmentation violation */ + (void)signal(SIGFPE, cmdclTaskSigHandler); /* arithmetic exception */ +} + +/*+/subr********************************************************************** +* NAME cmdclInTask - handle the keyboard and keyboard commands +* +* DESCRIPTION +* Gets input text and passes the input to cmdclTask. +* +* This task exists to avoid the possibility of blocking cmdclTask +* while waiting for operator input. +* +* This task waits for input to be available from the keyboard. When +* an input line is ready, this task does some preliminary processing: +* +* o if the command is control-D, "^D" is echoed and the command is +* changed to "quit" +* +* Then cmdclTask is signalled and this task goes into a sleeping loop +* until cmdclTask signals that it is ready for the next command. +* +* RETURNS +* OK, or +* ERROR +* +*-*/ +long +cmdclInTask(ppCxCmd) +CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */ +{ +/*---------------------------------------------------------------------------- +* wait for input from keyboard. When some is received, signal caller, +* wait for caller to process it, and then wait for some more input. +* +* stay in the main loop until .stop flag is set by cmdclTask. +*---------------------------------------------------------------------------*/ + while (1) { + while (pglCmdclCtx->cmdclInTaskInfo.serviceDone == 0) { + if (pglCmdclCtx->cmdclInTaskInfo.stop == 1) + goto cmdclInTaskDone; + taskSleep(SELF, 0, 500000); /* sleep .5 sec */ + } + cmdRead(ppCxCmd, &pglCmdclCtx->cmdclInTaskInfo.stop); + + if (pglCmdclCtx->cmdclInTaskInfo.stop == 1) + goto cmdclInTaskDone; + + if (strncmp((*ppCxCmd)->line, "quit", 4) == 0) { + char *prompt; + if (*ppCxCmd != (*ppCxCmd)->pCxCmdRoot) { + (void)printf("can't use quit command in source file\n"); + (*ppCxCmd)->line[0] = '\0'; + } + else { + prompt = (*ppCxCmd)->prompt; + (*ppCxCmd)->prompt = "are you sure? "; + cmdRead(ppCxCmd, &pglCmdclCtx->cmdclInTaskInfo.stop); + if ((*ppCxCmd)->line[0] == 'y' || (*ppCxCmd)->line[0] == 'Y') + (void)strcpy((*ppCxCmd)->line, "quit\n"); + else + (*ppCxCmd)->line[0] = '\0'; + (*ppCxCmd)->prompt = prompt; + } + } + pglCmdclCtx->cmdclInTaskInfo.serviceDone = 0; + pglCmdclCtx->cmdclInTaskInfo.serviceNeeded = 1; + } + +cmdclInTaskDone: + cmdCloseContext(ppCxCmd); + +#ifdef vxWorks + if (pglCmdclCtx->showStack) + checkStack(pglCmdclCtx->cmdclInTaskInfo.id); +#endif + pglCmdclCtx->cmdclInTaskInfo.stop = 1; + pglCmdclCtx->cmdclInTaskInfo.stopped = 1; + pglCmdclCtx->cmdclInTaskInfo.status = ERROR; + + return; +} + +/*+/subr********************************************************************** +* NAME cmdclInitAtStartup - initialization for cmdClient +* +* DESCRIPTION +* Perform several initialization duties: +* o initialize the command context block +* +* RETURNS +* OK, or +* ERROR +* +*-*/ +long +cmdclInitAtStartup(pCmdclCtx, pCxCmd) +CMDCL_CTX *pCmdclCtx; +CX_CMD *pCxCmd; +{ + pCxCmd->prompt = NULL; + pCxCmd->input = stdin; + pCxCmd->inputName = NULL; + pCxCmd->dataOut = stdout; + pCxCmd->pPrev = NULL; + pCxCmd->pCxCmdRoot = pCxCmd; + + return OK; +} diff --git a/src/util/cmdProto.c b/src/util/cmdProto.c new file mode 100644 index 000000000..d489e3ffa --- /dev/null +++ b/src/util/cmdProto.c @@ -0,0 +1,1235 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 10-24-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 10-24-90 rac initial version + * .02 mm-dd-yy rac version 1.0, installed in SCCS + * + * make options + * -DvxWorks makes a version for VxWorks + * -DNDEBUG don't compile assert() checking + */ +/*+/mod*********************************************************************** +* TITLE cmdProto - prototype for a multi-threaded program with command interface +* +* DESCRIPTION +* This skeleton can be used as the basis for a multi-threaded program +* which interfaces to a user with text strings, as from a keyboard. +* Several generic capabilities are provided: +* +* o scanning of input control lines +* o re-directing of input to come from a file +* o running as a stand-alone program, or as a server or client, +* with socket-based communications +* o on-line help +* o under VxWorks, "running in the background" using "bg" +* o handling cleanup on exception conditions (such as bus error) +* +* This skeleton runs under either SunOS (using the lightweight +* process library) or VxWorks. Under SunOS, the "bg" command +* isn't functional--the C shell "bg" and "fg" commands must be used +* instead. +* +* To run as a server, specify "server" as a command line option. +* +* To run as a client, specify "hostName" as a command line option. +* +* For use under VxWorks, this skeleton "catches" most errors which +* ordinarily result in just suspending the task. The result is that +* this skeleton will wrapup and exit rather than going into "suspended +* animation". +* +* Under SunOS, running under dbx doesn't allow testing the error +* handling features of the code--the debugger catches the signals. +* To do such testing, run without dbx. +* +* To use this skeleton: +* +* Change Zzz, zzz, and ZZZ to a desired name +* Customize a context block +* Remove the "server" items, if they aren't going to be used +* Add additional tasks, as necessary, using the scheme supplied +* Add additional commands in zzzTaskCmdCrunch +* Add additional help info in zzzInitAtStartup. Usually, this +* will mean adding to helpCmdsSpec and helpUsage. For +* any commands needing an individual help topic, then a +* specific HELP_TOPIC will have to be defined. +* If you're using sockets, assign a port number in , and +* change IPPORT_CMD_PROTO to the IPPORT_xxx you've chosen. +* +* SunOS +* o link with genLib.a +* +* VxWorks +* o ioc must have genLib.vxa loaded +* +* BUGS +* o this program should use tasks.h to establish priorities, stack +* sizes, etc. +* o not all signals are caught +* o using this skeleton, it isn't possible to have several invocations +* of the program running simultaneously +* o under VxWorks, it would be possible to use taskDeleteHookAdd() as +* an additional mechanism for invoking cleanup code. (There is a +* corresponding routine in the SunOS LWP library.) This mechanism +* isn't used in this skeleton, in large part because most error +* conditions under VxWorks result in suspending the task rather than +* deleting it. +* +*-***************************************************************************/ +#include +#include +#include +#include +#include +#include + +#ifdef vxWorks +/*---------------------------------------------------------------------------- +* includes and defines for VxWorks compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +# include +# include +# define MAXPRIO 160 +#else +/*---------------------------------------------------------------------------- +* includes and defines for Sun compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +# define MAXPRIO 50 +#endif + + +/*/subhead ZZZ_CTX------------------------------------------------------- +* ZZZ_CTX - context information +* +* A zzz descriptor is the `master handle' which is used for +* handling business. +*----------------------------------------------------------------------------*/ +#define ZzzLock semTake(pglZzzCtx->semLock) +#define ZzzUnlock semGive(pglZzzCtx->semLock) +#define ZzzLockCheck semClear(pglZzzCtx->semLock) +#define ZzzLockInitAndLock semInit(pglZzzCtx->semLock) + +typedef struct { + TASK_ID id; /* ID of task */ + int status; /* status of task--initially OK */ + int stop; /* task requested to stop if != 0 */ + int stopped; /* task has stopped if != 0 */ + int serviceNeeded; /* needs servicing by another task */ + int serviceDone; /* servicing by other completed */ + int reopenIO; /* task's std... should be re-directed */ + int reopenIOoption;/* 0,1,2; option for zzzFreopenAllStd() */ + jmp_buf sigEnv; /* environment for longjmp at signal time */ +} ZZZ_TASK_INFO; + +typedef struct zzzCtx { + SEM_ID semLock; + ZZZ_TASK_INFO zzzTaskInfo; + ZZZ_TASK_INFO zzzInTaskInfo; + ZZZ_TASK_INFO zzzListenTaskInfo; + int listenSock; /* fd for server's "listen" socket */ + int inUse; /* 1 says "presently engaged" by a client */ + char *inUseName; /* name of client's host */ + int clientSock; /* fd for socket to client */ + int oldStdinFd; /* original fd for stdin, before redirect */ + int oldStdoutFd; /* original fd for stdout, before redirect */ + int oldStderrFd; /* original fd for stderr, before redirect */ + int showStack; /* show stack stats on task terminate */ +} ZZZ_CTX; + +/*----------------------------------------------------------------------------- +* prototypes +*----------------------------------------------------------------------------*/ +int zzz(); +void zzzInTask(); +void zzzInTaskSigHandler(); +void zzzListenTask(); +void zzzListenTSetReopen(); +void zzzListenTaskSigHandler(); +void zzzTask(); +void zzzTaskCmdCrunch(); +void zzzTaskSigHandler(); +long zzzInitAtStartup(); +long zzzInitInfo(); +long zzzSpawnIfNonexist(); +long zzzStartup(); + +/*----------------------------------------------------------------------------- +* global definitions +*----------------------------------------------------------------------------*/ +static CX_CMD glZzzCxCmd; +static CX_CMD *pglZzzCxCmd=NULL; +static ZZZ_CTX glZzzCtx; +static ZZZ_CTX *pglZzzCtx=NULL; +static char *glZzzId="zzz 11/27/90"; + + +#ifndef vxWorks + main(argc, argv) + int argc; /* number of command line args */ + char *argv[]; /* command line args */ + { +/*---------------------------------------------------------------------------- +* do some lwp initialization: +* o set allowable priorities between 1 and MAXPRIO +* o set up a cache of stacks for MAXTHREAD stacks +* +* A side effect is that this routine is turned into a lwp thread with a +* priority of MAXPRIO. +*---------------------------------------------------------------------------*/ + (void)pod_setmaxpri(MAXPRIO); + lwp_setstkcache(100000, 9); + + if (argc > 2) { + printf("Usage: %s [server or hostName]\n", argv[0]); + exit(1); + } + else if (argc == 2) + return zzz(argv[1]); + else + return zzz((char *)NULL); + } +#endif + +/*+/subr********************************************************************** +* NAME zzz - shell callable interface for zzz +* +* DESCRIPTION +* This routine is the only part of zzz which is intended to be +* called directly from the shell. Several functions are performed here: +* o in CLIENT mode, exec appropriately to cmdClient +* o if the zzzTask doesn't exist, spawn it. Ideally, this +* stage would also detect if the zzzTask is suspended +* and take appropriate action. +* o if the zzzInTask doesn't exist, spawn it. If it does exist, an +* error exists. +* o if the zzzListenTask doesn't exist, spawn it. If it does exist, +* an error exists. +* o in NON-SERVER mode, wait until the zzzInTask quits, then return +* to the shell. If other tasks belonging to zzz are being +* stopped, then this routine waits until they, too, are stopped +* before returning to the shell. +* o in SERVER mode, set up for socket-based I/O and return to the +* shell. +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o stack size and priority should come from tasks.h +* o there are lots of "holes" in detecting whether tasks exist, are +* suspended, etc. +* +*-*/ +int +zzz(option) +char *option; /* I NULL, "hostName", or "server" */ +{ + long stat; /* status return from calls */ + int serverStart=0; /* 1 says start as server */ + +/*----------------------------------------------------------------------------- +* if the option is present and not "server", then run as a client +*----------------------------------------------------------------------------*/ + if (option != NULL) { + if (strcmp(option, "server") != 0) { +#ifdef vxWorks + (void)printf("can't operate as client under VxWorks\n"); +#else + char portText[10]; + (void)sprintf(portText, "%d", IPPORT_CMD_PROTO); + execl("cmdClient", "cmdClient", option, portText, (char *)0); + (void)printf("couldn't exec to cmdClient\n"); +#endif + return ERROR; + } + else + serverStart = 1; + } + if (pglZzzCxCmd == NULL) { +/*----------------------------------------------------------------------------- +* startup initialization is done only if it's never been done before +*----------------------------------------------------------------------------*/ + pglZzzCtx = &glZzzCtx; + pglZzzCxCmd = &glZzzCxCmd; + stat = zzzInitAtStartup(pglZzzCtx, pglZzzCxCmd); + assertAlways(stat == OK); + } + + if (!serverStart) { + ZzzLock; + if (pglZzzCtx->inUse) { + ZzzUnlock; + (void)printf("zzz in use from %s\n", pglZzzCtx->inUseName); + return ERROR; + } + pglZzzCtx->inUse = 1; + ZzzUnlock; + } + stat = zzzStartup(serverStart, "shell"); + assertAlways(stat == OK); + +/*----------------------------------------------------------------------------- +* wait for zzzInTask to exit and then return to the shell. If other +* "zzz tasks" are also exiting, wait until their wrapups are complete. +* +* In server mode, though, just exit. +*----------------------------------------------------------------------------*/ + if (!serverStart) { + while (!pglZzzCtx->zzzInTaskInfo.stopped) + taskSleep(SELF, 1, 0); + if (pglZzzCtx->zzzTaskInfo.stop) { + while (!pglZzzCtx->zzzTaskInfo.stopped) + taskSleep(SELF, 1, 0); + } + if (pglZzzCtx->zzzListenTaskInfo.stop) { + while (!pglZzzCtx->zzzListenTaskInfo.stopped) + taskSleep(SELF, 1, 0); + } + pglZzzCxCmd = NULL; + } + + return OK; +} + +/*+/subr********************************************************************** +* NAME zzzTask - main processing task for zzz +* +* DESCRIPTION +* This program is a prototype skeleton for building programs +* which interact with a keyboard/window environment. The program +* can be started with any of: +* +* % cmdProto (runs as normal interactive program) +* % cmdProto server (runs as a server) +* % cmdProto server& (runs as a server) +* % cmdProto hostName (runs as a client to cmdProto on hostName) +* +* RETURNS +* void +* +* BUGS +* o text +* +*-*/ +void +zzzTask(ppCxCmd) +CX_CMD **ppCxCmd; +{ + long stat; + CX_CMD *pCxCmd; + int sigNum; + int i; + + pCxCmd = *ppCxCmd; + + genSigInit(zzzTaskSigHandler); + if ((sigNum = setjmp(pglZzzCtx->zzzTaskInfo.sigEnv)) != 0) { + (void)printf("zzzTask signal received: %d\n", sigNum); + pglZzzCtx->zzzTaskInfo.status = ERROR; + goto zzzTaskWrapup; + } + + stat = ca_task_initialize(); + assert(stat == ECA_NORMAL); + +/*---------------------------------------------------------------------------- +* "processing loop" +*---------------------------------------------------------------------------*/ + i = 20; + while (!pglZzzCtx->zzzTaskInfo.stop) { + stat = ca_pend_event(0.001); + assert(stat != ECA_EVDISALLOW); + if (--i <= 0) { + (void)printf(".\n"); + i = 20; + } + if (pglZzzCtx->zzzTaskInfo.reopenIO) { + (void)zzzFreopenAllStd(pglZzzCtx, + pglZzzCtx->zzzTaskInfo.reopenIOoption); + pglZzzCtx->zzzTaskInfo.reopenIO = 0; + } + if (pglZzzCtx->zzzInTaskInfo.serviceNeeded) { + zzzTaskCmdCrunch(ppCxCmd, pglZzzCtx); + pCxCmd = *ppCxCmd; + pglZzzCtx->zzzInTaskInfo.serviceNeeded = 0; + pglZzzCtx->zzzInTaskInfo.serviceDone = 1; + } + + fflush(stdout); /* flushes needed for VxWorks and */ + fflush(stderr); /* sockets */ + fflush(pCxCmd->dataOut); + + taskSleep(SELF, 0, 100000); /* wait .1 sec */ + } + +zzzTaskWrapup: + pglZzzCtx->zzzInTaskInfo.stop = 1; + pglZzzCtx->zzzListenTaskInfo.stop = 1; + while (pglZzzCtx->zzzInTaskInfo.stopped == 0 || + pglZzzCtx->zzzListenTaskInfo.stopped == 0) { + taskSleep(SELF, 1); + } + + /* put code for cleanup here */ + + stat = ca_task_exit(); + if (stat != ECA_NORMAL) + (void)printf("zzz: ca_task_exit error: %s\n", ca_message(stat)); + + if (pCxCmd->dataOutRedir) { + (void)printf("close for dataOut\n"); +#if 0 + fclose(pCxCmd->dataOut); +#endif + } + +#ifdef vxWorks + if (pglZzzCtx->showStack) + checkStack(pglZzzCtx->zzzTaskInfo.id); +#endif + + pglZzzCtx->zzzTaskInfo.stopped = 1; + + fflush(stdout); /* flushes needed for VxWorks and */ + fflush(stderr); /* sockets */ + +#ifdef vxWorks + taskDelete(SELF); +#endif +} + +/*+/subr********************************************************************** +* NAME zzzTaskCmdCrunch - process a command line +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +*-*/ +static void +zzzTaskCmdCrunch(ppCxCmd, pZzzCtx) +CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */ +ZZZ_CTX *pZzzCtx; /* IO pointer to zzz context */ +{ + CX_CMD *pCxCmd; /* local copy of pointer, for convenience */ + + pCxCmd = *ppCxCmd; + if (strcmp(pCxCmd->pCommand, "assert0") == 0) { +/*----------------------------------------------------------------------------- +* under SunOS, this generates SIGABRT; for VxWorks, SIGUSR1 +*----------------------------------------------------------------------------*/ + assertAlways(0); + } + else if (strcmp(pCxCmd->pCommand, "quit") == 0) { + pglZzzCtx->zzzInTaskInfo.stop = 1; + pglZzzCtx->zzzTaskInfo.stop = 1; +#ifndef vxWorks + pglZzzCtx->zzzListenTaskInfo.stop = 1; +#endif + } +#ifdef vxWorks + else if (strcmp(pCxCmd->pCommand, "showStack") == 0) + pZzzCtx->showStack = 1; +#endif + else if (strcmp(pCxCmd->pCommand, "trap") == 0) { + int *j; + j = (int *)(-1); + j = (int *)(*j); + } + else { +/*---------------------------------------------------------------------------- +* help (or illegal command) +*----------------------------------------------------------------------------*/ + (void)nextNonSpaceField(&pCxCmd->pLine, &pCxCmd->pField, + &pCxCmd->delim); + helpIllegalCommand(stdout, &pCxCmd->helpList, pCxCmd->pCommand, + pCxCmd->pField); + } + + return; +} + +/*+/subr********************************************************************** +* NAME zzzTaskSigHandler - signal handling +* +* DESCRIPTION +* These routines set up for the signals to be caught for zzzTask +* and handle the signals when they occur. +* +* RETURNS +* void +* +* BUGS +* o not all signals are caught +* o under VxWorks, taskDeleteHookAdd isn't used +* o it's not clear how useful it is to catch the signals which originate +* from the keyboard +* +*-*/ +static void +zzzTaskSigHandler(signo) +int signo; +{ +#ifndef vxWorks + if (signo == SIGPIPE) { + if (zzzFreopenAllStd(pglZzzCtx, 0) != OK) + assertAlways(0); + if (!pglZzzCtx->zzzInTaskInfo.stop) { + (void)printf("client connection broken\n"); + pglZzzCtx->zzzInTaskInfo.stop = 1; + } + return; + } +#endif + printf("entered zzzTaskSigHandler for signal:%d\n", signo); + signal(signo, SIG_DFL); + longjmp(pglZzzCtx->zzzTaskInfo.sigEnv, signo); +} + +/*+/subr********************************************************************** +* NAME zzzInTask - handle the keyboard and keyboard commands +* +* DESCRIPTION +* Gets input text and passes the input to zzzTask. +* +* This task exists for two primary purposes: 1) avoid the possibility +* of blocking zzzTask while waiting for operator input; and +* 2) allow detaching zzzTask from the keyboard while still +* allowing zzzTask to run. +* +* It is important to note that this task does no command processing +* on zzzTask's behalf--all commands relating to zzzTask +* specifically are executed in zzzTask's own context. Some +* commands are related to getting input--these commands are processed +* by this task and are never visible to zzzTask. +* +* This task waits for input to be available from the keyboard. When +* an input line is ready, this task does some preliminary processing: +* o if the command is "bg", this task wraps itself up and returns. +* o if the command is "close", this task wraps itself up and returns. +* o if the command is "dataOut", this task sets a new destination +* for data output, closing the previous destination, if appropriate, +* and opening the new destination, if appropriate. +* o if the command is "source", this task pushes down a level in +* the command context and begins reading commands from the file. +* When EOF occurs on the file, zzzInTask closes the file, pops +* to the previous level in the command context, and resumes +* reading at that level. +* o if the command is "quit" (or ^D), zzzTask is signalled; this +* task continues running. The expectation is that zzzTask +* will eventually set this task's .stop flag. +* o otherwise, zzzTask is signalled and this task +* goes into a sleeping loop until zzzTask signals +* that it is ready for the next command. +* +* Ideally, this task would also support a mechanism for zzzTask +* to wait explicitly for keyboard input. An example would be when +* operator permission is needed prior to taking an action. +* +* RETURNS +* void +* +* BUGS +* o doesn't handle errors from zzzFreopenAllStd +* o doesn't support flexible redirection of output +* o the implementation of "dataOut" is clumsy +* +*-*/ +void +zzzInTask(ppCxCmd) +CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */ +{ + int readDone=0; + int sigNum; + + pglZzzCtx->zzzInTaskInfo.serviceDone = 1; + +#ifdef vxWorks + genSigInit(zzzInTaskSigHandler); + if ((sigNum = setjmp(pglZzzCtx->zzzInTaskInfo.sigEnv)) != 0) { + (void)printf("zzzInTask signal received: %d\n", sigNum); + pglZzzCtx->zzzInTaskInfo.status = ERROR; + goto zzzInTaskWrapup; + } +#endif + + while (!pglZzzCtx->zzzInTaskInfo.stop) { + + if (pglZzzCtx->zzzInTaskInfo.reopenIO) { + (void)zzzFreopenAllStd(pglZzzCtx, + pglZzzCtx->zzzInTaskInfo.reopenIOoption); + pglZzzCtx->zzzInTaskInfo.reopenIO = 0; + } + + if (pglZzzCtx->zzzInTaskInfo.serviceDone == 1) { +/*----------------------------------------------------------------------------- +* if processing of the previous input has completed, acquire the next +* input line. If EOF is signalled for input (which never happens for +* a source'd file) and if input is NOT from the keyboard (i.e., input +* is from a socket), then simulate a close command. +*----------------------------------------------------------------------------*/ + cmdRead(ppCxCmd, &pglZzzCtx->zzzInTaskInfo.stop); + if ((*ppCxCmd)->inputEOF && + strcmp(pglZzzCtx->inUseName, "shell") != 0) { + pglZzzCtx->zzzInTaskInfo.stop = 1; + } + if (!pglZzzCtx->zzzInTaskInfo.stop) + readDone = 1; + } + +/*----------------------------------------------------------------------------- +* When some input is actually received, process it. +*----------------------------------------------------------------------------*/ + if (readDone) { + readDone = 0; + + if (nextANField(&(*ppCxCmd)->pLine, &(*ppCxCmd)->pCommand, + &(*ppCxCmd)->delim) < 1) + ; /* ignore blank line (and those with non-AN commands) */ + else if (strcmp((*ppCxCmd)->pCommand, "bg") == 0) { + if (strcmp(pglZzzCtx->inUseName, "shell") != 0) + (void)printf("bg allowed only from shell\n"); + else if (cmdBgCheck(*ppCxCmd) == OK) + goto zzzInTaskDone; + } + else if (strcmp((*ppCxCmd)->pCommand, "close") == 0) { + if (*ppCxCmd != (*ppCxCmd)->pCxCmdRoot) + (void)printf("close illegal in source'd file\n"); + else if (strcmp(pglZzzCtx->inUseName, "shell") == 0) + (void)printf("close illegal--zzz not running as server\n"); + else + pglZzzCtx->zzzInTaskInfo.stop = 1; + } + else if (strcmp((*ppCxCmd)->pCommand, "dataOut") == 0) { + if ((*ppCxCmd)->dataOutRedir) + fclose ((*ppCxCmd)->dataOut); + (*ppCxCmd)->dataOutRedir = 0; + if (nextNonSpaceField( &(*ppCxCmd)->pLine, &(*ppCxCmd)->pField, + &(*ppCxCmd)->delim) < 1) + (*ppCxCmd)->dataOut = stdout; + else { + (*ppCxCmd)->dataOut = fopen((*ppCxCmd)->pField, "a"); + if ((*ppCxCmd)->dataOut == NULL) { + (void)printf("couldn't open %s\n", (*ppCxCmd)->pField); + (*ppCxCmd)->dataOut = stdout; + } + else + (*ppCxCmd)->dataOutRedir = 1; + } + } + else if (strcmp((*ppCxCmd)->pCommand, "server") == 0) { + (void)printf( "\"server\" legal only on command line\n"); + (void)printf( "use help usage for more information\n"); + } + else if (strcmp((*ppCxCmd)->pCommand, "source") == 0) { + cmdSource(ppCxCmd); + } + else { + pglZzzCtx->zzzInTaskInfo.serviceDone = 0; + pglZzzCtx->zzzInTaskInfo.serviceNeeded = 1; + } + } + + if (!pglZzzCtx->zzzInTaskInfo.stop) + taskSleep(SELF, 0, 500000); /* sleep .5 sec */ + } + +zzzInTaskWrapup: +zzzInTaskDone: + while ((*ppCxCmd)->pPrev != NULL) + cmdCloseContext(ppCxCmd); + (*ppCxCmd)->inputEOF = 0; + +#ifdef vxWorks + if (pglZzzCtx->showStack) + checkStack(pglZzzCtx->zzzInTaskInfo.id); +#endif + pglZzzCtx->zzzInTaskInfo.stopped = 1; + pglZzzCtx->zzzListenTaskInfo.serviceNeeded = 0; + pglZzzCtx->zzzListenTaskInfo.serviceDone = 1; + +#ifdef vxWorks + taskDelete(SELF); +#endif +} + +/*+/subr********************************************************************** +* NAME zzzInTaskSigHandler - signal handling +* +* DESCRIPTION +* These routines set up for the signals to be caught for zzzInTask +* and handle the signals when they occur. +* +* RETURNS +* void +* +* BUGS +* o not all signals are caught +* o under VxWorks, taskDeleteHookAdd isn't used +* o it's not clear how useful it is to catch the signals which originate +* from the keyboard +* +*-*/ +static void +zzzInTaskSigHandler(signo) +int signo; +{ + printf("entered zzzInTaskSigHandler for signal:%d\n", signo); + signal(signo, SIG_DFL); + longjmp(pglZzzCtx->zzzInTaskInfo.sigEnv, signo); +} + +/*+/subr********************************************************************** +* NAME zzzListenTask - listen for client connections +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o should use VxWorks pty facility, which would allow more flexible +* output to socket--output wouldn't be line buffered +* o cleanup of listen socket isn't dealt with. Under SunOS, the system +* seems to clean up OK. Under VxWorks, NOT CHECKED YET. +* +* In fact, under SunOS, shutdown and/or close seem to inhibit proper +* cleanup by the system. +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +zzzListenTask(ppCxCmd) +CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */ +{ + long stat; + int sigNum; + char clientName[30]; + char *oldPrompt; + char sockPrompt[80]; + int serverStart=0; + int inUseInit=0; + + (void)printf("listen task started\n"); + +#ifdef vxWorks + genSigInit(zzzListenTaskSigHandler); + if ((sigNum = setjmp(pglZzzCtx->zzzListenTaskInfo.sigEnv)) != 0) { + (void)printf("zzzListenTask signal received: %d\n", sigNum); + pglZzzCtx->zzzListenTaskInfo.status = ERROR; + goto zzzListenTaskWrapup; + } +#endif + +/*----------------------------------------------------------------------------- +* establish a socket to use to listen for potential clients to connect +*----------------------------------------------------------------------------*/ + if (ezsCreateListenSocket(&pglZzzCtx->listenSock, IPPORT_CMD_PROTO) != 0) { + perror("create listen socket"); + goto zzzListenTaskWrapup; + } + + while (!pglZzzCtx->zzzListenTaskInfo.stop) { +/*----------------------------------------------------------------------------- +* check to see if a connect attempt has been made. If the server is +* already in use by a client, connect attempts will be rejected. +*----------------------------------------------------------------------------*/ + ZzzLock; + if (ezsListenExclusiveUse(&pglZzzCtx->clientSock, + &pglZzzCtx->listenSock, &pglZzzCtx->inUse, + clientName, glZzzId) < 0) { + ZzzUnlock; + perror("check for connect attempt"); + assertAlways(0); + } + ZzzUnlock; + + if (pglZzzCtx->inUse && inUseInit == 0) { +/*----------------------------------------------------------------------------- +* When a connect attempt succeeds, change stdin and stdout to use +* the socket, so that server I/O (i.e., I/O for this program) will +* automatically be re-directed to the client. This must be done +* for all tasks in this program. +* +* The prompt is changed to a special prompt which ends witn \n. This +* is done to avoid buffer flushing problems on some operating systems. +* A special prefix of "#p#r#" is added to the normal prompt so that +* the client knows that some massaging needs to be done before +* displaying the prompt to the user. +*----------------------------------------------------------------------------*/ + (void)printf("%s connected\n", clientName); + if (zzzFreopenAllStd(pglZzzCtx, 1) != OK) + assertAlways(0); + oldPrompt = (*ppCxCmd)->prompt; + assert(strlen(oldPrompt) < 72); + (void)sprintf(sockPrompt, "#p#r#%s\n", oldPrompt); + (*ppCxCmd)->prompt = sockPrompt; + zzzListenTSetReopen(2); /* reopen all other tasks to socket */ + inUseInit = 1; + pglZzzCtx->zzzListenTaskInfo.serviceNeeded = 1; + pglZzzCtx->zzzListenTaskInfo.serviceDone = 0; + stat = zzzStartup(serverStart, clientName); + assertAlways(stat == OK); + } + + if (pglZzzCtx->zzzListenTaskInfo.serviceDone) { +/*----------------------------------------------------------------------------- +* When zzzInTask is done, reset everything back to normal +*----------------------------------------------------------------------------*/ + pglZzzCtx->zzzListenTaskInfo.serviceDone = 0; +#if 0 + shutdown(pglZzzCtx->clientSock, 2); +#endif + close(pglZzzCtx->clientSock); + pglZzzCtx->clientSock = -1; + pglZzzCtx->inUse = 0; + inUseInit = 0; + if (zzzFreopenAllStd(pglZzzCtx, 0) != OK) + assertAlways(0); + (void)printf("%s disconnected\n", pglZzzCtx->inUseName); + (*ppCxCmd)->prompt = oldPrompt; + zzzListenTSetReopen(0); /* reopen all other tasks to std... */ + } + taskSleep(SELF, 2, 0); /* sleep 2 seconds */ + } + +zzzListenTaskWrapup: + if (pglZzzCtx->clientSock != -1) { + shutdown(pglZzzCtx->clientSock, 2); + close(pglZzzCtx->clientSock); + } +#if 0 + shutdown(pglZzzCtx->listenSock, 2); + close(pglZzzCtx->listenSock); +#endif +#ifdef vxWorks + if (pglZzzCtx->showStack) + checkStack(pglZzzCtx->zzzListenTaskInfo.id); +#endif + pglZzzCtx->zzzInTaskInfo.stop = 1; + pglZzzCtx->zzzTaskInfo.stop = 1; + pglZzzCtx->zzzListenTaskInfo.stopped = 1; + +#ifdef vxWorks + taskDelete(SELF); +#endif +} +void +zzzListenTSetReopen(option) +int option; /* I option for reopen for use with zzzFreopenAllStd */ +{ + pglZzzCtx->zzzTaskInfo.reopenIOoption = option; + pglZzzCtx->zzzTaskInfo.reopenIO = 1; + pglZzzCtx->zzzInTaskInfo.reopenIOoption = option; + pglZzzCtx->zzzInTaskInfo.reopenIO = 1; +} + +/*+/subr********************************************************************** +* NAME zzzListenTaskSigHandler - signal handling +* +* DESCRIPTION +* These routines set up for the signals to be caught for zzzListenTask +* and handle the signals when they occur. +* +* RETURNS +* void +* +* BUGS +* o not all signals are caught +* o under VxWorks, taskDeleteHookAdd isn't used +* o it's not clear how useful it is to catch the signals which originate +* from the keyboard +* +*-*/ +static void +zzzListenTaskSigHandler(signo) +int signo; +{ +#ifdef vxWorks + if (signo == SIGPIPE) { + if (zzzFreopenAllStd(pglZzzCtx, 0) != OK) + assertAlways(0); + (void)printf("client connection broken\n"); + pglZzzCtx->zzzInTaskInfo.stop = 1; + return; + } +#endif + printf("entered zzzListenTaskSigHandler for signal:%d\n", signo); + signal(signo, SIG_DFL); + longjmp(pglZzzCtx->zzzListenTaskInfo.sigEnv, signo); +} + +/*+/subr********************************************************************** +* NAME zzzFreopenAllStd - reopen stdin, stdout, and stderr +* +* DESCRIPTION +* +* RETURNS +* OK, or +* ERROR (an error message has been printed) +* +* BUGS +* o text +* +*-*/ +static int +zzzFreopenAllStd(pCtx, option) +ZZZ_CTX *pCtx; /* IO pointer to zzz context */ +int option; /* I activity selector: + 0 set back to use old fd's + 1 use client socket, save old fd's + 2 use client socket, don't save old fd's */ +{ + if (option == 0) { + if (ezsFreopenToFd(stdin, &pCtx->oldStdinFd, NULL) < 0) { + perror("restore stdin"); + return ERROR; + } + if (ezsFreopenToFd(stdout, &pCtx->oldStdoutFd, NULL) < 0) { + perror("restore stdout"); + return ERROR; + } + if (ezsFreopenToFd(stderr, &pCtx->oldStderrFd, NULL) < 0) { + perror("restore stderr"); + return ERROR; + } + } + else if (option == 1) { + if (ezsFreopenToFd(stdin, &pCtx->clientSock, &pCtx->oldStdinFd) < 0) { + perror("reopen stdin"); + return ERROR; + } + if (ezsFreopenToFd(stdout, &pCtx->clientSock, &pCtx->oldStdoutFd) < 0) { + perror("reopen stdout"); + return ERROR; + } + if (ezsFreopenToFd(stderr, &pCtx->clientSock, &pCtx->oldStderrFd) < 0) { + perror("reopen stderr"); + return ERROR; + } + } + else if (option == 2) { + if (ezsFreopenToFd(stdin, &pCtx->clientSock, NULL) < 0) { + perror("reopen stdin"); + return ERROR; + } + if (ezsFreopenToFd(stdout, &pCtx->clientSock, NULL) < 0) { + perror("reopen stdout"); + return ERROR; + } + if (ezsFreopenToFd(stderr, &pCtx->clientSock, NULL) < 0) { + perror("reopen stderr"); + return ERROR; + } + } + return OK; +} + +/*+/subr********************************************************************** +* NAME zzzInitAtStartup - initialization for zzz +* +* DESCRIPTION +* Perform several initialization duties: +* o initialize the zzz context +* o initialize ZzzLock +* o initialize the command context block +* o initialize the help information +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +*-*/ +static long +zzzInitAtStartup(pZzzCtx, pCxCmd) +ZZZ_CTX *pZzzCtx; +CX_CMD *pCxCmd; +{ + /* code to initialize zzz context here */ + pZzzCtx->inUse = 0; + pZzzCtx->listenSock = -1; + pZzzCtx->clientSock = -1; + pZzzCtx->oldStdinFd = -1; + pZzzCtx->oldStdoutFd = -1; + pZzzCtx->oldStderrFd = -1; + pZzzCtx->showStack = 0; + + ZzzLockInitAndLock; + ZzzUnlock; + + pCxCmd->input = stdin; + pCxCmd->inputEOF = 0; + pCxCmd->inputName = NULL; + pCxCmd->dataOut = stdout; + pCxCmd->dataOutRedir = 0; + pCxCmd->prompt = " zzz: "; + pCxCmd->pPrev = NULL; + pCxCmd->pCxCmdRoot = pCxCmd; + +/*----------------------------------------------------------------------------- +* initialize the tasks; none of them should exist +*----------------------------------------------------------------------------*/ + if (zzzInitInfo("zzzTask", &pglZzzCtx->zzzTaskInfo) != OK) + return ERROR; + if (zzzInitInfo("zzzInTask", &pglZzzCtx->zzzInTaskInfo) != OK) + return ERROR; + if (zzzInitInfo("zzzListenT", &pglZzzCtx->zzzListenTaskInfo) != OK) + return ERROR; + +/*----------------------------------------------------------------------------- +* help information initialization +*----------------------------------------------------------------------------*/ + helpInit(&pCxCmd->helpList); +/*----------------------------------------------------------------------------- +* help info--generic commands +*----------------------------------------------------------------------------*/ + helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpCmds, "commands", "\n\ +Generic commands are:\n\ + bg\n\ + close (use help usage for more info)\n\ + dataOut [filePath] (default is to normal output)\n\ + help [topic]\n\ + quit (or ^D) (use help usage for more info)\n\ + source filePath\n\ +"); +/*----------------------------------------------------------------------------- +* help info--bg command +*----------------------------------------------------------------------------*/ + helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpBg, "bg", "\n\ +The bg command under vxWorks allows the zzz process to continue\n\ +running without accepting commands from the keyboard. This allows the\n\ +vxWorks shell to be used for other purposes without the need to\n\ +terminate zzz.\n\ +\n\ +Under SunOS, no bg command is directly available. Instead, if zzz\n\ +is being run from the C shell (csh), it can be put in the background\n\ +using several steps from the keyboard (with the first % on each line being\n\ +the prompt from csh):\n\ + type ^Z, then type\n\ + % bg %zzz\n\ +\n\ +To move zzz to the foreground, type\n\ + % fg %zzz\n\ +"); +/*----------------------------------------------------------------------------- +* help info--zzz-specific commands +*----------------------------------------------------------------------------*/ + helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpCmdsSpec, "commands", "\n\ +zzz-specific commands are:\n\ + abcdef\n\ +\n\ +Output from commands flagged with * can be routed to a file by using the\n\ +\"dataOut filePath\" command. The present contents of the file are\n\ +preserved, with new output being written at the end.\n\ +"); +/*----------------------------------------------------------------------------- +* help info--zzz usage information +*----------------------------------------------------------------------------*/ + helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpUsage, "usage", "\n\ +zzz performs some functions. This is a skeleton program.\n\ +\n\ +For information on moving zzz into the background, use the \"help bg\"\n\ +command.\n\ +\n\ +zzz can be run as a server by:\n\ + under SunOS: % zzz server\n\ + under VxWorks: > zzz \"server\"\n\ +\n\ +zzz can be run as a client connecting to a zzz server on \"hostName\" by:\n\ + under SunOS: % zzz hostName\n\ + under VxWorks: > zzz \"hostName\"\n\ +\n\ + When running zzz as a client, two commands are of special interest:\n\ +\n\ + close shuts down the client but leaves the server running\n\ + quit shuts down both the client and the server\n\ +"); +} + +/*+/subr********************************************************************** +* NAME zzzInitInfo - initialize a task info block +* +* DESCRIPTION +* +* RETURNS +* OK, or +* ERROR if name != NULL and task already exists +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +static long +zzzInitInfo(name, pInfo) +char *name; /* I name of task */ +ZZZ_TASK_INFO *pInfo; /* IO pointer to task information block */ +{ +#ifdef vxWorks + if (name != NULL && taskNameToId(name) != ERROR) + return ERROR; +#endif + + pInfo->status = OK; + pInfo->stop = 0; + pInfo->stopped = 1; + pInfo->serviceNeeded = 0; + pInfo->serviceDone = 0; + pInfo->reopenIO = 0; + pInfo->reopenIOoption = 0; + + return OK; +} + +/*+/subr********************************************************************** +* NAME zzzSpawnIfNonexist - spawn a task if it doesn't exist +* +* DESCRIPTION +* Checks to see if the task exists and spawns it if not. +* +* RETURNS +* OK, or +* ERROR if an error occurs during the spawn +* +* BUGS +* o handles only 3 arguments +* +*-*/ +static long +zzzSpawnIfNonexist(pCxCmd, name, pInfo, priority, stackSize, + routine, pArg1, pArg2, pArg3) +CX_CMD *pCxCmd; /* I pointer to command context */ +char *name; /* I name to use for task */ +ZZZ_TASK_INFO *pInfo; /* IO pointer to task info for task */ +int priority; /* I priority for spawn */ +int stackSize; /* I size of stack for task */ +int (*routine)(); /* I routine to spawn */ +void *pArg1; /* I argument */ +void *pArg2; /* I argument */ +void *pArg3; /* I argument */ +{ + if (pInfo->stopped) { + pInfo->stopped = 0; + pInfo->stop = 0; + pInfo->status = OK; + pInfo->id = taskSpawn(name, priority, VX_STDIO | VX_FP_TASK, + stackSize, routine, pArg1, pArg2, pArg3); + } + if (GenTaskNull(pInfo->id)) { + pInfo->stopped = 1; + pInfo->status = ERROR; + (void)printf("error spawning %s\n", name); + return ERROR; + } + + return OK; +} + +/*+/subr********************************************************************** +* NAME zzzStartup - start zzz into operation +* +* DESCRIPTION +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +static long +zzzStartup(serverStart, name) +int serverStart; /* I 1 says starting up as server */ +char *name; /* I name of client */ +{ + long stat; /* status return from calls */ + + pglZzzCtx->inUseName = name; + + if (serverStart) { + if (!pglZzzCtx->zzzTaskInfo.stopped) { + (void)printf("zzz already running--can't specify server"); + pglZzzCtx->inUse = 0; + return ERROR; + } + } + +/*----------------------------------------------------------------------------- +* spawn the necessary tasks, depending on startup mode. If the +* zzzInTask is being spawned, set its .serviceNeeded flag. +*----------------------------------------------------------------------------*/ + stat = zzzSpawnIfNonexist(pglZzzCxCmd, "zzzTask", + &pglZzzCtx->zzzTaskInfo, MAXPRIO, 50000, zzzTask, + (void *)&pglZzzCxCmd, (void *)NULL, (void *)NULL); + if (stat == ERROR) + return ERROR; + + if (!serverStart) { + stat = zzzSpawnIfNonexist(pglZzzCxCmd, "zzzInTask", + &pglZzzCtx->zzzInTaskInfo, MAXPRIO, 50000, zzzInTask, + (void *)&pglZzzCxCmd, (void *)NULL, (void *)NULL); + if (stat == ERROR) { + pglZzzCtx->inUse = 0; + return ERROR; + } + } + else { + pglZzzCtx->inUse = 0; + stat = zzzSpawnIfNonexist(pglZzzCxCmd, "zzzListenT", + &pglZzzCtx->zzzListenTaskInfo, MAXPRIO, 50000, zzzListenTask, + (void *)&pglZzzCxCmd, (void *)NULL, (void *)NULL); + if (stat == ERROR) + return ERROR; + } + return OK; +} diff --git a/src/util/pprPlot.c b/src/util/pprPlot.c new file mode 100644 index 000000000..036a126f2 --- /dev/null +++ b/src/util/pprPlot.c @@ -0,0 +1,4835 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 12-04-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 12-04-90 rac initial version + * .02 mm-dd-yy rac version 1.0, installed in SCCS + * + * make options + * -DvxWorks makes a version for VxWorks + * -DSUNVIEW to use SunView window system + * -DXWINDOWS to use X window system + * -DNDEBUG don't compile assert() checking + * -DPPR_TEST compile the embedded main program for testing + * + * UW is an "edit time" switch which sets the test program + * to use pprWinOpenUW and play like a user program which + * does all its own windowing operations. + */ +/*+/mod*********************************************************************** +* TITLE pprSubr.c - portable plotting routines +* +* DESCRIPTION +* These routines support simple 2-D plotting in a "window". The +* window can be either an actual display window, a PostScript +* printer page, or an EPS file for Encapsulated PostScript. +* +* QUICK REFERENCE +* PPR_AREA *pArea; pointer to a plot area +* PPR_WIN *pWin; pointer to a plot "window" +* +* void pprAnnotX( pArea, offset, dataL, dataR, nDiv, +* drawLine, xLabel, xAnnot, angle ) +* void pprAnnotY( pArea, offset, dataB, dataT, nDiv, +* drawLine, yLabel, yAnnot, angle ) +* void pprAnnotYMark( pArea, offset, markNum ) +* void pprAreaClose( pArea ) +* void pprAreaErase( pArea fracL, fracB, fracR, fracT ) +* PPR_AREA *pprAreaOpen( pWin, fracL, fracB, fracR, fracT, +* dataL, dataB, dataR, dataT, +* nXint, nYint, charHt ) +* PPR_AREA *pprAreaOpenDflt( pWin, fracL, fracB, fracR, fracT, +* dataL, dataB, dataR, dataT ) +* void pprAreaRescale( pArea, dataL, dataB, dataR, dataT ) +* long pprAreaSetAttr( pArea, code, attrVal, pAttr ) +* code: PPR_ATTR_{CLIP,COLORNUM,BG,FG,KEYNUM,LINE_THICK,NOCLIP,PATT_ARRAY} +* void pprAutoEnds( dbl1, dbl2, >pNewDbl1, >pNewDbl2 ) +* void pprAutoInterval( dbl1, dbl2, >pNint ) +* void pprAutoRangeD( dblAry, nPts, >minDbl, >maxDbl ) +* void pprAutoRangeF( fltAry, nPts, >minDbl, >maxDbl ) +* void pprAutoRangeL( lngAry, nPts, >minDbl, >maxDbl ) +* void pprAutoRangeS( shtAry, nPts, >minDbl, >maxDbl ) +* void pprChar( pArea, xDbl, yDbl, char, charHt, angle ) +* double pprCos_deg( angle ) +* void pprCvtDblToTxt( >text, width, dblVal, nSigDigits ) +* void pprGrid( pArea ) +* void pprGridErase( pArea ) +* void pprGridLabel( pArea, xLabel, xAnnot, yLabel, yAnnot, angle) +* void pprLineD( pArea, xDblAry, yDblAry, nPts ) +* void pprLineF( pArea, xFltAry, yFltAry, nPts ) +* void pprLineL( pArea, xLngAry, yLngAry, nPts ) +* void pprLineS( pArea, xShtAry, yShtAry, nPts ) +* void pprLineSegD( pArea, xDbl1, yDbl1, xDbl2, yDbl2 ) +* void pprLineSegL( pArea, xLng1, yLng1, xLng2, yLng2 ) +* void pprMarkD( pArea, xDbl1, yDbl1, markNum ) +* void pprMarkL( pArea, xLng1, yLng1, markNum ) +* void pprMoveD( pArea, xDbl1, yDbl1, penDown ) +* void pprPerim( pArea ) +* void pprPerimErase( pArea ) +* void pprPerimLabel( pArea, xLabel, xAnnot, yLabel, yAnnot, angle) +* void pprPointD( pArea, xDbl1, yDbl1 ) +* void pprPointL( pArea, xLng1, yLng1 ) +* double pprSin_deg( angle ) +* void pprText( pArea, xDbl, yDbl, text, just, charHt, angle) +* just: PPR_TXT_{CEN,RJ,LJ} +* void pprWinClose( pWin ) +* void pprWinErase( pWin ) +* void pprWinInfo( pWin, >pXpos, >pYpos, >pXwid, >pYht ) +* int pprWinIsMono( pWin ) +* long pprWinLoop( pWin, drawFn, pDrawArg ) +* void drawFn(pWin, pDrawArg) +* PPR_WIN *pprWinOpen( winType, dispName, winTitle, xPos, yPos, xWid,yHt) +* winType: PPR_WIN_{SCREEN,POSTSCRIPT,EPS} +* PPR_WIN *pprWinOpenUW( pFrame, pCanvas, NULL, NULL ) +* PPR_WIN *pprWinOpenUW( ppDisp, pWindow, pGC, NULL ) +* void pprWinReplot( pWin, drawFn, pDrawArg ) +* long pprWinSetAttr( pWin, code, pAttr ) +* code: PPR_ATTR_{COLORNUM,GC,KEYNUM,LINE_THICK,PATT_ARRAY} +* +* DESCRIPTION (continued) +* Plotting is done within "plot areas" which are defined within +* the window. Plot areas can be as large as the window, and they +* can overlap, if desired. Clipping service is optional, since +* plot areas are often calibrated for the "normal" range of data, +* and it is useful to see what's happening if the data are outside +* the normal range. +* +* One non-intuitive aspect of the term "plot area" is that the +* usual case ALWAYS plots outside the plot area. Generally, this +* happens when annotations and labels are added to a grid. The +* plot area itself only specified where the edges of the grid were +* to be drawn. Because of this, determining the size and placement +* of a plot area must take into account the necessary margins for +* text. (pprAreaOpenDflt automatically takes care of margins.) +* +* Most plotting is done using "data coordinates", which are the +* actual data values. In some cases, coordinates or distances are +* specified in terms of a fraction of the window height (see, for +* example, pprAreaOpen or pprText). +* +* Also provided in this package are some routines for interacting +* with the "window". In many cases, this means that a plotting +* program can totally avoid any knowledge of, or dependence on, a +* particular windowing environment. This makes easily available +* the capability for producing hard copies of plots on a PostScript +* printer. +* +* Many routines in this package require that values be +* represented with type 'double'. In some cases, however, other +* data types are directly supported, so that the caller doesn't +* need to alter the way data are stored. +* +* BUGS +* o Only linear axes are presently supported +* o There is presently no mechanism to allow a calling program to +* do other work while a plot is on the screen. It would be nice +* for a program to be able to periodically check with this package +* to see if resize or expose events need to be serviced. +* o The SunView version of this package won't run properly with +* programs which use the SunOs LightWeight Process library. +* o The SunView version of this package doesn't handle color. +* +* EXAMPLE +* The following program plots the first 80 points of a parabola. The +* size and position of the plot window can be freely changed; the +* window can be covered and exposed, iconified and de-iconified, etc. +* When a `click right' is done in the plot window, the window is +* closed. A PostScript file named "testPS" is produced; the size of +* the plot in this file is about the same as it was when the plot +* window was closed. (The plot window can be closed using the `quit' +* item on the window's title bar menu (but not under X!!).) +* +* The program is compiled and linked for SunView with the following +* command. (It will be necessary to find where pprPlot.o and +* pprPlotDefs.h are located and possibly `fix up' the command.) +* +* cc plotTest.c pprPlot.o -lsuntool -lsunwindow -lpixrect -lm +* +* The program is compiled and linked for X with the following +* command. (It will be necessary to find where pprPlot.o and +* pprPlotDefs.h are located and possibly `fix up' the command.) +* +* cc plotTest.c pprPlot.o -lX11 -lm +* +* (As of Feb 6, 1991, plotTest.c is available on the AT-division +* open network in ~gta/ar/plotTest.c. It should be copied into +* the user's working directory before using it.) +* +* (As of Feb 6, 1991, pprPlot is available on the AT-division +* open network in ~gta. Until access to this software has been +* formalized, use the following for Sun3/SunView: +* cc plotTest.c -I/home/obiwan/gtacs/h /home/obiwan/gtacs/ar/pprPlotS.o +* -lsuntool -lsunwindow -lpixrect -lm +* (For Sun3/X, use the following: +* cc plotTest.c -I/home/obiwan/gtacs/h /home/obiwan/gtacs/ar/pprPlotX.o +* -lX11 -lm +* (For Sun4 builds, use "/ar/ar4/" in place of "/ar/" in the above +* commands.) +* +* #include +* #include +* +* #define NPTS 80 +* ---------------------------------------------------------------------------- +* define a structure for holding the data. This is needed because the +* replot routine (which does the actual plotting) is only given a pointer +* to the information it needs. +* ---------------------------------------------------------------------------- +* typedef struct { +* int nPts; +* float x[NPTS]; +* float y[NPTS]; +* double xMin; +* double xMax; +* double yMin; +* double yMax; +* } MY_DATA; +* +* void replot(); +* +* main() +* { +* int i; +* int x=0,y=0,width=0,height=0; +* PPR_WIN *pWin; +* MY_DATA myData; +* long stat; +* +* ---------------------------------------------------------------------------- +* generate the data in the structure. Once it's generated, figure out +* the range of values and put the range into the structure using +* pprAutoRangeF. This specific routine was chosen because the data +* is in "float" arrays. Different forms of data would result in choosing +* a different routine. +* +* The routine pprAutoEnds is available to "round" the endpoints of the +* axes to "nicer" values. Depending on the application, it will be +* attractive to use this following the pprAutoRange call. +* ---------------------------------------------------------------------------- +* myData.nPts = NPTS; +* for (i=0; ixMin, pMyData->yMin, pMyData->xMax, pMyData->yMax, +* 5, 5, charHt); +* pprPerimLabel(pArea, "x label", NULL, "y label", NULL, 0.); +* +* pprLineF(pArea, pMyData->x, pMyData->y, pMyData->nPts); +* +* pprAreaClose(pArea); +* +* return; +* } +* +*-***************************************************************************/ + +#ifdef vxWorks +/*---------------------------------------------------------------------------- +* includes and defines for VxWorks compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +#else +/*---------------------------------------------------------------------------- +* includes and defines for Sun compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +# include +#endif + +#define PPR_PRIVATE +#include + +void pprAnnotX_gen(); +void pprAnnotY_gen(); +void pprArcD_gen(); +void pprLineSegPixD_ac(); +void pprLineSegPixD_wc(); +void pprLineSegDashD_wc(); +void pprText_gen(); + + +/*----------------------------------------------------------------------------- +* assert macros, so that this package can be portable without having to link +* to genLib.a +*----------------------------------------------------------------------------*/ + +#ifndef NDEBUG +# define PprAssert(expr) ((void)((expr) || pprAssertFail(__FILE__, __LINE__))) +#else +# define PprAssert(expr) ((void)0) +#endif +#define PprAssertAlways(expr) \ + ((void)((expr) || pprAssertFail(__FILE__, __LINE__))) +pprAssertFail(fileName, lineNum) +char *fileName; +int lineNum; +{ + (void)fprintf(stderr, "pprAssertFail: in file %s line %d\n", + fileName, lineNum); +#ifdef vxWorks + if (kill(taskIdSelf(), SIGUSR1) == ERROR) { + int *j; + j = (int *)(-1); + j = (int *)(*j); + } + exit(1); +#else + abort(); +#endif +} + +/*----------------------------------------------------------------------------- +* doubly linked list macros +*----------------------------------------------------------------------------*/ +#define DoubleListAppend(pItem,pHead,pTail) \ +{\ + pItem->pNext = NULL;\ + pItem->pPrev = pTail;\ + if (pTail != NULL)\ + pTail->pNext = pItem; /* link previous tail to here */\ + pTail = pItem;\ + if (pHead == NULL)\ + pHead = pItem; /* link to head if first item */\ +} + +#define DoubleListRemove(pItem,pHead,pTail) \ +{\ + if (pItem->pPrev != NULL)\ + (pItem->pPrev)->pNext = pItem->pNext; /* link prev to next */\ + else\ + pHead = pItem->pNext; /* link list head to next */\ + if (pItem->pNext != NULL)\ + (pItem->pNext)->pPrev = pItem->pPrev; /* link next to prev */\ + else\ + pTail = pItem->pPrev; /* link list tail to prev */\ + pItem->pNext = NULL;\ + pItem->pPrev = NULL;\ +} + +/*/subhead pprTest------------------------------------------------------------- +* +*----------------------------------------------------------------------------*/ +#ifdef PPR_TEST + +#ifndef vxWorks + main() { pprTest(); } +#endif +#define NPTS 80 +typedef struct { + double xMin; + double xMax; + double yMin; + double yMax; + int nPts; + float x[NPTS]; + float y[NPTS]; +} MY_DATA; + +void replot(); + +#ifdef SUNVIEW +struct pprWinWin { + Frame frame; + PPR_WIN *pWin1; + PPR_WIN *pWin2; + void *pMyData; +}; +#endif + +#define UW +#undef UW +pprTest() +{ + int i; + int x=0,y=0,width=0,height=0; + PPR_WIN *pWin, *pWin2; + MY_DATA myData; + long stat; +#ifdef SUNVIEW + Frame plotFrame; + Canvas plotCanvas, plotCanvas1, plotCanvas2; + struct pprWinWin winList; + void pprTestEvHandler(); +#elif defined XWINDOWS + Display *pDisp; /* pointer to X server connection */ + int screenNo; /* screen number */ + Window rootWindow; /* the root window on display */ + GC gc; /* graphics context */ + XEvent anEvent; /* a window event structure */ + Window plotWindow; /* the plot window on the display */ + Window subWin1, subWin2; + XSizeHints sizeHints; /* defaults for position and size */ +#endif + + for (i=0; ixMin, pMyData->yMin, pMyData->xMax, pMyData->yMax, + 5, 5, charHt); + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thin, NULL); + pprPerimLabel(pArea, "x label", NULL, "y label", NULL, 0.); + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, 0, NULL); + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + pprLineF(pArea, pMyData->x, pMyData->y, pMyData->nPts); + + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + for (x=1.; x<10.; x+=1.) + pprPointD(pArea, x, x*x+100.); + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thin, NULL); + for (x=1.; x<10.; x+=1.) + pprPointD(pArea, x, x*x+200.); + pprAnnotYMark(pArea, 0, 1); + + for (keyNum=0; keyNum<=PPR_NKEYS; keyNum++) { + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, keyNum, NULL); + pprLineSegD(pArea, 0., y, 50., y); + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + pprLineSegD(pArea, 50., y, 70., y); + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thin, NULL); + y -= 100.; + } + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, 0, NULL); + for (i=0; i<=PPR_NCOLORS; i++) { + pprAreaSetAttr(pArea, PPR_ATTR_COLORNUM, i, NULL); + pprLineSegD(pArea, 0., y, 50., y); + y -= 100.; + } + x = 1.; + for (i=0; iwinType == PPR_WIN_SCREEN) { +#ifdef XWINDOWS + XFlush(pWin->pDisp); +#endif + (void)printf("erase win, area, grid, or perim? (w,a,g,p, or cr) "); + fflush(stdout); + if (fgets(answer, 80, stdin) == NULL || answer[0] == '\n') + ; /* no action */ + else { + if (answer[0] == 'w') + pprWinErase(pWin); + else if (answer[0] == 'a') + pprAreaErase(pArea,.3,.3,.6,.9); + else if (answer[0] == 'p') + pprPerimErase(pArea); + else if (answer[0] == 'g') + pprGridErase(pArea); + (void)printf("done erasing: "); + fflush(stdout); + fgetc(stdin); + } + } +#endif + + pprAreaClose(pArea); + + return; +} +#endif + +/*/subhead test_SunView_EvHandler---------------------------------------------- +* +*----------------------------------------------------------------------------*/ +#if defined SUNVIEW && defined UW +void +pprTestEvHandler(window, pEvent, pArg) +Window window; +Event *pEvent; +void *pArg; +{ + struct pprWinWin *pWinList; + + pWinList = (struct pprWinWin *)window_get(window, WIN_CLIENT_DATA); + if (event_action(pEvent) == WIN_REPAINT) { + if (!window_get(window, FRAME_CLOSED)) { + if (window == pWinList->pWin1->canvas) + pprWinReplot(pWinList->pWin1, replot, pWinList->pMyData); + if (window == pWinList->pWin2->canvas) + pprWinReplot(pWinList->pWin2, replot, pWinList->pMyData); + } + } + else if (event_action(pEvent) == PPR_BTN_CLOSE) { + if (event_is_up(pEvent)) { + pprWinClose(pWinList->pWin1); + pprWinClose(pWinList->pWin2); + window_destroy(pWinList->frame); + } + } +} +#endif + +/*+/subr********************************************************************** +* NAME pprAnnotX - annotate an X axis, perhaps drawing a line and tick marks +* +* DESCRIPTION +* Annotate an X axis, placing annotations at the major tick intervals. +* +* If desired, an axis line is also drawn, with tick marks at the +* major intervals. The tick marks are drawn using the "generic" +* line attributes for the plot window. The axis line is drawn with +* the line attributes of the plot area; this allows using a dashed +* line pattern or color to associate an axis with a data line. +* +* The annotations and label are drawn using the default character +* height for the plot area, in the color, if any, for the plot area. +* +* An array of text strings can be supplied for annotating the tick +* intervals. This is useful if the desired annotations are text. +* If an annotation array isn't supplied, then numeric annotations +* are generated. +* +* To allow multiple calibrations for an axis, this routine accepts +* an `offset' argument. If this argument is greater than 0, then +* the annotation and labeling activity occurs that many lines (in +* the default character height for the plot area) below the axis +* which was established by pprAreaOpen. +* +* An alternate entry point, pprAnnotX_wc, is available to use the +* plot window's color for the annotation. +* +* RETURNS +* void +* +* BUGS +* o only linear axes are handled +* o doesn't presently support offset processing +* +* NOTES +* 1. Uses a space below the axis of 5 character heights. +* +* SEE ALSO +* pprAnnotY, pprGrid, pprPerim, pprAreaOpen, pprAreaSetAttr +* +*-*/ +void +pprAnnotX(pArea, offset, xLeft, xRight, xNint, drawLine, xLabel, xAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I offset as number of lines below yBot to annotate */ +double xLeft; /* I x data value at left end of axis */ +double xRight; /* I x data value at right end of axis */ +int xNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *xLabel; /* I label for x axis, or NULL; oriented horizontal */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ +{ + pprAnnotX_gen(pArea,offset,xLeft,xRight,xNint,drawLine,xLabel,xAnnot,angle, + pprLineSegD_ac, pprLineSegD, pprText); +} +void +pprAnnotX_wc(pArea,offset,xLeft,xRight,xNint,drawLine,xLabel,xAnnot,angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I offset as number of lines below yBot to annotate */ +double xLeft; /* I x data value at left end of axis */ +double xRight; /* I x data value at right end of axis */ +int xNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *xLabel; /* I label for x axis, or NULL; oriented horizontal */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ +{ + pprAnnotX_gen(pArea,offset,xLeft,xRight,xNint,drawLine,xLabel,xAnnot,angle, + pprLineSegD_wc, pprLineSegD, pprText_wc); +} +static void +pprAnnotX_gen(pArea,offset,xLeft,xRight,xNint,drawLine,xLabel,xAnnot,angle, + fnTick, fnLine, fnText) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I offset as number of lines below yBot to annotate */ +double xLeft; /* I x data value at left end of axis */ +double xRight; /* I x data value at right end of axis */ +int xNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *xLabel; /* I label for x axis, or NULL; oriented horizontal */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ +void (*fnTick)(); +void (*fnLine)(); +void (*fnText)(); +{ + double tickHalf, tick1, tick2; + int i; + double x, xInterval, y; + double xVal, xValInterval; + char text[80]; + int nCol=6; /* number of columns for annotation label */ + int sigDigits; /* sig digits to print */ + double maxVal; /* maximum of the end values for axis */ + + if (drawLine) { + tickHalf = pArea->tickHt / pArea->yScale; + tick1 = pArea->yBot - tickHalf; + tick2 = pArea->yBot + tickHalf; + } + maxVal = PprMax(PprAbs(xLeft),PprAbs(xRight)); + if (maxVal >= 100.) sigDigits = 0; + else if (maxVal >= 10.) sigDigits = 1; + else if (maxVal >= 1.) sigDigits = 2; + else sigDigits = 3; + + x = pArea->xLeft; + xInterval = (pArea->xRight - pArea->xLeft) / xNint; + xVal = xLeft; + xValInterval = (xRight - xLeft) / xNint; + y = pArea->yBot - 2. * pArea->charHt / pArea->yScale; + if (drawLine) + fnTick(pArea, x, tick1, x, tick2); + if (xAnnot == NULL) { + pprCvtDblToTxt(text, nCol, xVal, sigDigits); + fnText(pArea, x, y, text, PPR_TXT_LJ, 0., angle); + } + else + fnText(pArea, x, y, xAnnot[0], PPR_TXT_LJ, 0., angle); + for (i=1; ixRight; + if (drawLine) { + fnTick(pArea, x, tick1, x, tick2); +#if 0 + fnLine(pArea, xBase, pArea->yBot, xBase, pArea->yTop); +#endif + } + if (xAnnot == NULL) { + pprCvtDblToTxt(text, nCol, xRight, sigDigits); + fnText(pArea, x, y, text, PPR_TXT_RJ, 0., angle); + } + else + fnText(pArea, x, y, xAnnot[xNint], PPR_TXT_RJ, 0., angle); + if (xLabel != NULL) { + x = (pArea->xLeft + pArea->xRight) / 2.; + y = pArea->yBot - 4. * pArea->charHt / pArea->yScale; + fnText(pArea, x, y, xLabel, PPR_TXT_CEN, 0., angle); + } +} + +/*+/subr********************************************************************** +* NAME pprAnnotY - annotate a Y axis, perhaps drawing line and tick marks +* +* DESCRIPTION +* Annotate a Y axis, placing annotations at the major tick intervals. +* +* If desired, an axis line is also drawn, with tick marks at the +* major intervals. The tick marks are drawn using the "generic" +* line attributes for the plot window. The axis line is drawn with +* the line attributes of the plot area; this allows using a dashed +* line pattern or color to associate an axis with a data line. +* +* The annotations and label are drawn using the default character +* height for the plot area, in the color, if any, for the plot area. +* +* An array of text strings can be supplied for annotating the tick +* intervals. This is useful if the desired annotations are text. +* If an annotation array isn't supplied, then numeric annotations +* are generated. +* +* To allow multiple calibrations for an axis, this routine accepts +* an `offset' argument. If this argument is greater than 0, then +* the annotation and labeling activity occurs that many lines (in +* the default character height for the plot area) to the left of +* the axis which was established by pprAreaOpen. +* +* An alternate entry point, pprAnnotY_wc, is available to use the +* plot window's color for the annotation. +* +* RETURNS +* void +* +* BUGS +* o only linear axes are handled +* +* NOTES +* 1. Uses a space to the left of the axis of 12 character heights if +* annotations are horizontal, and a space of 5 character heights +* if they are vertical. +* +* SEE ALSO +* pprAnnotX, pprGrid, pprPerim, pprAreaOpen, pprAreaSetAttr +* +*-*/ +void +pprAnnotY(pArea, offset, yBot, yTop, yNint, drawLine, yLabel, yAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I number of lines to left of axis for annotation */ +double yBot; /* I y data value at bottom end of axis */ +double yTop; /* I y data value at top end of axis */ +int yNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *yLabel; /* I label for y axis, or NULL; oriented vertical */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ +{ + pprAnnotY_gen(pArea,offset,yBot,yTop,yNint,drawLine,yLabel,yAnnot,angle, + pprLineSegD_ac, pprLineSegD, pprText); +} +void +pprAnnotY_wc(pArea, offset, yBot, yTop, yNint, drawLine, yLabel, yAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I number of lines to left of axis for annotation */ +double yBot; /* I y data value at bottom end of axis */ +double yTop; /* I y data value at top end of axis */ +int yNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *yLabel; /* I label for y axis, or NULL; oriented vertical */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ +{ + pprAnnotY_gen(pArea,offset,yBot,yTop,yNint,drawLine,yLabel,yAnnot,angle, + pprLineSegD_wc, pprLineSegD, pprText_wc); +} +void +pprAnnotY_gen(pArea, offset, yBot, yTop, yNint, drawLine, yLabel, yAnnot, angle, + fnTick, fnLine, fnText) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I number of lines to left of axis for annotation */ +double yBot; /* I y data value at bottom end of axis */ +double yTop; /* I y data value at top end of axis */ +int yNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *yLabel; /* I label for y axis, or NULL; oriented vertical */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ +void (*fnTick)(); +void (*fnLine)(); +void (*fnText)(); +{ + double tickHalf, tick1, tick2; + int i; + double x; /* x coord for annotations */ + double xL; /* x coord for label */ + double xBase; /* base x coordinate */ + double y, yInterval; + double yVal, yValInterval; + char text[80]; + int nCol=6; /* number of columns for annotation label */ + int sigDigits; /* sig digits to print */ + double maxVal; /* maximum of the end values for axis */ + PPR_TXT_JUST just; /* justification flag for text */ + + xBase = pArea->xLeft - (double)offset * pArea->charHt / pArea->xScale; + if (drawLine) { + tickHalf = pArea->tickHt / pArea->xScale; + tick1 = xBase - tickHalf; + if (offset == 0) + tick2 = xBase + tickHalf; + else + tick2 = xBase; + } + maxVal = PprMax(PprAbs(yBot),PprAbs(yTop)); + if (maxVal >= 100.) sigDigits = 0; + else if (maxVal >= 10.) sigDigits = 1; + else if (maxVal >= 1.) sigDigits = 2; + else sigDigits = 3; + + if (angle == 0.) { + x = xBase - 2. * pArea->charHt / pArea->xScale; + xL = xBase - 10. * pArea->charHt / pArea->xScale; + } + else { + x = xBase - 2. * pArea->charHt / pArea->xScale; + xL = xBase - 4. * pArea->charHt / pArea->xScale; + } + y = pArea->yBot; + yInterval = (pArea->yTop - pArea->yBot) / yNint; + yVal = yBot; + yValInterval = (yTop - yBot) / yNint; + if (drawLine) + fnTick(pArea, tick1, y, tick2, y); + if (angle == 0.) just = PPR_TXT_RJ; + else just = PPR_TXT_LJ; + if (yAnnot == NULL) { + pprCvtDblToTxt(text, nCol, yVal, sigDigits); + fnText(pArea, x, y, text, just, 0., angle); + } + else + fnText(pArea, x, y, yAnnot[0], just, 0., angle); + if (angle == 0.) just = PPR_TXT_RJ; + else just = PPR_TXT_CEN; + for (i=1; iyTop; + if (angle == 0.) just = PPR_TXT_RJ; + else just = PPR_TXT_RJ; + if (yAnnot == NULL) { + pprCvtDblToTxt(text, nCol, yTop, sigDigits); + fnText(pArea, x, y, text, just, 0., angle); + } + else + fnText(pArea, x, y, yAnnot[yNint], just, 0., angle); + if (drawLine) { + fnTick(pArea, tick1, y, tick2, y); + fnLine(pArea, xBase, pArea->yBot, xBase, pArea->yTop); + } + if (yLabel != NULL) { + y = (pArea->yBot + pArea->yTop) / 2.; + fnText(pArea, xL, y, yLabel, PPR_TXT_CEN, 0., 90.); + } +} + +/*+/subr********************************************************************** +* NAME pprAnnotYMark - add plot marks to a Y axis annotation +* +* DESCRIPTION +* Draw two plot marks at the foot of the Y axis annotation, to allow +* associating the axis with a particular set of data. +* +* RETURNS +* void +* +* BUGS +* o only linear axes are handled +* +* SEE ALSO +* pprMark, pprAnnotY +* +* EXAMPLE +* +*-*/ +void +pprAnnotYMark(pArea, offset, markNum) +PPR_AREA *pArea; /* I pointer to plot area structure */ +int offset; /* I number of lines to left of axis for annotation */ +int markNum; /* I mark number */ +{ + double x, y; + + x = pArea->xLeft - (double)(offset+3) * pArea->charHt / pArea->xScale; + y = pArea->yBot - pArea->charHt * 2. / pArea->yScale; + pprMarkD(pArea, x, y, markNum); +} + +/*+/subr********************************************************************** +* NAME pprArc - draw an arc +* +* DESCRIPTION +* Draw an arc. The arc is specified by a radius and two angles. The +* angles, in degrees, specify the angle at which the arc starts and +* the angle at which it ends. An angle increment specifies both the +* direction of the arc and the size of the chords which approximate +* the arc. Angles are measured counter-clockwise from the positive +* X axis. +* +* The radius of the arc is treated as representing data values in the +* plot area. If both the X and Y axes of the plot area have the +* same data scaling, then circular arcs will be produced (assuming +* a square plot area). If the X scaling is not the same as the Y +* scaling, then elliptical arcs will be produced. +* +* The arc is drawn using the color, dashed line, and other attributes +* of the plot area. Alternate entry points are: +* +* pprArcD_ac uses the area color, but ignores other +* attributes +* +* RETURNS +* void +* +* SEE ALSO +* +*-*/ +void +pprArcD(pArea, xDbl, yDbl, radDbl, angle1, angle2, angleIncr) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double xDbl; /* I x data coordinate for center of arc */ +double yDbl; /* I y data coordinate for center of arc */ +double radDbl; +double angle1; /* I angle to start arc */ +double angle2; /* I angle to stop arc */ +double angleIncr; /* I size of steps in drawing arc */ +{ + pprArcD_gen(pArea, xDbl, yDbl, radDbl, angle1, angle2, angleIncr, + pprMoveD); +} +void +pprArcD_ac(pArea, xDbl, yDbl, radDbl, angle1, angle2, angleIncr) +PPR_AREA *pArea; +double xDbl, yDbl, radDbl, angle1, angle2, angleIncr; +{ + pprArcD_gen(pArea, xDbl, yDbl, radDbl, angle1, angle2, angleIncr, + pprMoveD_ac); +} +void +pprArcD_gen(pArea, xDbl, yDbl, radDbl, angle1, angle2, angleIncr, fn) +PPR_AREA *pArea; +double xDbl, yDbl, radDbl, angle1, angle2, angleIncr; +void (*fn)(); /* I line drawing function */ +{ + double x, y, angle; + int pen=0; + + if (angle1 > angle2 && angleIncr > 0.) { + while (angle1 > angle2) + angle2 += 360.; + } + else if (angle1 < angle2 && angleIncr < 0.) { + while (angle1 < angle2) + angle1 += 360.; + } + angle = angle1; + while (1) { + x = xDbl + radDbl * pprCos_deg(angle); + y = yDbl + radDbl * pprSin_deg(angle); + fn(pArea, x, y, pen); + pen = 1; + if (angle == angle2) + break; + if ((angle += angleIncr) > angle2) + angle = angle2; + } +} + +/*+/subr********************************************************************** +* NAME pprAreaClose - close a plot area +* +* DESCRIPTION +* Frees the memory associated with a plot area pointer and does other +* cleanup operations. This routine should be called prior to calling +* pprWinClose. +* +* RETURNS +* void +* +*-*/ +void +pprAreaClose(pArea) +PPR_AREA *pArea; /* I pointer to plot area structure */ +{ + PPR_WIN *pWin; + + pWin = pArea->pWin; + +#ifdef XWINDOWS + if (pWin->winType == PPR_WIN_SCREEN) { + if (pArea->attr.myGC) + XFree(pArea->attr.gc); + } +#endif + DoubleListRemove(pArea, pWin->pAreaHead, pWin->pAreaTail); + free((char *)pArea); +} + +/*+/subr********************************************************************** +* NAME pprAreaErase - erase an area within a plot window +* +* DESCRIPTION +* Erases an area within a plot window. +* +* RETURNS +* void +* +* SEE ALSO +* pprWinErase, pprGridErase, pprPerimErase +* +* NOTES +* 1. Another mode of calling pprAreaErase, in which the arguments are +* pixel offsets from the data area boundaries, can be invoked by +* having one or more of the arguments be greater than 1. or less +* than 0. +* +* EXAMPLES +* 1. Erase within a data area, preserving the perimeter line. +* +* pprAreaErase(pArea, 1., 1., -1., -1.); +* +*-*/ +void +pprAreaErase(pArea, wfracXleft, wfracYbot, wfracXright, wfracYtop) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double wfracXleft; /* I x window fraction of left edge of area */ +double wfracYbot; /* I y window fraction of bottom edge of area */ +double wfracXright; /* I x window fraction of right edge of area */ +double wfracYtop; /* I y window fraction of top edge of area */ +{ + int x,y,width,height; + + if (pArea->pWin->winType != PPR_WIN_SCREEN) + return; + if (wfracXleft<0. || wfracXleft>1. || wfracXright<0. || wfracXright>1. || + wfracYbot<0. || wfracYbot>1. || wfracYtop<0. || wfracYtop>1.) { + x = pArea->xPixLeft + wfracXleft; + y = pArea->yPixBot + wfracYbot; + width = pArea->xPixRight + wfracXright - x + 1.; + height = pArea->yPixTop + wfracYtop - y + 1.; + } + else { + x = wfracXleft * pArea->pWin->width; + y = wfracYbot * pArea->pWin->height; + width = (wfracXright - wfracXleft) * pArea->pWin->width; + height = (wfracYtop - wfracYbot) * pArea->pWin->height; + } +#ifdef SUNVIEW + y = pArea->pWin->height - y - height; + pw_writebackground(pArea->pWin->pw, x, y, width, height, PIX_SRC); +#elif defined XWINDOWS + y = pArea->pWin->height - y - height; + XClearArea(pArea->pWin->pDisp, pArea->pWin->plotWindow, + x, y, width, height, False); +#endif +} + +/*+/subr********************************************************************** +* NAME pprAreaOpen - initialize a plot area +* +* DESCRIPTION +* Initialize a plot area within the plot window. This initialization +* must be done before calling any of the routines which do actual +* plotting. +* +* This routine establishes a rectangular "data area" within the plot +* window. It is within the data area that data will be plotted. +* The size and position of the data area are specified in terms +* of fractions of the window size; they are expressed as +* "coordinates" of the lower left and upper right corners of the +* area. The data area specified in the call to pprAreaOpen does +* NOT include space for axis annotations and labels. (pprAreaOpenDflt +* can be used to automatically get the necessary margins.) +* +* This routine must also be told the data values at the two corners +* of the data area in order to determine scaling. +* +* In addition to establishing scaling, this routine accepts information +* about how many major divisions there are for each axis and what +* default character height is to be used for displaying text within +* the plot area (see pprText for more information). If any of these +* parameters is specified as zero, this routine chooses an appropriate +* value. +* +* The default line attributes for the plot are copied from those of +* the plot window. pprAreaSetAttr can be used to change them. Under +* X11, a gc is created for the plot area with the foreground and +* background being copied from the gc for the plot window; +* pprAreaSetAttr can be used to change the foreground and background. +* +* When plotting is complete for a plot area, pprAreaClose should +* be called. +* +* RETURNS +* pointer to plot area, or +* NULL +* +* BUGS +* o only linear calibration is handled +* +* SEE ALSO +* pprWinOpen, pprAreaOpenDflt, pprAreaSetAttr +* pprAutoEnds, pprAutoInterval, pprAutoRange +* pprText +* +* EXAMPLE +* 1. Set up an area which occupies the full width of the window, but +* which uses the middle third vertically. The range for x values +* is 0 to 100; for y, the range is -10 to 10. Both the x and y +* axes are to be divided into 5 intervals. +* +* Allow space below and to the left of the actual area for plotting +* for pprPerim to place labels and annotations. The required +* size of "margins" depends on the size of characters used, so a +* default size is determined (and put into effect as part of the +* pprAreaOpen call). +* +* PPR_AREA *pArea; +* double charHt, charHtX; +* +* charHt = PprDfltCharHt(.33, .67); +* charHtX = pprYFracToXFrac(pWin, charHt); +* pArea = pprAreaOpen(pWin, 0.+12.*charHtX, .33+6*charHt, 1., .67, +* 0., -10., 100., 10., 5, 5, charHt); +* ... +* pprAreaClose(pArea); +* +*-*/ +PPR_AREA * +pprAreaOpen(pWin, wfracXleft, wfracYbot, wfracXright, wfracYtop, + xLeft, yBot, xRight, yTop, xNint, yNint, charHt) +PPR_WIN *pWin; /* I pointer to plot window structure */ +double wfracXleft; /* I x window fraction of left edge of data area */ +double wfracYbot; /* I y window fraction of bottom edge of data area */ +double wfracXright; /* I x window fraction of right edge of data area */ +double wfracYtop; /* I y window fraction of top edge of data area */ +double xLeft; /* I x data value at left side of data area */ +double yBot; /* I y data value at bottom side of data area */ +double xRight; /* I x data value at right side of data area */ +double yTop; /* I y data value at top side of data area */ +int xNint; /* I x axis number of intervals; if <=0, a default + value is provided */ +int yNint; /* I y axis number of intervals; if <=0, a default + value is provided */ +double charHt; /* I value to use as default for character size, as + a fraction of window height; if <= 0., + a default value is provided */ +{ + PPR_AREA *pArea; /* pointer to plot area structure */ + + if ((pArea = (PPR_AREA *)malloc(sizeof(PPR_AREA))) == NULL) { + (void)printf("pprAreaOpen: couldn't malloc plot area struct\n"); + return NULL; + } + DoubleListAppend(pArea, pWin->pAreaHead, pWin->pAreaTail); + + pArea->pWin = pWin; + pArea->xFracLeft = wfracXleft; + pArea->xFracRight = wfracXright; + if (xNint <= 0) + pprAutoInterval(xLeft, xRight, &xNint); + pArea->xNint = xNint; + + pArea->yFracBot = wfracYbot; + pArea->yFracTop = wfracYtop; + if (yNint <= 0) + pprAutoInterval(yBot, yTop, &yNint); + pArea->yNint = yNint; + + pArea->charHt = charHt * pWin->height; + pArea->oldWinHt = pWin->height; + + pprAreaRescale(pArea, xLeft, yBot, xRight, yTop); + +#ifdef XWINDOWS + if (pWin->winType == PPR_WIN_SCREEN) { + pArea->attr.gc = XCreateGC(pWin->pDisp, pWin->plotWindow, 0, NULL); + XCopyGC(pWin->pDisp, pWin->attr.gc, GCForeground | GCBackground, + pArea->attr.gc); + pArea->attr.myGC = 1; + } +#endif + pArea->attr.pPatt = NULL; + pArea->attr.lineThick = 1; + pArea->attr.ltCurr = -1; + pArea->attr.clip = 0; + + return pArea; +} + +/*+/subr********************************************************************** +* NAME pprAreaOpenDflt - initialize a plot area using defaults +* +* DESCRIPTION +* Initialize a plot area within the plot window. This initialization +* must be done before calling any of the routines which do actual +* plotting. +* +* This routine is a variant on pprAreaOpen. It performs the functions +* of that routine, but uses some defaults rather than making the +* caller determine specific values. In particular, this routine: +* +* o sets a default character height +* +* o determines the number of major divisions for each axis +* +* o establishes, inside the plot area specified, margins which +* will be adequate for annotating and labeling the axes. +* +* See the description for pprAreaOpen for additional details. +* +* When plotting is complete for a plot area, pprAreaClose should +* be called. +* +* RETURNS +* pointer to plot area, or +* NULL +* +* BUGS +* o only linear calibration is handled +* +* SEE ALSO +* pprWinOpen, pprAreaOpen, pprAreaSetAttr +* pprAutoEnds, pprAutoInterval, pprAutoRange +* pprText +* +*-*/ +PPR_AREA * +pprAreaOpenDflt(pWin, wfracXleft, wfracYbot, wfracXright, wfracYtop, + xLeft, yBot, xRight, yTop) +PPR_WIN *pWin; /* I pointer to plot window structure */ +double wfracXleft; /* I x window fraction of left edge of plot area */ +double wfracYbot; /* I y window fraction of bottom edge of plot area */ +double wfracXright; /* I x window fraction of right edge of plot area */ +double wfracYtop; /* I y window fraction of top edge of plot area */ +double xLeft; /* I x data value at left side of data area */ +double yBot; /* I y data value at bottom side of data area */ +double xRight; /* I x data value at right side of data area */ +double yTop; /* I y data value at top side of data area */ +{ + int xNint; /* x axis number of intervals */ + int yNint; /* y axis number of intervals */ + double charHt, charHtX;/* default for character size */ + + pprAutoInterval(xLeft, xRight, &xNint); + pprAutoInterval(yBot, yTop, &yNint); + charHt = PprDfltCharHt(wfracYbot, wfracYtop); + charHtX = pprYFracToXFrac(pWin, charHt); + wfracXleft += 12. * charHtX; + wfracYbot += 6. * charHt; + return pprAreaOpen(pWin, wfracXleft, wfracYbot, wfracXright, wfracYtop, + xLeft, yBot, xRight, yTop, xNint, yNint, charHt); +} + +/*+/subr********************************************************************** +* NAME pprAreaRescale - change scaling for existing plot area +* +* DESCRIPTION +* Changes the scaling for a plot area using new data values at the +* edges of the data area. The actual size and position of the +* data area within the plot window aren't changed. +* +* No re-drawing is done by this routine. Typically, the caller will +* erase the appropriate area, draw the grid or perimeter, set the +* area for clipping, and, finally, replot the data. +* +* Default character height for the plot area is altered proportionally +* to the rescaling of the plot area. +* +* RETURNS +* void +* +* SEE ALSO +* pprAreaSetAttr, pprAreaErase +* +*-*/ +void +pprAreaRescale(pArea, xLeft, yBot, xRight, yTop) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double xLeft; /* I x data value at left side of data area */ +double yBot; /* I y data value at bottom side of data area */ +double xRight; /* I x data value at right side of data area */ +double yTop; /* I y data value at top side of data area */ +{ + PPR_WIN *pWin; + + pWin = pArea->pWin; + if (xLeft == xRight) { + (void)printf("pprAreaRescale: x left and right are equal\n"); + return; + } + else if (yBot == yTop) { + (void)printf("pprAreaRescale: y bottom and top are equal\n"); + return; + } + pArea->xPixLeft = ((double)pWin->width)*pArea->xFracLeft; + pArea->xPixRight = ((double)pWin->width)*pArea->xFracRight; + pArea->xLeft = xLeft; + pArea->xRight = xRight; + pArea->xInterval = (xRight - xLeft) / pArea->xNint; + pArea->xScale = ((double)pWin->width) * + (pArea->xFracRight - pArea->xFracLeft) / (xRight - xLeft); + pArea->yPixBot = ((double)pWin->height)*pArea->yFracBot; + pArea->yPixTop = ((double)pWin->height)*pArea->yFracTop; + pArea->yBot = yBot; + pArea->yTop = yTop; + pArea->yInterval = (yTop - yBot) / pArea->yNint; + pArea->yScale = ((double)pWin->height) * + (pArea->yFracTop - pArea->yFracBot) / (yTop - yBot); + pArea->tickHt = (double)pWin->height * + PprMin(PprAbs(.03 * (pArea->yFracTop - pArea->yFracBot)), .01); + if (pArea->charHt <= 0.) + pArea->charHt = pWin->height * + PprDfltCharHt(pArea->yFracBot, pArea->yFracTop); + else + pArea->charHt = pArea->charHt * pWin->height / pArea->oldWinHt; + pArea->oldWinHt = pWin->height; +} + +/*+/subr********************************************************************** +* NAME pprAreaSetAttr - set attributes for a plot area +* +* DESCRIPTION +* Set individual attributes for a plot area. In most cases, the +* attributes affect the drawing of lines in the plot area. +* +* To use this routine, an attribute code and a corresponding value +* are supplied. The form of the value argument depends on the code. +* +* o PPR_ATTR_CLIP sets the plot area so that line segments which lie +* outside the data area won't be drawn, but will terminate at their +* intersection with the edge of the data area. Clipping can be +* disabled by setting the PPR_ATTR_NOCLIP attribute; the default when +* a plot area is created is no clipping. +* +* pprAreaSetAttr(pArea, PPR_ATTR_CLIP, 0, NULL); +* pprAreaSetAttr(pArea, PPR_ATTR_NOCLIP, 0, NULL); +* +* o PPR_ATTR_COLORNUM selects a color for use in drawing lines in a plot +* area. For monochrome screens, no action is taken. There are +* PPR_NCOLORS colors provided, numbered starting with 1. A colorNum +* of 0 selects black. +* +* int colorNum; +* colorNum = 4; +* pprAreaSetAttr(pArea, PPR_ATTR_COLORNUM, colorNum, NULL); +* +* o PPR_ATTR_BG installs a caller-supplied background pixel value in +* the gc for the plot area. (For use only with X11. Under X11, +* pprAreaOpen initially set the gc for the plot area to have the +* same foreground and background colors as the gc for the plot window.) +* +* pprAreaSetAttr(pArea, PPR_ATTR_BG, 0, &bg); +* +* o PPR_ATTR_FG installs a caller-supplied foreground pixel value in +* the gc for the plot area. (For use only with X11. Under X11, +* pprAreaOpen initially set the gc for the plot area to have the +* same foreground and background colors as the gc for the plot window.) +* +* pprAreaSetAttr(pArea, PPR_ATTR_FG, 0, &fg); +* +* o PPR_ATTR_KEYNUM selects a legend key for identifying lines drawn +* in a plot area, and thus distinguishing them from the lines drawn +* by a different plot area. This is primarily useful for overlapping +* plot areas, where several sets of data are drawn on the same axis. +* The key number, which is expected to be in the range 0 to PPR_NKEYS, +* inclusive, selects either a dashed line pattern or a color, +* depending on the nature of the device on which the plot window +* resides. There are PPR_NKEYS unique patterns and colors; a key +* number of 0 resets to a "plain", solid line. +* +* Use of the PPR_ATTR_KEYNUM option provides a way to restart a +* dashed line pattern at its beginning. +* +* int keyNum; +* keyNum = 4; +* pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, keyNum, NULL); +* +* o PPR_ATTR_LINE_THICK sets the line thickness for the plot area. The +* thickness is the integer number of thousandths of plot window height. +* The thickness is used for data drawing operations. A thickness of +* 0. results in pixel-thick lines. As an example, a thickness of 10 +* represents 10/1000 (or .01) of the window height. +* +* int thick; +* thick = 3; .003 of window height +* pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); +* +* o PPR_ATTR_PATT_ARRAY installs a caller-supplied dashed line pattern +* array. This is an array of type short. The first element +* contains a number of pixels with `pen down'; the second has a +* number of pixels with `pen up'; the third with `pen down'; etc. +* Following the last element must be an element with a value of 0 . +* (pprAreaSetAttr stores only a pointer to the array, so the caller +* must preserve the array until pprAreaClose is called or until +* pprAreaSetAttr is called to `de-install' the pattern array.) An +* array pointer of NULL resets the plot area back to normal drawing. +* +* short pattern[]={16,4,2,4,0}; +* pprAreaSetAttr(pArea, PPR_ATTR_PATT_ARRAY, 0, pattern); +* +* +* Some pprXxx routines don't use the attributes from the plot +* area, but instead use the `generic' attributes from the +* plot window structure. The pprLineSegx_wc and pprMovex_wc +* routines provide an explicit mechanism for using the plot window +* attributes. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* BUGS +* o color is supported only for X +* o when color is used, ALL output for the plot area is colored; it's +* not clear yet whether this is a bug or a feature. +* o line thickness doesn't operate consistently under SunView +* +* SEE ALSO +* pprAreaOpen +* +*-*/ +long +pprAreaSetAttr(pArea, code, arg, pArg) +PPR_AREA *pArea; /* IO pointer to plotter area */ +PPR_ATTR_CODE code; /* I attribute code: one of PPR_ATTR_xxx */ +int arg; /* I attribute value, or 0 */ +void *pArg; /* I pointer to attribute, or NULL */ +{ + int keyNum, colorNum; + PPR_WIN *pWin; +#ifdef XWINDOWS + int screenNo; + Colormap cmap; + XColor color, rgbDb; +#endif + + if (code == PPR_ATTR_CLIP) { + pArea->attr.clip = 1; + return 0; + } + if (code == PPR_ATTR_NOCLIP) { + pArea->attr.clip = 0; + return 0; + } + if (code == PPR_ATTR_COLORNUM) { +#ifdef XWINDOWS + pWin = pArea->pWin; + if (pWin->winType != PPR_WIN_SCREEN) + return 0; + colorNum = arg; + if (colorNum == 0) + XCopyGC(pWin->pDisp, pWin->attr.gc, GCForeground, pArea->attr.gc); + else { + screenNo = DefaultScreen(pWin->pDisp); + colorNum = (colorNum - 1) % PPR_NCOLORS; + if (!pprWinIsMono(pWin)) { + cmap = DefaultColormap(pWin->pDisp, screenNo); + if (XAllocNamedColor(pWin->pDisp, cmap, pglPprColor[colorNum], + &color, &rgbDb)) { + XSetForeground(pWin->pDisp,pArea->attr.gc,color.pixel); + } + else + return -1; + } + } +#endif + return 0; + } + if (code == PPR_ATTR_BG) { +#ifdef XWINDOWS + pWin = pArea->pWin; + if (pWin->winType != PPR_WIN_SCREEN) + return 0; + XSetBackground(pWin->pDisp, pArea->attr.gc, *(unsigned long *)pArg); +#endif + return 0; + } + if (code == PPR_ATTR_FG) { +#ifdef XWINDOWS + pWin = pArea->pWin; + if (pWin->winType != PPR_WIN_SCREEN) + return 0; + XSetForeground(pWin->pDisp, pArea->attr.gc, *(unsigned long *)pArg); +#endif + return 0; + } + if (code == PPR_ATTR_KEYNUM) { + keyNum = arg; + pWin = pArea->pWin; + if (keyNum == 0) { + pArea->attr.pPatt = NULL; +#ifdef XWINDOWS + if (pWin->winType == PPR_WIN_SCREEN) + XCopyGC(pWin->pDisp,pWin->attr.gc,GCForeground,pArea->attr.gc); +#endif + } + else { + keyNum = (keyNum - 1) % PPR_NKEYS; +#ifdef XWINDOWS + if (pWin->winType == PPR_WIN_SCREEN) { + screenNo = DefaultScreen(pWin->pDisp); + if (!pprWinIsMono(pWin)) { + cmap = DefaultColormap(pWin->pDisp, screenNo); + if (XAllocNamedColor(pWin->pDisp, cmap, pglPprColor[keyNum], + &color, &rgbDb)) { + + XSetForeground(pWin->pDisp,pArea->attr.gc,color.pixel); + pArea->attr.pPatt = NULL; + return 0; + } + } + } +#endif +#if 0 + if (keyNum == 0) { + pArea->attr.pPatt = NULL; + return 0; + } +#endif + pArea->attr.pPatt = pglPprPat[keyNum]; + pArea->attr.sub = 0; + pArea->attr.rem = pArea->attr.pPatt[0]; + pArea->attr.pen = 1; + } + return 0; + } + if (code == PPR_ATTR_LINE_THICK) { + + pArea->attr.lineThick = arg; + return 0; + } + if (code == PPR_ATTR_COLORNUM) { +#ifdef XWINDOWS + pWin = pArea->pWin; + if (pWin->winType != PPR_WIN_SCREEN) + return 0; + colorNum = arg; + if (colorNum == 0) + XCopyGC(pWin->pDisp, pWin->attr.gc, GCForeground, pArea->attr.gc); + else { + colorNum = (colorNum - 1) % PPR_NCOLORS; + screenNo = DefaultScreen(pWin->pDisp); + if (!pprWinIsMono(pWin)) { + cmap = DefaultColormap(pWin->pDisp, screenNo); + if (XAllocNamedColor(pWin->pDisp, cmap, pglPprColor[colorNum], + &color, &rgbDb)) { + XSetForeground(pWin->pDisp,pArea->attr.gc,color.pixel); + } + else + return -1; + } + } +#endif + return 0; + } + if (code == PPR_ATTR_PATT_ARRAY) { + pArea->attr.pPatt = (short *)pArg; + if (pArg != NULL) { + pArea->attr.sub = 0; + pArea->attr.rem = pArea->attr.pPatt[0]; + pArea->attr.pen = 1; + } + return 0; + } + return -1; +} + +/*+/subr********************************************************************** +* NAME pprAutoEnds - choose `clean' endpoint valuess for an axis +* +* DESCRIPTION +* For a specific numeric range, this routine calculates a possibly +* altered numeric range which will produce more tasteful axis +* calibration. +* +* RETURNS +* void +* +* BUGS +* o this routine should probably focus some attention on choice of +* number of intervals for an axis; presently, the new endpoints +* chosen by this routine may be difficult to use for choosing +* interval size +* o uses exp10(), which doesn't exist in VxWorks +* o only linear calibration is handled +* +* SEE ALSO +* pprAutoInterval, pprAutoRange, pprAreaOpen +* +*-*/ +void +pprAutoEnds(left, right, pLeftNew, pRightNew) +double left; /* I leftmost value */ +double right; /* I rightmost value */ +double *pLeftNew; /* O new leftmost value */ +double *pRightNew; /* O new rightmost value */ +{ + double x1, x2, x1a, x2a, exp1, exp2, new1, new2; + double pwr1, pwr2, pwr; + + if (left == right) { + if (left == 0.) { + left = -1.; + right = 1.; + } + else if (left < 0.) { + left -= 1.; + right = 0.; + } + else { + left = 0.; + right += 1.; + } + } + +/*----------------------------------------------------------------------------- +* if axis if "reversed", temporarily put it "normal", to make life easy +*----------------------------------------------------------------------------*/ + if (left > right) { x1 = right; x2 = left; } + else { x1 = left; x2 = right; } + +/*----------------------------------------------------------------------------- +* now, find a reasonable place to round each end to; the larger magnitude +* number controls to what "boundary" to round. Use absolute values in +* the sleuthing. +*----------------------------------------------------------------------------*/ + x1a = x1 >= 0. ? x1 : -x1; + if (x1a == 0.) + pwr1 = x1a; + else + pwr1 = exp10((double)((int)log10(x1a))); + x2a = x2 >= 0. ? x2 : -x2; + if (x2a == 0.) + pwr2 = x2a; + else + pwr2 = exp10((double)((int)log10(x2a))); + pwr = pwr1>pwr2 ? pwr1 : pwr2; + +/*----------------------------------------------------------------------------- +* actually do the rounding; and restore the values' original signs +*----------------------------------------------------------------------------*/ + if (x1 < 0.) { new1 = (1+(int)(x1a/pwr-.0001)) * pwr; new1 = -new1; } + else new1 = ((int)(x1a/pwr-.0001)) * pwr; + + if (x2 < 0.) { new2 = ((int)(x2a/pwr-.0001)) * pwr; new2 = -new2; } + else new2 = (1+(int)(x2a/pwr-.0001)) * pwr; + +/*----------------------------------------------------------------------------- +* unscramble if input was "reversed"; and give values to caller +*----------------------------------------------------------------------------*/ + if (left < right) { + *pLeftNew = new1; + *pRightNew = new2; + } + else { + *pLeftNew = new2; + *pRightNew = new1; + } +} + +/*+/subr********************************************************************** +* NAME pprAutoInterval - figure out good interval size for an axis +* +* DESCRIPTION +* Determine a "good" interval size for an axis, so that axis +* annotation will be tasteful. +* +* RETURNS +* void +* +* BUGS +* o this routine always chooses to divide an axis into 5 intervals +* o only linear calibration is handled +* +* SEE ALSO +* pprAutoEnds, pprAutoRange, pprAreaOpen +* +*-*/ +void +pprAutoInterval(val1, val2, pNint) +double val1; /* I value at one end of axis */ +double val2; /* I value at other end of axis */ +int *pNint; /* O number of intervals */ +{ +#define PPR_EQ(v1, v2) PprAbs((v2)-(v1)) <= slop + + double vmin, vmax; + double slop; /* differences no larger mean == */ + double diff, ratio, aint; + int nInt; + + slop = PprAbs(val2 - val1); + + vmin = PprMin(val1, val2); + vmax = PprMax(val1, val2); + + if (PPR_EQ(vmin, 0.)) + /* pprAutoInt(vmax, &nInt); */ + ; + else if (PPR_EQ(vmax, 0.)) + /* pprAutoInt(vmin, &nInt); */ + ; + else if (vmin < 0. && vmax > 0.) { + ; + } + nInt = 5.; + *pNint = nInt; +} + +/*+/macro********************************************************************* +* NAME pprAutoRange - find minimum and maximum values for an array +* +* DESCRIPTION +* Finds the minimum and maximum values in an array of values. +* +* Four different routines are available, depending on the type of +* the input array. Each returns the min and max as a double: +* +* void pprAutoRangeD(doubleArray, nPoints, doubleMin, doubleMax) +* void pprAutoRangeF(floatArray, nPoints, doubleMin, doubleMax) +* void pprAutoRangeL(longArray, nPoints, doubleMin, doubleMax) +* void pprAutoRangeS(shortArray, nPoints, doubleMin, doubleMax) +* +* RETURNS +* void +* +* SEE ALSO +* pprAutoEnds, pprAutoInterval, pprAreaOpen +* +*-*/ +void pprAutoRangeD(dblArray, npts, dblMin, dblMax) +double *dblArray; /* I data value array */ +int npts; /* I number of data points */ +double *dblMin; /* O minimum value in array */ +double *dblMax; /* O maximum value in array */ +{ + int i; + + *dblMin = *dblMax = dblArray[0]; + for (i=1; i dblArray[i]) *dblMin = dblArray[i]; + if (*dblMax < dblArray[i]) *dblMax = dblArray[i]; + } +} +void pprAutoRangeF(fltArray, npts, dblMin, dblMax) +float *fltArray; /* I data value array */ +int npts; /* I number of data points */ +double *dblMin; /* O minimum value in array */ +double *dblMax; /* O maximum value in array */ +{ + int i; + *dblMin = *dblMax = fltArray[0]; + for (i=1; i fltArray[i]) *dblMin = fltArray[i]; + if (*dblMax < fltArray[i]) *dblMax = fltArray[i]; + } +} +void pprAutoRangeL(lngArray, npts, dblMin, dblMax) +long *lngArray; /* I data value array */ +int npts; /* I number of data points */ +double *dblMin; /* O minimum value in array */ +double *dblMax; /* O maximum value in array */ +{ + int i; + *dblMin = *dblMax = lngArray[0]; + for (i=1; i lngArray[i]) *dblMin = lngArray[i]; + if (*dblMax < lngArray[i]) *dblMax = lngArray[i]; + } +} +void pprAutoRangeS(shtArray, npts, dblMin, dblMax) +short *shtArray; /* I data value array */ +int npts; /* I number of data points */ +double *dblMin; /* O minimum value in array */ +double *dblMax; /* O maximum value in array */ +{ + int i; + *dblMin = *dblMax = shtArray[0]; + for (i=1; i shtArray[i]) *dblMin = shtArray[i]; + if (*dblMax < shtArray[i]) *dblMax = shtArray[i]; + } +} + +static int pprTrigInit=0; +static double pprCos[361]; /* cos for 0-90, in .25 steps */ +/*+/subr********************************************************************** +* NAME pprCos_deg - get the cosine of an angle in degrees +* +* DESCRIPTION +* Get the cosine of an angle in degrees. A table lookup technique +* is used. The angle argument is truncated to the next lower 1/4 +* degree prior to lookup. +* +* RETURNS +* cosine +* +*-*/ +double +pprCos_deg(angle) +double angle; /* I angle, in degrees */ +{ + int indx; + double cosine; + + if (!pprTrigInit) { + int i; + double angle; + + pprTrigInit = 1; + for (i=0; i<=360; i++) { + angle = (double)i / 4. * .017453292; + pprCos[i] = cos(angle); + } + } + indx = angle * 4.; + if (indx < 0) + indx = -indx; + if (indx >= 1440) + indx %= 1440; + if (indx <= 360) + cosine = pprCos[indx]; + else if ((indx -= 360) <= 360) + cosine = -pprCos[360 - indx]; + else if ((indx -= 360) <= 360) + cosine = -pprCos[indx]; + else { + indx -= 360; + cosine = pprCos[360 - indx]; + } + return cosine; +} + +/*+/subr********************************************************************** +* NAME pprChar - plot a character +* +* DESCRIPTION +* Plots a single text character at a location. The center of the +* character is placed at the specified x,y position. +* +* The character height specification is in terms of a fraction of the +* height of the window. This results in automatic scaling of +* character sizes as the window size changes. If a height of zero +* is specified in the call, then the default height for the plot +* area is used. (The default height was established by pprAreaOpen.) +* +* A macro is available which returns the default character height +* used by this plot package. The value returned is proportional to +* the height of the plot area. This value can be used to generate +* "big" and "small" character sizes. +* +* PprDfltCharHt(lowYfrac, highYfrac) +* +* lowYfrac is the vertical fraction of the window at which +* the bottom edge of the plot area lies +* highYfrac is the vertical fraction of the window at which +* the top edge of the plot area lies +* +* RETURNS +* void +* +* BUGS +* o technique used works only with linear axes +* o ASCII character codes are assumed +* o no checks are made for illegal characters +* +* SEE ALSO +* pprText, pprAreaOpen, pprLine, pprPoint +* +*-*/ +void +pprChar(pArea, x, y, chr, height, angle) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of character */ +double y; /* I y data coordinate of character */ +char chr; /* I character to plot */ +double height; /* I height of character, as a fraction of + the height of the window; a value of + zero results in using a default height */ +double angle; /* I orientation angle of character, ccw degrees */ +{ + double xWin, yWin; + double scale; /* convert character units to win coord */ + double cosT, sinT; + char str[2]; + + if (height <= 0.) + height = pArea->charHt; + else + height *= pArea->pWin->height; + xWin = pArea->xPixLeft + (x - pArea->xLeft) * pArea->xScale; + yWin = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + + if (pArea->pWin->winType == PPR_WIN_SCREEN) { + if (angle == 0.) cosT = 1., sinT = 0.; + else if (angle == 90.) cosT = 0., sinT = 1.; + else { cosT = pprCos_deg(angle); + sinT = pprSin_deg(angle); + } + scale = height / 6.; + pprText1(pArea, + xWin, yWin, chr, 0, scale, sinT, cosT, pprLineSegPixD_ac); + } + else { + height = 1.5 * height; + str[0] = chr; + str[1] = '\0'; + pprTextPS(pArea->pWin->file, xWin, yWin,PPR_TXT_CEN,str,height,angle); + } +} + +/*+/subr********************************************************************** +* NAME pprCvtDblToTxt - format a double for printing +* +* DESCRIPTION +* Formats a double for printing. This routine is dedicated to +* getting as large a range of values as possible into a particular +* field width. +* +* This routine doesn't attempt to handle extremely small values. +* It assumes that the field is large enough to handle the smallest +* significant value to be encountered. +* +* RETURNS +* void +* +* BUGS +* o extremely small values aren't handled well +* +* NOTES +* 1. If the value can't be represented at all in the field, the sign +* followed by *'s appears. +* 2. In extreme cases, only the magnitude of the value will appear, as +* En or Enn. For negative values, a - will precede the E. +* 3. When appropriate, the value is rounded to the nearest integer +* for formatting. +* +*-*/ +void +pprCvtDblToTxt(text, width, value, sigDig) +char *text; /* O text representation of value */ +int width; /* I max width of text string (not counting '\0') */ +double value; /* I value to print */ +int sigDig; /* I max # of dec places to print */ +{ + double valAbs; /* absolute value of caller's value */ + int wholeNdig; /* number of digits in "whole" part of value */ + double logVal; /* log10 of value */ + int decPlaces; /* number of decimal places to print */ + int expWidth; /* width needed for exponent field */ + int excess; /* number of low order digits which + won't fit into the field */ + + if (value == 0.) { + (void)strcpy(text, "0"); + return; + } + +/*----------------------------------------------------------------------------- +* find out how many columns are required to represent the integer part +* of the value. A - is counted as a column; the . isn't. +*----------------------------------------------------------------------------*/ + valAbs = value>0 ? value : -value; + logVal = log10(valAbs); + wholeNdig = 1 + (int)logVal; + if (wholeNdig < 0) + wholeNdig = 1; + if (value < 0.) + wholeNdig++; + if (wholeNdig < width-1) { +/*----------------------------------------------------------------------------- +* the integer part fits well within the field. Find out how many +* decimal places can be printed (honoring caller's sigDig limit). +*----------------------------------------------------------------------------*/ + decPlaces = width - wholeNdig - 1; + if (sigDig < decPlaces) + decPlaces = sigDig; + if (sigDig > 0) + (void)sprintf(text, "%.*f", decPlaces, value); + else + (void)sprintf(text, "%d", nint(value)); + } + else if (wholeNdig == width || wholeNdig == width-1) { +/*----------------------------------------------------------------------------- +* The integer part just fits within the field. Print the value as an +* integer, without printing the superfluous decimal point. +*----------------------------------------------------------------------------*/ + (void)sprintf(text, "%d", nint(value)); + } + else { +/*----------------------------------------------------------------------------- +* The integer part is too large to fit within the caller's field. Print +* with an abbreviated E notation. +*----------------------------------------------------------------------------*/ + expWidth = 2; /* assume that En will work */ + excess = wholeNdig - (width - 2); + if (excess > 999) { + expWidth = 5; /* no! it must be Ennnn */ + excess += 3; + } + else if (excess > 99) { + expWidth = 4; /* no! it must be Ennn */ + excess += 2; + } + else if (excess > 9) { + expWidth = 3; /* no! it must be Enn */ + excess += 1; + } +/*----------------------------------------------------------------------------- +* Four progressively worse cases, with all or part of exponent fitting +* into field, but not enough room for any of the value +* Ennn positive value; exponent fits +* -Ennn negative value; exponent fits +* +**** positive value; exponent too big +* -**** negative value; exponent too big +*----------------------------------------------------------------------------*/ + if (value >= 0. && expWidth == width) + (void)sprintf(text, "E%d", nint(logVal)); + else if (value < 0. && expWidth == width-1) + (void)sprintf(text, "-E%d", nint(logVal)); + else if (value > 0. && expWidth > width) + (void)sprintf(text, "%.*s", width, "+*******"); + else if (value < 0. && expWidth > width-1) + (void)sprintf(text, "%.*s", width, "-*******"); + else { +/*----------------------------------------------------------------------------- +* The value can fit, in exponential notation +*----------------------------------------------------------------------------*/ + (void)sprintf(text, "%dE%d", + nint(value/exp10((double)excess)), excess); + } + } +} + +/*+/subr********************************************************************** +* NAME pprGrid - draw a grid +* +* DESCRIPTION +* Draw a perimeter and grid lines. The number of intervals +* specified in the plot area structure is used for placing +* the grid lines. +* +* The color attributes for the plot window are used for drawing; +* dashed line and line thickness are ignored. +* +* RETURNS +* void +* +* BUGS +* o only linear axes are handled +* +* SEE ALSO +* pprGridLabel, pprGridErase, pprPerim, pprAnnotX, pprAnnotY, pprAreaOpen +* +*-*/ +void +pprGrid(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ +{ + int i; + double x, y; + static short patt[]={1, 14, 0}; + +/*----------------------------------------------------------------------------- +* draw the box +*----------------------------------------------------------------------------*/ + pprMoveD_wc(pArea, pArea->xLeft, pArea->yBot, 0); + pprMoveD_wc(pArea, pArea->xRight, pArea->yBot, 1); + pprMoveD_wc(pArea, pArea->xRight, pArea->yTop, 1); + pprMoveD_wc(pArea, pArea->xLeft, pArea->yTop, 1); + pprMoveD_wc(pArea, pArea->xLeft, pArea->yBot, 1); +/*----------------------------------------------------------------------------- +* draw the vertical grid lines +*----------------------------------------------------------------------------*/ + x = pArea->xLeft; + for (i=1; ixNint; i++) { + x += pArea->xInterval; + pprLineSegDashD_wc(pArea, x, pArea->yBot, x, pArea->yTop, patt); + } +/*----------------------------------------------------------------------------- +* ditto for y axis +*----------------------------------------------------------------------------*/ + y = pArea->yBot; + for (i=1; iyNint; i++) { + y += pArea->yInterval; + pprLineSegDashD_wc(pArea, pArea->xLeft, y, pArea->xRight, y, patt); + } +} + +/*+/subr********************************************************************** +* NAME pprGridErase - erase within a grid +* +* DESCRIPTION +* Erases the screen inside the grid for the plot area. (Actually, +* the entire data area is erased and then the grid is redrawn.) +* +* RETURNS +* void +* +* SEE ALSO +* pprPerimErase, pprAreaErase, pprWinErase +* +*-*/ +void +pprGridErase(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ +{ + int x,y,width,height; + + if (pArea->pWin->winType != PPR_WIN_SCREEN) + return; + x = pArea->xPixLeft - 3; + y = pArea->yPixBot - 3; + width = (pArea->xRight - pArea->xLeft) * pArea->xScale + 6; + height = (pArea->yTop - pArea->yBot) * pArea->yScale + 6; +#ifdef SUNVIEW + y = pArea->pWin->height - y - height; + pw_writebackground(pArea->pWin->pw, x, y, width, height, PIX_SRC); + pprGrid(pArea); +#elif defined XWINDOWS + y = pArea->pWin->height - y - height; + XClearArea(pArea->pWin->pDisp, pArea->pWin->plotWindow, + x, y, width, height, False); + pprGrid(pArea); + XFlush(pArea->pWin->pDisp); +#endif +} + +/*+/subr********************************************************************** +* NAME pprGridLabel - draw and label a grid +* +* DESCRIPTION +* Draw a perimeter and grid lines. The number of intervals +* specified in the plot area structure is used for placing +* the grid lines. +* +* Axis labels and annotations are drawn using the information from +* the plot area, as specified in the pprAreaOpen call. +* +* RETURNS +* void +* +* BUGS +* o only linear axes are handled +* +* SEE ALSO +* pprGrid, pprPerim, pprAnnotX, pprAnnotY, pprAreaOpen +* +*-*/ +void +pprGridLabel(pArea, xLabel, xAnnot, yLabel, yAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +char *xLabel; /* I label for x axis, or NULL */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +char *yLabel; /* I label for y axis, or NULL */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I angle for y annotations; 0. or 90. */ +{ + pprGrid(pArea); + pprAnnotX(pArea, 0, pArea->xLeft, pArea->xRight, pArea->xNint, + 0, xLabel, xAnnot, 0.); + pprAnnotY(pArea, 0, pArea->yBot, pArea->yTop, pArea->yNint, + 0, yLabel, yAnnot, angle); +} + +/*+/subr********************************************************************** +* NAME pprLine - plot a line using x and y data value vectors +* +* DESCRIPTION +* Draw a line along the path specified by two value arrays. +* +* Several entry points are available to accomodate various +* types of data: +* +* pprLineF(pArea, x, y, npts) x and y are float[] +* pprLineD(pArea, x, y, npts) x and y are double[] +* pprLineS(pArea, x, y, npts) x and y are short[] +* pprLineL(pArea, x, y, npts) x and y are long[] +* +* RETURNS +* void +* +* BUGS +* o only linear calibration is handled +* +* SEE ALSO +* pprLineSeg, pprMove, pprPoint, pprText +* +*-*/ +void +pprLineF(pArea, x, y, npts) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +float *x; /* I x array of data values */ +float *y; /* I y array of data values */ +int npts; /* I number of points to plot */ +{ + int i; + pprMoveD(pArea, (double)(x[0]), (double)(y[0]), 0); + for (i=1; ipWin->winType == PPR_WIN_SCREEN) { +#ifdef SUNVIEW + if (pArea->pWin->brush.width > 1) + pw_line(pArea->pWin->pw, (int)xp0, (int)yp0, (int)xp1, (int)yp1, + &pArea->pWin->brush, &texture, (int)PIX_SRC); + else + pw_vector(pArea->pWin->pw, (int)xp0, (int)yp0, (int)xp1, (int)yp1, + PIX_SRC, 1); +#elif defined XWINDOWS + XDrawLine(pArea->pWin->pDisp, pArea->pWin->plotWindow, + pArea->attr.gc, (int)xp0, (int)yp0, (int)xp1, (int)yp1); +#endif + } + else if (pArea->pWin->winType == PPR_WIN_POSTSCRIPT || + pArea->pWin->winType == PPR_WIN_EPS) { + (void)fprintf(pArea->pWin->file, "%.1f %.1f %.1f %.1f DS\n", + xp0, yp0, xp1, yp1); + } +} +static void +pprLineSegPixD_wc(pArea, xp0, yp0, xp1, yp1) +PPR_AREA *pArea; +double xp0, xp1, yp0, yp1; /* y must be corrected properly by the caller + for the windowing system being used. I.e., + most of the pprXxx routines assume 0,0 is + lower left, but X and SunView assume it is + upper left--the caller must have dealt with + this. */ +{ +#ifdef SUNVIEW + static int init=1; + + if (init) { + bzero((char *)&texture, sizeof(texture)); + texture.pattern = glSvPattern; + texture.offset = 0; + texture.options.startpoint = 1; + texture.options.endpoint = 1; + texture.options.givenpattern = 1; + init = 0; + } +#endif + if (pArea->pWin->winType == PPR_WIN_SCREEN) { +#ifdef SUNVIEW + if (pArea->pWin->brush.width > 1) + pw_line(pArea->pWin->pw, (int)xp0, (int)yp0, (int)xp1, (int)yp1, + &pArea->pWin->brush, &texture, (int)PIX_SRC); + else + pw_vector(pArea->pWin->pw, (int)xp0, (int)yp0, (int)xp1, (int)yp1, + PIX_SRC, 1); +#elif defined XWINDOWS + XDrawLine(pArea->pWin->pDisp, pArea->pWin->plotWindow, + pArea->pWin->attr.gc, (int)xp0, (int)yp0, (int)xp1, (int)yp1); +#endif + } + else if (pArea->pWin->winType == PPR_WIN_POSTSCRIPT || + pArea->pWin->winType == PPR_WIN_EPS) { + (void)fprintf(pArea->pWin->file, "%.1f %.1f %.1f %.1f DS\n", + xp0, yp0, xp1, yp1); + } +} + +/*+/internal****************************************************************** +* NAME pprLineSegDashD_wc - drawing a dashed line segment +* +* DESCRIPTION +* Draws a dashed line between the specified points. The dashed line +* starts at the beginning of the specified pattern. The dashes are +* drawn using the plot window color. +* +* (The dashed line attributes for the plot area are neither used nor +* altered.) +* +* No clipping service is provided. +* +* RETURNS +* void +* +*-*/ +static void +pprLineSegDashD_wc(pArea, x0, y0, x1, y1, pPatt) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x0; /* I x data coordinate of first point */ +double y0; /* I y data coordinate of first point */ +double x1; /* I x data coordinate of second point */ +double y1; /* I y data coordinate of second point */ +short *pPatt; /* I pointer to pattern array */ +{ + double xp0,yp0,xp1,yp1; + double xpA,ypA,xpB; + double segLen, endLen, dashLen, xbeg, xend, ybeg, yend; + int pen=0, sub=-1, rem=0; + + xbeg = xp0 = pArea->xPixLeft + (x0 - pArea->xLeft) * pArea->xScale; + ybeg = yp0 = pArea->yPixBot + (y0 - pArea->yBot) * pArea->yScale; + if (pArea->pWin->winType == PPR_WIN_SCREEN) + ybeg = yp0 = pArea->pWin->height - yp0; + xp1 = pArea->xPixLeft + (x1 - pArea->xLeft) * pArea->xScale; + yp1 = pArea->yPixBot + (y1 - pArea->yBot) * pArea->yScale; + if (pArea->pWin->winType == PPR_WIN_SCREEN) + yp1 = pArea->pWin->height - yp1; + pprLineThick(pArea, pArea->pWin->attr.lineThick); + segLen = sqrt((xp1-xp0)*(xp1-xp0) + (yp1-yp0)*(yp1-yp0)); + endLen = 0.; + while (endLen < segLen) { + if (rem < 1.) { + if ((rem = pPatt[++sub]) <= 0.) { + sub = 0; rem = pPatt[0]; pen = 1; + } + else + pen ^= 1; + } + dashLen = PprMin(rem, segLen-endLen); + endLen += dashLen; + if (PprAbs(endLen-segLen) < .5) + endLen = segLen; + xend = xp0 + endLen/segLen * (xp1 - xp0); + yend = yp0 + endLen/segLen * (yp1 - yp0); + if (pen) { + if (dashLen > 1.) pprLineSegPixD_wc(pArea, xbeg, ybeg, xend, yend); + else { + if (pArea->pWin->winType == PPR_WIN_SCREEN) { +#ifdef SUNVIEW + if (pArea->pWin->attr.ltPix > 1) { + ypA = ybeg; + xpA = xbeg - pArea->pWin->attr.ltPix / 2; + xpB = xpA + pArea->pWin->attr.ltPix - 1; +#elif defined XWINDOWS + if (pArea->attr.ltPix > 1) { + ypA = ybeg; + xpA = xbeg - pArea->attr.ltPix / 2; + xpB = xpA + pArea->attr.ltPix - 1; +#endif + /* for thick lines, draw a square 'blob' */ + pprLineSegPixD_wc(pArea, xpA, ypA, xpB, ypA); + } + else { +#ifdef SUNVIEW + pw_put(pArea->pWin->pw, (int)xbeg, (int)ybeg, 1); +#elif defined XWINDOWS + XDrawPoint(pArea->pWin->pDisp, pArea->pWin->plotWindow, + pArea->pWin->attr.gc, (int)xbeg, (int)ybeg, 1); +#endif + } + } + else if (pArea->pWin->winType == PPR_WIN_POSTSCRIPT || + pArea->pWin->winType == PPR_WIN_EPS) { + (void)fprintf(pArea->pWin->file,"%.1f %.1f DP\n",xbeg,ybeg); + } + } + } + xbeg = xend; ybeg = yend; + rem -= dashLen; + } +} + +/*+/internal****************************************************************** +* NAME pprLineThick - set line thickness +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o for X, each linewidth ought to have its own gc +* o doesn't work consistently for SunView (especially after first draw) +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +pprLineThick(pArea, thick) +PPR_AREA *pArea; +short thick; /* I thickness in thousandths of window height */ +{ + PPR_WIN *pWin; + double lt; /* line thickness */ + short ltPix; /* line thickness, in pixels */ + + pWin = pArea->pWin; + + if (pWin->winType==PPR_WIN_POSTSCRIPT || pWin->winType==PPR_WIN_EPS) { + if (thick != pWin->attr.ltCurr) { + pWin->attr.ltCurr = thick; + if (thick == 0) lt = .1; + else lt = .001 * thick * pWin->height; + (void)fprintf(pWin->file, "%.1f setlinewidth\n", lt); + } + } + else { +#ifdef SUNVIEW + if (thick != pWin->attr.ltCurr) { + pWin->attr.ltCurr = thick; +#elif defined XWINDOWS + if (thick != pArea->attr.ltCurr) { + pArea->attr.ltCurr = thick; +#endif + if (thick == 0) + ltPix = 1; + else { + lt = .001 * thick * pWin->height; + if (lt < 1.) ltPix = 1; + else ltPix = (int)(lt + .5); + } +#ifdef SUNVIEW + pWin->attr.ltPix = ltPix; + pWin->brush.width = ltPix; +#elif defined XWINDOWS + pArea->attr.ltPix = ltPix; + XSetLineAttributes(pWin->pDisp, pArea->attr.gc, ltPix, LineSolid, + CapButt, JoinRound); +#endif + } + } +} + +/*+/subr********************************************************************** +* NAME pprMarkD - draw a plotting mark +* +* DESCRIPTION +* Draw a plotting mark at the specified point. The color attribute +* (if any) of the plot area is used in drawing the mark. Line +* thickness and dashed line pattern attributes are ignored. +* +* Two entry points are available: +* +* pprMarkD(pArea, x, y, markNum) x and y are double +* pprMarkL(pArea, x, y, markNum) x and y are long +* +* RETURNS +* void +* +* BUGS +* o only linear calibration is handled +* +* SEE ALSO +* pprLine, pprLineSeg, pprPoint, pprText +* +*-*/ +void +pprMarkD(pArea, x, y, markNum) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double x; /* I x data coordinate */ +double y; /* I y data coordinate */ +int markNum; /* I mark number--0 to PPR_NMARKS-1, inclusive */ +{ + short *pMark; + double xp0, xp1, yp0, yp1; + int pen; + + pprLineThick(pArea, pArea->attr.lineThick); + markNum %= PPR_NMARKS; + pMark = pglPprMarkS[markNum]; /* use small marks */ + xp0 = pArea->xPixLeft + (x - pArea->xLeft) * pArea->xScale; + yp0 = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + if (pArea->pWin->winType == PPR_WIN_SCREEN) + yp0 = pArea->pWin->height - yp0; + if (pArea->attr.clip) { + if (xp0 < PprMin(pArea->xPixLeft, pArea->xPixRight) || + xp0 > PprMax(pArea->xPixLeft, pArea->xPixRight) || + yp0 < PprMin(pArea->yPixBot, pArea->yPixTop) || + yp0 > PprMax(pArea->yPixBot, pArea->yPixTop)) { + return; + } + } + while (1) { + xp1 = xp0 + *pMark++; + if (pArea->pWin->winType == PPR_WIN_SCREEN) yp1 = yp0 - *pMark++; + else yp1 = yp0 + *pMark++; + pen = *pMark++; + if (pen) { + pprLineSegPixD_ac(pArea, xp0, yp0, xp1, yp1); + if (pen == 2) + break; + } + xp0 = xp1; + yp0 = yp1; + } +} +void +pprMarkL(pArea, x, y, markNum) +PPR_AREA *pArea; /* I pointer to plot area structure */ +long x; /* I x data coordinate */ +long y; /* I y data coordinate */ +int markNum; /* I mark number--0 to PPR_NMARKS-1, inclusive */ +{ + short *pMark; + double xp0, xp1, yp0, yp1; + int pen; + + pprLineThick(pArea, pArea->attr.lineThick); + markNum %= PPR_NMARKS; + pMark = pglPprMarkS[markNum]; /* use small marks */ + xp0 = pArea->xPixLeft + (x - pArea->xLeft) * pArea->xScale; + yp0 = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + if (pArea->pWin->winType == PPR_WIN_SCREEN) + yp0 = pArea->pWin->height - yp0; + while (1) { + xp1 = xp0 + *pMark++; + if (pArea->pWin->winType == PPR_WIN_SCREEN) yp1 = yp0 - *pMark++; + else yp1 = yp0 + *pMark++; + pen = *pMark++; + if (pen) { + pprLineSegPixD_ac(pArea, xp0, yp0, xp1, yp1); + if (pen == 2) + break; + } + xp0 = xp1; + yp0 = yp1; + } +} + +/*+/subr********************************************************************** +* NAME pprMoveD - move the pen, possibly drawing a line +* +* DESCRIPTION +* Move the "pen" to the specified point. If the "pen" is "down" +* a line will be drawn. The line attributes of the plot area are +* used in drawing the line. If the attributes indicate a dashed +* line, the current dashed line pattern will be used. +* +* The "clipping" attribute for the plot area is honored only by +* pprMoveD; pprMoveD_ac and pprMoveD_wc never clip lines at the +* data area edges. +* +* Two alternate entry points are available for drawing plain lines, +* ignoring all attributes except color. One uses the color for the +* plot area (pprMoveD_ac); the other uses the plot window color +* (pprMoveD_wc). +* +* pprMoveD_ac(pArea, x, y, pen) x and y are double +* pprMoveD_wc(pArea, x, y, pen) x and y are double +* +* RETURNS +* void +* +* BUGS +* o only linear calibration is handled +* +* SEE ALSO +* pprLine, pprLineSeg, pprText +* +*-*/ +void +pprMoveD(pArea, x, y, pen) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of new point */ +double y; /* I y data coordinate of new point */ +int pen; /* I pen indicator--non-zero draws a line */ +{ + double xp0, xp1, yp0, yp1; + double segLen, endLen, dashLen, xbeg, xend, ybeg, yend; + + xp0 = pArea->xPix[0]; + yp0 = pArea->yPix[0]; + xp1 = pArea->xPix[1] = pArea->xPixLeft + (x - pArea->xLeft)* pArea->xScale; + yp1 = pArea->yPix[1] = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + if (pArea->pWin->winType == PPR_WIN_SCREEN) + yp1 = pArea->yPix[1] = pArea->pWin->height - pArea->yPix[1]; + pArea->xPix[0] = xp1; + pArea->yPix[0] = yp1; + if (pen) { + pprLineThick(pArea, pArea->attr.lineThick); + if (pArea->attr.clip) { + double xpl=pArea->xPixLeft, xpr=pArea->xPixRight; + double ypb=pArea->yPixBot, ypt=pArea->yPixTop; + double ypeb, ypet; /* "logical" top and bottom pix values */ + double xpmin=PprMin(xp0,xp1), xpmax=PprMax(xp0,xp1); + double ypmin=PprMin(yp0,yp1), ypmax=PprMax(yp0,yp1); + if (pArea->pWin->winType == PPR_WIN_SCREEN) { + ypb = pArea->pWin->height - ypb; + ypt = pArea->pWin->height - ypt; + } + ypeb = PprMin(ypb, ypt); + ypet = PprMax(ypb, ypt); + + if (xpmin > xpr || xpmax < xpl || ypmin > ypet || ypmax < ypeb) + return; /* no possible intersection with data area */ + if (xpmin < xpl || xpmax > xpr || ypmin < ypeb || ypmax > ypet) { + /* part of path is outside data area; find intersections */ + if (xp0 == xp1) { /* no intersection with sides */ + if (yp0 < ypeb) yp0 = ypeb; + else if (yp0 > ypet) yp0 = ypet; + if (yp1 < ypeb) yp1 = ypeb; + else if (yp1 > ypet) yp1 = ypet; + } + else if (yp0 == yp1) { /* no intersection with top or bot */ + if (xp0 < xpl) xp0 = xpl; + else if (xp0 > xpr) xp0 = xpr; + if (xp1 < xpl) xp1 = xpl; + else if (xp1 > xpr) xp1 = xpr; + } + else { + double S; /* slope of line to draw */ + double XP0=xp0, XP1=xp1, YP0=yp0, YP1=yp1; + S = (yp1 - yp0) / (xp1 - xp0); + if (XP0 < xpl) XP0 = xpl, YP0 = yp0 + (xpl-xp0) * S; + else if (XP0 > xpr) XP0 = xpr, YP0 = yp0 + (xpr-xp0) * S; + if (YP0 < ypeb) YP0 = ypeb, XP0 = xp0 + (ypeb-yp0)/S; + else if (YP0 > ypet) YP0 = ypet, XP0 = xp0 + (ypet-yp0)/S; + if (XP0 < xpl || XP0 > xpr) + return; /* no intersection */ + if (XP1 < xpl) XP1 = xpl, YP1 = yp0 + (xpl-xp0) * S; + else if (XP1 > xpr) XP1 = xpr, YP1 = yp0 + (xpr-xp0) * S; + if (YP1 < ypeb) YP1 = ypeb, XP1 = xp0 + (ypeb-yp0)/S; + else if (YP1 > ypet) YP1 = ypet, XP1 = xp0 + (ypet-yp0)/S; + if (XP1 < xpl || XP1 > xpr) + return; /* no intersection */ + xp0 = XP0, xp1 = XP1, yp0 = YP0, yp1 = YP1; + } + } + } + if (pArea->attr.pPatt == NULL) + pprLineSegPixD_ac(pArea, xp0, yp0, xp1, yp1); + else { +/*----------------------------------------------------------------------------- +* draw a dashed line pattern +*----------------------------------------------------------------------------*/ + segLen = sqrt((xp1-xp0)*(xp1-xp0) + (yp1-yp0)*(yp1-yp0)); + endLen = 0.; + xbeg = xp0; + ybeg = yp0; + while (endLen < segLen) { + if (pArea->attr.rem < 1.) { + if ((pArea->attr.rem = + pArea->attr.pPatt[++pArea->attr.sub]) <= 0.) { + pArea->attr.sub = 0; + pArea->attr.rem = pArea->attr.pPatt[0]; + pArea->attr.pen = 1; + } + else + pArea->attr.pen ^= 1; + } + dashLen = PprMin(pArea->attr.rem, segLen-endLen); + endLen += dashLen; + if (PprAbs(endLen-segLen) < .5) + endLen = segLen; + xend = xp0 + endLen/segLen * (xp1 - xp0); + yend = yp0 + endLen/segLen * (yp1 - yp0); + if (pArea->attr.pen) + pprLineSegPixD_ac(pArea, xbeg, ybeg, xend, yend); + xbeg = xend; + ybeg = yend; + pArea->attr.rem -= dashLen; + } + } + } +} +void +pprMoveD_ac(pArea, x, y, pen) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of new point */ +double y; /* I y data coordinate of new point */ +int pen; /* I pen indicator--1 draws a line */ +{ + pArea->xPix[1] = pArea->xPixLeft + (x - pArea->xLeft) * pArea->xScale; + pArea->yPix[1] = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + if (pArea->pWin->winType == PPR_WIN_SCREEN) + pArea->yPix[1] = pArea->pWin->height - pArea->yPix[1]; + if (pen) { + pprLineThick(pArea, pArea->pWin->attr.lineThick); + pprLineSegPixD_ac(pArea, pArea->xPix[0], pArea->yPix[0], + pArea->xPix[1], pArea->yPix[1]); + } + pArea->xPix[0] = pArea->xPix[1]; + pArea->yPix[0] = pArea->yPix[1]; +} +void +pprMoveD_wc(pArea, x, y, pen) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of new point */ +double y; /* I y data coordinate of new point */ +int pen; /* I pen indicator--1 draws a line */ +{ + pArea->xPix[1] = pArea->xPixLeft + (x - pArea->xLeft) * pArea->xScale; + pArea->yPix[1] = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + if (pArea->pWin->winType == PPR_WIN_SCREEN) + pArea->yPix[1] = pArea->pWin->height - pArea->yPix[1]; + if (pen) { + pprLineThick(pArea, pArea->pWin->attr.lineThick); + pprLineSegPixD_wc(pArea, pArea->xPix[0], pArea->yPix[0], + pArea->xPix[1], pArea->yPix[1]); + } + pArea->xPix[0] = pArea->xPix[1]; + pArea->yPix[0] = pArea->yPix[1]; +} + +/*+/subr********************************************************************** +* NAME pprPerim - draw a perimeter +* +* DESCRIPTION +* Draw a perimeter with tick marks. The number of intervals +* specified in the plot area structure is used for placing +* the tick marks. +* +* The color attributes for the plot window are used for drawing; +* dashed line and line thickness are ignored. +* +* RETURNS +* void +* +* BUGS +* o only linear axes are handled +* +* SEE ALSO +* pprPerimLabel, pprPerimErase, pprGrid, pprAnnotX, pprAnnotY, pprAreaOpen +* +*-*/ +void +pprPerim(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ +{ + double tickHalf; + int i; + double x, y; + +/*----------------------------------------------------------------------------- +* draw the box +*----------------------------------------------------------------------------*/ + pprMoveD_wc(pArea, pArea->xLeft, pArea->yBot, 0); + pprMoveD_wc(pArea, pArea->xRight, pArea->yBot, 1); + pprMoveD_wc(pArea, pArea->xRight, pArea->yTop, 1); + pprMoveD_wc(pArea, pArea->xLeft, pArea->yTop, 1); + pprMoveD_wc(pArea, pArea->xLeft, pArea->yBot, 1); +/*----------------------------------------------------------------------------- +* draw the x axis tick marks +*----------------------------------------------------------------------------*/ + tickHalf = pArea->tickHt / pArea->yScale; + x = pArea->xLeft; + y = pArea->yBot - 3. * tickHalf; + for (i=1; ixNint; i++) { + x += pArea->xInterval; + pprLineSegD_wc(pArea, x, pArea->yBot - tickHalf, x, pArea->yBot); + pprLineSegD_wc(pArea, x, pArea->yTop + tickHalf, x, pArea->yTop); + } +/*----------------------------------------------------------------------------- +* ditto for y axis +*----------------------------------------------------------------------------*/ + tickHalf = pArea->tickHt / pArea->xScale; + x = pArea->xLeft - 1.5 * tickHalf; + y = pArea->yBot; + for (i=1; iyNint; i++) { + y += pArea->yInterval; + pprLineSegD_wc(pArea, pArea->xLeft - tickHalf, y, pArea->xLeft, y); + pprLineSegD_wc(pArea, pArea->xRight + tickHalf, y, pArea->xRight, y); + } +} + +/*+/subr********************************************************************** +* NAME pprPerimErase - erase within a perimeter +* +* DESCRIPTION +* Erases the screen inside the perimeter for the plot area. (Actually, +* the perimeter and tick marks are erased as well, since plot marks +* may have been drawn on (and thus lie partly outside of) the perimeter +* itself. The perimeter and tick marks are then redrawn.) +* +* RETURNS +* void +* +* SEE ALSO +* pprGridErase, pprAreaErase, pprWinErase +* +*-*/ +void +pprPerimErase(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ +{ + int x,y,width,height; + + if (pArea->pWin->winType != PPR_WIN_SCREEN) + return; + x = pArea->xPixLeft - 3; + y = pArea->yPixBot - 3; + width = (pArea->xRight - pArea->xLeft) * pArea->xScale + 6; + height = (pArea->yTop - pArea->yBot) * pArea->yScale + 6; +#ifdef SUNVIEW + y = pArea->pWin->height - y - height; + pw_writebackground(pArea->pWin->pw, x, y, width, height, PIX_SRC); + pprPerim(pArea); +#elif defined XWINDOWS + y = pArea->pWin->height - y - height; + XClearArea(pArea->pWin->pDisp, pArea->pWin->plotWindow, + x, y, width, height, False); + pprPerim(pArea); + XFlush(pArea->pWin->pDisp); +#endif +} + +/*+/subr********************************************************************** +* NAME pprPerimLabel - draw and label a perimeter +* +* DESCRIPTION +* Draw a perimeter with tick marks. The number of intervals +* specified in the plot area structure is used for placing +* the tick marks. +* +* Axis labels and annotations are drawn using the information from +* the plot area, as specified in the pprAreaOpen call. +* +* RETURNS +* void +* +* BUGS +* o only linear axes are handled +* +* SEE ALSO +* pprPerim, pprGrid, pprAnnotX, pprAnnotY, pprAreaOpen +* +*-*/ +void +pprPerimLabel(pArea, xLabel, xAnnot, yLabel, yAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +char *xLabel; /* I label for x axis, or NULL */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +char *yLabel; /* I label for y axis, or NULL */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I angle for y annotations; 0. or 90. */ +{ + pprPerim(pArea); + pprAnnotX(pArea, 0, pArea->xLeft, pArea->xRight, pArea->xNint, + 0, xLabel, xAnnot, 0.); + pprAnnotY(pArea, 0, pArea->yBot, pArea->yTop, pArea->yNint, + 0, yLabel, yAnnot, angle); +} + +/*+/subr********************************************************************** +* NAME pprPoint - plot a point at a coordinate +* +* DESCRIPTION +* Plot a pixel sized point using the line thickness and color +* attributes of the plot area. +* +* Two entry points are available: +* +* pprPointD(pArea, x, y) x and y are double +* pprPointL(pArea, x, y) x and y are long +* +* RETURNS +* void +* +* BUGS +* o only linear calibration is handled +* +* SEE ALSO +* pprMark, pprAreaOpen, pprAreaSetAttr, pprLine, pprMove, pprText +* +*-*/ +void +pprPointD(pArea, x, y) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate */ +double y; /* I y data coordinate */ +{ + double xPix, yPix; + double xp0,yp0,xp1; + + xPix = pArea->xPixLeft + (x - pArea->xLeft) * pArea->xScale; + yPix = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + pprLineThick(pArea, pArea->attr.lineThick); + if (pArea->pWin->winType == PPR_WIN_SCREEN) { + yPix = pArea->pWin->height - yPix; +#ifdef SUNVIEW + if (pArea->pWin->attr.ltPix > 1) { + yp0 = yPix; + xp0 = xPix - pArea->pWin->attr.ltPix / 2; + xp1 = xp0 + pArea->pWin->attr.ltPix - 1; +#elif defined XWINDOWS + if (pArea->attr.ltPix > 1) { + yp0 = yPix; + xp0 = xPix - pArea->attr.ltPix / 2; + xp1 = xp0 + pArea->attr.ltPix - 1; +#endif + pprLineSegPixD_ac(pArea, xp0, yp0, xp1, yp0); + } + else { +#ifdef SUNVIEW + pw_put(pArea->pWin->pw, (int)xPix, (int)yPix, 1); +#elif defined XWINDOWS + XDrawPoint(pArea->pWin->pDisp, pArea->pWin->plotWindow, + pArea->attr.gc, (int)xPix, (int)yPix, 1); +#endif + } + } + else if (pArea->pWin->winType == PPR_WIN_POSTSCRIPT || + pArea->pWin->winType == PPR_WIN_EPS) { + (void)fprintf(pArea->pWin->file, "%.1f %.1f DP\n", xPix, yPix); + } +} +void +pprPointL(pArea, x, y) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +long x; /* I first x point */ +long y; /* I first y point */ +{ + pprPointD(pArea, (double)x, (double)y); +} + +/*/subhead pprPSProg------------------------------------------------------- +* PostScript routines for handling lines, points, text strings, etc. +*----------------------------------------------------------------------------*/ + +static char *pprPSProg[]={ +/*----------------------------------------------------------------------------- +* DP - draw a point +* x y DP - +*----------------------------------------------------------------------------*/ +"/DP {", +" newpath", +" moveto", +" currentlinewidth dup 2 div neg 0 rlineto 0 rlineto", +" stroke", +"} def", +/*----------------------------------------------------------------------------- +* DS - draw a line segment from x1,y1 to x2,y2 +* x1 y1 x2 y2 DS - +*----------------------------------------------------------------------------*/ +"/DS {", +" newpath", +" 4 2 roll moveto lineto", +" stroke", +"} def", +/*----------------------------------------------------------------------------- +* FSet - set size of current font; if already proper size, no action +* size FSet - +* +* BUGS +* o should keep a list of scalefont which have already been done, to +* avoid the need to do a new one +* o doesn't allow selecting font family +*----------------------------------------------------------------------------*/ +"/F /Helvetica findfont def", +"/F_HT -1 def", +"/FSet {", +" /F_ht exch def", +" F_ht F_HT ne {", +" /F_HT F_ht def", +" F F_HT scalefont dup /FF exch def setfont", +" } {", +" FF setfont", /* if not changing, still must set */ +" } ifelse", +"} def", + +/*----------------------------------------------------------------------------- +* PT - plot a text string +* Text appears at current location with specified size and angle. +* The text can be right, left, or center justified; the "half height" +* of the characters will be at the current location. +* +* string just angle size PT - +* "just" is one of (PTC), (PTR), or (PTL) +*----------------------------------------------------------------------------*/ + +"/PTL { } def", +"/PTC { dup stringwidth pop -2 div 0 rmoveto } def", +"/PTR { dup stringwidth pop neg 0 rmoveto } def", + +"/PT { FSet gsave", +" currentpoint translate rotate", +" cvn cvx exec", /* execute the PTx procedure */ +" 0 F_HT -3 div rmoveto", /* offset to vertical center of char */ +" show", +" grestore } def" +}; + +/*+/subr********************************************************************** +* NAME pprSin_deg - get the sine of an angle in degrees +* +* DESCRIPTION +* Get the sine of an angle in degrees. A table lookup technique +* is used. The angle argument is truncated to the next lower 1/4 +* degree prior to lookup. +* +* RETURNS +* sine +* +*-*/ +double +pprSin_deg(angle) +double angle; /* I angle, in degrees */ +{ + return pprCos_deg(angle - 90.); +} + +/*/subhead character_tables---------------------------------------------------- +* ITABLE = the array defining the character set. Each character is +* defined by one 34 character string "matrix". The "matrix" +* elements consist of a series of up to 17 (IDX,IDY)'s which +* define the next stroke. The vector is positioned at (IX1,IY1) +* and drawn to (IX2,IY2) where: +* IX1 = IX + IDX1 IY1 = IY + IDY1 +* IX2 = IX + IDX2 IY2 = IY + IDY2 +* (IX2,IY2) becomes the next (IX1,IY1) unless the new IDX = 0 +* in which case a new vector is started with the next 2 +* elements after the 0 becoming (IDX,IDY) etc. (ie., an IDX +* = 0 causes a break in the character strokes.) An IDY = 0 +* causes an offset to be added to IY for the remaining strokes +* in the character. This is used for characters such as g and +* y which extend below the relative 0 line. The element +* following an IDY = 0 becomes the IDY actually used. The +* (IDX,IDY) represents a position in a 6 x 9 matrix. IDX has +* a range of 0 - 6, where most characters fall within 1 - 5; +* 0 triggers a new stroke unless it is the 1st element of the +* string in which case it is really 0. IDY has a range of +* 0 - 9, where most characters fall within 1 - 7; 0 triggers +* an offset to lower the rest of the strokes. A non-numeric +* character in the string, ie. a period, is used to terminate +* the stroke matrix for the character. +* +* converted to C by Roger Cole, 12-90 +* +* EFC 4/16/85: included call to UNPKST to make character string +* matrices nearly as efficient as octal matrices. +* Uses 95 microseconds more per character on average. +* EFC 4/8/85: rewritten for STRING a character variable rather than +* hollerith. Extended and rewrote character stroke array +* for nicer characters. Changed definition of INSIZE = 0 +* matrix from 10 x 13 to 9 x 13. +* EFC 9/21/84: rewritten for FORTRAN 77 on DEC KL-10 to use character +* functions rather than binary masks and shifts. Changed +* matrix for tau and xsi. Changed coding to fix bug in +* "y" shifting of charcters and eliminate inconsistencies +* in described usage and actual usage. +* modified by Debby Hyman, 9-80 +* Greek characters almost match QUME printer char set, now +* +*changed 16-may-76 STB CTR +*----------------------------------------------------------------------------*/ +static char *glPprFont0[]={ +/*----------------------------------------------------------------------------- +* blank ! " # $ % & ' ( ) * + , - . / +*----------------------------------------------------------------------------*/ + ".", "2737242702221313222.", "27253727057454757.", "2721047410155501353.", + "5626152444521203137.", "57110162736251604132435241.", + "511516273746451312213153.", "35473735.", "41232547.", "21434527.", + "46220422605414.", "323605414.", "31433331.", "5414.", "3242413132.", + "5711.", +/*----------------------------------------------------------------------------- +* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? +*----------------------------------------------------------------------------*/ + "132141535547271513.", "51110313726.", "51111256472716.", + "44240122141524456472716.", "46410531337.", "57171425455452412112.", + "564727161221415253442413.", "315717.", + "44555647271615244453524121121324.", "5424151627475652412112.", + "454636354504342323343.", "3646453536031334331.", "521456.", + "155501353.", "125416.", "16274756553303231212232.", +/*----------------------------------------------------------------------------- +* @ A B C D E F G H I J K L M N O +*----------------------------------------------------------------------------*/ + "512112162747565443344554.", "23430513711.", "4453524111174756554414.", + "5647271612214152.", "17114153554717.", "1444051111757.", "44140111757.", + "56472716122141520515434.", "51570541401117.", "51110313705717.", + "122131424705727.", "51240571301117.", "511117.", "5157331711.", + "57511711.", "271612214152564727.", +/*----------------------------------------------------------------------------- +* P Q R S T U V W X Y Z [ \ ] ^ _ +*----------------------------------------------------------------------------*/ + "14445556471711.", "27161221415256472703361.", "5134014445556471711.", + "564727161524445352412112.", "313705717.", "575241211217.", "573117.", + "5741352117.", "571105117.", "57340313417.", "51115717.", "41212747.", + "1751.", "21414727.", "273847.", "10252.", +/*----------------------------------------------------------------------------- +* ` a b c d e f g h i j k l m n o +*----------------------------------------------------------------------------*/ + "37454737.", "14254554510524121122353.", "1545535241211201117.", + "512112142555.", "5525141221415205157.", "512112142545545313.", + "56473726210113103414.", "52412112142545540507524121.", "111701425455451.", + "214103135250373737.", "373737030747423121.", "45134101117.", + "41210313727.", "1115014253531034455551.", "111501425455451.", + "122141525445251412.", +/*----------------------------------------------------------------------------- +* p q r s t u v w x y z { | } ~ +*----------------------------------------------------------------------------*/ + "1425455452412112010711.", "544525141221415205075161.", + "1115014254554.", "54452514234352412112.", "2545037324151.", + "151221415205155.", "553115.", "5541352115.", "551105115.", + "15122141520507524121.", "51115515.", "47372622314101424.", + "3137.", "27374642312104454.", "16274657.", +/*----------------------------------------------------------------------------- +* box dot +*----------------------------------------------------------------------------*/ + "1252561612.", "3254361432." +}; +#if 0 +static char *glPprFont1[]={ +/*----------------------------------------------------------------------------- +* blank ! " # $ % & ' ( ) * + , - . / +*----------------------------------------------------------------------------*/ + ".", "115103137.", "212704147.", "15550571101353.", "52511135195958.", + "14264254462214.", "52422614224656.", "135351.", "492523401.", + "294543201.", "125601652.", "52120333705515.", "24324403236.", + "16560353101353.", "3343443433.", "2559.", +/*----------------------------------------------------------------------------- +* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? +*----------------------------------------------------------------------------*/ + "253546483929181625.", "45150353928.", "45151648392918.", + "37270162535463748392918.", "38350461629.", "491917283847463515.", + "4839291816253546372716.", "194925.", "374839291827374635251627.", + "153546483929182747.", "172728181703747483837.", + "45463635450434232334302454.", "13510531557.", "1353015264556.", + "53110135517.", "12213137475601555.", +/*----------------------------------------------------------------------------- +* @ A B C D E F G H I J K L M N O +*----------------------------------------------------------------------------*/ + "12213142332536273848570364433.", "1136510234303526374635.", + "17315705414.", "31370575544241517.", "11513711.", "12520541401656.", + "224253554626151322021410313702747.", "1131021270175756.", + "12214152564727161202444.", "122131475766.", "162747545241211213244453.", + "11212212110415152424103545463635.", "0121011375104161.", + "512112142555.", "114152544515.", "11415254451502353.", +/*----------------------------------------------------------------------------- +* P Q R S T U V W X Y Z [ \ ] ^ _ +*----------------------------------------------------------------------------*/ + "112127017570474151.", "132242535546261513.", "132147.", "52511144175756.", + "21270414705727161524.", "21410313627160364756.", "31571731.", + "0211212314162747565443415162.", "12115152043450442402325016175756.", + "111526465551.", "161221415256.", "492920141.", "33543505414.", + "294940121.", "24364403632.", "33143501454.", +/*----------------------------------------------------------------------------- +* ` a b c d e f g h i j k l m n o +*----------------------------------------------------------------------------*/ + "1353031643705515.", "5141443525141221314255.", + "2141525344140445556472716101.", "1626243343545604721.", + "56473726255352412112132434.", "52412112234302314254554.", + "452514122141525445047201.", "152636454131224556.", + "5414132141535547271514.", "25353141.", "2145473726254151.", + "1511051134555.", "11340512717.", "514204542312112015101.", "25315355.", + "171524445557482817.", +/*----------------------------------------------------------------------------- +* p q r s t u v w x y z { | } ~ +*----------------------------------------------------------------------------*/ + "252104144055442514.", "1626415101156.", "2231415254453524101.", + "552514132232434435.", "4131350552514.", "15132141525445.", + "145404354450251423.", "25141221320333241525445.", + "17260561615244402413125251402.", "152521024354554501.", + "1736560361312215150241.", "142404939282023141.", "6859493012112.", + "445402939484023121.", "041525445465.", +/*----------------------------------------------------------------------------- +* box dot / diamond triangle +*----------------------------------------------------------------------------*/ + "3434.", "13533713." +}; +#endif + +/*+/subr********************************************************************** +* NAME pprText - plot a text string +* +* DESCRIPTION +* Plots a text string at a location. The character "half height" is +* placed at the specified x,y position; the 'justification' option +* selects whether the left end, center, or right end of the text is +* placed at the x,y coordinate. +* +* The character height specification is in terms of a fraction of the +* height of the window. This results in automatic scaling of +* character sizes as the window size changes. If a height of zero +* is specified in the call, then the default height for the plot +* area is used. (The default height was established by pprAreaOpen.) +* +* A macro is available which returns the default character height +* used by this plot package. The value returned is proportional to +* the height of the plot area. This value can be used to generate +* "big" and "small" character sizes with simple multiplication of +* the default height by a "scale factor". +* +* PprDfltCharHt(lowYfrac, highYfrac) +* +* lowYfrac is the vertical fraction of the window at which +* the bottom edge of the plot area lies +* highYfrac is the vertical fraction of the window at which +* the top edge of the plot area lies +* +* It is also often useful to know what horizontal fraction of the +* window width corresponds to a vertical fraction of the height. The +* following routine returns the horizontal fraction: +* +* pprYFracToXFrac(pWin, yFrac) +* +* An alternate entry point, pprText_wc, is available to use the +* plot window's color for drawing the text. +* +* RETURNS +* void +* +* BUGS +* o technique used works only with linear axes +* o ASCII character codes are assumed +* o no checks are made for illegal characters +* o positioning and sizing are somewhat different for the various +* window types +* +* SEE ALSO +* pprChar, pprAreaOpen, pprLine, pprPoint, pprCvtDblToTxt +* +*-*/ +void +pprText(pArea, x, y, text, just, height, angle) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of text */ +double y; /* I y data coordinate of text */ +char *text; /* I text to plot */ +PPR_TXT_JUST just; /* I text justification selector: one of + PPR_TXT_CEN, PPR_TXT_RJ, or PPR_TXT_LJ */ +double height; /* I height of text characters, as a fraction of + the height of the window; a value of + zero results in using a default height */ +double angle; /* I orientation angle of text string, ccw degrees */ +{ + pprText_gen(pArea, x, y, text, just, height, angle, pprLineSegPixD_ac); +} +void +pprText_wc(pArea, x, y, text, just, height, angle) +PPR_AREA *pArea; +double x; +double y; +char *text; +PPR_TXT_JUST just; +double height; +double angle; +{ + pprText_gen(pArea, x, y, text, just, height, angle, pprLineSegPixD_wc); +} +static void +pprText_gen(pArea, x, y, text, just, height, angle, fn) +PPR_AREA *pArea; +double x; +double y; +char *text; +PPR_TXT_JUST just; +double height; +double angle; +void (*fn)(); +{ + double xWin, yWin; + double scale; /* convert character units to win coord */ + double cosT, sinT; + + if (height <= 0.) + height = pArea->charHt; + else + height *= pArea->pWin->height; + xWin = pArea->xPixLeft + (x - pArea->xLeft) * pArea->xScale; + yWin = pArea->yPixBot + (y - pArea->yBot) * pArea->yScale; + + if (pArea->pWin->winType == PPR_WIN_SCREEN) { + if (angle == 0.) cosT = 1., sinT = 0.; + else if (angle == 90.) cosT = 0., sinT = 1.; + else { cosT = pprCos_deg(angle); + sinT = pprSin_deg(angle); + } + + scale = height / 6.; + + if (just == PPR_TXT_CEN) { + xWin -= .5 * (scale * 6. * (double)(strlen(text)-1) * cosT); + yWin -= .5 * (scale * 6. * (double)(strlen(text)-1) * sinT); + } + else if (just == PPR_TXT_RJ) { + xWin -= scale * 6. * (double)(strlen(text)-1) * cosT; + yWin -= scale * 6. * (double)(strlen(text)-1) * sinT; + } + + while (*text != '\0') { + pprText1(pArea, xWin, yWin, *text, 0, scale, sinT, cosT, fn); + xWin += scale * 6. * cosT; + yWin += scale * 6. * sinT; + text++; + } + } + else { + height = 1.5 * height; + pprTextPS(pArea->pWin->file, xWin, yWin, just, text, height, angle); + } +} + +/*+/internal****************************************************************** +* NAME pprText1 - plot a "drawn" character +* +* DESCRIPTION +* Plots a character at x,y using specified scale factor and rotation. +* All sizes and coordinates use window units, rather than data units. +* +* RETURNS +* void +* +* BUGS +* o only linear calibration is handled +* +*-*/ +void +pprText1(pArea, xWin, yWin, ic, nfont, scale, sinT, cosT, fn) +PPR_AREA *pArea; /* I pointer to plot area */ +double xWin; /* IO x position, in window coordinates */ +double yWin; /* IO y position, in window coordinates */ +int ic; /* I character code to plot */ +int nfont; /* I font selector--0 or 1 */ +double scale; /* I scale factor to convert a character height of + 6 to the desired height in window coord */ +double sinT; /* I sine of orientation angle */ +double cosT; /* I cosine of orientation angle */ +void (*fn)(); /* I pointer to drawing fn: pprLineSegPixD_.. */ +{ +/* stolen (who knows how many times removed) from the Los Alamos National + Laboratory Plasma Physics Plotting Package */ + char *font; /* font string for this character */ + int nStrokes; + int mcx, mcy, indw, drawit; + double rx, ry; + int ibyt; + double xp0,xp1,yp0,yp1; + + if (ic < ' ' || ic > '~') + ic = '#'; + if (nfont == 0) + font = glPprFont0[ic-' ']; +#if 0 + else + font = glPprFont1[ic-' ']; +#endif + nStrokes = (int)(index(font, '.') - font) -1; + mcx = 3; + mcy = 4; + indw = 0; + + pprLineThick(pArea, pArea->pWin->attr.lineThick); + drawit = 0; + xp0 = pArea->xPix[0]; + yp0 = pArea->yPix[0]; + while (indw < nStrokes) { + ibyt = font[indw] - '0'; + if (ibyt == 0 && indw != 0) + drawit = 0; /* there is a break in the strokes */ + else { + rx = (float)(ibyt - mcx); + while ((ibyt = font[++indw] - '0') == 0) + mcy = 6; /* this char has a descender, as for y or g */ + ry = (float)(ibyt - mcy); + + xp1 = xWin + scale * (rx*cosT - ry*sinT); + yp1 = yWin + scale * (rx*sinT + ry*cosT); + if (pArea->pWin->winType == PPR_WIN_SCREEN) + yp1 = pArea->pWin->height - yp1; + if (drawit) + fn(pArea, xp0, yp0, xp1, yp1); + xp0 = xp1; + yp0 = yp1; + + drawit = 1; + } + indw++; + } +} + +/*+/internal****************************************************************** +* NAME pprTextPS - send the PostScript commands to plot some text +* +* DESCRIPTION +* This routine sets up the call to the routine (unique to this +* package) in the PostScript printer. This involves: +* o moving to the desired position +* o massaging the caller's text to enclose it in ( and ) and to +* "escape" characters which are special to PostScript +* o setting the font size +* o setting the angle for the text +* o setting for right, centered, or left justification +* +* All text is printed with the font selected by the PostScript +* routine. +* +* RETURNS +* void +* +* BUGS +* o assumes that all characters in the caller's text string are printable +* +*-*/ +static void +pprTextPS(psFile, x, y, just, text, height, angle) +FILE *psFile; /* I pointer to PostScript file */ +double x,y; /* I x and y position, in window coordinates */ +PPR_TXT_JUST just; /* I text justification selector: one of + PPR_TXT_CEN, PPR_TXT_RJ, or PPR_TXT_LJ */ +char *text; /* I the text to plot */ +double height; /* I the desired height of the text, in points */ +double angle; /* I orientation angle of the text, + is ccw */ +{ + char myJust[4]; + +/*----------------------------------------------------------------------------- +* send out the text string, enclosed in PostScript string delimiters of ( ) +* Special characters are sent out as an appropriate \ sequence +*----------------------------------------------------------------------------*/ + (void)fprintf(psFile, "%d %d moveto\n", (int)(x+.5), (int)(y+.5)); + fputc('(', psFile); + while (*text != '\0') { + switch (*text) { + case '\b': + fputc('\\', psFile); fputc('b', psFile); + break; + case '\f': + fputc('\\', psFile); fputc('f', psFile); + break; + case '\n': + fputc('\\', psFile); fputc('n', psFile); + break; + case '\r': + fputc('\\', psFile); fputc('r', psFile); + break; + case '\t': + fputc('\\', psFile); fputc('t', psFile); + break; + case '(': + case ')': + case '\\': + fputc('\\', psFile); + default: + fputc(*text, psFile); + } + text++; + } + fputc(')', psFile); + fputc(' ', psFile); +/*----------------------------------------------------------------------------- +* now send out the rest of the PostScript information, so that the whole +* thing from this routine will be something like the following, with +* the proper choice of PTC PTL and PTR: +* +* x y moveto (text) (PTx) angle typesize PT +*----------------------------------------------------------------------------*/ + if (just == PPR_TXT_CEN) + (void)strcpy(myJust, "PTC"); + else if (just == PPR_TXT_RJ) + (void)strcpy(myJust, "PTR"); + else + (void)strcpy(myJust, "PTL"); + (void)fprintf(psFile, "(%s) %.2f %d PT\n", myJust, angle, (int)(height+.5)); +} + +/*/subhead pprWinAttr---------------------------------------------------------- +* +*----------------------------------------------------------------------------*/ +static void +pprWinAttr(pWin) +PPR_WIN *pWin; +{ +#ifdef SUNVIEW + pWin->width = (int)window_get(pWin->canvas, WIN_WIDTH); + pWin->height = (int)window_get(pWin->canvas, WIN_HEIGHT); + pWin->x = (int)window_get(pWin->frame, WIN_X); + pWin->y = (int)window_get(pWin->frame, WIN_Y); +#elif defined XWINDOWS + XWindowAttributes winAttr; + XWindowAttributes parentAttr; + long stat; + Window rootWindow, parentWindow, *pChildWindows; + unsigned int nChildren; + int actualX, actualY; + + XGetWindowAttributes(pWin->pDisp, pWin->plotWindow, &winAttr); + actualX = winAttr.x; + actualY = winAttr.y; + stat = XQueryTree(pWin->pDisp, pWin->plotWindow, &rootWindow, + &parentWindow, &pChildWindows, &nChildren); + PprAssert(stat != 0); + XFree(pChildWindows); + if (rootWindow != parentWindow) { + XGetWindowAttributes(pWin->pDisp, parentWindow, &parentAttr); + actualX = parentAttr.x; + actualY = parentAttr.y; + } + pWin->width = winAttr.width; + pWin->height = winAttr.height; + pWin->x = actualX; + pWin->y = actualY; +#endif +} + +/*+/subr********************************************************************** +* NAME pprWinClose - close a plot window +* +* DESCRIPTION +* Free the memory associated with a plot window structure and do other +* cleanup activities. +* +* This routine should be called when plotting is complete for a plot +* window. Any plot areas not previously closed are automatically +* closed by this routine. +* +* No further references to the plot window may be made. +* +* RETURNS +* void +* +* SEE ALSO +* pprAreaClose, pprWinInfo, pprWinOpen +* +*-*/ +void +pprWinClose(pWin) +PPR_WIN *pWin; /* IO pointer to plot window structure */ +{ + PPR_AREA *pArea, *pAreaNext; +#ifdef XWINDOWS + if (pWin->winType == PPR_WIN_SCREEN) { + if (pWin->attr.myGC) + XFree(pWin->attr.gc); + } +#endif + pArea = pWin->pAreaHead; + while (pArea != NULL) { + pAreaNext = pArea->pNext; + pprAreaClose(pArea); + pArea = pAreaNext; + } + free((char *)pWin); +} + +/*+/subr********************************************************************** +* NAME pprWinErase - erase a plot window +* +* DESCRIPTION +* Erase the contents of the entire plot window. +* +* RETURNS +* void +* +* SEE ALSO +* pprGridErase, pprPerimErase, pprAreaErase +* +*-*/ +void +pprWinErase(pWin) +PPR_WIN *pWin; /* IO pointer to plot window structure */ +{ + if (pWin->winType == PPR_WIN_SCREEN) { +#ifdef SUNVIEW + pw_writebackground(pWin->pw, 0, 0, pWin->width, pWin->height, PIX_SRC); +#elif defined XWINDOWS + XClearArea(pWin->pDisp, pWin->plotWindow, 0, 0, + pWin->width, pWin->height, False); + XFlush(pWin->pDisp); +#endif + } +} + +/*+/internal****************************************************************** +* NAME pprWinEvHandler - handle events in the plotting window +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o action needs to depend on type of window +* o for SunView, a pw_lock() and pw_unlock() would make drawing more +* efficient +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +#ifdef SUNVIEW +static void +pprWinEvHandler(window, pEvent, pArg) +Window window; +Event *pEvent; +void *pArg; +{ + PPR_WIN *pWin; + + pWin = (PPR_WIN *)window_get(window, WIN_CLIENT_DATA); + if (pWin->winType != PPR_WIN_SCREEN) + PprAssertAlways(0); + if (event_action(pEvent) == WIN_REPAINT) { + if (window == pWin->canvas && !window_get(window, FRAME_CLOSED)) { + pprWinWrapup(pWin); + pWin->attr.ltCurr = -1; + pWin->attr.lineThick = 1; + pprWinAttr(pWin); + (pWin->drawFun)(pWin, pWin->pDrawArg); + } + } + else if (event_action(pEvent) == PPR_BTN_CLOSE) { + if (event_is_up(pEvent)) { + pprWinAttr(pWin); + window_destroy(pWin->frame); + pWin->frame = NULL; + pWin->canvas = NULL; + pprWinWrapup(pWin); + } + } +} +#endif + +#ifdef XWINDOWS +static void +pprWinEvHandler(pWin, pEvent) +PPR_WIN *pWin; +XEvent *pEvent; /* pointer to a window event structure */ +{ + + if (pEvent->type == Expose && pEvent->xexpose.count == 0) { +#define PPR_DEBUG_EVENTS 0 +#if PPR_DEBUG_EVENTS + (void)printf("expose event\n"); +#endif + pprWinWrapup(pWin); + pprWinAttr(pWin); + (pWin->drawFun)(pWin, pWin->pDrawArg); + } + else if (pEvent->type == ButtonRelease) { + pprWinAttr(pWin); + if (pEvent->xbutton.x < 0 || pEvent->xbutton.x > pWin->width || + pEvent->xbutton.y < 0 || pEvent->xbutton.y > pWin->height) { +#if PPR_DEBUG_EVENTS + (void)printf("button up but mouse not home\n"); + (void)printf("button x,y=%d,%d win x,y width,ht=%d,%d %d,%d\n", + pEvent->xbutton.x, pEvent->xbutton.y, + pWin->x, pWin->y, pWin->width, pWin->height); +#endif + ; /* no action */ + } + else if (pEvent->xbutton.button == PPR_BTN_CLOSE) { +#if PPR_DEBUG_EVENTS + (void)printf("button3 event\n"); +#endif + XCloseDisplay(pWin->pDisp); + pWin->pDisp = NULL; + pprWinWrapup(pWin); + } +#if PPR_DEBUG_EVENTS + else + (void)printf("other button event\n"); +#endif + } +#if PPR_DEBUG_EVENTS + else + (void)printf("some other event\n"); +#endif +} +#endif + +/*+/subr********************************************************************** +* NAME pprWinInfo - get some information about the plot window +* +* DESCRIPTION +* Get the size of the plot window and its position on the screen. +* +* RETURNS +* void +* +* NOTES +* 1. The information returned is window system dependent. To avoid +* portability problems, this information should be used only in +* calls to pprWinXxx routines. +* +*-*/ +void +pprWinInfo(pWin, pXpos, pYpos, pXwid, pYht) +PPR_WIN *pWin; /* I pointer to plot window structure */ +int *pXpos; /* O pointer to place to store window x coord., in pixels */ +int *pYpos; /* O pointer to place to store window y coord., in pixels */ +int *pXwid; /* O pointer to place to store window width, in pixels */ +int *pYht; /* O pointer to place to store window height, in pixels */ +{ + *pXpos = pWin->x; + *pYpos = pWin->y; + *pXwid = pWin->width; + *pYht = pWin->height; +} + +/*+/subr********************************************************************** +* NAME pprWinIsMono - test to see if plot window is monochrome +* +* DESCRIPTION +* +* RETURNS +* 1 if plot window is monochrome or gray scale +* 0 if plot window is color +* +*-*/ +int +pprWinIsMono(pWin) +PPR_WIN *pWin; /* I pointer to plot window structure */ +{ +#ifdef XWINDOWS + int screenNo; + Visual *pVisual; +#endif + +#ifdef XWINDOWS + if (pWin->winType != PPR_WIN_SCREEN) + return 1; + screenNo = DefaultScreen(pWin->pDisp); + pVisual = DefaultVisual(pWin->pDisp, screenNo); + if (pVisual->class != GrayScale && pVisual->class != StaticGray) + return 0; +#endif + return 1; /* color not supported if not XWINDOWS */ +} + +/*+/subr********************************************************************** +* NAME pprWinLoop - loop until "quit" event received in window +* +* DESCRIPTION +* Handles the interactions with the windowing system. The specific +* actions depend on the plot window type: +* +* PPR_WIN_SCREEN +* o creates a window on the screen +* o when the window actually appears, calls the caller's draw function +* o for all subsequent resize and expose events, calls the caller's +* draw function +* o when the right mouse button is clicked, closes the window and +* returns to the caller. The current position and size of the +* window are stored (and can be retrieved with pprWinInfo). +* +* PPR_WIN_POSTSCRIPT +* o calls the caller's draw function +* +* PPR_WIN_EPS +* o calls the caller's draw function +* +* The idea when using pprWinLoop is that a program +* will do some preliminary setup for the data to be plotted. +* Then the program must turn control over to the plot "window +* manager" using pprWinLoop, which will call the caller's +* actual plot routine. +* +* When pprWinLoop exits back to the calling program, that program +* can call pprWinInfo in order to "remember" the plot window +* size and position. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* BUGS +* o doesn't furnish information to the draw function which would allow +* a partial redraw +* o terminology is confusing and inconsistent: "draw" function, redraw, +* replot, repaint, etc. +* +* SEE ALSO +* pprWinOpen, pprWinInfo +* +* NOTES +* 1. Even though there aren't any "events" associated with plotting on +* a PostScript printer, this routine must be called even when using +* PPR_WIN_POSTSCRIPT and PPR_WIN_EPS, since this routine invokes the +* caller's "draw" function. +* +* EXAMPLE +* See pprWinOpen for an example of a replot function. +jjj +* +*-*/ +long +pprWinLoop(pWin, drawFun, pDrawArg) +PPR_WIN *pWin; /* IO pointer to plot window structure */ +void (*drawFun)();/* I pointer to function to draw the plot */ +void *pDrawArg;/* I pointer to pass to drawFun */ +{ +#ifdef XWINDOWS + XEvent anEvent; /* a window event structure */ +#endif + + pWin->drawFun = drawFun; + pWin->pDrawArg = pDrawArg; + if (pprWinMap(pWin) != 0) + return -1; + if (pWin->winType == PPR_WIN_SCREEN) { +#ifdef SUNVIEW + window_main_loop(pWin->frame); +#elif defined XWINDOWS + while (pWin->pDisp != NULL) { + XNextEvent(pWin->pDisp, &anEvent); + pprWinEvHandler(pWin, &anEvent); + } +#endif + } + else if (pWin->winType == PPR_WIN_POSTSCRIPT || + pWin->winType == PPR_WIN_EPS) { + (pWin->drawFun)(pWin, pWin->pDrawArg); + } + else + PprAssertAlways(0); + pprWinWrapup(pWin); + return 0; +} + +/*+/internal****************************************************************** +* NAME pprWinMap - create a plotting "window" and map it onto the display +* +* DESCRIPTION +* This routine actually creates a window on a display device. This +* must be done prior to drawing; the PPR_WIN structure must have +* been initialized by pprWinOpen . +* +* If the display "device" is a file for PostScript printing, then +* the "window" which is created isn't actually a window, but the +* plotting operations of the user routine don't know the difference. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* SEE ALSO +* pprWinOpen, pprWinLoop, pprWinWrapup +* +* NOTES +* 1. This routine is called automatically by pprWinLoop, so it should +* not be called by programs that use pprWinLoop. +* +*-*/ +long +pprWinMap(pWin) +PPR_WIN *pWin; /* IO pointer to plot window structure */ +{ +#ifdef XWINDOWS + int screenNo, x, y, width, height; + XSizeHints sizeHints; +#endif + if (pWin->winType == PPR_WIN_SCREEN) { +#ifdef SUNVIEW + pWin->frame = window_create(NULL, FRAME, + FRAME_LABEL, pWin->title, + FRAME_NO_CONFIRM, 1, + WIN_EVENT_PROC, pprWinEvHandler, + WIN_CLIENT_DATA, pWin, + WIN_X, pWin->x, WIN_Y, pWin->y, + 0); + window_set(pWin->frame, WIN_CONSUME_PICK_EVENTS, WIN_NO_EVENTS, + ACTION_OPEN, ACTION_CLOSE, + ACTION_FRONT, ACTION_BACK, + WIN_MOUSE_BUTTONS, WIN_UP_EVENTS, 0, + 0); + pWin->canvas = window_create(pWin->frame, CANVAS, + WIN_WIDTH, pWin->width, + WIN_HEIGHT, pWin->height, + WIN_EVENT_PROC, pprWinEvHandler, + WIN_CLIENT_DATA, pWin, + CANVAS_AUTO_SHRINK, TRUE, + CANVAS_AUTO_EXPAND, TRUE, + CANVAS_FIXED_IMAGE, FALSE, + CANVAS_RETAINED, FALSE, + CANVAS_AUTO_CLEAR, TRUE, + 0); + pWin->pw = canvas_pixwin(pWin->canvas); + window_set(pWin->canvas, WIN_CONSUME_PICK_EVENTS, WIN_NO_EVENTS, + WIN_MOUSE_BUTTONS, WIN_UP_EVENTS, 0, + 0); + window_fit(pWin->frame); + window_set(pWin->frame, WIN_SHOW, 1, 0); + window_set(pWin->canvas, WIN_SHOW, 1, 0); +#elif defined XWINDOWS + if (pWin->winDispName[0] == '\0') + pWin->pDisp = XOpenDisplay((char *)NULL); + else + pWin->pDisp = XOpenDisplay(pWin->winDispName); + if (pWin->pDisp == NULL) { + (void)printf("pprWinMap: XOpenDisplay to %s failed\n", + XDisplayName(pWin->winDispName)); + return -1; + } + screenNo = DefaultScreen(pWin->pDisp); + pWin->attr.gc = DefaultGC(pWin->pDisp, screenNo); + pWin->attr.myGC = 0; + sizeHints.x = x = pWin->x; + sizeHints.y = y = pWin->y; + sizeHints.width = width = pWin->width; + sizeHints.height = height = pWin->height; + sizeHints.flags = PSize | PPosition; + pWin->plotWindow = XCreateSimpleWindow(pWin->pDisp, + DefaultRootWindow(pWin->pDisp), + x, y, width, height, 1, + BlackPixel(pWin->pDisp, screenNo), + WhitePixel(pWin->pDisp, screenNo)); + + XSetStandardProperties(pWin->pDisp, pWin->plotWindow, + pWin->title, pWin->title, + None, 0, NULL, &sizeHints); + XSelectInput(pWin->pDisp, pWin->plotWindow, + ExposureMask | ButtonPressMask | ButtonReleaseMask); + XMapWindow(pWin->pDisp, pWin->plotWindow); +#endif + } + else if (pWin->winType == PPR_WIN_POSTSCRIPT || + pWin->winType == PPR_WIN_EPS) { + ; /* no action */ + } + else + PprAssertAlways(0); + + return 0; +} + +/*+/subr********************************************************************** +* NAME pprWinOpen - initialize a plotting "window" structure +* +* DESCRIPTION +* Initialize a plot window structure. This is the structure which +* keeps track of the information needed to interact with the +* device on which the "window" resides. The possible types of +* windows are: +* +* o PPR_WIN_SCREEN selects a window on a visual display. This +* will be a SunView or X window, depending on the version of +* the plotting used in linking the program. The pprWinOpen +* call actually creates a window, with the root window as the +* parent. The window thus created can be moved, resized, etc. +* according to the capabilities of the window manager of the +* windowing system being used. (To initialize a plotting +* window structure in a window which already exists, use +* pprWinOpenUW.) +* +* for SunView, +* `winDispName' is ignored and should be NULL +* `winTitle' appears on the window's title bar and icon +* `xPos' and `yPos' are pixel offsets from the upper left +* corner of the root window. If values of 0 are +* supplied, then the window will be positioned at +* 100,100. +* `xWid' and `yHt' are the width and height of the window, +* in pixels. If values of 0 are supplied, then +* a size of 512,512 will be used. +* +* for X, +* `winDispName' specifies the X display name on which the +* plot window is to appear. If NULL, then the +* DISPLAY environment variable will be used. +* `winTitle' appears on the window's title bar and icon +* `xPos' and `yPos' are pixel offsets from the upper left +* corner of the root window. If values of 0 are +* supplied, then the window will be positioned at +* 100,100. +* `xWid' and `yHt' are the width and height of the window, +* in pixels. If values of 0 are supplied, then +* a size of 512,512 will be used. +* +* o PPR_WIN_POSTSCRIPT selects a "window" on a PostScript +* printer. +* +* `winDispName' is the name of a file to receive PostScript +* output. This is the file which will eventually +* be sent to a PostScript printer to get a hard +* copy of the plot. If the file exists when +* pprWinOpen is called, its contents are erased +* before writing begins. +* `winTitle' is ignored and should be NULL +* `xPos' and `yPos' are ignored +* `xWid' and `yHt' are the width and height of the window, +* in pixels. If values of 0 are supplied, then +* a size of 512,512 will be used. If the size is +* larger than the page, then the plot will be scaled +* to fit. If necessary, the plot will be printed in +* landscape mode, rather than the default of portrait +* mode. +* +* o PPR_WIN_EPS selects a "window" in an Encapsulated PostScript +* file. EPS files are intended to be included into documents +* prepared by word processing programs such as Interleaf and +* TeX. EPS files will not print directly on a PostScript printer. +* The description of the arguments for PPR_WIN_POSTSCRIPT applies, +* except that scaling and rotation aren't done. +* +* The pprXxx routines can be used for several different styles of +* usage: +* +* o a complete set of data is available prior to calling any of +* the plotting routines. In this case, grids can be drawn and +* data can be plotted at the same time. +* +* For this style of usage, the typical usage will be: +* pprWinOpen to "open" a plot window +* pprWinLoop to map the plot window to the screen +* and call the caller's replot function +* when the window is exposed, resized, +* etc. +* pprWinClose to "close" a plot window +* +* o no data (or only part of the data) is available prior to calling +* any of the plotting routines. In this case, at least some of +* the data must be plotted at a later time than when the grids +* are drawn. The pprXxx routines don't automatically support +* this style of usage fully, but they do provide some tools which +* make it relatively easy to implement. See pprWinOpenUW for +* more details. +* +* Under all circumstances, the calling program is expected to call +* pprWinClose when plotting is complete for the plot window. +* +* RETURNS +* pointer to window structure, or +* NULL if an error is encountered +* +* SEE ALSO +* pprWinOpenUW, pprWinClose, pprWinInfo, pprWinLoop +* +* EXAMPLES +* 1. Plot an existing set of data, where the data is stored in `dataStruct'. +* +* PPR_WIN *pWindow; +* . +* pWindow = pprWinOpen(PPR_WIN_SCREEN, NULL, "test", 0, 0, 0, 0); +* if (pWindow == NULL) +* abort(); +* if (pprWinLoop(pWindow, replot, &dataStruct) != 0) +* abort(); +* pprWinClose(pWindow); +* +*-*/ +PPR_WIN * +pprWinOpen(winType, winDispName, winTitle, xPos, yPos, xWid, yHt) +PPR_WIN_TY winType; /* I type of plot window: PPR_WIN_xxx */ +char *winDispName; /* I name of "display" or file for window */ +char *winTitle; /* I title for window title bar and icon */ +int xPos; /* I x position for window; 0 for default */ +int yPos; /* I y position for window; 0 for default */ +int xWid; /* I width of window; 0 for default */ +int yHt; /* I height of window; 0 for default */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + + if ((pWin = (PPR_WIN *)malloc(sizeof(PPR_WIN))) == NULL) { + (void)printf("pprWinOpen: couldn't malloc plot window struct\n"); + return NULL; + } + pWin->pAreaHead = NULL; + pWin->pAreaTail = NULL; + pWin->winType = winType; + pWin->drawFun = NULL; + pWin->pDrawArg = NULL; + if (xPos != 0) + pWin->x = xPos; + else if (winType == PPR_WIN_POSTSCRIPT || winType == PPR_WIN_EPS) + pWin->x = 0; + else + pWin->x = 100; + if (yPos != 0) + pWin->y = yPos; + else if (winType == PPR_WIN_POSTSCRIPT || winType == PPR_WIN_EPS) + pWin->y = 0; + else + pWin->y = 100; + if (xWid != 0) + pWin->width = xWid; + else if (winType == PPR_WIN_POSTSCRIPT || winType == PPR_WIN_EPS) + pWin->width = 0; + else + pWin->width = 512; + if (yHt != 0) + pWin->height = yHt; + else if (winType == PPR_WIN_POSTSCRIPT || winType == PPR_WIN_EPS) + pWin->height = 0; + else + pWin->height = 512; + + if (winDispName == NULL) + pWin->winDispName[0] = '\0'; + else if (strlen(winDispName) >= 120) { + (void)printf("pprWinOpen: plot file or display name too long\n"); + free((char *)pWin); + return NULL; + } + else + (void)strcpy(pWin->winDispName, winDispName); + + if (winTitle == NULL) + pWin->title[0] = '\0'; + else if (strlen(winTitle) >= 80) { + (void)printf("pprWinOpen: winTitle too long\n"); + free((char *)pWin); + return NULL; + } + else + (void)strcpy(pWin->title, winTitle); + + pWin->attr.lineThick = 1; + pWin->attr.ltCurr = -1; + pWin->attr.pPatt = NULL; + pWin->attr.myGC = 0; + +/*----------------------------------------------------------------------------- +* PostScript printer initialization +* o for PostScript, +* - send the %!PS line +* o for Encapsulated PostScript, +* - send the %!PS,EPS line +* - send the %%BoundingBox line +* o send the various PostScript programs needed +* o translate, rotate, and scale the PostScript axis as needed for +* the most recent size the window had. +*----------------------------------------------------------------------------*/ + if (winType == PPR_WIN_POSTSCRIPT || winType == PPR_WIN_EPS) { + int i; + int nLines; + double scale, xscale, yscale; + + if ((pWin->file = fopen(winDispName, "w")) == NULL) { + perror("opening PostScript or EPS file"); + PprAssertAlways(0); + } + if (winType == PPR_WIN_POSTSCRIPT) + (void)fprintf(pWin->file, "%%!PS\n"); + else { + (void)fprintf(pWin->file, "%%!PS-Adobe-2.0 EPSF-1.2\n"); + (void)fprintf(pWin->file, "%%%%BoundingBox: 0 0 %d %d\n", + pWin->width - 1, pWin->height - 1); + } + +/*----------------------------------------------------------------------------- +* write PostScript programs and defaults to file +*----------------------------------------------------------------------------*/ + nLines = sizeof(pprPSProg)/sizeof(char *); + for (i=0; ifile, "%s\n", pprPSProg[i]); + if (winType == PPR_WIN_POSTSCRIPT) { + if (pWin->width <= 560 && pWin->height <= 720) { + (void)fprintf(pWin->file, "%d %d translate\n", + (int)((612-pWin->width)/2), (760-pWin->height)); + } + else if (pWin->width <= 720 && pWin->height <= 560) { + (void)fprintf(pWin->file, "612 0 translate 90 rotate\n"); + (void)fprintf(pWin->file, "%d %d translate\n", + (int)((792-pWin->width)/2), (560-pWin->height)); + } + else if (pWin->width <= pWin->height) { + xscale = 560. / (double)pWin->width; + yscale = 720. / (double)pWin->height; + if (xscale <= yscale) + scale = xscale; + else + scale = yscale; + (void)fprintf(pWin->file, "%.3f %.3f scale\n", scale, scale); + (void)fprintf(pWin->file, "%d %d translate\n", + (int)((612./scale-pWin->width)/2.), + (int)(760./scale-pWin->height)); + } + else { + xscale = 560. / (double)pWin->width; + yscale = 720. / (double)pWin->height; + if (xscale <= yscale) + scale = xscale; + else + scale = yscale; + (void)fprintf(pWin->file, "612 0 translate 90 rotate\n"); + (void)fprintf(pWin->file, "%.3f %.3f scale\n", scale, scale); + (void)fprintf(pWin->file, "%d %d translate\n", + (int)((792./scale-pWin->width)/2.), + (int)(560./scale-pWin->height)); + } + } + } + return pWin; +} + +/*+/subr********************************************************************** +* NAME pprWinOpenUW - open a plot "window" to an existing User Window +* +* DESCRIPTION +* Initialize a plot window structure. This is the structure which +* keeps track of the information needed to interact with the +* device on which the "window" resides. +* +* This routine is for use when the caller already has a window in +* the windowing system. It is for use exclusively for PPR_WIN_SCREEN +* plot window type. The form of the call to this routine is heavily +* dependent on the windowing system being used. This routine provides +* the basis for obtaining `asynchronous' plotting. (It can also be +* used for `batched' plotting, in which all data is available prior +* to the first plotting call.) +* +* Under all circumstances, the calling program is expected to call +* pprWinClose when plotting is complete for the plot window. +* +* RETURNS +* pointer to window structure, or +* NULL if an error is encountered +* +* SEE ALSO +* pprWinOpenUW, pprWinClose, pprWinInfo, pprWinLoop +* +* EXAMPLES +* 1. for X +* +* PPR_WIN *pWin; +* Display *pDisp; +* Window plotWindow; +* GC plotGC; +* +* pWin = pprWinOpenUW(&pDisp, &plotWindow, &plotGC, NULL); +* ... +* pprWinReplot(pWin, drawFn, drawArg); +* ... +* pprWinClose(pWin); +* +* +* 2. for SunView +* +* PPR_WIN *pWin; +* Frame plotFrame; +* Canvas plotCanvas; +* +* pWin = pprWinOpenUW(&plotFrame, &plotCanvas, NULL, NULL); +* ... +* pprWinReplot(pWin, drawFn, drawArg); +* ... +* pprWinClose(pWin); +* +* 3. for XView +* +* PPR_WIN *pWin; +* Display *pDisp; +* Window plotWindow; +* GC plotGC; +* +* pDisp = (Display *)xv_get(frame, XV_DISPLAY); +* window = (Window)xv_get(canvas_paint_window(canvas), XV_XID); +* plotGC = DefaultGC(pDisp, DefaultScreen(pDisp)); +* +* pWin = pprWinOpenUW(&pDisp, &plotWindow, &plotGC, NULL); +* ... +* pprWinReplot(pWin, drawFn, drawArg); +* ... +* pprWinClose(pWin); +* +*-*/ +PPR_WIN * +pprWinOpenUW(pArg1, pArg2, pArg3, pArg4) +void *pArg1; +void *pArg2; +void *pArg3; +void *pArg4; +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + + if ((pWin = (PPR_WIN *)malloc(sizeof(PPR_WIN))) == NULL) { + (void)printf("pprWinOpenUW: couldn't malloc plot window struct\n"); + return NULL; + } + pWin->pAreaHead = NULL; + pWin->pAreaTail = NULL; + pWin->winType = PPR_WIN_SCREEN; + pWin->drawFun = NULL; + pWin->pDrawArg = NULL; + pWin->winDispName[0] = '\0'; + pWin->title[0] = '\0'; +#ifdef SUNVIEW + pWin->frame = *(Frame *)pArg1; + pWin->canvas = *(Canvas *)pArg2; + pWin->pw = canvas_pixwin(pWin->canvas); +#elif defined XWINDOWS + pWin->pDisp = *(Display **)pArg1; + pWin->plotWindow = *(Window *)pArg2; + pWin->attr.gc = *(GC *)pArg3; + pWin->attr.myGC = 0; +#endif + pprWinAttr(pWin); + + pWin->attr.lineThick = 1; + pWin->attr.ltCurr = -1; + pWin->attr.pPatt = NULL; + + return pWin; +} + +/*+/subr********************************************************************** +* NAME pprWinReplot - redraw a plot in a user owned window +* +* DESCRIPTION +* Calls the "replot" function to repaint the plot window. This +* routine is intended to be used with "user owned" plot windows +* which have been opened with pprWinOpenUW. +* +* Prior to calling the replot function, this routine determines +* the size of the actual window and rescales the existing plot +* areas. +* +* RETURNS +* void +* +* SEE ALSO +* pprWinOpenUW +* +*-*/ +void +pprWinReplot(pWin, pFunc, pArg) +PPR_WIN *pWin; +void (*pFunc)(); +void *pArg; +{ + int width, height; + PPR_AREA *pArea; + + width = pWin->width; + height = pWin->height; + pprWinAttr(pWin); + if (pWin->width != width || pWin->height != height) { + pArea = pWin->pAreaHead; + while (pArea != NULL) { + pprAreaRescale(pArea, pArea->xLeft, pArea->yBot, + pArea->xRight, pArea->yTop); + pArea = pArea->pNext; + } + } + pWin->drawFun = pFunc; + pWin->pDrawArg = pArg; + (pWin->drawFun)(pWin, pWin->pDrawArg); + pprWinWrapup(pWin); +} + +/*+/internal****************************************************************** +* NAME pprWinWrapup - wrapup plotting operations +* +* DESCRIPTION +* Wrap up plotting operations. +* +* RETURNS +* void +* +* SEE ALSO +* pprWinMap, pprWinOpen, pprWinLoop, pprWinOpenUW, pprWinReplot +* +* NOTES +* 1. This routine is called automatically by pprWinLoop. It should be +* used only by programs which don't use pprWinLoop. +* +*-*/ +void +pprWinWrapup(pWin) +PPR_WIN *pWin; /* IO pointer to plot window structure */ +{ + if (pWin->winType == PPR_WIN_POSTSCRIPT) + (void)fprintf(pWin->file, "showpage"); + if (pWin->winType == PPR_WIN_POSTSCRIPT || pWin->winType == PPR_WIN_EPS) { + (void)fclose(pWin->file); + pWin->file = NULL; + } +} + +/*+/subr********************************************************************** +* NAME pprYFracToXFrac - convert a Y fraction to an X fraction +* +* DESCRIPTION +* Converts a value which is a fraction of window height into a value +* which is a fraction of window width, so that the two values will +* represent the same physical size, in pixels. +* +* This routine is useful for laying out a plot area, especially when +* a grid with annotations and labels is to be used. The choice of +* "data area" size (i.e., the size of the grid) depends on the size +* of the characters which will be used for the annotations and +* labels. +* +* RETURNS +* void +* +* SEE ALSO +* pprText +* +* EXAMPLE +* A plot area is use the full width and the vertical 1/3 of the window. +* x values range from 0 to 100, while y is from -10 to 10. The x +* axis is to be divided into 10 divisions, the y into 4. Use +* the default character size for annotations and labels. Also, +* use the default window position and size. +* +* PPR_WIN *pWin; +* PPR_AREA *pArea; +* float charHt; height as a fraction of window height +* float charWid; width as a fraction of window width +* +* charHt = PprDfltCharHt(.33, .67); +* charWid = pprYFracToXFrac(pWin, charHt); +* pArea = pprAreaOpen(pWin, 12.*charWid, .33+6.*charHt, 1., .67, +* 0., -10., 100., 10, 4, charHt); +* +*-*/ +double +pprYFracToXFrac(pWin, yFrac) +PPR_WIN *pWin; /* I pointer to plot window structure */ +double yFrac; /* I fraction of window height */ +{ + return (yFrac * pWin->height / pWin->width); +} + +/*+/internal****************************************************************** +* NAME pprSleep - simple sleep routine to use with plots +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +static void +pprSleep(seconds, usec) +int seconds; /* I number of seconds (added to usec) to sleep */ +int usec; /* I number of micro-sec (added to sec) to sleep */ +{ +#ifndef vxWorks + usleep((unsigned)(seconds*1000000 + usec)); +#else + int ticks; + static int ticksPerSec=0; + static int usecPerTick; + + if (ticksPerSec == 0) { + ticksPerSec = sysClkRateGet(); + usecPerTick = 1000000 / ticksPerSec; + } + + ticks = seconds*ticksPerSec + usec/usecPerTick + 1; + taskDelay(ticks); +#endif +} diff --git a/src/util/pprPlot.man b/src/util/pprPlot.man new file mode 100644 index 000000000..a8b2118ac --- /dev/null +++ b/src/util/pprPlot.man @@ -0,0 +1,1769 @@ +TITLE pprSubr.c - portable plotting routines + +DESCRIPTION + These routines support simple 2-D plotting in a "window". The + window can be either an actual display window, a PostScript + printer page, or an EPS file for Encapsulated PostScript. + +QUICK REFERENCE +PPR_AREA *pArea; pointer to a plot area + PPR_WIN *pWin; pointer to a plot "window" + + void pprAnnotX( pArea, offset, dataL, dataR, nDiv, + drawLine, xLabel, xAnnot, angle ) + void pprAnnotY( pArea, offset, dataB, dataT, nDiv, + drawLine, yLabel, yAnnot, angle ) + void pprAnnotYMark( pArea, offset, markNum ) + void pprAreaClose( pArea ) + void pprAreaErase( pArea fracL, fracB, fracR, fracT ) +PPR_AREA *pprAreaOpen( pWin, fracL, fracB, fracR, fracT, + dataL, dataB, dataR, dataT, + nXint, nYint, charHt ) +PPR_AREA *pprAreaOpenDflt( pWin, fracL, fracB, fracR, fracT, + dataL, dataB, dataR, dataT ) + void pprAreaRescale( pArea, dataL, dataB, dataR, dataT ) + long pprAreaSetAttr( pArea, code, attrVal, pAttr ) + code: PPR_ATTR_{CLIP,COLORNUM,BG,FG,KEYNUM,LINE_THICK,NOCLIP,PATT_ARRAY} + void pprAutoEnds( dbl1, dbl2, >pNewDbl1, >pNewDbl2 ) + void pprAutoInterval( dbl1, dbl2, >pNint ) + void pprAutoRangeD( dblAry, nPts, >minDbl, >maxDbl ) + void pprAutoRangeF( fltAry, nPts, >minDbl, >maxDbl ) + void pprAutoRangeL( lngAry, nPts, >minDbl, >maxDbl ) + void pprAutoRangeS( shtAry, nPts, >minDbl, >maxDbl ) + void pprChar( pArea, xDbl, yDbl, char, charHt, angle ) + double pprCos_deg( angle ) + void pprCvtDblToTxt( >text, width, dblVal, nSigDigits ) + void pprGrid( pArea ) + void pprGridErase( pArea ) + void pprGridLabel( pArea, xLabel, xAnnot, yLabel, yAnnot, angle) + void pprLineD( pArea, xDblAry, yDblAry, nPts ) + void pprLineF( pArea, xFltAry, yFltAry, nPts ) + void pprLineL( pArea, xLngAry, yLngAry, nPts ) + void pprLineS( pArea, xShtAry, yShtAry, nPts ) + void pprLineSegD( pArea, xDbl1, yDbl1, xDbl2, yDbl2 ) + void pprLineSegL( pArea, xLng1, yLng1, xLng2, yLng2 ) + void pprMarkD( pArea, xDbl1, yDbl1, markNum ) + void pprMarkL( pArea, xLng1, yLng1, markNum ) + void pprMoveD( pArea, xDbl1, yDbl1, penDown ) + void pprPerim( pArea ) + void pprPerimErase( pArea ) + void pprPerimLabel( pArea, xLabel, xAnnot, yLabel, yAnnot, angle) + void pprPointD( pArea, xDbl1, yDbl1 ) + void pprPointL( pArea, xLng1, yLng1 ) + double pprSin_deg( angle ) + void pprText( pArea, xDbl, yDbl, text, just, charHt, angle) + just: PPR_TXT_{CEN,RJ,LJ} + void pprWinClose( pWin ) + void pprWinErase( pWin ) + void pprWinInfo( pWin, >pXpos, >pYpos, >pXwid, >pYht ) + int pprWinIsMono( pWin ) + long pprWinLoop( pWin, drawFn, pDrawArg ) + void drawFn(pWin, pDrawArg) + PPR_WIN *pprWinOpen( winType, dispName, winTitle, xPos, yPos, xWid,yHt) + winType: PPR_WIN_{SCREEN,POSTSCRIPT,EPS} + PPR_WIN *pprWinOpenUW( pFrame, pCanvas, NULL, NULL ) + PPR_WIN *pprWinOpenUW( ppDisp, pWindow, pGC, NULL ) + void pprWinReplot( pWin, drawFn, pDrawArg ) + long pprWinSetAttr( pWin, code, pAttr ) + code: PPR_ATTR_{COLORNUM,GC,KEYNUM,LINE_THICK,PATT_ARRAY} + +DESCRIPTION (continued) + Plotting is done within "plot areas" which are defined within + the window. Plot areas can be as large as the window, and they + can overlap, if desired. Clipping service is optional, since + plot areas are often calibrated for the "normal" range of data, + and it is useful to see what's happening if the data are outside + the normal range. + + One non-intuitive aspect of the term "plot area" is that the + usual case ALWAYS plots outside the plot area. Generally, this + happens when annotations and labels are added to a grid. The + plot area itself only specified where the edges of the grid were + to be drawn. Because of this, determining the size and placement + of a plot area must take into account the necessary margins for + text. (pprAreaOpenDflt automatically takes care of margins.) + + Most plotting is done using "data coordinates", which are the + actual data values. In some cases, coordinates or distances are + specified in terms of a fraction of the window height (see, for + example, pprAreaOpen or pprText). + + Also provided in this package are some routines for interacting + with the "window". In many cases, this means that a plotting + program can totally avoid any knowledge of, or dependence on, a + particular windowing environment. This makes easily available + the capability for producing hard copies of plots on a PostScript + printer. + + Many routines in this package require that values be + represented with type 'double'. In some cases, however, other + data types are directly supported, so that the caller doesn't + need to alter the way data are stored. + +BUGS +o Only linear axes are presently supported +o There is presently no mechanism to allow a calling program to + do other work while a plot is on the screen. It would be nice + for a program to be able to periodically check with this package + to see if resize or expose events need to be serviced. +o The SunView version of this package won't run properly with + programs which use the SunOs LightWeight Process library. +o The SunView version of this package doesn't handle color. + +EXAMPLE + The following program plots the first 80 points of a parabola. The + size and position of the plot window can be freely changed; the + window can be covered and exposed, iconified and de-iconified, etc. + When a `click right' is done in the plot window, the window is + closed. A PostScript file named "testPS" is produced; the size of + the plot in this file is about the same as it was when the plot + window was closed. (The plot window can be closed using the `quit' + item on the window's title bar menu (but not under X!!).) + + The program is compiled and linked for SunView with the following + command. (It will be necessary to find where pprPlot.o and + pprPlotDefs.h are located and possibly `fix up' the command.) + + cc plotTest.c pprPlot.o -lsuntool -lsunwindow -lpixrect -lm + + The program is compiled and linked for X with the following + command. (It will be necessary to find where pprPlot.o and + pprPlotDefs.h are located and possibly `fix up' the command.) + + cc plotTest.c pprPlot.o -lX11 -lm + + (As of Feb 6, 1991, plotTest.c is available on the AT-division + open network in ~gta/ar/plotTest.c. It should be copied into + the user's working directory before using it.) + + (As of Feb 6, 1991, pprPlot is available on the AT-division + open network in ~gta. Until access to this software has been + formalized, use the following for Sun3/SunView: + cc plotTest.c -I/home/obiwan/gtacs/h /home/obiwan/gtacs/ar/pprPlotS.o + -lsuntool -lsunwindow -lpixrect -lm + (For Sun3/X, use the following: + cc plotTest.c -I/home/obiwan/gtacs/h /home/obiwan/gtacs/ar/pprPlotX.o + -lX11 -lm + (For Sun4 builds, use "/ar/ar4/" in place of "/ar/" in the above + commands.) + + #include + #include + + #define NPTS 80 +---------------------------------------------------------------------------- + define a structure for holding the data. This is needed because the + replot routine (which does the actual plotting) is only given a pointer + to the information it needs. +---------------------------------------------------------------------------- + typedef struct { + int nPts; + float x[NPTS]; + float y[NPTS]; + double xMin; + double xMax; + double yMin; + double yMax; + } MY_DATA; + + void replot(); + + main() + { + int i; + int x=0,y=0,width=0,height=0; + PPR_WIN *pWin; + MY_DATA myData; + long stat; + +---------------------------------------------------------------------------- + generate the data in the structure. Once it's generated, figure out + the range of values and put the range into the structure using + pprAutoRangeF. This specific routine was chosen because the data + is in "float" arrays. Different forms of data would result in choosing + a different routine. + + The routine pprAutoEnds is available to "round" the endpoints of the + axes to "nicer" values. Depending on the application, it will be + attractive to use this following the pprAutoRange call. +---------------------------------------------------------------------------- + myData.nPts = NPTS; + for (i=0; ixMin, pMyData->yMin, pMyData->xMax, pMyData->yMax, + 5, 5, charHt); + pprPerimLabel(pArea, "x label", NULL, "y label", NULL, 0.); + + pprLineF(pArea, pMyData->x, pMyData->y, pMyData->nPts); + + pprAreaClose(pArea); + + return; + } + + +NAME pprAnnotX - annotate an X axis, perhaps drawing a line and tick marks + +DESCRIPTION + Annotate an X axis, placing annotations at the major tick intervals. + + If desired, an axis line is also drawn, with tick marks at the + major intervals. The tick marks are drawn using the "generic" + line attributes for the plot window. The axis line is drawn with + the line attributes of the plot area; this allows using a dashed + line pattern or color to associate an axis with a data line. + + The annotations and label are drawn using the default character + height for the plot area, in the color, if any, for the plot area. + + An array of text strings can be supplied for annotating the tick + intervals. This is useful if the desired annotations are text. + If an annotation array isn't supplied, then numeric annotations + are generated. + + To allow multiple calibrations for an axis, this routine accepts + an `offset' argument. If this argument is greater than 0, then + the annotation and labeling activity occurs that many lines (in + the default character height for the plot area) below the axis + which was established by pprAreaOpen. + + An alternate entry point, pprAnnotX_wc, is available to use the + plot window's color for the annotation. + +RETURNS + void + +BUGS +o only linear axes are handled +o doesn't presently support offset processing + +NOTES +1. Uses a space below the axis of 5 character heights. + +SEE ALSO + pprAnnotY, pprGrid, pprPerim, pprAreaOpen, pprAreaSetAttr + +SYNOPSIS + +void +pprAnnotX(pArea, offset, xLeft, xRight, xNint, drawLine, xLabel, xAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I offset as number of lines below yBot to annotate */ +double xLeft; /* I x data value at left end of axis */ +double xRight; /* I x data value at right end of axis */ +int xNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *xLabel; /* I label for x axis, or NULL; oriented horizontal */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ + +NAME pprAnnotY - annotate a Y axis, perhaps drawing line and tick marks + +DESCRIPTION + Annotate a Y axis, placing annotations at the major tick intervals. + + If desired, an axis line is also drawn, with tick marks at the + major intervals. The tick marks are drawn using the "generic" + line attributes for the plot window. The axis line is drawn with + the line attributes of the plot area; this allows using a dashed + line pattern or color to associate an axis with a data line. + + The annotations and label are drawn using the default character + height for the plot area, in the color, if any, for the plot area. + + An array of text strings can be supplied for annotating the tick + intervals. This is useful if the desired annotations are text. + If an annotation array isn't supplied, then numeric annotations + are generated. + + To allow multiple calibrations for an axis, this routine accepts + an `offset' argument. If this argument is greater than 0, then + the annotation and labeling activity occurs that many lines (in + the default character height for the plot area) to the left of + the axis which was established by pprAreaOpen. + + An alternate entry point, pprAnnotY_wc, is available to use the + plot window's color for the annotation. + +RETURNS + void + +BUGS +o only linear axes are handled + +NOTES +1. Uses a space to the left of the axis of 12 character heights if + annotations are horizontal, and a space of 5 character heights + if they are vertical. + +SEE ALSO + pprAnnotX, pprGrid, pprPerim, pprAreaOpen, pprAreaSetAttr + +SYNOPSIS + +void +pprAnnotY(pArea, offset, yBot, yTop, yNint, drawLine, yLabel, yAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +int offset; /* I number of lines to left of axis for annotation */ +double yBot; /* I y data value at bottom end of axis */ +double yTop; /* I y data value at top end of axis */ +int yNint; /* I number of major intervals for axis */ +int drawLine; /* I 1 says to draw a line and tick marks */ +char *yLabel; /* I label for y axis, or NULL; oriented vertical */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I orientation angle for annotation text; 0. or 90. */ + +NAME pprAnnotYMark - add plot marks to a Y axis annotation + +DESCRIPTION + Draw two plot marks at the foot of the Y axis annotation, to allow + associating the axis with a particular set of data. + +RETURNS + void + +BUGS +o only linear axes are handled + +SEE ALSO + pprMark, pprAnnotY + +EXAMPLE + +SYNOPSIS + +void +pprAnnotYMark(pArea, offset, markNum) +PPR_AREA *pArea; /* I pointer to plot area structure */ +int offset; /* I number of lines to left of axis for annotation */ +int markNum; /* I mark number */ + +NAME pprArc - draw an arc + +DESCRIPTION + Draw an arc. The arc is specified by a radius and two angles. The + angles, in degrees, specify the angle at which the arc starts and + the angle at which it ends. An angle increment specifies both the + direction of the arc and the size of the chords which approximate + the arc. Angles are measured counter-clockwise from the positive + X axis. + + The radius of the arc is treated as representing data values in the + plot area. If both the X and Y axes of the plot area have the + same data scaling, then circular arcs will be produced (assuming + a square plot area). If the X scaling is not the same as the Y + scaling, then elliptical arcs will be produced. + + The arc is drawn using the color, dashed line, and other attributes + of the plot area. Alternate entry points are: + + pprArcD_ac uses the area color, but ignores other + attributes + +RETURNS + void + +SEE ALSO + +SYNOPSIS + +void +pprArcD(pArea, xDbl, yDbl, radDbl, angle1, angle2, angleIncr) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double xDbl; /* I x data coordinate for center of arc */ +double yDbl; /* I y data coordinate for center of arc */ +double radDbl; +double angle1; /* I angle to start arc */ +double angle2; /* I angle to stop arc */ +double angleIncr; /* I size of steps in drawing arc */ + +NAME pprAreaClose - close a plot area + +DESCRIPTION + Frees the memory associated with a plot area pointer and does other + cleanup operations. This routine should be called prior to calling + pprWinClose. + +RETURNS + void + +SYNOPSIS + +void +pprAreaClose(pArea) +PPR_AREA *pArea; /* I pointer to plot area structure */ + +NAME pprAreaErase - erase an area within a plot window + +DESCRIPTION + Erases an area within a plot window. + +RETURNS + void + +SEE ALSO + pprWinErase, pprGridErase, pprPerimErase + +NOTES +1. Another mode of calling pprAreaErase, in which the arguments are + pixel offsets from the data area boundaries, can be invoked by + having one or more of the arguments be greater than 1. or less + than 0. + +EXAMPLES +1. Erase within a data area, preserving the perimeter line. + + pprAreaErase(pArea, 1., 1., -1., -1.); + +SYNOPSIS + +void +pprAreaErase(pArea, wfracXleft, wfracYbot, wfracXright, wfracYtop) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double wfracXleft; /* I x window fraction of left edge of area */ +double wfracYbot; /* I y window fraction of bottom edge of area */ +double wfracXright; /* I x window fraction of right edge of area */ +double wfracYtop; /* I y window fraction of top edge of area */ + +NAME pprAreaOpen - initialize a plot area + +DESCRIPTION + Initialize a plot area within the plot window. This initialization + must be done before calling any of the routines which do actual + plotting. + + This routine establishes a rectangular "data area" within the plot + window. It is within the data area that data will be plotted. + The size and position of the data area are specified in terms + of fractions of the window size; they are expressed as + "coordinates" of the lower left and upper right corners of the + area. The data area specified in the call to pprAreaOpen does + NOT include space for axis annotations and labels. (pprAreaOpenDflt + can be used to automatically get the necessary margins.) + + This routine must also be told the data values at the two corners + of the data area in order to determine scaling. + + In addition to establishing scaling, this routine accepts information + about how many major divisions there are for each axis and what + default character height is to be used for displaying text within + the plot area (see pprText for more information). If any of these + parameters is specified as zero, this routine chooses an appropriate + value. + + The default line attributes for the plot are copied from those of + the plot window. pprAreaSetAttr can be used to change them. Under + X11, a gc is created for the plot area with the foreground and + background being copied from the gc for the plot window; + pprAreaSetAttr can be used to change the foreground and background. + + When plotting is complete for a plot area, pprAreaClose should + be called. + +RETURNS + pointer to plot area, or + NULL + +BUGS +o only linear calibration is handled + +SEE ALSO + pprWinOpen, pprAreaOpenDflt, pprAreaSetAttr + pprAutoEnds, pprAutoInterval, pprAutoRange + pprText + +EXAMPLE +1. Set up an area which occupies the full width of the window, but + which uses the middle third vertically. The range for x values + is 0 to 100; for y, the range is -10 to 10. Both the x and y + axes are to be divided into 5 intervals. + + Allow space below and to the left of the actual area for plotting + for pprPerim to place labels and annotations. The required + size of "margins" depends on the size of characters used, so a + default size is determined (and put into effect as part of the + pprAreaOpen call). + + PPR_AREA *pArea; + double charHt, charHtX; + + charHt = PprDfltCharHt(.33, .67); + charHtX = pprYFracToXFrac(pWin, charHt); + pArea = pprAreaOpen(pWin, 0.+12.*charHtX, .33+6*charHt, 1., .67, + 0., -10., 100., 10., 5, 5, charHt); + ... + pprAreaClose(pArea); + +SYNOPSIS + +PPR_AREA * +pprAreaOpen(pWin, wfracXleft, wfracYbot, wfracXright, wfracYtop, + xLeft, yBot, xRight, yTop, xNint, yNint, charHt) +PPR_WIN *pWin; /* I pointer to plot window structure */ +double wfracXleft; /* I x window fraction of left edge of data area */ +double wfracYbot; /* I y window fraction of bottom edge of data area */ +double wfracXright; /* I x window fraction of right edge of data area */ +double wfracYtop; /* I y window fraction of top edge of data area */ +double xLeft; /* I x data value at left side of data area */ +double yBot; /* I y data value at bottom side of data area */ +double xRight; /* I x data value at right side of data area */ +double yTop; /* I y data value at top side of data area */ +int xNint; /* I x axis number of intervals; if <=0, a default + value is provided */ +int yNint; /* I y axis number of intervals; if <=0, a default + value is provided */ +double charHt; /* I value to use as default for character size, as + a fraction of window height; if <= 0., + a default value is provided */ + +NAME pprAreaOpenDflt - initialize a plot area using defaults + +DESCRIPTION + Initialize a plot area within the plot window. This initialization + must be done before calling any of the routines which do actual + plotting. + + This routine is a variant on pprAreaOpen. It performs the functions + of that routine, but uses some defaults rather than making the + caller determine specific values. In particular, this routine: + + o sets a default character height + + o determines the number of major divisions for each axis + + o establishes, inside the plot area specified, margins which + will be adequate for annotating and labeling the axes. + + See the description for pprAreaOpen for additional details. + + When plotting is complete for a plot area, pprAreaClose should + be called. + +RETURNS + pointer to plot area, or + NULL + +BUGS +o only linear calibration is handled + +SEE ALSO + pprWinOpen, pprAreaOpen, pprAreaSetAttr + pprAutoEnds, pprAutoInterval, pprAutoRange + pprText + +SYNOPSIS + +PPR_AREA * +pprAreaOpenDflt(pWin, wfracXleft, wfracYbot, wfracXright, wfracYtop, + xLeft, yBot, xRight, yTop) +PPR_WIN *pWin; /* I pointer to plot window structure */ +double wfracXleft; /* I x window fraction of left edge of plot area */ +double wfracYbot; /* I y window fraction of bottom edge of plot area */ +double wfracXright; /* I x window fraction of right edge of plot area */ +double wfracYtop; /* I y window fraction of top edge of plot area */ +double xLeft; /* I x data value at left side of data area */ +double yBot; /* I y data value at bottom side of data area */ +double xRight; /* I x data value at right side of data area */ +double yTop; /* I y data value at top side of data area */ + +NAME pprAreaRescale - change scaling for existing plot area + +DESCRIPTION + Changes the scaling for a plot area using new data values at the + edges of the data area. The actual size and position of the + data area within the plot window aren't changed. + + No re-drawing is done by this routine. Typically, the caller will + erase the appropriate area, draw the grid or perimeter, set the + area for clipping, and, finally, replot the data. + + Default character height for the plot area is altered proportionally + to the rescaling of the plot area. + +RETURNS + void + +SEE ALSO + pprAreaSetAttr, pprAreaErase + +SYNOPSIS + +void +pprAreaRescale(pArea, xLeft, yBot, xRight, yTop) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double xLeft; /* I x data value at left side of data area */ +double yBot; /* I y data value at bottom side of data area */ +double xRight; /* I x data value at right side of data area */ +double yTop; /* I y data value at top side of data area */ + +NAME pprAreaSetAttr - set attributes for a plot area + +DESCRIPTION + Set individual attributes for a plot area. In most cases, the + attributes affect the drawing of lines in the plot area. + + To use this routine, an attribute code and a corresponding value + are supplied. The form of the value argument depends on the code. + + o PPR_ATTR_CLIP sets the plot area so that line segments which lie + outside the data area won't be drawn, but will terminate at their + intersection with the edge of the data area. Clipping can be + disabled by setting the PPR_ATTR_NOCLIP attribute; the default when + a plot area is created is no clipping. + + pprAreaSetAttr(pArea, PPR_ATTR_CLIP, 0, NULL); + pprAreaSetAttr(pArea, PPR_ATTR_NOCLIP, 0, NULL); + + o PPR_ATTR_COLORNUM selects a color for use in drawing lines in a plot + area. For monochrome screens, no action is taken. There are + PPR_NCOLORS colors provided, numbered starting with 1. A colorNum + of 0 selects black. + + int colorNum; + colorNum = 4; + pprAreaSetAttr(pArea, PPR_ATTR_COLORNUM, colorNum, NULL); + + o PPR_ATTR_BG installs a caller-supplied background pixel value in + the gc for the plot area. (For use only with X11. Under X11, + pprAreaOpen initially set the gc for the plot area to have the + same foreground and background colors as the gc for the plot window.) + + pprAreaSetAttr(pArea, PPR_ATTR_BG, 0, &bg); + + o PPR_ATTR_FG installs a caller-supplied foreground pixel value in + the gc for the plot area. (For use only with X11. Under X11, + pprAreaOpen initially set the gc for the plot area to have the + same foreground and background colors as the gc for the plot window.) + + pprAreaSetAttr(pArea, PPR_ATTR_FG, 0, &fg); + + o PPR_ATTR_KEYNUM selects a legend key for identifying lines drawn + in a plot area, and thus distinguishing them from the lines drawn + by a different plot area. This is primarily useful for overlapping + plot areas, where several sets of data are drawn on the same axis. + The key number, which is expected to be in the range 0 to PPR_NKEYS, + inclusive, selects either a dashed line pattern or a color, + depending on the nature of the device on which the plot window + resides. There are PPR_NKEYS unique patterns and colors; a key + number of 0 resets to a "plain", solid line. + + Use of the PPR_ATTR_KEYNUM option provides a way to restart a + dashed line pattern at its beginning. + + int keyNum; + keyNum = 4; + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, keyNum, NULL); + + o PPR_ATTR_LINE_THICK sets the line thickness for the plot area. The + thickness is the integer number of thousandths of plot window height. + The thickness is used for data drawing operations. A thickness of + 0. results in pixel-thick lines. As an example, a thickness of 10 + represents 10/1000 (or .01) of the window height. + + int thick; + thick = 3; .003 of window height + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + + o PPR_ATTR_PATT_ARRAY installs a caller-supplied dashed line pattern + array. This is an array of type short. The first element + contains a number of pixels with `pen down'; the second has a + number of pixels with `pen up'; the third with `pen down'; etc. + Following the last element must be an element with a value of 0 . + (pprAreaSetAttr stores only a pointer to the array, so the caller + must preserve the array until pprAreaClose is called or until + pprAreaSetAttr is called to `de-install' the pattern array.) An + array pointer of NULL resets the plot area back to normal drawing. + + short pattern[]={16,4,2,4,0}; + pprAreaSetAttr(pArea, PPR_ATTR_PATT_ARRAY, 0, pattern); + + + Some pprXxx routines don't use the attributes from the plot + area, but instead use the `generic' attributes from the + plot window structure. The pprLineSegx_wc and pprMovex_wc + routines provide an explicit mechanism for using the plot window + attributes. + +RETURNS + 0, or + -1 if an error is encountered + +BUGS +o color is supported only for X +o when color is used, ALL output for the plot area is colored; it's + not clear yet whether this is a bug or a feature. +o line thickness doesn't operate consistently under SunView + +SEE ALSO + pprAreaOpen + +SYNOPSIS + +long +pprAreaSetAttr(pArea, code, arg, pArg) +PPR_AREA *pArea; /* IO pointer to plotter area */ +PPR_ATTR_CODE code; /* I attribute code: one of PPR_ATTR_xxx */ +int arg; /* I attribute value, or 0 */ +void *pArg; /* I pointer to attribute, or NULL */ + +NAME pprAutoEnds - choose `clean' endpoint valuess for an axis + +DESCRIPTION + For a specific numeric range, this routine calculates a possibly + altered numeric range which will produce more tasteful axis + calibration. + +RETURNS + void + +BUGS +o this routine should probably focus some attention on choice of + number of intervals for an axis; presently, the new endpoints + chosen by this routine may be difficult to use for choosing + interval size +o uses exp10(), which doesn't exist in VxWorks +o only linear calibration is handled + +SEE ALSO + pprAutoInterval, pprAutoRange, pprAreaOpen + +SYNOPSIS + +void +pprAutoEnds(left, right, pLeftNew, pRightNew) +double left; /* I leftmost value */ +double right; /* I rightmost value */ +double *pLeftNew; /* O new leftmost value */ +double *pRightNew; /* O new rightmost value */ + +NAME pprAutoInterval - figure out good interval size for an axis + +DESCRIPTION + Determine a "good" interval size for an axis, so that axis + annotation will be tasteful. + +RETURNS + void + +BUGS +o this routine always chooses to divide an axis into 5 intervals +o only linear calibration is handled + +SEE ALSO + pprAutoEnds, pprAutoRange, pprAreaOpen + +SYNOPSIS + +void +pprAutoInterval(val1, val2, pNint) +double val1; /* I value at one end of axis */ +double val2; /* I value at other end of axis */ +int *pNint; /* O number of intervals */ + +NAME pprAutoRange - find minimum and maximum values for an array + +DESCRIPTION + Finds the minimum and maximum values in an array of values. + + Four different routines are available, depending on the type of + the input array. Each returns the min and max as a double: + + void pprAutoRangeD(doubleArray, nPoints, doubleMin, doubleMax) + void pprAutoRangeF(floatArray, nPoints, doubleMin, doubleMax) + void pprAutoRangeL(longArray, nPoints, doubleMin, doubleMax) + void pprAutoRangeS(shortArray, nPoints, doubleMin, doubleMax) + +RETURNS + void + +SEE ALSO + pprAutoEnds, pprAutoInterval, pprAreaOpen + + +NAME pprCos_deg - get the cosine of an angle in degrees + +DESCRIPTION + Get the cosine of an angle in degrees. A table lookup technique + is used. The angle argument is truncated to the next lower 1/4 + degree prior to lookup. + +RETURNS + cosine + +SYNOPSIS + +double +pprCos_deg(angle) +double angle; /* I angle, in degrees */ + +NAME pprChar - plot a character + +DESCRIPTION + Plots a single text character at a location. The center of the + character is placed at the specified x,y position. + + The character height specification is in terms of a fraction of the + height of the window. This results in automatic scaling of + character sizes as the window size changes. If a height of zero + is specified in the call, then the default height for the plot + area is used. (The default height was established by pprAreaOpen.) + + A macro is available which returns the default character height + used by this plot package. The value returned is proportional to + the height of the plot area. This value can be used to generate + "big" and "small" character sizes. + + PprDfltCharHt(lowYfrac, highYfrac) + + lowYfrac is the vertical fraction of the window at which + the bottom edge of the plot area lies + highYfrac is the vertical fraction of the window at which + the top edge of the plot area lies + +RETURNS + void + +BUGS +o technique used works only with linear axes +o ASCII character codes are assumed +o no checks are made for illegal characters + +SEE ALSO + pprText, pprAreaOpen, pprLine, pprPoint + +SYNOPSIS + +void +pprChar(pArea, x, y, chr, height, angle) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of character */ +double y; /* I y data coordinate of character */ +char chr; /* I character to plot */ +double height; /* I height of character, as a fraction of + the height of the window; a value of + zero results in using a default height */ +double angle; /* I orientation angle of character, ccw degrees */ + +NAME pprCvtDblToTxt - format a double for printing + +DESCRIPTION + Formats a double for printing. This routine is dedicated to + getting as large a range of values as possible into a particular + field width. + + This routine doesn't attempt to handle extremely small values. + It assumes that the field is large enough to handle the smallest + significant value to be encountered. + +RETURNS + void + +BUGS +o extremely small values aren't handled well + +NOTES +1. If the value can't be represented at all in the field, the sign + followed by *'s appears. +2. In extreme cases, only the magnitude of the value will appear, as + En or Enn. For negative values, a - will precede the E. +3. When appropriate, the value is rounded to the nearest integer + for formatting. + +SYNOPSIS + +void +pprCvtDblToTxt(text, width, value, sigDig) +char *text; /* O text representation of value */ +int width; /* I max width of text string (not counting '\0') */ +double value; /* I value to print */ +int sigDig; /* I max # of dec places to print */ + +NAME pprGrid - draw a grid + +DESCRIPTION + Draw a perimeter and grid lines. The number of intervals + specified in the plot area structure is used for placing + the grid lines. + + The color attributes for the plot window are used for drawing; + dashed line and line thickness are ignored. + +RETURNS + void + +BUGS +o only linear axes are handled + +SEE ALSO + pprGridLabel, pprGridErase, pprPerim, pprAnnotX, pprAnnotY, pprAreaOpen + +SYNOPSIS + +void +pprGrid(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ + +NAME pprGridErase - erase within a grid + +DESCRIPTION + Erases the screen inside the grid for the plot area. (Actually, + the entire data area is erased and then the grid is redrawn.) + +RETURNS + void + +SEE ALSO + pprPerimErase, pprAreaErase, pprWinErase + +SYNOPSIS + +void +pprGridErase(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ + +NAME pprGridLabel - draw and label a grid + +DESCRIPTION + Draw a perimeter and grid lines. The number of intervals + specified in the plot area structure is used for placing + the grid lines. + + Axis labels and annotations are drawn using the information from + the plot area, as specified in the pprAreaOpen call. + +RETURNS + void + +BUGS +o only linear axes are handled + +SEE ALSO + pprGrid, pprPerim, pprAnnotX, pprAnnotY, pprAreaOpen + +SYNOPSIS + +void +pprGridLabel(pArea, xLabel, xAnnot, yLabel, yAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +char *xLabel; /* I label for x axis, or NULL */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +char *yLabel; /* I label for y axis, or NULL */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I angle for y annotations; 0. or 90. */ + +NAME pprLine - plot a line using x and y data value vectors + +DESCRIPTION + Draw a line along the path specified by two value arrays. + + Several entry points are available to accomodate various + types of data: + + pprLineF(pArea, x, y, npts) x and y are float[] + pprLineD(pArea, x, y, npts) x and y are double[] + pprLineS(pArea, x, y, npts) x and y are short[] + pprLineL(pArea, x, y, npts) x and y are long[] + +RETURNS + void + +BUGS +o only linear calibration is handled + +SEE ALSO + pprLineSeg, pprMove, pprPoint, pprText + +SYNOPSIS + +void +pprLineF(pArea, x, y, npts) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +float *x; /* I x array of data values */ +float *y; /* I y array of data values */ +int npts; /* I number of points to plot */ + +NAME pprLineSeg - plot a line segment between a pair of points + +DESCRIPTION + Move to the first point and then draw a line to the second, using + the line attributes of the plot area. If the attributes indicate a + dashed line, the current dashed line pattern will be used. + + Two entry points are available: + + pprLineSegD(pArea, x1, y1, x2, y2) x and y are double + pprLineSegL(pArea, x1, y1, x2, y2) x and y are long + + For drawing ignoring the dashed line, line thickness, and other plot + area attributes, but using the area color attribute, two alternate + entry points are available: + + pprLineSegD_ac(pArea, x1, y1, x2, y2) x and y are double + pprLineSegL_ac(pArea, x1, y1, x2, y2) x and y are long + +RETURNS + void + +BUGS +o only linear calibration is handled + +SEE ALSO + pprLine, pprMove, pprPoint, pprText + +SYNOPSIS + +void +pprLineSegD(pArea, x1, y1, x2, y2) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x1; /* I first x point */ +double y1; /* I first y point */ +double x2; /* I second x point */ +double y2; /* I second y point */ + +NAME pprMarkD - draw a plotting mark + +DESCRIPTION + Draw a plotting mark at the specified point. The color attribute + (if any) of the plot area is used in drawing the mark. Line + thickness and dashed line pattern attributes are ignored. + + Two entry points are available: + + pprMarkD(pArea, x, y, markNum) x and y are double + pprMarkL(pArea, x, y, markNum) x and y are long + +RETURNS + void + +BUGS +o only linear calibration is handled + +SEE ALSO + pprLine, pprLineSeg, pprPoint, pprText + +SYNOPSIS + +void +pprMarkD(pArea, x, y, markNum) +PPR_AREA *pArea; /* I pointer to plot area structure */ +double x; /* I x data coordinate */ +double y; /* I y data coordinate */ +int markNum; /* I mark number--0 to PPR_NMARKS-1, inclusive */ + +NAME pprMoveD - move the pen, possibly drawing a line + +DESCRIPTION + Move the "pen" to the specified point. If the "pen" is "down" + a line will be drawn. The line attributes of the plot area are + used in drawing the line. If the attributes indicate a dashed + line, the current dashed line pattern will be used. + + The "clipping" attribute for the plot area is honored only by + pprMoveD; pprMoveD_ac and pprMoveD_wc never clip lines at the + data area edges. + + Two alternate entry points are available for drawing plain lines, + ignoring all attributes except color. One uses the color for the + plot area (pprMoveD_ac); the other uses the plot window color + (pprMoveD_wc). + + pprMoveD_ac(pArea, x, y, pen) x and y are double + pprMoveD_wc(pArea, x, y, pen) x and y are double + +RETURNS + void + +BUGS +o only linear calibration is handled + +SEE ALSO + pprLine, pprLineSeg, pprText + +SYNOPSIS + +void +pprMoveD(pArea, x, y, pen) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of new point */ +double y; /* I y data coordinate of new point */ +int pen; /* I pen indicator--non-zero draws a line */ + +NAME pprPerim - draw a perimeter + +DESCRIPTION + Draw a perimeter with tick marks. The number of intervals + specified in the plot area structure is used for placing + the tick marks. + + The color attributes for the plot window are used for drawing; + dashed line and line thickness are ignored. + +RETURNS + void + +BUGS +o only linear axes are handled + +SEE ALSO + pprPerimLabel, pprPerimErase, pprGrid, pprAnnotX, pprAnnotY, pprAreaOpen + +SYNOPSIS + +void +pprPerim(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ + +NAME pprPerimErase - erase within a perimeter + +DESCRIPTION + Erases the screen inside the perimeter for the plot area. (Actually, + the perimeter and tick marks are erased as well, since plot marks + may have been drawn on (and thus lie partly outside of) the perimeter + itself. The perimeter and tick marks are then redrawn.) + +RETURNS + void + +SEE ALSO + pprGridErase, pprAreaErase, pprWinErase + +SYNOPSIS + +void +pprPerimErase(pArea) +PPR_AREA *pArea; /* IO pointer to plotter area */ + +NAME pprPerimLabel - draw and label a perimeter + +DESCRIPTION + Draw a perimeter with tick marks. The number of intervals + specified in the plot area structure is used for placing + the tick marks. + + Axis labels and annotations are drawn using the information from + the plot area, as specified in the pprAreaOpen call. + +RETURNS + void + +BUGS +o only linear axes are handled + +SEE ALSO + pprPerim, pprGrid, pprAnnotX, pprAnnotY, pprAreaOpen + +SYNOPSIS + +void +pprPerimLabel(pArea, xLabel, xAnnot, yLabel, yAnnot, angle) +PPR_AREA *pArea; /* IO pointer to plotter area */ +char *xLabel; /* I label for x axis, or NULL */ +char **xAnnot; /* I pointer to array of x annotations, or NULL */ +char *yLabel; /* I label for y axis, or NULL */ +char **yAnnot; /* I pointer to array of y annotations, or NULL */ +double angle; /* I angle for y annotations; 0. or 90. */ + +NAME pprPoint - plot a point at a coordinate + +DESCRIPTION + Plot a pixel sized point using the line thickness and color + attributes of the plot area. + + Two entry points are available: + + pprPointD(pArea, x, y) x and y are double + pprPointL(pArea, x, y) x and y are long + +RETURNS + void + +BUGS +o only linear calibration is handled + +SEE ALSO + pprMark, pprAreaOpen, pprAreaSetAttr, pprLine, pprMove, pprText + +SYNOPSIS + +void +pprPointD(pArea, x, y) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate */ +double y; /* I y data coordinate */ + +NAME pprSin_deg - get the sine of an angle in degrees + +DESCRIPTION + Get the sine of an angle in degrees. A table lookup technique + is used. The angle argument is truncated to the next lower 1/4 + degree prior to lookup. + +RETURNS + sine + +SYNOPSIS + +double +pprSin_deg(angle) +double angle; /* I angle, in degrees */ + +NAME pprText - plot a text string + +DESCRIPTION + Plots a text string at a location. The character "half height" is + placed at the specified x,y position; the 'justification' option + selects whether the left end, center, or right end of the text is + placed at the x,y coordinate. + + The character height specification is in terms of a fraction of the + height of the window. This results in automatic scaling of + character sizes as the window size changes. If a height of zero + is specified in the call, then the default height for the plot + area is used. (The default height was established by pprAreaOpen.) + + A macro is available which returns the default character height + used by this plot package. The value returned is proportional to + the height of the plot area. This value can be used to generate + "big" and "small" character sizes with simple multiplication of + the default height by a "scale factor". + + PprDfltCharHt(lowYfrac, highYfrac) + + lowYfrac is the vertical fraction of the window at which + the bottom edge of the plot area lies + highYfrac is the vertical fraction of the window at which + the top edge of the plot area lies + + It is also often useful to know what horizontal fraction of the + window width corresponds to a vertical fraction of the height. The + following routine returns the horizontal fraction: + + pprYFracToXFrac(pWin, yFrac) + + An alternate entry point, pprText_wc, is available to use the + plot window's color for drawing the text. + +RETURNS + void + +BUGS +o technique used works only with linear axes +o ASCII character codes are assumed +o no checks are made for illegal characters +o positioning and sizing are somewhat different for the various + window types + +SEE ALSO + pprChar, pprAreaOpen, pprLine, pprPoint, pprCvtDblToTxt + +SYNOPSIS + +void +pprText(pArea, x, y, text, just, height, angle) +PPR_AREA *pArea; /* IO pointer to plot area structure */ +double x; /* I x data coordinate of text */ +double y; /* I y data coordinate of text */ +char *text; /* I text to plot */ +PPR_TXT_JUST just; /* I text justification selector: one of + PPR_TXT_CEN, PPR_TXT_RJ, or PPR_TXT_LJ */ +double height; /* I height of text characters, as a fraction of + the height of the window; a value of + zero results in using a default height */ +double angle; /* I orientation angle of text string, ccw degrees */ + +NAME pprWinClose - close a plot window + +DESCRIPTION + Free the memory associated with a plot window structure and do other + cleanup activities. + + This routine should be called when plotting is complete for a plot + window. Any plot areas not previously closed are automatically + closed by this routine. + + No further references to the plot window may be made. + +RETURNS + void + +SEE ALSO + pprAreaClose, pprWinInfo, pprWinOpen + +SYNOPSIS + +void +pprWinClose(pWin) +PPR_WIN *pWin; /* IO pointer to plot window structure */ + +NAME pprWinErase - erase a plot window + +DESCRIPTION + Erase the contents of the entire plot window. + +RETURNS + void + +SEE ALSO + pprGridErase, pprPerimErase, pprAreaErase + +SYNOPSIS + +void +pprWinErase(pWin) +PPR_WIN *pWin; /* IO pointer to plot window structure */ + +NAME pprWinInfo - get some information about the plot window + +DESCRIPTION + Get the size of the plot window and its position on the screen. + +RETURNS + void + +NOTES +1. The information returned is window system dependent. To avoid + portability problems, this information should be used only in + calls to pprWinXxx routines. + +SYNOPSIS + +void +pprWinInfo(pWin, pXpos, pYpos, pXwid, pYht) +PPR_WIN *pWin; /* I pointer to plot window structure */ +int *pXpos; /* O pointer to place to store window x coord., in pixels */ +int *pYpos; /* O pointer to place to store window y coord., in pixels */ +int *pXwid; /* O pointer to place to store window width, in pixels */ +int *pYht; /* O pointer to place to store window height, in pixels */ + +NAME pprWinIsMono - test to see if plot window is monochrome + +DESCRIPTION + +RETURNS + 1 if plot window is monochrome or gray scale + 0 if plot window is color + +SYNOPSIS + +int +pprWinIsMono(pWin) +PPR_WIN *pWin; /* I pointer to plot window structure */ + +NAME pprWinLoop - loop until "quit" event received in window + +DESCRIPTION + Handles the interactions with the windowing system. The specific + actions depend on the plot window type: + + PPR_WIN_SCREEN + o creates a window on the screen + o when the window actually appears, calls the caller's draw function + o for all subsequent resize and expose events, calls the caller's + draw function + o when the right mouse button is clicked, closes the window and + returns to the caller. The current position and size of the + window are stored (and can be retrieved with pprWinInfo). + + PPR_WIN_POSTSCRIPT + o calls the caller's draw function + + PPR_WIN_EPS + o calls the caller's draw function + + The idea when using pprWinLoop is that a program + will do some preliminary setup for the data to be plotted. + Then the program must turn control over to the plot "window + manager" using pprWinLoop, which will call the caller's + actual plot routine. + + When pprWinLoop exits back to the calling program, that program + can call pprWinInfo in order to "remember" the plot window + size and position. + +RETURNS + 0, or + -1 if an error is encountered + +BUGS +o doesn't furnish information to the draw function which would allow + a partial redraw +o terminology is confusing and inconsistent: "draw" function, redraw, + replot, repaint, etc. + +SEE ALSO + pprWinOpen, pprWinInfo + +NOTES +1. Even though there aren't any "events" associated with plotting on + a PostScript printer, this routine must be called even when using + PPR_WIN_POSTSCRIPT and PPR_WIN_EPS, since this routine invokes the + caller's "draw" function. + +EXAMPLE + See pprWinOpen for an example of a replot function. +j + +SYNOPSIS + +long +pprWinLoop(pWin, drawFun, pDrawArg) +PPR_WIN *pWin; /* IO pointer to plot window structure */ +void (*drawFun)();/* I pointer to function to draw the plot */ +void *pDrawArg;/* I pointer to pass to drawFun */ + +NAME pprWinOpen - initialize a plotting "window" structure + +DESCRIPTION + Initialize a plot window structure. This is the structure which + keeps track of the information needed to interact with the + device on which the "window" resides. The possible types of + windows are: + + o PPR_WIN_SCREEN selects a window on a visual display. This + will be a SunView or X window, depending on the version of + the plotting used in linking the program. The pprWinOpen + call actually creates a window, with the root window as the + parent. The window thus created can be moved, resized, etc. + according to the capabilities of the window manager of the + windowing system being used. (To initialize a plotting + window structure in a window which already exists, use + pprWinOpenUW.) + + for SunView, + `winDispName' is ignored and should be NULL + `winTitle' appears on the window's title bar and icon + `xPos' and `yPos' are pixel offsets from the upper left + corner of the root window. If values of 0 are + supplied, then the window will be positioned at + 100,100. + `xWid' and `yHt' are the width and height of the window, + in pixels. If values of 0 are supplied, then + a size of 512,512 will be used. + + for X, + `winDispName' specifies the X display name on which the + plot window is to appear. If NULL, then the + DISPLAY environment variable will be used. + `winTitle' appears on the window's title bar and icon + `xPos' and `yPos' are pixel offsets from the upper left + corner of the root window. If values of 0 are + supplied, then the window will be positioned at + 100,100. + `xWid' and `yHt' are the width and height of the window, + in pixels. If values of 0 are supplied, then + a size of 512,512 will be used. + + o PPR_WIN_POSTSCRIPT selects a "window" on a PostScript + printer. + + `winDispName' is the name of a file to receive PostScript + output. This is the file which will eventually + be sent to a PostScript printer to get a hard + copy of the plot. If the file exists when + pprWinOpen is called, its contents are erased + before writing begins. + `winTitle' is ignored and should be NULL + `xPos' and `yPos' are ignored + `xWid' and `yHt' are the width and height of the window, + in pixels. If values of 0 are supplied, then + a size of 512,512 will be used. If the size is + larger than the page, then the plot will be scaled + to fit. If necessary, the plot will be printed in + landscape mode, rather than the default of portrait + mode. + + o PPR_WIN_EPS selects a "window" in an Encapsulated PostScript + file. EPS files are intended to be included into documents + prepared by word processing programs such as Interleaf and + TeX. EPS files will not print directly on a PostScript printer. + The description of the arguments for PPR_WIN_POSTSCRIPT applies, + except that scaling and rotation aren't done. + + The pprXxx routines can be used for several different styles of + usage: + + o a complete set of data is available prior to calling any of + the plotting routines. In this case, grids can be drawn and + data can be plotted at the same time. + + For this style of usage, the typical usage will be: + pprWinOpen to "open" a plot window + pprWinLoop to map the plot window to the screen + and call the caller's replot function + when the window is exposed, resized, + etc. + pprWinClose to "close" a plot window + + o no data (or only part of the data) is available prior to calling + any of the plotting routines. In this case, at least some of + the data must be plotted at a later time than when the grids + are drawn. The pprXxx routines don't automatically support + this style of usage fully, but they do provide some tools which + make it relatively easy to implement. See pprWinOpenUW for + more details. + + Under all circumstances, the calling program is expected to call + pprWinClose when plotting is complete for the plot window. + +RETURNS + pointer to window structure, or + NULL if an error is encountered + +SEE ALSO + pprWinOpenUW, pprWinClose, pprWinInfo, pprWinLoop + +EXAMPLES +1. Plot an existing set of data, where the data is stored in `dataStruct'. + + PPR_WIN *pWindow; + . + pWindow = pprWinOpen(PPR_WIN_SCREEN, NULL, "test", 0, 0, 0, 0); + if (pWindow == NULL) + abort(); + if (pprWinLoop(pWindow, replot, &dataStruct) != 0) + abort(); + pprWinClose(pWindow); + +SYNOPSIS + +PPR_WIN * +pprWinOpen(winType, winDispName, winTitle, xPos, yPos, xWid, yHt) +PPR_WIN_TY winType; /* I type of plot window: PPR_WIN_xxx */ +char *winDispName; /* I name of "display" or file for window */ +char *winTitle; /* I title for window title bar and icon */ +int xPos; /* I x position for window; 0 for default */ +int yPos; /* I y position for window; 0 for default */ +int xWid; /* I width of window; 0 for default */ +int yHt; /* I height of window; 0 for default */ + +NAME pprWinOpenUW - open a plot "window" to an existing User Window + +DESCRIPTION + Initialize a plot window structure. This is the structure which + keeps track of the information needed to interact with the + device on which the "window" resides. + + This routine is for use when the caller already has a window in + the windowing system. It is for use exclusively for PPR_WIN_SCREEN + plot window type. The form of the call to this routine is heavily + dependent on the windowing system being used. This routine provides + the basis for obtaining `asynchronous' plotting. (It can also be + used for `batched' plotting, in which all data is available prior + to the first plotting call.) + + Under all circumstances, the calling program is expected to call + pprWinClose when plotting is complete for the plot window. + +RETURNS + pointer to window structure, or + NULL if an error is encountered + +SEE ALSO + pprWinOpenUW, pprWinClose, pprWinInfo, pprWinLoop + +EXAMPLES +1. for X + + PPR_WIN *pWin; + Display *pDisp; + Window plotWindow; + GC plotGC; + + pWin = pprWinOpenUW(&pDisp, &plotWindow, &plotGC, NULL); + ... + pprWinReplot(pWin, drawFn, drawArg); + ... + pprWinClose(pWin); + + +2. for SunView + + PPR_WIN *pWin; + Frame plotFrame; + Canvas plotCanvas; + + pWin = pprWinOpenUW(&plotFrame, &plotCanvas, NULL, NULL); + ... + pprWinReplot(pWin, drawFn, drawArg); + ... + pprWinClose(pWin); + +3. for XView + + PPR_WIN *pWin; + Display *pDisp; + Window plotWindow; + GC plotGC; + + pDisp = (Display *)xv_get(frame, XV_DISPLAY); + window = (Window)xv_get(canvas_paint_window(canvas), XV_XID); + plotGC = DefaultGC(pDisp, DefaultScreen(pDisp)); + + pWin = pprWinOpenUW(&pDisp, &plotWindow, &plotGC, NULL); + ... + pprWinReplot(pWin, drawFn, drawArg); + ... + pprWinClose(pWin); + +SYNOPSIS + +PPR_WIN * +pprWinOpenUW(pArg1, pArg2, pArg3, pArg4) +void *pArg1; +void *pArg2; +void *pArg3; +void *pArg4; + +NAME pprWinReplot - redraw a plot in a user owned window + +DESCRIPTION + Calls the "replot" function to repaint the plot window. This + routine is intended to be used with "user owned" plot windows + which have been opened with pprWinOpenUW. + + Prior to calling the replot function, this routine determines + the size of the actual window and rescales the existing plot + areas. + +RETURNS + void + +SEE ALSO + pprWinOpenUW + +SYNOPSIS + +void +pprWinReplot(pWin, pFunc, pArg) +PPR_WIN *pWin; +void (*pFunc)(); +void *pArg; + +NAME pprYFracToXFrac - convert a Y fraction to an X fraction + +DESCRIPTION + Converts a value which is a fraction of window height into a value + which is a fraction of window width, so that the two values will + represent the same physical size, in pixels. + + This routine is useful for laying out a plot area, especially when + a grid with annotations and labels is to be used. The choice of + "data area" size (i.e., the size of the grid) depends on the size + of the characters which will be used for the annotations and + labels. + +RETURNS + void + +SEE ALSO + pprText + +EXAMPLE + A plot area is use the full width and the vertical 1/3 of the window. + x values range from 0 to 100, while y is from -10 to 10. The x + axis is to be divided into 10 divisions, the y into 4. Use + the default character size for annotations and labels. Also, + use the default window position and size. + + PPR_WIN *pWin; + PPR_AREA *pArea; + float charHt; height as a fraction of window height + float charWid; width as a fraction of window width + + charHt = PprDfltCharHt(.33, .67); + charWid = pprYFracToXFrac(pWin, charHt); + pArea = pprAreaOpen(pWin, 12.*charWid, .33+6.*charHt, 1., .67, + 0., -10., 100., 10, 4, charHt); + +SYNOPSIS + +double +pprYFracToXFrac(pWin, yFrac) +PPR_WIN *pWin; /* I pointer to plot window structure */ +double yFrac; /* I fraction of window height */ diff --git a/src/util/sydPlot.c b/src/util/sydPlot.c new file mode 100644 index 000000000..40f43d219 --- /dev/null +++ b/src/util/sydPlot.c @@ -0,0 +1,2466 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 12-04-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 12-04-90 rac initial version + * .02 mm-dd-yy rac version 2.0, installed in SCCS + * + * make options + * -DXWINDOWS makes a version for X11 + * -DNDEBUG don't compile assert() checking + * -DDEBUG compile various debug code, including checks on + * malloc'd memory + */ +/*+/mod*********************************************************************** +* TITLE sydPlot.c - plotting for synchronous data +* +* DESCRIPTION +* +* QUICK REFERENCE +* +* void sydPlotAxisAutoRange(pSlave) +* long sydPlotAxisSetAttr(pSlave, attr, value, pArg) +* attr = SYD_PLATTR_{GC} +* SYD_PL_SLAVE *sydPlotChanAdd(pMstr, pSChan) +* long sydPlotDone(pMstr, quitFlag) +* long sydPlotEraseSamples(pMstr) +* long sydPlotInit(pMstr, pSspec, winType, dispName, winTitle, +* fullInit) +* long sydPlotInitUW(pMstr, pSspec, pDisp, window, gc) +* long sydPlotSamples(pMstr, begin, end, incrFlag) +* long sydPlotSetAttr(pMstr, attr, value, pArg) +* attr = SYD_PLATTR_{FG1,FG2,LINE,MARK,MONO,POINT,SHOW,UNDER,WRAP} +* long sydPlotSetTitles(pMstr, top, left, bottom, right) +* long sydPlotWinLoop(pMstr) +* long sydPlotWinReplot(pMstr) +* BUGS +* o sydPlotInitUW doesn't support SUNVIEW +* +*-***************************************************************************/ +#include +#include +#include +#ifdef XWINDOWS +# include +# include +#endif +#define SYD_PLOT_PRIVATE +#include + +#ifdef vxWorks +/*---------------------------------------------------------------------------- +* includes and defines for VxWorks compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +#else +/*---------------------------------------------------------------------------- +* includes and defines for Sun compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +# include +# include +#endif + +/*+/subr********************************************************************** +* NAME sydPlotAxisAutoRange - set axis ends to min and max data values +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlotAxisAutoRange(pSlave) +SYD_PL_SLAVE *pSlave; +{ + pSlave->originVal = pSlave->pSChan->minDataVal; + pSlave->extentVal = pSlave->pSChan->maxDataVal; +} + +/*+/subr********************************************************************** +* NAME sydPlotAxisSetAttr - set plot axis attributes +* +* DESCRIPTION +* Setting an attribute doesn't automatically reset other related +* attributes. +* +* sydPlotAxisSetAttr(pSlave, SYD_PLATTR_XCHAN, {0,1}, NULL) +* sydPlotAxisSetAttr(pSlave, SYD_PLATTR_BG, 0, pBgPixelValue) +* sydPlotAxisSetAttr(pSlave, SYD_PLATTR_FG, 0, pFgPixelValue) +* +* RETURNS +* S_syd_OK +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotAxisSetAttr(pSlave, attr, value, pArg) +SYD_PL_SLAVE *pSlave; /* I pointer to plot slave structure */ +SYD_PLATTR attr; /* I attribute selector--one of SYD_PLATTR_xxx */ +int value; /* I value for attribute */ +void *pArg; /* I pointer for value for attribute */ +{ + if (attr == SYD_PLATTR_XCHAN) pSlave->xChan = value; +#ifdef XWINDOWS + else if (attr == SYD_PLATTR_BG) + pSlave->bg = *(unsigned long *)pArg; + else if (attr == SYD_PLATTR_FG) + pSlave->fg = *(unsigned long *)pArg; +#endif + else assertAlways(0); + + return S_syd_OK; +} + +/*+/internal****************************************************************** +* NAME sydPlotAxisSetup - set up axis information for a channel +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlotAxisSetup(pSlave) +SYD_PL_SLAVE *pSlave; +{ + SYD_CHAN *pSChan=pSlave->pSChan; + double originVal, extentVal; + int nInt=5; + + if (pSChan->dbrType == DBR_TIME_FLOAT) { + originVal = pSChan->grBuf.gfltval.lower_disp_limit; + extentVal = pSChan->grBuf.gfltval.upper_disp_limit; + } + else if (pSChan->dbrType == DBR_TIME_SHORT) { + originVal = pSChan->grBuf.gshrtval.lower_disp_limit; + extentVal = pSChan->grBuf.gshrtval.upper_disp_limit; + } + else if (pSChan->dbrType == DBR_TIME_DOUBLE) { + originVal = pSChan->grBuf.gdblval.lower_disp_limit; + extentVal = pSChan->grBuf.gdblval.upper_disp_limit; + } + else if (pSChan->dbrType == DBR_TIME_LONG) { + originVal = pSChan->grBuf.glngval.lower_disp_limit; + extentVal = pSChan->grBuf.glngval.upper_disp_limit; + } + else if (pSChan->dbrType == DBR_TIME_CHAR) { + originVal = pSChan->grBuf.gchrval.lower_disp_limit; + extentVal = pSChan->grBuf.gchrval.upper_disp_limit; + } + else if (pSChan->dbrType == DBR_TIME_ENUM) { + nInt = pSChan->grBuf.genmval.no_str-1; + originVal = 0; + extentVal = nInt; + if (originVal >= extentVal) { + if (nInt < 0) + (void)strcpy(pSChan->grBuf.genmval.strs[0], " "); + extentVal = nInt = 1; + pSChan->grBuf.genmval.no_str = 2; + (void)strcpy(pSChan->grBuf.genmval.strs[1], " "); + } + } + if (originVal == extentVal) { + originVal = pSlave->pSChan->minDataVal; + extentVal = pSlave->pSChan->maxDataVal; + } + if (originVal == extentVal) { + if (originVal == 0.) + extentVal = 10.; + else if (originVal < 0.) + extentVal = 0; + else + originVal = 0.; + } + pSlave->originVal = originVal; + pSlave->extentVal = extentVal; + pSlave->nInt = nInt; +} + +/*+/subr********************************************************************** +* NAME sydPlotChanAdd - add a slave +* +* DESCRIPTION +* Adds a slave to a master plot structure. Some of the items needed +* for actual plotting are set up by this routine: +* +* o endpoints for plotting. If HOPR and LOPR are present for the +* channel, they are used as the plotting endpoints. If they +* aren't present (or if they are equal), then some relatively +* arbitrary endpoints are picked. For DBF_ENUM channels, the +* endpoints are determined by the states. +* o number of major tick intervals. For DBF_ENUM channels, the +* number of states determines the number of intervals. +* o a default plot mark to be used for mark plotting +* o a default line key to be used for monochrome plotting. This +* line key also establishes a default color to be used for +* color plotting; the default color can be overridden using +* sydPlotAxisSetAttr. +* +* RETURNS +* SYD_PL_SLAVE *, or +* NULL +* +* BUGS +* o the scheme for establishing mark and key numbers won't work if +* deleting and re-adding plot channels is allowed +* +*-*/ +SYD_PL_SLAVE * +sydPlotChanAdd(pMstr, pSChan) +SYD_PL_MSTR *pMstr; /* IO pointer to plot master */ +SYD_CHAN *pSChan; /* I pointer to synchronous data channel structure */ +{ + SYD_PL_SLAVE *pSlave; /* pointer to slave structure */ + int i; + + assert(pMstr != NULL); + + if (dbr_type_is_STRING(pSChan->dbrType)) { + (void)printf("sydPlotChanAdd: can't plot DBF_STRING values\n"); + return NULL; + } + if ((pSlave = (SYD_PL_SLAVE *)GenMalloc(sizeof(SYD_PL_SLAVE))) == NULL) { + (void)printf("sydPlotChanAdd: can't get memory\n"); + return NULL; + } + + pSlave->pSChan = pSChan; + pMstr->nSlaves++; + pSlave->markNum = pMstr->nSlaves - 1; + pSlave->lineKey = pMstr->nSlaves; + pSlave->timeLabel[0] = '\0'; + pSlave->xChan = 0; + pSlave->pArea = NULL; + pSlave->fg = 0; + pSlave->bg = 0; + pSlave->first = 1; + pSlave->xFracLeft = 0.; + pSlave->yFracBot = 0.; + pSlave->xFracRight = 0.; + pSlave->yFracTop = 0.; + + DoubleListAppend(pSlave, pMstr->pHead, pMstr->pTail); + sydPlotAxisSetup(pSlave); + if (dbr_type_is_ENUM(pSChan->dbrType)) { + for (i=0; i<=pSlave->extentVal; i++) + pSlave->pAnnot[i] = pSChan->grBuf.genmval.strs[i]; + pSlave->ppAnnot = pSlave->pAnnot; + } + else + pSlave->ppAnnot = NULL; + (void)sprintf(pSlave->timeLabel, "sec past %s", pMstr->refText); + + return pSlave; +} + +/*+/subr********************************************************************** +* NAME sydPlotDone - multiple x vs y rundown +* +* DESCRIPTION +* +* RETURNS +* S_syd_OK +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotDone(pMstr, quitFlag) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +int quitFlag; /* I use 0 for replot, 1 for total rundown */ +{ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + SYD_PL_SLAVE *pSlave1; /* pointer to individual slave struct */ + + assert(pMstr != NULL); + + if (quitFlag) { + pSlave = pMstr->pHead; + while (pSlave != NULL) { + pMstr->nSlaves--; + DoubleListRemove(pSlave, pMstr->pHead, pMstr->pTail); + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pSlave1 = pSlave; + pSlave = pSlave->pNext; + GenFree((char *)pSlave1); + } + pprWinInfo(pMstr->pWin, &pMstr->x, &pMstr->y, + &pMstr->width, &pMstr->height); + pprWinClose(pMstr->pWin); + } + + return S_syd_OK; +} + +/*+/subr********************************************************************** +* NAME sydPlotEraseSamples - erase samples from the screen +* +* DESCRIPTION +* +* RETURNS +* S_syd_OK +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotEraseSamples(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + + assert(pMstr != NULL); + + pSlave = pMstr->pHead; + while (pSlave != NULL) { + pSlave->first = 1; + if (pSlave->pArea != NULL) { + pprAreaErase(pSlave->pArea, 1., 1., -1., -1.); + } + pSlave = pSlave->pNext; + } + + return S_syd_OK; +} + +static void sydPlotInit_common(); +/*+/subr********************************************************************** +* NAME sydPlotInit - plotting initialization +* +* DESCRIPTION +* use sydPlotWinLoop for actual plotting +* `fullInit' results in initializing for a default window size and +* position. If fullInit is zero, then the size and position +* in the plot master are used. +* +* RETURNS +* S_syd_OK +* +* BUGS +* o need an sdrXxx call to initialize a master with caller-supplied +* or default size and position +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotInit(pMstr, pSspec, winType, dispName, winTitle, fullInit) +SYD_PL_MSTR *pMstr; /* IO pointer to plot master structure */ +SYD_SPEC *pSspec; /* I pointer to synchronous set spec */ +PPR_WIN_TY winType; /* I type of "plot window": PPR_WIN_xxx */ +char *dispName; /* I name of "plot window"--display, PostScript + file, EPS file, or NULL */ +char *winTitle; /* I title for window title bar and icon */ +int fullInit; /* I 0 or 1 to do partial or full initialization */ +{ + if (fullInit) { + pMstr->pWin = pprWinOpen(winType, dispName, winTitle, 0,0,0,0); + assertAlways(pMstr->pWin != NULL); + pprWinInfo(pMstr->pWin, &pMstr->x, &pMstr->y, &pMstr->width, + &pMstr->height); + } + else { + pMstr->pWin = pprWinOpen(winType, dispName, winTitle, + pMstr->x, pMstr->y, pMstr->width, pMstr->height); + assertAlways(pMstr->pWin != NULL); + } + + pMstr->winType = winType; + pMstr->plotAxis = SYD_PLAX_UNDEF; + pMstr->pSspec = pSspec; + sydPlotInit_common(pMstr); + return S_syd_OK; +} +static void +sydPlotInit_common(pMstr) +SYD_PL_MSTR *pMstr; +{ + pMstr->linePlot = 1; + pMstr->pointPlot = 0; + pMstr->markPlot = 0; + pMstr->showStat = 0; + pMstr->fillUnder = 0; + if (pprWinIsMono(pMstr->pWin)) + pMstr->noColor = 1; + else + pMstr->noColor = 0; +#ifdef XWINDOWS + pMstr->pDisp = NULL; + pMstr->window = NULL; + pMstr->bg = 0; + pMstr->fg = 0; + pMstr->altPixel1 = 0; + pMstr->altPixel2 = 0; +#endif + pMstr->label[0] = '\0'; + pMstr->title[0] = '\0'; + pMstr->lTitle[0] = '\0'; + pMstr->bTitle[0] = '\0'; + pMstr->rTitle[0] = '\0'; + pMstr->refText[0] = '\0'; + pMstr->pHead = NULL; + pMstr->pTail = NULL; + pMstr->nSlaves = 0; + pMstr->originVal = 0.; + pMstr->extentVal = 0.; + pMstr->wrapX = 0; + if (pMstr->pSspec->sampleCount >= 1) { + pMstr->extentVal = + pMstr->pSspec->pDeltaSec[pMstr->pSspec->sampleCount-1]; + } +} + +#ifdef XWINDOWS +/*+/subr********************************************************************** +* NAME sydPlotInitUW - plotting initialization, using User Window +* +* DESCRIPTION +* use sydPlotWinReplot for actual plotting +* sydPlotSamples can be used for incremental plotting +* +* RETURNS +* S_syd_OK +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotInitUW(pMstr, pSspec, pDisp, window, gc) +SYD_PL_MSTR *pMstr; /* IO pointer to plot master structure */ +SYD_SPEC *pSspec; /* I pointer to synchronous set spec */ +Display *pDisp; /* I X11 display pointer */ +Window window; /* I X11 window handle */ +GC gc; /* I X11 gc handle */ +{ + long stat; + + pMstr->pWin = pprWinOpenUW(&pDisp, &window, &gc, NULL); + assertAlways(pMstr->pWin != NULL); + + pMstr->winType = PPR_WIN_SCREEN; + pMstr->plotAxis = SYD_PLAX_UNDEF; + pMstr->pSspec = pSspec; + sydPlotInit_common(pMstr); + pMstr->pDisp = pDisp; + pMstr->window = window; + + return S_syd_OK; +} +#endif XWINDOWS + +/*+/subr********************************************************************** +* NAME sydPlotSamples - plot one or more sync samples +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* NOTES +* 1. The `incrFlag' argument allows plotting in either batch or +* incremental mode. If incrFlag is 1, then this set of samples will +* be treated as a continuation of a prior set of samples. This is +* important primarily for line plots. Both sydPlotChanAdd and +* sydPlotEraseSamples set the flag (for one or all slaves, respectively) +* indicating there was no prior set of samples; this might be used +* to avoid having to change incrFlag back and forth. +* +*-*/ +void +sydPlotSamples(pMstr, begin, end, incrFlag) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +int begin; /* I number of begin sample to plot */ +int end; /* I number of end sample to plot */ +int incrFlag; /* I 0,1 for batch,incremental plotting */ +{ + if (pMstr->plotAxis == SYD_PLAX_TY) + sydPlot_TYSamples(pMstr, begin, end, incrFlag); + else if (pMstr->plotAxis == SYD_PLAX_TYY) + sydPlot_TYSamples(pMstr, begin, end, incrFlag); + else if (pMstr->plotAxis == SYD_PLAX_XY) + sydPlot_XYSamples(pMstr, begin, end, incrFlag); + else if (pMstr->plotAxis == SYD_PLAX_XYY) + sydPlot_XYSamples(pMstr, begin, end, incrFlag); + else if (pMstr->plotAxis == SYD_PLAX_Y) + sydPlot_YSamples(pMstr, begin, end, incrFlag); + else if (pMstr->plotAxis == SYD_PLAX_YY) + sydPlot_YSamples(pMstr, begin, end, incrFlag); + else if (pMstr->plotAxis == SYD_PLAX_SMITH_IMP || + pMstr->plotAxis == SYD_PLAX_SMITH_ADM || + pMstr->plotAxis == SYD_PLAX_SMITH_IMM) + sydPlot_SmithSamples(pMstr, begin, end, incrFlag); + else + assertAlways(0); +} + +/*+/subr********************************************************************** +* NAME sydPlotSetAttr - set plot attributes +* +* DESCRIPTION +* Setting an attribute doesn't automatically reset other related +* attributes. +* +* sydPlotSetAttr(pMstr, SYD_PLATTR_FG1, 0, &fgPixVal) +* sydPlotSetAttr(pMstr, SYD_PLATTR_FG2, 0, &fgPixVal) +* sydPlotSetAttr(pMstr, SYD_PLATTR_LINE, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_MARK, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_MONO, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_POINT, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_SHOW, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_UNDER, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_WRAP, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_XLAB, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_XANN, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_YLAB, {0,1}, NULL) +* sydPlotSetAttr(pMstr, SYD_PLATTR_YANN, {0,1}, NULL) +* +* RETURNS +* S_syd_OK +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotSetAttr(pMstr, attr, value, pArg) +SYD_PL_MSTR *pMstr; /* I pointer to plot master structure */ +SYD_PLATTR attr; /* I attribute selector--one of SYD_PLATTR_xxx */ +int value; /* I value for attribute */ +void *pArg; /* I pointer for value for attribute */ +{ +#ifdef XWINDOWS + if (attr == SYD_PLATTR_FG1) pMstr->altPixel1 = *(unsigned long *)pArg; + else if (attr == SYD_PLATTR_FG2) pMstr->altPixel2 = *(unsigned long *)pArg; + else +#endif + if (attr == SYD_PLATTR_LINE) pMstr->linePlot = value; + else if (attr == SYD_PLATTR_MARK) pMstr->markPlot = value; + else if (attr == SYD_PLATTR_POINT) pMstr->pointPlot = value; + else if (attr == SYD_PLATTR_SHOW) pMstr->showStat = value; + else if (attr == SYD_PLATTR_UNDER) pMstr->fillUnder = value; + else if (attr == SYD_PLATTR_WRAP) pMstr->wrapX = value; + else if (attr == SYD_PLATTR_XLAB) pMstr->useXlabel = value; + else if (attr == SYD_PLATTR_XANN) pMstr->useXannot = value; + else if (attr == SYD_PLATTR_YLAB) pMstr->useYlabel = value; + else if (attr == SYD_PLATTR_YANN) pMstr->useYannot = value; + else if (attr == SYD_PLATTR_MONO) pMstr->noColor = value; + else assertAlways(0); + + return S_syd_OK; +} + +/*+/subr********************************************************************** +* NAME sydPlotSetTitles - establish or change titles for a plot +* +* DESCRIPTION +* Set the titles for a plot. +* +* By default, there are no titles for a plot. If the argument +* for a particular title isn't NULL, then that title is changed. +* +* RETURNS +* S_syd_OK +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotSetTitles(pMstr, top, left, bottom, right) +SYD_PL_MSTR *pMstr; /* I pointer to plot master structure */ +char *top; /* I title for top of plot, or NULL */ +char *left; /* I title for left of plot, or NULL */ +char *bottom; /* I title for bottom of plot, or NULL */ +char *right; /* I title for right of plot, or NULL */ +{ + if (top != NULL) { + assert(strlen(top) < sizeof(pMstr->title)); + strcpy(pMstr->title, top); + } + if (left != NULL) { + assert(strlen(left) < sizeof(pMstr->lTitle)); + strcpy(pMstr->lTitle, left); + } + if (bottom != NULL) { + assert(strlen(bottom) < sizeof(pMstr->bTitle)); + strcpy(pMstr->bTitle, bottom); + } + if (right != NULL) { + assert(strlen(right) < sizeof(pMstr->rTitle)); + strcpy(pMstr->rTitle, right); + } + + return S_syd_OK; +} + +/*+/subr********************************************************************** +* NAME sydPlotWinLoop - do the actual plotting +* +* DESCRIPTION +* for use with sydPlotInit +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotWinLoop(pMstr) +SYD_PL_MSTR *pMstr; /* IO pointer to plot master structure */ +{ + SYD_PLAX pltTy = pMstr->plotAxis; /* type of plot desired */ + long stat; + char refText[28]; + int npts; + SYD_SPEC *pSspec = pMstr->pSspec; + + npts = pSspec->sampleCount; + if (npts > 1 && pMstr->originVal != pMstr->extentVal) { + pprAutoEnds(pSspec->pDeltaSec[0], pSspec->pDeltaSec[npts-1], + &pMstr->originVal, &pMstr->extentVal); + pprAutoInterval(pMstr->originVal, pMstr->extentVal, &pMstr->nInt); + (void)tsStampToText(&pSspec->refTs, TS_TEXT_MMDDYY, refText); + (void)sprintf(pMstr->label, "sec past %s", refText); + (void)strcpy(pMstr->refText, refText); + } + else { + pMstr->originVal = 0.; + pMstr->extentVal = 100.; + strcpy(pMstr->label, "elapsed seconds"); + pMstr->refText[0] = '\0'; + pMstr->nInt = 5; + } + + stat = pprWinLoop(pMstr->pWin, sydPlot, pMstr); + if (stat != OK) + return S_syd_ERROR; + + pprWinInfo(pMstr->pWin, &pMstr->x, &pMstr->y,&pMstr->width,&pMstr->height); + + return S_syd_OK; +} + +/*+/subr********************************************************************** +* NAME sydPlotWinReplot - do the actual plotting +* +* DESCRIPTION +* for use with sydPlotInitUW +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +sydPlotWinReplot(pMstr) +SYD_PL_MSTR *pMstr; /* IO pointer to plot master structure */ +{ + char refText[28]; + int npts; + SYD_SPEC *pSspec = pMstr->pSspec; + + npts = pSspec->sampleCount; + if (npts > 1 && pMstr->originVal != pMstr->extentVal) { + pprAutoEnds(pSspec->pDeltaSec[0], pSspec->pDeltaSec[npts-1], + &pMstr->originVal, &pMstr->extentVal); + pprAutoInterval(pMstr->originVal, pMstr->extentVal, &pMstr->nInt); + (void)tsStampToText(&pSspec->refTs, TS_TEXT_MMDDYY, refText); + (void)sprintf(pMstr->label, "sec past %s", refText); + (void)strcpy(pMstr->refText, refText); + } + else { + pMstr->originVal = 0.; + pMstr->extentVal = 100.; + strcpy(pMstr->label, "elapsed seconds"); + pMstr->refText[0] = '\0'; + pMstr->nInt = 5; + } + + pprWinErase(pMstr->pWin); + pprWinReplot(pMstr->pWin, sydPlot, pMstr); + + return S_syd_OK; +} + +/*+/subr********************************************************************** +* NAME sydPlot - call the plot routine appropriate for plot type +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot(pWin, pMstr) +PPR_WIN *pWin; /* I pointer to plot window structure */ +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + pprWinInfo(pWin, &pMstr->x, &pMstr->y, &pMstr->width, &pMstr->height); + if (pMstr->plotAxis == SYD_PLAX_TY) + sydPlot_TYPlot(pMstr); + else if (pMstr->plotAxis == SYD_PLAX_TYY) + sydPlot_TYYPlot(pMstr); + else if (pMstr->plotAxis == SYD_PLAX_XY) + sydPlot_XYPlot(pMstr); + else if (pMstr->plotAxis == SYD_PLAX_XYY) + sydPlot_XYYPlot(pMstr); + else if (pMstr->plotAxis == SYD_PLAX_Y) + sydPlot_YPlot(pMstr); + else if (pMstr->plotAxis == SYD_PLAX_YY) + sydPlot_YYPlot(pMstr); + else if (pMstr->plotAxis == SYD_PLAX_SMITH_IMP || + pMstr->plotAxis == SYD_PLAX_SMITH_ADM || + pMstr->plotAxis == SYD_PLAX_SMITH_IMM) + sydPlot_SmithPlot(pMstr); + else + assertAlways(0); +} + +/*/subhead -------------------------------------------------------------------- +* +*----------------------------------------------------------------------------*/ +#define FetchIthValInto(pSChan, dbl) \ + if (dbr_type_is_FLOAT(pSChan->dbrType)) \ + dbl = (double)((float *)pSChan->pData)[i]; \ + else if (dbr_type_is_SHORT(pSChan->dbrType)) \ + dbl = (double)((short *)pSChan->pData)[i]; \ + else if (dbr_type_is_DOUBLE(pSChan->dbrType)) \ + dbl = (double)((double *)pSChan->pData)[i]; \ + else if (dbr_type_is_LONG(pSChan->dbrType)) \ + dbl = (double)((long *)pSChan->pData)[i]; \ + else if (dbr_type_is_CHAR(pSChan->dbrType)) \ + dbl = (double)((char *)pSChan->pData)[i]; \ + else if (dbr_type_is_ENUM(pSChan->dbrType)) \ + dbl = (double)((short *)pSChan->pData)[i]; \ + else \ + assertAlways(0); + + +/*+/subr********************************************************************** +* NAME sydPlot_setup - set up titles and margins for a plot window +* +* DESCRIPTION +* Plots whatever titles are present in the plot master, reserving +* an appropriate margin when necessary. The caller is given the +* appropriate coordinates and sizes for the sub-plots. +* +* RETURNS +* void +* +* BUGS +* o handles only vertical subdividing of the plot window +* +*-*/ +static void +sydPlot_setup(pMstr, nGrids, pXlo, pYlo, pXhi, pYhi, pYpart, pCh, pChX) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +double *pXlo, *pYlo, *pXhi, *pYhi, *pYpart, *pCh, *pChX; +int nGrids; +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + double xlo, ylo, xhi, yhi, yPart, charHt, charHtX; + PPR_AREA *pArea; + TS_STAMP now; + char nowText[32]; + + pWin = pMstr->pWin; + xlo = 0.; + xhi = .98; + ylo = 0.; + yhi = .98; + charHt = .012; + charHtX = pprYFracToXFrac(pWin, charHt); +/*----------------------------------------------------------------------------- +* initialize a plot area covering the whole window, with diagonal corners +* of 0,0 1,1 for plotting the titles +* +* for PostScript, plot current date and time +* plot the titles which aren't empty +*----------------------------------------------------------------------------*/ + pArea = pprAreaOpen(pWin, 0.,0., 1.,1., 0.,0., 1.,1., 1, 1, 0.); + assertAlways(pArea != NULL); + if (pMstr->winType == PPR_WIN_POSTSCRIPT) { + (void)tsLocalTime(&now); + (void)tsStampToText(&now, TS_TEXT_MONDDYYYY, nowText); + pprText(pArea, .98, .995, nowText, PPR_TXT_RJ, .008, 0.); + } + if (strlen(pMstr->title) > 0) { + yhi = 1. - charHt; + pprText(pArea, .5, yhi, pMstr->title, PPR_TXT_CEN, charHt, 0.); + yhi -= 2. * charHt; + } + if (strlen(pMstr->lTitle) > 0) { + xlo = 2. * charHtX; + pprText(pArea, xlo, .5, pMstr->lTitle, PPR_TXT_CEN, charHt, 90.); + xlo += 2. * charHtX; + } + if (strlen(pMstr->bTitle) > 0) { + ylo = 2. * charHt; + pprText(pArea, .5, ylo, pMstr->bTitle, PPR_TXT_CEN, charHt, 0.); + ylo += 2. * charHt; + } + if (strlen(pMstr->rTitle) > 0) { + xhi = 1. - 2. * charHtX; + pprText(pArea, xhi, .5, pMstr->rTitle, PPR_TXT_CEN, charHt, 90.); + xhi -= 2. * charHtX; + } + pprAreaClose(pArea); + + yPart = (yhi - ylo)/(double)nGrids; + yhi = yPart + ylo; + charHt = PprDfltCharHt(ylo, yhi); + charHtX = pprYFracToXFrac(pWin, charHt); + + *pXlo = xlo; + *pXhi = xhi; + *pYlo = ylo; + *pYhi = yhi; + *pYpart = yPart; + *pCh = charHt; + *pChX = charHtX; +} + +/*+/subr********************************************************************** +* NAME sydPlot_SmithPlot - handle Smith Chart plots +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* sydPlot_SmithGrid, sydPlot_SmithSamples +* +* EXAMPLE +* +*-*/ +void +sydPlot_SmithPlot(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_SPEC *pSspec; + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + + sydPlot_SmithGrid(pMstr); + sydPlot_SmithSamples(pMstr, pSspec->firstData, pSspec->lastData, 0); +} + +/*+/subr********************************************************************** +* NAME sydPlot_SmithGrid - draw a Smith chart overlay +* +* DESCRIPTION +* Draws a Smith chart overlay, to be used in plotting X vs Y data. +* Three overlays are available, with axis type controlling which is +* drawn: +* +* SYD_PLAX_SMITH_IMP results in an impedance overlay, with +* circles tangent on the right. If SYD_PLATTR_FG1 has been +* used to set an alternate foreground pixel value, then, on +* color displays, the overlay is drawn using that pixel value. +* SYD_PLAX_SMITH_ADM results in an admittance overlay, with +* circles tangent on the left. If SYD_PLATTR_FG2 has been +* used to set an alternate foreground pixel value, then, on +* color displays, the overlay is drawn using that pixel value. +* SYD_PLAX_SMITH_IMM results in an "immittance" overlay, which is +* a combination of the impedance overlay on top of the +* admittance overlay. On color displays when alternate +* foreground pixel values have been specified, the overlays +* are drawn as described above. On monochrome displays, or +* when no alternate foreground pixel values are specified, +* the impedance overlay is drawn with a solid line and the +* admittance overlay is drawn with a dashed line. +* +* On color displays, if no alternate foreground pixel values have been +* set, then the overlays are drawn using the color of the plot window. +* +* RETURNS +* void +* +* BUGS +* o channel names aren't displayed +* o colors are done only under X11 +* +*-*/ +void +sydPlot_SmithGrid(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + PPR_WIN *pWin; + PPR_AREA *pArea; + double incr=5.; /* use 5 degree increments */ + double x, y, rad; + int r; + static char *xTxt[]={"5","2","1","0.5","0.2","0"}; + double xlo, ylo, xhi, yhi; + double xmin, ymin, xmax, ymax; + double yPart; + double charHt, charHtX; + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + SYD_PL_SLAVE *pSlaveX; /* pointer to X axis slave struct */ + + pWin = pMstr->pWin; + + sydPlot_setup(pMstr, 1, &xlo, &ylo, &xhi, &yhi, &yPart, &charHt, &charHtX); + + xlo += 3. * charHtX; + ylo += 2. * charHt; + xhi -= charHt; + yhi -= 2. * charHt; + + pSlaveX = pMstr->pHead; + while (1) { + if (pSlaveX->xChan) + break; + pSlaveX = pSlaveX->pNext; + if (pSlaveX == NULL) { + pSlaveX = pMstr->pHead; + break; + } + } + if (pSlaveX == pMstr->pHead) + pSlave = pSlaveX->pNext; + else + pSlave = pMstr->pHead; + + pArea = pprAreaOpen(pWin, xlo,ylo, xhi,yhi, 0.,0., 1.,1., 1, 1, 0.); + + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM || + pMstr->plotAxis == SYD_PLAX_SMITH_IMM) { +/*----------------------------------------------------------------------------- +* admittance overlay, with circles tangent at x=0,y=.5 +* +* For immitance plots, with this as a secondary overlay, a dashed +* line pattern is used if the screen is monochrome. The outer +* circle isn't drawn and annotations aren't drawn. +*----------------------------------------------------------------------------*/ +#ifdef XWINDOWS + if (pMstr->noColor == 0 && pMstr->altPixel2 != 0) + pprAreaSetAttr(pArea, PPR_ATTR_FG, 0, &pMstr->altPixel2); + else { +#else + if (1) { +#endif + if (pMstr->plotAxis == SYD_PLAX_SMITH_IMM || pMstr->noColor == 0) + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, 1, NULL); + } + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprLineSegD(pArea, 0.,.5, 1.,.5); + y = .5; + for (r=6; r>0; r--) { + rad = (double)r / 12.; + x = rad; + if (r != 6 || pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprArcD(pArea, x, y, rad, 0., 360., incr); + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprText(pArea, x+rad+.015, y, xTxt[r-1], PPR_TXT_LJ, 0., 0.); + } + x = 0.; + rad = .25, y = .5 + rad; + pprArcD(pArea, x, y, rad, 270., 37., incr); + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprText(pArea, .19, .92, "2", PPR_TXT_RJ, 0., 0.); + y = .5 - rad; + pprArcD(pArea, x, y, rad, 323., 90., incr); + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprText(pArea, .19, .08, "2", PPR_TXT_RJ, 0., 0.); + rad = .5, y = .5 + rad; + pprArcD(pArea, x, y, rad, 270., 0., incr); + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprText(pArea, .5, 1.02, "1", PPR_TXT_CEN, 0., 0.); + y = .5 - rad; + pprArcD(pArea, x, y, rad, 0., 90., incr); + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprText(pArea, .5, -.02, "1", PPR_TXT_CEN, 0., 0.); + rad = 1., y = .5 + rad; + pprArcD(pArea, x, y, rad, 270., 323., incr); + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprText(pArea, .81, .92, "0.5", PPR_TXT_LJ, 0., 0.); + y = .5 - rad; + pprArcD(pArea, x, y, rad, 37., 90., incr); + if (pMstr->plotAxis == SYD_PLAX_SMITH_ADM) + pprText(pArea, .81, .08, "0.5", PPR_TXT_LJ, 0., 0.); + } + if (pMstr->plotAxis == SYD_PLAX_SMITH_IMP || + pMstr->plotAxis == SYD_PLAX_SMITH_IMM) { +/*----------------------------------------------------------------------------- +* impedance overlay, with circles tangent at x=1,y=.5 +*----------------------------------------------------------------------------*/ +#ifdef XWINDOWS + if (pMstr->noColor == 0 && pMstr->altPixel1 != 0) + pprAreaSetAttr(pArea, PPR_ATTR_FG, 0, &pMstr->altPixel1); + else { +#else + if (1) { +#endif + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, 0, NULL); + } + pprLineSegD(pArea, 0.,.5, 1.,.5); + y = .5; + for (r=6; r>0; r--) { + rad = (double)r / 12.; + x = 1. - rad; + pprArcD(pArea, x, y, rad, 0., 360., incr); + pprText(pArea, x-rad-.015, y, xTxt[r-1], PPR_TXT_RJ, 0., 0.); + } + x = 1.; + rad = .25, y = .5 + rad; + pprArcD(pArea, x, y, rad, 143., 270., incr); + pprText(pArea, .81, .92, "2", PPR_TXT_LJ, 0., 0.); + y = .5 - rad; + pprArcD(pArea, x, y, rad, 90., 217., incr); + pprText(pArea, .81, .08, "2", PPR_TXT_LJ, 0., 0.); + rad = .5, y = .5 + rad; + pprArcD(pArea, x, y, rad, 180., 270., incr); + pprText(pArea, .5, 1.02, "1", PPR_TXT_CEN, 0., 0.); + y = .5 - rad; + pprArcD(pArea, x, y, rad, 90., 180., incr); + pprText(pArea, .5, -.02, "1", PPR_TXT_CEN, 0., 0.); + rad = 1., y = .5 + rad; + pprArcD(pArea, x, y, rad, 217., 270., incr); + pprText(pArea, .19, .92, "0.5", PPR_TXT_RJ, 0., 0.); + y = .5 - rad; + pprArcD(pArea, x, y, rad, 90., 143., incr); + pprText(pArea, .19, .08, "0.5", PPR_TXT_RJ, 0., 0.); + } + pprAreaClose(pArea); + + pSlaveX->pArea = NULL; + xmin = pSlaveX->originVal; + xmax = pSlaveX->extentVal; + while (pSlave != NULL) { + ymin = pSlave->originVal; + ymax = pSlave->extentVal; + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pArea = pSlave->pArea = pprAreaOpen(pWin, + xlo,ylo, xhi,yhi, xmin, ymin, xmax, ymax, 1, 1, 0.); + assertAlways(pArea != NULL); + if (pSlave->fg != 0 && pMstr->noColor == 0) + pprAreaSetAttr(pSlave->pArea, PPR_ATTR_FG, 0, &pSlave->fg); + pSlave = pSlave->pNext; + if (pSlave == pSlaveX) + pSlave = pSlave->pNext; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_SmithSamples - plot one or more samples for a Smith Chart plot +* +* DESCRIPTION +* the first channel in the plot spec is used for the X axis +* +* RETURNS +* void +* +* BUGS +* o this isn't a true Smith chart plot--the caller must have transformed +* the data into simple X vs Y data +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_SmithSamples(pMstr, begin, end, incr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +int begin; /* I number of begin sample to plot */ +int end; /* I number of end sample to plot */ +int incr; /* I 0,1 for batch,incremental plotting */ +{ + sydPlot_XYSamples(pMstr, begin, end, incr); +} + +/*+/subr********************************************************************** +* NAME sydPlot_TYPlot - handle time vs Y plots +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_TYPlot(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_SPEC *pSspec; + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + + sydPlot_TYGrid(pMstr); + sydPlot_TYSamples(pMstr, pSspec->firstData, pSspec->lastData, 0); +} + +/*+/subr********************************************************************** +* NAME sydPlot_TYGrid - draw a grid for a time vs Y plot +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o labeling of x axis is un-esthetic. It should be time based, with +* some intelligent adaptation, based on time interval for X +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_TYGrid(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + double xlo, ylo, xhi, yhi; + double yPart; + PPR_AREA *pArea; + double xmin, xmax, ymin, ymax; + int xNint; + double charHt, charHtX; + SYD_CHAN *pSChan; + TS_STAMP now; + char nowText[32]; + int thick=3; + int nGrids; + + pWin = pMstr->pWin; + + nGrids = pMstr->nSlaves; + sydPlot_setup(pMstr, nGrids, + &xlo, &ylo, &xhi, &yhi, &yPart, &charHt, &charHtX); + + xmin = pMstr->originVal; + xmax = pMstr->extentVal; + xNint = pMstr->nInt; + if (xmin == xmax) { + xmin = 0.; + xmax = 100.; + xNint = 5; + } + pSlave = pMstr->pHead; + while (pSlave != NULL) { +/*----------------------------------------------------------------------------- +* for each channel, initialize a plot area. +* +* plot a perimeter with grid lines +*----------------------------------------------------------------------------*/ + pSChan = pSlave->pSChan; + ymin = pSlave->originVal; + ymax = pSlave->extentVal; + charHt = PprDfltCharHt(ylo, yhi); + charHtX = pprYFracToXFrac(pWin, charHt); + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pArea = pSlave->pArea = pprAreaOpen(pWin, + xlo+12.*charHtX, ylo+6.*charHt, xhi, yhi, + xmin, ymin, xmax, ymax, xNint, pSlave->nInt, charHt); + assertAlways(pArea != NULL); + pSlave->xFracLeft = xlo + 12. * charHtX; + pSlave->xFracRight = xhi; + pSlave->yFracBot = ylo + 6. * charHt; + pSlave->yFracTop = yhi; + if (pSlave->fg != 0 && pMstr->noColor == 0) + pprAreaSetAttr(pSlave->pArea, PPR_ATTR_FG, 0, &pSlave->fg); + else if (pMstr->linePlot) { + if (dbr_type_is_ENUM(pSChan->dbrType)) + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + } + pprGridLabel(pArea, pMstr->label, NULL, + pSlave->pSChan->label, pSlave->ppAnnot, 0.); + ylo += yPart; + yhi += yPart; + pSlave = pSlave->pNext; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_TYSamples - plot one or more samples for a time vs Y plot +* +* DESCRIPTION +* the first channel in the plot spec is used for the X axis +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_TYSamples(pMstr, begin, end, incr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +int begin; /* I number of begin sample to plot */ +int end; /* I number of end sample to plot */ +int incr; /* I 0,1 for batch,incremental plotting */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + PPR_AREA *pArea; + int i, j; + SYD_SPEC *pSspec; + SYD_CHAN *pSChan; + double oldX, oldY, newX, newY; + int skip; + int showStat; /* 1 to show status code on plot */ + int pointPlot; /* 1 for point plot */ + int linePlot; /* 1 to connect points with lines */ + int markPlot; /* 1 to draw marks at points */ + int markNum; /* number of mark to use */ + int nEl; /* number of array elements */ + int first; /* ==1 if this is the first sample */ + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + pWin = pMstr->pWin; + + linePlot = pMstr->linePlot; + pointPlot = pMstr->pointPlot; + markPlot = pMstr->markPlot; + showStat = pMstr->showStat; + + pSlave = pMstr->pHead; + while (pSlave != NULL) { + pArea = pSlave->pArea; + pSChan = pSlave->pSChan; + markNum = pSlave->markNum; + + nEl = pSChan->elCount; + + i = begin; + if (!incr) + first = 1; + else { + first = pSlave->first; + oldX = pSlave->oldX; + oldY = pSlave->oldY; + skip = pSlave->skip; + } + while (i >= 0) { + if (pSChan->pFlags[i].missing) + skip = 1; + else if (first || skip || pSChan->pFlags[i].restart) { + oldX = pSspec->pDeltaSec[i]; + if (pMstr->wrapX) { + while (oldX > pMstr->extentVal) + oldX -= pMstr->extentVal; + } + FetchIthValInto(pSChan, oldY) + if (markPlot) + pprMarkD(pArea, oldX, oldY, markNum); + if (showStat && pSChan->pDataCodeR[i] != ' ') { + pprChar(pArea, oldX, oldY, pSChan->pDataCodeR[i], 0., 0.); + } + else if (pointPlot) + pprPointD(pArea, oldX, oldY); + skip = 0; + } + else if (pSChan->pFlags[i].filled) + ; /* no action */ + else { + newX = pSspec->pDeltaSec[i]; + if (pMstr->wrapX) { + while (newX > pMstr->extentVal) + newX -= pMstr->extentVal; + } + if (linePlot && dbr_type_is_ENUM(pSChan->dbrType)) { + pprLineSegD(pArea, oldX, oldY, newX, oldY); + oldX = newX; + } + FetchIthValInto(pSChan, newY) + if (linePlot) + pprLineSegD(pArea, oldX, oldY, newX, newY); + if (markPlot) + pprMarkD(pArea, newX, newY, markNum); + if (showStat && pSChan->pDataCodeR[i] != ' ') { + pprChar(pArea, newX, newY, pSChan->pDataCodeR[i], 0., 0.); + } + else if (pointPlot) + pprPointD(pArea, newX, newY); + oldX = newX; + oldY = newY; + } + if (i == end) + i = -1; + else if (++i >= pSspec->dataDim) + i = 0; + first = 0; + } + pSlave->first = first; + pSlave->oldX = oldX; + pSlave->oldY = oldY; + pSlave->skip = skip; + pSlave = pSlave->pNext; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_TYYPlot - handle time vs multiple Y plots +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o labeling of x axis is un-esthetic. It should be time based, with +* some intelligent adaptation, based on time interval for X +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_TYYPlot(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_SPEC *pSspec; + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + + sydPlot_TYYGrid(pMstr); + sydPlot_TYSamples(pMstr, pSspec->firstData, pSspec->lastData, 0); +} + +/*+/subr********************************************************************** +* NAME sydPlot_TYYGrid - draw a grid for a time vs multiple Y plot +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_TYYGrid(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + double xlo, ylo, xhi, yhi, yPart; + PPR_AREA *pArea; + double xmin, xmax, ymin, ymax; + int xNint; + double charHt, charHtX; + SYD_CHAN *pSChan; + TS_STAMP now; + char nowText[32]; + int offsetAnnotY=0; + int drawAxis=0; + int thick=3; + int nGrids; + + pWin = pMstr->pWin; + + nGrids = 1; + sydPlot_setup(pMstr, nGrids, + &xlo, &ylo, &xhi, &yhi, &yPart, &charHt, &charHtX); + xlo += 6. * charHtX * (double)pMstr->nSlaves; + ylo += 6. * charHt; + + xmin = pMstr->originVal; + xmax = pMstr->extentVal; + xNint = pMstr->nInt; + if (xmin == xmax) { + xmin = 0.; + xmax = 100.; + xNint = 5; + } + pSlave = pMstr->pHead; + while (pSlave != NULL) { +/*----------------------------------------------------------------------------- +* for the first channel: +* initialize a plot area; its fractional size depends on how many +* "sub-plots" there are +* plot a perimeter with grid lines +* for the other channels: +* initialize an overlapping plot area +* set a dashed line pattern (unless this is a mark or point plot) +* draw a "floating" Y axis +*----------------------------------------------------------------------------*/ + pSChan = pSlave->pSChan; + ymin = pSlave->originVal; + ymax = pSlave->extentVal; + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pArea = pSlave->pArea = pprAreaOpen(pWin, xlo, ylo, xhi, yhi, + xmin, ymin, xmax, ymax, xNint, pSlave->nInt, charHt); + assertAlways(pArea != NULL); + pSlave->xFracLeft = xlo; + pSlave->xFracRight = xhi; + pSlave->yFracBot = ylo; + pSlave->yFracTop = yhi; + if (pSlave->fg != 0 && pMstr->noColor == 0) + pprAreaSetAttr(pSlave->pArea, PPR_ATTR_FG, 0, &pSlave->fg); + else if (pMstr->linePlot) { + if (dbr_type_is_ENUM(pSChan->dbrType)) + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + if (pSlave->lineKey > 1 || pMstr->noColor == 0) + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, pSlave->lineKey, NULL); + } + else if (pMstr->noColor == 0) + pprAreaSetAttr(pArea, PPR_ATTR_COLORNUM, pSlave->lineKey, NULL); + if (drawAxis == 0) { + pprGrid(pArea); + pprAnnotX_wc(pArea, 0, + xmin, xmax, xNint, 0, pMstr->label, NULL, 0.); + } + pprAnnotY(pArea, offsetAnnotY, pSlave->originVal, pSlave->extentVal, + pSlave->nInt, drawAxis, + pSlave->pSChan->label, pSlave->ppAnnot, 90.); + if (pMstr->markPlot) + pprAnnotYMark(pArea, offsetAnnotY, pSlave->markNum); + offsetAnnotY += 6; + drawAxis = 1; /* draw an "auxiliary" axis next time */ + pSlave = pSlave->pNext; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_XYPlot - handle X vs Y plots +* +* DESCRIPTION +* the first channel in the plot spec is used for the X axis +* +* alarm state of the "X" channel is not displayed +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_XYPlot(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_SPEC *pSspec; + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + + sydPlot_XYGrid(pMstr); + sydPlot_XYSamples(pMstr, pSspec->firstData, pSspec->lastData, 0); +} + +/*+/subr********************************************************************** +* NAME sydPlot_XYGrid - draw a grid for an X vs Y plot +* +* DESCRIPTION +* the first channel in the plot spec is used for the X axis +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_XYGrid(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + SYD_PL_SLAVE *pSlaveX; /* pointer to X axis slave struct */ + double xlo, ylo, xhi, yhi; + double yPart; + PPR_AREA *pArea; + double xmin, xmax, ymin, ymax; + double charHt, charHtX; + SYD_CHAN *pSChan; + SYD_CHAN *pSChanX; + int nGrids; + + pWin = pMstr->pWin; + + pSlaveX = pMstr->pHead; + while (1) { + if (pSlaveX->xChan) + break; + pSlaveX = pSlaveX->pNext; + if (pSlaveX == NULL) { + pSlaveX = pMstr->pHead; + break; + } + } + if (pSlaveX == pMstr->pHead) + pSlave = pSlaveX->pNext; + else + pSlave = pMstr->pHead; + nGrids = pMstr->nSlaves - 1; + sydPlot_setup(pMstr, nGrids, + &xlo, &ylo, &xhi, &yhi, &yPart, &charHt, &charHtX); + + pSlaveX->pArea = NULL; + pSChanX = pSlaveX->pSChan; + xmin = pSlaveX->originVal; + xmax = pSlaveX->extentVal; + while (pSlave != NULL) { +/*----------------------------------------------------------------------------- +* for each Y channel, plot a perimeter with grid lines +*----------------------------------------------------------------------------*/ + pSChan = pSlave->pSChan; + ymin = pSlave->originVal; + ymax = pSlave->extentVal; + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pArea = pSlave->pArea = pprAreaOpen(pWin, + xlo+12.*charHtX, ylo+6.*charHt, xhi, yhi, + xmin, ymin, xmax, ymax, pSlaveX->nInt, pSlave->nInt, charHt); + assertAlways(pArea != NULL); + pSlave->xFracLeft = xlo + 12. * charHtX; + pSlave->xFracRight = xhi; + pSlave->yFracBot = ylo + 6. * charHt; + pSlave->yFracTop = yhi; + if (pSlave->fg != 0 && pMstr->noColor == 0) + pprAreaSetAttr(pSlave->pArea, PPR_ATTR_FG, 0, &pSlave->fg); + pprGridLabel(pArea, pSlaveX->pSChan->label, NULL, + pSlave->pSChan->label, pSlave->ppAnnot, 0.); + ylo += yPart; + yhi += yPart; + pSlave = pSlave->pNext; + if (pSlave == pSlaveX) + pSlave = pSlave->pNext; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_XYSamples - plot one or more samples for an X vs Y plot +* +* DESCRIPTION +* the first channel in the plot spec is used for the X axis +* +* alarm state of the "X" channel is not displayed +* +* RETURNS +* void +* +* BUGS +* o line plot isn't handled +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_XYSamples(pMstr, begin, end, incr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +int begin; /* I number of begin sample to plot */ +int end; /* I number of end sample to plot */ +int incr; /* I 0,1 for batch,incremental plotting */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + SYD_PL_SLAVE *pSlaveX; /* pointer to X axis slave struct */ + PPR_AREA *pArea; + int i, j; + SYD_SPEC *pSspec; + SYD_CHAN *pSChan; + SYD_CHAN *pSChanX; + double oldX, oldY, newX, newY; + int skip; + int showStat; /* 1 to show status code on plot */ + int pointPlot; /* 1 for point plot */ + int linePlot; /* 1 to connect points with lines */ + int markPlot; /* 1 to draw marks at points */ + int markNum; /* number of mark to use */ + int nEl; /* number of array elements */ + int first; /* ==1 if this is the first sample */ + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + pWin = pMstr->pWin; + + linePlot = pMstr->linePlot; + pointPlot = pMstr->pointPlot; + markPlot = pMstr->markPlot; + showStat = pMstr->showStat; + + pSlaveX = pMstr->pHead; + while (1) { + if (pSlaveX->xChan) + break; + pSlaveX = pSlaveX->pNext; + if (pSlaveX == NULL) { + pSlaveX = pMstr->pHead; + break; + } + } + if (pSlaveX == pMstr->pHead) + pSlave = pSlaveX->pNext; + else + pSlave = pMstr->pHead; + pSChanX = pSlaveX->pSChan; + while (pSlave != NULL) { + pArea = pSlave->pArea; + pSChan = pSlave->pSChan; + markNum = pSlave->markNum; + + nEl = pSChanX->elCount; + if (nEl > pSChan->elCount) + nEl = pSChan->elCount; + + i = begin; + if (!incr) + first = 1; + else { + first = pSlave->first; + oldX = pSlave->oldX; + oldY = pSlave->oldY; + skip = pSlave->skip; + } + while (i >= 0) { + if (pSChan->pFlags[i].missing || pSChanX->pFlags[i].missing) + skip = 1; + else if (first || skip || + pSChan->pFlags[i].restart || pSChanX->pFlags[i].restart) { + if (nEl == 1) { + FetchIthValInto(pSChanX, oldX) + FetchIthValInto(pSChan, oldY) + if (markPlot) + pprMarkD(pArea, oldX, oldY, markNum); + if (showStat && pSChan->pDataCodeR[i] != ' ') { + pprChar(pArea, + oldX, oldY, pSChan->pDataCodeR[i], 0., 0.); + } + else if (pointPlot) + pprPointD(pArea, oldX, oldY); + } + else { + sydPlot_XYarray(pArea, pSChanX, pSChan, i); + } + skip = 0; + } + else if (pSChan->pFlags[i].filled) + ; /* no action */ + else { + if (nEl == 1) { + FetchIthValInto(pSChanX, newX) + FetchIthValInto(pSChan, newY) + if (linePlot) + pprLineSegD(pArea, oldX, oldY, newX, newY); + if (markPlot) + pprMarkD(pArea, newX, newY, markNum); + if (showStat && pSChan->pDataCodeR[i] != ' ') { + pprChar(pArea, + newX, newY, pSChan->pDataCodeR[i], 0., 0.); + } + else if (pointPlot) + pprPointD(pArea, newX, newY); + oldX = newX; + oldY = newY; + } + else { + sydPlot_XYarray(pArea, pSChanX, pSChan, i); + } + } + if (i == end) + i = -1; + else if (++i >= pSspec->dataDim) + i = 0; + first = 0; + } + pSlave->first = first; + pSlave->oldX = oldX; + pSlave->oldY = oldY; + pSlave->skip = skip; + pSlave = pSlave->pNext; + if (pSlave == pSlaveX) + pSlave = pSlave->pNext; + } +} + +/*+/internal****************************************************************** +* NAME sydPlot_XYarray - plot array vs array +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +static void +sydPlot_XYarray(pArea, pSChanX, pSChan, sub) +PPR_AREA *pArea; +SYD_CHAN *pSChan; +SYD_CHAN *pSChanX; +int sub; +{ + int nEl, nElX, nElY, i; + char *pSrcX, *pSrcY; + double newX, newY, oldX, oldY; + int nByteX, nByteY; + + nEl = nElX = pSChanX->elCount; + nElY = pSChan->elCount; + if (nElX > nElY) + nEl = nElY; + nByteX = dbr_value_size[pSChanX->dbrType]; + pSrcX = (char *)pSChanX->pData + sub * nByteX * nElX; + nByteY = dbr_value_size[pSChan->dbrType]; + pSrcY = (char *)pSChan->pData + sub * nByteY * nElY; + for (i=0; idbrType)) + newX = *(float *)pSrcX; + else if (dbr_type_is_SHORT(pSChanX->dbrType)) + newX = *(short *)pSrcX; + else if (dbr_type_is_DOUBLE(pSChanX->dbrType)) + newX = *(double *)pSrcX; + else if (dbr_type_is_LONG(pSChanX->dbrType)) + newX = *(long *)pSrcX; + else if (dbr_type_is_CHAR(pSChanX->dbrType)) + newX = *(unsigned char *)pSrcX; + else if (dbr_type_is_ENUM(pSChanX->dbrType)) + newX = *(short *)pSrcX; + if (dbr_type_is_FLOAT(pSChan->dbrType)) + newY = *(float *)pSrcY; + else if (dbr_type_is_SHORT(pSChan->dbrType)) + newY = *(short *)pSrcY; + else if (dbr_type_is_DOUBLE(pSChan->dbrType)) + newY = *(double *)pSrcY; + else if (dbr_type_is_LONG(pSChan->dbrType)) + newY = *(long *)pSrcY; + else if (dbr_type_is_CHAR(pSChan->dbrType)) + newY = *(unsigned char *)pSrcY; + else if (dbr_type_is_ENUM(pSChan->dbrType)) + newY = *(short *)pSrcY; + if (i > 0) + pprLineSegD(pArea, oldX, oldY, newX, newY); + oldX = newX; + oldY = newY; + pSrcX += nByteX; + pSrcY += nByteY; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_XYYPlot - handle X vs multiple Y plots +* +* DESCRIPTION +* the first channel in the plot spec is used for the X axis +* +* alarm state of the "X" channel is not displayed +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_XYYPlot(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_SPEC *pSspec; + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + + sydPlot_XYYGrid(pMstr); + sydPlot_XYSamples(pMstr, pSspec->firstData, pSspec->lastData, 0); +} + +/*+/subr********************************************************************** +* NAME sydPlot_XYYGrid - draw a grid for an X vs multiple Y plot +* +* DESCRIPTION +* the first channel in the plot spec is used for the X axis +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_XYYGrid(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + SYD_PL_SLAVE *pSlaveX; /* pointer to X axis slave struct */ + double xlo, ylo, xhi, yhi, yPart; + PPR_AREA *pArea; + double xmin, xmax, ymin, ymax; + double charHt, charHtX; + SYD_CHAN *pSChan; + SYD_CHAN *pSChanX; + TS_STAMP now; + char nowText[32]; + int offsetAnnotY=0; + int drawAxis=0; + int nGrids; + + pWin = pMstr->pWin; + + pSlaveX = pMstr->pHead; + while (1) { + if (pSlaveX->xChan) + break; + pSlaveX = pSlaveX->pNext; + if (pSlaveX == NULL) { + pSlaveX = pMstr->pHead; + break; + } + } + if (pSlaveX == pMstr->pHead) + pSlave = pSlaveX->pNext; + else + pSlave = pMstr->pHead; + pSChanX = pSlaveX->pSChan; + + nGrids = 1; + sydPlot_setup(pMstr, nGrids, + &xlo, &ylo, &xhi, &yhi, &yPart, &charHt, &charHtX); + xlo += 6. * charHtX * (double)(pMstr->nSlaves - 1); + ylo += 6. * charHt; + + pSlaveX->pArea = NULL; + xmin = pSlaveX->originVal; + xmax = pSlaveX->extentVal; + pSlaveX->yFracBot = pSlaveX->yFracTop = 0.; + while (pSlave != NULL) { +/*----------------------------------------------------------------------------- +* for the first Y channel: +* initialize a plot area; its fractional size depends on how many +* "sub-plots" there are +* plot a perimeter with grid lines +* set for solid line +* for the other channels: +* initialize an overlapping plot area +* set a dashed line pattern (unless this is a mark or point plot) +* draw a "floating" Y axis +*----------------------------------------------------------------------------*/ + pSChan = pSlave->pSChan; + ymin = pSlave->originVal; + ymax = pSlave->extentVal; + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pArea = pSlave->pArea = pprAreaOpen(pWin, xlo, ylo, xhi, yhi, + xmin, ymin, xmax, ymax, pSlaveX->nInt, pSlave->nInt, charHt); + assertAlways(pArea != NULL); + pSlave->xFracLeft = xlo; + pSlave->xFracRight = xhi; + pSlave->yFracBot = ylo; + pSlave->yFracTop = yhi; + if (pSlave->fg != 0 && pMstr->noColor == 0) + pprAreaSetAttr(pSlave->pArea, PPR_ATTR_FG, 0, &pSlave->fg); + else if (pMstr->linePlot) { + /* set keynum if color is being used or if this is auxiliary axis */ + if (drawAxis || pMstr->noColor == 0) + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, pSlave->lineKey, NULL); + } + else if (pMstr->noColor == 0) + pprAreaSetAttr(pArea, PPR_ATTR_COLORNUM, pSlave->lineKey, NULL); + if (drawAxis == 0) { + pprGrid(pArea); + pprAnnotX_wc(pArea, 0, pSlaveX->originVal, pSlaveX->extentVal, + pSlaveX->nInt, 0, pSlaveX->pSChan->label, NULL, 0.); + } + pprAnnotY(pArea, offsetAnnotY, pSlave->originVal, pSlave->extentVal, + pSlave->nInt, drawAxis, pSlave->pSChan->label, pSlave->ppAnnot, 90.); + if (pMstr->markPlot) + pprAnnotYMark(pArea, offsetAnnotY, pSlave->markNum); + offsetAnnotY += 6; + drawAxis = 1; /* draw an "auxiliary" axis next time */ + pSlave = pSlave->pNext; + if (pSlave == pSlaveX) + pSlave = pSlave->pNext; + } + + return; +} + +/*+/subr********************************************************************** +* NAME sydPlot_YPlot - handle Y plots +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_YPlot(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_SPEC *pSspec; + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + + sydPlot_YGrid(pMstr); + sydPlot_YSamples(pMstr, pSspec->firstData, pSspec->lastData, 0); +} + +/*+/subr********************************************************************** +* NAME sydPlot_YGrid - draw a grid for a Y plot +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_YGrid(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + double xlo, ylo, xhi, yhi; + double yPart; + PPR_AREA *pArea; + double xmin, xmax, ymin, ymax; + int xNint; + double charHt, charHtX; + SYD_CHAN *pSChan; + TS_STAMP now; + char nowText[32]; + int thick=3; + int nGrids; + + pWin = pMstr->pWin; + + nGrids = pMstr->nSlaves; + sydPlot_setup(pMstr, nGrids, + &xlo, &ylo, &xhi, &yhi, &yPart, &charHt, &charHtX); + + xmin = xmax = 0.; + pSlave = pMstr->pHead; + while (pSlave != NULL) { + if (pSlave->pSChan->elCount > xmax) + xmax = pSlave->pSChan->elCount; + pSlave = pSlave->pNext; + } + xNint = 1; + if (xmax == 1.) + xmax = pMstr->pSspec->reqCount - 1; + + pSlave = pMstr->pHead; + while (pSlave != NULL) { +/*----------------------------------------------------------------------------- +* for each channel, initialize a plot area. +* +* plot a perimeter with grid lines +*----------------------------------------------------------------------------*/ + pSChan = pSlave->pSChan; + ymin = pSlave->originVal; + ymax = pSlave->extentVal; + charHt = PprDfltCharHt(ylo, yhi); + charHtX = pprYFracToXFrac(pWin, charHt); + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pArea = pSlave->pArea = pprAreaOpen(pWin, + xlo+12.*charHtX, ylo+6.*charHt, xhi, yhi, + xmin, ymin, xmax, ymax, xNint, pSlave->nInt, charHt); + assertAlways(pArea != NULL); + pSlave->xFracLeft = xlo + 12. * charHtX; + pSlave->xFracRight = xhi; + pSlave->yFracBot = ylo + 6. * charHt; + pSlave->yFracTop = yhi; + if (pSlave->fg != 0 && pMstr->noColor == 0) + pprAreaSetAttr(pSlave->pArea, PPR_ATTR_FG, 0, &pSlave->fg); + else if (pMstr->linePlot) { + if (dbr_type_is_ENUM(pSChan->dbrType)) + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + } + pprGridLabel(pArea, "", NULL, + pSlave->pSChan->label, pSlave->ppAnnot, 0.); + ylo += yPart; + yhi += yPart; + pSlave = pSlave->pNext; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_YSamples - plot one or more samples for a Y plot +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_YSamples(pMstr, begin, end, incr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +int begin; /* I number of begin sample to plot */ +int end; /* I number of end sample to plot */ +int incr; /* I 0,1 for batch,incremental plotting */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + PPR_AREA *pArea; + int i, j; + SYD_SPEC *pSspec; + SYD_CHAN *pSChan; + double oldX, oldY, newX, newY; + int skip; + int showStat; /* 1 to show status code on plot */ + int pointPlot; /* 1 for point plot */ + int linePlot; /* 1 to connect points with lines */ + int markPlot; /* 1 to draw marks at points */ + int markNum; /* number of mark to use */ + int nEl; /* number of array elements */ + int first; /* ==1 if this is the first sample */ + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + pWin = pMstr->pWin; + + linePlot = pMstr->linePlot; + pointPlot = pMstr->pointPlot; + markPlot = pMstr->markPlot; + showStat = pMstr->showStat; + + pSlave = pMstr->pHead; + while (pSlave != NULL) { + pArea = pSlave->pArea; + pSChan = pSlave->pSChan; + markNum = pSlave->markNum; + + nEl = pSChan->elCount; + + i = begin; + if (!incr) + first = 1; + else { + first = pSlave->first; + oldX = pSlave->oldX; + oldY = pSlave->oldY; + skip = pSlave->skip; + } + while (i >= 0) { + if (pSChan->pFlags[i].missing) + skip = 1; + else if (first || skip || pSChan->pFlags[i].restart) { + if (nEl == 1) { + oldX = i; + FetchIthValInto(pSChan, oldY) + if (markPlot) + pprMarkD(pArea, oldX, oldY, markNum); + if (showStat && pSChan->pDataCodeR[i] != ' ') { + pprChar(pArea, oldX, oldY, + pSChan->pDataCodeR[i], 0., 0.); + } + else if (pointPlot) + pprPointD(pArea, oldX, oldY); + } + else { + sydPlot_Yarray(pArea, pSChan, i); + } + skip = 0; + } + else if (pSChan->pFlags[i].filled) + ; /* no action */ + else { + if (nEl == 1) { + newX = i; + if (linePlot && dbr_type_is_ENUM(pSChan->dbrType)) { + pprLineSegD(pArea, oldX, oldY, newX, oldY); + oldX = newX; + } + FetchIthValInto(pSChan, newY) + if (linePlot) + pprLineSegD(pArea, oldX, oldY, newX, newY); + if (markPlot) + pprMarkD(pArea, newX, newY, markNum); + if (showStat && pSChan->pDataCodeR[i] != ' ') { + pprChar(pArea, newX, newY, + pSChan->pDataCodeR[i], 0., 0.); + } + else if (pointPlot) + pprPointD(pArea, newX, newY); + oldX = newX; + oldY = newY; + } + else { + sydPlot_Yarray(pArea, pSChan, i); + } + } + if (i == end) + i = -1; + else if (++i >= pSspec->dataDim) + i = 0; + first = 0; + } + pSlave->first = first; + pSlave->oldX = oldX; + pSlave->oldY = oldY; + pSlave->skip = skip; + pSlave = pSlave->pNext; + } +} + +/*+/internal****************************************************************** +* NAME sydPlot_Yarray - plot array vs array +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +static void +sydPlot_Yarray(pArea, pSChan, sub) +PPR_AREA *pArea; +SYD_CHAN *pSChan; +int sub; +{ + int nEl, nElY, i; + char *pSrcY; + double newX, newY, oldX, oldY; + int nByteY; + + nEl = nElY = pSChan->elCount; + nByteY = dbr_value_size[pSChan->dbrType]; + pSrcY = (char *)pSChan->pData + sub * nByteY * nElY; + for (i=0; idbrType)) + newY = *(float *)pSrcY; + else if (dbr_type_is_SHORT(pSChan->dbrType)) + newY = *(short *)pSrcY; + else if (dbr_type_is_DOUBLE(pSChan->dbrType)) + newY = *(double *)pSrcY; + else if (dbr_type_is_LONG(pSChan->dbrType)) + newY = *(long *)pSrcY; + else if (dbr_type_is_CHAR(pSChan->dbrType)) + newY = *(unsigned char *)pSrcY; + else if (dbr_type_is_ENUM(pSChan->dbrType)) + newY = *(short *)pSrcY; + if (i > 0) + pprLineSegD(pArea, oldX, oldY, newX, newY); + oldX = newX; + oldY = newY; + pSrcY += nByteY; + } +} + +/*+/subr********************************************************************** +* NAME sydPlot_YYPlot - handle multiple Y plots +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_YYPlot(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + SYD_SPEC *pSspec; + + assert(pMstr != NULL); + pSspec = pMstr->pSspec; + assert(pSspec != NULL); + + sydPlot_YYGrid(pMstr); + sydPlot_YSamples(pMstr, pSspec->firstData, pSspec->lastData, 0); +} + +/*+/subr********************************************************************** +* NAME sydPlot_YYGrid - draw a grid for a multiple Y plot +* +* DESCRIPTION +* +* RETURNS +* void +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +void +sydPlot_YYGrid(pMstr) +SYD_PL_MSTR *pMstr; /* I pointer to master plot structure */ +{ + PPR_WIN *pWin; /* pointer to plot window structure */ + SYD_PL_SLAVE *pSlave; /* pointer to individual slave struct */ + double xlo, ylo, xhi, yhi, yPart; + PPR_AREA *pArea; + double xmin, xmax, ymin, ymax; + int xNint; + double charHt, charHtX; + SYD_CHAN *pSChan; + TS_STAMP now; + char nowText[32]; + int offsetAnnotY=0; + int drawAxis=0; + int thick=3; + int nGrids; + + pWin = pMstr->pWin; + + nGrids = 1; + sydPlot_setup(pMstr, nGrids, + &xlo, &ylo, &xhi, &yhi, &yPart, &charHt, &charHtX); + xlo += 6. * charHtX * (double)pMstr->nSlaves; + ylo += 6. * charHt; + + xmin = xmax = 0.; + pSlave = pMstr->pHead; + while (pSlave != NULL) { + if (pSlave->pSChan->elCount > xmax) + xmax = pSlave->pSChan->elCount; + pSlave = pSlave->pNext; + } + xNint = 1; + if (xmax == 1.) + xmax = pMstr->pSspec->reqCount - 1; + + pSlave = pMstr->pHead; + while (pSlave != NULL) { +/*----------------------------------------------------------------------------- +* for the first channel: +* initialize a plot area; its fractional size depends on how many +* "sub-plots" there are +* plot a perimeter with grid lines +* for the other channels: +* initialize an overlapping plot area +* set a dashed line pattern (unless this is a mark or point plot) +* draw a "floating" Y axis +*----------------------------------------------------------------------------*/ + pSChan = pSlave->pSChan; + ymin = pSlave->originVal; + ymax = pSlave->extentVal; + if (pSlave->pArea != NULL) + pprAreaClose(pSlave->pArea); + pArea = pSlave->pArea = pprAreaOpen(pWin, xlo, ylo, xhi, yhi, + xmin, ymin, xmax, ymax, xNint, pSlave->nInt, charHt); + assertAlways(pArea != NULL); + pSlave->xFracLeft = xlo; + pSlave->xFracRight = xhi; + pSlave->yFracBot = ylo; + pSlave->yFracTop = yhi; + if (pSlave->fg != 0 && pMstr->noColor == 0) + pprAreaSetAttr(pSlave->pArea, PPR_ATTR_FG, 0, &pSlave->fg); + else if (pMstr->linePlot) { + if (dbr_type_is_ENUM(pSChan->dbrType)) + pprAreaSetAttr(pArea, PPR_ATTR_LINE_THICK, thick, NULL); + if (pSlave->lineKey > 1 || pMstr->noColor == 0) + pprAreaSetAttr(pArea, PPR_ATTR_KEYNUM, pSlave->lineKey, NULL); + } + else if (pMstr->noColor == 0) + pprAreaSetAttr(pArea, PPR_ATTR_COLORNUM, pSlave->lineKey, NULL); + if (drawAxis == 0) { + pprGrid(pArea); + pprAnnotX_wc(pArea, 0, xmin, xmax, xNint, 0, "", NULL, 0.); + } + pprAnnotY(pArea, offsetAnnotY, pSlave->originVal, pSlave->extentVal, + pSlave->nInt, drawAxis, + pSlave->pSChan->label, pSlave->ppAnnot, 90.); + if (pMstr->markPlot) + pprAnnotYMark(pArea, offsetAnnotY, pSlave->markNum); + offsetAnnotY += 6; + drawAxis = 1; /* draw an "auxiliary" axis next time */ + pSlave = pSlave->pNext; + } +}