diff --git a/v2/libtpctacmod/histplot.c b/v2/libtpctacmod/histplot.c new file mode 100644 index 0000000000000000000000000000000000000000..c22bbe0551f1c87dd4e6ff8f060f733de2eaef9f --- /dev/null +++ b/v2/libtpctacmod/histplot.c @@ -0,0 +1,218 @@ +/// @file histplot.c +/// @brief Plot histogram in SVG format from dataset stored in TAC structure. +/// @author Vesa Oikonen +/// +/*****************************************************************************/ +#include "tpcclibConfig.h" +/*****************************************************************************/ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <time.h> +#include <string.h> +/*****************************************************************************/ +#include "tpcextensions.h" +#include "tpctac.h" +#include "libtpcsvg.h" +/*****************************************************************************/ +#include "tpctacmod.h" +/*****************************************************************************/ + +/*****************************************************************************/ +extern int SVG_INLINE; +/*****************************************************************************/ +/** SVG plot of histogram from data given in TAC data structure. + @return enum tpcerror (TPCERROR_OK when successful). + @sa tacPlotLineSVG, tacPlotFitSVG, mtgaPlotSVG + @author Vesa Oikonen + */ +int tacPlotHistogramSVG( + /** Pointer to TAC structure. Only the first dataset is used. */ + TAC *d, + /** String for plot main title, or "". */ + const char *main_title, + /** Start x value; NaN if determined from data. */ + const double x1, + /** End x value; NaN if determined from data. */ + const double x2, + /** Minimum y value; NaN if determined from data. */ + const double y1, + /** Maximum y value; NaN if determined from data. */ + const double y2, + /** SVG file name. */ + const char *fname, + /** Pointer to status data; obligatory. */ + TPCSTATUS *status +) { + if(status==NULL) return TPCERROR_FAIL; + if(status->verbose>1) + printf("%s(tac, '%s', %g, %g, %g, %g, '%s')\n", __func__, main_title, x1, x2, y1, y2, fname); + if(d==NULL || d->sampleNr<1 || d->tacNr<1) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_DATA); return(status->error);} + if(fname==NULL || strnlen(fname, 1)<1) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_FILENAME); return(status->error);} + + /* Determine the plot min and max x and y values */ + double minx, maxx, miny, maxy, tx1, tx2, ty1, ty2; + if(tacSampleXRange(d, &tx1, &tx2)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_XRANGE); return(status->error);} + minx=tx1; maxx=tx2; + if(minx>0.0) {double f=maxx-minx; minx-=0.05*f; if(minx<0.0) minx=0.0;} + if(isfinite(x1)) minx=x1; + if(isfinite(x2)) maxx=x2; + if(status->verbose>1) {printf(" minx := %g\n maxx := %g\n", minx, maxx); fflush(stdout);} + /* Determine the plot min and max y values inside the x range */ + if(tacYRangeInXRange(d, 0, minx, maxx, &ty1, &ty2, NULL, NULL, NULL, NULL)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_XRANGE); return(status->error);} +// if(ty1>0.0) ty1=0.0; +// if(ty2<0.0) ty2=0.0; + miny=ty1; maxy=ty2; + if(miny>0.0) {double f=maxy-miny; miny-=0.05*f; if(miny<0.0) miny=0.0;} + else if(maxy<0.0) maxy=0.0; + if(isfinite(y1)) miny=y1; + if(isfinite(y2)) maxy=y2; + if(status->verbose>1) {printf(" miny := %g\n maxy := %g\n", miny, maxy); fflush(stdout);} + + /* Calculate the axis ticks */ + struct svg_viewports viewports; svg_init_viewports(&viewports); + viewports.x.min=minx; viewports.x.max=maxx; + viewports.y.min=miny; viewports.y.max=maxy; + viewports.label_area_viewport.is=0; // no plot legends + if(svg_calculate_axes(&viewports, status->verbose-1)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_VALUE); return(status->error);} + + /* Set the plot window and window area sizes */ + if(svg_define_viewports(0, 0, strlen(main_title), 0, 0, 0, &viewports, status->verbose-1)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_VALUE); return(status->error);} + + /* Initiate graphics file */ + FILE *fp=svg_initiate(fname, 0, 0, &viewports, NULL, status->verbose-1); + if(fp==NULL) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_OPEN); return(status->error);} + + /* Put the graph titles into their own viewports */ + if(svg_create_main_title(fp, main_title, "", &viewports, NULL, status->verbose-2)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + /* Put the plot into its own viewport */ + if(svg_start_plot_viewport(fp, &viewports, NULL, status->verbose-2)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + /* Start coordinate area viewport */ + if(svg_start_coordinate_viewport(fp, &viewports, NULL, status->verbose-3)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + /* Write plot axes */ + if(svg_write_axes(fp, &viewports, NULL, status->verbose-3)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + + /* + * Draw the data + */ + char ilc[9], tmp[1024]; + if(SVG_INLINE) strcpy(ilc, "svg:"); else strcpy(ilc, ""); + /* Initiate the data object group */ + sprintf(tmp, "\n<%sg stroke=\"black\" stroke-width=\"25\" fill=\"black\">\n", ilc); + if(svg_write(fp, tmp, NULL, status->verbose-5)!=0) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + if(d->isframe==0) { // vertical lines + /* Open the path */ + sprintf(tmp, "<%spath fill=\"none\" d=\"", ilc); + svg_write(fp, tmp, NULL, status->verbose-5); + for(int i=0; i<d->sampleNr; i++) { + if(!isfinite(d->x[i]) || !isfinite(d->c[0].y[i])) continue; + double nx1, ny1, nx2, ny2; + nx1=nx2=viewports.x.origo+d->x[i]*viewports.x.scale; + ny1=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*0.0); + ny2=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*d->c[0].y[i]); + /* Do not plot if x is outside viewport */ + if(nx1<0 && nx2<=0) continue; + if(nx1>viewports.coordinate_area_viewport.w+1 || nx1>viewports.coordinate_area_viewport.w+1) + continue; + /* Do not plot if both y's are outside viewport */ + if((ny1<0 || ny2>viewports.coordinate_area_viewport.h+1) && + (ny2<0 || ny1>viewports.coordinate_area_viewport.h+1)) continue; + /* Set y limit to the viewport border */ + if(ny1<0) ny1=0.0; else if(ny2<0) ny2=0.0; + if(ny1>viewports.coordinate_area_viewport.h+1) ny1=viewports.coordinate_area_viewport.h+1; + else if(ny2>viewports.coordinate_area_viewport.h+1) ny2=viewports.coordinate_area_viewport.h+1; + /* Draw */ + sprintf(tmp, " M%.0f %.0f L%.0f %.0f", nx1, ny1, nx2, ny2); + svg_write(fp, tmp, NULL, status->verbose-5); + } + /* Close the path */ + strcpy(tmp, "\" />\n"); + svg_write(fp, tmp, NULL, status->verbose-5); + } else { // rectangles + for(int i=0; i<d->sampleNr; i++) { + if(!isfinite(d->x[i]) || !isfinite(d->c[0].y[i])) continue; + double nx1, ny1, nx2, ny2; + nx1=viewports.x.origo+d->x1[i]*viewports.x.scale; + nx2=viewports.x.origo+d->x2[i]*viewports.x.scale; + ny1=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*0.0); + ny2=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*d->c[0].y[i]); + /* Do not plot if x is outside viewport */ + if(nx1<0 && nx2<=0) continue; + if(nx1>viewports.coordinate_area_viewport.w+1 || nx1>viewports.coordinate_area_viewport.w+1) + continue; + /* Do not plot if both y's are outside viewport */ + if((ny1<0 || ny2>viewports.coordinate_area_viewport.h+1) && + (ny2<0 || ny1>viewports.coordinate_area_viewport.h+1)) continue; + /* Set y limit to the viewport border */ + if(ny1<0) ny1=0.0; else if(ny2<0) ny2=0.0; + if(ny1>viewports.coordinate_area_viewport.h+1) ny1=viewports.coordinate_area_viewport.h+1; + else if(ny2>viewports.coordinate_area_viewport.h+1) ny2=viewports.coordinate_area_viewport.h+1; + if(ny1>ny2) {double f=ny1; ny1=ny2; ny2=f;} + /* Draw */ + sprintf(tmp, " <rect x=\"%.0f\" y=\"%.0f\" width=\"%.0f\" height=\"%.0f\" />\n", + nx1, ny1, nx2-nx1, fabs(ny2-ny1)); + svg_write(fp, tmp, NULL, status->verbose-5); + } + } + /* Close the data object group */ + sprintf(tmp, "</%sg>\n", ilc); + if(svg_write(fp, tmp, NULL, status->verbose-5)!=0) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + + /* Close the coordinate viewport */ + if(svg_end_coordinate_viewport(fp, NULL, status->verbose-1)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + + /* Write the axis ticks */ + if(svg_write_xticks(fp, &viewports, NULL, status->verbose-3)!=0 || + svg_write_yticks(fp, &viewports, NULL, status->verbose-3)!=0) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + + /* Close the plot viewport */ + if(svg_end_plot_viewport(fp, NULL, status->verbose-2)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + + /* Close the SVG file */ + if(svg_close(fp, NULL, status->verbose-1)) { + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE); + fclose(fp); return(status->error); + } + + if(status->verbose>0) fprintf(stdout, " written file %s\n", fname); + + statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK); + return(TPCERROR_OK); +} +/*****************************************************************************/ + +/*****************************************************************************/ diff --git a/v2/libtpctacmod/test_histplot.c b/v2/libtpctacmod/test_histplot.c new file mode 100644 index 0000000000000000000000000000000000000000..0cee783d377a03874a290b33e6fe6fabf39d6c08 --- /dev/null +++ b/v2/libtpctacmod/test_histplot.c @@ -0,0 +1,111 @@ +/*****************************************************************************/ +#include "tpcclibConfig.h" +/*****************************************************************************/ +#include "unistd.h" +/*****************************************************************************/ +#include "tpcextensions.h" +#include "test_tpctacmod.h" +/*****************************************************************************/ + +/*****************************************************************************/ +int test_tacPlotHistogramSVG( + TPCSTATUS *status +) { + int verbose=0; if(status!=NULL) verbose=status->verbose; + statusSet(status, __func__, __FILE__, __LINE__, 0); + if(verbose>0) { + printf("\n=====================================\n"); + printf("\n%s\n", __func__); + printf("\n=====================================\n"); + fflush(stdout); + } + + int ret; + TAC tac; tacInit(&tac); + char fname[128]; strcpy(fname, ""); + + + if(verbose>1) {printf("\n testing with NULL data.\n"); fflush(stdout);} + strcpy(fname, "tacplothistogram0.svg"); + ret=tacPlotHistogramSVG(NULL, NULL, 0.0, 0.0, 0.0, 0.0, fname, status); + if(ret==TPCERROR_OK) return(1); + if(access(fname, 0)!=-1) return(2); + if(verbose>1) printf("\n testing with empty data.\n"); + ret=tacPlotHistogramSVG(&tac, "Main title", 0.0, 1.0, 0.0, 1.0, fname, status); + if(ret==TPCERROR_OK) return(3); + if(access(fname, 0)!=-1) return(4); + + + + if(verbose>1) {printf("\n creating test data\n"); fflush(stdout);} + /* Allocate memory */ + ret=tacAllocate(&tac, 5, 1); if(ret!=TPCERROR_OK) return(10); + tac.tacNr=1; tac.sampleNr=5; + /* Set TAC information */ + tac.isframe=0; + /* Set region names */ + for(int i=0; i<tac.tacNr; i++) sprintf(tac.c[i].name, "tac%d", 1+i); + /* Set data contents */ + int ri=0, fi; + fi=0; tac.x1[fi]=0.0; tac.x2[fi]=2.0; tac.x[fi]=1.0; tac.c[ri].y[fi]=20.0; + fi=1; tac.x1[fi]=2.0; tac.x2[fi]=4.0; tac.x[fi]=3.0; tac.c[ri].y[fi]=10.0; + fi=2; tac.x1[fi]=4.0; tac.x2[fi]=6.0; tac.x[fi]=5.0; tac.c[ri].y[fi]=60.0; + fi=3; tac.x1[fi]=6.0; tac.x2[fi]=10.0; tac.x[fi]=8.0; tac.c[ri].y[fi]=40.0; + fi=4; tac.x1[fi]=10.; tac.x2[fi]=13.0; tac.x[fi]=11.5; tac.c[ri].y[fi]=50.0; + + + if(verbose>1) {printf("\n plotting test data with x\n"); fflush(stdout);} + strcpy(fname, "tacplothistogram1.svg"); (void)remove(fname); + tac.isframe=0; + ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status); + if(ret!=TPCERROR_OK) {tacFree(&tac); return(11);} + if(access(fname, 0)==-1) {tacFree(&tac); return(12);} + + if(verbose>1) {printf("\n plotting test data with x1 and x2\n"); fflush(stdout);} + strcpy(fname, "tacplothistogram2.svg"); (void)remove(fname); + tac.isframe=1; + ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status); + if(ret!=TPCERROR_OK) {tacFree(&tac); return(21);} + if(access(fname, 0)==-1) {tacFree(&tac); return(22);} + + for(fi=0; fi<tac.sampleNr; fi++) for(ri=0; ri<tac.tacNr; ri++) tac.c[ri].y[fi]*=-1.0; + + if(verbose>1) {printf("\n plotting negative test data with x1 and x2\n"); fflush(stdout);} + strcpy(fname, "tacplothistogram2n.svg"); (void)remove(fname); + tac.isframe=1; + ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status); + if(ret!=TPCERROR_OK) {tacFree(&tac); return(23);} + if(access(fname, 0)==-1) {tacFree(&tac); return(24);} + + if(verbose>1) {printf("\n plotting negative test data with x\n"); fflush(stdout);} + strcpy(fname, "tacplothistogram1n.svg"); (void)remove(fname); + tac.isframe=0; + ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status); + if(ret!=TPCERROR_OK) {tacFree(&tac); return(13);} + if(access(fname, 0)==-1) {tacFree(&tac); return(14);} + + for(fi=0; fi<tac.sampleNr; fi++) for(ri=0; ri<tac.tacNr; ri++) tac.c[ri].y[fi]+=35.0; + + if(verbose>1) {printf("\n plotting +/- test data with x\n"); fflush(stdout);} + strcpy(fname, "tacplothistogram1np.svg"); (void)remove(fname); + tac.isframe=0; + ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status); + if(ret!=TPCERROR_OK) {tacFree(&tac); return(15);} + if(access(fname, 0)==-1) {tacFree(&tac); return(16);} + + if(verbose>1) {printf("\n plotting +/- test data with x1 and x2\n"); fflush(stdout);} + strcpy(fname, "tacplothistogram2np.svg"); (void)remove(fname); + tac.isframe=1; + ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status); + if(ret!=TPCERROR_OK) {tacFree(&tac); return(25);} + if(access(fname, 0)==-1) {tacFree(&tac); return(26);} + + + fflush(stdout); fflush(stderr); + tacFree(&tac); + statusSet(status, __func__, __FILE__, __LINE__, 0); + return(0); +} +/*****************************************************************************/ + +/*****************************************************************************/