You can subscribe to this list here.
| 2003 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(19) |
Nov
(45) |
Dec
(80) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2004 |
Jan
(58) |
Feb
(127) |
Mar
(74) |
Apr
(34) |
May
(117) |
Jun
(14) |
Jul
(26) |
Aug
(13) |
Sep
(1) |
Oct
(38) |
Nov
(13) |
Dec
(5) |
| 2005 |
Jan
(108) |
Feb
(134) |
Mar
(54) |
Apr
(133) |
May
(16) |
Jun
(54) |
Jul
(128) |
Aug
(99) |
Sep
(157) |
Oct
(182) |
Nov
(236) |
Dec
(212) |
| 2006 |
Jan
(86) |
Feb
(76) |
Mar
(121) |
Apr
(27) |
May
(7) |
Jun
(1) |
Jul
(6) |
Aug
(28) |
Sep
(1) |
Oct
(27) |
Nov
(5) |
Dec
|
| 2007 |
Jan
(32) |
Feb
(22) |
Mar
(22) |
Apr
(11) |
May
(3) |
Jun
(12) |
Jul
(11) |
Aug
(9) |
Sep
(37) |
Oct
(4) |
Nov
(9) |
Dec
(51) |
| 2008 |
Jan
(7) |
Feb
(31) |
Mar
(46) |
Apr
(31) |
May
(5) |
Jun
(27) |
Jul
(12) |
Aug
(5) |
Sep
(13) |
Oct
(24) |
Nov
(112) |
Dec
(15) |
| 2009 |
Jan
(6) |
Feb
(103) |
Mar
(66) |
Apr
(9) |
May
(8) |
Jun
(1) |
Jul
(20) |
Aug
(9) |
Sep
(2) |
Oct
(81) |
Nov
(88) |
Dec
(30) |
| 2010 |
Jan
(65) |
Feb
(57) |
Mar
(22) |
Apr
(12) |
May
(4) |
Jun
(12) |
Jul
(43) |
Aug
(6) |
Sep
(6) |
Oct
(4) |
Nov
(6) |
Dec
(3) |
| 2011 |
Jan
(10) |
Feb
(27) |
Mar
(11) |
Apr
(9) |
May
(69) |
Jun
(73) |
Jul
(67) |
Aug
(116) |
Sep
(40) |
Oct
(11) |
Nov
(34) |
Dec
(19) |
| 2012 |
Jan
|
Feb
(4) |
Mar
(28) |
Apr
(18) |
May
(9) |
Jun
(7) |
Jul
(4) |
Aug
(155) |
Sep
(264) |
Oct
(172) |
Nov
(15) |
Dec
(40) |
| 2013 |
Jan
(1) |
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
(20) |
Jul
(76) |
Aug
(67) |
Sep
(49) |
Oct
(27) |
Nov
(3) |
Dec
(3) |
| 2014 |
Jan
(7) |
Feb
(7) |
Mar
(16) |
Apr
|
May
(4) |
Jun
(1) |
Jul
(18) |
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
|
| 2015 |
Jan
(6) |
Feb
(5) |
Mar
(3) |
Apr
(23) |
May
(5) |
Jun
|
Jul
(2) |
Aug
(4) |
Sep
|
Oct
|
Nov
(2) |
Dec
(4) |
| 2016 |
Jan
(2) |
Feb
(7) |
Mar
(2) |
Apr
(1) |
May
(14) |
Jun
(3) |
Jul
|
Aug
(3) |
Sep
|
Oct
|
Nov
(1) |
Dec
(3) |
| 2017 |
Jan
(6) |
Feb
|
Mar
(3) |
Apr
|
May
|
Jun
|
Jul
|
Aug
(12) |
Sep
(6) |
Oct
|
Nov
(3) |
Dec
|
| 2018 |
Jan
(4) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
(8) |
Sep
|
Oct
|
Nov
|
Dec
(1) |
| 2019 |
Jan
|
Feb
|
Mar
(4) |
Apr
|
May
|
Jun
|
Jul
|
Aug
(3) |
Sep
(8) |
Oct
|
Nov
(2) |
Dec
(25) |
| 2020 |
Jan
|
Feb
(3) |
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
(3) |
Oct
(53) |
Nov
(33) |
Dec
|
| 2021 |
Jan
(2) |
Feb
|
Mar
|
Apr
|
May
|
Jun
(2) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(4) |
Dec
(5) |
| 2022 |
Jan
(1) |
Feb
|
Mar
|
Apr
|
May
|
Jun
(5) |
Jul
(93) |
Aug
(206) |
Sep
(39) |
Oct
(19) |
Nov
(11) |
Dec
|
| 2023 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(2) |
Jun
(150) |
Jul
(124) |
Aug
(14) |
Sep
(5) |
Oct
|
Nov
(1) |
Dec
|
| 2024 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(12) |
Jul
(62) |
Aug
|
Sep
(7) |
Oct
|
Nov
(7) |
Dec
|
| 2025 |
Jan
|
Feb
|
Mar
|
Apr
(14) |
May
(3) |
Jun
|
Jul
|
Aug
(76) |
Sep
(214) |
Oct
(6) |
Nov
|
Dec
|
|
From: <kin...@us...> - 2024-07-08 22:33:26
|
Revision: 7186
http://sourceforge.net/p/teem/code/7186
Author: kindlmann
Date: 2024-07-08 22:33:25 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
typo in verbose output
Modified Paths:
--------------
teem/trunk/src/limn/splineFit.c
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-07-08 20:23:19 UTC (rev 7185)
+++ teem/trunk/src/limn/splineFit.c 2024-07-08 22:33:25 UTC (rev 7186)
@@ -1140,7 +1140,7 @@
fctx->uu[ii] = 1;
}
if (fctx->verbose > 1) {
- printf("%s[%d,%d]: intial uu[%u] = %g\n", me, loi, hii, ii, fctx->uu[ii]);
+ printf("%s[%d,%d]: initial uu[%u] = %g\n", me, loi, hii, ii, fctx->uu[ii]);
}
}
delta /= spanlen - 2; /* within the pNum verts are pNum-2 interior verts */
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 20:23:21
|
Revision: 7185
http://sourceforge.net/p/teem/code/7185
Author: kindlmann
Date: 2024-07-08 20:23:19 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
this illustrates a situation for which single fit needs fixing
Modified Paths:
--------------
teem/trunk/src/limn/test/03-single.sh
Modified: teem/trunk/src/limn/test/03-single.sh
===================================================================
--- teem/trunk/src/limn/test/03-single.sh 2024-07-08 20:22:30 UTC (rev 7184)
+++ teem/trunk/src/limn/test/03-single.sh 2024-07-08 20:23:19 UTC (rev 7185)
@@ -33,16 +33,18 @@
# https://devmanual.gentoo.org/tools-reference/bash/
unset UNRRDU_QUIET_QUIT
-# HEY changing this from 200 to 400 to 800 significantly changes the fit; why?
-N=200
+# why changing this from 200 to 400 to 800 can significantly change the fit
+# because the nrp parms that make sense for a small number of points don't work great
+# for a huge number of points
+N=100
echo "-0.7 0.7
-1.4 0.7
-0.7 0.7
+2 0.7
+0 0.7
0.7 -0.7" | unu 2op x - 1 | unu 2op + - 0.0 | ./lpu cbfit -i - -synthn $N -sup 1 -syntho xy-0.txt
junk xy-0.txt
-./lpu cbfit -i xy-0.txt -scl 0 -psi 10000 -fs 0 -1 -v 3 -eps 0.001 2>&1 > log.txt
+./lpu cbfit -i xy-0.txt -scl 0 -fs 0 -1 -v 0 -psi 1000000000 -eps 0.001 -nim 8000 -deltathr 0.0001 -iota 0.01 -cap 10 2>&1 > log.txt
cat log.txt; junk log.txt
tail -n 4 log.txt | ./lpu cbfit -i - -synthn $N -sup 1 -syntho xy-1.txt
junk xy-1.txt
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 20:22:32
|
Revision: 7184
http://sourceforge.net/p/teem/code/7184
Author: kindlmann
Date: 2024-07-08 20:22:30 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
now lpu cbfit gets some defaults from limnCbfCtxNew
Modified Paths:
--------------
teem/trunk/src/limn/lpu_cbfit.c
teem/trunk/src/limn/splineFit.c
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-07-08 19:20:32 UTC (rev 7183)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-07-08 20:22:30 UTC (rev 7184)
@@ -34,14 +34,20 @@
int pret;
Nrrd *_nin, *nin;
- double *xy, deltaThresh, psi, cangle, epsilon, nrpIota, time0, dtime, scale, synthPow;
+ double *xy, deltaThresh, psi, cangle, epsilon, nrpIota, time0, dtime, scale, nrpCap,
+ synthPow;
unsigned int size0, size1, ii, synthNum, pNum, nrpIterMax;
int loop, petc, verbose, tvt[4], fitSingleLoHi[2];
- char *synthOut;
+ char *synthOut, buff[AIR_STRLEN_SMALL + 1];
limnCbfCtx *fctx;
limnCbfPath *path;
limnCbfPoints *lpnt;
+ mop = airMopNew();
+ airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
+ fctx = limnCbfCtxNew();
+ airMopAdd(mop, fctx, (airMopper)limnCbfCtxNix, airMopAlways);
+
hestOptAdd_1_Other(&hopt, "i", "input", &_nin, NULL, "input xy points", nrrdHestNrrd);
hestOptAdd_Flag(&hopt, "loop", &loop,
"-i input xy points are actually a loop: the first point logically "
@@ -60,20 +66,30 @@
"by raising to this power.");
hestOptAdd_4_Int(&hopt, "tvt", "loi hii vvi 1s", tvt, "0 0 0 -1",
"if last value is >= 0: make single call to limnCbfTVT and quit");
- hestOptAdd_1_UInt(&hopt, "im", "max", &nrpIterMax, "12",
+ sprintf(buff, "%u", fctx->nrpIterMax);
+ hestOptAdd_1_UInt(&hopt, "nim", "max", &nrpIterMax, buff,
"max # nrp iterations to run");
- hestOptAdd_1_Double(&hopt, "deltathr", "delta", &deltaThresh, "0.0005",
+ sprintf(buff, "%.17g", fctx->nrpDeltaThresh);
+ hestOptAdd_1_Double(&hopt, "deltathr", "delta", &deltaThresh, buff,
"(if non-zero) stop nrp when change in spline "
"domain sampling goes below this");
+ /* fctx does not come with useful default epsilon */
hestOptAdd_1_Double(&hopt, "eps", "dist", &epsilon, "0.01",
"(if non-zero) stop nrp when distance between spline "
"and points goes below this");
- hestOptAdd_1_Double(&hopt, "iota", "scl", &nrpIota, "0.25",
+ sprintf(buff, "%.17g", fctx->nrpIota);
+ hestOptAdd_1_Double(&hopt, "iota", "scl", &nrpIota, buff,
"scaling on nrp epsilon check");
- hestOptAdd_1_Double(&hopt, "psi", "psi", &psi, "10", "psi, of course");
- hestOptAdd_1_Double(&hopt, "ca", "angle", &cangle, "100", "angle indicating a corner");
- hestOptAdd_1_Double(&hopt, "scl", "scale", &scale, "0",
+ sprintf(buff, "%.17g", fctx->nrpPsi);
+ hestOptAdd_1_Double(&hopt, "psi", "psi", &psi, buff, "psi, of course");
+ sprintf(buff, "%.17g", fctx->cornAngle);
+ hestOptAdd_1_Double(&hopt, "ca", "angle", &cangle, buff, "angle indicating a corner");
+ sprintf(buff, "%.17g", fctx->scale);
+ hestOptAdd_1_Double(&hopt, "scl", "scale", &scale, buff,
"scale for geometry estimation");
+ sprintf(buff, "%.17g", fctx->nrpCap);
+ hestOptAdd_1_Double(&hopt, "cap", "cap", &nrpCap, buff,
+ "nrp cap parameterization change");
hestOptAdd_2_Int(&hopt, "fs", "loi hii", fitSingleLoHi, "-1 -1",
"(if loi is >= 0): just do a single call to limnCbfSingle and "
"quit, using the -i input points, and fitting a spline between "
@@ -85,9 +101,6 @@
hestOptAdd_1_String(&hopt, NULL, "output", &out, NULL, "output nrrd filename");
*/
- mop = airMopNew();
- airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
-
USAGE(myinfo);
PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
@@ -138,7 +151,7 @@
airMopAdd(mop, xy, airFree, airMopAlways);
if (2 == size0) {
unsigned int ci;
- printf("%s: synthetically sampling single spline with %u points", me, synthNum);
+ printf("%s: synthetically sampling single spline with %u points\n", me, synthNum);
for (ci = 0; ci < 8; ci++) {
seg.xy[ci] = cpt[ci];
}
@@ -190,11 +203,10 @@
airMopAdd(mop, lpnt, (airMopper)limnCbfPointsNix, airMopAlways);
path = limnCbfPathNew(0);
airMopAdd(mop, path, (airMopper)limnCbfPathNix, airMopAlways);
- fctx = limnCbfCtxNew();
- airMopAdd(mop, fctx, (airMopper)limnCbfCtxNix, airMopAlways);
fctx->verbose = verbose;
fctx->nrpIterMax = nrpIterMax;
fctx->scale = scale;
+ fctx->nrpCap = nrpCap;
fctx->epsilon = epsilon;
fctx->nrpDeltaThresh = deltaThresh;
fctx->nrpIota = nrpIota;
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-07-08 19:20:32 UTC (rev 7183)
+++ teem/trunk/src/limn/splineFit.c 2024-07-08 20:22:30 UTC (rev 7184)
@@ -276,11 +276,11 @@
fctx->verbose = 0;
fctx->cornerFind = AIR_TRUE;
fctx->cornerNMS = AIR_TRUE;
- fctx->nrpIterMax = 12;
- fctx->epsilon = 0; /* will need to be set to something valid elsewhere */
- fctx->scale = 0; /* will need to be set to something valid elsewhere */
- fctx->nrpCap = 3.0;
- fctx->nrpIota = 0.8;
+ 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 */
+ fctx->nrpCap = 10.0; /* not much of a cap, really */
+ fctx->nrpIota = 1.0 / 16; /* quite stringent */
fctx->nrpPsi = 100;
fctx->nrpDeltaThresh = 0.01;
fctx->alphaMin = 0.001;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 19:20:35
|
Revision: 7183
http://sourceforge.net/p/teem/code/7183
Author: kindlmann
Date: 2024-07-08 19:20:32 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
another script for debugging; may have found a bug (weird dependence on number of points in synthetic data)
Added Paths:
-----------
teem/trunk/src/limn/test/03-single.sh
Added: teem/trunk/src/limn/test/03-single.sh
===================================================================
--- teem/trunk/src/limn/test/03-single.sh (rev 0)
+++ teem/trunk/src/limn/test/03-single.sh 2024-07-08 19:20:32 UTC (rev 7183)
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+#
+# Teem: Tools to process and visualize scientific data and images
+# Copyright (C) 2009--2024 University of Chicago
+# Copyright (C) 2005--2008 Gordon Kindlmann
+# Copyright (C) 1998--2004 University of Utah
+#
+# This library is free software; you can redistribute it and/or modify it under the terms
+# of the GNU Lesser General Public License (LGPL) as published by the Free Software
+# Foundation; either version 2.1 of the License, or (at your option) any later version.
+# The terms of redistributing and/or modifying this software also include exceptions to
+# the LGPL that facilitate static linking.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along with
+# this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street,
+# Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+# 03 = FOURTH stage of testing- another test limnCbfSingle, via lpu cbfit -fs,
+# but with completely free-form input
+
+set -o errexit
+set -o nounset
+shopt -s expand_aliases
+JUNK=""
+function junk { JUNK="$JUNK $@"; }
+function cleanup { rm -rf $JUNK; }
+trap cleanup err exit int term
+# https://devmanual.gentoo.org/tools-reference/bash/
+unset UNRRDU_QUIET_QUIT
+
+# HEY changing this from 200 to 400 to 800 significantly changes the fit; why?
+N=200
+
+echo "-0.7 0.7
+1.4 0.7
+0.7 0.7
+0.7 -0.7" | unu 2op x - 1 | unu 2op + - 0.0 | ./lpu cbfit -i - -synthn $N -sup 1 -syntho xy-0.txt
+junk xy-0.txt
+
+./lpu cbfit -i xy-0.txt -scl 0 -psi 10000 -fs 0 -1 -v 3 -eps 0.001 2>&1 > log.txt
+cat log.txt; junk log.txt
+tail -n 4 log.txt | ./lpu cbfit -i - -synthn $N -sup 1 -syntho xy-1.txt
+junk xy-1.txt
+
+BIN=500
+for I in 0 1; do
+ unu jhisto -i xy-$I.txt -min -1.1 1.1 -max 1.1 -1.1 -b $BIN $BIN |
+ unu quantize -b 8 -max 1 -o xy-$I.png
+done
+
+unu join -i xy-{1,0,1}.png -a 0 -incr |
+ unu resample -s = x2 x2 -k box -o tmp.png
+
+open tmp.png
Property changes on: teem/trunk/src/limn/test/03-single.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 18:34:37
|
Revision: 7182
http://sourceforge.net/p/teem/code/7182
Author: kindlmann
Date: 2024-07-08 18:34:34 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
re-wrote idxLift and limnCbfTVT, and things may finally be working, but testing ongoing
Modified Paths:
--------------
teem/trunk/src/limn/lpu_cbfit.c
teem/trunk/src/limn/splineFit.c
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-07-08 18:32:55 UTC (rev 7181)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-07-08 18:34:34 UTC (rev 7182)
@@ -206,10 +206,17 @@
int pnum = AIR_INT(lpnt->num);
/* whoa - this is how GLK learned that AIR_MOD is garbage if args differ in
sign-ed-ness */
- unsigned int loi = AIR_UINT(AIR_MOD(tvt[0], pnum));
- unsigned int hii = AIR_UINT(AIR_MOD(tvt[1], pnum));
- unsigned int vvi = AIR_UINT(AIR_MOD(tvt[2], pnum));
+ unsigned int loi, hii, vvi;
int E, oneSided = !!tvt[3];
+ if (loop) {
+ loi = AIR_UINT(AIR_MOD(tvt[0], pnum));
+ hii = AIR_UINT(AIR_MOD(tvt[1], pnum));
+ vvi = AIR_UINT(AIR_MOD(tvt[2], pnum));
+ } else {
+ loi = AIR_UINT(AIR_CLAMP(0, tvt[0], pnum - 1));
+ hii = AIR_UINT(AIR_CLAMP(0, tvt[1], pnum - 1));
+ vvi = AIR_UINT(AIR_CLAMP(0, tvt[2], pnum - 1));
+ }
E = 0;
if (!E && fctx->verbose)
printf("%s: int %d in [%d,%d] --> uint %u in [%u,%u]\n", me, /* */
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-07-08 18:32:55 UTC (rev 7181)
+++ teem/trunk/src/limn/splineFit.c 2024-07-08 18:34:34 UTC (rev 7182)
@@ -30,15 +30,18 @@
https://dl.acm.org/doi/10.5555/90767.90941
The author's code is here:
http://www.realtimerendering.com/resources/GraphicsGems/gems/FitCurves.c
+but the code here was based more on reading the paper, than their code. Also, that code
+does not handle point loops, and does not handle smoothing built into tangent estimation,
+which were important to GLK, but which added significant implementation complexity.
The functions below do not use any other limnSpline structs or functions, since those
were written a long time ago when GLK was even more ignorant than now about splines.
-Hopefully that other code can be revisited and re-organized for a later version of
+Hopefully that older code can be revisited and re-organized for a later version of
Teem, at which point the code below can be integrated with it.
NOTE: spline fitting would be useful in 3D (or higher dimensions) too, but currently
this code only supports 2D. "DIM=2" flags places in code where that is explicit, with
-the hope that this code can later by generalized.
+the hope that this code can later be generalized.
NOTE: In Teem coding standards, "Cbf" would be better written as "CBF", but all these
initials got annoying with other CamelCase function names.
@@ -610,20 +613,23 @@
#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
/* idxLift: error-checked index lifting (from "actual" to "lifted" indices, explained
-above) for limnCbfTVT and maybe others. This is messy because of the flexibility in how
-we handle points: might not be a loop (so an actual [loi,hii] vertex index span is
-needed), or, is a loop but still only working within bounds of [loi,hii] span, or, is a
-loop and [loi,hii]=[0,0] says that there are no bounds (aka "loopy") */
+above) for limnCbfTVT and maybe others. If no error, *loiP will be same as given gloi,
+but in loops *vviP and *hiiP may be lifted (relative to gvvi and ghii), and outside loops
+*hiiP may be changed to #points-1.
+
+That sounds like nothing fancy, but this is messy because of the flexibility in how we
+handle points: might not be a loop or might not, and, consideration of vertices should
+either be bounded in a specific [loi,hii] or be "unbounded" loi==hii==0 (truly unbounded
+in a loop, or bounded only as much as needed in non-loop data). */
static int /* Biff: 1 */
-idxLift(uint *loiP, uint *hiiP, uint *vviP, int *loopyP, int verbose,
- const limnCbfPoints *lpnt, uint gloi, uint ghii, uint gvvi) {
+idxLift(uint *loiP, uint *hiiP, uint *vviP, int verbose, const limnCbfPoints *lpnt,
+ uint gloi, uint ghii, uint gvvi) {
static const char me[] = "idxLift";
- uint pnum, loi, hii, vvi;
- int loopy;
+ uint pnum = lpnt->num, loi, hii, vvi;
*loiP = *hiiP = *vviP = UINT_MAX; /* initialize to bogus indices */
- pnum = lpnt->num;
if (!(pnum < (1U << 29))) {
+ /* UB = undefined behavior */
biffAddf(LIMN, "%s: # points %u seems too big (to stay well clear of UB)", me, pnum);
return 1;
}
@@ -632,57 +638,56 @@
ghii, gvvi, pnum);
return 1;
}
+ /* now all of gloi, gvvi, ghii are all valid actual indices */
if (gloi == ghii && ghii != 0) {
- biffAddf(
- LIMN,
- "%s: can only have gloi == ghii if both 0 (not %u), to signify no bounds in "
- "point loop%s",
- me, gloi,
- lpnt->isLoop ? "" /* it is a loop */
- : " (and these points aren't in a loop anyway!)");
+ biffAddf(LIMN,
+ "%s: can only have gloi == ghii if both 0 (not %u), "
+ "to signify unbounded vertex consideration",
+ me, gloi);
return 1;
}
- /* else loi == hii implies loi == hii == 0 */
+ /* initialize values to return */
loi = gloi;
hii = ghii;
vvi = gvvi;
if (lpnt->isLoop) {
- if (0 == gloi && 0 == ghii) {
- loopy = AIR_TRUE;
- } else {
- if (ghii < gloi) hii += pnum;
- if (gvvi < gloi) vvi += pnum;
- loopy = AIR_FALSE;
+ if (gloi != ghii) { /* implies both == 0 because of test above */
+ if (gloi > ghii) hii += pnum;
+ if (gloi > gvvi) vvi += pnum;
}
} else {
- if (0 == gloi && 0 == ghii) {
- biffAddf(LIMN, "%s: can only have given loi == hii == 0 with point loop", me);
- return 1;
+ if (gloi == ghii) { /* (implies both == 0, again) */
+ /* we allow loi==hii==0 in non-loop to say: only bounded by data itself
+ loi is already 0, but hii needs fixing */
+ hii = pnum - 1;
+ } else {
+ if (gloi > ghii) {
+ biffAddf(LIMN,
+ "%s: if loi != hii, need loi (%u) < hii (%u) since not in a "
+ "point loop",
+ me, gloi, ghii);
+ return 1;
+ }
+ if (gloi > gvvi) {
+ biffAddf(LIMN, "%s: need given loi (%u) < vvi (%u) since not in point loop", me,
+ gloi, gvvi);
+ return 1;
+ }
}
- if (!(gloi < ghii)) {
- biffAddf(LIMN, "%s: need given loi (%u) < hii (%u) since not in point loop", me,
- gloi, ghii);
- return 1;
- }
- if (!(gvvi <= ghii)) {
- biffAddf(LIMN, "%s: need given vvi (%u) < hhi (%u) since not in point loop", me,
- gvvi, ghii);
- return 1;
- }
- loopy = AIR_FALSE;
}
+ /* now: must have loi <= vvi and loi <= hii */
if (verbose) {
- printf("%s: given loi,hii,vvi %u %u %u --> my %u %u %u loopy %d\n", me, gloi, ghii,
- gvvi, loi, hii, vvi, loopy);
+ printf("%s: given loi,hii,vvi %u %u %u --> lifted %u %u %u\n", me, gloi, ghii, gvvi,
+ loi, hii, vvi);
}
- /* now: must have loi < hii and vvi < hii */
- if (!loopy) {
+ if (loi < hii) {
/* need to check that vvi is inside consequential bounds [loi,hii] */
- if (!(loi <= vvi && vvi <= hii)) {
- biffAddf(LIMN, "%s: vvi %u->%u not in [%u,%u]->[%u,%u] span (not in loop)", me,
- gvvi, vvi, gloi, ghii, loi, hii);
+ if (vvi > hii) {
+ biffAddf(LIMN, "%s: vvi %u->%u not in [%u,%u]->[%u,%u] span", me, gvvi, vvi, gloi,
+ ghii, loi, hii);
return 1;
}
+ /* now (if bounded) have vvi <= hii */
}
/* all's well, set output values */
@@ -689,7 +694,6 @@
*loiP = loi;
*hiiP = hii;
*vviP = vvi;
- *loopyP = loopy;
return 0;
}
@@ -700,18 +704,38 @@
ELL_2V_NORM(dir, dir, len); /* normalize(dir) */
}
+/* utility function for getting pointer to position coordinates in lpnt,
+for point with signed and lifted index ssi. So this is the single place
+that we go from lifted index to actual index */
+static const double *
+PPlowerI(const limnCbfPoints *lpnt, int ssi) {
+ int pnum = lpnt->num;
+ ssi = AIR_MOD(ssi, pnum);
+ return PP(lpnt) + 2 * ssi; /* DIM=2 */
+}
+
+/* utility function for counting how many vertices are in (actual) index span [loi,hii]
+inclusive. It is not our job here to care about lpnt->isLoop; we just assume that if
+we're faced with hii<loi, it must be because of a loop */
+static uint
+spanLength(const limnCbfPoints *lpnt, uint loi, uint hii) {
+ uint topi = hii + (hii < loi) * (lpnt->num);
+ return topi - loi + 1;
+}
+
/*
limnCbfTVT: Find constraints for spline fitting: incoming/left tangent lt, center or
endpoint vertex vv, outgoing/right tangent rt; any but not all can be NULL. These are
-computed from the given points lpnt, at given vertex index gvvi, looking only within
-vertex index range [gloi, ghii]: that range is probably delimited by corners, and we have
-to be blind to anything past the corners on either side of us. HOWEVER, f gloi==ghii==0
-and lpnt is a point loop, then we can look at all the points.
+computed from the given points lpnt, at given vertex (actual) index gvvi, looking only
+within (actual) index range [gloi, ghii] if gloi!=ghii: that range is probably delimited
+by corners, and we have to be blind to anything past the corners on either side of us.
+HOWEVER, if gloi==ghii==0 and lpnt is a point loop, then we can look at all the points.
Given that this is the inner-loop of other things, it would make sense to have a
non-public version without all the error checking, but given the prolonged birthing pain
-of the code in this file, the error-checking is a useful and welcome safety-net, and is
-ok until profiling shows it is actually a problem.
+of the code in this file, the error-checking is a useful and welcome safety-net (and
+being a public function permits easier testing), and that is all ok until profiling shows
+that we are a bottleneck.
NOTE: this assumes that limnCbfCtxPrep(fctx, lpnt) was called without error!
That (via ctxBuffersSet) allocates things that we depend on here.
@@ -720,71 +744,60 @@
limnCbfTVT(double lt[2], double vv[2], double rt[2], const limnCbfCtx *fctx,
const limnCbfPoints *lpnt, uint gloi, uint ghii, uint gvvi, int oneSided) {
static const char me[] = "limnCbfTVT";
+ uint loi, /* error-checked gloi */
+ hii, /* error-checked ghii, lifted if needed */
+ vvi; /* error-checked gvvi, lifted if needed */
/* we use here (signed) int for things that might seem better as uint, but it
simplifies implementing arithmetic and comparisons given how indices wrap around in
point loops */
- uint loi, /* error-checked gloi */
- hii, /* error-checked ghii, with loop handling */
- vvi; /* error-checked gvvi, with loop handling */
- int slo, shi, /* signed versions of loi, hii */
- pnum, /* total number of points in lpnts */
- loopy, /* lpnt->isLoop && 0 == loi == hii, i.e. there are no bounds on indices */
- icent, iplus, imnus; /* icent is the actual data index corresponding to vvi; it is
- used for both the scale==0 and scale>0 cases; iplus and imnus
- are only needed with scale==0, but breaking those out into
- that specific branch is not worth the copypasta */
+ int slo, shi, svi; /* signed versions of loi, hii, vvi */
if (!((lt || vv || rt) && fctx && lpnt)) {
biffAddf(LIMN, "%s: got NULL pointer (or too many NULL pointers)", me);
return 1;
}
- /* so: each of lt, vv, rt can be NULL (they just can't be all NULL) */
+ /* so: each of lt, vv, rt can be NULL
+ (they just can't be all NULL, or else why are we being called) */
if (fctx->verbose > 1) {
printf("%s: hello: %u in [%u,%u] in %sloop with %u points (%s-sided)\n", me, gvvi,
gloi, ghii, lpnt->isLoop ? "" : "NON-", lpnt->num, oneSided ? "1" : "2");
}
- if (idxLift(&loi, &hii, &vvi, &loopy, fctx->verbose > 1, lpnt, gloi, ghii, gvvi)) {
+ if (idxLift(&loi, &hii, &vvi, fctx->verbose > 1, lpnt, gloi, ghii, gvvi)) {
biffAddf(LIMN, "%s: trouble with given loi %u, hii %u, or vvi %u", me, gloi, ghii,
gvvi);
return 1;
}
- pnum = AIR_INT(lpnt->num); /* YES really needs to be signed int; see below */
+ /* NOTE: If scale==0, then we will get NaNs for left tangent if loi == vvi, and for
+ right tangent if hii == vvi, but will avoid NaNs if scale > 0. Because it will be too
+ annoying to require being called in different ways depending on scale, we do *not* do
+ error-checking to prevent NaN generation. */
if (fctx->verbose) {
- printf("%s: (post-idxLift) %u in [%u,%u] -> %u in [%u,%u] (loopy=%d)\n", me, gvvi,
- gloi, ghii, vvi, loi, hii, loopy);
+ printf("%s: (post-idxLift) %u in [%u,%u] -> %u in [%u,%u]\n", me, gvvi, gloi, ghii,
+ vvi, loi, hii);
}
- /* now:
- -- 0 == loi == hii implies lpnt->isLoop (and this is called "loopy")
- (but loopy does not imply lpnt->isLoop: can work in loop on sub-span overlapping 0)
- -- loi == hii != 0 is always impossible
- -- always: loi < hii (even if given ghii < gloi), and thus any indices in range
- [loi,hii] need to be mod'd with pnum before indexing into PP(lpnt) */
- /* ------------------- now switch to signed ints ------------------- */
+ /* ----------- now switch to signed (lifted) indices ---------- */
slo = AIR_INT(loi);
shi = AIR_INT(hii);
- icent = AIR_INT(vvi);
- iplus = icent + 1;
- imnus = icent - 1;
- if (!loopy) {
- /* this is the code that motivated if (hii < loi) hii += pnum;
- otherwise clamping is too annoying */
- icent = AIR_CLAMP(slo, icent, shi);
- iplus = AIR_CLAMP(slo, iplus, shi);
- imnus = AIR_CLAMP(slo, imnus, shi);
- }
- /* now map to actual indices */
- icent = AIR_MOD(icent, pnum);
- iplus = AIR_MOD(iplus, pnum);
- imnus = AIR_MOD(imnus, pnum);
+ svi = AIR_INT(vvi);
if (0 == fctx->scale) {
/* DIM=2 through-out */
- const double *xyC = PP(lpnt) + 2 * icent, *xyP, *xyM;
+ const double *xyC, *xyP, *xyM;
+ int iplus = svi + 1, imnus = svi - 1;
+ if (slo < shi) { /* bounded */
+ iplus = AIR_CLAMP(slo, iplus, shi);
+ imnus = AIR_CLAMP(slo, imnus, shi);
+ }
+ xyM = PPlowerI(lpnt, imnus);
+ xyC = PPlowerI(lpnt, svi);
+ xyP = PPlowerI(lpnt, iplus);
+ if (fctx->verbose > 1) {
+ printf("%s: %d | %d | %d --> (%g,%g)|(%g,%g)|(%g,%g)\n", me, imnus, svi, iplus,
+ xyM[0], xyM[1], xyC[0], xyC[1], xyP[0], xyP[1]);
+ }
if (vv) {
ELL_2V_COPY(vv, xyC);
}
- xyP = PP(lpnt) + 2 * iplus;
- xyM = PP(lpnt) + 2 * imnus;
if (rt) {
subnorm2(rt, xyP, oneSided ? xyC : xyM);
}
@@ -796,43 +809,41 @@
/* for simplicity: regardless of dir, we compute average positions for points
centered around loi + ofi (posC), and for lower/higher indices (posM/posP) */
double posM[2] = {0, 0}, posC[2] = {0, 0}, posP[2] = {0, 0};
- const double *vw = fctx->vw;
- const double *tw = fctx->tw;
+ const double *vwa = fctx->vw;
+ const double *twa = fctx->tw;
int lim = (int)fctx->wlen - 1, /* limit on loop index */
ci; /* loops through [-lim,lim] */
- if (!(vw && tw)) {
+ if (!(vwa && twa)) {
biffAddf(LIMN, "%s: fctx internal buffers vw and tw not both allocated", me);
return 1;
}
- if (tw[0] != 0) {
- biffAddf(LIMN, "%s: first tangent weight fctx->tw[0] %g not zero", me, tw[0]);
+ if (twa[0] != 0) {
+ biffAddf(LIMN, "%s: first tangent weight fctx->tw[0] %g not zero", me, twa[0]);
return 1;
}
for (ci = -lim; ci <= lim; ci++) {
- uint wi = abs(ci); /* weight index into vw, tw */
- int di = icent + ci, /* signed (and not %-ed) index into data */
- cdi, /* clamped data index */
- adi; /* actual data index */
- const double *xy;
- cdi = loopy ? di : AIR_CLAMP(slo, di, shi);
- adi = AIR_MOD(cdi, pnum);
- xy = PP(lpnt) + 2 * adi;
- ELL_2V_SCALE_INCR(posC, vw[wi], xy);
+ uint wi = abs(ci); /* weight index into vwa, twa */
+ double vw = vwa[wi], tw = twa[wi]; /* current vert and tan weights */
+ int sui = svi + ci; /* signed, unbounded, vertex index */
+ int sbi = slo < shi /* signed, bounded, vertex index */
+ ? AIR_CLAMP(slo, sui, shi)
+ : sui;
+ const double *xy = PPlowerI(lpnt, sbi); /* coords at sbi */
+ ELL_2V_SCALE_INCR(posC, vw, xy);
if (fctx->verbose > 1) {
- printf(
- "%s: ci=%d (in [%d,%d]) di=%d cdi=%d (in[%d,%d]) adi=%d; v,t w %g,%g on "
- "xy=(%g,%g)\n",
- me, ci, -lim, lim, di, cdi, slo, shi, adi, vw[wi], tw[wi], xy[0], xy[1]);
+ printf("%s: ci=%d (in [%d,%d]) idx %d --[%d,%d]--> %d; v,t w %g,%g on "
+ "xy=(%g,%g)\n",
+ me, ci, -lim, lim, sui, slo, shi, sbi, vw, tw, xy[0], xy[1]);
printf("%s: ---> posC=(%g,%g)\n", me, posC[0], posC[1]);
}
if (ci < 0) {
- ELL_2V_SCALE_INCR(posM, tw[wi], xy);
+ ELL_2V_SCALE_INCR(posM, tw, xy);
if (fctx->verbose > 1) {
printf("%s: ---> posM=(%g,%g)\n", me, posM[0], posM[1]);
}
}
if (ci > 0) {
- ELL_2V_SCALE_INCR(posP, tw[wi], xy);
+ ELL_2V_SCALE_INCR(posP, tw, xy);
if (fctx->verbose > 1) {
printf("%s: ---> posP=(%g,%g)\n", me, posP[0], posP[1]);
}
@@ -842,14 +853,21 @@
/* limit distance from chosen (x,y) datapoint to posC to be (yes, harcoded) 95% of
fctx->epsilon. Being allowed to be further away can cause annoyances (for GLK in
some early stage of debugging) */
- double off[2], offlen, okoff = 0.95 * fctx->epsilon; /* DIM=2 throughout */
- const double *xy = PP(lpnt) + 2 * icent; /* center vertex in given data */
+ double off[2], offlen, clofflen,
+ okoff = 0.95 * fctx->epsilon; /* DIM=2 throughout */
+ const double *xy = PPlowerI(lpnt, svi); /* center vertex in given data */
ELL_2V_SUB(off, posC, xy); /* off = posC - xy, from given to computed */
ELL_2V_NORM(off, off, offlen); /* offlen = |off|; off /= |off| */
- offlen = AIR_MIN(okoff, offlen);
+ clofflen = AIR_MIN(okoff, offlen);
/* difference between chosen (x,y) datapoint and spline endpoint
can be in any direction, but we limit the length */
- ELL_2V_SCALE_ADD2(posC, 1, xy, offlen, off);
+ ELL_2V_SCALE_ADD2(posC, 1, xy, clofflen, off);
+ if (fctx->verbose > 1) {
+ printf("%s: clamping |posC - xy[%d]=(%g,%g)| dist %g to %g = %g --> (%g,%g)\n",
+ me, svi, xy[0], xy[1], offlen, okoff, clofflen, posC[0], posC[1]);
+ printf("%s: also: posM = (%g,%g) posP = (%g,%g)\n", me, posM[0], posM[0],
+ posP[0], posP[1]);
+ }
}
if (lt) {
subnorm2(lt, posM, oneSided ? posC : posP);
@@ -864,24 +882,6 @@
return 0;
}
-/* utility function for counting how many vertices are in (actual) index span [loi,hii]
-inclusive. It is not our job here to care about lpnt->isLoop; we just assume that if
-we're faced with hii<loi, it must be because of a loop */
-static uint
-spanLength(const limnCbfPoints *lpnt, uint loi, uint hii) {
- uint topi = hii + (hii < loi) * (lpnt->num);
- return topi - loi + 1;
-}
-
-/* utility function for getting pointer to coords in lpnt,
-for point with (lifted) index loi+ofi,
-while accounting for possibility of wrapping */
-static const double *
-pointPos(const limnCbfPoints *lpnt, uint loi, uint ofi) {
- uint ii = (loi + ofi) % lpnt->num;
- return PP(lpnt) + 2 * ii; /* DIM=2 */
-}
-
/*
(from paper page 620) solves for the alpha that minimize squared error between xy[i]
and Q(uu[i]) where Q(t) is cubic Bezier spline through vv0, vv0 + alpha[0]*tt1,
@@ -918,7 +918,7 @@
const double *uu = fctx->uu;
xx[0] = xx[1] = m11 = m12 = m22 = 0;
for (ii = 0; ii < spanlen; ii++) {
- const double *xy = pointPos(lpnt, loi, ii);
+ const double *xy = PPlowerI(lpnt, AIR_INT(loi + ii));
double bb[4], Ai1[2], Ai2[2], Pi[2], dmP[2];
double ui = uu[ii];
VCBD0(bb, ui);
@@ -939,7 +939,7 @@
ELL_4V_SET(MM, m11, m12, m12, m22);
ELL_2M_INV(MI, MM, det);
ELL_2MV_MUL(alpha, MI, xx);
- } else { /* pNum <= 2 */
+ } else { /* spanlen <= 2 */
det = 1; /* bogus but harmless */
alpha[0] = alpha[1] = 0; /* trigger simple arc code */
}
@@ -1000,7 +1000,7 @@
CBD0(Q, vv0, vv1, vv2, vv3, tt, ww);
CBD1(QD, vv0, vv1, vv2, vv3, tt, ww);
CBD2(QDD, vv0, vv1, vv2, vv3, tt, ww);
- xy = pointPos(lpnt, loi, ii);
+ xy = PPlowerI(lpnt, AIR_INT(loi + ii));
ELL_2V_SUB(df, Q, xy);
numer = ELL_2V_DOT(df, QD);
denom = ELL_2V_DOT(QD, QD) + ELL_2V_DOT(df, QDD);
@@ -1041,7 +1041,7 @@
are actually sufficiently close to the first and last points (or else the fit spline
won't meet the expected accuracy threshold) */
for (ii = 1; ii < spanlen - 1; ii++) {
- const double *xy = pointPos(lpnt, loi, ii);
+ const double *xy = PPlowerI(lpnt, AIR_INT(loi + ii));
double len, Q[2], df[2], ww[4];
CBD0(Q, vv0, vv1, vv2, vv3, uu[ii], ww);
ELL_2V_SUB(df, Q, xy);
@@ -1119,8 +1119,8 @@
double len;
const double *xyP, *xyM;
fctx->uu[0] = len = 0;
- xyM = pointPos(lpnt, loi, 0);
- xyP = pointPos(lpnt, loi, 1);
+ xyM = PPlowerI(lpnt, AIR_INT(loi));
+ xyP = PPlowerI(lpnt, AIR_INT(loi + 1));
for (ii = 1; ii < spanlen; ii++) {
double dd[2];
ELL_2V_SUB(dd, xyP, xyM);
@@ -1127,8 +1127,8 @@
len += ELL_2V_LEN(dd);
fctx->uu[ii] = len;
xyM = xyP;
- /* yes on last iter this is set to possibly bogus pointer but it's never used */
- xyP = pointPos(lpnt, loi, ii + 1);
+ /* yes on last iter this is set to wrong coord but it's never used */
+ xyP = PPlowerI(lpnt, AIR_INT(loi + ii + 1));
}
delta = 0;
for (ii = 0; ii < spanlen; ii++) {
@@ -1395,13 +1395,13 @@
double *RT = tvt + 4 + 6 * vi;
/* we find TVT for *every* vertex, despite this seeming like computational overkill.
Why: we don't know which vertex might be corner until we look at the
- tangent-to-tangent angles for each vertex, for which we don't need to know the vertex
- position (non-trivial as long as scale > 0). But once we do know which vertices are
+ tangent-to-tangent angles for EVERY vertex, for which we don't need to know the
+ vertex position (non-trivial when scale > 0). But once we do know which vertices are
corners, we'll need to know where the vertices are, but it would be unfortunate
revisit the input data (used previously for tangent estimation) to figure out the
corner vertices. In a non-loop, we know first and last points will be corners, but we
still need to find the vertex pos and (one-sided) tangent. */
- if (limnCbfTVT(LT, VV, RT, fctx, lpnt, 0, 0, vi, oneSided)) {
+ if (limnCbfTVT(LT, VV, RT, fctx, lpnt, 0 /* loi */, 0 /* hii */, vi, oneSided)) {
biffAddf(LIMN, "%s: trouble with tangents or vertices for point %u/%u", me, vi,
pnum);
airMopError(mop);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 18:32:57
|
Revision: 7181
http://sourceforge.net/p/teem/code/7181
Author: kindlmann
Date: 2024-07-08 18:32:55 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
testing continues
Modified Paths:
--------------
teem/trunk/src/limn/test/01-test-tvt.sh
teem/trunk/src/limn/test/02-test-fs.sh
Modified: teem/trunk/src/limn/test/01-test-tvt.sh
===================================================================
--- teem/trunk/src/limn/test/01-test-tvt.sh 2024-07-08 18:09:58 UTC (rev 7180)
+++ teem/trunk/src/limn/test/01-test-tvt.sh 2024-07-08 18:32:55 UTC (rev 7181)
@@ -32,8 +32,8 @@
# https://devmanual.gentoo.org/tools-reference/bash/
unset UNRRDU_QUIET_QUIT
-#IN=circ.txt
-IN=pointy.txt
+IN=circ.txt
+#IN=pointy.txt
N=$(cat $IN | wc -l | xargs echo)
VVOUT=out-vv.txt
LTOUT=out-rt.txt
@@ -50,7 +50,7 @@
# * without -loop and with -loop
# * -scl 0 and >0
# * oneside (4th arg to -tvt) 0 and 1
- CMD="./lpu cbfit -i $IN -loop -scl 1 -tvt $LO $HI $I 0 -eps 1 -v 0"
+ CMD="./lpu cbfit -i $IN -scl 0 -tvt $LO $HI $I 1 -eps 1 -v 0"
echo $CMD
rm -f log.txt
(eval $CMD 2>&1) > log.txt
Modified: teem/trunk/src/limn/test/02-test-fs.sh
===================================================================
--- teem/trunk/src/limn/test/02-test-fs.sh 2024-07-08 18:09:58 UTC (rev 7180)
+++ teem/trunk/src/limn/test/02-test-fs.sh 2024-07-08 18:32:55 UTC (rev 7181)
@@ -49,7 +49,7 @@
echo $LO
HI=$((LO+7))
LOO=$(printf %02d $LO)
- CMD="./lpu cbfit -i $IN -loop -scl 0 -psi 1000 -fs $LO $HI -v 0 -eps 0.01"
+ CMD="./lpu cbfit -i $IN -loop -scl 3 -psi 1000 -fs $LO $HI -v 0 -eps 0.3"
echo "==================== $LO $HI --> test-$LOO.png : $CMD"
eval $CMD 2>&1 > log-$LOO.txt
# cat log-$LOO.txt
@@ -62,3 +62,5 @@
# out: magenta
unu join -i out.png in.png out.png -a 0 -incr -o test-$LOO.png
done
+
+open test-??.png
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 18:10:01
|
Revision: 7180
http://sourceforge.net/p/teem/code/7180
Author: kindlmann
Date: 2024-07-08 18:09:58 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
testing in progress
Modified Paths:
--------------
teem/trunk/src/limn/test/01-test-tvt.sh
teem/trunk/src/limn/test/02-test-fs.sh
Modified: teem/trunk/src/limn/test/01-test-tvt.sh
===================================================================
--- teem/trunk/src/limn/test/01-test-tvt.sh 2024-07-08 17:44:32 UTC (rev 7179)
+++ teem/trunk/src/limn/test/01-test-tvt.sh 2024-07-08 18:09:58 UTC (rev 7180)
@@ -32,7 +32,8 @@
# https://devmanual.gentoo.org/tools-reference/bash/
unset UNRRDU_QUIET_QUIT
-IN=circ.txt
+#IN=circ.txt
+IN=pointy.txt
N=$(cat $IN | wc -l | xargs echo)
VVOUT=out-vv.txt
LTOUT=out-rt.txt
@@ -49,7 +50,7 @@
# * without -loop and with -loop
# * -scl 0 and >0
# * oneside (4th arg to -tvt) 0 and 1
- CMD="./lpu cbfit -i $IN -loop -scl 2 -tvt $LO $HI $I 1 -eps 1 -v 0"
+ CMD="./lpu cbfit -i $IN -loop -scl 1 -tvt $LO $HI $I 0 -eps 1 -v 0"
echo $CMD
rm -f log.txt
(eval $CMD 2>&1) > log.txt
Modified: teem/trunk/src/limn/test/02-test-fs.sh
===================================================================
--- teem/trunk/src/limn/test/02-test-fs.sh 2024-07-08 17:44:32 UTC (rev 7179)
+++ teem/trunk/src/limn/test/02-test-fs.sh 2024-07-08 18:09:58 UTC (rev 7180)
@@ -49,7 +49,7 @@
echo $LO
HI=$((LO+7))
LOO=$(printf %02d $LO)
- CMD="./lpu cbfit -i $IN -loop -scl 1 -psi 1000 -fs $LO $HI -v 0 -eps 0.03"
+ CMD="./lpu cbfit -i $IN -loop -scl 0 -psi 1000 -fs $LO $HI -v 0 -eps 0.01"
echo "==================== $LO $HI --> test-$LOO.png : $CMD"
eval $CMD 2>&1 > log-$LOO.txt
# cat log-$LOO.txt
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 17:44:34
|
Revision: 7179
http://sourceforge.net/p/teem/code/7179
Author: kindlmann
Date: 2024-07-08 17:44:32 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
one way to test limnCbfSingle
Added Paths:
-----------
teem/trunk/src/limn/test/02-test-fs.sh
Added: teem/trunk/src/limn/test/02-test-fs.sh
===================================================================
--- teem/trunk/src/limn/test/02-test-fs.sh (rev 0)
+++ teem/trunk/src/limn/test/02-test-fs.sh 2024-07-08 17:44:32 UTC (rev 7179)
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+#
+# Teem: Tools to process and visualize scientific data and images
+# Copyright (C) 2009--2024 University of Chicago
+# Copyright (C) 2005--2008 Gordon Kindlmann
+# Copyright (C) 1998--2004 University of Utah
+#
+# This library is free software; you can redistribute it and/or modify it under the terms
+# of the GNU Lesser General Public License (LGPL) as published by the Free Software
+# Foundation; either version 2.1 of the License, or (at your option) any later version.
+# The terms of redistributing and/or modifying this software also include exceptions to
+# the LGPL that facilitate static linking.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along with
+# this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street,
+# Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+# 02=THIRD tests, of limnCbfSingle, via lpu cbfit -fs
+
+set -o errexit
+set -o nounset
+shopt -s expand_aliases
+JUNK=""
+function junk { JUNK="$JUNK $@"; }
+function cleanup { rm -rf $JUNK; }
+trap cleanup err exit int term
+# https://devmanual.gentoo.org/tools-reference/bash/
+unset UNRRDU_QUIET_QUIT
+
+IN=pointy.txt
+#IN=circ.txt
+N=$(cat $IN | wc -l | xargs echo)
+BIN=900
+
+MMB="-min -1.1 1.1 -max 1.1 -1.1 -b $BIN $BIN"
+
+unu jhisto -i $IN $MMB -t float |
+ unu resample -s x1 x1 -k gauss:1.3,6 | unu quantize -b 8 | unu 2op x - 0.8 -o in.png
+junk in.png
+
+rm -f test-??.png
+
+for LO in $(seq 0 $((N-1))); do
+ echo $LO
+ HI=$((LO+7))
+ LOO=$(printf %02d $LO)
+ CMD="./lpu cbfit -i $IN -loop -scl 1 -psi 1000 -fs $LO $HI -v 0 -eps 0.03"
+ echo "==================== $LO $HI --> test-$LOO.png : $CMD"
+ eval $CMD 2>&1 > log-$LOO.txt
+ # cat log-$LOO.txt
+ junk log-$LOO.txt
+ tail -n 4 log-$LOO.txt |
+ ./lpu cbfit -i - -synthn $N -syntho out.txt 2>&1 | grep -v _hestDefaults
+ # lots of extraneous printf thwart piping
+ unu jhisto -i out.txt $MMB | unu quantize -b 8 -max 1 -o out.png
+ # in: green
+ # out: magenta
+ unu join -i out.png in.png out.png -a 0 -incr -o test-$LOO.png
+done
Property changes on: teem/trunk/src/limn/test/02-test-fs.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 17:17:52
|
Revision: 7178
http://sourceforge.net/p/teem/code/7178
Author: kindlmann
Date: 2024-07-08 17:17:50 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
for testing limnCbfTVT
Added Paths:
-----------
teem/trunk/src/limn/test/01-test-tvt.sh
Added: teem/trunk/src/limn/test/01-test-tvt.sh
===================================================================
--- teem/trunk/src/limn/test/01-test-tvt.sh (rev 0)
+++ teem/trunk/src/limn/test/01-test-tvt.sh 2024-07-08 17:17:50 UTC (rev 7178)
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+#
+# Teem: Tools to process and visualize scientific data and images
+# Copyright (C) 2009--2024 University of Chicago
+# Copyright (C) 2005--2008 Gordon Kindlmann
+# Copyright (C) 1998--2004 University of Utah
+#
+# This library is free software; you can redistribute it and/or modify it under the terms
+# of the GNU Lesser General Public License (LGPL) as published by the Free Software
+# Foundation; either version 2.1 of the License, or (at your option) any later version.
+# The terms of redistributing and/or modifying this software also include exceptions to
+# the LGPL that facilitate static linking.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along with
+# this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street,
+# Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+# 01=SECOND script to run to test limnCbfTVT via lpu cbfit -tvt
+
+set -o errexit
+set -o nounset
+shopt -s expand_aliases
+JUNK=""
+function junk { JUNK="$JUNK $@"; }
+function cleanup { rm -rf $JUNK; }
+trap cleanup err exit int term
+# https://devmanual.gentoo.org/tools-reference/bash/
+unset UNRRDU_QUIET_QUIT
+
+IN=circ.txt
+N=$(cat $IN | wc -l | xargs echo)
+VVOUT=out-vv.txt
+LTOUT=out-rt.txt
+RTOUT=out-lt.txt
+BIN=670
+
+rm -f $VVOUT; touch $VVOUT; junk $VVOUT
+rm -f $LTOUT; touch $LTOUT; junk $LTOUT
+rm -f $RTOUT; touch $RTOUT; junk $RTOUT
+for I in $(seq 0 $((N-1))); do
+ LO=$((I-4))
+ HI=$((I+4))
+ # 8-fold TEST:
+ # * without -loop and with -loop
+ # * -scl 0 and >0
+ # * oneside (4th arg to -tvt) 0 and 1
+ CMD="./lpu cbfit -i $IN -loop -scl 2 -tvt $LO $HI $I 1 -eps 1 -v 0"
+ echo $CMD
+ rm -f log.txt
+ (eval $CMD 2>&1) > log.txt
+ grep "lt =" log.txt | cut -d' ' -f 3,4 >> $LTOUT
+ grep "vv =" log.txt | cut -d' ' -f 3,4 >> $VVOUT
+ grep "rt =" log.txt | cut -d' ' -f 3,4 >> $RTOUT
+done
+rm -f log.txt
+
+MMB="-min -1.1 1.1 -max 1.1 -1.1 -b $BIN $BIN"
+unu jhisto -i $IN $MMB -t float | unu axinsert -a 0 -s 3 |
+ unu resample -s = x1 x1 -k gauss:1.3,6 | unu quantize -b 8 | unu 2op x - 0.5 -o in.png
+junk in.png
+unu jhisto -i $VVOUT $MMB | unu quantize -b 8 -max 1 -o vv.png
+junk vv.png
+
+TSCL=0.02
+unu 2op x $LTOUT $TSCL | unu 2op + - $VVOUT | unu jhisto $MMB | unu quantize -b 8 -max 1 -o lt.png
+unu 2op x $RTOUT $TSCL | unu 2op + - $VVOUT | unu jhisto $MMB | unu quantize -b 8 -max 1 -o rt.png
+junk {l,r}t.png
+
+# in: gray fuzzy blob
+# lt: blue vv: green rt: red points
+unu join -i rt.png vv.png lt.png -a 0 -incr |
+ unu 2op max in.png - |
+ unu resample -s = x2 x2 -k box -o tvt.png
+
+open tvt.png
Property changes on: teem/trunk/src/limn/test/01-test-tvt.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-08 17:06:10
|
Revision: 7177
http://sourceforge.net/p/teem/code/7177
Author: kindlmann
Date: 2024-07-08 17:06:09 +0000 (Mon, 08 Jul 2024)
Log Message:
-----------
data generation for for testing
Added Paths:
-----------
teem/trunk/src/limn/test/00-data.sh
Added: teem/trunk/src/limn/test/00-data.sh
===================================================================
--- teem/trunk/src/limn/test/00-data.sh (rev 0)
+++ teem/trunk/src/limn/test/00-data.sh 2024-07-08 17:06:09 UTC (rev 7177)
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+#
+# Teem: Tools to process and visualize scientific data and images
+# Copyright (C) 2009--2024 University of Chicago
+# Copyright (C) 2005--2008 Gordon Kindlmann
+# Copyright (C) 1998--2004 University of Utah
+#
+# This library is free software; you can redistribute it and/or modify it under the terms
+# of the GNU Lesser General Public License (LGPL) as published by the Free Software
+# Foundation; either version 2.1 of the License, or (at your option) any later version.
+# The terms of redistributing and/or modifying this software also include exceptions to
+# the LGPL that facilitate static linking.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along with
+# this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street,
+# Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+# 00=FIRST script to run to generate circ.txt and pointy.txt data
+
+set -o errexit
+set -o nounset
+shopt -s expand_aliases
+JUNK=""
+function junk { JUNK="$JUNK $@"; }
+function cleanup { rm -rf $JUNK; }
+trap cleanup err exit int term
+# https://devmanual.gentoo.org/tools-reference/bash/
+unset UNRRDU_QUIET_QUIT
+
+N=60
+
+echo 0 1 |
+ unu reshape -s 2 |
+ unu convert -t double |
+ unu 3op x - 2 pi |
+ unu resample -s $((N+1)) -k tent -c node -o a; junk a
+unu 1op cos -i a -o x; junk x
+unu 1op sin -i a -o y; junk y
+unu gamma -i x -g 1.0 | # gamma > 1 flattens right side, makes left side pointy
+unu join -i - y -a 0 -incr |
+ unu crop -min 0 0 -max M M-1 -o circ.txt
+
+unu 2op spow circ.txt 3 -o pointy.txt
Property changes on: teem/trunk/src/limn/test/00-data.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-04 16:05:21
|
Revision: 7176
http://sourceforge.net/p/teem/code/7176
Author: kindlmann
Date: 2024-07-04 16:05:19 +0000 (Thu, 04 Jul 2024)
Log Message:
-----------
fctx->distMaxIdx is now set correctly, hopefully, and that and lots of other things still need to be tested more. Added documentation to reflect new term lifted indices, which is the right word for something GLK has been struggling to put into precise words
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-03 19:54:06 UTC (rev 7175)
+++ teem/trunk/src/limn/limn.h 2024-07-04 16:05:19 UTC (rev 7176)
@@ -911,7 +911,7 @@
int isLoop);
LIMN_EXPORT limnCbfPoints *limnCbfPointsNix(limnCbfPoints *lpnt);
LIMN_EXPORT int limnCbfPointsCheck(const limnCbfPoints *lpnt);
-LIMN_EXPORT limnCbfPath *limnCbfPathNew(void);
+LIMN_EXPORT limnCbfPath *limnCbfPathNew(unsigned segNum);
LIMN_EXPORT limnCbfPath *limnCbfPathNix(limnCbfPath *path);
LIMN_EXPORT void limnCbfPathJoin(limnCbfPath *dst, const limnCbfPath *src);
LIMN_EXPORT limnCbfCtx *limnCbfCtxNew();
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-07-03 19:54:06 UTC (rev 7175)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-07-04 16:05:19 UTC (rev 7176)
@@ -35,7 +35,7 @@
Nrrd *_nin, *nin;
double *xy, deltaThresh, psi, cangle, epsilon, nrpIota, time0, dtime, scale, synthPow;
- unsigned int ii, synthNum, pNum, nrpIterMax;
+ unsigned int size0, size1, ii, synthNum, pNum, nrpIterMax;
int loop, petc, verbose, tvt[4], fitSingleLoHi[2];
char *synthOut;
limnCbfCtx *fctx;
@@ -51,9 +51,10 @@
"if saving spline sampling to -so, how many sample.");
hestOptAdd_1_String(
&hopt, "syntho", "synth out", &synthOut, "",
- "if non-empty, input xy points are actually control points for a single spline "
- "segment, to be sampled -sn times, and this is the filename into which to save "
- "the synthesized xy pts, and then quit without any fitting.");
+ "if non-empty, input xy points are actually either: (1) 2-by-4 array of control "
+ "points for a single spline segment, or (2) an 8-by-N array for a sequence of "
+ "splines; either way the path should be sampled -sn times, and this is the filename "
+ "into which to save the synthesized xy pts, and then quit without any fitting.");
hestOptAdd_1_Double(&hopt, "sup", "expo", &synthPow, "1",
"when synthesizing data on a single segment, warp U parameters "
"by raising to this power.");
@@ -91,17 +92,24 @@
PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
- if (!(2 == _nin->dim && 2 == _nin->axis[0].size)) {
- fprintf(stderr,
- "%s: want 2-D (not %u) array with axis[0].size "
- "2 (not %u)\n",
- me, _nin->dim, (unsigned int)_nin->axis[0].size);
+ if (!(2 == _nin->dim)) {
+ fprintf(stderr, "%s: need 2-D (not %u) input array\n", me, _nin->dim);
airMopError(mop);
return 1;
}
- if (airStrlen(synthOut) && 4 != _nin->axis[1].size) {
- fprintf(stderr, "%s: need 2-by-4 array (not 2-by-%u) for synthetic xy\n", me,
- (unsigned int)_nin->axis[1].size);
+ size0 = AIR_UINT(_nin->axis[0].size);
+ size1 = AIR_UINT(_nin->axis[1].size);
+ if (airStrlen(synthOut)) {
+ if (!((2 == size0 && 4 == size1) || (8 == size0))) {
+ fprintf(stderr,
+ "%s: for synthesizing, need either 2-by-4 array or "
+ "8-by-N (not %u-by-%u)\n",
+ me, size0, size1);
+ airMopError(mop);
+ return 1;
+ }
+ } else if (!(2 == size0)) {
+ fprintf(stderr, "%s: need 2-by-N input XY points (not %u-by-N)", me, size0);
airMopError(mop);
return 1;
}
@@ -119,7 +127,6 @@
/* synthesize data from control points */
double *cpt = (double *)nin->data;
limnCbfSeg seg;
- int ci;
Nrrd *nsyn;
if (!(synthNum >= 3)) {
fprintf(stderr, "%s: for data synthesis need at least 3 samples (not %u)\n", me,
@@ -127,18 +134,37 @@
airMopError(mop);
return 1;
}
- for (ci = 0; ci < 4; ci++) {
- ELL_2V_COPY(seg.xy + 2 * ci, cpt + 2 * ci);
- }
- printf("%s: synth seg: (%g,%g) -- (%g,%g) -- (%g,%g) -- (%g,%g)\n", me, seg.xy[0],
- seg.xy[1], seg.xy[2], seg.xy[3], seg.xy[4], seg.xy[5], seg.xy[6], seg.xy[7]);
xy = AIR_MALLOC(2 * synthNum, double);
airMopAdd(mop, xy, airFree, airMopAlways);
- for (ii = 0; ii < synthNum; ii++) {
- double uu = AIR_AFFINE(0, ii, synthNum - 1, 0, 1);
- uu = pow(uu, synthPow);
- limnCbfSegEval(xy + 2 * ii, &seg, uu);
+ if (2 == size0) {
+ unsigned int ci;
+ printf("%s: synthetically sampling single spline with %u points", me, synthNum);
+ for (ci = 0; ci < 8; ci++) {
+ seg.xy[ci] = cpt[ci];
+ }
+ printf("%s: synth seg: (%g,%g) -- (%g,%g) -- (%g,%g) -- (%g,%g)\n", me, seg.xy[0],
+ seg.xy[1], seg.xy[2], seg.xy[3], seg.xy[4], seg.xy[5], seg.xy[6],
+ seg.xy[7]);
+ for (ii = 0; ii < synthNum; ii++) {
+ double uu = AIR_AFFINE(0, ii, synthNum - 1, 0, 1);
+ uu = pow(uu, synthPow);
+ limnCbfSegEval(xy + 2 * ii, &seg, uu);
+ }
+ } else {
+ unsigned int ci, si;
+ limnCbfPath *spath = limnCbfPathNew(size1);
+ airMopAdd(mop, spath, (airMopper)limnCbfPathNix, airMopAlways);
+ printf("%s: synthetically sampling %u splines with %u points", me, size1,
+ synthNum);
+ /* copy in control point data */
+ for (si = 0; si < size1; si++) {
+ for (ci = 0; ci < 8; ci++) {
+ spath->seg[si].xy[ci] = (cpt + 8 * si)[ci];
+ }
+ }
+ limnCbfPathSample(xy, synthNum, spath);
}
+
nsyn = nrrdNew();
airMopAdd(mop, nsyn, (airMopper)nrrdNix, airMopAlways);
if (nrrdWrap_va(nsyn, xy, nrrdTypeDouble, 2, (size_t)2, (size_t)synthNum)
@@ -162,7 +188,7 @@
return 1;
}
airMopAdd(mop, lpnt, (airMopper)limnCbfPointsNix, airMopAlways);
- path = limnCbfPathNew();
+ path = limnCbfPathNew(0);
airMopAdd(mop, path, (airMopper)limnCbfPathNix, airMopAlways);
fctx = limnCbfCtxNew();
airMopAdd(mop, fctx, (airMopper)limnCbfCtxNix, airMopAlways);
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-07-03 19:54:06 UTC (rev 7175)
+++ teem/trunk/src/limn/splineFit.c 2024-07-04 16:05:19 UTC (rev 7176)
@@ -37,12 +37,97 @@
Teem, at which point the code below can be integrated with it.
NOTE: spline fitting would be useful in 3D (or higher dimensions) too, but currently
-this code only supports 2D. "DIM=2" flags places in code where that is explicit.
+this code only supports 2D. "DIM=2" flags places in code where that is explicit, with
+the hope that this code can later by generalized.
NOTE: In Teem coding standards, "Cbf" would be better written as "CBF", but all these
initials got annoying with other CamelCase function names.
+
+This code was unusually slow for GLK to write because he struggled to gracefully handle
+the combination of possibilities:
+ - points may trace an open segment, or a closed loop
+ - geometry (spline endpoints and tangents) is computed without or with some smoothing.
+ - smoothing may be bounded by the two corners on either side, or smoothing
+ may cover all available vertices (which may or may not loop around)
+How to represent an interval of vertex *indices* -- termed a "span", in contrast to the
+single cubic spline "segment" -- is fundamental to all of this. A uint span[2] array was
+tried, but ended up just passing around a lot of loi,hii (low index, high index) pairs to
+represent the span.
+
+In the presence of a loop of N points, we could imagine different kinds of indices:
+ 0 .. N-1 : "actual" indices
+ .. -2 -1 0 .. N-1 N N-1 .. : "lifted" indices, in the sense that the real number line
+ is a cover of the circle, and a path in the circle can be
+ lifted up to the real number line. A lifted index J is
+ converted to an actual index by AIR_MOD(J, N) or if J > 0
+ just J % N.
+
+Some computations are easier to do and reason about with lifted indices, but obviously
+you can only use actual indices for any memory access. Early iterations of the code used
+lifted indices in lots of places, but GLK got confused, so the following summary of who
+calls whom ("who -- whom") was made to trace where [loi,hii] spans originate, and to
+ensure that all the functions here only take actual indices as parameters, including
+limnCbfTVT (which computes a tangent,vertex,tangent triple at a given point). Nearly all
+of the cleverness with lifted indices happens in limnCbfTVT, and its idxLift helper
+function ensures it gets actual indices.
+
+limnCbfTVT -- idxLift to convert given actual indices into lifted,
+ and does computations with lifted indices (with signed offsets)
+
+findAlpha -- spanLength (with given loi, hii)
+ (and otherwise only works with actual indices)
+
+findDist -- spanLength (with given loi, hii)
+ internally used lifted indices, but:
+ *** sets fctx->distMaxIdx to an actual index
+
+vttvCalcOrCopy -- limnCbfTVT (with given loi, hii, and vvi=loi or hii)
+
+fitSingle -- spanLength (with given loi, hii)
+ -- findAlpha (with given loi, hii)
+ -- findDist (with given loi, hii)
+
+limnCbfCorners -- limnCbfTVT (either: with loi=0 hii=pnum-1
+ and vvi also=0 or pnum-1
+ or: with loi=hii=0
+ and all vvi from 0..pnum-1
+
+limnCbfMulti -- vttvCalcOrCopy (with given loi,hii)
+ -- fitSingle (with given loi,hii)
+ -- limnCbfTVT (with given loi,hii and fctx->distMaxIdx)
+ -- limnCbfMulti (with given loi,hii and fctx->distMaxIdx)
+ -- limnCbfPathNew, limnCbfPathJoin, limnCbfPathJoin
+
+limnCbfGo -- limnCbfCtxPrep
+ -- limnCbfCorners
+ -- limnCbfMulti (either with loi==hii==0 or with loi,hii at corners)
+ -- limnCbfPathNew, limnCbfPathJoin, limnCbfPathNix
*/
+/*
+TODO:
+test fitSingle uu initialization with points that are already exactly uniform
+test findDist - is distMaxIdx the correct actual index?
+testing corners: corners near and at start==stop of isLoop
+corners not at start or stop of isLoop: do spline wrap around from last to first index?
+
+use performance tests to explore optimal settings in fctx:
+ nrpIterMax, nrpCap, nrpIota, nrpPsi, nrpDeltaThresh
+evaluated in terms of time and #splines needed for fit
+(may want to pay in time for more economical representation)
+
+"What GLK hasn't thought through is: what is the interaction of nrp iterations and
+findAlpha generating the simple arc on some but not all iterations (possibly
+unstable?)"
+
+valgrind everything
+
+search for HEY (handle two adjacent equally strong corners)
+
+(DIM=2) explore what would be required to generalized from 2D to 3D,
+perhaps at least at the API level, even if 3D is not yet implemented
+*/
+
#define PNMIN(ISLOOP) ((ISLOOP) ? 4 : 3)
/*
@@ -144,7 +229,7 @@
}
limnCbfPath * /* Biff: nope */
-limnCbfPathNew() {
+limnCbfPathNew(unsigned int segNum) {
limnCbfPath *path;
path = AIR_MALLOC(1, limnCbfPath);
if (path) {
@@ -152,6 +237,13 @@
128 /* incr */);
airArrayStructCB(path->segArr, segInit, NULL);
path->isLoop = AIR_FALSE;
+ if (segNum) {
+ airArrayLenSet(path->segArr, segNum);
+ if (!path->segArr->data) {
+ /* whoa, couldn't allocate requested segments; return NULL and possibly leak */
+ path = NULL;
+ }
+ }
}
return path;
}
@@ -197,8 +289,8 @@
fctx->cidx = NULL;
fctx->ulen = fctx->wlen = fctx->cnum = 0;
/* initialize outputs to bogus valus */
- fctx->nrpIterDone = (uint)(-1);
- fctx->distMaxIdx = (uint)(-1);
+ fctx->nrpIterDone = UINT_MAX;
+ fctx->distMaxIdx = UINT_MAX;
fctx->distMax = AIR_POS_INF;
fctx->nrpDeltaDone = AIR_POS_INF;
fctx->alphaDet = 0;
@@ -280,7 +372,7 @@
const uint wlbig = 128;
/* if the initial weights for the tangent computation sum to smaller than this
(they will be later normalized to sum to 1) then something isn't right */
- const double tinysum = 1.0 / 64;
+ const double tinysum = 1.0 / 128;
double kw, kparm[2], vsum, tsum;
uint wlen;
/* else need to (possibly allocate and) set vw and tw buffers */
@@ -309,7 +401,7 @@
if (2 * wlen > pNum) {
/* note: #verts involved in computation == 2*wlen - 1, so this is only going to
complain only when ~all the verts are contributing to computations for each
- vertex, which is pretty excessive */
+ vertex, which is clearly excessive */
biffAddf(LIMN,
"%s: weight buffer length %u (from scale %g) seems "
"too large compared to #points %u",
@@ -517,16 +609,15 @@
/* cheesy macro as short-hand to access either pp or ppOwn */
#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
-/* idxPrep: error-checked index processing for limnCbfTVT and maybe others
-This is messy because of the flexibility in how we handle points:
-might not be a loop (so an actual [loi,hii] vertex index span is needed),
-or, is a loop but still only working within bounds of [loi,hii] span,
-or, is a loop and [loi,hii]=[0,0] says that there are no bounds (aka "loopy")
-*/
+/* idxLift: error-checked index lifting (from "actual" to "lifted" indices, explained
+above) for limnCbfTVT and maybe others. This is messy because of the flexibility in how
+we handle points: might not be a loop (so an actual [loi,hii] vertex index span is
+needed), or, is a loop but still only working within bounds of [loi,hii] span, or, is a
+loop and [loi,hii]=[0,0] says that there are no bounds (aka "loopy") */
static int /* Biff: 1 */
-idxPrep(uint *loiP, uint *hiiP, uint *vviP, int *loopyP, int verbose,
+idxLift(uint *loiP, uint *hiiP, uint *vviP, int *loopyP, int verbose,
const limnCbfPoints *lpnt, uint gloi, uint ghii, uint gvvi) {
- static const char me[] = "idxPrep";
+ static const char me[] = "idxLift";
uint pnum, loi, hii, vvi;
int loopy;
@@ -652,7 +743,7 @@
printf("%s: hello: %u in [%u,%u] in %sloop with %u points (%s-sided)\n", me, gvvi,
gloi, ghii, lpnt->isLoop ? "" : "NON-", lpnt->num, oneSided ? "1" : "2");
}
- if (idxPrep(&loi, &hii, &vvi, &loopy, fctx->verbose > 1, lpnt, gloi, ghii, gvvi)) {
+ if (idxLift(&loi, &hii, &vvi, &loopy, fctx->verbose > 1, lpnt, gloi, ghii, gvvi)) {
biffAddf(LIMN, "%s: trouble with given loi %u, hii %u, or vvi %u", me, gloi, ghii,
gvvi);
return 1;
@@ -659,7 +750,7 @@
}
pnum = AIR_INT(lpnt->num); /* YES really needs to be signed int; see below */
if (fctx->verbose) {
- printf("%s: (post-idxPrep) %u in [%u,%u] -> %u in [%u,%u] (loopy=%d)\n", me, gvvi,
+ printf("%s: (post-idxLift) %u in [%u,%u] -> %u in [%u,%u] (loopy=%d)\n", me, gvvi,
gloi, ghii, vvi, loi, hii, loopy);
}
@@ -748,9 +839,9 @@
}
}
{
- /* limit distance from chosen (x,y) datapoint to posC to be (HEY harcoded) 95% of
- fctx->epsilon. Being allowed to be further away can cause annoyances (for GLK in
- some early stage of debugging) */
+ /* limit distance from chosen (x,y) datapoint to posC to be (yes, harcoded) 95% of
+ fctx->epsilon. Being allowed to be further away can cause annoyances (for GLK in
+ some early stage of debugging) */
double off[2], offlen, okoff = 0.95 * fctx->epsilon; /* DIM=2 throughout */
const double *xy = PP(lpnt) + 2 * icent; /* center vertex in given data */
ELL_2V_SUB(off, posC, xy); /* off = posC - xy, from given to computed */
@@ -773,9 +864,9 @@
return 0;
}
-/* utility function for counting how many vertices are in index span [loi,hii] inclusive.
-It is not our job here to care about lpnt->isLoop; we just assume that if we're faced
-with hii<loi, it must be because of a loop */
+/* utility function for counting how many vertices are in (actual) index span [loi,hii]
+inclusive. It is not our job here to care about lpnt->isLoop; we just assume that if
+we're faced with hii<loi, it must be because of a loop */
static uint
spanLength(const limnCbfPoints *lpnt, uint loi, uint hii) {
uint topi = hii + (hii < loi) * (lpnt->num);
@@ -782,7 +873,8 @@
return topi - loi + 1;
}
-/* utility function for getting pointer to coords in lpnt, for point with index loi+ofi,
+/* utility function for getting pointer to coords in lpnt,
+for point with (lifted) index loi+ofi,
while accounting for possibility of wrapping */
static const double *
pointPos(const limnCbfPoints *lpnt, uint loi, uint ofi) {
@@ -815,19 +907,18 @@
const double vv0[2], const double tt1[2], const double tt2[2],
const double vv3[2], const limnCbfPoints *lpnt, uint loi, uint hii) {
static const char me[] = "findAlpha";
- uint ii, spanlen;
+ uint ii, spanlen = spanLength(lpnt, loi, hii);
double det, F2L[2], lenF2L;
const double *xy = PP(lpnt);
ELL_2V_SUB(F2L, xy + 2 * hii, xy + 2 * loi); /* DIM=2 throughout this */
lenF2L = ELL_2V_LEN(F2L);
- spanlen = spanLength(lpnt, loi, hii);
if (spanlen > 2) {
double xx[2], m11, m12, m22, MM[4], MI[4];
const double *uu = fctx->uu;
xx[0] = xx[1] = m11 = m12 = m22 = 0;
for (ii = 0; ii < spanlen; ii++) {
- const double *xy;
+ const double *xy = pointPos(lpnt, loi, ii);
double bb[4], Ai1[2], Ai2[2], Pi[2], dmP[2];
double ui = uu[ii];
VCBD0(bb, ui);
@@ -841,7 +932,6 @@
m12 += ELL_2V_DOT(Ai1, Ai2);
m22 += ELL_2V_DOT(Ai2, Ai2);
ELL_2V_SCALE_ADD2(Pi, bb[0] + bb[1], vv0, bb[2] + bb[3], vv3);
- xy = pointPos(lpnt, loi, ii);
ELL_2V_SUB(dmP, xy, Pi);
xx[0] += ELL_2V_DOT(dmP, Ai1);
xx[1] += ELL_2V_DOT(dmP, Ai2);
@@ -867,9 +957,8 @@
printf("%s: [%u,%u] spanlen %u tiny --> simple arc\n", me, loi, hii, spanlen);
}
}
- /* generate simple arc: set both alphas to 1/3 of distance from
- first to last point, but also handle non-unit-length tt1 and
- tt2 */
+ /* generate simple arc: set both alphas to 1/3 of distance from first to last point,
+ but also handle non-unit-length tt1 and tt2 */
alpha[0] = lenF2L / (3 * ELL_2V_LEN(tt1));
alpha[1] = lenF2L / (3 * ELL_2V_LEN(tt2));
} else {
@@ -905,7 +994,7 @@
/* only changing parameterization of interior points,
not the first (ii=0) or last (ii=pNum-1) */
for (ii = 1; ii < spanlen - 1; ii++) {
- double numer, denom, delu, df[2], ww[4], tt, Q[2], QD[2], QDD[2];
+ double numer, denom, delu, absdelu, df[2], ww[4], tt, Q[2], QD[2], QDD[2];
const double *xy;
tt = uu[ii];
CBD0(Q, vv0, vv1, vv2, vv3, tt, ww);
@@ -916,22 +1005,18 @@
numer = ELL_2V_DOT(df, QD);
denom = ELL_2V_DOT(QD, QD) + ELL_2V_DOT(df, QDD);
delu = numer / denom;
- if (AIR_ABS(delu) > maxdelu) {
+ absdelu = fabs(delu);
+ if (absdelu > maxdelu) {
/* cap Newton step */
- delu = maxdelu * airSgn(delu);
+ delu *= maxdelu / absdelu;
}
uu[ii] = tt - delu;
- delta += AIR_ABS(delu);
+ delta += fabs(delu);
if (fctx->verbose > 1) {
printf("%s[%2u]: %g <-- %g - %g\n", me, ii, uu[ii], tt, delu);
}
}
delta /= spanlen - 2; /* number of interior points */
- /* HEY TODO: need to make sure that half-way between points,
- spline isn't wildly diverging; this can happen with the
- spline making a loop away from a small number of points, e.g.:
- 4 points spline defined by vv0 = (1,1), tt1 = (1,2),
- tt2 = (1,2), vv3 = (0,1) */
return delta;
}
@@ -941,6 +1026,7 @@
findDist(limnCbfCtx *fctx, const double alpha[2], const double vv0[2],
const double tt1[2], const double tt2[2], const double vv3[2],
const limnCbfPoints *lpnt, uint loi, uint hii) {
+ static const char me[] = "findDist";
uint ii, distMaxIdx, spanlen;
double vv1[2], vv2[2], distMax;
const double *uu = fctx->uu;
@@ -949,25 +1035,26 @@
assert(spanlen >= 3);
ELL_2V_SCALE_ADD2(vv1, 1, vv0, alpha[0], tt1); /* DIM=2 everywhere here */
ELL_2V_SCALE_ADD2(vv2, 1, vv3, alpha[1], tt2);
- distMax = AIR_NAN;
+ distMax = -1; /* any computed distance will be >= 0 */
/* NOTE that the first and last points are actually not part of the max distance
calculation, which motivates ensuring that the endpoints generated by limnCbfTVT
are actually sufficiently close to the first and last points (or else the fit spline
won't meet the expected accuracy threshold) */
for (ii = 1; ii < spanlen - 1; ii++) {
+ const double *xy = pointPos(lpnt, loi, ii);
double len, Q[2], df[2], ww[4];
- const double *xy;
CBD0(Q, vv0, vv1, vv2, vv3, uu[ii], ww);
- xy = pointPos(lpnt, loi, ii);
ELL_2V_SUB(df, Q, xy);
len = ELL_2V_LEN(df);
- if (!AIR_EXISTS(distMax) || len > distMax) {
+ if (len > distMax) {
distMax = len;
- distMaxIdx = ii;
+ distMaxIdx = loi + ii; /* lifted index */
}
}
fctx->distMax = distMax;
- fctx->distMaxIdx = distMaxIdx;
+ /* we could use a lifted index for internal distMaxIdx but upon saving to fctx it needs
+ to be an actual index */
+ fctx->distMaxIdx = distMaxIdx % lpnt->num;
fctx->distBig = (distMax <= fctx->nrpIota * fctx->epsilon
? 0
: (distMax <= fctx->epsilon /* */
@@ -975,6 +1062,10 @@
: (distMax <= fctx->nrpPsi * fctx->epsilon /* */
? 2
: 3)));
+ if (fctx->verbose > 2) {
+ printf("%s[%u,%u]: distMax %g @ %u (big %d)\n", me, loi, hii, fctx->distMax,
+ fctx->distMaxIdx, fctx->distBig);
+ }
return;
}
@@ -1003,7 +1094,7 @@
const double vv3[2], limnCbfCtx *fctx, const limnCbfPoints *lpnt, uint loi,
uint hii) {
static const char me[] = "fitSingle";
- uint iter, pNum;
+ uint iter, spanlen = spanLength(lpnt, loi, hii);
/* DIM=2 pretty much everywhere here */
if (fctx->verbose) {
@@ -1011,9 +1102,8 @@
"tt2=(%g,%g), vv3=(%g,%g)\n",
me, loi, hii, vv0[0], vv0[1], tt1[0], tt1[1], tt2[0], tt2[1], vv3[0], vv3[1]);
}
- pNum = spanLength(lpnt, loi, hii);
- if (2 == pNum) {
- /* relying on code in findAlpha() that handles pNum==2 */
+ if (2 == spanlen) {
+ /* relying on code in findAlpha() that handles slen==2 */
findAlpha(alpha, fctx, vv0, tt1, tt2, vv3, lpnt, loi, hii);
/* nrp is moot */
fctx->nrpIterDone = 0;
@@ -1021,7 +1111,7 @@
fctx->distMax = fctx->nrpDeltaDone = 0;
fctx->distMaxIdx = 0;
fctx->distBig = 0;
- } else { /* pNum >= 3 */
+ } else { /* slen >= 3 */
double delta; /* avg parameterization change of interior points */
{
/* initialize uu parameterization to chord length */
@@ -1029,19 +1119,20 @@
double len;
const double *xyP, *xyM;
fctx->uu[0] = len = 0;
+ xyM = pointPos(lpnt, loi, 0);
xyP = pointPos(lpnt, loi, 1);
- xyM = pointPos(lpnt, loi, 0);
- for (ii = 1; ii < pNum; ii++) {
+ for (ii = 1; ii < spanlen; ii++) {
double dd[2];
ELL_2V_SUB(dd, xyP, xyM);
len += ELL_2V_LEN(dd);
fctx->uu[ii] = len;
xyM = xyP;
+ /* yes on last iter this is set to possibly bogus pointer but it's never used */
xyP = pointPos(lpnt, loi, ii + 1);
}
delta = 0;
- for (ii = 0; ii < pNum; ii++) {
- if (ii < pNum - 1) {
+ for (ii = 0; ii < spanlen; ii++) {
+ if (ii < spanlen - 1) {
fctx->uu[ii] /= len;
delta += fctx->uu[ii];
} else {
@@ -1052,7 +1143,7 @@
printf("%s[%d,%d]: intial uu[%u] = %g\n", me, loi, hii, ii, fctx->uu[ii]);
}
}
- delta /= pNum - 2; /* within the pNum verts are pNum-2 interior verts */
+ delta /= spanlen - 2; /* within the pNum verts are pNum-2 interior verts */
if (fctx->verbose) {
printf("%s[%d,%d]: initial (chord length) delta = %g\n", me, loi, hii, delta);
}
@@ -1088,6 +1179,14 @@
converged = AIR_TRUE;
break;
}
+ /* maybe TODO: add logic here to catch if delta is getting bigger and bigger,
+ i.e. the reparm is diverging instead of converging. A younger GLK seemed to think
+ this could happen with the spline making a loop away from a small number of
+ points, e.g.: 4 points on spline defined by vv0 = (1,1), tt1 = (1,2), tt2 =
+ (1,2), vv3 = (0,1). On the other hand, it's not like we have a strategy for
+ doing a different/smarter reparm to handle that, and if it does happen, our
+ failure to fit will likely (in the context of limnCbfMulti) merely trigger
+ subdivision, which isn't terrible */
}
if (fctx->verbose) {
printf("%s[%d,%d]: nrp done after %u iters: ", me, loi, hii, iter);
@@ -1243,7 +1342,7 @@
*tvt; /* 6-by-pnum array of tangent,vertex,tangent */
int *corny, /* corny[i] means vertex i seems like a corner */
oneSided = AIR_TRUE;
- uint pnum = lpnt->num, loi = 0, hii, cnum, vi;
+ uint pnum = lpnt->num, hii, cnum, vi;
if (!(fctx && lpnt)) {
biffAddf(LIMN, "%s: got NULL pointer", me);
@@ -1266,13 +1365,13 @@
}
hii = pnum - 1;
if (limnCbfTVT(fctx->ctvt + 0, fctx->ctvt + 2, fctx->ctvt + 4, /* */
- fctx, lpnt, loi, hii, 0, oneSided)
+ fctx, lpnt, 0, hii, 0, oneSided)
|| limnCbfTVT(fctx->ctvt + 6, fctx->ctvt + 8, fctx->ctvt + 10, /* */
- fctx, lpnt, loi, hii, hii, oneSided)) {
+ fctx, lpnt, 0, hii, hii, oneSided)) {
biffAddf(LIMN, "%s: trouble with tangents or vertices for endpoints", me);
return 1;
}
- fctx->cidx[0] = loi;
+ fctx->cidx[0] = 0;
fctx->cidx[1] = hii;
}
return 0;
@@ -1280,7 +1379,7 @@
/* else we search for corners */
mop = airMopNew();
- /* allocate arrays and add them to the mop, but bails if any allocation fails */
+ /* allocate arrays and add them to the mop, but bail if any allocation fails */
if (!((angle = AIR_CALLOC(pnum, double))
&& (airMopAdd(mop, angle, airFree, airMopAlways), 1)
&& (corny = AIR_CALLOC(pnum, int))
@@ -1424,10 +1523,10 @@
uint midi = fctx->distMaxIdx;
double TL[2], VM[2], TR[2];
limnCbfCtx fctxL, fctxR;
- limnCbfPath *pRth = limnCbfPathNew(); /* path on right side of new middle */
- /* HEY holy heck sneakiness: we make two shallow copies of the context, because we
- need each one's output information, but we don't need to re-allocate any of the
- internal buffers, because there is no concurrency */
+ limnCbfPath *pRth = limnCbfPathNew(0); /* path on right side of new middle */
+ /* holy moly sneakiness: we make two shallow copies of the context, because we
+ need each one's output information, but we don't need to re-allocate any of the
+ internal buffers, because there is no concurrency */
memcpy(&fctxL, fctx, sizeof(limnCbfCtx));
memcpy(&fctxR, fctx, sizeof(limnCbfCtx));
if (fctx->verbose) {
@@ -1515,7 +1614,7 @@
uint cii;
for (cii = 0; cii < fctx->cnum; cii++) {
uint cjj = (cii + 1) % fctx->cnum;
- limnCbfPath *subpath = limnCbfPathNew();
+ limnCbfPath *subpath = limnCbfPathNew(0);
/* 0: left tangent 2: vertex 4: right tangent */
const double *V0 = fctx->ctvt + 2 * cii, *T1 = fctx->ctvt + 4 * cii,
*T2 = fctx->ctvt + 0 * cjj, *V3 = fctx->ctvt + 2 * cjj;
@@ -1541,32 +1640,3 @@
path->isLoop = lpnt->isLoop;
return 0;
}
-
-/*
-TODO:
-
-testing corners: corners near and at start==stop of isLoop
-corners not at start or stop of isLoop: do spline wrap around from last to first index?
-
-use performance tests to explore optimal settings in fctx:
- nrpIterMax, nrpCap, nrpIota, nrpPsi, nrpDeltaThresh
-evaluated in terms of time and #splines needed for fit
-(may want to pay in time for more economical representation)
-
-"What GLK hasn't thought through is: what is the interaction of nrp iterations and
-findAlpha generating the simple arc on some but not all iterations (possibly
-unstable?)"
-
-reparm: "HEY TODO: need to make sure that half-way between points,
- spline isn't wildly diverging; this can happen with the
- spline making a loop away from a small number of points, e.g.:
- 4 points spline defined by vv0 = (1,1), tt1 = (1,2),
- tt2 = (1,2), vv3 = (0,1)"
-
-valgrind everything
-
-search for HEY
-
-(DIM=2) explore what would be required to generalized from 2D to 3D,
-perhaps at least at the API level, even if 3D is not yet implemented
-*/
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-03 19:54:08
|
Revision: 7175
http://sourceforge.net/p/teem/code/7175
Author: kindlmann
Date: 2024-07-03 19:54:06 +0000 (Wed, 03 Jul 2024)
Log Message:
-----------
code is all there, but TVT computations still need fixing, and all of this still needs debugging
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-01 16:37:53 UTC (rev 7174)
+++ teem/trunk/src/limn/limn.h 2024-07-03 19:54:06 UTC (rev 7175)
@@ -511,9 +511,9 @@
} limnSplineTypeSpec;
/*
-******** limnCBFSeg
+******** limnCbfSeg
**
-** how one cubic Bezier spline segment is represented for limnCBF functions
+** how one cubic Bezier spline segment is represented for limnCbf functions
** (using DIM=2 to mark places where the 2D-ness of the code surfaces )
**
** No constructor (New) or destructor (Nix) functions since it is so simple
@@ -528,27 +528,28 @@
or because there's nothing else to be continuous with */
/* how many points does this represent */
unsigned int pointNum;
-} limnCBFSeg;
+} limnCbfSeg;
/*
-******** limnCBFPath
+******** limnCbfPath
**
** a multi-spline path in the context of cubic Bezier fitting
*/
typedef struct {
- limnCBFSeg *seg; /* array of limnCBFSeg structs (NOT pointers to them) */
+ limnCbfSeg *seg; /* array of limnCbfSeg structs (NOT pointers to them) */
unsigned int segNum; /* length of seg array */
airArray *segArr; /* manages seg and segNum */
int isLoop; /* path is closed loop */
-} limnCBFPath;
+} limnCbfPath;
/*
-******** limnCBFCtx
+******** limnCbfCtx
**
-** The complete bag of input parameters and state for limnCBF functions.
+** The complete bag of input parameters and state for limnCbf functions, especially the
+** top-level limnCbfGo function.
**
** note: "nrp" = Newton-based Re-Parameterization of where the given points
-** fall along the spline, the iterative process inside limnCBFSingle
+** fall along the spline, the iterative process inside limnCbfSingle
*/
typedef struct {
/* ----------- input ---------- */
@@ -561,8 +562,8 @@
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
- nrp within limnCBFSingle. Fitting has successfully finished when spline
+ to given points: this affects both splitting done by limnCbfMulti, and
+ nrp within limnCbfSingle. Fitting has successfully finished when spline
path never strays further than this from input points */
scale, /* scale (in sense of nrrdKernelDiscreteGaussian) at which to estimate
spline endpoints and tangents; scale=0 means the endpoints are
@@ -582,20 +583,19 @@
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; /* 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. */
+ 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. */
/* ----------- internal --------- */
double *uu, /* used for nrp: buffer of parameterizations in [0,1] of point along
currently considered spline segment */
*vw, /* weights for endpoint vertex calculation */
*tw, /* weights for endpoint tangent calculation */
- *cpp, /* x,y positions of corners */
- *clt, /* x,y left (incoming) tangents at corners */
- *crt; /* x,y right (outgoing) tangents at corners */
- unsigned int uLen, /* how long is uu */
- wLen, /* how long are vw, tw */
- cNum; /* number of corners = # positions in cpp = # tangent vecs in clt, crt */
+ *ctvt; /* corner info: 6-by-cNum (DIM=2) of tangent,vertex,tangent */
+ unsigned int ulen, /* how long is uu */
+ wlen, /* how long are vw, tw */
+ *cidx, /* indices (into lpnt point data) of corners */
+ cnum; /* number of corners described by ctvt and cidx */
/* ----------- output --------- */
unsigned int nrpIterDone, /* number of nrp iters taken */
distMaxIdx; /* which point had distance distMax */
@@ -607,12 +607,12 @@
1: wee < distMax <= eps (eps = epsilon)
2: eps < distMax <= far (far = nrpPsi*epsilon)
3: far < distMax */
-} limnCBFCtx;
+} limnCbfCtx;
/*
-******** limnCBFPoints
+******** limnCbfPoints
**
-** a container for 1D array of points; currently used for limnCBF functions
+** a container for 1D array of points; currently used for limnCbf functions
** Both pp and ppOwn can point to the array of point locations, but exactly
** one of pp and ppOwn can be non-NULL.
**
@@ -628,7 +628,7 @@
int isLoop; /* points form a loop: logical indices into coord
array are . . . num-2, num-1, 0, 1, . . .
and index 0 is effectively arbitrary */
-} limnCBFPoints;
+} limnCbfPoints;
/* defaultsLimn.c */
LIMN_EXPORT const int limnPresent;
@@ -906,38 +906,35 @@
double maxT);
/* splineFit.c */
-LIMN_EXPORT limnCBFPoints *limnCBFPointsNew(const void *pdata, int ptype,
+LIMN_EXPORT limnCbfPoints *limnCbfPointsNew(const void *pdata, int ptype,
unsigned int dim, unsigned int pnum,
int isLoop);
-LIMN_EXPORT limnCBFPoints *limnCBFPointsNix(limnCBFPoints *lpnt);
-LIMN_EXPORT int limnCBFPointsCheck(const limnCBFPoints *lpnt);
-LIMN_EXPORT limnCBFPath *limnCBFPathNew(void);
-LIMN_EXPORT limnCBFPath *limnCBFPathNix(limnCBFPath *path);
-LIMN_EXPORT void limnCBFPathJoin(limnCBFPath *dst, const limnCBFPath *src);
-LIMN_EXPORT limnCBFCtx *limnCBFCtxNew();
-LIMN_EXPORT limnCBFCtx *limnCBFCtxNix(limnCBFCtx *fctx);
-LIMN_EXPORT int limnCBFCtxPrep(limnCBFCtx *fctx, const limnCBFPoints *lpnt);
-LIMN_EXPORT void limnCBFSegEval(double *xy, const limnCBFSeg *seg, double tt);
-LIMN_EXPORT void limnCBFPathSample(double *xy, unsigned int pointNum,
- const limnCBFPath *path);
-LIMN_EXPORT int limnCBFFindTVT(double lt[2], double vv[2], double rt[2],
- const limnCBFCtx *fctx, const limnCBFPoints *lpnt,
- unsigned int loi, unsigned int hii, unsigned int ofi,
- int oneSided);
-LIMN_EXPORT int limnCBFitSingle(limnCBFSeg *seg, const double vv0[2],
- const double tt1[2], const double tt2[2],
- const double vv3[2], limnCBFCtx *fctx,
- limnCBFPoints *lpnt, unsigned int loi, unsigned int hii);
-#if 0
-LIMN_EXPORT int limnCBFCorners(unsigned int **cornIdx, unsigned int *cornNum,
- limnCBFCtx *fctx, const limnCBFPoints *lpnt);
-LIMN_EXPORT int limnCBFMulti(limnCBFPath *path, limnCBFCtx *fctx, const double vv0[2],
- const double tt1[2], const double tt2[2],
- const double vv3[2], const limnCBFPoints *lpnt,
- unsigned int loi, unsigned int hii);
-LIMN_EXPORT int limnCBFit(limnCBFPath *path, limnCBFCtx *fctx,
- const limnCBFPoints *lpnt);
-#endif
+LIMN_EXPORT limnCbfPoints *limnCbfPointsNix(limnCbfPoints *lpnt);
+LIMN_EXPORT int limnCbfPointsCheck(const limnCbfPoints *lpnt);
+LIMN_EXPORT limnCbfPath *limnCbfPathNew(void);
+LIMN_EXPORT limnCbfPath *limnCbfPathNix(limnCbfPath *path);
+LIMN_EXPORT void limnCbfPathJoin(limnCbfPath *dst, const limnCbfPath *src);
+LIMN_EXPORT limnCbfCtx *limnCbfCtxNew();
+LIMN_EXPORT limnCbfCtx *limnCbfCtxNix(limnCbfCtx *fctx);
+LIMN_EXPORT int limnCbfCtxPrep(limnCbfCtx *fctx, const limnCbfPoints *lpnt);
+LIMN_EXPORT void limnCbfSegEval(double *xy, const limnCbfSeg *seg, double tt);
+LIMN_EXPORT void limnCbfPathSample(double *xy, unsigned int pointNum,
+ const limnCbfPath *path);
+LIMN_EXPORT int limnCbfTVT(double lt[2], double vv[2], double rt[2],
+ const limnCbfCtx *fctx, const limnCbfPoints *lpnt,
+ unsigned int loi, unsigned int hii, unsigned int vvi,
+ int oneSided);
+LIMN_EXPORT int limnCbfSingle(limnCbfSeg *seg, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], limnCbfCtx *fctx,
+ const limnCbfPoints *lpnt, unsigned int loi,
+ unsigned int hii);
+LIMN_EXPORT int limnCbfCorners(limnCbfCtx *fctx, const limnCbfPoints *lpnt);
+LIMN_EXPORT int limnCbfMulti(limnCbfPath *path, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], limnCbfCtx *fctx,
+ const limnCbfPoints *lpnt, unsigned int loi,
+ unsigned int hii);
+LIMN_EXPORT int limnCbfGo(limnCbfPath *path, limnCbfCtx *fctx,
+ const limnCbfPoints *lpnt);
/* lpu{Flotsam,. . .}.c */
#define LIMN_DECLARE(C) LIMN_EXPORT const unrrduCmd limnPu_##C##Cmd;
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-07-01 16:37:53 UTC (rev 7174)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-07-03 19:54:06 UTC (rev 7175)
@@ -38,9 +38,9 @@
unsigned int ii, synthNum, pNum, nrpIterMax;
int loop, petc, verbose, tvt[4], fitSingleLoHi[2];
char *synthOut;
- limnCBFCtx *fctx;
- limnCBFPath *path;
- limnCBFPoints *lpnt;
+ limnCbfCtx *fctx;
+ limnCbfPath *path;
+ limnCbfPoints *lpnt;
hestOptAdd_1_Other(&hopt, "i", "input", &_nin, NULL, "input xy points", nrrdHestNrrd);
hestOptAdd_Flag(&hopt, "loop", &loop,
@@ -57,10 +57,8 @@
hestOptAdd_1_Double(&hopt, "sup", "expo", &synthPow, "1",
"when synthesizing data on a single segment, warp U parameters "
"by raising to this power.");
- hestOptAdd_4_Int(&hopt, "tvt", "loi hii absi 1s", tvt, "0 0 0 -1",
- "if last value is >= 0: make single call to limnCBFFindTVT and quit, "
- "but note that the given loi,hii,absi args are not exactly what is "
- "passed to limnCBFFindTVT");
+ hestOptAdd_4_Int(&hopt, "tvt", "loi hii vvi 1s", tvt, "0 0 0 -1",
+ "if last value is >= 0: make single call to limnCbfTVT and quit");
hestOptAdd_1_UInt(&hopt, "im", "max", &nrpIterMax, "12",
"max # nrp iterations to run");
hestOptAdd_1_Double(&hopt, "deltathr", "delta", &deltaThresh, "0.0005",
@@ -76,7 +74,7 @@
hestOptAdd_1_Double(&hopt, "scl", "scale", &scale, "0",
"scale for geometry estimation");
hestOptAdd_2_Int(&hopt, "fs", "loi hii", fitSingleLoHi, "-1 -1",
- "(if loi is >= 0): just do a single call to limnCBFitSingle and "
+ "(if loi is >= 0): just do a single call to limnCbfSingle and "
"quit, using the -i input points, and fitting a spline between "
"the loi and hii indices given here. A negative hii will be "
"incremented by the number of points, so -1 works to indicate "
@@ -120,7 +118,7 @@
if (airStrlen(synthOut)) {
/* synthesize data from control points */
double *cpt = (double *)nin->data;
- limnCBFSeg seg;
+ limnCbfSeg seg;
int ci;
Nrrd *nsyn;
if (!(synthNum >= 3)) {
@@ -139,7 +137,7 @@
for (ii = 0; ii < synthNum; ii++) {
double uu = AIR_AFFINE(0, ii, synthNum - 1, 0, 1);
uu = pow(uu, synthPow);
- limnCBFSegEval(xy + 2 * ii, &seg, uu);
+ limnCbfSegEval(xy + 2 * ii, &seg, uu);
}
nsyn = nrrdNew();
airMopAdd(mop, nsyn, (airMopper)nrrdNix, airMopAlways);
@@ -157,17 +155,17 @@
xy = (double *)nin->data;
pNum = (unsigned int)nin->axis[1].size;
- if (!(lpnt = limnCBFPointsNew(xy, nrrdTypeDouble, 2, pNum, loop))) {
+ if (!(lpnt = limnCbfPointsNew(xy, nrrdTypeDouble, 2, pNum, loop))) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble setting up points:\n%s", me, err);
airMopError(mop);
return 1;
}
- airMopAdd(mop, lpnt, (airMopper)limnCBFPointsNix, airMopAlways);
- path = limnCBFPathNew();
- airMopAdd(mop, path, (airMopper)limnCBFPathNix, airMopAlways);
- fctx = limnCBFCtxNew();
- airMopAdd(mop, fctx, (airMopper)limnCBFCtxNix, airMopAlways);
+ airMopAdd(mop, lpnt, (airMopper)limnCbfPointsNix, airMopAlways);
+ path = limnCbfPathNew();
+ airMopAdd(mop, path, (airMopper)limnCbfPathNix, airMopAlways);
+ fctx = limnCbfCtxNew();
+ airMopAdd(mop, fctx, (airMopper)limnCbfCtxNix, airMopAlways);
fctx->verbose = verbose;
fctx->nrpIterMax = nrpIterMax;
fctx->scale = scale;
@@ -177,7 +175,7 @@
fctx->nrpPsi = psi;
fctx->cornAngle = cangle;
- if (tvt[3] >= 0) { /* here just to call limnCBFFindTVT once */
+ if (tvt[3] >= 0) { /* here just to call limnCbfTVT once */
double lt[2], vv[2], rt[2];
int pnum = AIR_INT(lpnt->num);
/* whoa - this is how GLK learned that AIR_MOD is garbage if args differ in
@@ -184,16 +182,15 @@
sign-ed-ness */
unsigned int loi = AIR_UINT(AIR_MOD(tvt[0], pnum));
unsigned int hii = AIR_UINT(AIR_MOD(tvt[1], pnum));
- unsigned int ofi = AIR_UINT(AIR_MOD(tvt[2] - tvt[0], pnum));
+ unsigned int vvi = AIR_UINT(AIR_MOD(tvt[2], pnum));
int E, oneSided = !!tvt[3];
E = 0;
if (!E && fctx->verbose)
- printf("%s: TVT %d (absolute) in [%d,%d] --> %u (offset) in [%u,%u]\n", me, /* */
- tvt[2], tvt[0], tvt[1], ofi, loi, hii);
- if (!E) E |= limnCBFCtxPrep(fctx, lpnt);
- if (!E && fctx->verbose)
- printf("%s: limnCBFCtxPrep done, calling limnCBFFindTVT\n", me);
- if (!E) E |= limnCBFFindTVT(lt, vv, rt, fctx, lpnt, loi, hii, ofi, oneSided);
+ printf("%s: int %d in [%d,%d] --> uint %u in [%u,%u]\n", me, /* */
+ tvt[2], tvt[0], tvt[1], vvi, loi, hii);
+ if (!E) E |= limnCbfCtxPrep(fctx, lpnt);
+ if (!E && fctx->verbose) printf("%s: limnCbfCtxPrep done, calling limnCbfTVT\n", me);
+ if (!E) E |= limnCbfTVT(lt, vv, rt, fctx, lpnt, loi, hii, vvi, oneSided);
if (E) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble doing lone tangent-vertex-tangent:\n%s", me, err);
@@ -200,7 +197,7 @@
airMopError(mop);
return 1;
}
- printf("%s: loi,hii=[%d,%d] ofi=%d oneSided=%d limnCBFFindTVT:\n", me, loi, hii, ofi,
+ printf("%s: loi,hii=[%d,%d] vvi=%d oneSided=%d limnCbfTVT:\n", me, loi, hii, vvi,
oneSided);
printf("lt = %g %g\n", lt[0], lt[1]);
printf("vv = %g %g\n", vv[0], vv[1]);
@@ -211,18 +208,18 @@
}
if (fitSingleLoHi[0] >= 0) {
- limnCBFSeg seg;
+ limnCbfSeg seg;
int pnum = AIR_INT(lpnt->num);
/* re-using the logic from the TVT case above */
unsigned int loi = AIR_UINT(AIR_MOD(fitSingleLoHi[0], pnum));
unsigned int hii = AIR_UINT(AIR_MOD(fitSingleLoHi[1], pnum));
- if (limnCBFitSingle(&seg, NULL, NULL, NULL, NULL, fctx, lpnt, loi, hii)) {
+ if (limnCbfSingle(&seg, NULL, NULL, NULL, NULL, fctx, lpnt, loi, hii)) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble doing single segment fit:\n%s", me, err);
airMopError(mop);
return 1;
}
- printf("%s: limnCBFitSingle results:\n", me);
+ printf("%s: limnCbfSingle results:\n", me);
for (ii = 0; ii < 4; ii++) {
printf("%g %g\n", seg.xy[0 + 2 * ii], seg.xy[1 + 2 * ii]);
}
@@ -236,22 +233,22 @@
fflush(stderr);
getchar();
}
-#if 0 /* HEY */
- if (limnCBFit(path, fctx, lpnt)) {
+
+ if (limnCbfGo(path, fctx, lpnt)) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble doing fitting:\n%s", me, err);
airMopError(mop);
return 1;
}
-#endif
+
dtime = (airTime() - time0) * 1000;
- printf("%s: time= %g ms;iterDone= %u ;deltaDone=%g, distMax=%g (@%u)\n", me, dtime,
+ printf("%s: time= %g ms;iterDone= %u ;deltaDone=%g, distMax=%g @ %u\n", me, dtime,
fctx->nrpIterDone, fctx->nrpDeltaDone, fctx->distMax, fctx->distMaxIdx);
{
unsigned int si;
printf("%s: path has %u segments:\n", me, path->segNum);
for (si = 0; si < path->segNum; si++) {
- limnCBFSeg *seg = path->seg + si;
+ limnCbfSeg *seg = path->seg + si;
printf("seg %u: (%g,%g) -- (%g,%g) -- (%g,%g) -- (%g,%g)\n", si, seg->xy[0],
seg->xy[1], seg->xy[2], seg->xy[3], seg->xy[4], seg->xy[5], seg->xy[6],
seg->xy[7]);
@@ -262,7 +259,7 @@
unsigned int oNum = pNum * 100;
double *pp = AIR_MALLOC(oNum * 2, double);
airMopAdd(mop, pp, airFree, airMopAlways);
- limnCBFPathSample(pp, oNum, path);
+ limnCbfPathSample(pp, oNum, path);
for (ii = 0; ii < oNum; ii++) {
printf("done %u %g %g\n", ii, (pp + 2 * ii)[0], (pp + 2 * ii)[1]);
}
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-07-01 16:37:53 UTC (rev 7174)
+++ teem/trunk/src/limn/splineFit.c 2024-07-03 19:54:06 UTC (rev 7175)
@@ -38,12 +38,15 @@
NOTE: spline fitting would be useful in 3D (or higher dimensions) too, but currently
this code only supports 2D. "DIM=2" flags places in code where that is explicit.
+
+NOTE: In Teem coding standards, "Cbf" would be better written as "CBF", but all these
+initials got annoying with other CamelCase function names.
*/
#define PNMIN(ISLOOP) ((ISLOOP) ? 4 : 3)
/*
-limnCBFPointsNew
+limnCbfPointsNew
create a point data container, possibly around given pdata pointer. In an aspirational
hope of API stability, this is one of the few functions for which the interface itself
@@ -50,10 +53,10 @@
does not expose the specificity to DIM=2 and type double (though the code inside
does (apologetically) enforce that).
*/
-limnCBFPoints * /* Biff: NULL */
-limnCBFPointsNew(const void *pdata, int ptype, uint dim, uint pnum, int isLoop) {
- static const char me[] = "limnCBFPointsNew";
- limnCBFPoints *lpnt;
+limnCbfPoints * /* Biff: NULL */
+limnCbfPointsNew(const void *pdata, int ptype, uint dim, uint pnum, int isLoop) {
+ static const char me[] = "limnCbfPointsNew";
+ limnCbfPoints *lpnt;
if (airEnumValCheck(nrrdType, ptype)) {
biffAddf(LIMN, "%s: point data type %d not valid", me, ptype);
return NULL;
@@ -72,8 +75,10 @@
isLoop ? "loop" : "non-loop", pnum);
return NULL;
}
- lpnt = AIR_CALLOC(1, limnCBFPoints);
- assert(lpnt);
+ if (!(lpnt = AIR_CALLOC(1, limnCbfPoints))) {
+ biffAddf(LIMN, "%s: couldn't allocate point container", me);
+ return NULL;
+ }
if (pdata) {
/* we are wrapping around a given pre-allocated buffer */
lpnt->pp = pdata;
@@ -81,8 +86,10 @@
} else {
/* we are allocating our own buffer */
lpnt->pp = NULL;
- lpnt->ppOwn = AIR_CALLOC(pnum, double);
- assert(lpnt->ppOwn);
+ if (!(lpnt->ppOwn = AIR_CALLOC(dim * pnum, double))) {
+ biffAddf(LIMN, "%s: couldn't allocate %u %u-D point", me, pnum, dim);
+ return NULL;
+ }
}
lpnt->num = pnum;
lpnt->dim = dim; /* but really DIM=2 because of above */
@@ -90,8 +97,8 @@
return lpnt;
}
-limnCBFPoints * /* Biff: nope */
-limnCBFPointsNix(limnCBFPoints *lpnt) {
+limnCbfPoints * /* Biff: nope */
+limnCbfPointsNix(limnCbfPoints *lpnt) {
if (lpnt) {
/* don't touch lpnt->pp */
if (lpnt->ppOwn) free(lpnt->ppOwn);
@@ -101,8 +108,8 @@
}
int /* Biff: 1 */
-limnCBFPointsCheck(const limnCBFPoints *lpnt) {
- static const char me[] = "limnCBFPointsCheck";
+limnCbfPointsCheck(const limnCbfPoints *lpnt) {
+ static const char me[] = "limnCbfPointsCheck";
uint pnmin;
int have;
@@ -112,7 +119,7 @@
}
pnmin = PNMIN(lpnt->isLoop);
if (!(lpnt->num >= pnmin)) {
- biffAddf(LIMN, "%s: need %u or more points in limnCBFPoints (not %u)%s", me, pnmin,
+ biffAddf(LIMN, "%s: need %u or more points in limnCbfPoints (not %u)%s", me, pnmin,
lpnt->num, lpnt->isLoop ? " for loop" : "");
return 1;
}
@@ -126,7 +133,7 @@
static void
segInit(void *_seg) {
- limnCBFSeg *seg = (limnCBFSeg *)_seg;
+ limnCbfSeg *seg = (limnCbfSeg *)_seg;
ELL_2V_NAN_SET(seg->xy + 0); /* DIM=2 */
ELL_2V_NAN_SET(seg->xy + 2);
ELL_2V_NAN_SET(seg->xy + 4);
@@ -136,12 +143,12 @@
return;
}
-limnCBFPath * /* Biff: nope */
-limnCBFPathNew() {
- limnCBFPath *path;
- path = AIR_MALLOC(1, limnCBFPath);
+limnCbfPath * /* Biff: nope */
+limnCbfPathNew() {
+ limnCbfPath *path;
+ path = AIR_MALLOC(1, limnCbfPath);
if (path) {
- path->segArr = airArrayNew((void **)(&path->seg), &path->segNum, sizeof(limnCBFSeg),
+ path->segArr = airArrayNew((void **)(&path->seg), &path->segNum, sizeof(limnCbfSeg),
128 /* incr */);
airArrayStructCB(path->segArr, segInit, NULL);
path->isLoop = AIR_FALSE;
@@ -149,8 +156,8 @@
return path;
}
-limnCBFPath * /* Biff: nope */
-limnCBFPathNix(limnCBFPath *path) {
+limnCbfPath * /* Biff: nope */
+limnCbfPathNix(limnCbfPath *path) {
if (path) {
airArrayNuke(path->segArr);
free(path);
@@ -159,18 +166,18 @@
}
void
-limnCBFPathJoin(limnCBFPath *dst, const limnCBFPath *src) {
+limnCbfPathJoin(limnCbfPath *dst, const limnCbfPath *src) {
uint bb = airArrayLenIncr(dst->segArr, src->segNum);
- memcpy(dst->seg + bb, src->seg, (src->segNum) * sizeof(limnCBFSeg));
+ memcpy(dst->seg + bb, src->seg, (src->segNum) * sizeof(limnCbfSeg));
return;
}
-/* initialize a freshly allocated limnCBFCtx struct;
+/* initialize a freshly allocated limnCbfCtx struct;
the pointers therein do not point to anything valid */
static void
-ctxInit(limnCBFCtx *fctx) {
+ctxInit(limnCbfCtx *fctx) {
if (!fctx) return;
- /* defaults for input parameters to various CBF functions */
+ /* defaults for input parameters to various Cbf functions */
fctx->verbose = 0;
fctx->cornerFind = AIR_TRUE;
fctx->cornerNMS = AIR_TRUE;
@@ -179,7 +186,7 @@
fctx->scale = 0; /* will need to be set to something valid elsewhere */
fctx->nrpCap = 3.0;
fctx->nrpIota = 0.8;
- fctx->nrpPsi = 10;
+ fctx->nrpPsi = 100;
fctx->nrpDeltaThresh = 0.01;
fctx->alphaMin = 0.001;
fctx->detMin = 0.01;
@@ -186,8 +193,9 @@
fctx->cornAngle = 120.0; /* degrees */
/* internal state */
/* initialize buffer pointers to NULL and buffer lengths to 0 */
- fctx->uu = fctx->vw = fctx->tw = fctx->cpp = fctx->clt = fctx->crt = NULL;
- fctx->uLen = fctx->wLen = fctx->cNum = 0;
+ fctx->uu = fctx->vw = fctx->tw = fctx->ctvt = NULL;
+ fctx->cidx = NULL;
+ fctx->ulen = fctx->wlen = fctx->cnum = 0;
/* initialize outputs to bogus valus */
fctx->nrpIterDone = (uint)(-1);
fctx->distMaxIdx = (uint)(-1);
@@ -198,25 +206,23 @@
return;
}
-limnCBFCtx * /* Biff: nope */
-limnCBFCtxNew() {
- limnCBFCtx *ret;
+limnCbfCtx * /* Biff: nope */
+limnCbfCtxNew() {
+ limnCbfCtx *ret;
- ret = AIR_CALLOC(1, limnCBFCtx);
- assert(ret);
- ctxInit(ret);
+ ret = AIR_CALLOC(1, limnCbfCtx);
+ if (ret) ctxInit(ret);
return ret;
}
-limnCBFCtx * /* Biff: nope */
-limnCBFCtxNix(limnCBFCtx *fctx) {
+limnCbfCtx * /* Biff: nope */
+limnCbfCtxNix(limnCbfCtx *fctx) {
if (fctx) {
if (fctx->uu) free(fctx->uu);
if (fctx->vw) free(fctx->vw);
if (fctx->tw) free(fctx->tw);
- if (fctx->cpp) free(fctx->cpp);
- if (fctx->clt) free(fctx->clt);
- if (fctx->crt) free(fctx->crt);
+ if (fctx->ctvt) free(fctx->ctvt);
+ if (fctx->cidx) free(fctx->cidx);
free(fctx);
}
return NULL;
@@ -225,10 +231,10 @@
/*
ctxBuffersSet: ensures that some buffers in fctx: uu, vw, tw are set up for current
#points pNum and measurement scale fctx->scale. The buffers are re-allocated only when
-necessary. Does NOT touch the corner-related buffers: cpp, clt, crt
+necessary. Does NOT touch the corner-related buffers: ctvt, cidx
*/
static int /* Biff: 1 */
-ctxBuffersSet(limnCBFCtx *fctx, uint pNum) {
+ctxBuffersSet(limnCbfCtx *fctx, uint pNum) {
static const char me[] = "ctxBuffersSet";
double scale;
uint ulen, ii;
@@ -247,7 +253,7 @@
(big enough to parameterize all the points at once) is safe, though it will likely
be excessive given how the path may be split at corners into separate segments. */
ulen = pNum * 2; /* DIM=2 */
- if (ulen != fctx->uLen) {
+ if (ulen != fctx->ulen) {
airFree(fctx->uu);
if (!(fctx->uu = AIR_CALLOC(ulen, double))) {
biffAddf(LIMN, "%s: failed to allocate uu buffer (%u doubles)", me, ulen);
@@ -254,13 +260,13 @@
return 1;
}
}
- fctx->uLen = ulen;
+ fctx->ulen = ulen;
if (0 == scale) {
/* will do simplest possible finite differences; no need for weights */
fctx->vw = airFree(fctx->vw);
fctx->tw = airFree(fctx->tw);
- fctx->wLen = 0;
+ fctx->wlen = 0;
} else {
/* one: what value in summing kernel weights should count as 1.0. This should
probably be a parm in fctx, but not very interesting to change; it reflects something
@@ -310,7 +316,7 @@
me, wlen, scale, pNum);
return 1;
}
- if (wlen != fctx->wLen) {
+ if (wlen != fctx->wlen) {
airFree(fctx->vw);
airFree(fctx->tw);
if (!((fctx->vw = AIR_CALLOC(wlen, double))
@@ -318,7 +324,7 @@
biffAddf(LIMN, "%s: couldn't allocate weight buffers (%u doubles)", me, wlen);
return 1;
}
- fctx->wLen = wlen;
+ fctx->wlen = wlen;
}
/* normalization intent:
1 = sum_i(vw[|i|]) for i=-(len-1)...len-1
@@ -355,20 +361,20 @@
}
/*
-limnCBFCtxPrep
+limnCbfCtxPrep
checks the things that are going to be passed around a lot, and makes call to initialize
buffers inside fctx
*/
int /* Biff: 1 */
-limnCBFCtxPrep(limnCBFCtx *fctx, const limnCBFPoints *lpnt) {
- static const char me[] = "limnCBFCtxPrep";
+limnCbfCtxPrep(limnCbfCtx *fctx, const limnCbfPoints *lpnt) {
+ static const char me[] = "limnCbfCtxPrep";
if (!(fctx && lpnt)) {
biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
- if (limnCBFPointsCheck(lpnt)) {
+ if (limnCbfPointsCheck(lpnt)) {
biffAddf(LIMN, "%s: problem with points", me);
return 1;
}
@@ -463,12 +469,12 @@
#define CBD2(P, V0, V1, V2, V3, T, W) CBDI(P, VCBD2, V0, V1, V2, V3, T, W)
/*
-limnCBFSegEval
+limnCbfSegEval
-evaluates a single limnCBFSeg at one point tt in [0.0,1.0]
+evaluates a single limnCbfSeg at one point tt in [0.0,1.0]
*/
void
-limnCBFSegEval(double *vv, const limnCBFSeg *seg, double tt) {
+limnCbfSegEval(double *vv, const limnCbfSeg *seg, double tt) {
double ww[4];
const double *xy = seg->xy;
CBD0(vv, xy + 0, xy + 2, xy + 4, xy + 6, tt, ww); /* DIM=2 */
@@ -475,7 +481,7 @@
/*
fprintf(stderr, "!%s: tt=%g -> ww={%g,%g,%g,%g} * "
"{(%g,%g),(%g,%g),(%g,%g),(%g,%g)} = (%g,%g)\n",
- "limnCBFSegEval", tt, ww[0], ww[1], ww[2], ww[3],
+ "limnCbfSegEval", tt, ww[0], ww[1], ww[2], ww[3],
(xy + 0)[0], (xy + 0)[1],
(xy + 2)[0], (xy + 2)[1],
(xy + 4)[0], (xy + 4)[1],
@@ -485,23 +491,23 @@
}
/*
-limnCBFPathSample
+limnCbfPathSample
-evaluates limnCBFPath at pNum locations, uniformly (and very naively) distributed among
+evaluates limnCbfPath at pNum locations, uniformly (and very naively) distributed among
the path segments, and saves into (pre-allocated) xy
*/
void
-limnCBFPathSample(double *xy, uint pNum, const limnCBFPath *path) {
+limnCbfPathSample(double *xy, uint pNum, const limnCbfPath *path) {
uint ii, sNum = path->segNum;
for (ii = 0; ii < pNum; ii++) {
uint segi = airIndex(0, ii, pNum - 1, sNum);
- const limnCBFSeg *seg = path->seg + segi;
+ const limnCbfSeg *seg = path->seg + segi;
double tmpf = AIR_AFFINE(0, ii, pNum - 1, 0, sNum);
double tt = tmpf - segi;
- limnCBFSegEval(xy + 2 * ii, seg, tt); /* DIM=2 */
+ limnCbfSegEval(xy + 2 * ii, seg, tt); /* DIM=2 */
/*
fprintf(stderr, "!%s: %u -> %u (%g) %g -> (%g,%g)\n",
- "limnCBFPathSample", ii, segi, tmpf, tt,
+ "limnCbfPathSample", ii, segi, tmpf, tt,
(xy + 2*ii)[0], (xy + 2*ii)[1]);
*/
}
@@ -508,68 +514,90 @@
return;
}
-/* utility function for counting how many vertices are in index span [loi,hii] inclusive.
-It is not our job here to care about lpnt->isLoop; we just assume that if we're faced
-with hii<loi, it must be because of a loop */
-static uint
-spanLength(const limnCBFPoints *lpnt, uint loi, uint hii) {
- uint topi = hii + (hii < loi) * (lpnt->num);
- return topi - loi + 1;
-}
-
/* cheesy macro as short-hand to access either pp or ppOwn */
#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
-/* error-checked index processing for limnCBFFindTVT and maybe others */
+/* idxPrep: error-checked index processing for limnCbfTVT and maybe others
+This is messy because of the flexibility in how we handle points:
+might not be a loop (so an actual [loi,hii] vertex index span is needed),
+or, is a loop but still only working within bounds of [loi,hii] span,
+or, is a loop and [loi,hii]=[0,0] says that there are no bounds (aka "loopy")
+*/
static int /* Biff: 1 */
-idxPrep(int *sloP, int *shiP, int *loopyP, const limnCBFCtx *fctx,
- const limnCBFPoints *lpnt, uint loi, uint hii) {
+idxPrep(uint *loiP, uint *hiiP, uint *vviP, int *loopyP, int verbose,
+ const limnCbfPoints *lpnt, uint gloi, uint ghii, uint gvvi) {
static const char me[] = "idxPrep";
- int slo, shi, loopy, spanlen;
+ uint pnum, loi, hii, vvi;
+ int loopy;
- *sloP = *shiP = 10 * lpnt->num; /* initialize to bogus indices */
- if (!(loi < lpnt->num && hii < lpnt->num)) {
- biffAddf(LIMN, "%s: loi %u, hii %u not both < #points %u", me, loi, hii, lpnt->num);
+ *loiP = *hiiP = *vviP = UINT_MAX; /* initialize to bogus indices */
+ pnum = lpnt->num;
+ if (!(pnum < (1U << 29))) {
+ biffAddf(LIMN, "%s: # points %u seems too big (to stay well clear of UB)", me, pnum);
return 1;
}
- if (loi == hii && hii != 0) {
- biffAddf(LIMN,
- "%s: can only have loi == hii if both 0 (not %u), to signify no bounds in "
- "point loop",
- me, loi);
+ if (!(gloi < pnum && ghii < pnum && gvvi < pnum)) {
+ biffAddf(LIMN, "%s: given loi %u, hii %u, vvi %u not all < #points %u", me, gloi,
+ ghii, gvvi, pnum);
return 1;
}
- slo = AIR_INT(loi);
- shi = AIR_INT(hii);
- if (fctx->verbose > 1) {
- printf("%s: span as uint [%u,%u] -> int [%d,%d]\n", me, loi, hii, slo, shi);
+ if (gloi == ghii && ghii != 0) {
+ biffAddf(
+ LIMN,
+ "%s: can only have gloi == ghii if both 0 (not %u), to signify no bounds in "
+ "point loop%s",
+ me, gloi,
+ lpnt->isLoop ? "" /* it is a loop */
+ : " (and these points aren't in a loop anyway!)");
+ return 1;
}
+ /* else loi == hii implies loi == hii == 0 */
+ loi = gloi;
+ hii = ghii;
+ vvi = gvvi;
if (lpnt->isLoop) {
- loopy = (0 == loi && 0 == hii);
- if (hii < loi) {
- shi += lpnt->num;
+ if (0 == gloi && 0 == ghii) {
+ loopy = AIR_TRUE;
+ } else {
+ if (ghii < gloi) hii += pnum;
+ if (gvvi < gloi) vvi += pnum;
+ loopy = AIR_FALSE;
}
} else {
- if (0 == loi && 0 == hii) {
- biffAddf(LIMN, "%s: can only have loi == hii == 0 with point loop", me);
+ if (0 == gloi && 0 == ghii) {
+ biffAddf(LIMN, "%s: can only have given loi == hii == 0 with point loop", me);
return 1;
}
- if (hii < loi) {
- biffAddf(LIMN, "%s: can only have hii (%u) < loi (%u) in a point loop", me, hii,
- loi);
+ if (!(gloi < ghii)) {
+ biffAddf(LIMN, "%s: need given loi (%u) < hii (%u) since not in point loop", me,
+ gloi, ghii);
return 1;
}
+ if (!(gvvi <= ghii)) {
+ biffAddf(LIMN, "%s: need given vvi (%u) < hhi (%u) since not in point loop", me,
+ gvvi, ghii);
+ return 1;
+ }
loopy = AIR_FALSE;
}
- spanlen = shi - slo + 1;
- if (spanlen <= 1 && !loopy) {
- biffAddf(LIMN, "%s: [%u,%u]->[%d,%d] span length %d <= 1 but not in loop", me, loi,
- hii, slo, shi, spanlen);
- return 1;
+ if (verbose) {
+ printf("%s: given loi,hii,vvi %u %u %u --> my %u %u %u loopy %d\n", me, gloi, ghii,
+ gvvi, loi, hii, vvi, loopy);
}
+ /* now: must have loi < hii and vvi < hii */
+ if (!loopy) {
+ /* need to check that vvi is inside consequential bounds [loi,hii] */
+ if (!(loi <= vvi && vvi <= hii)) {
+ biffAddf(LIMN, "%s: vvi %u->%u not in [%u,%u]->[%u,%u] span (not in loop)", me,
+ gvvi, vvi, gloi, ghii, loi, hii);
+ return 1;
+ }
+ }
+
/* all's well, set output values */
- *sloP = slo;
- *shiP = shi;
+ *loiP = loi;
+ *hiiP = hii;
+ *vviP = vvi;
*loopyP = loopy;
return 0;
}
@@ -582,78 +610,73 @@
}
/*
-limnCBFFindTVT: Find constraints for spline fitting: incoming/left tangent lt, center or
+limnCbfTVT: Find constraints for spline fitting: incoming/left tangent lt, center or
endpoint vertex vv, outgoing/right tangent rt; any but not all can be NULL. These are
-computed from the given points lpnt, at offset index ofi within index range [loi, hii]
-and only that range: that range is probably delimited by corners, and we have to be blind
-to anything past the corners on either side of us (except if loi==hii==0 in a loop, in
-which case we can look at all the points).
+computed from the given points lpnt, at given vertex index gvvi, looking only within
+vertex index range [gloi, ghii]: that range is probably delimited by corners, and we have
+to be blind to anything past the corners on either side of us. HOWEVER, f gloi==ghii==0
+and lpnt is a point loop, then we can look at all the points.
Given that this is the inner-loop of other things, it would make sense to have a
-non-public version without all the error checking, but given the birthing pains of this
-code, the error-checking is a safety-net, and is welcome until profiling shows it is
-actually a problem.
+non-public version without all the error checking, but given the prolonged birthing pain
+of the code in this file, the error-checking is a useful and welcome safety-net, and is
+ok until profiling shows it is actually a problem.
-NOTE: this assumes that limnCBFCtxPrep(fctx, lpnt) was called without error!
-It (via ctxBuffersSet) allocates things that we depend on here
+NOTE: this assumes that limnCbfCtxPrep(fctx, lpnt) was called without error!
+That (via ctxBuffersSet) allocates things that we depend on here.
*/
int /* Biff: 1 */
-limnCBFFindTVT(double lt[2], double vv[2], double rt[2], const limnCBFCtx *fctx,
- const limnCBFPoints *lpnt, uint loi, uint hii, uint ofi, int oneSided) {
- static const char me[] = "limnCBFFindTVT";
+limnCbfTVT(double lt[2], double vv[2], double rt[2], const limnCbfCtx *fctx,
+ const limnCbfPoints *lpnt, uint gloi, uint ghii, uint gvvi, int oneSided) {
+ static const char me[] = "limnCbfTVT";
/* we use here (signed) int for things that might seem better as uint, but it
simplifies implementing arithmetic and comparisons given how indices wrap around in
point loops */
- int slo, /* signed version of loi */
- shi, /* signed version of hii, but shi = hii + lpnt->num if hii < loi in loop */
- sof, /* signed versions of ofi */
- loopy, /* lpnt->isLoop && loi == 0 && hii = 0, i.e. there are no bounds on indices */
- pnum, /* total number of points in lpnts */
- spanlen, /* span length: number of points in [loi,hii] */
- icent, iplus,
- imnus; /* icent is the actual data index corresponding to loi + ofi; it is used for
- both the scale==0 and scale>0 cases; iplus and imnus are only needed with
- scale==0, but too annoying to break those out into that specific branch */
+ uint loi, /* error-checked gloi */
+ hii, /* error-checked ghii, with loop handling */
+ vvi; /* error-checked gvvi, with loop handling */
+ int slo, shi, /* signed versions of loi, hii */
+ pnum, /* total number of points in lpnts */
+ loopy, /* lpnt->isLoop && 0 == loi == hii, i.e. there are no bounds on indices */
+ icent, iplus, imnus; /* icent is the actual data index corresponding to vvi; it is
+ used for both the scale==0 and scale>0 cases; iplus and imnus
+ are only needed with scale==0, but breaking those out into
+ that specific branch is not worth the copypasta */
if (!((lt || vv || rt) && fctx && lpnt)) {
biffAddf(LIMN, "%s: got NULL pointer (or too many NULL pointers)", me);
return 1;
}
+ /* so: each of lt, vv, rt can be NULL (they just can't be all NULL) */
if (fctx->verbose > 1) {
- printf("%s: hello: %u in [%u,%u] in %sloop with %u points (%s-sided)\n", me, ofi,
- loi, hii, lpnt->isLoop ? "" : "NON-", lpnt->num, oneSided ? "1" : "2");
+ printf("%s: hello: %u in [%u,%u] in %sloop with %u points (%s-sided)\n", me, gvvi,
+ gloi, ghii, lpnt->isLoop ? "" : "NON-", lpnt->num, oneSided ? "1" : "2");
}
- /* so: each of lt, vv, rt can be NULL (they just can't be all NULL) */
- if (idxPrep(&slo, &shi, &loopy, fctx, lpnt, loi, hii)) {
- biffAddf(LIMN, "%s: trouble with loi %u or hii %u", me, loi, hii);
+ if (idxPrep(&loi, &hii, &vvi, &loopy, fctx->verbose > 1, lpnt, gloi, ghii, gvvi)) {
+ biffAddf(LIMN, "%s: trouble with given loi %u, hii %u, or vvi %u", me, gloi, ghii,
+ gvvi);
return 1;
}
- spanlen = shi - slo + 1;
- pnum = AIR_INT(lpnt->num);
+ pnum = AIR_INT(lpnt->num); /* YES really needs to be signed int; see below */
if (fctx->verbose) {
- printf("%s: %d pnts: [%u,%u]->[%d,%d] (len=%d) (loopy=%u)\n", me, pnum, loi, hii,
- slo, shi, spanlen, loopy);
+ printf("%s: (post-idxPrep) %u in [%u,%u] -> %u in [%u,%u] (loopy=%d)\n", me, gvvi,
+ gloi, ghii, vvi, loi, hii, loopy);
}
/* now:
- 0 == slo == shi implies lpnt->isLoop (and this is called "loopy")
- slo == shi != 0 is always impossible
- always: slo < shi (even if given hii < loi), and thus any indices in range [slo,shi]
- need to be mod'd with pnum before indexing into PP(lpnt)
- spanlen >= 2 */
- if (!loopy && !(ofi < AIR_UINT(spanlen))) {
- biffAddf(LIMN,
- "%s: ofi %u too high for [%u,%u]->[%d,%d] span length %d (not in loop)", me,
- ofi, loi, hii, slo, shi, spanlen);
- return 1;
- }
- /* now ofi is a valid index in [0,..,spanlen-1] */
- sof = AIR_INT(ofi);
- icent = slo + sof;
+ -- 0 == loi == hii implies lpnt->isLoop (and this is called "loopy")
+ (but loopy does not imply lpnt->isLoop: can work in loop on sub-span overlapping 0)
+ -- loi == hii != 0 is always impossible
+ -- always: loi < hii (even if given ghii < gloi), and thus any indices in range
+ [loi,hii] need to be mod'd with pnum before indexing into PP(lpnt) */
+ /* ------------------- now switch to signed ints ------------------- */
+ slo = AIR_INT(loi);
+ shi = AIR_INT(hii);
+ icent = AIR_INT(vvi);
iplus = icent + 1;
imnus = icent - 1;
if (!loopy) {
- /* this is the code that motivated if (hii < loi) shi += pnum;
+ /* this is the code that motivated if (hii < loi) hii += pnum;
otherwise clamping is too annoying */
icent = AIR_CLAMP(slo, icent, shi);
iplus = AIR_CLAMP(slo, iplus, shi);
@@ -684,7 +707,7 @@
double posM[2] = {0, 0}, posC[2] = {0, 0}, posP[2] = {0, 0};
const double *vw = fctx->vw;
const double *tw = fctx->tw;
- int lim = (int)fctx->wLen - 1, /* limit on loop index */
+ int lim = (int)fctx->wlen - 1, /* limit on loop index */
ci; /* loops through [-lim,lim] */
if (!(vw && tw)) {
biffAddf(LIMN, "%s: fctx internal buffers vw and tw not both allocated", me);
@@ -695,23 +718,33 @@
return 1;
}
for (ci = -lim; ci <= lim; ci++) {
- uint wi = abs(ci); /* weight index into vw, tw */
- int adi, /* actual data index */
- di = slo + sof + ci; /* signed (and not %-ed) index into data */
+ uint wi = abs(ci); /* weight index into vw, tw */
+ int di = icent + ci, /* signed (and not %-ed) index into data */
+ cdi, /* clamped data index */
+ adi; /* actual data index */
const double *xy;
- if (!loopy) di = AIR_CLAMP(slo, di, shi);
- adi = AIR_MOD(di, pnum);
+ cdi = loopy ? di : AIR_CLAMP(slo, di, shi);
+ adi = AIR_MOD(cdi, pnum);
xy = PP(lpnt) + 2 * adi;
ELL_2V_SCALE_INCR(posC, vw[wi], xy);
if (fctx->verbose > 1) {
- printf("%s: ci=%d (in [%d,%d]) --> di=%d --> adi=%d; v,t weights %g,%g\n", me,
- ci, -lim, lim, di, adi, vw[wi], tw[wi]);
+ printf(
+ "%s: ci=%d (in [%d,%d]) di=%d cdi=%d (in[%d,%d]) adi=%d; v,t w %g,%g on "
+ "xy=(%g,%g)\n",
+ me, ci, -lim, lim, di, cdi, slo, shi, adi, vw[wi], tw[wi], xy[0], xy[1]);
+ printf("%s: ---> posC=(%g,%g)\n", me, posC[0], posC[1]);
}
if (ci < 0) {
ELL_2V_SCALE_INCR(posM, tw[wi], xy);
+ if (fctx->verbose > 1) {
+ printf("%s: ---> posM=(%g,%g)\n", me, posM[0], posM[1]);
+ }
}
if (ci > 0) {
ELL_2V_SCALE_INCR(posP, tw[wi], xy);
+ if (fctx->verbose > 1) {
+ printf("%s: ---> posP=(%g,%g)\n", me, posP[0], posP[1]);
+ }
}
}
{
@@ -720,8 +753,8 @@
some early stage of debugging) */
double off[2], offlen, okoff = 0.95 * fctx->epsilon; /* DIM=2 throughout */
const double *xy = PP(lpnt) + 2 * icent; /* center vertex in given data */
- ELL_2V_SUB(off, posC, xy); /* off = posC - xy, from given to computed */
- ELL_2V_NORM(off, off, offlen);
+ ELL_2V_SUB(off, posC, xy); /* off = posC - xy, from given to computed */
+ ELL_2V_NORM(off, off, offlen); /* offlen = |off|; off /= |off| */
offlen = AIR_MIN(okoff, offlen);
/* difference between chosen (x,y) datapoint and spline endpoint
can be in any direction, but we limit the length */
@@ -740,10 +773,19 @@
return 0;
}
+/* utility function for counting how many vertices are in index span [loi,hii] inclusive.
+It is not our job here to care about lpnt->isLoop; we just assume that if we're faced
+with hii<loi, it must be because of a loop */
+static uint
+spanLength(const limnCbfPoints *lpnt, uint loi, uint hii) {
+ uint topi = hii + (hii < loi) * (lpnt->num);
+ return topi - loi + 1;
+}
+
/* utility function for getting pointer to coords in lpnt, for point with index loi+ofi,
while accounting for possibility of wrapping */
static const double *
-pointPos(const limnCBFPoints *lpnt, uint loi, uint ofi) {
+pointPos(const limnCbfPoints *lpnt, uint loi, uint ofi) {
uint ii = (loi + ofi) % lpnt->num;
return PP(lpnt) + 2 * ii; /* DIM=2 */
}
@@ -769,9 +811,9 @@
arc on some but not all iterations (possibly unstable?)
*/
static void
-findAlpha(double alpha[2], limnCBFCtx *fctx, /* must be non-NULL */
+findAlpha(double alpha[2], limnCbfCtx *fctx, /* must be non-NULL */
const double vv0[2], const double tt1[2], const double tt2[2],
- const double vv3[2], const limnCBFPoints *lpnt, uint loi, uint hii) {
+ const double vv3[2], const limnCbfPoints *lpnt, uint loi, uint hii) {
static const char me[] = "findAlpha";
uint ii, spanlen;
double det, F2L[2], lenF2L;
@@ -844,9 +886,9 @@
order to match the given points xy
*/
static double
-reparm(const limnCBFCtx *fctx, /* must be non-NULL */
+reparm(const limnCbfCtx *fctx, /* must be non-NULL */
const double alpha[2], const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2], const limnCBFPoints *lpnt, uint loi,
+ const double tt2[2], const double vv3[2], const limnCbfPoints *lpnt, uint loi,
uint hii) {
static const char me[] = "reparm";
uint ii, spanlen;
@@ -896,9 +938,9 @@
/* (assuming current parameterization in fctx->uu) sets fctx->distMax to max distance
to spline, at point fctx->distMaxIdx, and then sets fctx->distBig accordingly */
static void
-findDist(limnCBFCtx *fctx, const double alpha[2], const double vv0[2],
+findDist(limnCbfCtx *fctx, const double alpha[2], const double vv0[2],
const double tt1[2], const double tt2[2], const double vv3[2],
- const limnCBFPoints *lpnt, uint loi, uint hii) {
+ const limnCbfPoints *lpnt, uint loi, uint hii) {
uint ii, distMaxIdx, spanlen;
double vv1[2], vv2[2], distMax;
const double *uu = fctx->uu;
@@ -909,7 +951,7 @@
ELL_2V_SCALE_ADD2(vv2, 1, vv3, alpha[1], tt2);
distMax = AIR_NAN;
/* NOTE that the first and last points are actually not part of the max distance
- calculation, which motivates ensuring that the endpoints generated by limnCBFFindTVT
+ calculation, which motivates ensuring that the endpoints generated by limnCbfTVT
are actually sufficiently close to the first and last points (or else the fit spline
won't meet the expected accuracy threshold) */
for (ii = 1; ii < spanlen - 1; ii++) {
@@ -937,8 +979,8 @@
}
/*
-fitSingle: fits a single cubic Bezier spline, w/out error checking (limnCBFSingle is an
-error-checking wrapper around this). The given points coordinates are in limnCBFPoints
+fitSingle: fits a single cubic Bezier spline, WITHOUT error checking (limnCbfSingle is an
+error-checking wrapper around this). The given points coordinates are in limnCbfPoints
lpnt, between low/high indices loi/hii (inclusively); hii can be < loi in the case of a
point loop. From GIVEN initial endpoint vv0, initial tangent tt1, final tangent tt2
(pointing backwards), and final endpoint vv3, the job of this function is actually just
@@ -957,8 +999,8 @@
Information about the results of this process are set in the given fctx.
*/
static void
-fitSingle(double alpha[2], limnCBFCtx *fctx, const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2], const limnCBFPoints *lpnt, uint loi,
+fitSingle(double alpha[2], const double vv0[2], const double tt1[2], const double tt2[2],
+ const double vv3[2], limnCbfCtx *fctx, const limnCbfPoints *lpnt, uint loi,
uint hii) {
static const char me[] = "fitSingle";
uint iter, pNum;
@@ -1077,37 +1119,81 @@
return;
}
+static int
+vttvCalcOrCopy(double *vttv[4], int *givenP, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], limnCbfCtx *fctx,
+ const limnCbfPoints *lpnt, uint loi, uint hii) {
+ static const char me[] = "vttvCalcOrCopy";
+
+ /* DIM=2 throughout! */
+ if (vv0 && tt1 && tt2 && vv3) {
+ /* copy given geometry info */
+ ELL_2V_COPY(vttv[0], vv0);
+ ELL_2V_COPY(vttv[1], tt1);
+ ELL_2V_COPY(vttv[2], tt2);
+ ELL_2V_COPY(vttv[3], vv3);
+ if (givenP) *givenP = AIR_TRUE;
+ } else {
+ double v0c[2], t1c[2], t2c[2], v3c[3]; /* locally computed info */
+ if (vv0 || tt1 || tt2 || vv3) {
+ biffAddf(LIMN,
+ "%s: should either give all vv0,tt1,tt2,vv3 or none (not %p,%p,%p,%p)",
+ me, (const void *)vv0, (const void *)tt1, (const void *)tt2,
+ (const void *)vv3);
+ return 1;
+ }
+ /* do not have geometry info; must find it all */
+ if (limnCbfTVT(NULL, v0c, t1c, /* */
+ fctx, lpnt, loi, hii, loi, /* */
+ AIR_TRUE /* oneSided */)
+ || limnCbfTVT(t2c, v3c, NULL, /* */
+ fctx, lpnt, loi, hii, hii, /* */
+ AIR_TRUE /* oneSided */)) {
+ biffAddf(LIMN, "%s: trouble finding geometry info", me);
+ return 1;
+ }
+ if (fctx->verbose) {
+ printf("%s: found geometry (%g,%g) --> (%g,%g) -- (%g,%g) <-- (%g,%g)\n", me,
+ v0c[0], v0c[1], t1c[0], t1c[1], t2c[0], t2c[1], v3c[0], v3c[1]);
+ }
+ ELL_2V_COPY(vttv[0], v0c);
+ ELL_2V_COPY(vttv[1], t1c);
+ ELL_2V_COPY(vttv[2], t2c);
+ ELL_2V_COPY(vttv[3], v3c);
+ if (givenP) *givenP = AIR_FALSE;
+ }
+ return 0;
+}
+
/*
-limnCBFitSingle
+limnCbfSingle
Basically and error-checking version of fitSingle; in the limn API because it's needed
for testing. Unlike fitSingle, the geometry info vv0, tt1, tt2, vv3 can either be punted
on (by passing NULL for all) or not, by passing specific vectors for all. The results are
-converted into the fields in the given limnCBFSeg *seg. Despite misgivings, we set
+converted into the fields in the given limnCbfSeg *seg. Despite misgivings, we set
both seg->corner[0,1] to AIR_TRUE.
Perservating on seg->corner[0,1]: we really don't have the information to consistently
set them with certainty. If not given the geometry vectors, we do assert oneSided when
estimating the vertices and tangents, so maybe then we can set set->corner[0,1] to true,
-but on the other hand we don't know what to do when the geometry vectors are given. But
-it wouldn't be cool to sometimes set fields in the output struct and sometimes not.
+but on the other hand we don't know what to do when the geometry vectors are given. Since
+it is not ok to sometimes set fields in the output struct and sometimes not, we always
+set them all.
This function used to also be lower-overhead, by not requiring a fctx, and taking a coord
data pointer instead of a lpnts. But for the sake of testing, there needed to be way of
passing specific loi and hii in a point loop, so that's what this accepts now. As a
result, that means this function isn't as convenient a function for one-off single-spline
-fits, and at this point limn doesn't have one. The real entry point for spline fitting
-is limnCBFit().
+fits, and currently limn does not have one. The real entry point for general-purpose
+spline fitting is limnCbfGo().
*/
int /* Biff: 1 */
-limnCBFitSingle(limnCBFSeg *seg, const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2], limnCBFCtx *fctx,
- limnCBFPoints *lpnt, uint loi, uint hii) {
- static const char me[] = "limnCBFitSingle";
- double v0c[2], t1c[2], t2c[2], v3c[2]; /* locally computed geometry info */
- const double *v0p, *t1p, *t2p, *v3p; /* pointers for the geometry info */
- double alpha[2]; /* recovered by fitting */
- uint spanlen;
+limnCbfSingle(limnCbfSeg *seg, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], limnCbfCtx *fctx,
+ const limnCbfPoints *lpnt, uint loi, uint hii) {
+ static const char me[] = "limnCbfSingle";
+ double alpha[2], V0[2], T1[2], T2[2], V3[2], *vttv[4] = {V0, T1, T2, V3};
airArray *mop;
if (!(seg && fctx && lpnt)) {
@@ -1115,58 +1201,344 @@
return 1;
}
mop = airMopNew();
- if (limnCBFCtxPrep(fctx, lpnt)) {
+ if (limnCbfCtxPrep(fctx, lpnt)) {
biffAddf(LIMN, "%s: problem with fctx or lpnt", me);
airMopError(mop);
return 1;
}
- spanlen = spanLength(lpnt, loi, hii);
- if (vv0 && tt1 && tt2 && vv3) {
- /* point to the given containers of geometry info */
- v0p = vv0; /* DIM=2 ? */
- t1p = tt1;
- t2p = tt2;
- v3p = vv3;
- } else {
- int oneSided = AIR_TRUE;
- if (vv0 || tt1 || tt2 || vv3) {
- biffAddf(LIMN,
- "%s: should either give all vv0,tt1,tt2,vv3 or none (not %p,%p,%p,%p)",
- me, (const void *)vv0, (const void *)tt1, (const void *)tt2,
- (const void *)vv3);
+ if (vttvCalcOrCopy(vttv, NULL, vv0, tt1, tt2, vv3, fctx, lpnt, loi, hii)) {
+ biffAddf(LIMN, "%s: problem getting vertex or tangent info", me);
+ airMopError(mop);
+ return 1;
+ }
+ /* now actually do the work */
+ fitSingle(alpha, vttv[0], vttv[1], vttv[2], vttv[3], fctx, lpnt, loi, hii);
+
+ /* process the results to generate info in output limnCbfSeg */
+ ELL_2V_COPY(seg->xy + 0, V0);
+ ELL_2V_SCALE_ADD2(seg->xy + 2, 1, V0, alpha[0], T1);
+ ELL_2V_SCALE_ADD2(seg->xy + 4, 1, V3, alpha[1], T2);
+ ELL_2V_COPY(seg->xy + 6, V3);
+ seg->corner[0] = seg->corner[1] = AIR_TRUE; /* misgivings . . . */
+ seg->pointNum = spanLength(lpnt, loi, hii);
+
+ airMopOkay(mop);
+ return 0;
+}
+
+/*
+limnCbfCorners: discover the corners in the given points: i.e. a point where the incoming
+and outgoing tangents are so non-colinear that no attempt is made to fit a spline across
+the point; instead it is an endpoint for different splines on either side of it. This
+sets fctx->ctvt, fctx->cidx, and fctx->cnum.
+
+NOTE: this assumes that limnCbfCtxPrep(fctx, lpnt) was called without error!
+That (via ctxBuffersSet) allocates things that we depend on here.
+*/
+int /* Biff: 1 */
+limnCbfCorners(limnCbfCtx *fctx, const limnCbfPoints *lpnt) {
+ static const char me[] = "limnCbfCorners";
+ airArray *mop;
+ double *angle, /* angle[i] is angle at vertex i */
+ *tvt; /* 6-by-pnum array of tangent,vertex,tangent */
+ int *corny, /* corny[i] means vertex i seems like a corner */
+ oneSided = AIR_TRUE;
+ uint pnum = lpnt->num, loi = 0, hii, cnum, vi;
+
+ if (!(fctx && lpnt)) {
+ biffAddf(LIMN, "%s: got NULL pointer", me);
+ return 1;
+ }
+ /* reset corner-related pointers */
+ fctx->ctvt = airFree(fctx->ctvt);
+ fctx->cidx = airFree(fctx->cidx);
+ fctx->cnum = 0;
+
+ if (!fctx->cornerFind) {
+ /* caller not interested in doing computations to discover corners */
+ if (!(lpnt->isLoop)) {
+ /* but we still have to describe the start and end of the points as "corners" */
+ fctx->cnum = 2;
+ if (!((fctx->ctvt = AIR_CALLOC(6 * fctx->cnum, double))
+ && (fctx->cidx = AIR_CALLOC(fctx->cnum, uint)))) {
+ biffAddf(LIMN, "%s: trouble allocating %u points", me, fctx->cnum);
+ return 1;
+ }
+ hii = pnum - 1;
+ if (limnCbfTVT(fctx->ctvt + 0, fctx->ctvt + 2, fctx->ctvt + 4, /* */
+ fctx, lpnt, loi, hii, 0, oneSided)
+ || limnCbfTVT(fctx->ctvt + 6, fctx->ctvt + 8, fctx->ctvt + 10, /* */
+ fctx, lpnt, loi, hii, hii, oneSided)) {
+ biffAddf(LIMN, "%s: trouble with tangents or vertices for endpoints", me);
+ return 1;
+ }
+ fctx->cidx[0] = loi;
+ fctx->cidx[1] = hii;
+ }
+ return 0;
+ }
+
+ /* else we search for corners */
+ mop = airMopNew();
+ /* allocate arrays and add them to the mop, but bails if any allocation fails */
+ if (!((angle = AIR_CALLOC(pnum, double))
+ && (airMopAdd(mop, angle, airFree, airMopAlways), 1)
+ && (corny = AIR_CALLOC(pnum, int))
+ && (airMopAdd(mop, corny, airFree, airMopAlways), 1)
+ && (tvt = AIR_CALLOC(6 * pnum, double))
+ && (airMopAdd(mop, tvt, airFree, airMopAlways), 1))) {
+ biffAddf(LIMN, "%s: trouble allocating working buffers for %u points", me, pnum);
+ return 1;
+ }
+ for (vi = 0; vi < pnum; vi++) {
+ double *LT = tvt + 0 + 6 * vi;
+ double *VV = tvt + 2 + 6 * vi;
+ double *RT = tvt + 4 + 6 * vi;
+ /* we find TVT for *every* vertex, despite this seeming like computational overkill.
+ Why: we don't know which vertex might be corner until we look at the
+ tangent-to-tangent angles for each vertex, for which we don't need to know the vertex
+ position (non-trivial as long as scale > 0). But once we do know which vertices are
+ corners, we'll need to know where the vertices are, but it would be unfortunate
+ revisit the input data (used previously for tangent estimation) to figure out the
+ corner vertices. In a non-loop, we know first and last points will be corners, but we
+ still need to find the vertex pos and (one-sided) tangent. */
+ if (limnCbfTVT(LT, VV, RT, fctx, lpnt, 0, 0, vi, oneSided)) {
+ biffAddf(LIMN, "%s: trouble with tangents or vertices for point %u/%u", me, vi,
+ pnum);
airMopError(mop);
return 1;
}
- /* do not have geometry info; must find it all */
- if (limnCBFFindTVT(NULL, v0c, t1c, /* */
- fctx, lpnt, loi, hii, 0 /* ofi */, oneSided)
- || limnCBFFindTVT(t2c, v3c, NULL, /* */
- fctx, lpnt, loi, hii, spanlen - 1 /* ofi */, oneSided)) {
- biffAddf(LIMN, "%s: trouble finding geometry info", me);
- airMopError(mop);
+ if (!lpnt->isLoop && (!vi || vi == pnum - 1)) {
+ /* not a loop, and, either at first or last point ==> necessarily a "corner" */
+ corny[vi] = AIR_TRUE;
+ /* for later NMS: set high angle, to allow adjacent point to stay as corner */
+ angle[vi] = 180;
+ } 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->cornerNMS) {
+ 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" */
+ continue;
+ }
+ uint iplus, imnus;
+ iplus = (vi < pnum - 1 ? vi + 1 : (lpnt->isLoop ? 0 : pnum - 1));
+ imnus = (vi ? vi - 1 : (lpnt->isLoop ? pnum - 1 : 0));
+ /* stays a corner only if angle smaller than neighbors */
+ corny[vi] &= (angle[vi] < angle[iplus] && angle[vi] < angle[imnus]);
+ /* HEY handle here if two adjacent corners */
+ }
+ }
+ /* now, corny[vi] iff vert vi really is a corner; so now count # corners */
+ cnum = 0;
+ for (vi = 0; vi < pnum; vi++) {
+ cnum += !!corny[vi];
+ }
+ if (cnum) {
+ unsigned int ci;
+ /* can now allocate new corner-related buffers */
+ if (!((fctx->ctvt = AIR_CALLOC(6 * cnum, double))
+ && (fctx->cidx = AIR_CALLOC(cnum, uint)))) {
+ biffAddf(LIMN, "%s: trouble allocating info for %u corners", me, cnum);
return 1;
}
+ /* now fill in the corner buffers */
+ ci = 0;
+ for (vi = 0; vi < cnum; vi++) {
+ double *id, *od;
+ if (!corny[vi]) continue;
+ fctx->cidx[ci] = vi;
+ id = tvt + 6 * vi;
+ od = fctx->ctvt + 6 * ci;
+ ELL_6V_COPY(od, id);
+ if (fctx->verbose) {
+ printf("%s: corner %u is vertex %u\n T,V,T = (%g,%g) (%g,%g) (%g,%g)\n", me,
+ ci, vi, od[0], od[1], od[2], od[3], od[4], od[5]);
+ }
+ ci++;
+ }
+ }
+
+ airMopOkay(mop);
+ return 0;
+}
+
+/*
+limnCbfMulti: Fits one or more geometrically continuous splines to a set of points.
+Does NOT create new internal "corners" (which break geometric continuity with
+non-colinear incoming and outgoing tangents), but does recursively subdivide the points
+into left and right sides around points with the highest error from fitSingle.
+
+NOTE: this assumes that limnCbfCtxPrep(fctx, lpnt) was called without error!
+That (via ctxBuffersSet) allocates things that we depend on here.
+*/
+int /* Biff: 1 */
+limnCbfMulti(limnCbfPath *path, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], limnCbfCtx *fctx,
+ const limnCbfPoints *lpnt, uint loi, uint hii) {
+ static const char me[] = "limnCbfMulti";
+ double alpha[2], V0[2], T1[2], T2[2], V3[2], *vttv[4] = {V0, T1, T2, V3};
+ int geomGiven;
+
+ if (!(path && fctx ...
[truncated message content] |
|
From: <kin...@us...> - 2024-07-01 16:37:59
|
Revision: 7174
http://sourceforge.net/p/teem/code/7174
Author: kindlmann
Date: 2024-07-01 16:37:53 +0000 (Mon, 01 Jul 2024)
Log Message:
-----------
fixing memory leak
Modified Paths:
--------------
teem/trunk/src/limn/lpu_cbfit.c
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-07-01 16:00:38 UTC (rev 7173)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-07-01 16:37:53 UTC (rev 7174)
@@ -163,9 +163,11 @@
airMopError(mop);
return 1;
}
+ airMopAdd(mop, lpnt, (airMopper)limnCBFPointsNix, airMopAlways);
path = limnCBFPathNew();
airMopAdd(mop, path, (airMopper)limnCBFPathNix, airMopAlways);
fctx = limnCBFCtxNew();
+ airMopAdd(mop, fctx, (airMopper)limnCBFCtxNix, airMopAlways);
fctx->verbose = verbose;
fctx->nrpIterMax = nrpIterMax;
fctx->scale = scale;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-07-01 16:00:39
|
Revision: 7173
http://sourceforge.net/p/teem/code/7173
Author: kindlmann
Date: 2024-07-01 16:00:38 +0000 (Mon, 01 Jul 2024)
Log Message:
-----------
forgot to add this after having added the JSF code
Added Paths:
-----------
teem/trunk/src/air/test/trandJSF.c
Added: teem/trunk/src/air/test/trandJSF.c
===================================================================
--- teem/trunk/src/air/test/trandJSF.c (rev 0)
+++ teem/trunk/src/air/test/trandJSF.c 2024-07-01 16:00:38 UTC (rev 7173)
@@ -0,0 +1,54 @@
+/*
+ Teem: Tools to process and visualize scientific data and images
+ Copyright (C) 2009--2023 University of Chicago
+ Copyright (C) 2005--2008 Gordon Kindlmann
+ Copyright (C) 1998--2004 University of Utah
+
+ This library is free software; you can redistribute it and/or modify it under the terms
+ of the GNU Lesser General Public License (LGPL) as published by the Free Software
+ Foundation; either version 2.1 of the License, or (at your option) any later version.
+ The terms of redistributing and/or modifying this software also include exceptions to
+ the LGPL that facilitate static linking.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ this library; if not, write to Free Software Foundation, Inc., 51 Franklin Street,
+ Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../air.h"
+
+int
+main(int argc, char *argv[]) {
+ char *me, *SS, *NS, *MS;
+ unsigned ii, S, N, M;
+ airJSFRand *jsf;
+
+ me = argv[0];
+ if (4 != argc) {
+ /* 0 1 2 3 (4) */
+ fprintf(stderr, "usage: %s <S> <N> <M>\n", me);
+ exit(1);
+ }
+ SS = argv[1];
+ NS = argv[2];
+ MS = argv[3];
+ if (3 != sscanf(SS, "%u", &S) + sscanf(NS, "%u", &N) + sscanf(MS, "%u", &M)) {
+ fprintf(stderr, "%s: couldn't parse %s,%s,%s as uint S,N,M\n", me, SS, NS, MS);
+ exit(1);
+ }
+ /* to stderr to can better pipe output */
+ fprintf(stderr, "S=%u N=%u M=%u\n", S, N, M);
+ fprintf(stderr, "airJSFRandSanity() = %d\n", airJSFRandSanity());
+
+ jsf = airJSFRandNew(S);
+ for (ii = 0; ii < N; ii++) {
+ printf("%u\n", airJSFRandValMod(jsf, M));
+ }
+ airJSFRandNix(jsf);
+
+ exit(0);
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-30 00:59:41
|
Revision: 7172
http://sourceforge.net/p/teem/code/7172
Author: kindlmann
Date: 2024-06-30 00:59:39 +0000 (Sun, 30 Jun 2024)
Log Message:
-----------
limnCBFitSingle seems to work on tests so far; more to come ...
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-06-28 04:44:18 UTC (rev 7171)
+++ teem/trunk/src/limn/limn.h 2024-06-30 00:59:39 UTC (rev 7172)
@@ -911,12 +911,13 @@
int isLoop);
LIMN_EXPORT limnCBFPoints *limnCBFPointsNix(limnCBFPoints *lpnt);
LIMN_EXPORT int limnCBFPointsCheck(const limnCBFPoints *lpnt);
+LIMN_EXPORT limnCBFPath *limnCBFPathNew(void);
+LIMN_EXPORT limnCBFPath *limnCBFPathNix(limnCBFPath *path);
+LIMN_EXPORT void limnCBFPathJoin(limnCBFPath *dst, const limnCBFPath *src);
LIMN_EXPORT limnCBFCtx *limnCBFCtxNew();
LIMN_EXPORT limnCBFCtx *limnCBFCtxNix(limnCBFCtx *fctx);
LIMN_EXPORT int limnCBFCtxPrep(limnCBFCtx *fctx, const limnCBFPoints *lpnt);
LIMN_EXPORT void limnCBFSegEval(double *xy, const limnCBFSeg *seg, double tt);
-LIMN_EXPORT limnCBFPath *limnCBFPathNew(void);
-LIMN_EXPORT limnCBFPath *limnCBFPathNix(limnCBFPath *path);
LIMN_EXPORT void limnCBFPathSample(double *xy, unsigned int pointNum,
const limnCBFPath *path);
LIMN_EXPORT int limnCBFFindTVT(double lt[2], double vv[2], double rt[2],
@@ -923,19 +924,20 @@
const limnCBFCtx *fctx, const limnCBFPoints *lpnt,
unsigned int loi, unsigned int hii, unsigned int ofi,
int oneSided);
-LIMN_EXPORT int limnCBFCtxInit(const limnCBFCtx *fctx, const limnCBFPoints *lpnt);
LIMN_EXPORT int limnCBFitSingle(limnCBFSeg *seg, const double vv0[2],
const double tt1[2], const double tt2[2],
- const double vv3[2], limnCBFCtx *fctx, const double *xy,
- unsigned int pointNum);
+ const double vv3[2], limnCBFCtx *fctx,
+ limnCBFPoints *lpnt, unsigned int loi, unsigned int hii);
+#if 0
+LIMN_EXPORT int limnCBFCorners(unsigned int **cornIdx, unsigned int *cornNum,
+ limnCBFCtx *fctx, const limnCBFPoints *lpnt);
LIMN_EXPORT int limnCBFMulti(limnCBFPath *path, limnCBFCtx *fctx, const double vv0[2],
const double tt1[2], const double tt2[2],
const double vv3[2], const limnCBFPoints *lpnt,
unsigned int loi, unsigned int hii);
-LIMN_EXPORT int limnCBFCorners(unsigned int **cornIdx, unsigned int *cornNum,
- limnCBFCtx *fctx, const limnCBFPoints *lpnt);
LIMN_EXPORT int limnCBFit(limnCBFPath *path, limnCBFCtx *fctx,
const limnCBFPoints *lpnt);
+#endif
/* lpu{Flotsam,. . .}.c */
#define LIMN_DECLARE(C) LIMN_EXPORT const unrrduCmd limnPu_##C##Cmd;
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-06-28 04:44:18 UTC (rev 7171)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-06-30 00:59:39 UTC (rev 7172)
@@ -1,6 +1,6 @@
/*
Teem: Tools to process and visualize scientific data and images
- Copyright (C) 2009--2023 University of Chicago
+ Copyright (C) 2009--2024 University of Chicago
Copyright (C) 2005--2008 Gordon Kindlmann
Copyright (C) 1998--2004 University of Utah
@@ -36,7 +36,7 @@
Nrrd *_nin, *nin;
double *xy, deltaThresh, psi, cangle, epsilon, nrpIota, time0, dtime, scale, synthPow;
unsigned int ii, synthNum, pNum, nrpIterMax;
- int loop, petc, verbose, tvt[4], fitSingle;
+ int loop, petc, verbose, tvt[4], fitSingleLoHi[2];
char *synthOut;
limnCBFCtx *fctx;
limnCBFPath *path;
@@ -47,10 +47,10 @@
"-i input xy points are actually a loop: the first point logically "
"follows the last point");
hestOptAdd_1_Int(&hopt, "v", "verbose", &verbose, "1", "verbosity level");
- hestOptAdd_1_UInt(&hopt, "sn", "num", &synthNum, "51",
+ hestOptAdd_1_UInt(&hopt, "synthn", "num", &synthNum, "51",
"if saving spline sampling to -so, how many sample.");
hestOptAdd_1_String(
- &hopt, "so", "synth out", &synthOut, "",
+ &hopt, "syntho", "synth out", &synthOut, "",
"if non-empty, input xy points are actually control points for a single spline "
"segment, to be sampled -sn times, and this is the filename into which to save "
"the synthesized xy pts, and then quit without any fitting.");
@@ -75,8 +75,12 @@
hestOptAdd_1_Double(&hopt, "ca", "angle", &cangle, "100", "angle indicating a corner");
hestOptAdd_1_Double(&hopt, "scl", "scale", &scale, "0",
"scale for geometry estimation");
- hestOptAdd_Flag(&hopt, "fs", &fitSingle,
- "just do a single call to limnCBFitSingle and quit");
+ hestOptAdd_2_Int(&hopt, "fs", "loi hii", fitSingleLoHi, "-1 -1",
+ "(if loi is >= 0): just do a single call to limnCBFitSingle and "
+ "quit, using the -i input points, and fitting a spline between "
+ "the loi and hii indices given here. A negative hii will be "
+ "incremented by the number of points, so -1 works to indicate "
+ "the last point.");
hestOptAdd_Flag(&hopt, "petc", &petc, "(Press Enter To Continue) ");
/*
hestOptAdd_1_String(&hopt, NULL, "output", &out, NULL, "output nrrd filename");
@@ -204,10 +208,13 @@
return 0;
}
- if (fitSingle) {
- int ii;
+ if (fitSingleLoHi[0] >= 0) {
limnCBFSeg seg;
- if (limnCBFitSingle(&seg, NULL, NULL, NULL, NULL, fctx, lpnt->pp, lpnt->num)) {
+ int pnum = AIR_INT(lpnt->num);
+ /* re-using the logic from the TVT case above */
+ unsigned int loi = AIR_UINT(AIR_MOD(fitSingleLoHi[0], pnum));
+ unsigned int hii = AIR_UINT(AIR_MOD(fitSingleLoHi[1], pnum));
+ if (limnCBFitSingle(&seg, NULL, NULL, NULL, NULL, fctx, lpnt, loi, hii)) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble doing single segment fit:\n%s", me, err);
airMopError(mop);
@@ -227,6 +234,7 @@
fflush(stderr);
getchar();
}
+#if 0 /* HEY */
if (limnCBFit(path, fctx, lpnt)) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble doing fitting:\n%s", me, err);
@@ -233,6 +241,7 @@
airMopError(mop);
return 1;
}
+#endif
dtime = (airTime() - time0) * 1000;
printf("%s: time= %g ms;iterDone= %u ;deltaDone=%g, distMax=%g (@%u)\n", me, dtime,
fctx->nrpIterDone, fctx->nrpDeltaDone, fctx->distMax, fctx->distMaxIdx);
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-06-28 04:44:18 UTC (rev 7171)
+++ teem/trunk/src/limn/splineFit.c 2024-06-30 00:59:39 UTC (rev 7172)
@@ -158,7 +158,7 @@
return NULL;
}
-static void
+void
limnCBFPathJoin(limnCBFPath *dst, const limnCBFPath *src) {
uint bb = airArrayLenIncr(dst->segArr, src->segNum);
memcpy(dst->seg + bb, src->seg, (src->segNum) * sizeof(limnCBFSeg));
@@ -508,6 +508,15 @@
return;
}
+/* utility function for counting how many vertices are in index span [loi,hii] inclusive.
+It is not our job here to care about lpnt->isLoop; we just assume that if we're faced
+with hii<loi, it must be because of a loop */
+static uint
+spanLength(const limnCBFPoints *lpnt, uint loi, uint hii) {
+ uint topi = hii + (hii < loi) * (lpnt->num);
+ return topi - loi + 1;
+}
+
/* cheesy macro as short-hand to access either pp or ppOwn */
#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
@@ -731,15 +740,6 @@
return 0;
}
-/* utility function for counting how many vertices are in index span [loi,hii] inclusive.
-It is not our job here to care about lpnt->isLoop; we just assume that if we're faced
-with hii<loi, it must be because of a loop */
-static uint
-spanLength(const limnCBFPoints *lpnt, uint loi, uint hii) {
- uint topi = hii + (hii < loi) * (lpnt->num);
- return topi - loi + 1;
-}
-
/* utility function for getting pointer to coords in lpnt, for point with index loi+ofi,
while accounting for possibility of wrapping */
static const double *
@@ -937,12 +937,12 @@
}
/*
-fitSingle: fits a single cubic Bezier spline, w/out error checking (limnCBFSingle is a
-wrapper around this). The given points coordinates are in limnCBFPoints lpnt, between
-low/high indices loi/hii (inclusively); hii can be < loi in the case of a point loop.
-From *given* initial endpoint vv0, initial tangent tt1, final tangent tt2 (pointing
-backwards), and final endpoint vv3, the job of this function is actually just to set
-alpha[0],alpha[1] such that the cubic Bezier spline with control points vv0,
+fitSingle: fits a single cubic Bezier spline, w/out error checking (limnCBFSingle is an
+error-checking wrapper around this). The given points coordinates are in limnCBFPoints
+lpnt, between low/high indices loi/hii (inclusively); hii can be < loi in the case of a
+point loop. From GIVEN initial endpoint vv0, initial tangent tt1, final tangent tt2
+(pointing backwards), and final endpoint vv3, the job of this function is actually just
+to set alpha[0],alpha[1] such that the cubic Bezier spline with control points vv0,
vv0+alpha[0]*tt1, vv3+alpha[1]*tt2, vv3 approximates all the given points.
This is an iterative process, in which alpha is solved for multiples times, after taking
@@ -1018,9 +1018,10 @@
findAlpha(alpha, fctx, vv0, tt1, tt2, vv3, lpnt, loi, hii);
findDist(fctx, alpha, vv0, tt1, tt2, vv3, lpnt, loi, hii);
if (fctx->verbose) {
- printf("%s[%d,%d]: found alpha %g %g, dist %g @ %u (big %d) (%u max nrp iters)\n",
- me, loi, hii, alpha[0], alpha[1], fctx->distMax, fctx->distMaxIdx,
- fctx->distBig, fctx->nrpIterMax);
+ printf(
+ "%s[%d,%d]: found alpha %g %g, maxdist %g @ %u (big %d) (%u max nrp iters)\n",
+ me, loi, hii, alpha[0], alpha[1], fctx->distMax, fctx->distMaxIdx, fctx->distBig,
+ fctx->nrpIterMax);
}
if (fctx->distBig < 3) {
/* initial fit isn't awful; try making it better with nrp */
@@ -1039,8 +1040,8 @@
}
if (delta <= fctx->nrpDeltaThresh) {
if (fctx->verbose) {
- printf("%s[%d,%d]: nrp iter %u delta %g <= min %g --> break\n", me, loi, hii,
- iter, delta, fctx->nrpDeltaThresh);
+ printf("%s[%d,%d]: nrp iter %u delta %g <= thresh %g --> break\n", me, loi,
+ hii, iter, delta, fctx->nrpDeltaThresh);
}
converged = AIR_TRUE;
break;
@@ -1049,11 +1050,14 @@
if (fctx->verbose) {
printf("%s[%d,%d]: nrp done after %u iters: ", me, loi, hii, iter);
if (converged) {
- printf("converged!\n");
+ printf("converged! with maxdist %g @ %u (big %d)\n", fctx->distMax,
+ fctx->distMaxIdx, fctx->distBig);
} else if (!fctx->distBig) {
- printf("nice small dist %g @ %u\n", fctx->distMax, fctx->distMaxIdx);
+ printf("NICE small dist %g (<%g) @ %u\n", fctx->distMax, fctx->epsilon,
+ fctx->distMaxIdx);
} else {
- printf("hit itermax %u\n", fctx->nrpIterMax);
+ printf("hit nrp itermax %u; maxdist %g @ %u (big %d)\n", fctx->nrpIterMax,
+ fctx->distMax, fctx->distMaxIdx, fctx->distBig);
}
}
fctx->nrpIterDone = iter;
@@ -1076,45 +1080,47 @@
/*
limnCBFitSingle
-builds a limnCBFPoints around given xy, determines spline constraints if necessary, and
-calls fitSingle
+Basically and error-checking version of fitSingle; in the limn API because it's needed
+for testing. Unlike fitSingle, the geometry info vv0, tt1, tt2, vv3 can either be punted
+on (by passing NULL for all) or not, by passing specific vectors for all. The results are
+converted into the fields in the given limnCBFSeg *seg. Despite misgivings, we set
+both seg->corner[0,1] to AIR_TRUE.
+
+Perservating on seg->corner[0,1]: we really don't have the information to consistently
+set them with certainty. If not given the geometry vectors, we do assert oneSided when
+estimating the vertices and tangents, so maybe then we can set set->corner[0,1] to true,
+but on the other hand we don't know what to do when the geometry vectors are given. But
+it wouldn't be cool to sometimes set fields in the output struct and sometimes not.
+
+This function used to also be lower-overhead, by not requiring a fctx, and taking a coord
+data pointer instead of a lpnts. But for the sake of testing, there needed to be way of
+passing specific loi and hii in a point loop, so that's what this accepts now. As a
+result, that means this function isn't as convenient a function for one-off single-spline
+fits, and at this point limn doesn't have one. The real entry point for spline fitting
+is limnCBFit().
*/
int /* Biff: 1 */
limnCBFitSingle(limnCBFSeg *seg, const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2], limnCBFCtx *_fctx,
- const double *xy, uint pNum) {
+ const double tt2[2], const double vv3[2], limnCBFCtx *fctx,
+ limnCBFPoints *lpnt, uint loi, uint hii) {
static const char me[] = "limnCBFitSingle";
double v0c[2], t1c[2], t2c[2], v3c[2]; /* locally computed geometry info */
+ const double *v0p, *t1p, *t2p, *v3p; /* pointers for the geometry info */
double alpha[2]; /* recovered by fitting */
- const double *v0p, *t1p, *t2p, *v3p; /* pointers for the geometry info */
- uint loi, hii;
- limnCBFCtx *fctx;
- limnCBFPoints *lpnt;
+ uint spanlen;
airArray *mop;
- if (!(seg && xy && pNum)) {
- biffAddf(LIMN, "%s: got NULL pointer or 0 points", me);
+ if (!(seg && fctx && lpnt)) {
+ biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
mop = airMopNew();
- lpnt = limnCBFPointsNew(xy, nrrdTypeDouble, 2, pNum, AIR_FALSE /* isLoop */);
- airMopAdd(mop, lpnt, (airMopper)limnCBFPointsNix, airMopAlways);
- loi = 0;
- hii = pNum - 1;
- if (_fctx) {
- fctx = _fctx; /* caller has supplied info */
- } else {
- fctx = limnCBFCtxNew();
- airMopAdd(mop, fctx, (airMopper)limnCBFCtxNix, airMopAlways);
- fctx->scale = 0;
- fctx->epsilon = 0.01; /* HEY maybe instead some multiple of stdv? */
- /* (if you don't like these hard-coded defaults then pass your own fctx) */
- }
if (limnCBFCtxPrep(fctx, lpnt)) {
- biffAddf(LIMN, "%s: problem with %s fctx or lpnt", me, _fctx ? "given" : "own");
+ biffAddf(LIMN, "%s: problem with fctx or lpnt", me);
airMopError(mop);
return 1;
}
+ spanlen = spanLength(lpnt, loi, hii);
if (vv0 && tt1 && tt2 && vv3) {
/* point to the given containers of geometry info */
v0p = vv0; /* DIM=2 ? */
@@ -1131,11 +1137,11 @@
airMopError(mop);
return 1;
}
- /* have NO given geometry info; must find it all */
+ /* do not have geometry info; must find it all */
if (limnCBFFindTVT(NULL, v0c, t1c, /* */
fctx, lpnt, loi, hii, 0 /* ofi */, oneSided)
|| limnCBFFindTVT(t2c, v3c, NULL, /* */
- fctx, lpnt, loi, hii, hii /* ofi */, oneSided)) {
+ fctx, lpnt, loi, hii, spanlen - 1 /* ofi */, oneSided)) {
biffAddf(LIMN, "%s: trouble finding geometry info", me);
airMopError(mop);
return 1;
@@ -1149,14 +1155,16 @@
t2p = t2c;
v3p = v3c;
}
+ /* HERE is the call that actually does the work */
fitSingle(alpha, fctx, v0p, t1p, t2p, v3p, lpnt, loi, hii);
+ /* process the results to generate info in output limnCBFSeg */
ELL_2V_COPY(seg->xy + 0, v0p);
ELL_2V_SCALE_ADD2(seg->xy + 2, 1, v0p, alpha[0], t1p);
ELL_2V_SCALE_ADD2(seg->xy + 4, 1, v3p, alpha[1], t2p);
ELL_2V_COPY(seg->xy + 6, v3p);
- /* HEY is it our job to set seg->corner[] ?!? */
- seg->pointNum = pNum;
+ seg->corner[0] = seg->corner[1] = AIR_TRUE; /* misgivings . . . */
+ seg->pointNum = spanlen;
airMopOkay(mop);
return 0;
@@ -1163,91 +1171,6 @@
}
/*
-******** limnCBFit
-**
-** top-level function for fitting cubic beziers to given points
-*/
-int /* Biff: 1 */
-limnCBFit(limnCBFPath *pathWhole, limnCBFCtx *fctx, const limnCBFPoints *lpnt) {
- static const char me[] = "limnCBFit";
- uint *cornIdx, /* (if non-NULL) array of logical indices into PP(lpnt) of corners */
- cornNum, /* length of cornIdx array */
- cii;
- int ret;
- airArray *mop;
-
- if (!(pathWhole && fctx && lpnt)) {
- biffAddf(LIMN, "%s: got NULL pointer", me);
- return 1;
- }
- if (limnCBFCtxPrep(fctx, lpnt)) {
- biffAddf(LIMN, "%s: trouble preparing", me);
- return 1;
- }
- if (fctx->cornerFind) {
- /* HEY RESURRECT ME
- if (limnCBFCorners(&cornIdx, &cornNum, fctx, lpnt)) {
- biffAddf(LIMN, "%s: trouble finding corners", me);
- return 1;
- }
- */
- } else {
- if (lpnt->isLoop) {
- /* there really are no corners */
- cornIdx = NULL;
- cornNum = 0;
- } else {
- /* even without "corners": if not a loop, first and last verts act as corners */
- cornIdx = AIR_CALLOC(2, uint);
- assert(cornIdx);
- cornNum = 2;
- cornIdx[0] = 0;
- cornIdx[1] = lpnt->num - 1;
- }
- }
- mop = airMopNew();
- if (cornIdx) {
- airMopAdd(mop, cornIdx, airFree, airMopAlways);
- }
- airArrayLenSet(pathWhole->segArr, 0);
- if (!cornNum) {
-/* no corners; do everything with one multi call */
-#if 0 /* HEY RESURRECT ME */
- if (limnCBFMulti(pathWhole, fctx, NULL, NULL, NULL, NULL, lpnt, 0 /* loi */,
- 0 /* hii */)) {
- biffAddf(LIMN, "%s: trouble", me);
- airMopError(mop);
- return 1;
- }
-#endif
- } else {
- /* do have corners: split points into segments between corners. The corner vertex is
- both last point in segment I and first point in segment I+1 */
- for (cii = 0; cii < cornNum; cii++) {
- uint cjj = (cii + 1) % cornNum;
- uint loi = cornIdx[cii];
- uint hii = cornIdx[cjj];
- limnCBFPath *subpath = limnCBFPathNew();
-#if 0 /* HEY RESURRECT ME */
- ret = limnCBFMulti(subpath, fctx, NULL, NULL, NULL, NULL, lpnt, loi, hii);
- if (ret) {
- biffAddf(LIMN, "%s: trouble from corners [%u,%u] (points [%u,%u])", me, cii,
- cjj, loi, hii); limnCBFPathNix(subpath); airMopError(mop); return 1;
- }
-#endif
- subpath->seg[0].corner[0] = 1;
- subpath->seg[subpath->segNum - 1].corner[1] = 1;
- limnCBFPathJoin(pathWhole, subpath);
- limnCBFPathNix(subpath);
- }
- }
-
- pathWhole->isLoop = lpnt->isLoop;
- airMopOkay(mop);
- return 0;
-}
-
-/*
TODO:
-- avoid repeated calls to limnCBFCtxPrep
@@ -1267,8 +1190,6 @@
findAlpha generating the simple arc on some but not all iterations (possibly
unstable?)"
-resurrect in segment struct: how many points were represented by a single spline
-
reparm: "HEY TODO: need to make sure that half-way between points,
spline isn't wildly diverging; this can happen with the
spline making a loop away from a small number of points, e.g.:
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-28 04:44:20
|
Revision: 7171
http://sourceforge.net/p/teem/code/7171
Author: kindlmann
Date: 2024-06-28 04:44:18 +0000 (Fri, 28 Jun 2024)
Log Message:
-----------
may have limnCBFitSingle working; added --help support to test/lpu
Modified Paths:
--------------
teem/trunk/src/limn/limn.h
teem/trunk/src/limn/lpu_cbfit.c
teem/trunk/src/limn/lpu_ccfind.c
teem/trunk/src/limn/lpu_meas.c
teem/trunk/src/limn/lpu_psel.c
teem/trunk/src/limn/lpu_rast.c
teem/trunk/src/limn/lpu_sort.c
teem/trunk/src/limn/lpu_verts.c
teem/trunk/src/limn/privateLimn.h
teem/trunk/src/limn/splineFit.c
teem/trunk/src/limn/test/lpu.c
Modified: teem/trunk/src/limn/limn.h
===================================================================
--- teem/trunk/src/limn/limn.h 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/limn.h 2024-06-28 04:44:18 UTC (rev 7171)
@@ -515,6 +515,8 @@
**
** how one cubic Bezier spline segment is represented for limnCBF functions
** (using DIM=2 to mark places where the 2D-ness of the code surfaces )
+**
+** No constructor (New) or destructor (Nix) functions since it is so simple
*/
typedef struct {
double xy[8]; /* four control points of cubic Bezier:
@@ -524,6 +526,8 @@
or path-ending vertices, i.e. reasons to not have geometric
continuity here, either because we intend to have a corner,
or because there's nothing else to be continuous with */
+ /* how many points does this represent */
+ unsigned int pointNum;
} limnCBFSeg;
/*
@@ -920,10 +924,10 @@
unsigned int loi, unsigned int hii, unsigned int ofi,
int oneSided);
LIMN_EXPORT int limnCBFCtxInit(const limnCBFCtx *fctx, const limnCBFPoints *lpnt);
-LIMN_EXPORT int limnCBFitSingle(double alpha[2], limnCBFCtx *fctx, /* */
- const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2],
- const double *xy, unsigned int pointNum);
+LIMN_EXPORT int limnCBFitSingle(limnCBFSeg *seg, const double vv0[2],
+ const double tt1[2], const double tt2[2],
+ const double vv3[2], limnCBFCtx *fctx, const double *xy,
+ unsigned int pointNum);
LIMN_EXPORT int limnCBFMulti(limnCBFPath *path, limnCBFCtx *fctx, const double vv0[2],
const double tt1[2], const double tt2[2],
const double vv3[2], const limnCBFPoints *lpnt,
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -34,9 +34,9 @@
int pret;
Nrrd *_nin, *nin;
- double *xy, deltaThresh, psi, cangle, epsilon, nrpIota, time0, dtime, scale, supow;
- unsigned int ii, pNum, iterMax;
- int loop, petc, verbose, tvt[4];
+ double *xy, deltaThresh, psi, cangle, epsilon, nrpIota, time0, dtime, scale, synthPow;
+ unsigned int ii, synthNum, pNum, nrpIterMax;
+ int loop, petc, verbose, tvt[4], fitSingle;
char *synthOut;
limnCBFCtx *fctx;
limnCBFPath *path;
@@ -43,11 +43,18 @@
limnCBFPoints *lpnt;
hestOptAdd_1_Other(&hopt, "i", "input", &_nin, NULL, "input xy points", nrrdHestNrrd);
+ hestOptAdd_Flag(&hopt, "loop", &loop,
+ "-i input xy points are actually a loop: the first point logically "
+ "follows the last point");
hestOptAdd_1_Int(&hopt, "v", "verbose", &verbose, "1", "verbosity level");
- hestOptAdd_1_String(&hopt, "so", "synth out", &synthOut, "",
- "if non-empty, filename in which to save synthesized xy pts, "
- "and then quit before any fitting.");
- hestOptAdd_1_Double(&hopt, "sup", "expo", &supow, "1",
+ hestOptAdd_1_UInt(&hopt, "sn", "num", &synthNum, "51",
+ "if saving spline sampling to -so, how many sample.");
+ hestOptAdd_1_String(
+ &hopt, "so", "synth out", &synthOut, "",
+ "if non-empty, input xy points are actually control points for a single spline "
+ "segment, to be sampled -sn times, and this is the filename into which to save "
+ "the synthesized xy pts, and then quit without any fitting.");
+ hestOptAdd_1_Double(&hopt, "sup", "expo", &synthPow, "1",
"when synthesizing data on a single segment, warp U parameters "
"by raising to this power.");
hestOptAdd_4_Int(&hopt, "tvt", "loi hii absi 1s", tvt, "0 0 0 -1",
@@ -54,8 +61,8 @@
"if last value is >= 0: make single call to limnCBFFindTVT and quit, "
"but note that the given loi,hii,absi args are not exactly what is "
"passed to limnCBFFindTVT");
- hestOptAdd_1_UInt(&hopt, "im", "max", &iterMax, "0",
- "(if non-zero) max # nrp iterations to run");
+ hestOptAdd_1_UInt(&hopt, "im", "max", &nrpIterMax, "12",
+ "max # nrp iterations to run");
hestOptAdd_1_Double(&hopt, "deltathr", "delta", &deltaThresh, "0.0005",
"(if non-zero) stop nrp when change in spline "
"domain sampling goes below this");
@@ -68,9 +75,8 @@
hestOptAdd_1_Double(&hopt, "ca", "angle", &cangle, "100", "angle indicating a corner");
hestOptAdd_1_Double(&hopt, "scl", "scale", &scale, "0",
"scale for geometry estimation");
- hestOptAdd_Flag(&hopt, "loop", &loop,
- "given xy points are actually a loop: the first point logically "
- "follows the last point");
+ hestOptAdd_Flag(&hopt, "fs", &fitSingle,
+ "just do a single call to limnCBFitSingle and quit");
hestOptAdd_Flag(&hopt, "petc", &petc, "(Press Enter To Continue) ");
/*
hestOptAdd_1_String(&hopt, NULL, "output", &out, NULL, "output nrrd filename");
@@ -80,7 +86,7 @@
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
USAGE(myinfo);
- PARSE();
+ PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
if (!(2 == _nin->dim && 2 == _nin->axis[0].size)) {
@@ -91,8 +97,8 @@
airMopError(mop);
return 1;
}
- if (airStrlen(synthOut) && 6 != _nin->axis[1].size) {
- fprintf(stderr, "%s: need 2-by-6 array (not 2-by-%u) for synthetic xy\n", me,
+ if (airStrlen(synthOut) && 4 != _nin->axis[1].size) {
+ fprintf(stderr, "%s: need 2-by-4 array (not 2-by-%u) for synthetic xy\n", me,
(unsigned int)_nin->axis[1].size);
airMopError(mop);
return 1;
@@ -107,54 +113,46 @@
return 1;
}
- if (!airStrlen(synthOut)) {
- xy = (double *)nin->data;
- pNum = (unsigned int)nin->axis[1].size;
- } else {
- double alpha[2], vv0[2], tt1[2], tt2[2], vv3[2];
+ if (airStrlen(synthOut)) {
/* synthesize data from control points */
double *cpt = (double *)nin->data;
limnCBFSeg seg;
- pNum = (unsigned int)cpt[1];
- if (!(0 == cpt[0] && pNum == cpt[1])) {
- fprintf(stderr, "%s: need 0,int for first 2 cpt values (not %g,%g)\n", me, cpt[0],
- cpt[1]);
+ int ci;
+ Nrrd *nsyn;
+ if (!(synthNum >= 3)) {
+ fprintf(stderr, "%s: for data synthesis need at least 3 samples (not %u)\n", me,
+ synthNum);
airMopError(mop);
return 1;
}
- ELL_2V_COPY(alpha, cpt + 2);
- ELL_2V_COPY(vv0, cpt + 4);
- ELL_2V_COPY(seg.xy + 0, vv0);
- ELL_2V_COPY(tt1, cpt + 6);
- ELL_2V_COPY(tt2, cpt + 8);
- ELL_2V_COPY(vv3, cpt + 10);
- ELL_2V_COPY(seg.xy + 6, vv3);
- ELL_2V_SCALE_ADD2(seg.xy + 2, 1, vv0, alpha[0], tt1);
- ELL_2V_SCALE_ADD2(seg.xy + 4, 1, vv3, alpha[1], tt2);
+ for (ci = 0; ci < 4; ci++) {
+ ELL_2V_COPY(seg.xy + 2 * ci, cpt + 2 * ci);
+ }
printf("%s: synth seg: (%g,%g) -- (%g,%g) -- (%g,%g) -- (%g,%g)\n", me, seg.xy[0],
seg.xy[1], seg.xy[2], seg.xy[3], seg.xy[4], seg.xy[5], seg.xy[6], seg.xy[7]);
- xy = AIR_MALLOC(2 * pNum, double);
+ xy = AIR_MALLOC(2 * synthNum, double);
airMopAdd(mop, xy, airFree, airMopAlways);
- for (ii = 0; ii < pNum; ii++) {
- double uu = AIR_AFFINE(0, ii, pNum - 1, 0, 1);
- uu = pow(uu, supow);
+ for (ii = 0; ii < synthNum; ii++) {
+ double uu = AIR_AFFINE(0, ii, synthNum - 1, 0, 1);
+ uu = pow(uu, synthPow);
limnCBFSegEval(xy + 2 * ii, &seg, uu);
}
- if (airStrlen(synthOut)) {
- Nrrd *nsyn = nrrdNew();
- airMopAdd(mop, nsyn, (airMopper)nrrdNix, airMopAlways);
- if (nrrdWrap_va(nsyn, xy, nrrdTypeDouble, 2, (size_t)2, (size_t)pNum)
- || nrrdSave(synthOut, nsyn, NULL)) {
- airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
- fprintf(stderr, "%s: trouble saving synthetic data:\n%s", me, err);
- airMopError(mop);
- return 1;
- }
- printf("%s: saved synthetic output to %s; bye\n", me, synthOut);
- airMopOkay(mop);
- return 0;
+ nsyn = nrrdNew();
+ airMopAdd(mop, nsyn, (airMopper)nrrdNix, airMopAlways);
+ if (nrrdWrap_va(nsyn, xy, nrrdTypeDouble, 2, (size_t)2, (size_t)synthNum)
+ || nrrdSave(synthOut, nsyn, NULL)) {
+ airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
+ fprintf(stderr, "%s: trouble saving synthetic data:\n%s", me, err);
+ airMopError(mop);
+ return 1;
}
+ printf("%s: saved synthetic output to %s; bye\n", me, synthOut);
+ airMopOkay(mop);
+ return 0;
}
+
+ xy = (double *)nin->data;
+ pNum = (unsigned int)nin->axis[1].size;
if (!(lpnt = limnCBFPointsNew(xy, nrrdTypeDouble, 2, pNum, loop))) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble setting up points:\n%s", me, err);
@@ -165,7 +163,7 @@
airMopAdd(mop, path, (airMopper)limnCBFPathNix, airMopAlways);
fctx = limnCBFCtxNew();
fctx->verbose = verbose;
- fctx->nrpIterMax = iterMax;
+ fctx->nrpIterMax = nrpIterMax;
fctx->scale = scale;
fctx->epsilon = epsilon;
fctx->nrpDeltaThresh = deltaThresh;
@@ -172,7 +170,8 @@
fctx->nrpIota = nrpIota;
fctx->nrpPsi = psi;
fctx->cornAngle = cangle;
- if (tvt[3] >= 0) {
+
+ if (tvt[3] >= 0) { /* here just to call limnCBFFindTVT once */
double lt[2], vv[2], rt[2];
int pnum = AIR_INT(lpnt->num);
/* whoa - this is how GLK learned that AIR_MOD is garbage if args differ in
@@ -191,7 +190,7 @@
if (!E) E |= limnCBFFindTVT(lt, vv, rt, fctx, lpnt, loi, hii, ofi, oneSided);
if (E) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
- fprintf(stderr, "%s: trouble doing single tangent-vertex-tangent:\n%s", me, err);
+ fprintf(stderr, "%s: trouble doing lone tangent-vertex-tangent:\n%s", me, err);
airMopError(mop);
return 1;
}
@@ -204,6 +203,24 @@
airMopOkay(mop);
return 0;
}
+
+ if (fitSingle) {
+ int ii;
+ limnCBFSeg seg;
+ if (limnCBFitSingle(&seg, NULL, NULL, NULL, NULL, fctx, lpnt->pp, lpnt->num)) {
+ airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
+ fprintf(stderr, "%s: trouble doing single segment fit:\n%s", me, err);
+ airMopError(mop);
+ return 1;
+ }
+ printf("%s: limnCBFitSingle results:\n", me);
+ for (ii = 0; ii < 4; ii++) {
+ printf("%g %g\n", seg.xy[0 + 2 * ii], seg.xy[1 + 2 * ii]);
+ }
+ airMopOkay(mop);
+ return 0;
+ }
+
time0 = airTime();
if (petc) {
fprintf(stderr, "%s: Press Enter to Continue ... ", me);
Modified: teem/trunk/src/limn/lpu_ccfind.c
===================================================================
--- teem/trunk/src/limn/lpu_ccfind.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/lpu_ccfind.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -44,7 +44,7 @@
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
USAGE(myinfo);
- PARSE();
+ PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
nmeas = nrrdNew();
Modified: teem/trunk/src/limn/lpu_meas.c
===================================================================
--- teem/trunk/src/limn/lpu_meas.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/lpu_meas.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -45,7 +45,7 @@
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
USAGE(myinfo);
- PARSE();
+ PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
nout = nrrdNew();
Modified: teem/trunk/src/limn/lpu_psel.c
===================================================================
--- teem/trunk/src/limn/lpu_psel.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/lpu_psel.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -50,7 +50,7 @@
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
USAGE(myinfo);
- PARSE();
+ PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
if (!(prange[0] <= pldIn->primNum - 1 && prange[1] <= pldIn->primNum - 1)) {
Modified: teem/trunk/src/limn/lpu_rast.c
===================================================================
--- teem/trunk/src/limn/lpu_rast.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/lpu_rast.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -52,7 +52,7 @@
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
USAGE(myinfo);
- PARSE();
+ PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
nout = nrrdNew();
Modified: teem/trunk/src/limn/lpu_sort.c
===================================================================
--- teem/trunk/src/limn/lpu_sort.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/lpu_sort.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -46,7 +46,7 @@
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
USAGE(myinfo);
- PARSE();
+ PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
if (limnPolyDataPrimitiveSort(pld, nin)) {
Modified: teem/trunk/src/limn/lpu_verts.c
===================================================================
--- teem/trunk/src/limn/lpu_verts.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/lpu_verts.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -44,7 +44,7 @@
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
USAGE(myinfo);
- PARSE();
+ PARSE(myinfo);
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
nout = nrrdNew();
Modified: teem/trunk/src/limn/privateLimn.h
===================================================================
--- teem/trunk/src/limn/privateLimn.h 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/privateLimn.h 2024-06-28 04:44:18 UTC (rev 7171)
@@ -34,7 +34,7 @@
return 2; \
}
-#define PARSE() \
+#define PARSE(INFO) \
if ((pret = hestParse(hopt, argc, argv, &perr, hparm))) { \
if (1 == pret) { \
fprintf(stderr, "%s: %s\n", me, perr); \
@@ -45,6 +45,11 @@
} else { \
exit(1); \
} \
+ } else if (hopt->helpWanted) { \
+ hestInfo(stdout, me, (INFO), hparm); \
+ hestUsage(stdout, hopt, me, hparm); \
+ hestGlossary(stdout, hopt, hparm); \
+ return 0; \
}
#ifdef __cplusplus
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/splineFit.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -43,12 +43,12 @@
#define PNMIN(ISLOOP) ((ISLOOP) ? 4 : 3)
/*
-******** limnCBFPointsNew
-**
-** create a point data container, possibly around given pdata pointer. In an aspirational
-** hope of API stability, this is one of the few functions for which the interface itself
-** does not expose the specificity to DIM=2 and type double (though the code inside
-** does (apologetically) enforce that).
+limnCBFPointsNew
+
+create a point data container, possibly around given pdata pointer. In an aspirational
+hope of API stability, this is one of the few functions for which the interface itself
+does not expose the specificity to DIM=2 and type double (though the code inside
+does (apologetically) enforce that).
*/
limnCBFPoints * /* Biff: NULL */
limnCBFPointsNew(const void *pdata, int ptype, uint dim, uint pnum, int isLoop) {
@@ -132,6 +132,7 @@
ELL_2V_NAN_SET(seg->xy + 4);
ELL_2V_NAN_SET(seg->xy + 6);
seg->corner[0] = seg->corner[1] = AIR_FALSE;
+ seg->pointNum = 0;
return;
}
@@ -173,7 +174,7 @@
fctx->verbose = 0;
fctx->cornerFind = AIR_TRUE;
fctx->cornerNMS = AIR_TRUE;
- fctx->nrpIterMax = 10;
+ fctx->nrpIterMax = 12;
fctx->epsilon = 0; /* will need to be set to something valid elsewhere */
fctx->scale = 0; /* will need to be set to something valid elsewhere */
fctx->nrpCap = 3.0;
@@ -222,10 +223,9 @@
}
/*
-** ctxBuffersSet: ensures that some buffers in fctx: uu, vw, tw
-** are set up for current #points pNum and measurement scale fctx->scale.
-** The buffers are re-allocated only when necessary.
-** Does NOT touch the corner-related buffers: cpp, clt, crt
+ctxBuffersSet: ensures that some buffers in fctx: uu, vw, tw are set up for current
+#points pNum and measurement scale fctx->scale. The buffers are re-allocated only when
+necessary. Does NOT touch the corner-related buffers: cpp, clt, crt
*/
static int /* Biff: 1 */
ctxBuffersSet(limnCBFCtx *fctx, uint pNum) {
@@ -355,10 +355,10 @@
}
/*
-******** limnCBFCtxPrep
-**
-** checks the things that are going to be passed around a lot,
-** and makes call to initialize buffers inside fctx
+limnCBFCtxPrep
+
+checks the things that are going to be passed around a lot, and makes call to initialize
+buffers inside fctx
*/
int /* Biff: 1 */
limnCBFCtxPrep(limnCBFCtx *fctx, const limnCBFPoints *lpnt) {
@@ -372,6 +372,10 @@
biffAddf(LIMN, "%s: problem with points", me);
return 1;
}
+ if (!(fctx->nrpIterMax >= 1)) {
+ biffAddf(LIMN, "%s: need at least 1 nrp iteration (not %u)", me, fctx->nrpIterMax);
+ return 1;
+ }
if (!(fctx->epsilon > 0)) {
biffAddf(LIMN, "%s: need positive epsilon (not %g)", me, fctx->epsilon);
return 1;
@@ -459,9 +463,9 @@
#define CBD2(P, V0, V1, V2, V3, T, W) CBDI(P, VCBD2, V0, V1, V2, V3, T, W)
/*
-******** limnCBFSegEval
-**
-** evaluates a single limnCBFSeg at one point tt in [0.0,1.0]
+limnCBFSegEval
+
+evaluates a single limnCBFSeg at one point tt in [0.0,1.0]
*/
void
limnCBFSegEval(double *vv, const limnCBFSeg *seg, double tt) {
@@ -481,10 +485,10 @@
}
/*
-******** limnCBFPathSample
-**
-** evaluates limnCBFPath at pNum locations, uniformly (and very naively)
-** distributed among the path segments, and saves into (pre-allocated) xy
+limnCBFPathSample
+
+evaluates limnCBFPath at pNum locations, uniformly (and very naively) distributed among
+the path segments, and saves into (pre-allocated) xy
*/
void
limnCBFPathSample(double *xy, uint pNum, const limnCBFPath *path) {
@@ -507,7 +511,7 @@
/* cheesy macro as short-hand to access either pp or ppOwn */
#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
-/* error-checked index processing for limnCBFFindVT and maybe others */
+/* error-checked index processing for limnCBFFindTVT and maybe others */
static int /* Biff: 1 */
idxPrep(int *sloP, int *shiP, int *loopyP, const limnCBFCtx *fctx,
const limnCBFPoints *lpnt, uint loi, uint hii) {
@@ -727,7 +731,438 @@
return 0;
}
+/* utility function for counting how many vertices are in index span [loi,hii] inclusive.
+It is not our job here to care about lpnt->isLoop; we just assume that if we're faced
+with hii<loi, it must be because of a loop */
+static uint
+spanLength(const limnCBFPoints *lpnt, uint loi, uint hii) {
+ uint topi = hii + (hii < loi) * (lpnt->num);
+ return topi - loi + 1;
+}
+
+/* utility function for getting pointer to coords in lpnt, for point with index loi+ofi,
+while accounting for possibility of wrapping */
+static const double *
+pointPos(const limnCBFPoints *lpnt, uint loi, uint ofi) {
+ uint ii = (loi + ofi) % lpnt->num;
+ return PP(lpnt) + 2 * ii; /* DIM=2 */
+}
+
/*
+(from paper page 620) solves for the alpha that minimize squared error between xy[i]
+and Q(uu[i]) where Q(t) is cubic Bezier spline through vv0, vv0 + alpha[0]*tt1,
+vv3 + alpha[1]*tt2, and vv3.
+
+There are various conditions where the generated spline ignores the xy array and
+instead is what one could call a "simple arc" (with control points at 1/3 and 2/3 the
+distance between the end points):
+ - having only two points (xy contains only the end points)
+ - the determinant of the 2x2 matrix that is inverted to solve
+ for alpha is too close to zero (this test was not part of the
+ author's code)
+ - the solved alphas are not convincingly positive
+
+This function is the only place where the "simple arc" is generated, and generating the
+simple arc is not actually an error or problem: if it is bad at fitting the data (as
+determined by findDist) then it may be subdivided, and that's ok. What GLK hasn't thought
+through is: what is the interaction of nrp iterations and findAlpha generating the simple
+arc on some but not all iterations (possibly unstable?)
+*/
+static void
+findAlpha(double alpha[2], limnCBFCtx *fctx, /* must be non-NULL */
+ const double vv0[2], const double tt1[2], const double tt2[2],
+ const double vv3[2], const limnCBFPoints *lpnt, uint loi, uint hii) {
+ static const char me[] = "findAlpha";
+ uint ii, spanlen;
+ double det, F2L[2], lenF2L;
+ const double *xy = PP(lpnt);
+
+ ELL_2V_SUB(F2L, xy + 2 * hii, xy + 2 * loi); /* DIM=2 throughout this */
+ lenF2L = ELL_2V_LEN(F2L);
+ spanlen = spanLength(lpnt, loi, hii);
+ if (spanlen > 2) {
+ double xx[2], m11, m12, m22, MM[4], MI[4];
+ const double *uu = fctx->uu;
+ xx[0] = xx[1] = m11 = m12 = m22 = 0;
+ for (ii = 0; ii < spanlen; ii++) {
+ const double *xy;
+ double bb[4], Ai1[2], Ai2[2], Pi[2], dmP[2];
+ double ui = uu[ii];
+ VCBD0(bb, ui);
+ ELL_2V_SCALE(Ai1, bb[1], tt1);
+ ELL_2V_SCALE(Ai2, bb[2], tt2);
+ /* GLK using "m" and "M" instead of author's "C". Note that Ai1 and Ai2 are
+ scalings of (nominally) unit-length tt1 and tt2 by evaluations of the spline
+ basis functions, so they (and the M computed from them, and det(M)), are
+ invariant w.r.t over-all rescalings of the data points */
+ m11 += ELL_2V_DOT(Ai1, Ai1);
+ m12 += ELL_2V_DOT(Ai1, Ai2);
+ m22 += ELL_2V_DOT(Ai2, Ai2);
+ ELL_2V_SCALE_ADD2(Pi, bb[0] + bb[1], vv0, bb[2] + bb[3], vv3);
+ xy = pointPos(lpnt, loi, ii);
+ ELL_2V_SUB(dmP, xy, Pi);
+ xx[0] += ELL_2V_DOT(dmP, Ai1);
+ xx[1] += ELL_2V_DOT(dmP, Ai2);
+ }
+ ELL_4V_SET(MM, m11, m12, m12, m22);
+ ELL_2M_INV(MI, MM, det);
+ ELL_2MV_MUL(alpha, MI, xx);
+ } else { /* pNum <= 2 */
+ det = 1; /* bogus but harmless */
+ alpha[0] = alpha[1] = 0; /* trigger simple arc code */
+ }
+ /* test if we should return simple arc */
+ if (!(AIR_EXISTS(det) && AIR_ABS(det) > fctx->detMin
+ && alpha[0] > lenF2L * (fctx->alphaMin)
+ && alpha[1] > lenF2L * (fctx->alphaMin))) {
+ if (fctx->verbose) {
+ if (spanlen > 2) {
+ printf("%s: bad |det| %g (vs %g) or alpha %g,%g (vs %g*%g) "
+ "--> simple arc\n",
+ me, AIR_ABS(det), fctx->detMin, alpha[0], alpha[1], lenF2L,
+ fctx->alphaMin);
+ } else {
+ printf("%s: [%u,%u] spanlen %u tiny --> simple arc\n", me, loi, hii, spanlen);
+ }
+ }
+ /* generate simple arc: set both alphas to 1/3 of distance from
+ first to last point, but also handle non-unit-length tt1 and
+ tt2 */
+ alpha[0] = lenF2L / (3 * ELL_2V_LEN(tt1));
+ alpha[1] = lenF2L / (3 * ELL_2V_LEN(tt2));
+ } else {
+ if (fctx->verbose > 1) {
+ printf("%s: all good: det %g, alpha %g,%g\n", me, det, alpha[0], alpha[1]);
+ }
+ }
+ fctx->alphaDet = det;
+ return;
+}
+
+/*
+using Newton iterations to try to find a better places at which to evaluate the spline in
+order to match the given points xy
+*/
+static double
+reparm(const limnCBFCtx *fctx, /* must be non-NULL */
+ const double alpha[2], const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], const limnCBFPoints *lpnt, uint loi,
+ uint hii) {
+ static const char me[] = "reparm";
+ uint ii, spanlen;
+ double vv1[2], vv2[2], delta, maxdelu;
+ double *uu = fctx->uu;
+
+ spanlen = spanLength(lpnt, loi, hii);
+ assert(spanlen >= 3);
+ /* average interior u[i+1]-u[i] is 1/(spanlen-2) */
+ maxdelu = fctx->nrpCap / (spanlen - 2);
+ ELL_2V_SCALE_ADD2(vv1, 1, vv0, alpha[0], tt1);
+ ELL_2V_SCALE_ADD2(vv2, 1, vv3, alpha[1], tt2);
+ delta = 0;
+ /* only changing parameterization of interior points,
+ not the first (ii=0) or last (ii=pNum-1) */
+ for (ii = 1; ii < spanlen - 1; ii++) {
+ double numer, denom, delu, df[2], ww[4], tt, Q[2], QD[2], QDD[2];
+ const double *xy;
+ tt = uu[ii];
+ CBD0(Q, vv0, vv1, vv2, vv3, tt, ww);
+ CBD1(QD, vv0, vv1, vv2, vv3, tt, ww);
+ CBD2(QDD, vv0, vv1, vv2, vv3, tt, ww);
+ xy = pointPos(lpnt, loi, ii);
+ ELL_2V_SUB(df, Q, xy);
+ numer = ELL_2V_DOT(df, QD);
+ denom = ELL_2V_DOT(QD, QD) + ELL_2V_DOT(df, QDD);
+ delu = numer / denom;
+ if (AIR_ABS(delu) > maxdelu) {
+ /* cap Newton step */
+ delu = maxdelu * airSgn(delu);
+ }
+ uu[ii] = tt - delu;
+ delta += AIR_ABS(delu);
+ if (fctx->verbose > 1) {
+ printf("%s[%2u]: %g <-- %g - %g\n", me, ii, uu[ii], tt, delu);
+ }
+ }
+ delta /= spanlen - 2; /* number of interior points */
+ /* HEY TODO: need to make sure that half-way between points,
+ spline isn't wildly diverging; this can happen with the
+ spline making a loop away from a small number of points, e.g.:
+ 4 points spline defined by vv0 = (1,1), tt1 = (1,2),
+ tt2 = (1,2), vv3 = (0,1) */
+ return delta;
+}
+
+/* (assuming current parameterization in fctx->uu) sets fctx->distMax to max distance
+ to spline, at point fctx->distMaxIdx, and then sets fctx->distBig accordingly */
+static void
+findDist(limnCBFCtx *fctx, const double alpha[2], const double vv0[2],
+ const double tt1[2], const double tt2[2], const double vv3[2],
+ const limnCBFPoints *lpnt, uint loi, uint hii) {
+ uint ii, distMaxIdx, spanlen;
+ double vv1[2], vv2[2], distMax;
+ const double *uu = fctx->uu;
+
+ spanlen = spanLength(lpnt, loi, hii);
+ assert(spanlen >= 3);
+ ELL_2V_SCALE_ADD2(vv1, 1, vv0, alpha[0], tt1); /* DIM=2 everywhere here */
+ ELL_2V_SCALE_ADD2(vv2, 1, vv3, alpha[1], tt2);
+ distMax = AIR_NAN;
+ /* NOTE that the first and last points are actually not part of the max distance
+ calculation, which motivates ensuring that the endpoints generated by limnCBFFindTVT
+ are actually sufficiently close to the first and last points (or else the fit spline
+ won't meet the expected accuracy threshold) */
+ for (ii = 1; ii < spanlen - 1; ii++) {
+ double len, Q[2], df[2], ww[4];
+ const double *xy;
+ CBD0(Q, vv0, vv1, vv2, vv3, uu[ii], ww);
+ xy = pointPos(lpnt, loi, ii);
+ ELL_2V_SUB(df, Q, xy);
+ len = ELL_2V_LEN(df);
+ if (!AIR_EXISTS(distMax) || len > distMax) {
+ distMax = len;
+ distMaxIdx = ii;
+ }
+ }
+ fctx->distMax = distMax;
+ fctx->distMaxIdx = distMaxIdx;
+ fctx->distBig = (distMax <= fctx->nrpIota * fctx->epsilon
+ ? 0
+ : (distMax <= fctx->epsilon /* */
+ ? 1
+ : (distMax <= fctx->nrpPsi * fctx->epsilon /* */
+ ? 2
+ : 3)));
+ return;
+}
+
+/*
+fitSingle: fits a single cubic Bezier spline, w/out error checking (limnCBFSingle is a
+wrapper around this). The given points coordinates are in limnCBFPoints lpnt, between
+low/high indices loi/hii (inclusively); hii can be < loi in the case of a point loop.
+From *given* initial endpoint vv0, initial tangent tt1, final tangent tt2 (pointing
+backwards), and final endpoint vv3, the job of this function is actually just to set
+alpha[0],alpha[1] such that the cubic Bezier spline with control points vv0,
+vv0+alpha[0]*tt1, vv3+alpha[1]*tt2, vv3 approximates all the given points.
+
+This is an iterative process, in which alpha is solved for multiples times, after taking
+a Newton step to try to optimize the parameterization of the points (in the
+already-allocated fctx->uu array); we calls this process "nrp" for Newton
+Re-Parameterization. nrp iterations are stopped after any one of following is true (the
+original published method did not have these fine-grained controls):
+ - have done nrpIterMax iterations of nrp, or,
+ - distance from spline (as evaluated at the current parameterization) to the given
+ points falls below fctx->nrpIota * fctx->epsilon, or,
+ - parameterization change falls below fctx->nrpDeltaThresh
+Information about the results of this process are set in the given fctx.
+*/
+static void
+fitSingle(double alpha[2], limnCBFCtx *fctx, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], const limnCBFPoints *lpnt, uint loi,
+ uint hii) {
+ static const char me[] = "fitSingle";
+ uint iter, pNum;
+
+ /* DIM=2 pretty much everywhere here */
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: hello, vv0=(%g,%g), tt1=(%g,%g), "
+ "tt2=(%g,%g), vv3=(%g,%g)\n",
+ me, loi, hii, vv0[0], vv0[1], tt1[0], tt1[1], tt2[0], tt2[1], vv3[0], vv3[1]);
+ }
+ pNum = spanLength(lpnt, loi, hii);
+ if (2 == pNum) {
+ /* relying on code in findAlpha() that handles pNum==2 */
+ findAlpha(alpha, fctx, vv0, tt1, tt2, vv3, lpnt, loi, hii);
+ /* nrp is moot */
+ fctx->nrpIterDone = 0;
+ /* emmulate results of calling findDist() */
+ fctx->distMax = fctx->nrpDeltaDone = 0;
+ fctx->distMaxIdx = 0;
+ fctx->distBig = 0;
+ } else { /* pNum >= 3 */
+ double delta; /* avg parameterization change of interior points */
+ {
+ /* initialize uu parameterization to chord length */
+ unsigned int ii;
+ double len;
+ const double *xyP, *xyM;
+ fctx->uu[0] = len = 0;
+ xyP = pointPos(lpnt, loi, 1);
+ xyM = pointPos(lpnt, loi, 0);
+ for (ii = 1; ii < pNum; ii++) {
+ double dd[2];
+ ELL_2V_SUB(dd, xyP, xyM);
+ len += ELL_2V_LEN(dd);
+ fctx->uu[ii] = len;
+ xyM = xyP;
+ xyP = pointPos(lpnt, loi, ii + 1);
+ }
+ delta = 0;
+ for (ii = 0; ii < pNum; ii++) {
+ if (ii < pNum - 1) {
+ fctx->uu[ii] /= len;
+ delta += fctx->uu[ii];
+ } else {
+ /* ii == pNum-1 last vertex */
+ fctx->uu[ii] = 1;
+ }
+ if (fctx->verbose > 1) {
+ printf("%s[%d,%d]: intial uu[%u] = %g\n", me, loi, hii, ii, fctx->uu[ii]);
+ }
+ }
+ delta /= pNum - 2; /* within the pNum verts are pNum-2 interior verts */
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: initial (chord length) delta = %g\n", me, loi, hii, delta);
+ }
+ }
+ findAlpha(alpha, fctx, vv0, tt1, tt2, vv3, lpnt, loi, hii);
+ findDist(fctx, alpha, vv0, tt1, tt2, vv3, lpnt, loi, hii);
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: found alpha %g %g, dist %g @ %u (big %d) (%u max nrp iters)\n",
+ me, loi, hii, alpha[0], alpha[1], fctx->distMax, fctx->distMaxIdx,
+ fctx->distBig, fctx->nrpIterMax);
+ }
+ if (fctx->distBig < 3) {
+ /* initial fit isn't awful; try making it better with nrp */
+ int converged = AIR_FALSE;
+ for (iter = 0; fctx->distBig && iter < fctx->nrpIterMax; iter++) {
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: nrp iter %u starting with alpha %g,%g (det %g) (big %d)\n",
+ me, loi, hii, iter, alpha[0], alpha[1], fctx->alphaDet, fctx->distBig);
+ }
+ delta = reparm(fctx, alpha, vv0, tt1, tt2, vv3, lpnt, loi, hii);
+ findAlpha(alpha, fctx, vv0, tt1, tt2, vv3, lpnt, loi, hii);
+ findDist(fctx, alpha, vv0, tt1, tt2, vv3, lpnt, loi, hii);
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: nrp iter %u (reparm) delta = %g (big %d)\n", me, loi, hii,
+ iter, delta, fctx->distBig);
+ }
+ if (delta <= fctx->nrpDeltaThresh) {
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: nrp iter %u delta %g <= min %g --> break\n", me, loi, hii,
+ iter, delta, fctx->nrpDeltaThresh);
+ }
+ converged = AIR_TRUE;
+ break;
+ }
+ }
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: nrp done after %u iters: ", me, loi, hii, iter);
+ if (converged) {
+ printf("converged!\n");
+ } else if (!fctx->distBig) {
+ printf("nice small dist %g @ %u\n", fctx->distMax, fctx->distMaxIdx);
+ } else {
+ printf("hit itermax %u\n", fctx->nrpIterMax);
+ }
+ }
+ fctx->nrpIterDone = iter;
+ } else {
+ /* else fctx->distBig == 3: dist so big that we don't even try nrp */
+ fctx->nrpIterDone = 0;
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: such big (%d) dist %g > %g we didn't try nrp\n", me, loi, hii,
+ fctx->distBig, fctx->distMax, fctx->nrpPsi * fctx->epsilon);
+ }
+ }
+ fctx->nrpDeltaDone = delta;
+ }
+ if (fctx->verbose) {
+ printf("%s[%d,%d]: leaving with alpha %g %g\n", me, loi, hii, alpha[0], alpha[1]);
+ }
+ return;
+}
+
+/*
+limnCBFitSingle
+
+builds a limnCBFPoints around given xy, determines spline constraints if necessary, and
+calls fitSingle
+*/
+int /* Biff: 1 */
+limnCBFitSingle(limnCBFSeg *seg, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], limnCBFCtx *_fctx,
+ const double *xy, uint pNum) {
+ static const char me[] = "limnCBFitSingle";
+ double v0c[2], t1c[2], t2c[2], v3c[2]; /* locally computed geometry info */
+ double alpha[2]; /* recovered by fitting */
+ const double *v0p, *t1p, *t2p, *v3p; /* pointers for the geometry info */
+ uint loi, hii;
+ limnCBFCtx *fctx;
+ limnCBFPoints *lpnt;
+ airArray *mop;
+
+ if (!(seg && xy && pNum)) {
+ biffAddf(LIMN, "%s: got NULL pointer or 0 points", me);
+ return 1;
+ }
+ mop = airMopNew();
+ lpnt = limnCBFPointsNew(xy, nrrdTypeDouble, 2, pNum, AIR_FALSE /* isLoop */);
+ airMopAdd(mop, lpnt, (airMopper)limnCBFPointsNix, airMopAlways);
+ loi = 0;
+ hii = pNum - 1;
+ if (_fctx) {
+ fctx = _fctx; /* caller has supplied info */
+ } else {
+ fctx = limnCBFCtxNew();
+ airMopAdd(mop, fctx, (airMopper)limnCBFCtxNix, airMopAlways);
+ fctx->scale = 0;
+ fctx->epsilon = 0.01; /* HEY maybe instead some multiple of stdv? */
+ /* (if you don't like these hard-coded defaults then pass your own fctx) */
+ }
+ if (limnCBFCtxPrep(fctx, lpnt)) {
+ biffAddf(LIMN, "%s: problem with %s fctx or lpnt", me, _fctx ? "given" : "own");
+ airMopError(mop);
+ return 1;
+ }
+ if (vv0 && tt1 && tt2 && vv3) {
+ /* point to the given containers of geometry info */
+ v0p = vv0; /* DIM=2 ? */
+ t1p = tt1;
+ t2p = tt2;
+ v3p = vv3;
+ } else {
+ int oneSided = AIR_TRUE;
+ if (vv0 || tt1 || tt2 || vv3) {
+ biffAddf(LIMN,
+ "%s: should either give all vv0,tt1,tt2,vv3 or none (not %p,%p,%p,%p)",
+ me, (const void *)vv0, (const void *)tt1, (const void *)tt2,
+ (const void *)vv3);
+ airMopError(mop);
+ return 1;
+ }
+ /* have NO given geometry info; must find it all */
+ if (limnCBFFindTVT(NULL, v0c, t1c, /* */
+ fctx, lpnt, loi, hii, 0 /* ofi */, oneSided)
+ || limnCBFFindTVT(t2c, v3c, NULL, /* */
+ fctx, lpnt, loi, hii, hii /* ofi */, oneSided)) {
+ biffAddf(LIMN, "%s: trouble finding geometry info", me);
+ airMopError(mop);
+ return 1;
+ }
+ if (fctx->verbose) {
+ printf("%s: found geometry (%g,%g) --> (%g,%g) -- (%g,%g) <-- (%g,%g)\n", me,
+ v0c[0], v0c[1], t1c[0], t1c[1], t2c[0], t2c[1], v3c[0], v3c[1]);
+ }
+ v0p = v0c;
+ t1p = t1c;
+ t2p = t2c;
+ v3p = v3c;
+ }
+ fitSingle(alpha, fctx, v0p, t1p, t2p, v3p, lpnt, loi, hii);
+
+ ELL_2V_COPY(seg->xy + 0, v0p);
+ ELL_2V_SCALE_ADD2(seg->xy + 2, 1, v0p, alpha[0], t1p);
+ ELL_2V_SCALE_ADD2(seg->xy + 4, 1, v3p, alpha[1], t2p);
+ ELL_2V_COPY(seg->xy + 6, v3p);
+ /* HEY is it our job to set seg->corner[] ?!? */
+ seg->pointNum = pNum;
+
+ airMopOkay(mop);
+ return 0;
+}
+
+/*
******** limnCBFit
**
** top-level function for fitting cubic beziers to given points
@@ -829,7 +1264,7 @@
(may want to pay in time for more economical representation)
"What GLK hasn't thought through is: what is the interaction of nrp iterations and
-findalpha generating the simple arc on some but not all iterations (possibly
+findAlpha generating the simple arc on some but not all iterations (possibly
unstable?)"
resurrect in segment struct: how many points were represented by a single spline
Modified: teem/trunk/src/limn/test/lpu.c
===================================================================
--- teem/trunk/src/limn/test/lpu.c 2024-06-27 18:44:48 UTC (rev 7170)
+++ teem/trunk/src/limn/test/lpu.c 2024-06-28 04:44:18 UTC (rev 7171)
@@ -72,6 +72,7 @@
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
hparm->elideMultipleEmptyStringDefault = AIR_TRUE;
hparm->cleverPluralizeOtherY = AIR_TRUE;
+ hparm->respectDashDashHelp = AIR_TRUE;
hparm->columns = 78;
/* if there are no arguments, then we give general usage information */
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-27 18:44:50
|
Revision: 7170
http://sourceforge.net/p/teem/code/7170
Author: kindlmann
Date: 2024-06-27 18:44:48 +0000 (Thu, 27 Jun 2024)
Log Message:
-----------
more progress; limnCBFFindTVT seems to work now
Modified Paths:
--------------
teem/trunk/src/limn/lpu_cbfit.c
teem/trunk/src/limn/splineFit.c
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-06-27 18:08:47 UTC (rev 7169)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-06-27 18:44:48 UTC (rev 7170)
@@ -50,9 +50,10 @@
hestOptAdd_1_Double(&hopt, "sup", "expo", &supow, "1",
"when synthesizing data on a single segment, warp U parameters "
"by raising to this power.");
- hestOptAdd_4_Int(&hopt, "tvt", "loi hii ofi 1s", tvt, "-1 -1 -1 -1",
- "if all values are >= 0: make single call to "
- "limnCBFFindTVT and quit");
+ hestOptAdd_4_Int(&hopt, "tvt", "loi hii absi 1s", tvt, "0 0 0 -1",
+ "if last value is >= 0: make single call to limnCBFFindTVT and quit, "
+ "but note that the given loi,hii,absi args are not exactly what is "
+ "passed to limnCBFFindTVT");
hestOptAdd_1_UInt(&hopt, "im", "max", &iterMax, "0",
"(if non-zero) max # nrp iterations to run");
hestOptAdd_1_Double(&hopt, "deltathr", "delta", &deltaThresh, "0.0005",
@@ -171,12 +172,24 @@
fctx->nrpIota = nrpIota;
fctx->nrpPsi = psi;
fctx->cornAngle = cangle;
- if (tvt[0] >= 0 && tvt[1] >= 0 && tvt[2] >= 0 && tvt[3] >= 0) {
+ if (tvt[3] >= 0) {
double lt[2], vv[2], rt[2];
- unsigned int loi = AIR_UINT(tvt[0]), hii = AIR_UINT(tvt[1]), ofi = AIR_UINT(tvt[2]);
- int oneSided = tvt[3];
- if (limnCBFCtxPrep(fctx, lpnt)
- || limnCBFFindTVT(lt, vv, rt, fctx, lpnt, loi, hii, ofi, oneSided)) {
+ int pnum = AIR_INT(lpnt->num);
+ /* whoa - this is how GLK learned that AIR_MOD is garbage if args differ in
+ sign-ed-ness */
+ unsigned int loi = AIR_UINT(AIR_MOD(tvt[0], pnum));
+ unsigned int hii = AIR_UINT(AIR_MOD(tvt[1], pnum));
+ unsigned int ofi = AIR_UINT(AIR_MOD(tvt[2] - tvt[0], pnum));
+ int E, oneSided = !!tvt[3];
+ E = 0;
+ if (!E && fctx->verbose)
+ printf("%s: TVT %d (absolute) in [%d,%d] --> %u (offset) in [%u,%u]\n", me, /* */
+ tvt[2], tvt[0], tvt[1], ofi, loi, hii);
+ if (!E) E |= limnCBFCtxPrep(fctx, lpnt);
+ if (!E && fctx->verbose)
+ printf("%s: limnCBFCtxPrep done, calling limnCBFFindTVT\n", me);
+ if (!E) E |= limnCBFFindTVT(lt, vv, rt, fctx, lpnt, loi, hii, ofi, oneSided);
+ if (E) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble doing single tangent-vertex-tangent:\n%s", me, err);
airMopError(mop);
@@ -184,9 +197,9 @@
}
printf("%s: loi,hii=[%d,%d] ofi=%d oneSided=%d limnCBFFindTVT:\n", me, loi, hii, ofi,
oneSided);
- printf(" lt = %g %g\n", lt[0], lt[1]);
- printf(" vv = %g %g\n", vv[0], vv[1]);
- printf(" rt = %g %g\n", rt[0], rt[1]);
+ printf("lt = %g %g\n", lt[0], lt[1]);
+ printf("vv = %g %g\n", vv[0], vv[1]);
+ printf("rt = %g %g\n", rt[0], rt[1]);
printf("(quitting)\n");
airMopOkay(mop);
return 0;
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-06-27 18:08:47 UTC (rev 7169)
+++ teem/trunk/src/limn/splineFit.c 2024-06-27 18:44:48 UTC (rev 7170)
@@ -264,25 +264,33 @@
} else {
/* one: what value in summing kernel weights should count as 1.0. This should
probably be a parm in fctx, but not very interesting to change; it reflects something
- about the confidence that the nrrdKernelDiscreteGaussian is working as expected,
- rather than something about tuning cubic spline fitting */
- const double one = 0.999;
+ about the confidence that the nrrdKernelDiscreteGaussian is working as expected
+ (specifically: its weights should really sum to unity), rather than something about
+ tuning cubic spline fitting. We could compare fctx->scale with
+ nrrdKernelDiscreteGaussianGoodSigmaMax but that is set conservatively low (only 6, as
+ of June 2024) */
+ const double one = 0.99;
/* if vw and tw are allocated for length wlbig (or bigger) something isn't right */
const uint wlbig = 128;
/* if the initial weights for the tangent computation sum to smaller than this
(they will be later normalized to sum to 1) then something isn't right */
- const double tinytsum = 1.0 / 64;
- double kparm[2], vsum, tsum;
+ const double tinysum = 1.0 / 64;
+ double kw, kparm[2], vsum, tsum;
uint wlen;
/* else need to (possibly allocate and) set vw and tw buffers */
kparm[0] = scale;
- kparm[1] = 100000; /* effectively no cut-off; sanity check comes later */
+ kparm[1] = 1000000; /* effectively no cut-off; sanity check comes later */
ii = 0;
vsum = 0;
do {
- vsum += (!ii ? 1 : 2) * nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
+ kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
+ kw = fabs(kw); /* should be moot, but discrete kernels aren't bullet-proof */
+ vsum += (!ii ? 1 : 2) * kw;
+ if (fctx->verbose > 1) {
+ printf("%s: kw[%u] = %g --> vsum = %g\n", me, ii, kw, vsum);
+ }
ii++;
- } while (vsum < one && ii < wlbig);
+ } while (vsum < one && kw);
/* wlen = intended length of blurring kernel weight vectors */
wlen = ii;
if (wlen > wlbig) {
@@ -292,7 +300,10 @@
me, wlen, scale);
return 1;
}
- if (wlen > pNum / 2) {
+ if (2 * wlen > pNum) {
+ /* note: #verts involved in computation == 2*wlen - 1, so this is only going to
+ complain only when ~all the verts are contributing to computations for each
+ vertex, which is pretty excessive */
biffAddf(LIMN,
"%s: weight buffer length %u (from scale %g) seems "
"too large compared to #points %u",
@@ -311,7 +322,7 @@
}
/* normalization intent:
1 = sum_i(vw[|i|]) for i=-(len-1)...len-1
- 1 = sum_i(tw[i]) for i=1...len-1 */
+ 1 = sum_i(tw[i]) for i=0...len-1 */
vsum = tsum = 0;
for (ii = 0; ii < wlen; ii++) {
double kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
@@ -318,7 +329,7 @@
vsum += (!ii ? 1 : 2) * (fctx->vw[ii] = kw);
tsum += (fctx->tw[ii] = ii * kw);
}
- if (tsum < tinytsum) {
+ if (tsum < tinysum) {
biffAddf(LIMN,
"%s: scale %g led to tiny unnormalized tangent weight sum %g; "
"purpose of scale is to do blurring but scale %g won't do that",
@@ -325,6 +336,11 @@
me, scale, tsum, scale);
return 1;
}
+ if (vsum < tinysum) {
+ biffAddf(LIMN, "%s: scale %g led to unexpected tiny vertex weight sum %g", me,
+ scale, vsum);
+ return 1;
+ }
for (ii = 0; ii < wlen; ii++) {
fctx->vw[ii] /= vsum;
fctx->tw[ii] /= tsum;
@@ -493,8 +509,8 @@
/* error-checked index processing for limnCBFFindVT and maybe others */
static int /* Biff: 1 */
-idxPrep(int *sloP, int *shiP, int *loopyP, const limnCBFPoints *lpnt, uint loi,
- uint hii) {
+idxPrep(int *sloP, int *shiP, int *loopyP, const limnCBFCtx *fctx,
+ const limnCBFPoints *lpnt, uint loi, uint hii) {
static const char me[] = "idxPrep";
int slo, shi, loopy, spanlen;
@@ -512,6 +528,9 @@
}
slo = AIR_INT(loi);
shi = AIR_INT(hii);
+ if (fctx->verbose > 1) {
+ printf("%s: span as uint [%u,%u] -> int [%d,%d]\n", me, loi, hii, slo, shi);
+ }
if (lpnt->isLoop) {
loopy = (0 == loi && 0 == hii);
if (hii < loi) {
@@ -549,13 +568,19 @@
ELL_2V_NORM(dir, dir, len); /* normalize(dir) */
}
-/* limnCBFFindTVT: Find constraints for spline fitting: incoming/left tangent lt, center
-or endpoint vertex vv, outgoing/right tangent rt; any but not all can be NULL. These are
-computed from the given points lpnt, at offset index ofi within index range loi, hii
+/*
+limnCBFFindTVT: Find constraints for spline fitting: incoming/left tangent lt, center or
+endpoint vertex vv, outgoing/right tangent rt; any but not all can be NULL. These are
+computed from the given points lpnt, at offset index ofi within index range [loi, hii]
and only that range: that range is probably delimited by corners, and we have to be blind
-to anything past the corners on either side of us (except if loi==hii==0, in which case
-we can look at all the points in a loop).
+to anything past the corners on either side of us (except if loi==hii==0 in a loop, in
+which case we can look at all the points).
+Given that this is the inner-loop of other things, it would make sense to have a
+non-public version without all the error checking, but given the birthing pains of this
+code, the error-checking is a safety-net, and is welcome until profiling shows it is
+actually a problem.
+
NOTE: this assumes that limnCBFCtxPrep(fctx, lpnt) was called without error!
It (via ctxBuffersSet) allocates things that we depend on here
*/
@@ -581,13 +606,21 @@
biffAddf(LIMN, "%s: got NULL pointer (or too many NULL pointers)", me);
return 1;
}
+ if (fctx->verbose > 1) {
+ printf("%s: hello: %u in [%u,%u] in %sloop with %u points (%s-sided)\n", me, ofi,
+ loi, hii, lpnt->isLoop ? "" : "NON-", lpnt->num, oneSided ? "1" : "2");
+ }
/* so: each of lt, vv, rt can be NULL (they just can't be all NULL) */
- if (idxPrep(&slo, &shi, &loopy, lpnt, loi, hii)) {
+ if (idxPrep(&slo, &shi, &loopy, fctx, lpnt, loi, hii)) {
biffAddf(LIMN, "%s: trouble with loi %u or hii %u", me, loi, hii);
return 1;
}
spanlen = shi - slo + 1;
pnum = AIR_INT(lpnt->num);
+ if (fctx->verbose) {
+ printf("%s: %d pnts: [%u,%u]->[%d,%d] (len=%d) (loopy=%u)\n", me, pnum, loi, hii,
+ slo, shi, spanlen, loopy);
+ }
/* now:
0 == slo == shi implies lpnt->isLoop (and this is called "loopy")
@@ -623,7 +656,6 @@
if (vv) {
ELL_2V_COPY(vv, xyC);
}
- /* printf("!%s: iplus=%u imnus=%u xy=%g %g\n", me, iplus, imnus, xy[0], xy[1]); */
xyP = PP(lpnt) + 2 * iplus;
xyM = PP(lpnt) + 2 * imnus;
if (rt) {
@@ -632,8 +664,6 @@
if (lt) {
subnorm2(lt, xyM, oneSided ? xyC : xyP);
}
- /* printf("!%s: xyP=%g %g xyM=%g %g len=%g tt=%g %g\n", me, xyP[0], xyP[1],
- xyM[0], xyM[1], len, tt[0], tt[1]); */
} else {
/* using scale>0 for endpoint and tangent estimation */
/* for simplicity: regardless of dir, we compute average positions for points
@@ -652,24 +682,23 @@
return 1;
}
for (ci = -lim; ci <= lim; ci++) {
- uint wi = abs(ci); /* weight index into vw, tw */
- int di = slo + sof + ci; /* signed index into data */
+ uint wi = abs(ci); /* weight index into vw, tw */
+ int adi, /* actual data index */
+ di = slo + sof + ci; /* signed (and not %-ed) index into data */
const double *xy;
if (!loopy) di = AIR_CLAMP(slo, di, shi);
- di = AIR_MOD(di, pnum);
- xy = PP(lpnt) + 2 * di;
+ adi = AIR_MOD(di, pnum);
+ xy = PP(lpnt) + 2 * adi;
ELL_2V_SCALE_INCR(posC, vw[wi], xy);
- /* printf("!%s: (ci=%d/wi=%u) posC += %g*(%g %g) --> %g %g\n", me, ci, wi,
- vw[wi], xy[0], xy[1], posC[0], posC[1]); */
+ if (fctx->verbose > 1) {
+ printf("%s: ci=%d (in [%d,%d]) --> di=%d --> adi=%d; v,t weights %g,%g\n", me,
+ ci, -lim, lim, di, adi, vw[wi], tw[wi]);
+ }
if (ci < 0) {
ELL_2V_SCALE_INCR(posM, tw[wi], xy);
- /* printf("!%s: (ci=%d/wi=%u) posM += %g*(%g %g) --> %g %g\n", me, ci, wi,
- tw[wi], xy[0], xy[1], posM[0], posM[1]); */
}
if (ci > 0) {
ELL_2V_SCALE_INCR(posP, tw[wi], xy);
- /* printf("!%s: (ci=%d/wi=%u) posP += %g*(%g %g) --> %g %g\n", me, ci, wi,
- tw[wi], xy[0], xy[1], posP[0], posP[1]); */
}
}
{
@@ -789,15 +818,11 @@
-- avoid repeated calls to limnCBFCtxPrep
-- when should lenF2L be replaced by posStdv?
-test logic at end of limnCBFFindTVT about capping distance between VV and given pos
-test limnCBFFindTVT with span length as low as 2
debug limnCBFitSingle with various conditions
testing corners: corners near and at start==stop of isLoop
corners not at start or stop of isLoop: do spline wrap around from last to first index?
-do profiling to see if non-error-checking version of limnCBFFindVT is warranted
-
use performance tests to explore optimal settings in fctx:
nrpIterMax, nrpCap, nrpIota, nrpPsi, nrpDeltaThresh
evaluated in terms of time and #splines needed for fit
@@ -817,8 +842,10 @@
valgrind everything
-remove big debugging comment blocks
+search for HEY
+remove big commented-out print statements: at least in limnCBFSegEval, limnCBFPathSample
+
(DIM=2) explore what would be required to generalized from 2D to 3D,
perhaps at least at the API level, even if 3D is not yet implemented
*/
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-27 18:08:49
|
Revision: 7169
http://sourceforge.net/p/teem/code/7169
Author: kindlmann
Date: 2024-06-27 18:08:47 +0000 (Thu, 27 Jun 2024)
Log Message:
-----------
added warning about how AIR_MOD can give bad results if args differ in sign-ed-ness, and other formatting tweaks
Modified Paths:
--------------
teem/trunk/src/air/air.h
Modified: teem/trunk/src/air/air.h
===================================================================
--- teem/trunk/src/air/air.h 2024-06-27 02:28:35 UTC (rev 7168)
+++ teem/trunk/src/air/air.h 2024-06-27 18:08:47 UTC (rev 7169)
@@ -908,13 +908,14 @@
/*
******** AIR_MOD(i, N)
**
-** returns that integer in [0, N-1] which is i plus a multiple of N. It
-** may be unfortunate that the expression (i)%(N) appears three times;
-** this should be inlined. Or perhaps the compiler's optimizations
-** (common sub-expression elimination) will save us.
+** returns that integer in [0, N-1] which is i plus a multiple of N. It may be
+** unfortunate that the expression (i)%(N) appears three times; we can hope the compiler
+** optimizes the common sub-expression.
**
-** Note: integer divisions are not very fast on some modern chips;
-** don't go silly using this one.
+** NOTE: !! Results will not be as expected if i and N differ in sign-ed-ness !!
+**
+** Note: integer divisions are not very fast on some modern chips; don't go silly using
+** this one.
*/
#define AIR_MOD(i, N) ((i) % (N) >= 0 ? (i) % (N) : N + (i) % (N))
@@ -972,9 +973,9 @@
** rounds integers up or down; just wrappers around floor and ceil
*/
#define AIR_ROUNDUP(x) ((int)(floor((x) + 0.5)))
-#define AIR_ROUNDDOWN(x) ((int)(ceil((x)-0.5)))
+#define AIR_ROUNDDOWN(x) ((int)(ceil((x) - 0.5)))
#define AIR_ROUNDUP_UI(x) ((unsigned int)(floor((x) + 0.5)))
-#define AIR_ROUNDDOWN_UI(x) ((unsigned int)(ceil((x)-0.5)))
+#define AIR_ROUNDDOWN_UI(x) ((unsigned int)(ceil((x) - 0.5)))
#ifdef __cplusplus
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-27 02:28:36
|
Revision: 7168
http://sourceforge.net/p/teem/code/7168
Author: kindlmann
Date: 2024-06-27 02:28:35 +0000 (Thu, 27 Jun 2024)
Log Message:
-----------
finally making some progress on cubic spline fitting; still very much in progress; committing work so far to facilitate testing on different platforms
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-06-21 07:44:59 UTC (rev 7167)
+++ teem/trunk/src/limn/limn.h 2024-06-27 02:28:35 UTC (rev 7168)
@@ -517,21 +517,22 @@
** (using DIM=2 to mark places where the 2D-ness of the code surfaces )
*/
typedef struct {
- double xy[8]; /* four control points of cubic Bezier:
- x0, y0, x1, y1, x2, y2, x3, y3
- 0 1 2 3 4 5 6 7 DIM=2 */
- int corner[2]; /* corner[0,1] non-zero if xy[0,3] are corner vertices;
- segments otherwise assumed geometrically continuous */
- unsigned int pNum; /* (if non-zero) this segment approximates pNum points */
+ double xy[8]; /* four control points of cubic Bezier:
+ x0, y0, x1, y1, x2, y2, x3, y3
+ 0 1 2 3 4 5 6 7 DIM=2 */
+ int corner[2]; /* corner[0,1] non-zero if xy[0,3] are either corner vertices
+ or path-ending vertices, i.e. reasons to not have geometric
+ continuity here, either because we intend to have a corner,
+ or because there's nothing else to be continuous with */
} limnCBFSeg;
/*
******** limnCBFPath
**
-** a multi-segment path in the context of cubic Bezier fitting
+** a multi-spline path in the context of cubic Bezier fitting
*/
typedef struct {
- limnCBFSeg *seg; /* array of limnCBFSeg */
+ limnCBFSeg *seg; /* array of limnCBFSeg structs (NOT pointers to them) */
unsigned int segNum; /* length of seg array */
airArray *segArr; /* manages seg and segNum */
int isLoop; /* path is closed loop */
@@ -540,7 +541,7 @@
/*
******** limnCBFCtx
**
-** The bag of state for limnCBF functions.
+** The complete bag of input parameters and state for limnCBF functions.
**
** note: "nrp" = Newton-based Re-Parameterization of where the given points
** fall along the spline, the iterative process inside limnCBFSingle
@@ -547,57 +548,61 @@
*/
typedef struct {
/* ----------- input ---------- */
- int verbose, /* verbosity level */
- cornNMS; /* non-minimal-suppression of corners: accept as
- corners only those with locally minimal angle */
+ int verbose, /* verbosity level */
+ cornerFind, /* do first search for corners: places where the path is not
+ geometrically continuous (between corners, the path is geometrically
+ 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 */
- double scale, /* scale (in sense of nrrdKernelDiscreteGaussian) at which to estimate
- spline endpoints and tangents; scale=0 means the endpoints are
- exactly on vertices, and tangents are from the smallest-support
- finite differences. This is the ONLY floating point that should be set
- by a method (limnCBFScaleSet); the rest can be set directly. */
- distMin, /* min distance to given points: this controls both splitting done by
- limnCBFMulti, and nrp within limnCBFSingle */
- nrpDeltaMax, /* in nrp, capping parameterization change to this scaling of average
- u[i+1]-u[i]. This wasn't in author's original code (so their idea of
- doing at most ~5 iters of nrp may no longer hold), but it can help
- stabilize things */
- nrpDistScl, /* scaling on distMin to use when testing distance during nrp; setting
- this < 1 means that nrp tries to be more stringent than the overall
- fitting, but with the benefit of sometimes being smarter about where
- to split, when that is needed */
- nrpPsi, /* don't even try nrp if max dist is bigger than nrpPsi*distMin, instead just
- subdivide */
- nrpDeltaMin, /* min total parameterization change by nrp */
- 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 */
+ double
+ epsilon, /* error threshold on min distance from spline (as currently parameterized)
+ to given points: this affects both splitting done by limnCBFMulti, and
+ nrp within limnCBFSingle. Fitting has successfully finished when spline
+ path never strays further than this from input points */
+ scale, /* scale (in sense of nrrdKernelDiscreteGaussian) at which to estimate
+ spline endpoints and tangents; scale=0 means the endpoints are
+ exactly on vertices, and tangents are from the smallest-support
+ finite differences */
+ nrpCap, /* in nrp, cap parameterization change to this scaling of average
+ u[i+1]-u[i]. This wasn't in author's original code (so their idea of doing
+ at most ~5 iters of nrp no longer holds), but it can help stabilize things
+ with gnarly inputs */
+ nrpIota, /* (also not in author's original code) scaling on epsilon to use when
+ testing distance during nrp; setting this < 1 means that nrp tries to be
+ more stringent than the overall fitting, but with the benefit of
+ sometimes being smarter about where to split, when that is needed */
+ 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; /* 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. Or, if 0, no effort is made
- to detect corners. */
+ straight line have an angle of 180 degrees. */
/* ----------- internal --------- */
- double *uu, /* buffer used for nrp */
+ double *uu, /* used for nrp: buffer of parameterizations in [0,1] of point along
+ currently considered spline segment */
*vw, /* weights for endpoint vertex calculation */
*tw, /* weights for endpoint tangent calculation */
- *mine; /* helps remember who allocated the above */
- unsigned int wLen; /* how long are vw, tw */
- double lenF2L; /* length of segment from first to last */
+ *cpp, /* x,y positions of corners */
+ *clt, /* x,y left (incoming) tangents at corners */
+ *crt; /* x,y right (outgoing) tangents at corners */
+ unsigned int uLen, /* how long is uu */
+ wLen, /* how long are vw, tw */
+ cNum; /* number of corners = # positions in cpp = # tangent vecs in clt, crt */
/* ----------- output --------- */
unsigned int nrpIterDone, /* number of nrp iters taken */
- distIdx; /* which point had distance distDone */
- double dist, /* max distance to given points */
- nrpDeltaDone, /* latest total parameterization change by nrp */
+ distMaxIdx; /* which point had distance distMax */
+ double distMax, /* max distance to given points */
+ nrpDeltaDone, /* latest mean parameterization change by nrp */
alphaDet; /* min det of matrix inverted to find alpha */
- int distBig; /* how big dist (above) is:
- 0: dist <= nD
- 1: nD < dist <= DM
- 2: DM < dist <= fD
- 3: fD < dist
- where
- nD = nrpDistScl*distMin,
- DM = distMin,
- fD = nrpPsi*distMin: */
+ int distBig; /* how big distMax (above) is:
+ 0: distMax <= wee (wee = nrpIota*epsilon)
+ 1: wee < distMax <= eps (eps = epsilon)
+ 2: eps < distMax <= far (far = nrpPsi*epsilon)
+ 3: far < distMax */
} limnCBFCtx;
/*
@@ -609,16 +614,16 @@
**
** NOTE: For now, point data is only double (not float), and only in 2D (not
** 3D), but if this becomes more general, that generality will be inside here.
-** For time being DIM=2 tags locations where 2D-ness is explicit in code.
+** For time being "DIM=2" tags locations where 2D-ness is explicit in code.
*/
typedef struct {
- /* assuming DIM=2: 2 values per logical element pp */
const double *pp; /* point coords, we do not own buffer */
double *ppOwn; /* point coords, we DO own buffer */
- unsigned int num; /* how many points */
- int isLoop; /* points form a loop: logical indices into coord
- array are . . . num-2, num-1, 0, 1, . . .
- and index 0 is effectively arbitrary */
+ unsigned int num, /* how many points */
+ dim; /* points live in what dimension (currently only dim=2 implemented) */
+ int isLoop; /* points form a loop: logical indices into coord
+ array are . . . num-2, num-1, 0, 1, . . .
+ and index 0 is effectively arbitrary */
} limnCBFPoints;
/* defaultsLimn.c */
@@ -897,25 +902,28 @@
double maxT);
/* splineFit.c */
-LIMN_EXPORT limnCBFPoints *limnCBFPointsNew(const double *pp, unsigned int nn,
+LIMN_EXPORT limnCBFPoints *limnCBFPointsNew(const void *pdata, int ptype,
+ unsigned int dim, unsigned int pnum,
int isLoop);
LIMN_EXPORT limnCBFPoints *limnCBFPointsNix(limnCBFPoints *lpnt);
LIMN_EXPORT int limnCBFPointsCheck(const limnCBFPoints *lpnt);
-LIMN_EXPORT limnCBFCtx *limnCBFCtxNew(unsigned int pointNum, double scale);
+LIMN_EXPORT limnCBFCtx *limnCBFCtxNew();
LIMN_EXPORT limnCBFCtx *limnCBFCtxNix(limnCBFCtx *fctx);
+LIMN_EXPORT int limnCBFCtxPrep(limnCBFCtx *fctx, const limnCBFPoints *lpnt);
LIMN_EXPORT void limnCBFSegEval(double *xy, const limnCBFSeg *seg, double tt);
LIMN_EXPORT limnCBFPath *limnCBFPathNew(void);
LIMN_EXPORT limnCBFPath *limnCBFPathNix(limnCBFPath *path);
LIMN_EXPORT void limnCBFPathSample(double *xy, unsigned int pointNum,
const limnCBFPath *path);
-LIMN_EXPORT int limnCBFFindVT(double vv[2], double tt[2], const limnCBFCtx *fctx,
- const limnCBFPoints *lpnt, unsigned int loi,
- unsigned int hii, unsigned int ofi, int dir);
-LIMN_EXPORT int limnCBFCtxCheck(const limnCBFCtx *fctx, const limnCBFPoints *lpnt);
-LIMN_EXPORT int limnCBFitSingle(double alpha[2], limnCBFCtx *fctx, const double vv0[2],
- const double tt1[2], const double tt2[2],
- const double vv3[2], const double *xy,
- unsigned int pointNum, int isLoop);
+LIMN_EXPORT int limnCBFFindTVT(double lt[2], double vv[2], double rt[2],
+ const limnCBFCtx *fctx, const limnCBFPoints *lpnt,
+ unsigned int loi, unsigned int hii, unsigned int ofi,
+ int oneSided);
+LIMN_EXPORT int limnCBFCtxInit(const limnCBFCtx *fctx, const limnCBFPoints *lpnt);
+LIMN_EXPORT int limnCBFitSingle(double alpha[2], limnCBFCtx *fctx, /* */
+ const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2],
+ const double *xy, unsigned int pointNum);
LIMN_EXPORT int limnCBFMulti(limnCBFPath *path, limnCBFCtx *fctx, const double vv0[2],
const double tt1[2], const double tt2[2],
const double vv3[2], const limnCBFPoints *lpnt,
@@ -922,8 +930,8 @@
unsigned int loi, unsigned int hii);
LIMN_EXPORT int limnCBFCorners(unsigned int **cornIdx, unsigned int *cornNum,
limnCBFCtx *fctx, const limnCBFPoints *lpnt);
-LIMN_EXPORT int limnCBFit(limnCBFPath *path, limnCBFCtx *fctx, const double *xy,
- unsigned int pointNum, int isLoop);
+LIMN_EXPORT int limnCBFit(limnCBFPath *path, limnCBFCtx *fctx,
+ const limnCBFPoints *lpnt);
/* lpu{Flotsam,. . .}.c */
#define LIMN_DECLARE(C) LIMN_EXPORT const unrrduCmd limnPu_##C##Cmd;
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-06-21 07:44:59 UTC (rev 7167)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-06-27 02:28:35 UTC (rev 7168)
@@ -34,43 +34,42 @@
int pret;
Nrrd *_nin, *nin;
- double *xy, alpha[2], vv0[2], tt1[2], tt2[2], vv3[2], deltaMin, psi, cangle, distMin,
- distScl, utt1[2], utt2[2], time0, dtime, scale;
+ double *xy, deltaThresh, psi, cangle, epsilon, nrpIota, time0, dtime, scale, supow;
unsigned int ii, pNum, iterMax;
- int loop, petc, verbose, synth, nofit;
+ int loop, petc, verbose, tvt[4];
char *synthOut;
limnCBFCtx *fctx;
limnCBFPath *path;
+ limnCBFPoints *lpnt;
hestOptAdd_1_Other(&hopt, "i", "input", &_nin, NULL, "input xy points", nrrdHestNrrd);
hestOptAdd_1_Int(&hopt, "v", "verbose", &verbose, "1", "verbosity level");
- hestOptAdd_Flag(&hopt, "s", &synth, "synthesize xy points from control points");
hestOptAdd_1_String(&hopt, "so", "synth out", &synthOut, "",
- "if non-empty, filename in which to save synthesized xy pts");
- hestOptAdd_Flag(&hopt, "snf", &nofit,
- "actually do not fit, just save -so synthetic "
- "output and quit");
- hestOptAdd_2_Double(&hopt, "t1", "tan", utt1, "nan nan",
- "if non-nan, the outgoing tangent from the first point");
- hestOptAdd_2_Double(&hopt, "t2", "tan", utt2, "nan nan",
- "if non-nan, the incoming tangent to the last point");
+ "if non-empty, filename in which to save synthesized xy pts, "
+ "and then quit before any fitting.");
+ hestOptAdd_1_Double(&hopt, "sup", "expo", &supow, "1",
+ "when synthesizing data on a single segment, warp U parameters "
+ "by raising to this power.");
+ hestOptAdd_4_Int(&hopt, "tvt", "loi hii ofi 1s", tvt, "-1 -1 -1 -1",
+ "if all values are >= 0: make single call to "
+ "limnCBFFindTVT and quit");
hestOptAdd_1_UInt(&hopt, "im", "max", &iterMax, "0",
"(if non-zero) max # nrp iterations to run");
- hestOptAdd_1_Double(&hopt, "deltam", "delta", &deltaMin, "0.0005",
+ hestOptAdd_1_Double(&hopt, "deltathr", "delta", &deltaThresh, "0.0005",
"(if non-zero) stop nrp when change in spline "
"domain sampling goes below this");
- hestOptAdd_1_Double(&hopt, "distm", "dist", &distMin, "0.01",
+ hestOptAdd_1_Double(&hopt, "eps", "dist", &epsilon, "0.01",
"(if non-zero) stop nrp when distance between spline "
"and points goes below this");
- hestOptAdd_1_Double(&hopt, "dists", "scl", &distScl, "0.25",
- "scaling on nrp distMin check");
+ hestOptAdd_1_Double(&hopt, "iota", "scl", &nrpIota, "0.25",
+ "scaling on nrp epsilon check");
hestOptAdd_1_Double(&hopt, "psi", "psi", &psi, "10", "psi, of course");
hestOptAdd_1_Double(&hopt, "ca", "angle", &cangle, "100", "angle indicating a corner");
hestOptAdd_1_Double(&hopt, "scl", "scale", &scale, "0",
"scale for geometry estimation");
hestOptAdd_Flag(&hopt, "loop", &loop,
- "given xy points are actually a loop; BUT "
- "the first and last points need to be the same!");
+ "given xy points are actually a loop: the first point logically "
+ "follows the last point");
hestOptAdd_Flag(&hopt, "petc", &petc, "(Press Enter To Continue) ");
/*
hestOptAdd_1_String(&hopt, NULL, "output", &out, NULL, "output nrrd filename");
@@ -91,7 +90,7 @@
airMopError(mop);
return 1;
}
- if (synth && 6 != _nin->axis[1].size) {
+ if (airStrlen(synthOut) && 6 != _nin->axis[1].size) {
fprintf(stderr, "%s: need 2-by-6 array (not 2-by-%u) for synthetic xy\n", me,
(unsigned int)_nin->axis[1].size);
airMopError(mop);
@@ -107,10 +106,11 @@
return 1;
}
- if (!synth) {
+ if (!airStrlen(synthOut)) {
xy = (double *)nin->data;
pNum = (unsigned int)nin->axis[1].size;
} else {
+ double alpha[2], vv0[2], tt1[2], tt2[2], vv3[2];
/* synthesize data from control points */
double *cpt = (double *)nin->data;
limnCBFSeg seg;
@@ -135,7 +135,9 @@
xy = AIR_MALLOC(2 * pNum, double);
airMopAdd(mop, xy, airFree, airMopAlways);
for (ii = 0; ii < pNum; ii++) {
- limnCBFSegEval(xy + 2 * ii, &seg, AIR_AFFINE(0, ii, pNum - 1, 0, 1));
+ double uu = AIR_AFFINE(0, ii, pNum - 1, 0, 1);
+ uu = pow(uu, supow);
+ limnCBFSegEval(xy + 2 * ii, &seg, uu);
}
if (airStrlen(synthOut)) {
Nrrd *nsyn = nrrdNew();
@@ -147,42 +149,48 @@
airMopError(mop);
return 1;
}
- if (nofit) {
- fprintf(stderr, "%s: got -nf nofit; bye\n", me);
- airMopOkay(mop);
- return 0;
- }
+ printf("%s: saved synthetic output to %s; bye\n", me, synthOut);
+ airMopOkay(mop);
+ return 0;
}
}
- {
- /* set up 2-vector-valued arguments to fitting */
- double len;
- ELL_2V_COPY(vv0, xy);
- ELL_2V_COPY(vv3, xy + 2 * (pNum - 1));
- if (ELL_2V_EXISTS(utt1)) {
- ELL_2V_COPY(tt1, utt1);
- } else {
- /* TODO: better tangent estimation */
- ELL_2V_SUB(tt1, xy + 2, xy);
- }
- if (ELL_2V_EXISTS(utt2)) {
- ELL_2V_COPY(tt2, utt2);
- } else {
- ELL_2V_SUB(tt2, xy + 2 * (pNum - 2), vv3);
- }
- ELL_2V_NORM(tt1, tt1, len);
- ELL_2V_NORM(tt2, tt2, len);
+ if (!(lpnt = limnCBFPointsNew(xy, nrrdTypeDouble, 2, pNum, loop))) {
+ airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
+ fprintf(stderr, "%s: trouble setting up points:\n%s", me, err);
+ airMopError(mop);
+ return 1;
}
path = limnCBFPathNew();
airMopAdd(mop, path, (airMopper)limnCBFPathNix, airMopAlways);
- fctx = limnCBFCtxNew(pNum, scale);
+ fctx = limnCBFCtxNew();
+ fctx->verbose = verbose;
fctx->nrpIterMax = iterMax;
- fctx->nrpDeltaMin = deltaMin;
- fctx->distMin = distMin;
- fctx->nrpDistScl = distScl;
- fctx->verbose = verbose;
+ fctx->scale = scale;
+ fctx->epsilon = epsilon;
+ fctx->nrpDeltaThresh = deltaThresh;
+ fctx->nrpIota = nrpIota;
fctx->nrpPsi = psi;
fctx->cornAngle = cangle;
+ if (tvt[0] >= 0 && tvt[1] >= 0 && tvt[2] >= 0 && tvt[3] >= 0) {
+ double lt[2], vv[2], rt[2];
+ unsigned int loi = AIR_UINT(tvt[0]), hii = AIR_UINT(tvt[1]), ofi = AIR_UINT(tvt[2]);
+ int oneSided = tvt[3];
+ if (limnCBFCtxPrep(fctx, lpnt)
+ || limnCBFFindTVT(lt, vv, rt, fctx, lpnt, loi, hii, ofi, oneSided)) {
+ airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
+ fprintf(stderr, "%s: trouble doing single tangent-vertex-tangent:\n%s", me, err);
+ airMopError(mop);
+ return 1;
+ }
+ printf("%s: loi,hii=[%d,%d] ofi=%d oneSided=%d limnCBFFindTVT:\n", me, loi, hii, ofi,
+ oneSided);
+ printf(" lt = %g %g\n", lt[0], lt[1]);
+ printf(" vv = %g %g\n", vv[0], vv[1]);
+ printf(" rt = %g %g\n", rt[0], rt[1]);
+ printf("(quitting)\n");
+ airMopOkay(mop);
+ return 0;
+ }
time0 = airTime();
if (petc) {
fprintf(stderr, "%s: Press Enter to Continue ... ", me);
@@ -189,23 +197,23 @@
fflush(stderr);
getchar();
}
- if (limnCBFit(path, fctx, xy, pNum, loop)) {
+ if (limnCBFit(path, fctx, lpnt)) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
- fprintf(stderr, "%s: trouble:\n%s", me, err);
+ fprintf(stderr, "%s: trouble doing fitting:\n%s", me, err);
airMopError(mop);
return 1;
}
dtime = (airTime() - time0) * 1000;
- printf("%s: time= %g ms;iterDone= %u ;deltaDone=%g, dist=%g (@%u)\n", me, dtime,
- fctx->nrpIterDone, fctx->nrpDeltaDone, fctx->dist, fctx->distIdx);
+ printf("%s: time= %g ms;iterDone= %u ;deltaDone=%g, distMax=%g (@%u)\n", me, dtime,
+ fctx->nrpIterDone, fctx->nrpDeltaDone, fctx->distMax, fctx->distMaxIdx);
{
unsigned int si;
printf("%s: path has %u segments:\n", me, path->segNum);
for (si = 0; si < path->segNum; si++) {
limnCBFSeg *seg = path->seg + si;
- printf("seg %u (%3u): (%g,%g) -- (%g,%g) -- (%g,%g) -- (%g,%g)\n", si, seg->pNum,
- seg->xy[0], seg->xy[1], seg->xy[2], seg->xy[3], seg->xy[4], seg->xy[5],
- seg->xy[6], seg->xy[7]);
+ printf("seg %u: (%g,%g) -- (%g,%g) -- (%g,%g) -- (%g,%g)\n", si, seg->xy[0],
+ seg->xy[1], seg->xy[2], seg->xy[3], seg->xy[4], seg->xy[5], seg->xy[6],
+ seg->xy[7]);
}
}
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-06-21 07:44:59 UTC (rev 7167)
+++ teem/trunk/src/limn/splineFit.c 2024-06-27 02:28:35 UTC (rev 7168)
@@ -1,6 +1,6 @@
/*
Teem: Tools to process and visualize scientific data and images
- Copyright (C) 2009--2022 University of Chicago
+ Copyright (C) 2009--2024 University of Chicago
Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
@@ -24,37 +24,68 @@
#include <assert.h>
/*
- This file contains GLK's implementation of the curve fitting described in:
- Philip J. Schneider. “An Algorithm for Automatically Fitting Digitized
- Curves”. In Graphics Gems, Academic Press, 1990, pp. 612–626.
- https://dl.acm.org/doi/10.5555/90767.90941
- The author's code is here:
- http://www.realtimerendering.com/resources/GraphicsGems/gems/FitCurves.c
+This file contains GLK's implementation of the curve fitting described in:
+Philip J. Schneider. "An Algorithm for Automatically Fitting Digitized Curves".
+In Graphics Gems, Academic Press, 1990, pp. 612–626.
+https://dl.acm.org/doi/10.5555/90767.90941
+The author's code is here:
+http://www.realtimerendering.com/resources/GraphicsGems/gems/FitCurves.c
- The functions below do not use any other limnSpline structs or functions, since those
- were written a long time ago and thus they reflect GLK's ignorance about splines at the
- time. Hopefully this will be revisited and re-organized in a later version of Teem, at
- which point the code below can be integrated with the rest of limn, but this too will
- benefit from ongoing scrutiny and re-writing; ignorance persists.
+The functions below do not use any other limnSpline structs or functions, since those
+were written a long time ago when GLK was even more ignorant than now about splines.
+Hopefully that other code can be revisited and re-organized for a later version of
+Teem, at which point the code below can be integrated with it.
+
+NOTE: spline fitting would be useful in 3D (or higher dimensions) too, but currently
+this code only supports 2D. "DIM=2" flags places in code where that is explicit.
*/
-limnCBFPoints * /* Biff: nope */
-limnCBFPointsNew(const double *pp, uint nn, int isLoop) {
- /* implicit: DIM=2 */
+#define PNMIN(ISLOOP) ((ISLOOP) ? 4 : 3)
+
+/*
+******** limnCBFPointsNew
+**
+** create a point data container, possibly around given pdata pointer. In an aspirational
+** hope of API stability, this is one of the few functions for which the interface itself
+** does not expose the specificity to DIM=2 and type double (though the code inside
+** does (apologetically) enforce that).
+*/
+limnCBFPoints * /* Biff: NULL */
+limnCBFPointsNew(const void *pdata, int ptype, uint dim, uint pnum, int isLoop) {
+ static const char me[] = "limnCBFPointsNew";
limnCBFPoints *lpnt;
+ if (airEnumValCheck(nrrdType, ptype)) {
+ biffAddf(LIMN, "%s: point data type %d not valid", me, ptype);
+ return NULL;
+ }
+ if (ptype != nrrdTypeDouble) {
+ biffAddf(LIMN, "%s: sorry, only %s-type data implemented now (not %s)", me,
+ airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, ptype));
+ return NULL;
+ }
+ if (2 != dim) {
+ biffAddf(LIMN, "%s: sorry, only 2-D data implemented now (not %u)", me, dim);
+ return NULL;
+ }
+ if (pnum < PNMIN(isLoop)) {
+ biffAddf(LIMN, "%s: need at least %u points in %s (not %u)", me, PNMIN(isLoop),
+ isLoop ? "loop" : "non-loop", pnum);
+ return NULL;
+ }
lpnt = AIR_CALLOC(1, limnCBFPoints);
assert(lpnt);
- if (pp) {
+ if (pdata) {
/* we are wrapping around a given pre-allocated buffer */
- lpnt->pp = pp;
+ lpnt->pp = pdata;
lpnt->ppOwn = NULL;
} else {
/* we are allocating our own buffer */
lpnt->pp = NULL;
- lpnt->ppOwn = AIR_CALLOC(nn, double);
+ lpnt->ppOwn = AIR_CALLOC(pnum, double);
assert(lpnt->ppOwn);
}
- lpnt->num = nn;
+ lpnt->num = pnum;
+ lpnt->dim = dim; /* but really DIM=2 because of above */
lpnt->isLoop = isLoop;
return lpnt;
}
@@ -79,7 +110,7 @@
biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
- pnmin = lpnt->isLoop ? 3 : 2;
+ pnmin = PNMIN(lpnt->isLoop);
if (!(lpnt->num >= pnmin)) {
biffAddf(LIMN, "%s: need %u or more points in limnCBFPoints (not %u)%s", me, pnmin,
lpnt->num, lpnt->isLoop ? " for loop" : "");
@@ -93,52 +124,73 @@
return 0;
}
-/* cheesy macro to access either pp or ppOwn, useful since they differ in const-ness */
-#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
+static void
+segInit(void *_seg) {
+ limnCBFSeg *seg = (limnCBFSeg *)_seg;
+ ELL_2V_NAN_SET(seg->xy + 0); /* DIM=2 */
+ ELL_2V_NAN_SET(seg->xy + 2);
+ ELL_2V_NAN_SET(seg->xy + 4);
+ ELL_2V_NAN_SET(seg->xy + 6);
+ seg->corner[0] = seg->corner[1] = AIR_FALSE;
+ return;
+}
-/* number of points between low,high indices loi,hii */
-static uint
-pntNum(const limnCBFPoints *lpnt, uint loi, uint hii) {
- if (hii < loi) {
- assert(lpnt->isLoop);
- hii += lpnt->num;
+limnCBFPath * /* Biff: nope */
+limnCBFPathNew() {
+ limnCBFPath *path;
+ path = AIR_MALLOC(1, limnCBFPath);
+ if (path) {
+ path->segArr = airArrayNew((void **)(&path->seg), &path->segNum, sizeof(limnCBFSeg),
+ 128 /* incr */);
+ airArrayStructCB(path->segArr, segInit, NULL);
+ path->isLoop = AIR_FALSE;
}
- return hii - loi + 1;
+ return path;
}
-/* coordinates of point with index loi+ii */
-static const double *
-pntCrd(const limnCBFPoints *lpnt, uint loi, uint ii) {
- uint jj = loi + ii;
- while (jj >= lpnt->num)
- jj -= lpnt->num;
- return PP(lpnt) + 2 * jj; /* DIM=2 */
+limnCBFPath * /* Biff: nope */
+limnCBFPathNix(limnCBFPath *path) {
+ if (path) {
+ airArrayNuke(path->segArr);
+ free(path);
+ }
+ return NULL;
}
static void
+limnCBFPathJoin(limnCBFPath *dst, const limnCBFPath *src) {
+ uint bb = airArrayLenIncr(dst->segArr, src->segNum);
+ memcpy(dst->seg + bb, src->seg, (src->segNum) * sizeof(limnCBFSeg));
+ return;
+}
+
+/* initialize a freshly allocated limnCBFCtx struct;
+ the pointers therein do not point to anything valid */
+static void
ctxInit(limnCBFCtx *fctx) {
if (!fctx) return;
/* defaults for input parameters to various CBF functions */
fctx->verbose = 0;
- fctx->cornNMS = AIR_TRUE;
+ fctx->cornerFind = AIR_TRUE;
+ fctx->cornerNMS = AIR_TRUE;
fctx->nrpIterMax = 10;
- fctx->distMin = 0;
- fctx->nrpDeltaMax = 3.0;
- fctx->nrpDistScl = 0.8;
- fctx->nrpPsi = 6;
- fctx->nrpDeltaMin = 0.001;
+ fctx->epsilon = 0; /* will need to be set to something valid elsewhere */
+ fctx->scale = 0; /* will need to be set to something valid elsewhere */
+ fctx->nrpCap = 3.0;
+ fctx->nrpIota = 0.8;
+ fctx->nrpPsi = 10;
+ fctx->nrpDeltaThresh = 0.01;
fctx->alphaMin = 0.001;
fctx->detMin = 0.01;
- fctx->cornAngle = 100.0; /* degrees */
- /* internal */
- fctx->uu = fctx->vw = fctx->tw = NULL;
- fctx->mine = NULL;
- fctx->wLen = 0;
- fctx->lenF2L = AIR_NAN;
+ fctx->cornAngle = 120.0; /* degrees */
+ /* internal state */
+ /* initialize buffer pointers to NULL and buffer lengths to 0 */
+ fctx->uu = fctx->vw = fctx->tw = fctx->cpp = fctx->clt = fctx->crt = NULL;
+ fctx->uLen = fctx->wLen = fctx->cNum = 0;
/* initialize outputs to bogus valus */
fctx->nrpIterDone = (uint)(-1);
- fctx->distIdx = (uint)(-1);
- fctx->dist = AIR_POS_INF;
+ fctx->distMaxIdx = (uint)(-1);
+ fctx->distMax = AIR_POS_INF;
fctx->nrpDeltaDone = AIR_POS_INF;
fctx->alphaDet = 0;
fctx->distBig = 0;
@@ -145,118 +197,215 @@
return;
}
+limnCBFCtx * /* Biff: nope */
+limnCBFCtxNew() {
+ limnCBFCtx *ret;
+
+ ret = AIR_CALLOC(1, limnCBFCtx);
+ assert(ret);
+ ctxInit(ret);
+ return ret;
+}
+
+limnCBFCtx * /* Biff: nope */
+limnCBFCtxNix(limnCBFCtx *fctx) {
+ if (fctx) {
+ if (fctx->uu) free(fctx->uu);
+ if (fctx->vw) free(fctx->vw);
+ if (fctx->tw) free(fctx->tw);
+ if (fctx->cpp) free(fctx->cpp);
+ if (fctx->clt) free(fctx->clt);
+ if (fctx->crt) free(fctx->crt);
+ free(fctx);
+ }
+ return NULL;
+}
+
/*
-** ctxBuffersSet: allocates in fctx:
-** uu, vw, tw
+** ctxBuffersSet: ensures that some buffers in fctx: uu, vw, tw
+** are set up for current #points pNum and measurement scale fctx->scale.
+** The buffers are re-allocated only when necessary.
+** Does NOT touch the corner-related buffers: cpp, clt, crt
*/
static int /* Biff: 1 */
-ctxBuffersSet(limnCBFCtx *fctx, uint pNum, double scl) {
+ctxBuffersSet(limnCBFCtx *fctx, uint pNum) {
static const char me[] = "ctxBuffersSet";
- double kparm[2], vsum, tsum;
- /* one: what value in summing kernel weights should count as 1.0. This
- should probably be a parm in fctx, but not very interesting to
- change */
- double one = 0.999;
- uint ii, len;
+ double scale;
+ uint ulen, ii;
- if (fctx->uu) free(fctx->uu);
- fctx->uu = AIR_CALLOC(pNum * 2, double); /* DIM=2 */
- if (!fctx->uu) {
- biffAddf(LIMN, "%s: failed to allocate parameter buffer", me);
+ if (!fctx) {
+ biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
- if (0 == scl) {
- /* will do simplest possible finite differences; we're done */
- fctx->vw = fctx->tw = NULL;
- return 0;
+ scale = fctx->scale;
+ if (!pNum || scale < 0 || !AIR_EXISTS(scale)) {
+ biffAddf(LIMN, "%s: pNum %u or scale %g not valid", me, pNum, scale);
+ return 1;
}
- /* else need to allocate and set vw and tw buffers */
- kparm[0] = scl;
- kparm[1] = 1000; /* effectively, no limit on scale; sanity check comes later */
- ii = 0;
- vsum = 0;
- do {
- double kw;
- kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
- vsum += (!ii ? 1 : 2) * kw;
- ii++;
- } while (vsum < one);
- /* len = intended length of blurring kernel weight vectors */
- len = ii + 1;
- if (len > 128) {
- biffAddf(LIMN,
- "%s: weight buffer length %u (from scale %g) seems "
- "unreasonable",
- me, len, scl);
+
+ /* assuming pNum = lpnt->num for points lpnt in consideration, this allocation size
+ (big enough to parameterize all the points at once) is safe, though it will likely
+ be excessive given how the path may be split at corners into separate segments. */
+ ulen = pNum * 2; /* DIM=2 */
+ if (ulen != fctx->uLen) {
+ airFree(fctx->uu);
+ if (!(fctx->uu = AIR_CALLOC(ulen, double))) {
+ biffAddf(LIMN, "%s: failed to allocate uu buffer (%u doubles)", me, ulen);
+ return 1;
+ }
+ }
+ fctx->uLen = ulen;
+
+ if (0 == scale) {
+ /* will do simplest possible finite differences; no need for weights */
+ fctx->vw = airFree(fctx->vw);
+ fctx->tw = airFree(fctx->tw);
+ fctx->wLen = 0;
+ } else {
+ /* one: what value in summing kernel weights should count as 1.0. This should
+ probably be a parm in fctx, but not very interesting to change; it reflects something
+ about the confidence that the nrrdKernelDiscreteGaussian is working as expected,
+ rather than something about tuning cubic spline fitting */
+ const double one = 0.999;
+ /* if vw and tw are allocated for length wlbig (or bigger) something isn't right */
+ const uint wlbig = 128;
+ /* if the initial weights for the tangent computation sum to smaller than this
+ (they will be later normalized to sum to 1) then something isn't right */
+ const double tinytsum = 1.0 / 64;
+ double kparm[2], vsum, tsum;
+ uint wlen;
+ /* else need to (possibly allocate and) set vw and tw buffers */
+ kparm[0] = scale;
+ kparm[1] = 100000; /* effectively no cut-off; sanity check comes later */
+ ii = 0;
+ vsum = 0;
+ do {
+ vsum += (!ii ? 1 : 2) * nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
+ ii++;
+ } while (vsum < one && ii < wlbig);
+ /* wlen = intended length of blurring kernel weight vectors */
+ wlen = ii;
+ if (wlen > wlbig) {
+ biffAddf(LIMN,
+ "%s: weight buffer length %u (from scale %g) seems "
+ "too large",
+ me, wlen, scale);
+ return 1;
+ }
+ if (wlen > pNum / 2) {
+ biffAddf(LIMN,
+ "%s: weight buffer length %u (from scale %g) seems "
+ "too large compared to #points %u",
+ me, wlen, scale, pNum);
+ return 1;
+ }
+ if (wlen != fctx->wLen) {
+ airFree(fctx->vw);
+ airFree(fctx->tw);
+ if (!((fctx->vw = AIR_CALLOC(wlen, double))
+ && (fctx->tw = AIR_CALLOC(wlen, double)))) {
+ biffAddf(LIMN, "%s: couldn't allocate weight buffers (%u doubles)", me, wlen);
+ return 1;
+ }
+ fctx->wLen = wlen;
+ }
+ /* normalization intent:
+ 1 = sum_i(vw[|i|]) for i=-(len-1)...len-1
+ 1 = sum_i(tw[i]) for i=1...len-1 */
+ vsum = tsum = 0;
+ for (ii = 0; ii < wlen; ii++) {
+ double kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
+ vsum += (!ii ? 1 : 2) * (fctx->vw[ii] = kw);
+ tsum += (fctx->tw[ii] = ii * kw);
+ }
+ if (tsum < tinytsum) {
+ biffAddf(LIMN,
+ "%s: scale %g led to tiny unnormalized tangent weight sum %g; "
+ "purpose of scale is to do blurring but scale %g won't do that",
+ me, scale, tsum, scale);
+ return 1;
+ }
+ for (ii = 0; ii < wlen; ii++) {
+ fctx->vw[ii] /= vsum;
+ fctx->tw[ii] /= tsum;
+ if (fctx->verbose) {
+ printf("%s: ii=%3u v=%0.17g t=%0.17g\n", me, ii, fctx->vw[ii],
+ fctx->tw[ii]);
+ }
+ }
+ } /* else scale > 0 */
+
+ return 0;
+}
+
+/*
+******** limnCBFCtxPrep
+**
+** checks the things that are going to be passed around a lot,
+** and makes call to initialize buffers inside fctx
+*/
+int /* Biff: 1 */
+limnCBFCtxPrep(limnCBFCtx *fctx, const limnCBFPoints *lpnt) {
+ static const char me[] = "limnCBFCtxPrep";
+
+ if (!(fctx && lpnt)) {
+ biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
- if (fctx->vw) free(fctx->vw);
- if (fctx->tw) free(fctx->tw);
- fctx->vw = AIR_CALLOC(len, double);
- fctx->tw = AIR_CALLOC(len, double);
- if (!(fctx->vw && fctx->tw)) {
- biffAddf(LIMN, "%s: couldn't allocate weight buffers (len %u)", me, len);
+ if (limnCBFPointsCheck(lpnt)) {
+ biffAddf(LIMN, "%s: problem with points", me);
return 1;
}
- fctx->wLen = len;
- /* normalization intent:
- 1 = sum_i(vw[|i|]) for i=-(len-1)...len-1
- 1 = sum_i(tw[i]) for i=0...len-1
- */
- vsum = tsum = 0;
- for (ii = 0; ii < len; ii++) {
- double kw;
- kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
- vsum += (!ii ? 1 : 2) * (fctx->vw[ii] = kw);
- tsum += (fctx->tw[ii] = ii * kw);
+ if (!(fctx->epsilon > 0)) {
+ biffAddf(LIMN, "%s: need positive epsilon (not %g)", me, fctx->epsilon);
+ return 1;
}
- for (ii = 0; ii < len; ii++) {
- fctx->vw[ii] /= vsum;
- fctx->tw[ii] /= tsum;
- /* printf("!%s: %u %g %g\n", me, ii, fctx->vw[ii], fctx->tw[ii]); */
+ if (!(fctx->scale >= 0)) {
+ biffAddf(LIMN, "%s: need non-negative scale (not %g)", me, fctx->scale);
+ return 1;
}
- return 0;
-}
-
-limnCBFCtx * /* Biff: NULL */
-limnCBFCtxNew(unsigned int pointNum, double scale) {
- static const char me[] = "limnCBFCtxNew";
- limnCBFCtx *ret;
- if (!(pointNum >= 4)) {
- biffAddf(LIMN, "%s: got tiny #points %u", me, pointNum);
- return NULL;
+ if (!(fctx->nrpCap > 0)) {
+ biffAddf(LIMN, "%s: need positive nrpCap (not %g)", me, fctx->nrpCap);
+ return 1;
}
- if (!(scale >= 0)) {
- biffAddf(LIMN, "%s: need scale >= 0 (not %g)", me, scale);
- return NULL;
+ if (!(0 < fctx->nrpIota && fctx->nrpIota <= 1)) {
+ biffAddf(LIMN, "%s: nrpIota (%g) must be in (0,1]", me, fctx->nrpIota);
+ return 1;
}
- ret = AIR_CALLOC(1, limnCBFCtx);
- if (!ret) {
- biffAddf(LIMN, "%s: allocation failure?", me);
- return NULL;
+ if (!(1 <= fctx->nrpPsi)) {
+ biffAddf(LIMN, "%s: nrpPsi (%g) must be >= 1", me, fctx->nrpPsi);
+ return 1;
}
- ctxInit(ret);
- if (ctxBuffersSet(ret, pointNum, scale)) {
- biffAddf(LIMN, "%s: trouble allocating buffers", me);
- free(ret);
- return NULL;
+ if (!(fctx->nrpDeltaThresh > 0)) {
+ biffAddf(LIMN, "%s: need positive nrpDeltaThresh (not %g) ", me,
+ fctx->nrpDeltaThresh);
+ return 1;
}
- ret->scale = scale;
+ if (!(fctx->alphaMin > 0)) {
+ biffAddf(LIMN, "%s: need positive alphaMin (not %g) ", me, fctx->alphaMin);
+ return 1;
+ }
+ if (!(fctx->detMin > 0)) {
+ biffAddf(LIMN, "%s: need positive detMin (not %g) ", me, fctx->detMin);
+ return 1;
+ }
+ {
+ 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);
+ return 1;
+ }
+ }
+ if (ctxBuffersSet(fctx, lpnt->num)) {
+ biffAddf(LIMN, "%s: trouble setting up buffers", me);
+ return 1;
+ }
- return ret;
+ return 0;
}
-limnCBFCtx * /* Biff: nope */
-limnCBFCtxNix(limnCBFCtx *fctx) {
- if (fctx) {
- if (fctx->uu) free(fctx->uu);
- if (fctx->vw) free(fctx->vw);
- if (fctx->tw) free(fctx->tw);
- free(fctx);
- }
- return NULL;
-}
-
/* CB0, CB1, CB2, CB3 = degree 3 Bernstein polynomials, for *C*ubic
*B*ezier curves, and their derivatives D0, D1, D2 (not using any
nice recursion properties for evaluation, oh well) */
@@ -339,144 +488,179 @@
return;
}
-/*
-** limnCBFFindVT: Find endpoint vertex vv and tangent tt (constraints for spline fitting)
-** from the given points lpnt at offset index ofi within index range [loi,hii]
-** (e.g. ofi=1 means looking at lpnt coord index loi+1). The tangent direction
-** dir controls which points are looked at:
-** >0: considering only ofi and higher-index vertices,
-** 0: for tangent centered at ofi, using lower- and higher-index vertices
-** <0: considering only ofi and lower-index vertices
-** For >0 and 0: the tangent points towards the positions of higher-
-** index vertices. For <0, it points the other way.
-** The only point indices accessed will be in [loi,hii]; this is what
-** enforces the possible corner-ness of those indices (which prevents
-** vertices past corners influencing how vv or tt are found)
-*/
-int /* Biff: 1 */
-limnCBFFindVT(double vv[2], double tt[2], const limnCBFCtx *fctx,
- const limnCBFPoints *lpnt, uint _loi, uint _hii, uint _ofi, int dir) {
- static const char me[] = "limnCBFFindVT";
- double len;
- /* we use here (signed) int for things that might seem better as uint, but that's
- because of the frequent need to handle how indices loop around in loops */
- int loi, hii, ofi, /* int versions of given args */
- pNum, /* total number of points in lpnts */
- sgsz, /* "segment" size: number of points in [loi,hii]
- (but not in sense of the limnCBFSeg specifically) */
- icent, iplus, imnus; /* icent is the actual data index corresponding to _loi + _ofi;
- it is used for both the scale==0 and scale>0 cases;
- iplus and imnus are only needed with scale==0, but too annoying
- to break those out into that specific branch */
+/* cheesy macro as short-hand to access either pp or ppOwn */
+#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
- if (!(/* vv can be NULL */ tt && fctx && lpnt)) {
- biffAddf(LIMN, "%s: got NULL pointer", me);
+/* error-checked index processing for limnCBFFindVT and maybe others */
+static int /* Biff: 1 */
+idxPrep(int *sloP, int *shiP, int *loopyP, const limnCBFPoints *lpnt, uint loi,
+ uint hii) {
+ static const char me[] = "idxPrep";
+ int slo, shi, loopy, spanlen;
+
+ *sloP = *shiP = 10 * lpnt->num; /* initialize to bogus indices */
+ if (!(loi < lpnt->num && hii < lpnt->num)) {
+ biffAddf(LIMN, "%s: loi %u, hii %u not both < #points %u", me, loi, hii, lpnt->num);
return 1;
}
- if (!(_loi < lpnt->num && _hii < lpnt->num)) {
- biffAddf(LIMN, "%s: _loi %u or _hii %u too high for %u points", me, _loi, _hii,
- lpnt->num);
+ if (loi == hii && hii != 0) {
+ biffAddf(LIMN,
+ "%s: can only have loi == hii if both 0 (not %u), to signify no bounds in "
+ "point loop",
+ me, loi);
+ return 1;
}
- /* now both _loi and _hii are valid indices in [0,..,lpnt->num-1] */
- if (_loi == _hii) {
- biffAddf(LIMN, "%s: got _loi==_hii %u", me, _loi);
+ slo = AIR_INT(loi);
+ shi = AIR_INT(hii);
+ if (lpnt->isLoop) {
+ loopy = (0 == loi && 0 == hii);
+ if (hii < loi) {
+ shi += lpnt->num;
+ }
+ } else {
+ if (0 == loi && 0 == hii) {
+ biffAddf(LIMN, "%s: can only have loi == hii == 0 with point loop", me);
+ return 1;
+ }
+ if (hii < loi) {
+ biffAddf(LIMN, "%s: can only have hii (%u) < loi (%u) in a point loop", me, hii,
+ loi);
+ return 1;
+ }
+ loopy = AIR_FALSE;
+ }
+ spanlen = shi - slo + 1;
+ if (spanlen <= 1 && !loopy) {
+ biffAddf(LIMN, "%s: [%u,%u]->[%d,%d] span length %d <= 1 but not in loop", me, loi,
+ hii, slo, shi, spanlen);
return 1;
}
- if (_hii < _loi && !lpnt->isLoop) {
- biffAddf(LIMN, "%s: _hii %u < _loi %u sensible only in point loop", me, _hii, _loi);
+ /* all's well, set output values */
+ *sloP = slo;
+ *shiP = shi;
+ *loopyP = loopy;
+ return 0;
+}
+
+static void
+subnorm2(double dir[2], const double aa[2], const double bb[2]) {
+ double len;
+ ELL_2V_SUB(dir, aa, bb); /* dir = aa - bb */
+ ELL_2V_NORM(dir, dir, len); /* normalize(dir) */
+}
+
+/* limnCBFFindTVT: Find constraints for spline fitting: incoming/left tangent lt, center
+or endpoint vertex vv, outgoing/right tangent rt; any but not all can be NULL. These are
+computed from the given points lpnt, at offset index ofi within index range loi, hii
+and only that range: that range is probably delimited by corners, and we have to be blind
+to anything past the corners on either side of us (except if loi==hii==0, in which case
+we can look at all the points in a loop).
+
+NOTE: this assumes that limnCBFCtxPrep(fctx, lpnt) was called without error!
+It (via ctxBuffersSet) allocates things that we depend on here
+*/
+int /* Biff: 1 */
+limnCBFFindTVT(double lt[2], double vv[2], double rt[2], const limnCBFCtx *fctx,
+ const limnCBFPoints *lpnt, uint loi, uint hii, uint ofi, int oneSided) {
+ static const char me[] = "limnCBFFindTVT";
+ /* we use here (signed) int for things that might seem better as uint, but it
+ simplifies implementing arithmetic and comparisons given how indices wrap around in
+ point loops */
+ int slo, /* signed version of loi */
+ shi, /* signed version of hii, but shi = hii + lpnt->num if hii < loi in loop */
+ sof, /* signed versions of ofi */
+ loopy, /* lpnt->isLoop && loi == 0 && hii = 0, i.e. there are no bounds on indices */
+ pnum, /* total number of points in lpnts */
+ spanlen, /* span length: number of points in [loi,hii] */
+ icent, iplus,
+ imnus; /* icent is the actual data index corresponding to loi + ofi; it is used for
+ both the scale==0 and scale>0 cases; iplus and imnus are only needed with
+ scale==0, but too annoying to break those out into that specific branch */
+
+ if (!((lt || vv || rt) && fctx && lpnt)) {
+ biffAddf(LIMN, "%s: got NULL pointer (or too many NULL pointers)", me);
return 1;
}
- /* we proceed with either _loi < _hii or (_hii < _loi and) lpnt->isLoop
- i.e. now _hii < _loi (and soon hii < loi) implies lpnt->isLoop */
- dir = airSgn(dir);
- pNum = AIR_INT(lpnt->num);
- loi = AIR_INT(_loi);
- hii = AIR_INT(_hii);
- sgsz = (hii < loi ? pNum : 0) + hii - loi + 1;
- if (sgsz <= 1) {
- biffAddf(LIMN, "%s: sgsz %d <= 1", me, sgsz);
+ /* so: each of lt, vv, rt can be NULL (they just can't be all NULL) */
+ if (idxPrep(&slo, &shi, &loopy, lpnt, loi, hii)) {
+ biffAddf(LIMN, "%s: trouble with loi %u or hii %u", me, loi, hii);
return 1;
}
- /* now sgsz >= 2 */
- if (!(_ofi < AIR_UINT(sgsz))) {
- biffAddf(LIMN, "%s: _ofi %u too high for segment size %d", me, _ofi, sgsz);
+ spanlen = shi - slo + 1;
+ pnum = AIR_INT(lpnt->num);
+
+ /* now:
+ 0 == slo == shi implies lpnt->isLoop (and this is called "loopy")
+ slo == shi != 0 is always impossible
+ always: slo < shi (even if given hii < loi), and thus any indices in range [slo,shi]
+ need to be mod'd with pnum before indexing into PP(lpnt)
+ spanlen >= 2 */
+ if (!loopy && !(ofi < AIR_UINT(spanlen))) {
+ biffAddf(LIMN,
+ "%s: ofi %u too high for [%u,%u]->[%d,%d] span length %d (not in loop)", me,
+ ofi, loi, hii, slo, shi, spanlen);
return 1;
}
- /* now _ofi is a valid index in [0,..,sgsz-1] */
- ofi = AIR_INT(_ofi);
- icent = loi + ofi;
+ /* now ofi is a valid index in [0,..,spanlen-1] */
+ sof = AIR_INT(ofi);
+ icent = slo + sof;
iplus = icent + 1;
imnus = icent - 1;
- if (lpnt->isLoop) {
- icent = AIR_MOD(icent, pNum);
- iplus = AIR_MOD(iplus, pNum);
- imnus = AIR_MOD(imnus, pNum);
- } else {
- icent = AIR_CLAMP(loi, icent, hii);
- iplus = AIR_CLAMP(loi, iplus, hii);
- imnus = AIR_CLAMP(loi, imnus, hii);
+ if (!loopy) {
+ /* this is the code that motivated if (hii < loi) shi += pnum;
+ otherwise clamping is too annoying */
+ icent = AIR_CLAMP(slo, icent, shi);
+ iplus = AIR_CLAMP(slo, iplus, shi);
+ imnus = AIR_CLAMP(slo, imnus, shi);
}
+ /* now map to actual indices */
+ icent = AIR_MOD(icent, pnum);
+ iplus = AIR_MOD(iplus, pnum);
+ imnus = AIR_MOD(imnus, pnum);
if (0 == fctx->scale) {
- const double *xy, *xyP, *xyM;
- int mi, ci, pi;
+ /* DIM=2 through-out */
+ const double *xyC = PP(lpnt) + 2 * icent, *xyP, *xyM;
if (vv) {
- ELL_2V_COPY(vv, PP(lpnt) + 2 * icent); /* DIM=2 */
+ ELL_2V_COPY(vv, xyC);
}
- switch (dir) {
- case 1:
- pi = iplus;
- mi = icent;
- break;
- case 0:
- pi = iplus;
- mi = imnus;
- break;
- case -1:
- /* mi and pi switched to point other way */
- mi = icent;
- pi = imnus;
- break;
+ /* printf("!%s: iplus=%u imnus=%u xy=%g %g\n", me, iplus, imnus, xy[0], xy[1]); */
+ xyP = PP(lpnt) + 2 * iplus;
+ xyM = PP(lpnt) + 2 * imnus;
+ if (rt) {
+ subnorm2(rt, xyP, oneSided ? xyC : xyM);
}
- if (pi == mi) {
- biffAddf(LIMN, "%s: imnus,icent,iplus=%d,%d,%d --dir=%d--> mi == pi %d", me, imnus,
- icent, iplus, dir, mi);
- return 1;
+ if (lt) {
+ subnorm2(lt, xyM, oneSided ? xyC : xyP);
}
- /* printf("!%s: iplus=%u imnus=%u xy=%g %g\n", me, iplus, imnus, xy[0], xy[1]); */
- xyP = PP(lpnt) + 2 * pi; /* DIM=2 and following */
- xyM = PP(lpnt) + 2 * mi;
- ELL_2V_SUB(tt, xyP, xyM);
- ELL_2V_NORM(tt, tt, len);
/* printf("!%s: xyP=%g %g xyM=%g %g len=%g tt=%g %g\n", me, xyP[0], xyP[1],
xyM[0], xyM[1], len, tt[0], tt[1]); */
} else {
/* using scale>0 for endpoint and tangent estimation */
- /* regardless of dir, we compute average positions for points
- centered around loi + ofi (posC), and for lower,higher indices (posM,posP) */
- double posP[2] = {0, 0}, posC[2] = {0, 0}, posM[2] = {0, 0};
+ /* for simplicity: regardless of dir, we compute average positions for points
+ centered around loi + ofi (posC), and for lower/higher indices (posM/posP) */
+ double posM[2] = {0, 0}, posC[2] = {0, 0}, posP[2] = {0, 0};
const double *vw = fctx->vw;
const double *tw = fctx->tw;
- int smax = (int)fctx->wLen - 1, /* bound of loop index */
- ci; /* loop through [-smax,smax] */
+ int lim = (int)fctx->wLen - 1, /* limit on loop index */
+ ci; /* loops through [-lim,lim] */
if (!(vw && tw)) {
biffAddf(LIMN, "%s: fctx internal buffers vw and tw not both allocated", me);
return 1;
}
- /* various signed indices */
- /* printf("!%s: ofi = %d, dir=%d\n", me, ofi, dir); */
- for (ci = -smax; ci <= smax; ci++) {
+ if (tw[0] != 0) {
+ biffAddf(LIMN, "%s: first tangent weight fctx->tw[0] %g not zero", me, tw[0]);
+ return 1;
+ }
+ for (ci = -lim; ci <= lim; ci++) {
uint wi = abs(ci); /* weight index into vw, tw */
- int di = loi + ofi + ci; /* signed index into data */
+ int di = slo + sof + ci; /* signed index into data */
const double *xy;
- if (lpnt->isLoop) {
- di = AIR_MOD(di, pNum);
- } else {
- di = AIR_CLAMP(loi, di, hii);
- }
+ if (!loopy) di = AIR_CLAMP(slo, di, shi);
+ di = AIR_MOD(di, pnum);
xy = PP(lpnt) + 2 * di;
ELL_2V_SCALE_INCR(posC, vw[wi], xy);
- /* printf("!%s: (ci=%d/wi=%u) posC += %g*(%g %g) --> %g %g\n", me, ci, wi, vw[wi],
- xy[0], xy[1], posC[0], posC[1]); */
+ /* printf("!%s: (ci=%d/wi=%u) posC += %g*(%g %g) --> %g %g\n", me, ci, wi,
+ vw[wi], xy[0], xy[1], posC[0], posC[1]); */
if (ci < 0) {
ELL_2V_SCALE_INCR(posM, tw[wi], xy);
/* printf("!%s: (ci=%d/wi=%u) posM += %g*(%g %g) --> %g %g\n", me, ci, wi,
@@ -488,789 +672,149 @@
tw[wi], xy[0], xy[1], posP[0], posP[1]); */
}
}
- switch (dir) {
- case 1:
- ELL_2V_SUB(tt, posP, posC);
- break;
- case 0:
- ELL_2V_SUB(tt, posP, posM);
- break;
- case -1:
- ELL_2V_SUB(tt, posM, posC);
- break;
+ {
+ /* limit distance from chosen (x,y) datapoint to posC to be (HEY harcoded) 95% of
+ fctx->epsilon. Being allowed to be further away can cause annoyances (for GLK in
+ some early stage of debugging) */
+ double off[2], offlen, okoff = 0.95 * fctx->epsilon; /* DIM=2 throughout */
+ const double *xy = PP(lpnt) + 2 * icent; /* center vertex in given data */
+ ELL_2V_SUB(off, posC, xy); /* off = posC - xy, from given to computed */
+ ELL_2V_NORM(off, off, offlen);
+ offlen = AIR_MIN(okoff, offlen);
+ /* difference between chosen (x,y) datapoint and spline endpoint
+ can be in any direction, but we limit the length */
+ ELL_2V_SCALE_ADD2(posC, 1, xy, offlen, off);
}
- ELL_2V_NORM(tt, tt, len);
+ if (lt) {
+ subnorm2(lt, posM, oneSided ? posC : posP);
+ }
+ if (rt) {
+ subnorm2(rt, posP, oneSided ? posC : posM);
+ }
if (vv) {
ELL_2V_COPY(vv, posC);
- /* some post-proceessing of computed spline endpoint */
- double off[2], pp[2], operp, okoff;
- const double *xy;
- /* DIM=2 */
- xy = PP(lpnt) + 2 * icent; /* center vertex in given data */
- ELL_2V_SET(pp, tt[1], -tt[0]); /* pp is perpendicular to computed tt */
- ELL_2V_SUB(off, vv, xy); /* off = vv - xy, from given to computed */
- operp = ELL_2V_DOT(off, pp);
- /* limit distance from chosen (x,y) datapoint to spline endpoint to be
- (HEY harcoded) 95% of fctx->distMin. Being allowed to be further away
- can cause annoyances */
- okoff = 0.95 * fctx->distMin;
- operp = AIR_CLAMP(-okoff, operp, okoff);
- /* constrain difference between chosen (x,y) datapoint and spline
- endpoint to be perpendicular to estimated tangent */
- ELL_2V_SCALE_ADD2(vv, 1, xy, operp, pp);
}
}
return 0;
}
-static int /* Biff: 1 */
-setVTTV(int *given, /* */
- double vv0[2], double tt1[2], double tt2[2], double vv3[2], /* */
- const double _vv0[2], const double _tt1[2], const double _tt2[2],
- const double _vv3[2], /* */
- const limnCBFCtx *fctx, const limnCBFPoints *lpnt, uint loi, uint hii) {
- static const char me[] = "setVTTV";
-
- /* either: all the _vv0, _tt1, _tt2, _vv3 can be NULL, or none are NULL */
- if (_vv0 && _tt1 && _tt2 && _vv3) {
- /* copy the given endpoint geometry */
- ELL_2V_COPY(vv0, _vv0);
- ELL_2V_COPY(tt1, _tt1);
- ELL_2V_COPY(tt2, _tt2);
- ELL_2V_COPY(vv3, _vv3);
- if (given) {
- *given = AIR_TRUE;
- }
- } else {
- /* not given v, t, t, v values, so we compute them */
- if (_vv0 || _tt1 || _tt2 || _vv3) {
- biffAddf(LIMN, "%s: either all or none of _vv0,_tt1,_tt2,_vv3 should be NULL", me);
- return 1;
- }
- if (lpnt->isLoop) {
- limnCBFFindVT(vv0, tt1, fctx, lpnt, loi, hii, 0, 0);
- ELL_2V_COPY(vv3, vv0);
- ELL_2V_SCALE(tt2, -1, tt1);
- } else {
- limnCBFFindVT(vv0, tt1, fctx, lpnt, loi, hii, 0, +1);
- limnCBFFindVT(vv3, tt2, fctx, lpnt, loi, hii, hii - loi, -1);
- }
- if (given) {
- *given = AIR_FALSE;
- }
- }
- return 0;
-}
-
/*
-** (from paper page 620) solves for the alpha that minimize squared error
-** between xy[i] and Q(uu[i]) where Q(t) is cubic Bezier spline through vv0,
-** vv0 + alpha[0]*tt1, vv3 + alpha[1]*tt2, and vv3.
+******** limnCBFit
**
-** There are various conditions where the generated spline ignores the
-** xy array and instead is what one could call a "simple arc" (with
-** control points at 1/3 and 2/3 the distance between the end points):
-** - having only two points (xy contains only the end points)
-** - the determinant of the 2x2 matrix that is inverted to solve
-** for alpha is too close to zero (this test was not part of the
-** author's code)
-** - the solved alphas are not convincingly positive
-** This function is the only place where the "simple arc" is
-** generated, and generating the simple arc is not actually an error
-** or problem: if it is bad at fitting the data (as determined by
-** finddist) then it may be subdivided, and that's ok. What GLK hasn't
-** thought through is: what is the interaction of nrp iterations and
-** findalpha generating the simple arc on some but not all iterations
-** (possibly unstable?)
+** top-level function for fitting cubic beziers to given points
*/
-static void
-findalpha(double alpha[2], limnCBFCtx *fctx, /* must be non-NULL */
- const double vv0[2], const double tt1[2], const double tt2[2],
- const double vv3[2], const limnCBFPoints *lpnt, uint loi, uint hii) {
- static const char me[] = "findalpha";
- uint ii, pNum;
- double det;
-
- pNum = pntNum(lpnt, loi, hii);
- if (pNum > 2) {
- double xx[2], m11, m12, m22, MM[4], MI[4]; /* DIM=2 throughout this */
- const double *uu = fctx->uu;
- xx[0] = xx[1] = m11 = m12 = m22 = 0;
- for (ii = 0; ii < pNum; ii++) {
- const double *xy;
- double bb[4], Ai1[2], Ai2[2], Pi[2], dmP[2];
- double ui = uu[ii];
- VCBD0(bb, ui);
- ELL_2V_SCALE(Ai1, bb[1], tt1);
- ELL_2V_SCALE(Ai2, bb[2], tt2);
- /* GLK using "m" and "M" instead author's "C". Note that Ai1 and
- Ai2 are scalings of (nominally) unit-length tt1 and tt2 by
- evaluations of the spline basis functions, so they (and the M
- computed from them, and det(M)), are invariant w.r.t over-all
- rescalings of the data points */
- m11 += ELL_2V_DOT(Ai1, Ai1);
- m12 += ELL_2V_DOT(Ai1, Ai2);
- m22 += ELL_2V_DOT(Ai2, Ai2);
- ELL_2V_SCALE_ADD2(Pi, bb[0] + bb[1], vv0, bb[2] + bb[3], vv3);
- xy = pntCrd(lpnt, loi, ii);
- ELL_2V_SUB(dmP, xy, Pi);
- xx[0] += ELL_2V_DOT(dmP, Ai1);
- xx[1] += ELL_2V_DOT(dmP, Ai2);
- }
- ELL_4V_SET(MM, m11, m12, m12, m22);
- ELL_2M_INV(MI, MM, det);
- ELL_2MV_MUL(alpha, MI, xx);
- } else { /* pNum <= 2 */
- det = 1; /* bogus but harmless */
- alpha[0] = alpha[1] = 0; /* trigger simple arc code */
- }
- /* test if we should return simple arc */
- if (!(AIR_EXISTS(det) && AIR_ABS(det) > fctx->detMin
- && alpha[0] > (fctx->lenF2L) * (fctx->alphaMin)
- && alpha[1] > (fctx->lenF2L) * (fctx->alphaMin))) {
- if (fctx->verbose) {
- printf("%s: bad |det| %g (vs %g) or alpha %g,%g (vs %g*%g) "
- "--> simple arc\n",
- me, AIR_ABS(det), fctx->detMin, alpha[0], alpha[1], fctx->lenF2L,
- fctx->alphaMin);
- }
- /* generate simple arc: set both alphas to 1/3 of distance from
- first to last point, but also handle non-unit-length tt1 and
- tt2 */
- alpha[0] = fctx->lenF2L / (3 * ELL_2V_LEN(tt1));
- alpha[1] = fctx->lenF2L / (3 * ELL_2V_LEN(tt2));
- } else {
- if (fctx->verbose > 1) {
- printf("%s: all good: det %g, alpha %g,%g\n", me, det, alpha[0], alpha[1]);
- }
- }
- fctx->alphaDet = det;
- return;
-}
-
-/*
-** using Newton iterations to try to find a better places at which
-** to evaluate the spline in order to match the given points xy
-*/
-static double
-reparm(const limnCBFCtx *fctx, /* must be non-NULL */
- const double alpha[2], const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2], const limnCBFPoints *lpnt, uint loi,
- uint hii) {
- static const char me[] = "reparm";
- uint ii, pNum;
- double vv1[2], vv2[2], delta, maxdelu;
- double *uu = fctx->uu;
-
- pNum = pntNum(lpnt, loi, hii);
- assert(pNum >= 3);
- /* average u[i+1]-u[i] is 1/(pNum-1) */
- maxdelu = fctx->nrpDeltaMax / (pNum - 1);
- ELL_2V_SCALE_ADD2(vv1, 1, vv0, alpha[0], tt1);
- ELL_2V_SCALE_ADD2(vv2, 1, vv3, alpha[1], tt2);
- delta = 0;
- /* only changing parameterization of interior points,
- not the first (ii=0) or last (ii=pNum-1) */
- for (ii = 1; ii < pNum - 1; ii++) {
- double numer, denom, delu, df[2], ww[4], tt, Q[2], QD[2], QDD[2];
- const double *xy;
- tt = uu[ii];
- CBD0(Q, vv0, vv1, vv2, vv3, tt, ww);
- CBD1(QD, vv0, vv1, vv2, vv3, tt, ww);
- CBD2(QDD, vv0, vv1, vv2, vv3, tt, ww);
- xy = pntC...
[truncated message content] |
|
From: <kin...@us...> - 2024-06-21 07:45:02
|
Revision: 7167
http://sourceforge.net/p/teem/code/7167
Author: kindlmann
Date: 2024-06-21 07:44:59 +0000 (Fri, 21 Jun 2024)
Log Message:
-----------
synching with source
Modified Paths:
--------------
teem/trunk/python/cffi/biffdata/limn.csv
teem/trunk/python/cffi/cdef/cdef_limn.h
teem/trunk/python/cffi/teem.py
Modified: teem/trunk/python/cffi/biffdata/limn.csv
===================================================================
--- teem/trunk/python/cffi/biffdata/limn.csv 2024-06-21 07:43:16 UTC (rev 7166)
+++ teem/trunk/python/cffi/biffdata/limn.csv 2024-06-21 07:44:59 UTC (rev 7167)
@@ -60,11 +60,14 @@
limnSplineUpdate,int,1,0,limn,limn/splineMethods.c:422
limnSplineTypeSpecParse,limnSplineTypeSpec *,NULL,0,limn,limn/splineMisc.c:222
limnSplineParse,limnSpline *,NULL,0,limn,limn/splineMisc.c:278
-limnCBFCheck,int,1,0,limn,limn/splineFit.c:589
-limnCBFitSingle,int,1,0,limn,limn/splineFit.c:860
-limnCBFMulti,int,1,0,limn,limn/splineFit.c:951
-limnCBFCorners,int,1,0,limn,limn/splineFit.c:1053
-limnCBFit,int,1,0,limn,limn/splineFit.c:1123
+limnCBFPointsCheck,int,1,0,limn,limn/splineFit.c:73
+limnCBFCtxNew,limnCBFCtx *,NULL,0,limn,limn/splineFit.c:222
+limnCBFFindVT,int,1,0,limn,limn/splineFit.c:357
+limnCBFCtxCheck,int,1,0,limn,limn/splineFit.c:751
+limnCBFitSingle,int,1,0,limn,limn/splineFit.c:925
+limnCBFMulti,int,1,0,limn,limn/splineFit.c:1016
+limnCBFCorners,int,1,0,limn,limn/splineFit.c:1116
+limnCBFit,int,1,0,limn,limn/splineFit.c:1184
limnObjectWorldHomog,int,1,0,limn,limn/transform.c:25
limnObjectFaceNormals,int,1,0,limn,limn/transform.c:47
limnObjectSpaceTransform,int,1,0,limn,limn/transform.c:210
Modified: teem/trunk/python/cffi/cdef/cdef_limn.h
===================================================================
--- teem/trunk/python/cffi/cdef/cdef_limn.h 2024-06-21 07:43:16 UTC (rev 7166)
+++ teem/trunk/python/cffi/cdef/cdef_limn.h 2024-06-21 07:44:59 UTC (rev 7167)
@@ -451,11 +451,12 @@
******** limnCBFSeg
**
** how one cubic Bezier spline segment is represented for limnCBF functions
+** (using DIM=2 to mark places where the 2D-ness of the code surfaces )
*/
typedef struct {
double xy[8]; /* four control points of cubic Bezier:
x0, y0, x1, y1, x2, y2, x3, y3
- 0 1 2 3 4 5 6 7 */
+ 0 1 2 3 4 5 6 7 DIM=2 */
int corner[2]; /* corner[0,1] non-zero if xy[0,3] are corner vertices;
segments otherwise assumed geometrically continuous */
unsigned int pNum; /* (if non-zero) this segment approximates pNum points */
@@ -472,13 +473,9 @@
int isLoop; /* path is closed loop */
} limnCBFPath;
/*
-******** limnCBFContext
+******** limnCBFCtx
**
-** The bag of state for limnCBF functions. Callers of limnCBF functions do not
-** need to worry about the dynamically allocated things within (so: no
-** limnCBFContextNew or limnCBFContextNix), but a limnCBFContext variable
-** should be initialized with limnCBFContextInit() in order to set default
-** parameters, before passing to limnCBF functions.
+** The bag of state for limnCBF functions.
**
** note: "nrp" = Newton-based Re-Parameterization of where the given points
** fall along the spline, the iterative process inside limnCBFSingle
@@ -489,38 +486,31 @@
cornNMS; /* non-minimal-suppression of corners: accept as
corners only those with locally minimal angle */
unsigned int nrpIterMax; /* max # iters of nrp */
- double scale, /* scale (in sense of nrrdKernelDiscreteGaussian)
- at which to estimate spline endpoints and
- tangents; scale=0 means the endpoints are
- exactly on vertices, and tangents are from
- the smallest-support finite differences */
- distMin, /* min distance to given points: this controls
- both splitting done by limnCBFMulti, and nrp
- within limnCBFSingle */
- nrpDeltaMax, /* in nrp, capping parameterization change to this
- scaling of average u[i+1]-u[i]. This wasn't in
- author's original code (so their idea of doing
- at most ~5 iters of nrp may no longer hold), but
- it can help stabilize things */
- nrpDistScl, /* scaling on distMin to use when testing distance
- during nrp; setting this < 1 means that nrp
- tries to be more stringent that the overall
- fitting, but with the benefit of sometimes
- being smarter about where to split, when that
- is needed */
- nrpPsi, /* don't even try nrp if max dist is bigger than
- nrpPsi*distMin, instead just subdivide */
- nrpDeltaMin, /* min total parameterization change by nrp */
- alphaMin, /* alpha can't be negative, and we enforce
- distinct positivity to ensure that spline
- doesn't slow down too much near endpoints */
- detMin, /* absolute value of determinant of 2x2 matrix
- to invert can't below this */
- cornAngle; /* 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. Or,
- if 0, no effort is made to detect corners. */
+ double scale, /* scale (in sense of nrrdKernelDiscreteGaussian) at which to estimate
+ spline endpoints and tangents; scale=0 means the endpoints are
+ exactly on vertices, and tangents are from the smallest-support
+ finite differences. This is the ONLY floating point that should be set
+ by a method (limnCBFScaleSet); the rest can be set directly. */
+ distMin, /* min distance to given points: this controls both splitting done by
+ limnCBFMulti, and nrp within limnCBFSingle */
+ nrpDeltaMax, /* in nrp, capping parameterization change to this scaling of average
+ u[i+1]-u[i]. This wasn't in author's original code (so their idea of
+ doing at most ~5 iters of nrp may no longer hold), but it can help
+ stabilize things */
+ nrpDistScl, /* scaling on distMin to use when testing distance during nrp; setting
+ this < 1 means that nrp tries to be more stringent than the overall
+ fitting, but with the benefit of sometimes being smarter about where
+ to split, when that is needed */
+ nrpPsi, /* don't even try nrp if max dist is bigger than nrpPsi*distMin, instead just
+ subdivide */
+ nrpDeltaMin, /* min total parameterization change by nrp */
+ 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; /* 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. Or, if 0, no effort is made
+ to detect corners. */
/* ----------- internal --------- */
double *uu, /* buffer used for nrp */
*vw, /* weights for endpoint vertex calculation */
@@ -540,12 +530,12 @@
2: DM < dist <= fD
3: fD < dist
where
+ nD = nrpDistScl*distMin,
DM = distMin,
- nD = nrpDistScl*distMin,
fD = nrpPsi*distMin: */
-} limnCBFContext;
+} limnCBFCtx;
/*
-******** limnPoints
+******** limnCBFPoints
**
** a container for 1D array of points; currently used for limnCBF functions
** Both pp and ppOwn can point to the array of point locations, but exactly
@@ -552,9 +542,11 @@
** one of pp and ppOwn can be non-NULL.
**
** NOTE: For now, point data is only double (not float), and only in 2D (not
-** 3D), but if this becomes more general, that generality will be inside here
+** 3D), but if this becomes more general, that generality will be inside here.
+** For time being DIM=2 tags locations where 2D-ness is explicit in code.
*/
typedef struct {
+ /* assuming DIM=2: 2 values per logical element pp */
const double *pp; /* point coords, we do not own buffer */
double *ppOwn; /* point coords, we DO own buffer */
unsigned int num; /* how many points */
@@ -561,7 +553,7 @@
int isLoop; /* points form a loop: logical indices into coord
array are . . . num-2, num-1, 0, 1, . . .
and index 0 is effectively arbitrary */
-} limnPoints;
+} limnCBFPoints;
/* defaultsLimn.c */
extern const int limnPresent;
extern const char *const limnBiffKey;
@@ -819,27 +811,33 @@
extern int limnSplineSample(Nrrd *nout, limnSpline *spline, double minT, size_t M,
double maxT);
/* splineFit.c */
-extern limnPoints *limnPointsNew(const double *pp, unsigned int nn, int isLoop);
-extern limnPoints *limnPointsNix(limnPoints *lpnt);
+extern limnCBFPoints *limnCBFPointsNew(const double *pp, unsigned int nn,
+ int isLoop);
+extern limnCBFPoints *limnCBFPointsNix(limnCBFPoints *lpnt);
+extern int limnCBFPointsCheck(const limnCBFPoints *lpnt);
+extern limnCBFCtx *limnCBFCtxNew(unsigned int pointNum, double scale);
+extern limnCBFCtx *limnCBFCtxNix(limnCBFCtx *fctx);
extern void limnCBFSegEval(double *xy, const limnCBFSeg *seg, double tt);
extern limnCBFPath *limnCBFPathNew(void);
extern limnCBFPath *limnCBFPathNix(limnCBFPath *path);
-extern void limnCBFPathSample(double *xy, unsigned int pNum,
+extern void limnCBFPathSample(double *xy, unsigned int pointNum,
const limnCBFPath *path);
-extern void limnCBFContextInit(limnCBFContext *fctx, int outputOnly);
-extern int limnCBFCheck(const limnCBFContext *fctx, const limnPoints *lpnt);
-extern int limnCBFitSingle(double alpha[2], limnCBFContext *fctx,
- const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2],
- const double *xy, unsigned int pNum, int isLoop);
-extern int limnCBFMulti(limnCBFPath *path, limnCBFContext *fctx,
- const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2],
- const limnPoints *lpnt, unsigned int loi, unsigned int hii);
+extern int limnCBFFindVT(double vv[2], double tt[2], const limnCBFCtx *fctx,
+ const limnCBFPoints *lpnt, unsigned int loi,
+ unsigned int hii, unsigned int ofi, int dir);
+extern int limnCBFCtxCheck(const limnCBFCtx *fctx, const limnCBFPoints *lpnt);
+extern int limnCBFitSingle(double alpha[2], limnCBFCtx *fctx, const double vv0[2],
+ const double tt1[2], const double tt2[2],
+ const double vv3[2], const double *xy,
+ unsigned int pointNum, int isLoop);
+extern int limnCBFMulti(limnCBFPath *path, limnCBFCtx *fctx, const double vv0[2],
+ const double tt1[2], const double tt2[2],
+ const double vv3[2], const limnCBFPoints *lpnt,
+ unsigned int loi, unsigned int hii);
extern int limnCBFCorners(unsigned int **cornIdx, unsigned int *cornNum,
- limnCBFContext *fctx, const limnPoints *lpnt);
-extern int limnCBFit(limnCBFPath *path, limnCBFContext *fctx, const double *xy,
- unsigned int pNum, int isLoop);
+ limnCBFCtx *fctx, const limnCBFPoints *lpnt);
+extern int limnCBFit(limnCBFPath *path, limnCBFCtx *fctx, const double *xy,
+ unsigned int pointNum, int isLoop);
/* lpu{Flotsam,. . .}.c */
/* F(clip) \ */
/* F(vwflip) \ */
Modified: teem/trunk/python/cffi/teem.py
===================================================================
--- teem/trunk/python/cffi/teem.py 2024-06-21 07:43:16 UTC (rev 7166)
+++ teem/trunk/python/cffi/teem.py 2024-06-21 07:44:59 UTC (rev 7167)
@@ -293,14 +293,6 @@
'mossSamplerSample': (_equals_one, 0, b'moss', 'moss/sampler.c:195'),
'mossLinearTransform': (_equals_one, 0, b'moss', 'moss/xform.c:140'),
'mossFourPointTransform': (_equals_one, 0, b'moss', 'moss/xform.c:219'),
- 'alanUpdate': (_equals_one, 0, b'alan', 'alan/coreAlan.c:60'),
- 'alanInit': (_equals_one, 0, b'alan', 'alan/coreAlan.c:99'),
- 'alanRun': (_equals_one, 0, b'alan', 'alan/coreAlan.c:453'),
- 'alanDimensionSet': (_equals_one, 0, b'alan', 'alan/methodsAlan.c:104'),
- 'alan2DSizeSet': (_equals_one, 0, b'alan', 'alan/methodsAlan.c:119'),
- 'alan3DSizeSet': (_equals_one, 0, b'alan', 'alan/methodsAlan.c:139'),
- 'alanTensorSet': (_equals_one, 0, b'alan', 'alan/methodsAlan.c:161'),
- 'alanParmSet': (_equals_one, 0, b'alan', 'alan/methodsAlan.c:208'),
'gageContextCopy': (_equals_null, 0, b'gage', 'gage/ctx.c:88'),
'gageKernelSet': (_equals_one, 0, b'gage', 'gage/ctx.c:199'),
'gagePerVolumeAttach': (_equals_one, 0, b'gage', 'gage/ctx.c:398'),
@@ -347,31 +339,6 @@
'gageOptimSigErrorPlotSliding': (_equals_one, 0, b'gage', 'gage/optimsig.c:1253'),
'dyeConvert': (_equals_one, 0, b'dye', 'dye/convertDye.c:351'),
'dyeColorParse': (_equals_one, 0, b'dye', 'dye/methodsDye.c:185'),
- 'baneClipNew': (_equals_null, 0, b'bane', 'bane/clip.c:102'),
- 'baneClipAnswer': (_equals_one, 0, b'bane', 'bane/clip.c:152'),
- 'baneClipCopy': (_equals_null, 0, b'bane', 'bane/clip.c:167'),
- 'baneFindInclusion': (_equals_one, 0, b'bane', 'bane/hvol.c:87'),
- 'baneMakeHVol': (_equals_one, 0, b'bane', 'bane/hvol.c:248'),
- 'baneGKMSHVol': (_equals_null, 0, b'bane', 'bane/hvol.c:447'),
- 'baneIncNew': (_equals_null, 0, b'bane', 'bane/inc.c:251'),
- 'baneIncAnswer': (_equals_one, 0, b'bane', 'bane/inc.c:360'),
- 'baneIncCopy': (_equals_null, 0, b'bane', 'bane/inc.c:375'),
- 'baneMeasrNew': (_equals_null, 0, b'bane', 'bane/measr.c:33'),
- 'baneMeasrCopy': (_equals_null, 0, b'bane', 'bane/measr.c:149'),
- 'baneRangeNew': (_equals_null, 0, b'bane', 'bane/rangeBane.c:89'),
- 'baneRangeCopy': (_equals_null, 0, b'bane', 'bane/rangeBane.c:130'),
- 'baneRangeAnswer': (_equals_one, 0, b'bane', 'bane/rangeBane.c:144'),
- 'baneRawScatterplots': (_equals_one, 0, b'bane', 'bane/scat.c:26'),
- 'baneOpacInfo': (_equals_one, 0, b'bane', 'bane/trnsf.c:29'),
- 'bane1DOpacInfoFrom2D': (_equals_one, 0, b'bane', 'bane/trnsf.c:144'),
- 'baneSigmaCalc': (_equals_one, 0, b'bane', 'bane/trnsf.c:222'),
- 'banePosCalc': (_equals_one, 0, b'bane', 'bane/trnsf.c:253'),
- 'baneOpacCalc': (_equals_one, 0, b'bane', 'bane/trnsf.c:403'),
- 'baneInputCheck': (_equals_one, 0, b'bane', 'bane/valid.c:26'),
- 'baneHVolCheck': (_equals_one, 0, b'bane', 'bane/valid.c:64'),
- 'baneInfoCheck': (_equals_one, 0, b'bane', 'bane/valid.c:106'),
- 'banePosCheck': (_equals_one, 0, b'bane', 'bane/valid.c:144'),
- 'baneBcptsCheck': (_equals_one, 0, b'bane', 'bane/valid.c:179'),
'limnCameraUpdate': (_equals_one, 0, b'limn', 'limn/cam.c:33'),
'limnCameraAspectSet': (_equals_one, 0, b'limn', 'limn/cam.c:130'),
'limnCameraPathMake': (_equals_one, 0, b'limn', 'limn/cam.c:189'),
@@ -433,11 +400,14 @@
'limnSplineUpdate': (_equals_one, 0, b'limn', 'limn/splineMethods.c:422'),
'limnSplineTypeSpecParse': (_equals_null, 0, b'limn', 'limn/splineMisc.c:222'),
'limnSplineParse': (_equals_null, 0, b'limn', 'limn/splineMisc.c:278'),
- 'limnCBFCheck': (_equals_one, 0, b'limn', 'limn/splineFit.c:589'),
- 'limnCBFitSingle': (_equals_one, 0, b'limn', 'limn/splineFit.c:860'),
- 'limnCBFMulti': (_equals_one, 0, b'limn', 'limn/splineFit.c:951'),
- 'limnCBFCorners': (_equals_one, 0, b'limn', 'limn/splineFit.c:1053'),
- 'limnCBFit': (_equals_one, 0, b'limn', 'limn/splineFit.c:1123'),
+ 'limnCBFPointsCheck': (_equals_one, 0, b'limn', 'limn/splineFit.c:73'),
+ 'limnCBFCtxNew': (_equals_null, 0, b'limn', 'limn/splineFit.c:222'),
+ 'limnCBFFindVT': (_equals_one, 0, b'limn', 'limn/splineFit.c:357'),
+ 'limnCBFCtxCheck': (_equals_one, 0, b'limn', 'limn/splineFit.c:751'),
+ 'limnCBFitSingle': (_equals_one, 0, b'limn', 'limn/splineFit.c:925'),
+ 'limnCBFMulti': (_equals_one, 0, b'limn', 'limn/splineFit.c:1016'),
+ 'limnCBFCorners': (_equals_one, 0, b'limn', 'limn/splineFit.c:1116'),
+ 'limnCBFit': (_equals_one, 0, b'limn', 'limn/splineFit.c:1184'),
'limnObjectWorldHomog': (_equals_one, 0, b'limn', 'limn/transform.c:25'),
'limnObjectFaceNormals': (_equals_one, 0, b'limn', 'limn/transform.c:47'),
'limnObjectSpaceTransform': (_equals_one, 0, b'limn', 'limn/transform.c:210'),
@@ -612,21 +582,6 @@
'pullTraceMultiPlotAdd': (_equals_one, 0, b'pull', 'pull/trace.c:704'),
'pullTraceMultiWrite': (_equals_one, 0, b'pull', 'pull/trace.c:1014'),
'pullTraceMultiRead': (_equals_one, 0, b'pull', 'pull/trace.c:1119'),
- 'coilStart': (_equals_one, 0, b'coil', 'coil/coreCoil.c:287'),
- 'coilIterate': (_equals_one, 0, b'coil', 'coil/coreCoil.c:362'),
- 'coilFinish': (_equals_one, 0, b'coil', 'coil/coreCoil.c:407'),
- 'coilVolumeCheck': (_equals_one, 0, b'coil', 'coil/methodsCoil.c:25'),
- 'coilContextAllSet': (_equals_one, 0, b'coil', 'coil/methodsCoil.c:69'),
- 'coilOutputGet': (_equals_one, 0, b'coil', 'coil/methodsCoil.c:200'),
- 'pushOutputGet': (_equals_one, 0, b'push', 'push/action.c:71'),
- 'pushBinProcess': (_equals_one, 0, b'push', 'push/action.c:161'),
- 'pushBinPointAdd': (_equals_one, 0, b'push', 'push/binning.c:180'),
- 'pushRebin': (_equals_one, 0, b'push', 'push/binning.c:197'),
- 'pushStart': (_equals_one, 0, b'push', 'push/corePush.c:183'),
- 'pushIterate': (_equals_one, 0, b'push', 'push/corePush.c:233'),
- 'pushRun': (_equals_one, 0, b'push', 'push/corePush.c:306'),
- 'pushFinish': (_equals_one, 0, b'push', 'push/corePush.c:396'),
- 'pushEnergySpecParse': (_equals_one, 0, b'push', 'push/forces.c:304'),
'miteSample': (_math.isnan, 0, b'mite', 'mite/ray.c:151'),
'miteRenderBegin': (_equals_one, 0, b'mite', 'mite/renderMite.c:63'),
'miteShadeSpecParse': (_equals_one, 0, b'mite', 'mite/shade.c:69'),
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-21 07:43:19
|
Revision: 7166
http://sourceforge.net/p/teem/code/7166
Author: kindlmann
Date: 2024-06-21 07:43:16 +0000 (Fri, 21 Jun 2024)
Log Message:
-----------
finally re-started progress on limnCBF functions
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-06-20 21:03:47 UTC (rev 7165)
+++ teem/trunk/src/limn/limn.h 2024-06-21 07:43:16 UTC (rev 7166)
@@ -514,11 +514,12 @@
******** limnCBFSeg
**
** how one cubic Bezier spline segment is represented for limnCBF functions
+** (using DIM=2 to mark places where the 2D-ness of the code surfaces )
*/
typedef struct {
double xy[8]; /* four control points of cubic Bezier:
x0, y0, x1, y1, x2, y2, x3, y3
- 0 1 2 3 4 5 6 7 */
+ 0 1 2 3 4 5 6 7 DIM=2 */
int corner[2]; /* corner[0,1] non-zero if xy[0,3] are corner vertices;
segments otherwise assumed geometrically continuous */
unsigned int pNum; /* (if non-zero) this segment approximates pNum points */
@@ -537,13 +538,9 @@
} limnCBFPath;
/*
-******** limnCBFContext
+******** limnCBFCtx
**
-** The bag of state for limnCBF functions. Callers of limnCBF functions do not
-** need to worry about the dynamically allocated things within (so: no
-** limnCBFContextNew or limnCBFContextNix), but a limnCBFContext variable
-** should be initialized with limnCBFContextInit() in order to set default
-** parameters, before passing to limnCBF functions.
+** The bag of state for limnCBF functions.
**
** note: "nrp" = Newton-based Re-Parameterization of where the given points
** fall along the spline, the iterative process inside limnCBFSingle
@@ -554,38 +551,31 @@
cornNMS; /* non-minimal-suppression of corners: accept as
corners only those with locally minimal angle */
unsigned int nrpIterMax; /* max # iters of nrp */
- double scale, /* scale (in sense of nrrdKernelDiscreteGaussian)
- at which to estimate spline endpoints and
- tangents; scale=0 means the endpoints are
- exactly on vertices, and tangents are from
- the smallest-support finite differences */
- distMin, /* min distance to given points: this controls
- both splitting done by limnCBFMulti, and nrp
- within limnCBFSingle */
- nrpDeltaMax, /* in nrp, capping parameterization change to this
- scaling of average u[i+1]-u[i]. This wasn't in
- author's original code (so their idea of doing
- at most ~5 iters of nrp may no longer hold), but
- it can help stabilize things */
- nrpDistScl, /* scaling on distMin to use when testing distance
- during nrp; setting this < 1 means that nrp
- tries to be more stringent that the overall
- fitting, but with the benefit of sometimes
- being smarter about where to split, when that
- is needed */
- nrpPsi, /* don't even try nrp if max dist is bigger than
- nrpPsi*distMin, instead just subdivide */
- nrpDeltaMin, /* min total parameterization change by nrp */
- alphaMin, /* alpha can't be negative, and we enforce
- distinct positivity to ensure that spline
- doesn't slow down too much near endpoints */
- detMin, /* absolute value of determinant of 2x2 matrix
- to invert can't below this */
- cornAngle; /* 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. Or,
- if 0, no effort is made to detect corners. */
+ double scale, /* scale (in sense of nrrdKernelDiscreteGaussian) at which to estimate
+ spline endpoints and tangents; scale=0 means the endpoints are
+ exactly on vertices, and tangents are from the smallest-support
+ finite differences. This is the ONLY floating point that should be set
+ by a method (limnCBFScaleSet); the rest can be set directly. */
+ distMin, /* min distance to given points: this controls both splitting done by
+ limnCBFMulti, and nrp within limnCBFSingle */
+ nrpDeltaMax, /* in nrp, capping parameterization change to this scaling of average
+ u[i+1]-u[i]. This wasn't in author's original code (so their idea of
+ doing at most ~5 iters of nrp may no longer hold), but it can help
+ stabilize things */
+ nrpDistScl, /* scaling on distMin to use when testing distance during nrp; setting
+ this < 1 means that nrp tries to be more stringent than the overall
+ fitting, but with the benefit of sometimes being smarter about where
+ to split, when that is needed */
+ nrpPsi, /* don't even try nrp if max dist is bigger than nrpPsi*distMin, instead just
+ subdivide */
+ nrpDeltaMin, /* min total parameterization change by nrp */
+ 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; /* 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. Or, if 0, no effort is made
+ to detect corners. */
/* ----------- internal --------- */
double *uu, /* buffer used for nrp */
*vw, /* weights for endpoint vertex calculation */
@@ -605,13 +595,13 @@
2: DM < dist <= fD
3: fD < dist
where
+ nD = nrpDistScl*distMin,
DM = distMin,
- nD = nrpDistScl*distMin,
fD = nrpPsi*distMin: */
-} limnCBFContext;
+} limnCBFCtx;
/*
-******** limnPoints
+******** limnCBFPoints
**
** a container for 1D array of points; currently used for limnCBF functions
** Both pp and ppOwn can point to the array of point locations, but exactly
@@ -618,9 +608,11 @@
** one of pp and ppOwn can be non-NULL.
**
** NOTE: For now, point data is only double (not float), and only in 2D (not
-** 3D), but if this becomes more general, that generality will be inside here
+** 3D), but if this becomes more general, that generality will be inside here.
+** For time being DIM=2 tags locations where 2D-ness is explicit in code.
*/
typedef struct {
+ /* assuming DIM=2: 2 values per logical element pp */
const double *pp; /* point coords, we do not own buffer */
double *ppOwn; /* point coords, we DO own buffer */
unsigned int num; /* how many points */
@@ -627,7 +619,7 @@
int isLoop; /* points form a loop: logical indices into coord
array are . . . num-2, num-1, 0, 1, . . .
and index 0 is effectively arbitrary */
-} limnPoints;
+} limnCBFPoints;
/* defaultsLimn.c */
LIMN_EXPORT const int limnPresent;
@@ -905,27 +897,33 @@
double maxT);
/* splineFit.c */
-LIMN_EXPORT limnPoints *limnPointsNew(const double *pp, unsigned int nn, int isLoop);
-LIMN_EXPORT limnPoints *limnPointsNix(limnPoints *lpnt);
+LIMN_EXPORT limnCBFPoints *limnCBFPointsNew(const double *pp, unsigned int nn,
+ int isLoop);
+LIMN_EXPORT limnCBFPoints *limnCBFPointsNix(limnCBFPoints *lpnt);
+LIMN_EXPORT int limnCBFPointsCheck(const limnCBFPoints *lpnt);
+LIMN_EXPORT limnCBFCtx *limnCBFCtxNew(unsigned int pointNum, double scale);
+LIMN_EXPORT limnCBFCtx *limnCBFCtxNix(limnCBFCtx *fctx);
LIMN_EXPORT void limnCBFSegEval(double *xy, const limnCBFSeg *seg, double tt);
LIMN_EXPORT limnCBFPath *limnCBFPathNew(void);
LIMN_EXPORT limnCBFPath *limnCBFPathNix(limnCBFPath *path);
-LIMN_EXPORT void limnCBFPathSample(double *xy, unsigned int pNum,
+LIMN_EXPORT void limnCBFPathSample(double *xy, unsigned int pointNum,
const limnCBFPath *path);
-LIMN_EXPORT void limnCBFContextInit(limnCBFContext *fctx, int outputOnly);
-LIMN_EXPORT int limnCBFCheck(const limnCBFContext *fctx, const limnPoints *lpnt);
-LIMN_EXPORT int limnCBFitSingle(double alpha[2], limnCBFContext *fctx,
- const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2],
- const double *xy, unsigned int pNum, int isLoop);
-LIMN_EXPORT int limnCBFMulti(limnCBFPath *path, limnCBFContext *fctx,
- const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2],
- const limnPoints *lpnt, unsigned int loi, unsigned int hii);
+LIMN_EXPORT int limnCBFFindVT(double vv[2], double tt[2], const limnCBFCtx *fctx,
+ const limnCBFPoints *lpnt, unsigned int loi,
+ unsigned int hii, unsigned int ofi, int dir);
+LIMN_EXPORT int limnCBFCtxCheck(const limnCBFCtx *fctx, const limnCBFPoints *lpnt);
+LIMN_EXPORT int limnCBFitSingle(double alpha[2], limnCBFCtx *fctx, const double vv0[2],
+ const double tt1[2], const double tt2[2],
+ const double vv3[2], const double *xy,
+ unsigned int pointNum, int isLoop);
+LIMN_EXPORT int limnCBFMulti(limnCBFPath *path, limnCBFCtx *fctx, const double vv0[2],
+ const double tt1[2], const double tt2[2],
+ const double vv3[2], const limnCBFPoints *lpnt,
+ unsigned int loi, unsigned int hii);
LIMN_EXPORT int limnCBFCorners(unsigned int **cornIdx, unsigned int *cornNum,
- limnCBFContext *fctx, const limnPoints *lpnt);
-LIMN_EXPORT int limnCBFit(limnCBFPath *path, limnCBFContext *fctx, const double *xy,
- unsigned int pNum, int isLoop);
+ limnCBFCtx *fctx, const limnCBFPoints *lpnt);
+LIMN_EXPORT int limnCBFit(limnCBFPath *path, limnCBFCtx *fctx, const double *xy,
+ unsigned int pointNum, int isLoop);
/* lpu{Flotsam,. . .}.c */
#define LIMN_DECLARE(C) LIMN_EXPORT const unrrduCmd limnPu_##C##Cmd;
Modified: teem/trunk/src/limn/lpu_cbfit.c
===================================================================
--- teem/trunk/src/limn/lpu_cbfit.c 2024-06-20 21:03:47 UTC (rev 7165)
+++ teem/trunk/src/limn/lpu_cbfit.c 2024-06-21 07:43:16 UTC (rev 7166)
@@ -39,7 +39,7 @@
unsigned int ii, pNum, iterMax;
int loop, petc, verbose, synth, nofit;
char *synthOut;
- limnCBFContext fctx;
+ limnCBFCtx *fctx;
limnCBFPath *path;
hestOptAdd_1_Other(&hopt, "i", "input", &_nin, NULL, "input xy points", nrrdHestNrrd);
@@ -175,15 +175,14 @@
}
path = limnCBFPathNew();
airMopAdd(mop, path, (airMopper)limnCBFPathNix, airMopAlways);
- limnCBFContextInit(&fctx, AIR_FALSE);
- fctx.nrpIterMax = iterMax;
- fctx.nrpDeltaMin = deltaMin;
- fctx.distMin = distMin;
- fctx.nrpDistScl = distScl;
- fctx.verbose = verbose;
- fctx.nrpPsi = psi;
- fctx.cornAngle = cangle;
- fctx.scale = scale;
+ fctx = limnCBFCtxNew(pNum, scale);
+ fctx->nrpIterMax = iterMax;
+ fctx->nrpDeltaMin = deltaMin;
+ fctx->distMin = distMin;
+ fctx->nrpDistScl = distScl;
+ fctx->verbose = verbose;
+ fctx->nrpPsi = psi;
+ fctx->cornAngle = cangle;
time0 = airTime();
if (petc) {
fprintf(stderr, "%s: Press Enter to Continue ... ", me);
@@ -190,7 +189,7 @@
fflush(stderr);
getchar();
}
- if (limnCBFit(path, &fctx, xy, pNum, loop)) {
+ if (limnCBFit(path, fctx, xy, pNum, loop)) {
airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
fprintf(stderr, "%s: trouble:\n%s", me, err);
airMopError(mop);
@@ -198,7 +197,7 @@
}
dtime = (airTime() - time0) * 1000;
printf("%s: time= %g ms;iterDone= %u ;deltaDone=%g, dist=%g (@%u)\n", me, dtime,
- fctx.nrpIterDone, fctx.nrpDeltaDone, fctx.dist, fctx.distIdx);
+ fctx->nrpIterDone, fctx->nrpDeltaDone, fctx->dist, fctx->distIdx);
{
unsigned int si;
printf("%s: path has %u segments:\n", me, path->segNum);
Modified: teem/trunk/src/limn/splineFit.c
===================================================================
--- teem/trunk/src/limn/splineFit.c 2024-06-20 21:03:47 UTC (rev 7165)
+++ teem/trunk/src/limn/splineFit.c 2024-06-21 07:43:16 UTC (rev 7166)
@@ -31,18 +31,18 @@
The author's code is here:
http://www.realtimerendering.com/resources/GraphicsGems/gems/FitCurves.c
- The functions below do not use any existing limnSpline structs or functions;
- those were written a long time ago, and reflect GLK's ignorance about
- splines at the time. Hopefully this will be revisited and re-organized in a
- later version of Teem, at which point the code below can be integrated with
- the rest of limn, but this too will benefit from ongoing scrutiny and
- re-writing; ignorance persists.
+ The functions below do not use any other limnSpline structs or functions, since those
+ were written a long time ago and thus they reflect GLK's ignorance about splines at the
+ time. Hopefully this will be revisited and re-organized in a later version of Teem, at
+ which point the code below can be integrated with the rest of limn, but this too will
+ benefit from ongoing scrutiny and re-writing; ignorance persists.
*/
-limnPoints * /* Biff: nope */
-limnPointsNew(const double *pp, uint nn, int isLoop) {
- limnPoints *lpnt;
- lpnt = AIR_CALLOC(1, limnPoints);
+limnCBFPoints * /* Biff: nope */
+limnCBFPointsNew(const double *pp, uint nn, int isLoop) {
+ /* implicit: DIM=2 */
+ limnCBFPoints *lpnt;
+ lpnt = AIR_CALLOC(1, limnCBFPoints);
assert(lpnt);
if (pp) {
/* we are wrapping around a given pre-allocated buffer */
@@ -52,7 +52,7 @@
/* we are allocating our own buffer */
lpnt->pp = NULL;
lpnt->ppOwn = AIR_CALLOC(nn, double);
- assert(lpnt->pp);
+ assert(lpnt->ppOwn);
}
lpnt->num = nn;
lpnt->isLoop = isLoop;
@@ -59,8 +59,8 @@
return lpnt;
}
-limnPoints * /* Biff: nope */
-limnPointsNix(limnPoints *lpnt) {
+limnCBFPoints * /* Biff: nope */
+limnCBFPointsNix(limnCBFPoints *lpnt) {
if (lpnt) {
/* don't touch lpnt->pp */
if (lpnt->ppOwn) free(lpnt->ppOwn);
@@ -69,9 +69,9 @@
return NULL;
}
-static int /* Biff: 1 */
-pointsCheck(const limnPoints *lpnt) {
- static const char me[] = "pointsCheck";
+int /* Biff: 1 */
+limnCBFPointsCheck(const limnCBFPoints *lpnt) {
+ static const char me[] = "limnCBFPointsCheck";
uint pnmin;
int have;
@@ -81,7 +81,7 @@
}
pnmin = lpnt->isLoop ? 3 : 2;
if (!(lpnt->num >= pnmin)) {
- biffAddf(LIMN, "%s: need %u or more points in limnPoints (not %u)%s", me, pnmin,
+ biffAddf(LIMN, "%s: need %u or more points in limnCBFPoints (not %u)%s", me, pnmin,
lpnt->num, lpnt->isLoop ? " for loop" : "");
return 1;
}
@@ -93,11 +93,12 @@
return 0;
}
+/* cheesy macro to access either pp or ppOwn, useful since they differ in const-ness */
#define PP(lpnt) ((lpnt)->pp ? (lpnt)->pp : (lpnt)->ppOwn)
/* number of points between low,high indices loi,hii */
static uint
-pntNum(const limnPoints *lpnt, uint loi, uint hii) {
+pntNum(const limnCBFPoints *lpnt, uint loi, uint hii) {
if (hii < loi) {
assert(lpnt->isLoop);
hii += lpnt->num;
@@ -107,13 +108,155 @@
/* coordinates of point with index loi+ii */
static const double *
-pntCrd(const limnPoints *lpnt, uint loi, uint ii) {
+pntCrd(const limnCBFPoints *lpnt, uint loi, uint ii) {
uint jj = loi + ii;
while (jj >= lpnt->num)
jj -= lpnt->num;
- return PP(lpnt) + 2 * jj;
+ return PP(lpnt) + 2 * jj; /* DIM=2 */
}
+static void
+ctxInit(limnCBFCtx *fctx) {
+ if (!fctx) return;
+ /* defaults for input parameters to various CBF functions */
+ fctx->verbose = 0;
+ fctx->cornNMS = AIR_TRUE;
+ fctx->nrpIterMax = 10;
+ fctx->distMin = 0;
+ fctx->nrpDeltaMax = 3.0;
+ fctx->nrpDistScl = 0.8;
+ fctx->nrpPsi = 6;
+ fctx->nrpDeltaMin = 0.001;
+ fctx->alphaMin = 0.001;
+ fctx->detMin = 0.01;
+ fctx->cornAngle = 100.0; /* degrees */
+ /* internal */
+ fctx->uu = fctx->vw = fctx->tw = NULL;
+ fctx->mine = NULL;
+ fctx->wLen = 0;
+ fctx->lenF2L = AIR_NAN;
+ /* initialize outputs to bogus valus */
+ fctx->nrpIterDone = (uint)(-1);
+ fctx->distIdx = (uint)(-1);
+ fctx->dist = AIR_POS_INF;
+ fctx->nrpDeltaDone = AIR_POS_INF;
+ fctx->alphaDet = 0;
+ fctx->distBig = 0;
+ return;
+}
+
+/*
+** ctxBuffersSet: allocates in fctx:
+** uu, vw, tw
+*/
+static int /* Biff: 1 */
+ctxBuffersSet(limnCBFCtx *fctx, uint pNum, double scl) {
+ static const char me[] = "ctxBuffersSet";
+ double kparm[2], vsum, tsum;
+ /* one: what value in summing kernel weights should count as 1.0. This
+ should probably be a parm in fctx, but not very interesting to
+ change */
+ double one = 0.999;
+ uint ii, len;
+
+ if (fctx->uu) free(fctx->uu);
+ fctx->uu = AIR_CALLOC(pNum * 2, double); /* DIM=2 */
+ if (!fctx->uu) {
+ biffAddf(LIMN, "%s: failed to allocate parameter buffer", me);
+ return 1;
+ }
+ if (0 == scl) {
+ /* will do simplest possible finite differences; we're done */
+ fctx->vw = fctx->tw = NULL;
+ return 0;
+ }
+ /* else need to allocate and set vw and tw buffers */
+ kparm[0] = scl;
+ kparm[1] = 1000; /* effectively, no limit on scale; sanity check comes later */
+ ii = 0;
+ vsum = 0;
+ do {
+ double kw;
+ kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
+ vsum += (!ii ? 1 : 2) * kw;
+ ii++;
+ } while (vsum < one);
+ /* len = intended length of blurring kernel weight vectors */
+ len = ii + 1;
+ if (len > 128) {
+ biffAddf(LIMN,
+ "%s: weight buffer length %u (from scale %g) seems "
+ "unreasonable",
+ me, len, scl);
+ return 1;
+ }
+ if (fctx->vw) free(fctx->vw);
+ if (fctx->tw) free(fctx->tw);
+ fctx->vw = AIR_CALLOC(len, double);
+ fctx->tw = AIR_CALLOC(len, double);
+ if (!(fctx->vw && fctx->tw)) {
+ biffAddf(LIMN, "%s: couldn't allocate weight buffers (len %u)", me, len);
+ return 1;
+ }
+ fctx->wLen = len;
+ /* normalization intent:
+ 1 = sum_i(vw[|i|]) for i=-(len-1)...len-1
+ 1 = sum_i(tw[i]) for i=0...len-1
+ */
+ vsum = tsum = 0;
+ for (ii = 0; ii < len; ii++) {
+ double kw;
+ kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
+ vsum += (!ii ? 1 : 2) * (fctx->vw[ii] = kw);
+ tsum += (fctx->tw[ii] = ii * kw);
+ }
+ for (ii = 0; ii < len; ii++) {
+ fctx->vw[ii] /= vsum;
+ fctx->tw[ii] /= tsum;
+ /* printf("!%s: %u %g %g\n", me, ii, fctx->vw[ii], fctx->tw[ii]); */
+ }
+ return 0;
+}
+
+limnCBFCtx * /* Biff: NULL */
+limnCBFCtxNew(unsigned int pointNum, double scale) {
+ static const char me[] = "limnCBFCtxNew";
+ limnCBFCtx *ret;
+ if (!(pointNum >= 4)) {
+ biffAddf(LIMN, "%s: got tiny #points %u", me, pointNum);
+ return NULL;
+ }
+ if (!(scale >= 0)) {
+ biffAddf(LIMN, "%s: need scale >= 0 (not %g)", me, scale);
+ return NULL;
+ }
+ ret = AIR_CALLOC(1, limnCBFCtx);
+ if (!ret) {
+ biffAddf(LIMN, "%s: allocation failure?", me);
+ return NULL;
+ }
+ ctxInit(ret);
+ if (ctxBuffersSet(ret, pointNum, scale)) {
+ biffAddf(LIMN, "%s: trouble allocating buffers", me);
+ free(ret);
+ return NULL;
+ }
+ ret->scale = scale;
+
+ return ret;
+}
+
+limnCBFCtx * /* Biff: nope */
+limnCBFCtxNix(limnCBFCtx *fctx) {
+ if (fctx) {
+ if (fctx->uu) free(fctx->uu);
+ if (fctx->vw) free(fctx->vw);
+ if (fctx->tw) free(fctx->tw);
+ free(fctx);
+ }
+ return NULL;
+}
+
/* CB0, CB1, CB2, CB3 = degree 3 Bernstein polynomials, for *C*ubic
*B*ezier curves, and their derivatives D0, D1, D2 (not using any
nice recursion properties for evaluation, oh well) */
@@ -123,12 +266,12 @@
#define CB3D0(T) ((T) * (T) * (T))
#define CB0D1(T) (-3 * (1 - (T)) * (1 - (T)))
-#define CB1D1(T) (3 * ((T)-1) * (3 * (T)-1))
+#define CB1D1(T) (3 * ((T) - 1) * (3 * (T) - 1))
#define CB2D1(T) (3 * (T) * (2 - 3 * (T)))
#define CB3D1(T) (3 * (T) * (T))
#define CB0D2(T) (6 * (1 - (T)))
-#define CB1D2(T) (6 * (3 * (T)-2))
+#define CB1D2(T) (6 * (3 * (T) - 2))
#define CB2D2(T) (6 * (1 - 3 * (T)))
#define CB3D2(T) (6 * (T))
@@ -145,6 +288,7 @@
#define CBDI(P, CB, V0, V1, V2, V3, T, W) \
(CB(W, T), \
ELL_2V_SCALE_ADD4(P, (W)[0], (V0), (W)[1], (V1), (W)[2], (V2), (W)[3], (V3)))
+/* _2V_ above: DIM=2 */
#define CBD0(P, V0, V1, V2, V3, T, W) CBDI(P, VCBD0, V0, V1, V2, V3, T, W)
#define CBD1(P, V0, V1, V2, V3, T, W) CBDI(P, VCBD1, V0, V1, V2, V3, T, W)
#define CBD2(P, V0, V1, V2, V3, T, W) CBDI(P, VCBD2, V0, V1, V2, V3, T, W)
@@ -158,7 +302,7 @@
limnCBFSegEval(double *vv, const limnCBFSeg *seg, double tt) {
double ww[4];
const double *xy = seg->xy;
- CBD0(vv, xy + 0, xy + 2, xy + 4, xy + 6, tt, ww);
+ CBD0(vv, xy + 0, xy + 2, xy + 4, xy + 6, tt, ww); /* DIM=2 */
/*
fprintf(stderr, "!%s: tt=%g -> ww={%g,%g,%g,%g} * "
"{(%g,%g),(%g,%g),(%g,%g),(%g,%g)} = (%g,%g)\n",
@@ -185,7 +329,7 @@
const limnCBFSeg *seg = path->seg + segi;
double tmpf = AIR_AFFINE(0, ii, pNum - 1, 0, sNum);
double tt = tmpf - segi;
- limnCBFSegEval(xy + 2 * ii, seg, tt);
+ limnCBFSegEval(xy + 2 * ii, seg, tt); /* DIM=2 */
/*
fprintf(stderr, "!%s: %u -> %u (%g) %g -> (%g,%g)\n",
"limnCBFPathSample", ii, segi, tmpf, tt,
@@ -196,13 +340,13 @@
}
/*
-** Find endpoint vertex vv and tangent tt (constraints for spline fitting)
-** from the given points lpnt at coord index ii within index range [loi,hoi]
-** (e.g. ii=1 means looking at lpnt coord index loi+1). The tangent direction
+** limnCBFFindVT: Find endpoint vertex vv and tangent tt (constraints for spline fitting)
+** from the given points lpnt at offset index ofi within index range [loi,hii]
+** (e.g. ofi=1 means looking at lpnt coord index loi+1). The tangent direction
** dir controls which points are looked at:
-** >0: considering only ii and higher-index vertices,
-** 0: for tangent centered at ii, using lower- and higher-index vertices
-** <0: considering only ii and lower-index vertices
+** >0: considering only ofi and higher-index vertices,
+** 0: for tangent centered at ofi, using lower- and higher-index vertices
+** <0: considering only ofi and lower-index vertices
** For >0 and 0: the tangent points towards the positions of higher-
** index vertices. For <0, it points the other way.
** The only point indices accessed will be in [loi,hii]; this is what
@@ -209,38 +353,79 @@
** enforces the possible corner-ness of those indices (which prevents
** vertices past corners influencing how vv or tt are found)
*/
-static void
-findVT(double vv[2], double tt[2], const limnCBFContext *fctx, const limnPoints *lpnt,
- uint loi, uint hii, uint ii, int dir) {
- /* static const char me[] = "findVT"; */
+int /* Biff: 1 */
+limnCBFFindVT(double vv[2], double tt[2], const limnCBFCtx *fctx,
+ const limnCBFPoints *lpnt, uint _loi, uint _hii, uint _ofi, int dir) {
+ static const char me[] = "limnCBFFindVT";
double len;
- uint pNum, /* total number of points in lpnts */
- sgsz; /* segment size: number of points in [loi,hii] */
+ /* we use here (signed) int for things that might seem better as uint, but that's
+ because of the frequent need to handle how indices loop around in loops */
+ int loi, hii, ofi, /* int versions of given args */
+ pNum, /* total number of points in lpnts */
+ sgsz, /* "segment" size: number of points in [loi,hii]
+ (but not in sense of the limnCBFSeg specifically) */
+ icent, iplus, imnus; /* icent is the actual data index corresponding to _loi + _ofi;
+ it is used for both the scale==0 and scale>0 cases;
+ iplus and imnus are only needed with scale==0, but too annoying
+ to break those out into that specific branch */
+ if (!(/* vv can be NULL */ tt && fctx && lpnt)) {
+ biffAddf(LIMN, "%s: got NULL pointer", me);
+ return 1;
+ }
+ if (!(_loi < lpnt->num && _hii < lpnt->num)) {
+ biffAddf(LIMN, "%s: _loi %u or _hii %u too high for %u points", me, _loi, _hii,
+ lpnt->num);
+ }
+ /* now both _loi and _hii are valid indices in [0,..,lpnt->num-1] */
+ if (_loi == _hii) {
+ biffAddf(LIMN, "%s: got _loi==_hii %u", me, _loi);
+ return 1;
+ }
+ if (_hii < _loi && !lpnt->isLoop) {
+ biffAddf(LIMN, "%s: _hii %u < _loi %u sensible only in point loop", me, _hii, _loi);
+ return 1;
+ }
+ /* we proceed with either _loi < _hii or (_hii < _loi and) lpnt->isLoop
+ i.e. now _hii < _loi (and soon hii < loi) implies lpnt->isLoop */
dir = airSgn(dir);
- pNum = lpnt->num;
+ pNum = AIR_INT(lpnt->num);
+ loi = AIR_INT(_loi);
+ hii = AIR_INT(_hii);
+ sgsz = (hii < loi ? pNum : 0) + hii - loi + 1;
+ if (sgsz <= 1) {
+ biffAddf(LIMN, "%s: sgsz %d <= 1", me, sgsz);
+ return 1;
+ }
+ /* now sgsz >= 2 */
+ if (!(_ofi < AIR_UINT(sgsz))) {
+ biffAddf(LIMN, "%s: _ofi %u too high for segment size %d", me, _ofi, sgsz);
+ return 1;
+ }
+ /* now _ofi is a valid index in [0,..,sgsz-1] */
+ ofi = AIR_INT(_ofi);
+ icent = loi + ofi;
+ iplus = icent + 1;
+ imnus = icent - 1;
if (lpnt->isLoop) {
- sgsz = (hii < loi ? pNum : 0) + hii - loi + 1;
+ icent = AIR_MOD(icent, pNum);
+ iplus = AIR_MOD(iplus, pNum);
+ imnus = AIR_MOD(imnus, pNum);
} else {
- sgsz = hii - loi + 1;
+ icent = AIR_CLAMP(loi, icent, hii);
+ iplus = AIR_CLAMP(loi, iplus, hii);
+ imnus = AIR_CLAMP(loi, imnus, hii);
}
if (0 == fctx->scale) {
- uint mi, pi, iplus, imnus;
const double *xy, *xyP, *xyM;
- if (lpnt->isLoop) {
- iplus = (loi + ii + 1) % pNum;
- imnus = (uint)AIR_MOD((int)(loi + ii) - 1, (int)pNum);
- } else {
- /* regardless of lpnt->isLoop, we only look in [loi,hii] */
- iplus = loi + AIR_MIN(ii + 1, sgsz - 1);
- imnus = loi + AIR_MAX(1, ii) - 1;
+ int mi, ci, pi;
+ if (vv) {
+ ELL_2V_COPY(vv, PP(lpnt) + 2 * icent); /* DIM=2 */
}
- xy = pntCrd(lpnt, loi, ii);
- if (vv) ELL_2V_COPY(vv, xy);
switch (dir) {
case 1:
pi = iplus;
- mi = ii;
+ mi = icent;
break;
case 0:
pi = iplus;
@@ -248,120 +433,131 @@
break;
case -1:
/* mi and pi switched to point other way */
- mi = ii;
+ mi = icent;
pi = imnus;
break;
}
- /* if (with !isLoop) ii=0 and dir=-1, or, ii=pNum-1 and dir=+1
- ==> mi=pi ==> tt will be (nan,nan), which is appropriate */
- xyP = pntCrd(lpnt, loi, pi);
- xyM = pntCrd(lpnt, loi, mi);
+ if (pi == mi) {
+ biffAddf(LIMN, "%s: imnus,icent,iplus=%d,%d,%d --dir=%d--> mi == pi %d", me, imnus,
+ icent, iplus, dir, mi);
+ return 1;
+ }
+ /* printf("!%s: iplus=%u imnus=%u xy=%g %g\n", me, iplus, imnus, xy[0], xy[1]); */
+ xyP = PP(lpnt) + 2 * pi; /* DIM=2 and following */
+ xyM = PP(lpnt) + 2 * mi;
ELL_2V_SUB(tt, xyP, xyM);
ELL_2V_NORM(tt, tt, len);
+ /* printf("!%s: xyP=%g %g xyM=%g %g len=%g tt=%g %g\n", me, xyP[0], xyP[1],
+ xyM[0], xyM[1], len, tt[0], tt[1]); */
} else {
-#if 0
/* using scale>0 for endpoint and tangent estimation */
+ /* regardless of dir, we compute average positions for points
+ centered around loi + ofi (posC), and for lower,higher indices (posM,posP) */
+ double posP[2] = {0, 0}, posC[2] = {0, 0}, posM[2] = {0, 0};
const double *vw = fctx->vw;
const double *tw = fctx->tw;
+ int smax = (int)fctx->wLen - 1, /* bound of loop index */
+ ci; /* loop through [-smax,smax] */
+ if (!(vw && tw)) {
+ biffAddf(LIMN, "%s: fctx internal buffers vw and tw not both allocated", me);
+ return 1;
+ }
/* various signed indices */
- int sii=(int)ii, /* we compute around vertex ii */
- smax=(int)fctx->wLen - 1, /* bound of loop index */
- sj; /* loop through [-smax,smax] */
- if (vv) ELL_2V_SET(vv, 0, 0);
- ELL_2V_SET(tt, 0, 0);
- /* printf("!%s: ii = %u, dir=%d\n", me, ii, dir); */
- /* j indices are for the local looping */
- for (sj=-smax; sj<=smax; sj++) {
- uint xj, /* eventual index into data */
- asj = (uint)AIR_ABS(sj); /* index into vw, tw */
- int sgn=1,
- sxj = sii + sj; /* signed (tmp) j idx into data */
- double ttw;
- /* printf("!%s[sj=%d,asj=%u]: sxj0 = %d\n", me, sj, asj, sxj); */
- switch (dir) {
- case 1:
- sxj = AIR_MAX(sxj, sii);
- break;
- case -1:
- sgn=-1;
- sxj = AIR_MIN(sxj, sii);
- break;
- }
- /* sxj = sii+sj, but capped at sii according to dir */
- /* printf("!%s[sj=%d]: dir=%d -> sxj1 = %d\n", me, sj, dir, sxj); */
+ /* printf("!%s: ofi = %d, dir=%d\n", me, ofi, dir); */
+ for (ci = -smax; ci <= smax; ci++) {
+ uint wi = abs(ci); /* weight index into vw, tw */
+ int di = loi + ofi + ci; /* signed index into data */
+ const double *xy;
if (lpnt->isLoop) {
- sxj = AIR_MOD(sxj, (int)pNum);
+ di = AIR_MOD(di, pNum);
} else {
- sxj = AIR_CLAMP(0, sxj, (int)pNum-1);
+ di = AIR_CLAMP(loi, di, hii);
}
- xj = (uint)sxj;
- /* printf("!%s[sj=%d]: isLoop=%d -> sxj2 = %d -> xj = %u\n", me, sj, lpnt->isLoop, sxj, xj); */
- if (vv) ELL_2V_SCALE_INCR(vv, vw[asj], xy + 2*xj);
- /* printf("!%s[sj=%d]: vv += %g*(%g,%g) -> (%g,%g)\n", me, sj, vw[asj], (xy + 2*xj)[0], (xy + 2*xj)[1], vv[0], vv[1]); */
- ttw = sgn*airSgn(sj)*tw[asj];
- /* printf("!%s[sj=%d]: %d * %d * %g = %g\n", me, sj, sgn, airSgn(sj), tw[asj], ttw); */
- ELL_2V_SCALE_INCR(tt, ttw, xy + 2*xj);
- /* printf("!%s[sj=%d]: tt += %g*(%g,%g) -> (%g,%g)\n", me, sj, ttw, (xy + 2*xj)[0], (xy + 2*xj)[1], tt[0], tt[1]); */
+ xy = PP(lpnt) + 2 * di;
+ ELL_2V_SCALE_INCR(posC, vw[wi], xy);
+ /* printf("!%s: (ci=%d/wi=%u) posC += %g*(%g %g) --> %g %g\n", me, ci, wi, vw[wi],
+ xy[0], xy[1], posC[0], posC[1]); */
+ if (ci < 0) {
+ ELL_2V_SCALE_INCR(posM, tw[wi], xy);
+ /* printf("!%s: (ci=%d/wi=%u) posM += %g*(%g %g) --> %g %g\n", me, ci, wi,
+ tw[wi], xy[0], xy[1], posM[0], posM[1]); */
+ }
+ if (ci > 0) {
+ ELL_2V_SCALE_INCR(posP, tw[wi], xy);
+ /* printf("!%s: (ci=%d/wi=%u) posP += %g*(%g %g) --> %g %g\n", me, ci, wi,
+ tw[wi], xy[0], xy[1], posP[0], posP[1]); */
+ }
}
+ switch (dir) {
+ case 1:
+ ELL_2V_SUB(tt, posP, posC);
+ break;
+ case 0:
+ ELL_2V_SUB(tt, posP, posM);
+ break;
+ case -1:
+ ELL_2V_SUB(tt, posM, posC);
+ break;
+ }
ELL_2V_NORM(tt, tt, len);
- /* fix the boundary conditions as a post-process */
- if ( 0==ii && -1==dir) ELL_2V_SET(tt, AIR_NAN, AIR_NAN);
- if (pNum-1==ii && +1==dir) ELL_2V_SET(tt, AIR_NAN, AIR_NAN);
if (vv) {
+ ELL_2V_COPY(vv, posC);
/* some post-proceessing of computed spline endpoint */
- double off[2], pp[2], operp;
- ELL_2V_SET(pp, tt[1], -tt[0]); /* pp is perpendicular to tt */
- ELL_2V_SUB(off, vv, xy + 2*ii);
+ double off[2], pp[2], operp, okoff;
+ const double *xy;
+ /* DIM=2 */
+ xy = PP(lpnt) + 2 * icent; /* center vertex in given data */
+ ELL_2V_SET(pp, tt[1], -tt[0]); /* pp is perpendicular to computed tt */
+ ELL_2V_SUB(off, vv, xy); /* off = vv - xy, from given to computed */
operp = ELL_2V_DOT(off, pp);
/* limit distance from chosen (x,y) datapoint to spline endpoint to be
(HEY harcoded) 95% of fctx->distMin. Being allowed to be further away
can cause annoyances */
- operp = AIR_MIN(0.95*fctx->distMin, operp);
+ okoff = 0.95 * fctx->distMin;
+ operp = AIR_CLAMP(-okoff, operp, okoff);
/* constrain difference between chosen (x,y) datapoint and spline
endpoint to be perpendicular to estimated tangent */
- ELL_2V_SCALE_ADD2(vv, 1, xy + 2*ii, operp, pp);
+ ELL_2V_SCALE_ADD2(vv, 1, xy, operp, pp);
}
-#endif
}
- return;
+ return 0;
}
-static int /* Biff: 1 */
-setVTTV(int *given, double vv0[2], double tt1[2], double tt2[2], double vv3[2],
+static int /* Biff: 1 */
+setVTTV(int *given, /* */
+ double vv0[2], double tt1[2], double tt2[2], double vv3[2], /* */
const double _vv0[2], const double _tt1[2], const double _tt2[2],
- const double _vv3[2], const limnCBFContext *fctx, const limnPoints *lpnt,
- uint loi, uint hii) {
+ const double _vv3[2], /* */
+ const limnCBFCtx *fctx, const limnCBFPoints *lpnt, uint loi, uint hii) {
static const char me[] = "setVTTV";
- /* either all the _vv0, _tt1, _tt2, _vv3 can be NULL, or none */
- if (!(_vv0 && _tt1 && _tt2 && _vv3)) {
+ /* either: all the _vv0, _tt1, _tt2, _vv3 can be NULL, or none are NULL */
+ if (_vv0 && _tt1 && _tt2 && _vv3) {
+ /* copy the given endpoint geometry */
+ ELL_2V_COPY(vv0, _vv0);
+ ELL_2V_COPY(tt1, _tt1);
+ ELL_2V_COPY(tt2, _tt2);
+ ELL_2V_COPY(vv3, _vv3);
+ if (given) {
+ *given = AIR_TRUE;
+ }
+ } else {
+ /* not given v, t, t, v values, so we compute them */
if (_vv0 || _tt1 || _tt2 || _vv3) {
- biffAddf(LIMN,
- "%s: either all or none of vv0,tt1,tt2,vv3 "
- "should be NULL",
- me);
+ biffAddf(LIMN, "%s: either all or none of _vv0,_tt1,_tt2,_vv3 should be NULL", me);
return 1;
}
if (lpnt->isLoop) {
- findVT(vv0, tt1, fctx, lpnt, loi, hii, loi, 0);
+ limnCBFFindVT(vv0, tt1, fctx, lpnt, loi, hii, 0, 0);
ELL_2V_COPY(vv3, vv0);
ELL_2V_SCALE(tt2, -1, tt1);
} else {
- findVT(vv0, tt1, fctx, lpnt, loi, hii, loi, +1);
- findVT(vv0, tt1, fctx, lpnt, loi, hii, hii, -1);
+ limnCBFFindVT(vv0, tt1, fctx, lpnt, loi, hii, 0, +1);
+ limnCBFFindVT(vv3, tt2, fctx, lpnt, loi, hii, hii - loi, -1);
}
if (given) {
*given = AIR_FALSE;
}
- } else {
- /* copy the given endpoint geometry */
- ELL_2V_COPY(vv0, _vv0);
- ELL_2V_COPY(tt1, _tt1);
- ELL_2V_COPY(tt2, _tt2);
- ELL_2V_COPY(vv3, _vv3);
- if (given) {
- *given = AIR_TRUE;
- }
}
return 0;
}
@@ -388,9 +584,9 @@
** (possibly unstable?)
*/
static void
-findalpha(double alpha[2], limnCBFContext *fctx, /* must be non-NULL */
+findalpha(double alpha[2], limnCBFCtx *fctx, /* must be non-NULL */
const double vv0[2], const double tt1[2], const double tt2[2],
- const double vv3[2], const limnPoints *lpnt, uint loi, uint hii) {
+ const double vv3[2], const limnCBFPoints *lpnt, uint loi, uint hii) {
static const char me[] = "findalpha";
uint ii, pNum;
double det;
@@ -397,7 +593,7 @@
pNum = pntNum(lpnt, loi, hii);
if (pNum > 2) {
- double xx[2], m11, m12, m22, MM[4], MI[4];
+ double xx[2], m11, m12, m22, MM[4], MI[4]; /* DIM=2 throughout this */
const double *uu = fctx->uu;
xx[0] = xx[1] = m11 = m12 = m22 = 0;
for (ii = 0; ii < pNum; ii++) {
@@ -457,9 +653,9 @@
** to evaluate the spline in order to match the given points xy
*/
static double
-reparm(const limnCBFContext *fctx, /* must be non-NULL */
+reparm(const limnCBFCtx *fctx, /* must be non-NULL */
const double alpha[2], const double vv0[2], const double tt1[2],
- const double tt2[2], const double vv3[2], const limnPoints *lpnt, uint loi,
+ const double tt2[2], const double vv3[2], const limnCBFPoints *lpnt, uint loi,
uint hii) {
static const char me[] = "reparm";
uint ii, pNum;
@@ -506,12 +702,12 @@
return delta;
}
-/* sets fctx->dist to max distance to spline, at point fctx->distIdx,
- and then sets fctx->distBig accordingly */
+/* (assuming current parameterization in fctx->uu) sets fctx->dist to max distance to
+ spline, at point fctx->distIdx, and then sets fctx->distBig accordingly */
static void
-finddist(limnCBFContext *fctx, const double alpha[2], const double vv0[2],
+finddist(limnCBFCtx *fctx, const double alpha[2], const double vv0[2],
const double tt1[2], const double tt2[2], const double vv3[2],
- const limnPoints *lpnt, uint loi, uint hii) {
+ const limnCBFPoints *lpnt, uint loi, uint hii) {
uint ii, distI, pNum;
double vv1[2], vv2[2], dist;
const double *uu = fctx->uu;
@@ -518,14 +714,13 @@
pNum = pntNum(lpnt, loi, hii);
assert(pNum >= 3);
- ELL_2V_SCALE_ADD2(vv1, 1, vv0, alpha[0], tt1);
+ ELL_2V_SCALE_ADD2(vv1, 1, vv0, alpha[0], tt1); /* DIM=2 everywhere here */
ELL_2V_SCALE_ADD2(vv2, 1, vv3, alpha[1], tt2);
dist = AIR_NAN;
- /* NOTE that the first and last points are actually not part of the max
- distance calculation, which motivates ensuring that the endpoints
- generated by findVT are actually sufficiently close to the first and last
- points (or else the fit spline won't meet the expected accuracy
- threshold) */
+ /* NOTE that the first and last points are actually not part of the max distance
+ calculation, which motivates ensuring that the endpoints generated by limnCBFFindVT
+ are actually sufficiently close to the first and last points (or else the fit spline
+ won't meet the expected accuracy threshold) */
for (ii = 1; ii < pNum - 1; ii++) {
double len, Q[2], df[2], ww[4];
const double *xy;
@@ -547,53 +742,20 @@
return;
}
-void
-limnCBFContextInit(limnCBFContext *fctx, int outputOnly) {
- if (!fctx) return;
- if (!outputOnly) {
- /* defaults for input parameters to various CBF functions */
- fctx->verbose = 0;
- fctx->cornNMS = AIR_TRUE;
- fctx->nrpIterMax = 10;
- fctx->scale = 0;
- fctx->distMin = 0;
- fctx->nrpDeltaMax = 3.0;
- fctx->nrpDistScl = 0.8;
- fctx->nrpPsi = 6;
- fctx->nrpDeltaMin = 0.001;
- fctx->alphaMin = 0.001;
- fctx->detMin = 0.01;
- fctx->cornAngle = 100.0; /* degrees */
- }
- /* internal */
- fctx->uu = fctx->vw = fctx->tw = NULL;
- fctx->mine = NULL;
- fctx->wLen = 0;
- fctx->lenF2L = AIR_NAN;
- /* initialize outputs to bogus valus */
- fctx->nrpIterDone = (uint)(-1);
- fctx->distIdx = (uint)(-1);
- fctx->dist = AIR_POS_INF;
- fctx->nrpDeltaDone = AIR_POS_INF;
- fctx->alphaDet = 0;
- fctx->distBig = 0;
- return;
-}
-
/*
-******** limnCBFCheck
+******** limnCBFCtxCheck
**
** checks the things that are going to be passed around a lot
*/
int /* Biff: 1 */
-limnCBFCheck(const limnCBFContext *fctx, const limnPoints *lpnt) {
- static const char me[] = "limnCBFCheck";
+limnCBFCtxCheck(const limnCBFCtx *fctx, const limnCBFPoints *lpnt) {
+ static const char me[] = "limnCBFCtxCheck";
if (!(fctx && lpnt)) {
biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
- if (pointsCheck(lpnt)) {
+ if (limnCBFPointsCheck(lpnt)) {
biffAddf(LIMN, "%s: problem with points", me);
return 1;
}
@@ -631,7 +793,7 @@
** fitSingle: fits a single cubic Bezier spline, w/out error checking,
** limnCBFSingle is a wrapper around this.
**
-** The given points coordinates are in limnPoints lpnt, between low/high
+** The given points coordinates are in limnCBFPoints lpnt, between low/high
** indices loi/hii (inclusively); hii can be < loi in the case of a point
** loop. From initial endpoint vv0, initial tangent tt1, final endpoint vv3
** and final tangent tt2 (pointing backwards), this function finds alpha such
@@ -652,9 +814,9 @@
** fctx.
*/
static void
-fitSingle(double alpha[2], limnCBFContext *fctx, const double vv0[2],
- const double tt1[2], const double tt2[2], const double vv3[2],
- const limnPoints *lpnt, uint loi, uint hii) {
+fitSingle(double alpha[2], limnCBFCtx *fctx, const double vv0[2], const double tt1[2],
+ const double tt2[2], const double vv3[2], const limnCBFPoints *lpnt, uint loi,
+ uint hii) {
static const char me[] = "fitSingle";
uint iter, pNum;
const double *xy;
@@ -754,147 +916,50 @@
}
/*
-** buffersNew: allocates in fctx:
-** uu, vw, tw
-*/
-static int /* Biff: 1 */
-buffersNew(limnCBFContext *fctx, uint pNum) {
- static const char me[] = "buffersNew";
- double kw, kparm[2], vsum, tsum, scl = fctx->scale;
- /* one: what value in summing kernel weights should count as 1.0. This
- should probably be a parm in fctx, but not very interesting to
- change */
- double one = 0.999;
- uint ii, len;
-
- fctx->uu = AIR_CALLOC(pNum * 2, double);
- if (!fctx->uu) {
- biffAddf(LIMN, "%s: failed to allocate parameter buffer", me);
- return 1;
- }
- if (0 == scl) {
- /* will do simplest possible finite differences; we're done */
- fctx->vw = fctx->tw = NULL;
- return 0;
- }
- /* else need to allocate and set vw and tw buffers */
- kparm[0] = scl;
- kparm[1] = 1000000; /* effectively no cut-off */
- ii = 0;
- vsum = 0;
- do {
- kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
- vsum += (!ii ? 1 : 2) * kw;
- ii++;
- } while (vsum < one);
- /* intended length of vectors */
- len = ii + 1;
- if (len > 128) {
- biffAddf(LIMN,
- "%s: weight buffer length %u (from scale %g) seems "
- "unreasonable",
- me, len, scl);
- return 1;
- }
- fctx->vw = AIR_CALLOC(len, double);
- fctx->tw = AIR_CALLOC(len, double);
- if (!(fctx->vw && fctx->tw)) {
- biffAddf(LIMN, "%s: couldn't allocate weight buffers (len %u)", me, len);
- return 1;
- }
- fctx->wLen = len;
- /* normalization intent:
- 1 = sum_i(vw[|i|]) for i=-(len-1)...len-1
- 1 = sum_i(tw[i]) for i=0...len-1
- */
- vsum = tsum = 0;
- for (ii = 0; ii < len; ii++) {
- kw = nrrdKernelDiscreteGaussian->eval1_d(ii, kparm);
- vsum += (!ii ? 1 : 2) * (fctx->vw[ii] = kw);
- tsum += (fctx->tw[ii] = ii * kw);
- }
- for (ii = 0; ii < len; ii++) {
- fctx->vw[ii] /= vsum;
- fctx->tw[ii] /= tsum;
- /* printf("!%s: %u %g %g\n", me, ii, fctx->vw[ii], fctx->tw[ii]); */
- }
- return 0;
-}
-
-/* returning a pointer so compatible with an airMopper */
-static void *
-buffersNix(limnCBFContext *fctx) {
- fctx->uu = (double *)airFree(fctx->uu);
- fctx->vw = (double *)airFree(fctx->vw);
- fctx->tw = (double *)airFree(fctx->tw);
- return NULL;
-}
-
-/* macros to manage the heap-allocated things inside limnCBFContext; working
- with the idea that each caller passes an OWN variable on their stack, so
- the NIX macro only frees thing when the address of OWN matches that passed
- to the NEW. Nothing else in Teem uses this strategy; it may be exploring
- the clever/stupid boundary that David and Nigel famously identified. */
-#define BUFFERS_NEW(FCTX, NN, OWN) \
- if (!(FCTX)->uu) { \
- if (buffersNew((FCTX), (NN))) { \
- biffAddf(LIMN, "%s: failed to allocate buffers", me); \
- return 1; \
- } \
- (FCTX)->mine = &(OWN); \
- }
-
-#define BUFFERS_NIX(FCTX, OWN) \
- if ((FCTX)->mine == &(OWN)) { \
- buffersNix(FCTX); \
- (FCTX)->mine = NULL; \
- }
-
-/*
******** limnCBFitSingle
**
-** builds a limnPoints around given xy, determines spline
+** builds a limnCBFPoints around given xy, determines spline
** constraints if necessary, and calls fitSingle
*/
int /* Biff: 1 */
-limnCBFitSingle(double alpha[2], limnCBFContext *_fctx, const double _vv0[2],
+limnCBFitSingle(double alpha[2], limnCBFCtx *_fctx, const double _vv0[2],
const double _tt1[2], const double _tt2[2], const double _vv3[2],
const double *xy, uint pNum, int isLoop) {
static const char me[] = "limnCBFitSingle";
- double own, vv0[2], tt1[2], tt2[2], vv3[2];
+ double vv0[2], tt1[2], tt2[2], vv3[2];
uint loi, hii;
- limnCBFContext *fctx, myfctx;
- limnPoints *lpnt;
+ limnCBFCtx *fctx;
+ limnCBFPoints *lpnt;
+ airArray *mop;
if (!(alpha && xy && pNum)) {
biffAddf(LIMN, "%s: got NULL pointer or 0 points", me);
return 1;
}
- lpnt = limnPointsNew(xy, pNum, isLoop);
+ mop = airMopNew();
+ lpnt = limnCBFPointsNew(xy, pNum, isLoop);
+ airMopAdd(mop, lpnt, (airMopper)limnCBFPointsNix, airMopAlways);
loi = 0;
hii = pNum - 1;
if (_fctx) {
fctx = _fctx; /* caller has supplied info */
- if (limnCBFCheck(fctx, lpnt)) {
- biffAddf(LIMN, "%s: problem with fctx", me);
- limnPointsNix(lpnt);
+ if (limnCBFCtxCheck(fctx, lpnt)) {
+ biffAddf(LIMN, "%s: problem with fctx given points lpnt", me);
+ airMopError(mop);
return 1;
}
- limnCBFContextInit(fctx, AIR_TRUE /* outputOnly */);
} else {
- fctx = &myfctx; /* caller supplied nothing: use defaults */
- limnCBFContextInit(fctx, AIR_FALSE /* outputOnly */);
+ fctx = limnCBFCtxNew(pNum, 0.0 /* scale */);
+ airMopAdd(mop, fctx, (airMopper)limnCBFCtxNix, airMopAlways);
}
- BUFFERS_NEW(fctx, pNum, own);
if (setVTTV(NULL, vv0, tt1, tt2, vv3, _vv0, _tt1, _tt2, _vv3, fctx, lpnt, loi, hii)) {
biffAddf(LIMN, "%s: trouble", me);
- limnPointsNix(lpnt);
+ airMopError(mop);
return 1;
}
fitSingle(alpha, fctx, vv0, tt1, tt2, vv3, lpnt, loi, hii);
- BUFFERS_NIX(fctx, own);
- limnPointsNix(lpnt);
+ airMopOkay(mop);
return 0;
}
@@ -901,7 +966,7 @@
static void
segInit(void *_seg) {
limnCBFSeg *seg = (limnCBFSeg *)_seg;
- ELL_2V_NAN_SET(seg->xy + 0);
+ ELL_2V_NAN_SET(seg->xy + 0); /* DIM=2 */
ELL_2V_NAN_SET(seg->xy + 2);
ELL_2V_NAN_SET(seg->xy + 4);
ELL_2V_NAN_SET(seg->xy + 6);
@@ -948,9 +1013,9 @@
** left and right sides around points with the highest error from fitSingle.
*/
int /* Biff: 1 */
-limnCBFMulti(limnCBFPath *path, limnCBFContext *fctx, const double _vv0[2],
+limnCBFMulti(limnCBFPath *path, limnCBFCtx *fctx, const double _vv0[2],
const double _tt1[2], const double _tt2[2], const double _vv3[2],
- const limnPoints *lpnt, uint loi, uint hii) {
+ const limnCBFPoints *lpnt, uint loi, uint hii) {
static const char me[] = "limnCBFMulti";
double vv0[2], tt1[2], tt2[2], vv3[2], alpha[2];
/* &ownbuff determines who frees buffers inside fctx, since each
@@ -960,7 +1025,7 @@
uint pNum;
/* need non-NULL fctx in order to know fctx->distMin */
- if (limnCBFCheck(fctx, lpnt)) {
+ if (limnCBFCtxCheck(fctx, lpnt)) {
biffAddf(LIMN, "%s: got bad args", me);
return 1;
}
@@ -977,7 +1042,6 @@
return 1;
}
pNum = pntNum(lpnt, loi, hii);
- BUFFERS_NEW(fctx, pNum, ownbuff);
if (setVTTV(&geomGiven, vv0, tt1, tt2, vv3, _vv0, _tt1, _tt2, _vv3, fctx, lpnt, loi,
hii)) {
biffAddf(LIMN, "%s: trouble", me);
@@ -1013,14 +1077,14 @@
uint mi = fctx->distIdx;
double ttL[2], mid[2], ttR[2];
limnCBFPath *prth = limnCBFPathNew(); /* right path */
- limnCBFContext fctxL, fctxR;
- memcpy(&fctxL, fctx, sizeof(limnCBFContext));
- memcpy(&fctxR, fctx, sizeof(limnCBFContext));
+ limnCBFCtx fctxL, fctxR;
+ memcpy(&fctxL, fctx, sizeof(limnCBFCtx));
+ memcpy(&fctxR, fctx, sizeof(limnCBFCtx));
if (fctx->verbose) {
printf("%s[%u,%u]: dist %g big (%d) --> split at %u\n", me, loi, hii, fctx->dist,
fctx->distBig, mi);
}
- findVT(mid, ttR, fctx, lpnt, loi, hii, mi, 0);
+ limnCBFFindVT(mid, ttR, fctx, lpnt, loi, hii, mi, 0);
ELL_2V_SCALE(ttL, -1, ttR);
/* on recursion, can't be a loop, so isLoop is AIR_FALSE */
if (limnCBFMulti(path, &fctxL, vv0, tt1, ttL, mid, lpnt, loi, mi)
@@ -1045,13 +1109,12 @@
fctx->alphaDet = AIR_MIN(fctxL.alphaDet, fctxR.alphaDet);
}
- BUFFERS_NIX(fctx, ownbuff);
return 0;
}
int /* Biff: 1 */
-limnCBFCorners(uint **cornIdx, uint *cornNum, limnCBFContext *fctx,
- const limnPoints *lpnt) {
+limnCBFCorners(uint **cornIdx, uint *cornNum, limnCBFCtx *fctx,
+ const limnCBFPoints *lpnt) {
static const char me[] = "limnCBFCorners";
airArray *mop, *cornArr;
double ownbuff, *angle;
@@ -1062,7 +1125,7 @@
biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
- if (limnCBFCheck(fctx, lpnt)) {
+ if (limnCBFCtxCheck(fctx, lpnt)) {
biffAddf(LIMN, "%s: got bad args", me);
return 1;
}
@@ -1086,11 +1149,10 @@
cornArr = airArrayNew(AIR_CAST(void **, cornIdx), cornNum, sizeof(uint), 32);
/* free with Nix, not Nuke, because we are managing the given pointers */
airMopAdd(mop, cornArr, (airMopper)airArrayNix, airMopAlways);
- BUFFERS_NEW(fctx, pNum, ownbuff);
for (ii = 0; ii < pNum; ii++) {
double LT[2], RT[2];
- findVT(NULL, LT, fctx, lpnt, loi, hii, ii, -1);
- findVT(NULL, RT, fctx, lpnt, loi, hii, ii, +1);
+ limnCBFFindVT(NULL, LT, fctx, lpnt, loi, hii, ii, -1);
+ limnCBFFindVT(NULL, RT, fctx, lpnt, loi, hii, ii, +1);
angle[ii] = 180 * ell_2v_angle_d(LT, RT) / AIR_PI;
corn[ii] = (angle[ii] < fctx->cornAngle);
}
@@ -1109,7 +1171,6 @@
ci = airArrayLenIncr(cornArr, 1);
(*cornIdx)[ci] = ii;
}
- BUFFERS_NIX(fctx, ownbuff);
airMopOkay(mop);
return 0;
}
@@ -1120,12 +1181,11 @@
** top-level function for fitting cubic beziers to given points
*/
int /* Biff: 1 */
-limnCBFit(limnCBFPath *path, limnCBFContext *fctx, const double *xy, uint pNum,
- int isLoop) {
+limnCBFit(limnCBFPath *path, limnCBFCtx *fctx, const double *xy, uint pNum, int isLoop) {
static const char me[] = "limnCBFit";
uint *cornIdx = NULL, cornNum = 0, cii, loi, hii;
limnCBFPath *rpth;
- limnPoints *lpnt;
+ limnCBFPoints *lpnt;
int ret;
airArray *mop;
@@ -1133,25 +1193,22 @@
biffAddf(LIMN, "%s: got NULL pointer", me);
return 1;
}
- lpnt = limnPointsNew(xy, pNum, isLoop);
+ lpnt = limnCBFPointsNew(xy, pNum, isLoop);
mop = airMopNew();
- airMopAdd(mop, lpnt, (airMopper)limnPointsNix, airMopAlways);
- if (limnCBFCheck(fctx, lpnt)) {
+ airMopAdd(mop, lpnt, (airMopper)limnCBFPointsNix, airMopAlways);
+ if (limnCBFCtxCheck(fctx, lpnt)) {
biffAddf(LIMN, "%s: got bad args", me);
airMopError(mop);
return 1;
}
if (fctx->uu) {
- biffAddf(LIMN, "%s: not expecting limnCBFContext buffers allocated", me);
+ biffAddf(LIMN,
+ "%s: not expecting limnCBFCtx internal buffers to already "
+ "be allocated",
+ me);
airMopError(mop);
return 1;
}
- if (buffersNew(fctx, pNum)) {
- biffAddf(LIMN, "%s: failed to allocate buffers", me);
- airMopError(mop);
- return 1;
- }
- airMopAdd(mop, fctx, (airMopper)buffersNix, airMopAlways);
if (limnCBFCorners(&cornIdx, &cornNum, fctx, lpnt)) {
biffAddf(LIMN, "%s: trouble finding corners", me);
@@ -1204,12 +1261,6 @@
/*
TODO:
-rewrite things to use limnPointList, with first and last indices,
-naturally handling the case that last < first, with isLoop
-and new logic: isLoop does NOT depend on duplicate 1st,last coords
-and subtlty that if (with isLoop) hii = (loi-1 % #points) then using
-all points, with no notion of corner possible
-
testing corners: corners at start==stop of isLoop
corners not at start or stop of isLoop: do spline wrap around from last to first index?
@@ -1221,4 +1272,9 @@
(may want to pay in time for more economical representation)
valgrind everything
+
+remove big debugging comment blocks
+
+(DIM=2) explore what would be required to generalized from 2D to 3D,
+perhaps at least at the API level, even if 3D is not yet implemented
*/
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-20 21:03:48
|
Revision: 7165
http://sourceforge.net/p/teem/code/7165
Author: kindlmann
Date: 2024-06-20 21:03:47 +0000 (Thu, 20 Jun 2024)
Log Message:
-----------
refecting reality that Teem v2 is going to be next release
Modified Paths:
--------------
teem/trunk/python/cffi/README.md
Modified: teem/trunk/python/cffi/README.md
===================================================================
--- teem/trunk/python/cffi/README.md 2024-06-20 21:03:09 UTC (rev 7164)
+++ teem/trunk/python/cffi/README.md 2024-06-20 21:03:47 UTC (rev 7165)
@@ -1,6 +1,6 @@
# CFFI-based Python wrappers for Teem
-The `teem.py` in this directory is a new (as of Teem v1.13 in 2023) Python wrapper for all of Teem, built via CFFI. It does useful error handling, and has one or more helper/wrappers (including a Python object "foo" wrapping of "const airEnum \*const foo").
+The `teem.py` in this directory is a new (as of Teem v2 in 2024) Python wrapper for all of Teem, built via CFFI. It does useful error handling, and has one or more helper/wrappers (including a Python object "foo" wrapping of "const airEnum \*const foo").
These notes by GLK are mostly to document for future GLK what's involved in creating teem.py, but also for anyone else who is in a position to improve how Python users can access and benefit from Teem.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-20 21:03:11
|
Revision: 7164
http://sourceforge.net/p/teem/code/7164
Author: kindlmann
Date: 2024-06-20 21:03:09 +0000 (Thu, 20 Jun 2024)
Log Message:
-----------
fixing writing fumble in comment
Modified Paths:
--------------
teem/trunk/.clang-format
Modified: teem/trunk/.clang-format
===================================================================
--- teem/trunk/.clang-format 2024-06-18 17:40:30 UTC (rev 7163)
+++ teem/trunk/.clang-format 2024-06-20 21:03:09 UTC (rev 7164)
@@ -1,6 +1,6 @@
# Created by GLK in June 2022 as part of rebooting effort to do a Teem release
# Significant experimentation went into trying to match the existing style,
-# with with columns going from 79 to 89.
+# except for # columns going up from 79 to 89.
# This file is what provides info to "clang-format -style=file" when run in
# this directory or a subdirectory. The teem/src/_util/clang-format.sh
# script helps do this.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-18 17:40:33
|
Revision: 7163
http://sourceforge.net/p/teem/code/7163
Author: kindlmann
Date: 2024-06-18 17:40:30 +0000 (Tue, 18 Jun 2024)
Log Message:
-----------
synching with source
Modified Paths:
--------------
teem/trunk/python/cffi/cdef/cdef_hest.h
Modified: teem/trunk/python/cffi/cdef/cdef_hest.h
===================================================================
--- teem/trunk/python/cffi/cdef/cdef_hest.h 2024-06-18 17:30:00 UTC (rev 7162)
+++ teem/trunk/python/cffi/cdef/cdef_hest.h 2024-06-18 17:40:30 UTC (rev 7163)
@@ -258,7 +258,7 @@
extern void hestOptAddDeclsPrint(FILE *f);
/* Many many non-var-args alternatives to hestOptAdd, also usefully type-specific for the
type of value to be parsed in a way that hestOptAdd_nva cannot match. These capture all
-the common uses (and them some) of hest within Teem. They can be categorized, like
+the common uses (and then some) of hest within Teem. They can be categorized, like
hestOpt->kind, in terms of the min, max number of (type T) parameters to the option:
min == max == 0 hestOptAdd_Flag (stand-alone flag; no parameters)
min == max == 1 hestOptAdd_1_T single fixed parameter
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <kin...@us...> - 2024-06-18 17:30:02
|
Revision: 7162
http://sourceforge.net/p/teem/code/7162
Author: kindlmann
Date: 2024-06-18 17:30:00 +0000 (Tue, 18 Jun 2024)
Log Message:
-----------
fixing typo in comment
Modified Paths:
--------------
teem/trunk/src/hest/hest.h
Modified: teem/trunk/src/hest/hest.h
===================================================================
--- teem/trunk/src/hest/hest.h 2024-06-05 03:08:24 UTC (rev 7161)
+++ teem/trunk/src/hest/hest.h 2024-06-18 17:30:00 UTC (rev 7162)
@@ -291,7 +291,7 @@
HEST_EXPORT void hestOptAddDeclsPrint(FILE *f);
/* Many many non-var-args alternatives to hestOptAdd, also usefully type-specific for the
type of value to be parsed in a way that hestOptAdd_nva cannot match. These capture all
-the common uses (and them some) of hest within Teem. They can be categorized, like
+the common uses (and then some) of hest within Teem. They can be categorized, like
hestOpt->kind, in terms of the min, max number of (type T) parameters to the option:
min == max == 0 hestOptAdd_Flag (stand-alone flag; no parameters)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|