|
From: <kin...@us...> - 2024-07-24 23:33:53
|
Revision: 7231
http://sourceforge.net/p/teem/code/7231
Author: kindlmann
Date: 2024-07-24 23:33:49 +0000 (Wed, 24 Jul 2024)
Log Message:
-----------
main change: now can use a callback function as part of determining which vertices count as a corner
Modified Paths:
--------------
teem/trunk/src/limn/limn.h
teem/trunk/src/limn/lpu_cbfit.c
teem/trunk/src/limn/splineFit.c
Modified: teem/trunk/src/limn/limn.h
===================================================================
--- teem/trunk/src/limn/limn.h 2024-07-24 01:09:57 UTC (rev 7230)
+++ teem/trunk/src/limn/limn.h 2024-07-24 23:33:49 UTC (rev 7231)
@@ -552,7 +552,7 @@
** note: "nrp" = Newton-based Re-Parameterization of where the given points
** fall along the spline, the iterative process inside limnCbfSingle
*/
-typedef struct {
+typedef struct limnCbfCtx_t {
/* ----------- input ---------- */
int verbose, /* verbosity level */
cornerFind, /* do first search for corners: places where the path is not
@@ -560,7 +560,13 @@
continuous between multiple spline segments) */
cornerNMS; /* (if cornerFind) non-minimal-suppression of corners: accept as
corners only those with locally minimal angle */
- unsigned int nrpIterMax; /* max # iters of nrp */
+ int (*cornerCB)(double *tvtNew, /* if corner, set new TVT */
+ const struct limnCbfCtx_t *fctx, /* this struct */
+ double angleMeas, /* measured angle */
+ const double *tvtMeas, /* measured TVT */
+ const double *vertOrig); /* original vertex pos */
+ const void *cornerCBData; /* holds extra info for cornerCB */
+ unsigned int nrpIterMax; /* max # iters of nrp */
double
epsilon, /* error threshold on min distance from spline (as currently parameterized)
to given points: this affects both splitting done by limnCbfMulti, and
@@ -581,20 +587,20 @@
nrpPsi, /* don't even try nrp if max dist is bigger than nrpPsi*epsilon, instead
just subdivide */
nrpDeltaThresh, /* finish npr when mean parameterization change fall below this */
- alphaMin, /* alpha can't be negative, and we enforce distinct positivity to ensure
- that spline doesn't slow down too much near endpoints */
- detMin, /* abs(determinant) of 2x2 matrix to invert can't go below this */
- cornAngle, /* interior angle, in degrees, between (one-sided) incoming and outgoing
- tangents, *below* which a vertex should be considered a corner.
- Vertices in a straight line have an angle of 180 degrees. */
- wackyAngle; /* in cases where we are only looking at three points: a spline can
- always be fit through the middle point, even with constraints on
- position and tangent at first and last points, but the spline looks
- wacky if its tangent at the middle point is wildly different than
- the (two-sided) tangent that would have been estimated at that point
- for the purpose of splitting. If the angle (in degrees) between the
- two tangents exceeds this, then fitting will generate the simple
- (punted) arc, which will likely trigger splitting. */
+ alphaMin, /* alpha can't be negative, and we enforce distinct positivity to ensure
+ that spline doesn't slow down too much near endpoints */
+ detMin, /* abs(determinant) of 2x2 matrix to invert can't go below this */
+ cornerAngle, /* interior angle, in degrees, between (one-sided) incoming and outgoing
+ tangents, *below* which a vertex should be considered a corner.
+ Vertices in a straight line have an angle of 180 degrees. */
+ wackyAngle; /* in cases where we are only looking at three points: a spline can
+ always be fit through the middle point, even with constraints on
+ position and tangent at first and last points, but the spline looks
+ wacky if its tangent at the middle point is wildly different than
+ the (two-sided) tangent that would have been estimated at that point
+ for the purpose of splitting. If the angle (in degrees) between the
+ two tangents exceeds this, then fitting will generate the simple
+ (punted) arc, which will likely trigger splitting. */
/* ----------- internal --------- */
double *uu, /* used for nrp: buffer of parameterizations in [0,1] of point along
currently considered spline segment */
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-07-24 01:09:57 UTC (rev 7230)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-07-24 23:33:49 UTC (rev 7231)
@@ -136,7 +136,7 @@
"scaling on nrp epsilon check");
sprintf(buff, "%.17g", fctx->nrpPsi);
hestOptAdd_1_Double(&hopt, "psi", "psi", &psi, buff, "psi, of course");
- sprintf(buff, "%.17g", fctx->cornAngle);
+ sprintf(buff, "%.17g", fctx->cornerAngle);
hestOptAdd_1_Double(&hopt, "ca", "angle", &cangle, buff,
"angle indicating a corner, or, "
"0 to say that no corner finding should be done");
@@ -327,7 +327,7 @@
fctx->nrpIota = nrpIota;
fctx->nrpPsi = psi;
if (cangle) {
- fctx->cornAngle = cangle;
+ fctx->cornerAngle = cangle;
fctx->cornerFind = AIR_TRUE;
} else {
fctx->cornerFind = AIR_FALSE;
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-07-24 01:09:57 UTC (rev 7230)
+++ teem/trunk/src/limn/splineFit.c 2024-07-24 23:33:49 UTC (rev 7231)
@@ -38,6 +38,8 @@
the distance between the data and splines, so it is better at handling cases
where the chord-length-based parameterization initialization is terrible
- is smarter about handling single-spline fits to only 3 points
+- is more flexible about how corners (vertices without geometric continuity) are
+ defined and detected (including via callback)
- has robust error handling and error reporting
All of this adds implementation complexity (this is ~4 times longer than author's file)
@@ -55,7 +57,7 @@
neighbors of the point, with only lower or higher indies), or two-sided (looking at
neighbors on both sides of the point).
- corner: a point at which the angle between the one-sided tangents is below some
- threshold (limnCbfCtx->cornAngle), or, sometimes for completeness, the first and
+ threshold (limnCbfCtx->cornerAngle), or, sometimes for completeness, the first and
last points in an open path.
- span: a closed interval of point indices (typically "[loi,hii]"), within which
computations are done, often delimited by corners
@@ -161,7 +163,7 @@
described here, N=18 and N=19 do equally well (yay!)
*/
-#define PNMIN(ISLOOP) ((ISLOOP) ? 4 : 3)
+#define PNMIN(ISLOOP) ((ISLOOP) ? 3 : 3)
/*
limnCbfPointsNew
@@ -325,6 +327,8 @@
fctx->verbose = 0;
fctx->cornerFind = AIR_TRUE;
fctx->cornerNMS = AIR_TRUE;
+ fctx->cornerCB = NULL;
+ fctx->cornerCBData = NULL;
fctx->nrpIterMax = 40; /* authors originally thought ~6 */
fctx->epsilon = 0; /* NOTE: will need to be set to something valid elsewhere */
fctx->scale = 0; /* scale 0 means no filtering at all */
@@ -334,8 +338,8 @@
fctx->nrpDeltaThresh = 0.01;
fctx->alphaMin = 0.001;
fctx->detMin = 0.01;
- fctx->cornAngle = 120.0; /* degrees */
- fctx->wackyAngle = 30.0; /* degrees */
+ fctx->cornerAngle = 120.0; /* degrees */
+ fctx->wackyAngle = 30.0; /* degrees */
/* ----- internal state ----- */
/* initialize buffer pointers to NULL and buffer lengths to 0 */
fctx->uu = fctx->vw = fctx->tw = fctx->ctvt = NULL;
@@ -564,9 +568,9 @@
{
const double amin = 60;
const double amax = 180;
- if (!(amin <= fctx->cornAngle && fctx->cornAngle <= amax)) {
- biffAddf(LIMN, "%s: cornAngle (%g) outside sane range [%g,%g]", me,
- fctx->cornAngle, amin, amax);
+ if (!(amin <= fctx->cornerAngle && fctx->cornerAngle <= amax)) {
+ biffAddf(LIMN, "%s: cornerAngle (%g) outside sane range [%g,%g]", me,
+ fctx->cornerAngle, amin, amax);
return 1;
}
}
@@ -1612,6 +1616,12 @@
}
if (fctx->verbose) {
printf("%s: looking for corners among %u points\n", me, pnum);
+ if (fctx->verbose > 1 && pnum <= 10) {
+ const double *pp = PP(lpnt);
+ for (vi = 0; vi < pnum; vi++) {
+ printf(" %u: %g \t%g\n", vi, pp[0 + 2 * vi], pp[1 + 2 * vi]);
+ }
+ }
}
/* else we search for corners */
@@ -1653,7 +1663,18 @@
} else {
/* it is a loop, or: not a loop and at an interior point */
angle[vi] = 180 * ell_2v_angle_d(LT, RT) / AIR_PI;
- corny[vi] = (angle[vi] < fctx->cornAngle);
+ if (fctx->cornerCB) {
+ double tvtNew[6];
+ if (fctx->cornerCB(tvtNew, fctx, angle[vi], vtvt + 0 + 6 * vi,
+ PPlowerI(lpnt, vi))) {
+ corny[vi] = AIR_TRUE;
+ ELL_6V_COPY(vtvt + 0 + 6 * vi, tvtNew);
+ } else {
+ corny[vi] = AIR_FALSE;
+ }
+ } else {
+ corny[vi] = (angle[vi] < fctx->cornerAngle);
+ }
}
if (fctx->verbose > 1) {
printf("%s: vi=%3u corny %d angle %g\n", me, vi, corny[vi], angle[vi]);
@@ -1663,7 +1684,9 @@
}
}
}
- if (fctx->cornerNMS) {
+ if (fctx->cornerNMS && pnum >= 6) {
+ /* we only try non-maximal-supression on longer loops (HEY hard-coded value here);
+ ideally this would be a user-set-able parameter, but it would be very rarely set */
for (vi = 0; vi < pnum; vi++) {
if (!lpnt->isLoop && (!vi || vi == pnum - 1)) {
/* not a loop, and, either at first or last point ==> must stay a "corner" */
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|