libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmGnuPlot.c 29KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmPrefix.h"
  4. #include "cmGlobal.h"
  5. #include "cmRpt.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmGnuPlot.h"
  9. #include <errno.h>
  10. #include <signal.h>
  11. #include <sys/wait.h>
  12. #include <unistd.h> // read/write/close
  13. enum
  14. {
  15. kX_PdFl = 0x01, // the data set contains explicit x coordinates
  16. kY_PdFl = 0x02, // the data set contains explicit y coordinates
  17. kZ_PdFl = 0x04, // the data set contains explicit z coordinates
  18. kImpulse_PdFl = 0x08, // plot using the gnuplot impulse style
  19. kInvalidLineId = -2,
  20. kSolidLineId = -1,
  21. kDashedLineId = 0
  22. };
  23. typedef struct
  24. {
  25. unsigned flags; // see kXXX_PdFl
  26. char* legendStrH; // plot legend label for this data set
  27. char* colorStrH; // string containing a gnuplot color spec
  28. double* xyzMtxPtr; // data to be plotted contained in a column major mtx with 1,2, or 3 columns
  29. unsigned rn; //
  30. unsigned cn; //
  31. int lineId; // gnuplot line style id
  32. int pointId; // gnuplot point style id
  33. } cmPlotData;
  34. //----------------------------------------------------------------------------------------------------------
  35. enum
  36. {
  37. kTitleIdx = 0,
  38. kXLabelIdx,
  39. kYLabelIdx,
  40. kZLabelIdx,
  41. kPlotStrCnt
  42. };
  43. enum
  44. {
  45. kXMinRangeIdx,
  46. kXMaxRangeIdx,
  47. kYMinRangeIdx,
  48. kYMaxRangeIdx,
  49. kZMinRangeIdx,
  50. kZMaxRangeIdx,
  51. kRangeCnt
  52. };
  53. // There is one cmPlot per sub-plot. These records are held in cmPlotPage.plotPtrArray
  54. typedef struct
  55. {
  56. cmChar_t* strArray[ kPlotStrCnt ]; // an array of various labels and titles
  57. double range[ kRangeCnt ]; // a set of range limits for each dimension (used to automatically fill in coord values when they are not explicitely given)
  58. unsigned rowIdx; // the plot page row index of this sub-plot
  59. unsigned colIdx; // the plot page col index of this sub-plot
  60. cmPlotData** dataPtrArray; // pointer to data sets containing data for this sub-plot
  61. unsigned dataCnt;
  62. } cmPlot;
  63. //----------------------------------------------------------------------------------------------------------
  64. // The plotter contains a single cmPlotPage (pointed to by _cmpp).
  65. typedef struct
  66. {
  67. unsigned rowCnt; // number of rows of sub-plots
  68. unsigned colCnt; // number of columns of sub-plots
  69. cmChar_t* titleStrH; // page title
  70. int pipeFd[2]; // communication pipe with gnuplot process
  71. int pid; // process id of the gnuplot process
  72. cmPlot** plotPtrArray; // vector of sub-plots
  73. unsigned plotCnt; //
  74. unsigned curPlotIdx; // the sub-plot currently receiving plotting commands and data
  75. } cmPlotPage;
  76. cmPlotPage _cmPlotPage = { 0,0,NULL,{-1,-1},-1,NULL,0,cmInvalidIdx};
  77. cmPlotPage* _cmpp = NULL;
  78. void _cmPrintf( int fd, const char* fmt, ... )
  79. {
  80. const int bufCnt = 255;
  81. char buf[bufCnt+1];
  82. buf[bufCnt]='\0';
  83. va_list vl;
  84. va_start(vl,fmt);
  85. int n = vsnprintf(buf,bufCnt,fmt,vl);
  86. assert( n < 255 );
  87. write(fd,buf,n);
  88. va_end(vl);
  89. }
  90. // unexpected event signal handler
  91. void cmPlotSignalHandler( int sig )
  92. {
  93. switch( sig )
  94. {
  95. case SIGCHLD:
  96. if( _cmpp != NULL )
  97. _cmpp->pid = -1;
  98. break;
  99. case SIGBUS:
  100. case SIGSEGV:
  101. case SIGTERM:
  102. cmPlotFinalize();
  103. break;
  104. }
  105. }
  106. unsigned _cmPlotError( cmPlotPage* p, unsigned rc, bool sysErrFl, const char* fmt, ... )
  107. {
  108. va_list vl;
  109. va_start(vl,fmt);
  110. fprintf(stderr,"cmPlot Error:");
  111. vfprintf(stderr,fmt,vl);
  112. if( sysErrFl )
  113. fprintf(stderr," System Msg:%s\n",strerror(errno));
  114. va_end(vl);
  115. return rc;
  116. }
  117. //----------------------------------------------------------------------------------------------------------
  118. void _cmPlotDataInit( cmPlotData* p )
  119. {
  120. p->flags = 0;
  121. p->legendStrH = NULL;
  122. p->colorStrH = NULL;
  123. p->xyzMtxPtr = NULL;
  124. p->rn = 0;
  125. p->cn = 0;
  126. p->lineId = kInvalidLineId;
  127. p->pointId = kInvalidPlotPtId;
  128. }
  129. void _cmPlotDataCons( cmPlotData* p, unsigned flags, const char* legendStr, const char* colorStr, double* mtxPtr, unsigned rn, unsigned cn, unsigned styleFlags )
  130. {
  131. p->flags = flags + (cmIsFlag(styleFlags,kImpulsePlotFl) ? kImpulse_PdFl : 0);
  132. p->legendStrH = cmMemAllocStr(legendStr);
  133. p->colorStrH = cmMemAllocStr(colorStr);
  134. p->xyzMtxPtr = mtxPtr;
  135. p->rn = rn;
  136. p->cn = cn;
  137. p->lineId = ((styleFlags & kPlotLineMask) >> kPlotLineShift) - 2; // convert from the interface style flags to gnuplot id's
  138. p->pointId = styleFlags & kPlotPtMask;
  139. }
  140. void _cmPlotDataFree( cmPlotData* p )
  141. {
  142. cmMemPtrFree(&p->legendStrH);
  143. cmMemPtrFree(&p->colorStrH);
  144. //cmDM_Free(&p->xyzMtxPtr);
  145. cmMemPtrFree(&p->xyzMtxPtr);
  146. }
  147. /*
  148. bool _cmPlotDataFreeFE( unsigned i, cmPlotData* p, void *vp )
  149. {
  150. _cmPlotDataFree(p);
  151. return true;
  152. }
  153. */
  154. //----------------------------------------------------------------------------------------------------------
  155. void _cmPlotInit( cmPlot* p )
  156. {
  157. unsigned i;
  158. for(i=0; i<kPlotStrCnt; ++i)
  159. p->strArray[i] = NULL;
  160. for(i=0; i<kRangeCnt; ++i)
  161. p->range[i] = 0;
  162. p->rowIdx = cmInvalidIdx;
  163. p->colIdx = cmInvalidIdx;
  164. p->dataPtrArray = NULL;
  165. p->dataCnt = 0;
  166. }
  167. void _cmPlotCons( cmPlot* p, unsigned ri, unsigned ci )
  168. {
  169. p->rowIdx = ri;
  170. p->colIdx = ci;
  171. assert( p->dataPtrArray == NULL );
  172. //p->dataPtrArray = cmPlotDataVect_AllocEmpty();
  173. }
  174. void _cmPlotInsertData( cmPlot* p, cmPlotData* rp)
  175. {
  176. cmPlotData* nrp = cmMemAlloc(cmPlotData,1);
  177. *nrp = *rp;
  178. p->dataPtrArray = cmMemResizeP( cmPlotData*, p->dataPtrArray, p->dataCnt + 1 );
  179. p->dataPtrArray[ p->dataCnt ] = nrp;
  180. ++p->dataCnt;
  181. }
  182. void _cmPlotClearData( cmPlot* p )
  183. {
  184. unsigned i;
  185. // release the strings
  186. for(i=0; i<kPlotStrCnt; ++i)
  187. cmMemPtrFree(&p->strArray[i]);
  188. // release the plot data
  189. for(i=0; i<p->dataCnt; ++i)
  190. {
  191. _cmPlotDataFree( p->dataPtrArray[i] );
  192. cmMemPtrFree( &p->dataPtrArray[i] );
  193. }
  194. // set the data cnt to 0
  195. p->dataCnt = 0;
  196. }
  197. void _cmPlotFree( cmPlot* p )
  198. {
  199. _cmPlotClearData(p);
  200. cmMemPtrFree(&p->dataPtrArray);
  201. }
  202. //----------------------------------------------------------------------------------------------------------
  203. void _cmPlotPageInit( cmPlotPage* rp )
  204. {
  205. rp->rowCnt = 0;
  206. rp->colCnt = 0;
  207. rp->titleStrH = NULL;
  208. rp->pipeFd[0] = -1;
  209. rp->pipeFd[1] = -1;
  210. rp->pid = -1;
  211. rp->plotPtrArray = NULL;
  212. rp->curPlotIdx = cmInvalidIdx;
  213. }
  214. cmRC_t _cmPlotPageCons( cmPlotPage* rp, int pid, int inFd, int outFd, const char* terminalStr )
  215. {
  216. cmRC_t rc = kOkPlRC;
  217. rp->pid = pid;
  218. rp->pipeFd[0] = inFd;
  219. rp->pipeFd[1] = outFd;
  220. rp->plotPtrArray = NULL; //cmPlotVect_AllocEmpty();
  221. if(terminalStr != NULL )
  222. _cmPrintf( outFd, "set terminal %s\n",terminalStr );
  223. return rc;
  224. }
  225. void _cmPlotPageSetup( cmPlotPage* rp, const char* title, unsigned rowCnt, unsigned colCnt )
  226. {
  227. unsigned i,ri, ci;
  228. rp->titleStrH = cmMemResizeStr(rp->titleStrH,title); // acStringAssign(&rp->titleStrH,title);
  229. rp->rowCnt = rowCnt;
  230. rp->colCnt = colCnt;
  231. rp->curPlotIdx = rowCnt*colCnt > 0 ? 0 : cmInvalidIdx;
  232. // free any resources held by each plot and empty the plot array
  233. for(i=0; i<rp->plotCnt; ++i)
  234. {
  235. _cmPlotFree(rp->plotPtrArray[i]);
  236. cmMemPtrFree( &rp->plotPtrArray[i] );
  237. }
  238. rp->plotCnt = 0;
  239. // insert rowCnt*colCnt blank plot records
  240. // allocate the plotVect[]
  241. rp->plotPtrArray = cmMemResizeZ( cmPlot*, rp->plotPtrArray, rowCnt*colCnt );
  242. rp->plotCnt = rowCnt * colCnt;
  243. // initialize each cmPlot record
  244. for(ri=0,i=0; ri<rowCnt; ++ri)
  245. for(ci=0; ci<colCnt; ++ci,++i)
  246. {
  247. rp->plotPtrArray[i] = cmMemAllocZ(cmPlot,1);
  248. _cmPlotInit( rp->plotPtrArray[i]);
  249. _cmPlotCons( rp->plotPtrArray[i], ri, ci);
  250. }
  251. }
  252. cmRC_t _cmPlotPageFree( cmPlotPage* rp )
  253. {
  254. unsigned i;
  255. cmRC_t rc = kOkPlRC;
  256. cmMemPtrFree(&rp->titleStrH);
  257. //acStringDelete( &rp->titleStrH );
  258. // if the plot process was successfully started - stop it here
  259. if( rp->pid > 0)
  260. {
  261. int rc;
  262. kill(rp->pid,SIGKILL);
  263. wait(&rc);
  264. rp->pid = -1;
  265. }
  266. // close the pipe input to the plot process
  267. for(i=0; i<2; ++i)
  268. {
  269. if( rp->pipeFd[i] != -1 )
  270. if( close(rp->pipeFd[i]) == -1 )
  271. rc = _cmPlotError(rp,kPipeCloseFailedPlRC,true,"Pipe %i close() failed.",i);
  272. rp->pipeFd[i] = -1;
  273. }
  274. // deallocate the plot array
  275. if( rp->plotPtrArray != NULL )
  276. {
  277. for(i=0; i<rp->plotCnt; ++i)
  278. {
  279. _cmPlotFree(rp->plotPtrArray[i] );
  280. cmMemPtrFree(&rp->plotPtrArray[i]);
  281. }
  282. cmMemPtrFree(&rp->plotPtrArray);
  283. rp->plotCnt = 0;
  284. }
  285. return rc;
  286. }
  287. //----------------------------------------------------------------------------------------------------------
  288. cmRC_t cmPlotInitialize( const char* terminalStr )
  289. {
  290. cmRC_t rc = kOkPlRC;
  291. // if this is the first call to this function
  292. if( _cmpp == NULL )
  293. {
  294. struct sigaction sa;
  295. _cmpp = &_cmPlotPage;
  296. _cmPlotPageInit(_cmpp);
  297. sa.sa_handler = cmPlotSignalHandler;
  298. sigemptyset(&sa.sa_mask);
  299. if (sigaction(SIGBUS, &sa, NULL) == -1)
  300. {
  301. rc = _cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGBUS) failed.");
  302. goto errLabel;
  303. }
  304. sa.sa_handler = cmPlotSignalHandler;
  305. sigemptyset(&sa.sa_mask);
  306. if (sigaction(SIGSEGV, &sa, NULL) == -1)
  307. {
  308. rc = _cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGSEGV) failed.");
  309. goto errLabel;
  310. }
  311. sa.sa_handler = cmPlotSignalHandler;
  312. sigemptyset(&sa.sa_mask);
  313. if (sigaction(SIGTERM, &sa, NULL) == -1)
  314. {
  315. rc = _cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGTERM) failed.");
  316. goto errLabel;
  317. }
  318. sa.sa_handler = cmPlotSignalHandler;
  319. sigemptyset(&sa.sa_mask);
  320. if (sigaction(SIGCHLD, &sa, NULL) == -1)
  321. {
  322. rc = _cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGCHLD) failed.");
  323. goto errLabel;
  324. }
  325. }
  326. else // if this is the second or greater call to this function
  327. {
  328. if((rc = cmPlotFinalize()) != kOkPlRC )
  329. return rc;
  330. }
  331. int pipeFD[2];
  332. // create the pipe
  333. if( pipe(pipeFD) == -1 )
  334. {
  335. rc = _cmPlotError(_cmpp,kPipeFailedPlRC,true,"pipe() failed.");
  336. goto errLabel;
  337. }
  338. int pid;
  339. // create the child proces
  340. switch( pid = fork() )
  341. {
  342. case -1:
  343. printf("Error\n");
  344. rc = _cmPlotError(_cmpp,kForkFailedPlRC,true,"fork() failed.");
  345. goto errLabel;
  346. break;
  347. case 0:
  348. close(fileno(stdin)); // close stdin
  349. dup(pipeFD[0]); // replace stdin with the pipe input
  350. execlp("gnuplot","gnuplot",NULL); // start gnuplot
  351. // under normal conditions execlp() should never return
  352. rc = _cmPlotError(_cmpp,kExecFailedPlRC,true,"exec() failed.");
  353. goto errLabel;
  354. break;
  355. default:
  356. // normal return for parent process
  357. rc = _cmPlotPageCons(_cmpp,pid,pipeFD[0],pipeFD[1], terminalStr );
  358. break;
  359. }
  360. return rc;
  361. errLabel:
  362. cmPlotFinalize();
  363. return rc;
  364. }
  365. cmRC_t cmPlotFinalize()
  366. {
  367. cmRC_t rc = kOkPlRC, rc0;
  368. struct sigaction sa;
  369. if( _cmpp == NULL )
  370. return kOkPlRC;
  371. // install some unexpected event signal handlers to clean up if the application
  372. // process crashes prior to calling cmPlotFinalize(). This will prevent unconnected gnuplot
  373. // processes from being left in the process list.
  374. sa.sa_handler = SIG_DFL;
  375. sigemptyset(&sa.sa_mask);
  376. if( sigaction( SIGCHLD,&sa,NULL) == -1 )
  377. rc =_cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGCHLD) restore failed.");
  378. if( sigaction( SIGTERM,&sa,NULL) == -1 )
  379. rc =_cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGTERM) restore failed.");
  380. if( sigaction( SIGSEGV,&sa,NULL) == -1 )
  381. rc =_cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGSEGV) restore failed.");
  382. if( sigaction( SIGBUS,&sa,NULL) == -1 )
  383. rc =_cmPlotError(_cmpp,kSignalFailedPlRC,true,"sigaction(SIGBUS) restore failed.");
  384. // restore the child termination signal handler
  385. signal(SIGCHLD,SIG_DFL);
  386. rc0 = _cmPlotPageFree(_cmpp);
  387. return rc==kOkPlRC ? rc0 : rc;
  388. }
  389. cmRC_t cmPlotInitialize2( const char* terminalStr, const char* title, unsigned rowCnt, unsigned colCnt )
  390. {
  391. cmRC_t rc;
  392. if((rc = cmPlotInitialize(terminalStr)) != cmOkRC )
  393. return rc;
  394. return cmPlotSetup(title,rowCnt,colCnt);
  395. }
  396. cmRC_t cmPlotSetup( const char* title, unsigned rowCnt, unsigned colCnt )
  397. {
  398. _cmPlotPageSetup( _cmpp,title,rowCnt,colCnt);
  399. return kOkPlRC;
  400. }
  401. // called to locate a cmPlot given plot row/col indexes
  402. unsigned _cmRowColToPlotIndex( cmPlotPage* p, unsigned ri, unsigned ci )
  403. {
  404. unsigned i;
  405. for(i=0; i<_cmpp->plotCnt; ++i)
  406. if( _cmpp->plotPtrArray[i]->rowIdx==ri && _cmpp->plotPtrArray[i]->colIdx==ci )
  407. return i;
  408. _cmPlotError(_cmpp,kPlotNotFoundPlRC,false,"No plot exists at row:%i and col:%i\n",ri,ci);
  409. return cmInvalidIdx;
  410. }
  411. cmRC_t cmPlotSelectSubPlot( unsigned ri, unsigned ci )
  412. {
  413. unsigned i;
  414. if((i= _cmRowColToPlotIndex( _cmpp, ri, ci ) ) != cmInvalidIdx )
  415. _cmpp->curPlotIdx = i;
  416. return kOkPlRC;
  417. }
  418. cmPlot* _cmPlotGetCurPlotPtr()
  419. {
  420. if( _cmpp->curPlotIdx == cmInvalidIdx )
  421. {
  422. _cmPlotError(_cmpp,kNoCurPlotPlRC,false,"No plot exists for the current page.");
  423. assert(0);
  424. return NULL;
  425. };
  426. assert( _cmpp->curPlotIdx < _cmpp->plotCnt );
  427. cmPlot* p = _cmpp->plotPtrArray[_cmpp->curPlotIdx];
  428. assert( p != NULL );
  429. return p;
  430. }
  431. cmRC_t cmPlotSetLabels( const char* titleStr, const char* xLabelStr, const char* yLabelStr, const char* zLabelStr )
  432. {
  433. cmPlot* p = _cmPlotGetCurPlotPtr();
  434. p->strArray[kTitleIdx] = cmMemAllocStr( titleStr ); // acStringAssign( &p->strArray[ kTitleIdx ], titleStr );
  435. p->strArray[kXLabelIdx] = cmMemAllocStr( xLabelStr ); // acStringAssign( &p->strArray[ kXLabelIdx ], xLabelStr );
  436. p->strArray[kYLabelIdx] = cmMemAllocStr( yLabelStr ); // acStringAssign( &p->strArray[ kYLabelIdx ], yLabelStr );
  437. p->strArray[kZLabelIdx] = cmMemAllocStr( zLabelStr ); //acStringAssign( &p->strArray[ kZLabelIdx ], zLabelStr );
  438. return kOkPlRC;
  439. }
  440. cmRC_t cmPlotSetRange( double xMin, double xMax, double yMin, double yMax, double zMin, double zMax )
  441. {
  442. cmPlot* p = _cmPlotGetCurPlotPtr();
  443. if( xMin != 0 || xMax != 0 )
  444. {
  445. p->range[ kXMinRangeIdx ] = xMin;
  446. p->range[ kXMaxRangeIdx ] = xMax;
  447. }
  448. if( yMin != 0 || yMax != 0 )
  449. {
  450. p->range[ kYMinRangeIdx ] = yMin;
  451. p->range[ kYMaxRangeIdx ] = yMax;
  452. }
  453. if( zMin != 0 || zMax != 0 )
  454. {
  455. p->range[ kZMinRangeIdx ] = zMin;
  456. p->range[ kZMaxRangeIdx ] = zMax;
  457. }
  458. return kOkPlRC;
  459. }
  460. void cmPlot2DLine( const float* x, const float* y, unsigned n, const char* color, int lineType, int lineWidth, int pointType )
  461. {
  462. //char cmd[] = "reset\nset size 1,1\nset origin 0,0\nset multiplot layout 1,1\nplot '-' binary array=16 format='%float' origin=(16,0) dx=10 using 1 with lines\n";
  463. char cmd[] = "reset\nset size 1,1\nset origin 0,0\nset multiplot layout 1,1\nplot '-' binary record=16 format='%float' using 1:2 with lines\n";
  464. char cmd2[] = "unset multiplot\n";
  465. unsigned i;
  466. int rc = write(_cmpp->pipeFd[1],cmd,strlen(cmd));
  467. //int rc = fprintf(fp,"%s",cmd);
  468. for( i=0; i<n; ++i)
  469. {
  470. write(_cmpp->pipeFd[1],x+i,sizeof(float));
  471. write(_cmpp->pipeFd[1],y+i,sizeof(float));
  472. }
  473. write(_cmpp->pipeFd[1],cmd2,strlen(cmd2));
  474. printf("%i %s",rc,cmd);
  475. }
  476. void cmPlot2DLine1( const float* x, const float* y, unsigned n, const char* color, int lineType, int lineWidth, int pointType )
  477. {
  478. //char cmd[] = "reset\nset size 1,1\nset origin 0,0\nset multiplot layout 1,1\nplot '/home/kevin/src/gnuplot/data1.bin' binary record=16x2\n";
  479. char cmd[] = "reset\nset size 1,1\nset origin 0,0\nset multiplot layout 1,1\nplot '-' binary record=16 format='%float' using 1:2 with lines\n";
  480. char cmd2[] = "unset multiplot\n";
  481. unsigned i;
  482. int rc = write(_cmpp->pipeFd[1],cmd,strlen(cmd));
  483. //int rc = fprintf(fp,"%s",cmd);
  484. for( i=0; i<n; ++i)
  485. {
  486. write(_cmpp->pipeFd[1],x+i,sizeof(float));
  487. write(_cmpp->pipeFd[1],y+i,sizeof(float));
  488. }
  489. write(_cmpp->pipeFd[1],cmd2,strlen(cmd2));
  490. printf("%i %s",rc,cmd);
  491. }
  492. /// Clear the current current subplot
  493. cmRC_t cmPlotClear()
  494. {
  495. cmPlot* p = _cmPlotGetCurPlotPtr();
  496. _cmPlotClearData(p);
  497. return kOkPlRC;
  498. }
  499. void _cmPlotInsertDataRecd( cmPlot* p, unsigned flags, const char* legendStr, const char* colorStr, unsigned styleFlags, double* mtxPtr, unsigned rn, unsigned cn )
  500. {
  501. cmPlotData r;
  502. _cmPlotDataInit(&r);
  503. _cmPlotDataCons(&r,flags,legendStr,colorStr,mtxPtr,rn,cn,styleFlags);
  504. _cmPlotInsertData(p,&r);
  505. }
  506. cmRC_t cmPlotLineF( const char* legendStr, const float* x, const float* y, const float* z, unsigned n, const char* colorStr, unsigned styleFlags )
  507. {
  508. cmPlot* p = _cmPlotGetCurPlotPtr();
  509. unsigned flags = 0;
  510. unsigned rn = 0;
  511. unsigned ri = 0;
  512. const float* array[3] = {x,y,z};
  513. unsigned i;
  514. // determine which data vectors were provided
  515. for(i=0; i<3; ++i)
  516. if( array[i] != NULL )
  517. {
  518. ++rn;
  519. switch(i)
  520. {
  521. case 0: flags = cmSetFlag(flags,kX_PdFl); break;
  522. case 1: flags = cmSetFlag(flags,kY_PdFl); break;
  523. case 2: flags = cmSetFlag(flags,kZ_PdFl); break;
  524. default:
  525. {assert(0);}
  526. }
  527. }
  528. // create the matrix to hold the data
  529. double* mtxPtr = cmMemAlloc(double,rn*n);
  530. // copy the data into the matrix
  531. for(i=0; i<3; ++i)
  532. if( array[i] != NULL )
  533. {
  534. unsigned ci;
  535. for(ci=0; ci<n; ++ci)
  536. mtxPtr[ ci*rn + ri ] = array[i][ci];
  537. ++ri;
  538. }
  539. // store the a record to represent this line
  540. _cmPlotInsertDataRecd(p, flags, legendStr, colorStr, styleFlags, mtxPtr, rn, n );
  541. return kOkPlRC;
  542. }
  543. cmRC_t cmPlotLineD( const char* legendStr, const double* x, const double* y, const double* z, unsigned n, const char* colorStr, unsigned styleFlags )
  544. {
  545. cmPlot* p = _cmPlotGetCurPlotPtr();
  546. unsigned flags = 0;
  547. unsigned rn = 0;
  548. unsigned ri = 0;
  549. const double* array[3] = { x,y,z};
  550. unsigned i;
  551. // determine wihc data vectors were provided
  552. for(i=0; i<3; ++i)
  553. if( array[i] != NULL )
  554. {
  555. ++rn;
  556. switch(i)
  557. {
  558. case 0: flags = cmSetFlag(flags,kX_PdFl); break;
  559. case 1: flags = cmSetFlag(flags,kY_PdFl); break;
  560. case 2: flags = cmSetFlag(flags,kZ_PdFl); break;
  561. default:
  562. {assert(0);}
  563. }
  564. }
  565. // create the matrix to hold the data
  566. double* mtxPtr = cmMemAlloc(double,rn*n);
  567. // copy the data into the matrix
  568. for(i=0; i<3; ++i)
  569. if( array[i] != NULL )
  570. {
  571. unsigned ci;
  572. for(ci=0; ci<n; ++ci)
  573. mtxPtr[ ci*rn + ri ] = array[i][ci];
  574. ++ri;
  575. }
  576. // store the a record to represent this line
  577. _cmPlotInsertDataRecd(p, flags, legendStr, colorStr, styleFlags, mtxPtr, rn, n );
  578. return kOkPlRC;
  579. }
  580. cmRC_t cmPlotLineMD( const double* x, const double* y, const double* z, unsigned rn, unsigned cn, unsigned styleFlags )
  581. {
  582. cmRC_t rc;
  583. unsigned i;
  584. for(i=0; i<cn; ++i)
  585. if((rc = cmPlotLineD( NULL, x==NULL ? NULL : x+(i*rn), y==NULL ? NULL : y+(i*rn), z==NULL ? NULL : z+(i*rn), rn, NULL, styleFlags )) != cmOkRC )
  586. return rc;
  587. return cmOkRC;
  588. }
  589. const double* _cmPrintData( int fd, unsigned i, const double* p, double minV, double fact )
  590. {
  591. if( p != NULL )
  592. _cmPrintf(fd,"%f ",*p++);
  593. else
  594. if( fact != 0 )
  595. {
  596. double v = minV + (fact * i );
  597. _cmPrintf(fd,"%f ",v);
  598. }
  599. return p;
  600. }
  601. cmRC_t _cmPlotDraw(int fd, bool printDataFl )
  602. {
  603. unsigned ri,ci,di;
  604. _cmPrintf(fd,"reset\n");
  605. _cmPrintf(fd,"set size 1,1\n");
  606. _cmPrintf(fd,"set origin 0,0\n");
  607. _cmPrintf(fd,"set multiplot layout %i,%i\n",_cmpp->rowCnt,_cmpp->colCnt);
  608. for(ri=0; ri<_cmpp->rowCnt; ++ri)
  609. for(ci=0; ci<_cmpp->colCnt; ++ci)
  610. {
  611. // get the plot at ri,ci
  612. unsigned plotIdx = _cmRowColToPlotIndex(_cmpp,ri,ci);
  613. assert( plotIdx != cmInvalidIdx );
  614. cmPlot* p = _cmpp->plotPtrArray[plotIdx];
  615. // get the count of data sets assigned to this plot
  616. unsigned dataCnt = p->dataCnt;
  617. if( dataCnt > 0 )
  618. {
  619. bool printPlotKeywordFl = false;
  620. // note which ranges are valid
  621. bool isXRangeFl = p->range[ kXMinRangeIdx ] != 0 || p->range[ kXMaxRangeIdx != 0 ];
  622. bool isYRangeFl = p->range[ kYMinRangeIdx ] != 0 || p->range[ kYMaxRangeIdx != 0 ];
  623. bool isZRangeFl = p->range[ kZMinRangeIdx ] != 0 || p->range[ kZMaxRangeIdx != 0 ];
  624. // set the plot title
  625. if( p->strArray[kTitleIdx] != NULL )
  626. _cmPrintf(fd,"set title '%s'\n",(p->strArray[kTitleIdx]));
  627. else
  628. {
  629. // if this is a one plot page use the page title as the plot title
  630. if( _cmpp->titleStrH != NULL && _cmpp->rowCnt==1 && _cmpp->colCnt == 1 )
  631. _cmPrintf(fd,"set title '%s'\n", _cmpp->titleStrH );
  632. }
  633. // set the plot x label
  634. if( p->strArray[kXLabelIdx] != NULL )
  635. _cmPrintf(fd,"set xlabel '%s'\n",(p->strArray[kXLabelIdx]));
  636. // set the plot y label
  637. if( p->strArray[kYLabelIdx] != NULL )
  638. _cmPrintf(fd,"set ylabel '%s'\n",(p->strArray[kYLabelIdx]));
  639. for(di=0; di<dataCnt; ++di)
  640. {
  641. cmPlotData* dp = p->dataPtrArray[di];
  642. unsigned eleCnt = dp->cn; //acDM_Cols(dp->xyzMtxPtr);
  643. unsigned dimCnt = dp->rn; //acDM_Rows(dp->xyzMtxPtr);
  644. if( eleCnt == 0 || dimCnt==0 )
  645. continue;
  646. // must defer printing the 'plot' command until we are sure there is a non-empty matrix to print
  647. if( printPlotKeywordFl == false )
  648. {
  649. _cmPrintf(fd,"plot ");
  650. printPlotKeywordFl = true;
  651. }
  652. bool useXRangeFl = (cmIsFlag(dp->flags,kX_PdFl)==false) && isXRangeFl;
  653. bool useYRangeFl = (cmIsFlag(dp->flags,kY_PdFl)==false) && isYRangeFl;
  654. bool useZRangeFl = (cmIsFlag(dp->flags,kZ_PdFl)==false) && isZRangeFl;
  655. bool useRangeFl = useXRangeFl | useYRangeFl | useZRangeFl;
  656. _cmPrintf(fd," '-' binary %s=%i format='%%double' ", (dimCnt==1) && useRangeFl ? "array":"record",eleCnt);
  657. if( (dimCnt == 1) && (useXRangeFl || useYRangeFl) )
  658. {
  659. _cmPrintf(fd," origin=(%f,%f) ", useXRangeFl ? p->range[ kXMinRangeIdx ] : 0, useYRangeFl ? p->range[ kYMinRangeIdx ] : 0);
  660. if( useXRangeFl )
  661. _cmPrintf(fd, " dx=%f ", (p->range[ kXMaxRangeIdx ] - p->range[ kXMinRangeIdx ]) / eleCnt );
  662. if( useYRangeFl )
  663. _cmPrintf(fd, " dy=%f ", (p->range[ kYMaxRangeIdx ] - p->range[ kYMinRangeIdx ]) / eleCnt );
  664. _cmPrintf(fd," using %i ", 1 );
  665. }
  666. else
  667. _cmPrintf(fd," using %i:%i ", dimCnt==1 ? 0 : 1, dimCnt==1 ? 1 : 2 );
  668. if( dp->legendStrH != NULL )
  669. _cmPrintf(fd," title '%s' ", dp->legendStrH);
  670. else
  671. _cmPrintf(fd, " notitle ");
  672. bool impulseFl = cmIsFlag(dp->flags,kImpulse_PdFl );
  673. if( impulseFl || (dp->lineId != kInvalidLineId) || (dp->pointId != kInvalidPlotPtId) )
  674. {
  675. _cmPrintf(fd," with ");
  676. if( impulseFl )
  677. _cmPrintf(fd,"impulses");
  678. else
  679. {
  680. if( dp->lineId != kInvalidLineId )
  681. _cmPrintf(fd,"lines");
  682. if( dp->pointId != kInvalidPlotPtId )
  683. _cmPrintf(fd,"points pt %i ", dp->pointId);
  684. }
  685. if( dp->colorStrH != NULL )
  686. _cmPrintf(fd," lt rgb '%s' ", dp->colorStrH );
  687. }
  688. if( di+1 < dataCnt )
  689. _cmPrintf(fd,",");
  690. else
  691. {
  692. _cmPrintf(fd,"\n");
  693. // for each data set contained in this plot
  694. for(di=0; di<dataCnt; ++di)
  695. {
  696. cmPlotData* dp = p->dataPtrArray[di];
  697. //acDM* mp = dp->xyzMtxPtr;
  698. unsigned eleCnt = dp->cn; //acDM_Cols(mp);
  699. const double* ddp = dp->xyzMtxPtr; //acDM_ConstPtr(mp);
  700. // if we are printing the output to the console instead of sending it too gnuplot
  701. if( fd == fileno(stdout) )
  702. {
  703. if( printDataFl )
  704. {
  705. unsigned i = 0;
  706. for(i=0; i<eleCnt; ++i )
  707. ddp=_cmPrintData( fd, i,ddp, 0, 0 );
  708. }
  709. }
  710. else
  711. {
  712. // Note: each row contains a of the matrix contains the data for a given dimension
  713. // (e.g. x coords are in row 0, y coords are in row 1, etc). If the matrix contains
  714. // multiple rows then writing the matrix memory buffer out linearly will result
  715. // in interleaving the coordinates as: x0 y0 x1 y1 x2 y2 ....
  716. write(fd,ddp,dp->rn*eleCnt*sizeof(double));
  717. }
  718. }
  719. }
  720. } // if dataCnt > 0
  721. } // for ci
  722. } // for ri
  723. /*
  724. _cmPrintf(fd,"\n");
  725. for(ri=0; ri<_cmpp->rowCnt; ++ri)
  726. for(ci=0; ci<_cmpp->colCnt; ++ci)
  727. {
  728. // get the plot at ri,ci
  729. cmPlot* p = cmPlotVect_Ptr(_cmpp->plotPtrArray,_acRowColToPlotIndex(_cmpp,ri,ci));
  730. // get the count of data sets assigned to this plot
  731. unsigned dataCnt = cmPlotDataVect_Count(p->dataPtrArray);
  732. // for each data set contained in this plot
  733. for(di=0; di<dataCnt; ++di)
  734. {
  735. cmPlotData* dp = cmPlotDataVect_Ptr(p->dataPtrArray,di);
  736. acDM* mp = dp->xyzMtxPtr;
  737. unsigned eleCnt = acDM_Cols(mp);
  738. const double* ddp = acDM_ConstPtr(mp);
  739. // if we are printing the output to the console instead of sending it too gnuplot
  740. if( fd == fileno(stdout) )
  741. {
  742. if( printDataFl )
  743. {
  744. unsigned i = 0;
  745. for(i=0; i<eleCnt; ++i )
  746. ddp=_acPrintData( fd, i,ddp, 0, 0 );
  747. }
  748. }
  749. else
  750. {
  751. // Note: each row contains a of the matrix contains the data for a given dimension
  752. // (e.g. x coords are in row 0, y coords are in row 1, etc). If the matrix contains
  753. // multiple rows then writing the matrix memory buffer out linearly will result
  754. // in interleaving the coordinates as: x0 y0 x1 y1 x2 y2 ....
  755. write(fd,ddp,acDM_Rows(mp)*eleCnt*sizeof(double));
  756. }
  757. }
  758. }
  759. */
  760. _cmPrintf(fd,"\nunset multiplot\n");
  761. return kOkPlRC;
  762. }
  763. cmRC_t cmPlotDraw()
  764. {
  765. return _cmPlotDraw(_cmpp->pipeFd[1],false);
  766. }
  767. cmRC_t cmPlotPrint( bool printDataFl )
  768. {
  769. return _cmPlotDraw(fileno(stdout),printDataFl);
  770. }
  771. cmRC_t cmPlotDrawAndPrint( bool printDataFl )
  772. {
  773. cmPlotDraw();
  774. return _cmPlotDraw(fileno(stdout),printDataFl);
  775. }
  776. // ncl - min column value
  777. // nch - max column value
  778. // nrl - min row value
  779. // nrh - max row value
  780. int fwrite_matrix(FILE* fout, float**m, int nrl, int nrh, int ncl, int nch, float* row_title, float* column_title)
  781. {
  782. int j;
  783. float length;
  784. int col_length;
  785. int status;
  786. // calc the number of columns
  787. length = (float)(col_length = nch-ncl+1);
  788. printf("cols:%i %f\n",col_length,length);
  789. // write the number of columns
  790. if((status = fwrite((char*)&length,sizeof(float),1,fout))!=1)
  791. {
  792. fprintf(stderr,"fwrite 1 returned %d\n",status);
  793. return(0);
  794. }
  795. // write the column titles
  796. fwrite((char*)column_title,sizeof(float),col_length,fout);
  797. // write the row_title followed by the data on each line
  798. for(j=nrl; j<=nrh; j++)
  799. {
  800. fwrite( (char*)&row_title[j], sizeof(float), 1, fout);
  801. fwrite( (char*)(m[j]+ncl), sizeof(float), col_length,fout);
  802. printf("%i %li\n",j,ftell(fout));
  803. }
  804. return(1);
  805. }
  806. // Generate a 'matrix-binary' data file for use with: plot "data0.bin" binary
  807. void cmPlotWriteBinaryMatrix()
  808. {
  809. const char fn[] = "/home/kevin/src/gnuplot/data0.bin";
  810. unsigned rn = 2; // row cnt
  811. unsigned cn = 16; // col cnt
  812. float srate = 16;
  813. float d[rn*cn];
  814. //float* m[rn];
  815. float c[cn];
  816. //float r[rn];
  817. unsigned i;
  818. for(i=0; i<cn; ++i)
  819. {
  820. d[i] = (float)sin(2*M_PI*i/srate);
  821. d[cn + i] = (float)cos(2*M_PI*i/srate);
  822. c[i] = i;
  823. }
  824. //m[0] = d;
  825. //r[0] = 0;
  826. FILE* fp = fopen(fn,"wb");
  827. float fcn = cn;
  828. fwrite((char*)&fcn,sizeof(fcn),1,fp); // write the count of columns
  829. fwrite((char*)c,sizeof(float),cn,fp); // write the column labels
  830. for(i=0; i<rn; ++i)
  831. {
  832. fwrite((char*)&i,sizeof(float),1,fp); // write the row label
  833. fwrite((char*)(d +(i*cn)),sizeof(float),cn,fp); // write a data row
  834. }
  835. fclose(fp);
  836. }
  837. // Generate a 'matrix-binary' data file for use with: plot "data1.bin" binary record=16x2 using 1:2
  838. void cmPlotWriteBinaryGeneralExample()
  839. {
  840. const char fn[] = "/home/kevin/src/gnuplot/data1.bin";
  841. unsigned rn = 16; // row cnt
  842. unsigned cn = 2; // col cnt
  843. float srate = 16;
  844. float d[rn*cn];
  845. unsigned i;
  846. for(i=0; i<rn; ++i)
  847. {
  848. d[(i*cn)+0] = (float)cos(2*M_PI*i/srate);
  849. d[(i*cn)+1] = (float)sin(2*M_PI*i/srate);
  850. }
  851. FILE* fp = fopen(fn,"wb");
  852. fwrite((char*)d,sizeof(float),rn*cn,fp);
  853. fclose(fp);
  854. }