/* Refines a list of candidate pairings, using DP on geometric chains. */ /* Last edited on 2024-01-11 08:19:48 by stolfi */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include TYPE MismatchOptions == pz_mismatch.Options; Options = struct ??? { char *input; /* Input candidate file name. */ char *chainDir; /* Directory where to find chain files. */ char *chainPrefix; /* Invariant chain file name prefix. */ char *extension; /* Extension of chain files, usually ".flc" */ unsigned band; /* Nominal band width (lambda) for file names. */ double step; /* Sampling step. */ unsigned maxInputCands; /* For debugging: truncate input at this count. */ /* Pairing parameters: */ MismatchOptions mp; /* Mismatch formula parameters. */ double minLength; /* Minimum length of pairing segments. */ double blurFactor; /* Corner broadening factor. */ double extraLength; /* Extra length to add at each candidate end. */ double maxRefineShift; /* Max align. shift during refinement. */ bool_t simpleMatch; /* TRUE uses "FindBest" match instead of "Refine". */ /* Sort/prune parameters: */ unsigned maxChainCands; /* Max candidates per chain. */ unsigned maxPairCands; /* Max candidates per chain pair. */ /* Placement/optimization parameters: */ bool_t dontGuess; /* TRUE to use identity as the initial matrix */ bool_t perturbMatrix; /* TRUE to perturb placement matrix */ unsigned maxEvals; /* Max trials in evaluation (0 = dont' optimize). */ bool_t plotFunction; /* TRUE to plot the goal function. */ /* Plot parameters: */ bool_t draw; /* TRUE to draw the the initial and final placements */ unsigned drawEvery; /* Plot one every this many samples. */ double grid; /* Coordinate grid spacing (0 = no grid). */ bool_t noColors; /* TRUE uses only black, FALSE uses red/blue for segs. */ double displace; /* Separate curves by this amount when plotting */ bool_t epsFormat; /* TRUE: eps format, FALSE: plain ps format */ double plotWidth; /* Plot area width in mm. */ double plotHeight; /* Plot area height in mm. */ /* Parameters that control the output: */ char *output; /* Output file name (without extensions) */ } ???; TYPE ChainData == pz_r3_chain_read_data; int main(int argc, char **argv ) { { /* with */ ??? o = pz_get_options(int argc, char **argv); ??? candData = pz_candidate_read(open_read(o.input & ".can", TRUE)); ??? oldCand = SUBARRAY(candData.c^, 0, MIN(o.maxInputCands, (candData.c^.ne))); ??? lambda = candData.lambda; ??? chainUsed = pz_candidate_chains_used(oldCand)^; chAllData = pz_r3_chain_read_all( o.chainPrefix, o.band, o.extension, sel := chainUsed; dir := o.chainDir, header_only := FALSE, recenter := pz_ctr_NONE ); ??? newCand = RefineCandidates(oldCand, chAllData.chData^, o, lambda)^; ??? runCmt = RunComments(o); /* do */ { /* with */ ??? wr = open_write(o.output & ".can", TRUE); /* do */ pz_candidate_write(wr, candData.cmt & "\n" & runCmt, newCand, lambda);; };; }; } /* Main */ Options pz_get_options(int argc, char **argv ) VAR o: Options; { 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, "-maxInputCands") )){ o.maxInputCands = argparser_get_next_int(pp, 1) }else{ o.maxInputCands = (unsigned.ne - 1); }; if (( argparser_keyword_present(pp, "-chainDir") )){ o.chainDir = argparser_get_next(pp) }else{ o.chainDir = "."; }; argparser_get_keyword(pp, "-chainPrefix"); o.chainPrefix = argparser_get_next(pp); argparser_get_keyword(pp, "-band"); o.band = argparser_get_next_int(pp); argparser_get_keyword(pp, "-step"); o.step = argparser_get_next_double(pp, 0.0e0, 1024.0e0); if (( argparser_keyword_present(pp, "-extension") )){ o.extension = argparser_get_next(pp) }else{ o.extension = ".flc"; }; argparser_get_keyword(pp, "-output"); o.output = argparser_get_next(pp); o.plotFunction = argparser_keyword_present(pp, "-plotFunction"); o.simpleMatch = argparser_keyword_present(pp, "-simpleMatch"); argparser_get_keyword(pp, "-minLength"); o.minLength = argparser_get_next_double(pp); argparser_get_keyword(pp, "-blurFactor"); o.blurFactor = argparser_get_next_double(pp, 0.0e0, 5.0e0); argparser_get_keyword(pp, "-extraLength"); o.extraLength = argparser_get_next_double(pp, 0.0e0); argparser_get_keyword(pp, "-maxRefineShift"); o.maxRefineShift = argparser_get_next_double(pp); o.mp = pz_mismatch.ParseOptions(pp); if (( argparser_keyword_present(pp, "-maxChainCands") )){ o.maxChainCands = argparser_get_next_int(pp, 1) }else{ o.maxChainCands = (unsigned.ne - 1); }; if (( argparser_keyword_present(pp, "-maxPairCands") )){ o.maxPairCands = argparser_get_next_int(pp, 1, o.maxChainCands) }else{ o.maxPairCands = (unsigned.ne - 1); }; if (( argparser_keyword_present(pp, "-dontGuess") )){ o.dontGuess = TRUE; }else{ o.dontGuess = FALSE;; }; o.perturbMatrix = argparser_keyword_present(pp, "-perturbMatrix"); if (( argparser_keyword_present(pp, "-dontOptimize") )){ o.maxEvals = 0 }else{ argparser_get_keyword(pp, "-maxEvals"); o.maxEvals = argparser_get_next_int(pp);; }; o.draw = argparser_keyword_present(pp, "-draw"); if (( argparser_keyword_present(pp, "-displace") )){ o.displace = argparser_get_next_double(pp); }else{ o.displace = 0.0e0;; }; if (( argparser_keyword_present(pp, "-grid") )){ o.grid = argparser_get_next_double(pp, 1.0e-10, 1.0e+10) }else{ o.grid = 0.0e0; }; if (( argparser_keyword_present(pp, "-drawEvery") )){ o.drawEvery = argparser_get_next_int(pp, 1, (unsigned.ne - 1)) }else{ o.drawEvery = 1; }; o.noColors = argparser_keyword_present(pp, "-noColors"); o.epsFormat = argparser_keyword_present(pp, "-epsFormat"); if (( o.epsFormat )){ if (( argparser_keyword_present(pp, "-plotSize") )){ o.plotWidth = argparser_get_next_double(pp, 10.0e0, 1000.0e0); o.plotHeight = argparser_get_next_double(pp, 10.0e0, 1000.0e0); }else{ o.plotWidth = 150.0e0; o.plotHeight = 50.0e0;; }; }else{ o.plotWidth = 150.0e0; o.plotHeight = 150.0e0;; }; argparser_finish(pp); EXCEPT | ParseParams.Error ==> fprintf(stderr, "Usage: PZPZRefineCandsGeometry \\\n"); fprintf(stderr, " -input NAME [ -maxInputCands NUMBER ] \\\n"); fprintf(stderr, " [ -chainDir DIR ] -chainPrefix NAME \\\n"); fprintf(stderr, " -band BAND -step NUMBER \\\n"); fprintf(stderr, " [ -extension EXT ] \\\n"); fprintf(stderr, " -output NAME \\\n"); fprintf(stderr, " [ -plotFunction ] \\\n"); fprintf(stderr, " [ -simpleMatch ] \\\n"); fprintf(stderr, " -maxRefineShift NUM \\\n"); fprintf(stderr, " %s \\\n", pz_mismatch.OptionsHelp ); fprintf(stderr, " -minLength NUM \\\n"); fprintf(stderr, " -blurFactor NUM -extraLength NUM \\\n"); fprintf(stderr, " [ -maxChainCands NUMBER ] \\\n"); fprintf(stderr, " [ -maxPairCands NUMBER ] \\\n"); fprintf(stderr, " [ -dontGuess ] [ -perturbMatrix ] \\\n"); fprintf(stderr, " { -dontOptimize | -maxEvals NUM } \\\n"); fprintf(stderr, " [ -draw \\\n"); fprintf(stderr, " [ -epsFormat [ -plotSize WIDTH HEIGHT ] ] \\\n"); fprintf(stderr, " [ -drawEvery NUM ] [ -grid SPACING ] \\\n"); fprintf(stderr, " [ -displace NUM ] [ -noColors ] \\\n"); fprintf(stderr, " ] \n"); Process.Exit(1);; };; }; return o; } GetOptions; pz_candidate.List *RefineCandidates( pz_candidate.List *cand, ARRAY *OF ChainData ch, Options *o, double lambda ) VAR c: REF pz_candidate.List; /* Refined candidates. */ n: unsigned = 0; /* Refined candidate count. */ minAvgDist: REF double_vec_t = NULL; rCost: REF pz_match_cost_matrix_t = NULL; plotName: char *; { { /* with */ ??? nCands = (cand.ne); ??? nChains = (ch.ne); ??? length = double_vec_new(nChains)^; /* do */ for (k = 0; k <= nChains-1 ; k++){ if (( ch[k].c != NULL )){ length[k] = ChainLength(ch[k].c^) ;}; }; fprintf(stderr, "candidates before refine loop == %s\n", Fmt.Int(nCands) ); c = NEW(REF pz_candidate.List, nCands); for (i = 0; i <= nCands-1 ; i++){ { /* with */ ??? candTag = Fmt.Pad(Fmt.Int(i), 6, '0'); /* do */ fprintf(stderr, "===== candidate %s ====================================\n", candTag ); if (( i == 0 ) ANDAND ( o.plotFunction )){ plotName = o.output & "-" & candTag & "-fn" }else{ plotName = ""; }; c[n] = RefineOneCandidate( cand[i], ch, o, lambda, plotName = plotName, drawName = o.output & "-" & candTag & "-dr", candCmt = CandComments(i, cand[i], o.step), /*WORK*/ minAvgDist = minAvgDist, rCost = rCost ); if (( (i MOD 80 == 0) )){ fprintf(stderr, "\n") ;}; if (( c[n].mismatch <= 0.0e0 )){ fprintf(stderr, "+"); n++ }else{ fprintf(stderr, "-");; };; };; }; fprintf(stderr, "\n"); fprintf(stderr, "candidates before pruning == %s\n", Fmt.Int(n) ); pz_candidate_sort(SUBARRAY(c^, 0, n), pz_candidate_pair_mismatch_better); pz_candidate_prune_cands_per_pair(c^, n, o.maxPairCands); fprintf(stderr, "candidates after pair pruning == %s\n", Fmt.Int(n) ); pz_candidate_sort(SUBARRAY(c^, 0, n), pz_candidate_mismatch_better); pz_candidate_prune_cands_per_chain(c^, n, o.maxChainCands); fprintf(stderr, "candidates after chain pruning == %s\n", Fmt.Int(n) ); { /* with */ ??? ct = NEW(REF pz_candidate.List, n); /* do */ ct^ = SUBARRAY(c^,0,n); return ct; };; } } /* RefineCandidates */ double ChainLength( pz_r3_chain_t *c ) VAR s: double := 0.0e0; VAR j := (c.ne - 1); { for (i = 0; i < (c.ne ) ; i++){ s = s + r3_dist(c[i], c[j]); j = i; }; return s } /* ChainLength */ pz_candidate_t RefineOneCandidate( pz_candidate_t *cOld, ARRAY *OF ChainData ch, Options *o, double lambda, char *plotName, char *drawName, char *candCmt, /* (WORK) */ REF *double_vec_t minAvgDist, REF *pz_match_cost_matrix_t rCost ) VAR mt: REF pz_match_t; mismatch: double; candLength: double; matchedLength: double; mat: r4x4_t; aMap: REF pz_r3_chain_t; extra: i2_t; { /* Compute the expansion needed, in samples: */ for (j = 0; j <= 1 ; j++){ extra[j] = CEILING(o.extraLength / o.step); }; { /* with */ ??? cExp = pz_candidate_expand(cOld, extra, extra); ??? cvxa = cOld.seg[0].cvx; ??? ca = pz_r3_chain_extract-segment(ch[cvxa].c^, cExp.seg[0])^; ??? cvxb = cOld.seg[1].cvx; ??? cb = pz_r3_chain_extract-segment(ch[cvxb].c^, cExp.seg[1])^; /* do */ affirm((ch[cvxa].c^.ne) == cOld.seg[0].tot ); affirm((ch[cvxb].c^.ne) == cOld.seg[1].tot ); pz_candidate_print(stderr, cExp); ComputeMatrix( a = ca, b = cb, o = o, step = o.step, minLength = o.minLength - o.blurFactor * 2.0e0 * lambda, plotName = plotName, drawName = drawName, candCmt = candCmt, /*OUT*/ mat = mat, mt = mt, mismatch = mismatch, length = candLength, matchedLength = matchedLength, aMap= aMap, /*WORK*/ minAvgDist = minAvgDist, rCost = rCost ); fprintf(stderr, "\n"); { /* with */ cNew = pz_candidate_cutAndPack( cExp, mt^; mismatch := mismatch; length := candLength; matchedLength := matchedLength ); /* do */ pz_candidate_print(stderr, cNew); return cNew; }; } } /* RefineOneCandidate */ void ComputeMatrix( pz_r3_chain_t *a, pz_r3_chain_t *b, /* Contour segments to match. */ Options *o, double step, /* Mean sampling step. */ double minLength, /* Minimum length of pairing. */ char *drawName, /* File name prefix for drawings. */ char *plotName, /* File name prefix for goal function plots. */ char *candCmt, /* Candidate description for plot caption. */ /* (OUT) */ r4x4_t *mat, REF *pz_match_t mt, double *mismatch, double *length, double *matchedLength, REF *pz_r3_chain_t aMap, /* (WORK) */ REF *double_vec_t minAvgDist, pz_match_cost_matrix_t *rCost ) VAR f, f1: Value; nEvals: unsigned; { { /* with */ ??? ma = (a.ne); ??? mb = (b.ne); ??? Ra = Dist(a[0], a[ma-1])/2.0e0; ??? Rb = Dist(b[0], b[mb-1])/2.0e0; ??? R = (Ra + Rb)/2.0e0; ??? scaleY = Math.sqrt(1.0e0/3.0e0) * R; ??? scaleX = scaleY / 4.0e0; ??? minChainEdges = MAX(0, FLOOR((2.0e0 * minLength)/step)); /* do */ double GoalFunction( r4x4_t *mat ) /* Computes the image "aMap" of chain "a" by "mat", the best pairing "mt" of "aMap" with "b", and returns the mismatch if negative. Otherwise the mismatch must be zero; in that case, returns the distance squared between the midpoint of "a" and the nearest point of "b". */ { aMap = pz_r3_chain_map(a, mat); ComputeMatch(aMap^, b, o, minChainEdges = minChainEdges, step = step, mt = mt, mismatch = mismatch, length = length, matchedLength = matchedLength, minAvgDist = minAvgDist, rCost = rCost ); return mismatch } /* GoalFunction */ { /* Compute an initial guess for the placement matrix: */ mat = ComputeInitialMatrix(a, b, o); /* Compute initial mapped chain, pairing, and mismatch: */ f = GoalFunction(mat); affirm(f == mismatch ); /* Plot initial placement and pairing: */ if (( o.draw )){ DrawPlacement(drawName & "-i", o, candCmt, aMap^, b, mt^, nEvals, mismatch = mismatch, length = length, matchedLength = matchedLength ); }; /* Optimize matrix */ pz_opt_matrix.Optimize( func = GoalFunction, scaleX = scaleX, scaleY = scaleY, maxEvals = o.maxEvals, nEvals = nEvals, m = mat, f = f, plotName = plotName ); /* Compute final mapped chainh, pairing, and mismatch: */ f1 = GoalFunction(mat); affirm(f1 == f ); affirm(f == mismatch ); /* Plot optimum placement and pairing: */ if (( o.draw )){ DrawPlacement(drawName & "-f", o, candCmt, aMap^, b, mt^, nEvals, mismatch = mismatch, length = length, matchedLength = matchedLength ); };; }; } } /* ComputeMatrix */ r4x4_t ComputeInitialMatrix( pz_r3_chain_t *a, pz_r3_chain_t *b, Options *o ) VAR mat: r4x4_t; { if (( o.dontGuess )){ mat = Identity(); }else{ mat = pz_r3_chain_alignment_matrix(a, b, pz_ctr_SMPS, 0.20); }; if (( o.perturbMatrix )){ mat = PerturbMatrix (mat, dTheta = 0.1e0, dX = 1.0e0, dY = 1.0e0); }; return mat } /* ComputeInitialMatrix */ r4x4_t PerturbMatrix( r4x4_t *mat, double dTheta, double dX, double dY ) VAR n: r4x4_t := LR4x4.Identity(); { { /* with */ ??? cos = Math.cos(dTheta); ??? sin = Math.sin(dTheta); /* do */ affirm(cos != 0.0e0 ) || ( sin != 0.0e0 ); n[1,1] = cos; n[1,2] = sin; n[2,1] = -sin; n[2,2] = cos; n[0,1] = dX; n[0,2] = dY;; }; return LR4x4.Mul(mat, n) } /* PerturbMatrix */ void ComputeMatch( pz_r3_chain_t *a, pz_r3_chain_t *b, Options *o, unsigned minChainEdges, double step, /* (OUT) */ REF *pz_match_t mt, double *mismatch, double *length, double *matchedLength, /* (WORK) */ REF *double_vec_t minAvgDist, pz_match_cost_matrix_t *rCost ) VAR avgDist: double; { { /* with */ ??? NA = (a.ne); ??? NB = (b.ne); ??? ctr = PickCenter(a, b); ??? mtOld = pz_match_t{ctr}; /* do */ if (( minChainEdges < (NA)+(NB-1) )){ if (( o.simpleMatch )){ pz_r3_chain_match_find_best( a, b, maxDistSqr = o.mp.maxDistSqr, removeUnmatchedEnds = TRUE, /*OUT*/ mt = mt, avgDist = avgDist, length = length, matchedLength = matchedLength, /*WORK*/ rCost = rCost ); mismatch = (avgDist*avgDist - o.mp.critDistSqr)*length }else{ if (( minAvgDist == NULL ) || ( (minAvgDist^.ne) < NA + NB - 1 )){ minAvgDist = NEW(REF double_vec_t, NA + NB - 1); }; pz_r3_chain_match_refine( a, b, step = step, mtOld = mtOld, maxAdjust = CEILING(o.maxRefineShift / o.step), critDistSqr = o.mp.critDistSqr, maxDistSqr = o.mp.maxDistSqr, /*OUT*/ mt = mt, mismatch = mismatch, length = length, matchedLength = matchedLength, /*WORK*/ minAvgDist = SUBARRAY(minAvgDist^, 0, NA+NB-1), rCost = rCost );; }; /* If the pairing has zero length, provide a useful "mismatch": */ if (( mt == NULL ) || ( (mt^.ne) < 2 ) || ( length <= 0.0e0 )){ affirm(mismatch == 0.0e0 ); { /* with */ ??? ac = (NA-1) DIV 2; ??? bc = IndexNearestPoint(b, a[ac]); /* do */ mismatch = r3_dist_sqr(a[ac], b[bc]);; }; } }else{ mismatch = (double.ne - 1); length = 0.0e0; matchedLength = 0.0e0; mt = pz_match_new(1); mt[0] = ctr; }; }; } /* ComputeMatch */ pz_match_pair PickCenter( pz_r3_chain_t *a, pz_r3_chain_t *b ) { { /* with */ ??? ai = IndexNearestPoint(a, b[0]); ??? af = IndexNearestPoint(a, b[(b.ne - 1)]); ??? bi = IndexNearestPoint(b, a[0]); ??? bf = IndexNearestPoint(b, a[(a.ne - 1)]); /* do */ return pz_match_pair{(ai + af) DIV 2, (bi + bf) DIV 2}; } } /* PickCenter */ unsigned IndexNearestPoint( pz_r3_chain_t *b, r3_t p ) VAR index: unsigned; mindist: double; { { /* with */ ??? m = (b.ne); ??? mHalf = (m-1) DIV 2; /* do */ index = 0; mindist = Dist(b[0],p); for (i = mHalf; i <= 1 BY -1 ; i++){ { /* with */ ??? d = Dist(b[i],p); /* do */ if (( d < mindist )){ index = i; mindist = d ;};; }; }; for (i = mHalf+1; i <= m-1 ; i++){ { /* with */ ??? d = Dist(b[i],p); /* do */ if (( d < mindist )){ index = i; mindist = d ;};; }; };; }; return index } /* IndexNearestPoint */ void DrawPlacement( char *fileName /* File name without extension. */ Options *o, char *candCmt, /* Candidate description for caption. */ pz_r3_chain_t *a, pz_r3_chain_t *b, /* Partial contours, reversed and mapped. */ pz_match_t *mt, /* The match, relative to "a" and "b". */ unsigned nEvals, /* Goal function evaluations done. */ double mismatch, /* Mismatch of "mt". */ double length, /* Total length of "a" and "b" */ double matchedLength, /* Length actually matched. */ ) { { /* with */ ??? ma = (a.ne); ??? mb = (b.ne); ??? w = r3_t{-1.0e0, 0.0e0, 0.0e0}; ??? p = a[0]; ??? q = a[(a.ne - 1)]; ??? u = pz_geo.SegDir(p, q); ??? mat = pz_geo.Rotation(u, w); cand = pz_candidate_t{ seg := pz_segment_pair{ pz_segment_t{cvx := 0, tot := ma, ini := 0, ns := ma, rev := FALSE}; pz_segment_t{cvx := 0, tot := mb, ini := 0, ns := mb, rev := FALSE} }; mismatch := mismatch; length := length; matchedLength := matchedLength; pm := pz_match_pack(mt) }; ??? fd = o.displace/2.0e0; ??? am = DisplaceChain(pz_r3_chain_map(a, mat)^, r3_t{0.0e0, +fd, 0.0e0})^; ??? bm = DisplaceChain(pz_r3_chain_map(b, mat)^, r3_t{0.0e0, -fd, 0.0e0})^; caption = candCmt & MatchComments( nEvals, mat, mt, mismatch, length, matchedLength ); /* do */ pz_full_plot.Candidate( fileName, cand, c0 = am, c1 = bm, whole = FALSE, closed = FALSE, colors = NOT o.noColors, thicker = TRUE, dots = (o.drawEvery <= 1), pointers = FALSE, labelSize = 0.0, axes = FALSE, grid = o.grid, epsFormat = o.epsFormat, caption = caption, drawEvery = o.drawEvery, drawMatchEvery = 1, XSize = o.plotWidth, YSize = o.plotHeight );; }; fprintf( stderr, "."); } /* DrawPlacement */ pz_r3_chain_t *DisplaceChain( pz_r3_chain_t *a, r3_t *d ) { { /* with */ ??? r = NEW(REF pz_r3_chain_t, (a.ne)), ad = r^; /* do */ for (i = 0; i < (a.ne ) ; i++){ ad[i] = r3_add(a[i], d) ;}; return r; } } /* DisplaceChain */ char *CandComments( unsigned iCand, pz_candidate_t *cand, double step ) { return "Candidate " & Fmt.Pad(Fmt.Int(iCand), 6, '0') & " step == " & Fmt.LongReal(step, prec = 6) & "\n" & SegmentComments(cand.seg[0]) & SegmentComments(cand.seg[1]) } /* CandComments */ <* UNUSED ); char *FI( int x, unsigned w ) { return Fmt.Pad(Fmt.Int(x), w, '0') } /* FI */ char *SegmentComments( pz_segment_t *s ) { { /* with */ ??? wr = NEW(TextFILE *).init(); /* do */ fprintf( wr, " chain " & Fmt.Pad(Fmt.Int(s.cvx), 4, '0')); fprintf(wr, " (total %s samples)", Fmt.Pad(Fmt.Int(s.tot), 5) ); fprintf(wr, " %s samples", Fmt.Pad(Fmt.Int(s.ns), 5) ); fprintf( wr, " from sample " & Fmt.Pad(Fmt.Int(s.ini), 5)); if (( s.rev )){ fprintf( wr, " (reversed)") ;}; fprintf( wr, "\n"); return TextFILE *oText(wr); } } /* SegmentComments */ char *MatchComments( unsigned nEvals, r4x4_t *mat, pz_match_t *mt, double mismatch, double length, double matchedLength ) { { /* with */ ??? wr = NEW(TextFILE *).init(); ??? nm = (mt.ne); /* do */ fprintf(wr, " matched "); fprintf(wr, FmtMatchedRange(mt[0][0], mt[nm-1][0])); fprintf(wr, " to "); fprintf(wr, FmtMatchedRange(mt[0][1], mt[nm-1][1])); fprintf(wr, " nEvals == %s\n", Fmt.Int(nEvals) ); fprintf(wr, " (cos,sin) == %s\n", PairToText(mat[1,1], mat[1,2]) ); fprintf(wr, " (dX,dY) == %s\n", PairToText(mat[0,1], mat[0,2]) ); fprintf(wr, " mismatch == " & Fmt.LongReal(mismatch, prec = 6)); fprintf(wr, " length == " & Fmt.LongReal(length, prec = 6)); fprintf(wr, " matchedLength == %s\n", Fmt.LongReal(matchedLength, prec = 6) ); return TextFILE *oText(wr); } } /* MatchComments */ char *RunComments( Options *o ) { { /* with */ ??? wr = NEW(TextFILE *).init(); /* do */ fprintf(wr, " input == %s\n", o.input ); fprintf(wr, " maxInputCands == %s\n", Fmt.Int(o.maxInputCands) ); fprintf(wr, " chainDir == " & o.chainDir); fprintf(wr, " chainPrefix == " & o.chainPrefix); fprintf(wr, " band == %s\n", Fmt.Int(o.band) ); fprintf(wr, " output == %s\n", o.output ); fprintf(wr, " maxRefineShift == %s\n", Fmt.LongReal(o.maxRefineShift, prec = 6) ); fprintf(wr, " maxDist == %s\n", Fmt.LongReal(sqrt(o.mp.maxDistSqr), prec = 6) ); fprintf(wr, " critDist == " & Fmt.LongReal(sqrt(o.mp.critDistSqr), prec = 6)); fprintf(wr, " minLength == %s\n", Fmt.LongReal(o.minLength, prec = 6) ); fprintf(wr, " blurFactor == " & Fmt.LongReal(o.blurFactor, prec = 6)); fprintf(wr, " extraLength == %s\n", Fmt.LongReal(o.extraLength, prec = 6) ); return TextFILE *oText(wr); } } /* RunComments */ char *FmtMatchedRange( unsigned iniMatch, unsigned finMatch ) { { /* with */ ??? wr = NEW(TextFILE *).init(); /* do */ fprintf(wr, " [" & Fmt.Int(iniMatch)); fprintf(wr, "..%s]", Fmt.Int(finMatch) ); return TextFILE *oText(wr); } } /* FmtMatchedRange */ char *PairToText( double x, double y ) { return "(" & Fmt.LongReal(x, prec = 6) & ", " & Fmt.LongReal(y, prec = 6) & ")" } /* PairToText */ { /* 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. */