/* Draws the dynamic programming graphs and paths for given candidate pairings */ /* Last edited on 2015-01-20 16:45:12 by stolfilocal */ /* Output file names are: | | "XXX-NNNNN-grid.eps" if "-epsFormat" and "-perCandidate", | "XXX-AAAA-BBBB-grid.eps" if "-epsFormat" and "-perCurvePair", or | "XXX-grid.ps" otherwise | where XXX is the "-output" parameter, NNNNN is a candidate's index in the input file, and AAAA and BBBB are the ID numbers of two curves. */ #include #include #include #include #include #include #include #include TYPE Options = struct ??? { char *input; /* Input candidate file name. */ char *ideal; /* Ideal candidate file name, or "". */ char *output; /* Output candidate file (without ".???"). */ unsigned maxCands; /* Num candidates to plot. */ /* Plotting options */ bool_t perCandidate; /* TRUE = per candidate, FALSE = per curve pair. */ SegmentPair segments; /* Segments of interest, or empty segs. */ double cellSize; /* DP grid cell size in millimeters, or 0. */ double refGridLineWidth; /* Line width for reference grid (mm). */ Color refGridLineColor; /* Line color for reference grid (0-1 RGB). */ double candGridLineWidth; /* Line width for candidate grid (mm). */ Color candGridLineColor; /* Line color for candidate grid (0-1 RGB). */ double boxSize; /* Box size for fattened match pairs (cells). */ Color boxColor; /* Box color for fattened match pairs (mm). */ double matchLineWidth; /* Line width for match path (mm). */ Color matchLineColor; /* Line color for match path (0-1 RGB). */ double dotSize; /* Dot diameter for match pairs (mm). */ Color dotColor; /* Dot color for match pairs (0-1 RGB). */ /* Options for output file format and scale: */ pz_plot.DrawFmtOptions drawFmt; } ???; /* If "segments" is specified (meaning "cvx != (unsigned.ne - 1)"), only pairs belonging to the same curve pair as "segments" will be considered, and the plots will be scaled to fit those segments. (If either "tot" or "ns" is zero, the segment is understood to be the whole curve). If "segments" is not given, all candidates will be considered. The plot will be scaled to fit each individual candidate, if "perCandidate" is TRUE, or the full curves, if "perCandidate" is FALSE. In the last case, the file should be sorted by curve pair. If an "ideal" file is named, the candidates from that file are plotted too, before the "input" candidates. They are plotted with slightly different color, and with slightly larger line width and dot size. */ TYPE Color == PSPlot.Color; SegmentPair == pz_segment_pair; int main(int argc, char **argv ) VAR o := pz_get_options(int argc, char **argv); { fprintf(stderr, "pz_draw_cand_grids %s\n", o.input ); if (( o.maxCands > 0 )){ { /* with */ ??? cand = ReadCandidates(o.input, o.ideal)^; /* do */ DrawCandidates(cand, o);; };; } } /* Main */ pz_candidate.List *ReadCandidates( char *input, char *ideal ) /* Returns the concatenation of the input and ideal candidates, sorted by curve pair. The "mismatch" field is discarded and used to distinguish the ideal candidates ("mismatch > 0") from the ordinary ones ("mismatch < 0"). */ pz_candidate.List *DoRead( char *file ) { if (( Text.Empty(file) )){ return NEW(REF pz_candidate.List, 0) }else{ { /* with */ ??? cd = pz_candidate_read(open_read(file & ".can", TRUE)); /* do */ return cd.c; }; } } /* DoRead */ { { /* with */ ??? inputC = DoRead(input)^, inputN = (inputC.ne); ??? idealC = DoRead(ideal)^, idealN = (idealC.ne); ??? res = NEW(REF pz_candidate.List, inputN+idealN); ??? mergeC = res^; /* do */ SUBARRAY(mergeC, 0, inputN) = inputC; SUBARRAY(mergeC, inputN, idealN) = idealC; for (i = 0; i < (mergeC.ne ) ; i++){ { /* with */ ??? cand = mergeC[i]; /* do */ if (( i < inputN )){ cand.mismatch = +1.0e0 }else{ cand.mismatch = -1.0e0; }; }; }; return res; } } /* ReadCandidates */ void DrawCandidates( pz_candidate.List *cand, Options *o ) VAR f: PSPlot.File := NIL; nPlotted: unsigned = 0; ini, fin: unsigned; nInFile: unsigned = 0; { fprintf(stderr,"DrawCandidates...\n"); if (( NOT o.perCandidate )){ /* Sort candidates by curve pair, then `ideal' before `ordinary': */ pz_candidate_sort(cand, pz_candidate_pair_mismatch_better); }; ini = 0; while ( ini < (cand.ne ) ) ANDAND ( nPlotted < o.maxCands ){ /* Locate the range "[ini..fin-1]" of candidates to plot: */ fin = ini + 1; while ( fin < (cand.ne ) ) ANDAND ( NOT (o.perCandidate ) || ( NOT SameCurves(cand[fin].seg, cand[ini].seg)) ){ fin++; }; { /* with */ ??? cvx0 = cand[ini].seg[0].cvx; ??? cvx1 = cand[ini].seg[1].cvx; /* do */ if (( (o.segments[0].cvx == (unsigned.ne - 1) ) || ( cvx0 == o.segments[0].cvx) ) ANDAND ( (o.segments[1].cvx == (unsigned.ne - 1) ) || ( cvx1 == o.segments[1].cvx) )){ { /* with */ ??? nBatch = MIN(fin - ini, o.maxCands - nPlotted); ??? batch = SUBARRAY(cand, ini, nBatch); /* do */ DrawCandidateBatch(f, ini, batch, o, nInFile); INC(nPlotted, nBatch); }; }; }; ini = fin; }; fprintf(stderr,"\n"); pz_plot.NoMoreDrawings(f, o.drawFmt, nInFile); } /* DrawCandidates */ bool_t SameCurves( pz_segment_pair *sa, pz_segment_pair *sb ) { return sa[0].cvx == sb[0].cvx ) ANDAND ( sa[1].cvx == sb[1].cvx } /* SameCurves */ PROCEDURE DrawCandidateBatch( PSPlot.File *f, /* Postscript file. */ unsigned ini, /* Position of "cand[0]" in input file. */ pz_candidate.List *cand, /* The candidates ( all of the same curve pair ). */ Options *o, /* The command line options. */ VAR nInFile: unsigned; /* Drawings started in "f". */ ) == VAR segRef: pz_segment_pair; /* Segments to plot. */ plotName: char *; /* Name of batch */ caption: char *; /* Caption for page. */ gridStep: double; { /* Start a new drawing. */ if (( o.perCandidate )){ /* Start new drawing for a single candidate: */ affirm((cand.ne) == 1 ); { /* with */ ??? candSeg = cand[0].seg; ??? candName = Fmt.Pad(Fmt.Int(ini), 5, '0'); /* do */ segRef = ChooseReferenceSegments(o.segments, candSeg); plotName = candName; caption = txtcat("Candidate " , candName); } }else{ /* Start new drawing for a curve pair: */ { /* with */ ??? wholeSeg = JoinCurvePairSegments(cand); curveName = ARRAY [0..1] OF char *{ Fmt.Pad(Fmt.Int(wholeSeg[0].cvx), 4, '0'); Fmt.Pad(Fmt.Int(wholeSeg[1].cvx), 4, '0') }; /* do */ segRef = ChooseReferenceSegments(o.segments, wholeSeg); plotName = txtcat(curveName[0] & "-" , curveName[1]); caption = txtcat("Curves " & curveName[0] & " and " , curveName[1]);; };; }; BeginDrawing(f, plotName, segRef, caption, o, /*IO*/ nInFile, /*OUT*/ gridStep); /* Draw global grid, if necessary: */ if (( (cand.ne) > 1 ) || ( segRef != cand[0].seg ) || ( o.candGridLineWidth == 0.0 )){ DrawGrid(f, segRef, segRef, gridStep = gridStep, lineWidth = o.refGridLineWidth, lineColor = o.refGridLineColor, drawDiags = FALSE ); }; /* Draw candidate grids: */ for ( iCand = 0 TO (cand.ne - 1) ){ { /* with */ ??? cd = cand[iCand]; /* do */ DrawGrid(f, cd.seg, segRef, gridStep = gridStep, lineWidth = o.candGridLineWidth, lineColor = o.candGridLineColor, drawDiags = TRUE ); }; }; /* Draw the candidate pairings: */ for ( iCand = 0 TO (cand.ne - 1) ){ { /* with */ ??? cd = cand[iCand]; /* do */ DrawMatch(f, cd.pm, cd.seg, segRef, gridStep = gridStep, lineWidth = o.matchLineWidth, lineColor = o.matchLineColor, dotSize = o.dotSize, dotColor = o.dotColor, boxSize = o.boxSize, boxColor = o.boxColor, ideal = (cd.mismatch < 0.0e0) ); }; }; } /* DrawCandidateBatch */ SegmentPair JoinCurvePairSegments( pz_candidate.List *cand ) /* Returns a pair of segments that describe the whole curves underlying "seg[0]" and "seg[1]", with the same orientation. The segments will be open (without the step from last sample to first sample). */ VAR segJoin: pz_segment_pair := cand[0].seg; { for (i = 1; i < (cand.ne ) ; i++){ for (j = 0; j <= 1 ; j++){ { /* with */ ??? sC = cand[i].seg[j], sJ = segJoin[j]; /* do */ affirm(sC.cvx == sJ.cvx ); affirm(sC.tot == sJ.tot ); /* Allow segments with discordant directions: */ sJ.rev = sC.rev; sJ = pz_segment_join(sJ, sC); }; }; }; for (j = 0; j <= 1 ; j++){ { /* with */ ??? sJ = segJoin[j]; /* do */ if (( sJ.ns > sJ.tot )){ sJ.ini = 0; sJ.ns = sJ.tot ;}; }; }; return segJoin } /* JoinCurvePairSegments */ SegmentPair ChooseReferenceSegments( SegmentPair *segUser, SegmentPair *segCand ) /* Return "segUser[0..1]", completing it with "segCand[0..1]" where unspecified. */ VAR seg: SegmentPair; { for (j = 0; j <= 1 ; j++){ if (( segUser[j].cvx == (unsigned.ne - 1) )){ seg[j] = segCand[j] }else{ seg[j] = segUser[j]; if (( seg[j].ns == 0 ) || ( seg[j].tot == 0 )){ seg[j].ns = segCand[j].ns; seg[j].tot = segCand[j].tot;; };; }; }; return seg } /* ChooseReferenceSegments */ void BeginDrawing( PSPlot.File *f, char *plotName, SegmentPair *segRef, char *caption, Options *o, VAR /*IO*/ nInFile: unsigned; /* Drawings started in "f". */ double *gridStep /* (OUT) Actual grid cell size ( mm, ). */ ) /* Starts a new drawing for one or more candidates. For ".ps" format, starts a new page within the given file "f". For ".eps" format, opens a new file and return it in "f". In either case the plot is placed on the page assuming that all grids and machings will be contained in segments "segRef[0..1]". The "plotName" must be a string identifying the plot. The grid will be drawn with the given "o.cellSize" (mm), if that parameter is nonzero. Otherwise the grid step will be computed from "segRef" and the available plot area. In any case, the chosen cell size is returned in the "gridStep" parameter. */ { pz_plot.BeginNewDrawing(f, o.output, plotName & "-grid", o.drawFmt, nInFile); { /* with */ /* These numbers include a half-cell safety border around the grid: */ ??? nXCells = ((double)segRef[0].ns); ??? nYCells = ((double)segRef[1].ns); /* do */ /* Determine the actual grid step size to use: */ gridStep = min(o.drawFmt.actualWidth/nXCells, o.drawFmt.actualHeight/nYCells); if (( o.cellSize != 0.0e0 ) ANDAND ( o.cellSize < gridStep )){ gridStep = o.cellSize; }; /* Set up the plotting environment: */ { /* with */ ??? xSize = nXCells * gridStep; ??? dx = (o.drawFmt.actualWidth - xSize)/2.0e0; ??? ySize = nYCells * gridStep; ??? dy = (o.drawFmt.actualHeight - ySize)/2.0e0; /* do */ /* f.caption("plot " & caption); */ fprintf(stderr, "drawing %s\n", caption ); fprintf(stderr, " xSize == " & FLR(xSize,4)); fprintf(stderr, " ySize == %s\n", FLR(ySize,4) ); f.setScale(PSPlot.Axis.X, - dx, xSize + dx); f.setScale(PSPlot.Axis.Y, - dy, ySize + dy);; };; } } /* BeginDrawing */ void DrawGrid( PSPlot.File f, SegmentPair *seg, SegmentPair *segRef, double gridStep, double lineWidth, Color lineColor, bool_t drawDiags ) void DoDrawGrid( SegmentPair *t, SegmentPair *tRef ) { pz_plot.SegSegGrid(f, t, tRef, gridStep, drawDiags) } /* DoDrawGrid */ { if (( lineWidth > 0.0 )){ f.setLineWidth(lineWidth); f.setLineColor(lineColor); f.setLineSolid(); pz_plot.TryAllShifts(seg, segRef, DoDrawGrid); }; } /* DrawGrid */ void DrawMatch( PSPlot.File f, pz_match_packed_t *pm, SegmentPair *seg, SegmentPair *segRef, double gridStep, /* In mm */ double lineWidth, /* In mm */ Color lineColor, double dotSize, /* In mm */ Color dotColor, double boxSize, /* In mm */ Color boxColor, bool_t ideal ) VAR rm: REF pz_match_t; { if (( lineWidth > 0.0 ) || ( dotSize > 0.0 ) || ( boxSize > 0.0 )){ if (( pm != NULL )){ rm = pz_match_unpack(pm^) }else{ rm = pz_match_most_perfect(seg[0].ns, seg[1].ns); }; if (( ideal )){ /* Adjust dot size/color and line width/color for ideal candidates: */ if (( dotSize > 0.0 )){ dotSize = dotSize + 0.2 ;}; if (( lineWidth > 0.0 )){ lineWidth = lineWidth + 0.2 ;}; dotColor = Highlight(dotColor); lineColor = Highlight(lineColor);; }; { /* with */ ??? m = rm^; /* do */ void DoDrawMatchBoxes( SegmentPair *t, SegmentPair *tRef ) { pz_plot.MatchBoxes(f, m, t, tRef, gridStep, boxSize*FLOAT(gridStep,double)) } /* DoDrawMatchBoxes */ void DoDrawMatchPath( SegmentPair *t, SegmentPair *tRef ) { pz_plot.MatchPath(f, m, t, tRef, gridStep) } /* DoDrawMatchPath */ void DoDrawMatchDots( SegmentPair *t, SegmentPair *tRef ) { pz_plot.MatchDots(f, m, t, tRef, gridStep, dotSize) } /* DoDrawMatchDots */ { if (( boxSize > 0.0 )){ /* Draw squares around path nodes: */ f.setLineWidth(0.075); f.setLineColor(PSPlot.Invisible); f.setLineSolid(); f.setFillColor(boxColor); pz_plot.TryAllShifts(seg, segRef, DoDrawMatchBoxes); }; if (( lineWidth > 0.0 )){ /* Draw the pairing "cand.pm" as a path on the DP grid: */ f.setLineWidth(lineWidth); f.setLineColor(lineColor); f.setLineSolid(); f.setFillColor(PSPlot.Invisible); pz_plot.TryAllShifts(seg, segRef, DoDrawMatchPath); }; if (( dotSize > 0.0 )){ /* Draw the pairs of the pairing "cand.pm" as vertices of the DP grid: */ f.setLineWidth(dotSize); f.setLineColor(dotColor); f.setLineSolid(); f.setFillColor(PSPlot.Invisible); pz_plot.TryAllShifts(seg, segRef, DoDrawMatchDots); }; }; }; } } /* DrawMatch */ Color Highlight( Color c ) { for (i = 0; i <= 2 ; i++){ if (( c[i] > 0.5 )){ c[i] = 0.50*c[i] }else{ c[i] = 0.50 + 0.50*c[i] ;}; }; return c } /* Highlight */ Options pz_get_options(int argc, char **argv ) VAR o: Options; CONST Inch == 25.4e0; /* One inch in mm */ { argparser_t *pp = argparser_new(stderr, argc, argv); argparser_set_help(pp, PROG_NAME " version " PROG_VERS ", usage:\n" PROG_HELP); argparser_set_info(pp, PROG_INFO); argparser_process_help_info_options(pp); { /* with */ /* do */ TRY argparser_get_keyword(pp, "-input"); o.input = argparser_get_next(pp); if (( argparser_keyword_present(pp, "-ideal") )){ o.ideal = argparser_get_next(pp) }else{ o.ideal = ""; }; argparser_get_keyword(pp, "-output"); o.output = argparser_get_next(pp); if (( argparser_keyword_present(pp, "-maxCands") )){ o.maxCands = argparser_get_next_int(pp, 1) }else{ o.maxCands = 160; }; if (( argparser_keyword_present(pp, "-perCandidate") )){ o.perCandidate = TRUE }else if (( argparser_keyword_present(pp, "-perCurvePair") )){ o.perCandidate = FALSE }else{ argparser_error(pp, "must specify either \"-perCandidate\" ) || ( \"-perCurvePair\""); }; if (( argparser_keyword_present(pp, "-segments") )){ o.segments[0] = ParseSegmentParam(pp); o.segments[1] = ParseSegmentParam(pp); }else if (( argparser_keyword_present(pp, "-curves") )){ { /* with */ ??? cvx0 = argparser_get_next_int(pp); ??? cvx1 = argparser_get_next_int(pp); ??? s = o.segments; /* do */ s[0] = pz_segment_empty; s[0].cvx = cvx0; s[0].rev = FALSE; s[1] = pz_segment_empty; s[1].cvx = cvx1; s[1].rev = TRUE; } }else{ { /* with */ ??? s = o.segments; /* do */ s[0] = pz_segment_empty; s[0].cvx = (unsigned.ne - 1); s[1] = pz_segment_empty; s[1].cvx = (unsigned.ne - 1); }; ; }; if (( argparser_keyword_present(pp, "-cellSize") )){ o.cellSize = argparser_get_next_double(pp, 0.1e0, 100.0e0); }else{ o.cellSize = 0.0e0; }; if (( argparser_keyword_present(pp, "-refGridLine") )){ o.refGridLineWidth = pp.getNextReal(0.0, 10.0); o.refGridLineColor = ParseColorParam(pp); }else{ o.refGridLineWidth = 0.1; o.refGridLineColor = Color{0.667, 0.667, 0.667};; }; if (( argparser_keyword_present(pp, "-candGridLine") )){ o.candGridLineWidth = pp.getNextReal(0.0, 10.0); o.candGridLineColor = ParseColorParam(pp); }else{ o.candGridLineWidth = 0.1; o.candGridLineColor = Color{0.333, 0.333, 0.333};; }; if (( argparser_keyword_present(pp, "-matchLine") )){ o.matchLineWidth = pp.getNextReal(0.0, 10.0); o.matchLineColor = ParseColorParam(pp); }else{ o.matchLineWidth = 0.4; o.matchLineColor = PSPlot.Black;; }; if (( argparser_keyword_present(pp, "-dots") )){ o.dotSize = pp.getNextReal(0.0, 10.0); o.dotColor = ParseColorParam(pp); }else{ o.dotSize = 0.8; o.dotColor = PSPlot.Black;; }; if (( argparser_keyword_present(pp, "-boxes") )){ o.boxSize = pp.getNextReal(0.0, 100.0); o.boxColor = ParseColorParam(pp); }else{ o.boxSize = 0.0; o.boxColor = PSPlot.Black;; }; o.drawFmt = pz_plot.ParseDrawFmtOptions( pp, single = FALSE, defaultWidth = PSPlot.LetterXSize - 1.0e0 * Inch, defaultHeight = PSPlot.LetterYSize - 2.0e0 * Inch ); argparser_finish(pp); EXCEPT | ParseParams.Error ==> fprintf(stderr, "Usage: pz_draw_cands \\\n"); fprintf(stderr, " -input NAME [ -ideal NAME ] \\\n"); fprintf(stderr, " -output NAME \\\n"); fprintf(stderr, " [ -maxCands NUMBER ] \\\n"); fprintf(stderr, " { -perCandidate | -perCurvePair } \\\n"); fprintf(stderr, " [ -segments {CHAIN TOTSAMP START NSAMP {+|-}}^2 | \\\n"); fprintf(stderr, " -curves CHAIN0 CHAIN1 \\\n"); fprintf(stderr, " ] \\\n"); fprintf(stderr, " [ -cellSize NUMBER ] \\\n"); fprintf(stderr, " [ -refGridLine WIDTH RED GRN BLU ] \\\n"); fprintf(stderr, " [ -candGridLine WIDTH RED GRN BLU ] \\\n"); fprintf(stderr, " [ -matchLine WIDTH RED GRN BLU ] \\\n"); fprintf(stderr, " [ -dots DIAMETER RED GRN BLU ] \\\n"); fprintf(stderr, " [ -boxes SIDE RED GRN BLU ] \\\n"); fprintf(stderr, pz_plot.DrawFmtOptionsHelp & " \n"); Process.Exit(1);; };; }; return o } /* GetOptions */ pz_segment_t ParseSegmentParam( arparser_t pp ) RAISES {ParseParams.Error} = VAR rev: bool_t; { { /* with */ ??? cvx = argparser_get_next_int(pp, 0, (unsigned.ne - 1)); ??? tot = argparser_get_next_int(pp, 0, 100000); ??? ini = argparser_get_next_int(pp, 0, 100000); ??? ns = argparser_get_next_int(pp, 1, 100000); ??? s = argparser_get_next(pp); /* do */ if (( Text.Equal(s, "+") )){ rev = FALSE }else if (( Text.Equal(s, "-") )){ rev = FALSE }else{ argparser_error(pp, "bad segment direction flag"); }; return pz_segment_t{ cvx = cvx, tot = tot, ini = ini, ns = ns, rev = rev }; } } /* ParseSegmentParam */ Color ParseColorParam( arparser_t pp ) RAISES {ParseParams.Error} = { { /* with */ ??? red = pp.getNextReal(0.0, 1.0); ??? grn = pp.getNextReal(0.0, 1.0); ??? blu = pp.getNextReal(0.0, 1.0); /* do */ return Color{red, grn, blu}; } } /* ParseColorParam */ char *FLR( double x, unsigned prec ) { return Fmt.LongReal(x, prec = prec, style = Fmt.Style.Fix) } /* FLR */ { } pz_draw_cand_grids. /* Copyright © 2001 Universidade Estadual de Campinas (UNICAMP). Authors: Helena C. G. Leitão and Jorge Stolfi. This file can be freely distributed, used, and modified, provided that this copyright and authorship notice is preserved, and that any modified versions are clearly marked as such. This software has NO WARRANTY of correctness or applicability for any purpose. Neither the authors nor their employers chall be held responsible for any losses or damages that may result from its use. */