/* Gerris - The GNU Flow Solver
* Copyright (C) 2010-2012 National Institute of Water and Atmospheric Research
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include "kdt.h"
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define VERSION 20120405 /* the file format version */
/* same as the system tmpfile() but uses the working directory (rather
than /tmp) */
FILE * kdt_tmpfile (void)
{
char name[] = "kdtXXXXXX";
int fd = mkstemp (name);
if (fd == -1) {
perror ("kdt_tmpfile");
exit (1);
}
FILE * fp = fdopen (fd, "r+w");
assert (unlink (name) == 0);
if (fp == NULL) {
perror ("kdt_tmpfile");
exit (1);
}
return fp;
}
/* refcounted buffer */
typedef struct {
KdtPoint * p;
int ref;
} Buffer;
static Buffer * buffer_new (long len)
{
Buffer * b = malloc (sizeof (Buffer));
b->p = malloc (len*sizeof (KdtPoint));
b->ref = 1;
return b;
}
static Buffer * buffer_ref (Buffer * b)
{
b->ref++;
return b;
}
static void buffer_unref (Buffer * b)
{
b->ref--;
if (b->ref == 0) {
free (b->p);
free (b);
}
}
/* KdtHeap */
void kdt_heap_resize (KdtHeap * h, long len)
{
assert (h->len < 0 || len < h->len);
if (h->len == h->buflen) {
h->buflen = len;
h->end = h->buflen;
}
else if (len <= h->buflen) {
h->buflen = len;
kdt_heap_rewind (h);
assert (h->end == len);
}
h->len = len;
}
static long heap_read (KdtHeap * h, long len)
{
if (ftell (h->fp) != h->current)
assert (fseek (h->fp, h->current, SEEK_SET) == 0);
if (h->len > 0) {
long maxlen = h->start + h->len - h->current/sizeof (KdtPoint);
if (len > maxlen)
len = maxlen;
}
long n = 0;
if (len > 0) {
n = fread (h->p, sizeof (KdtPoint), len, h->fp);
h->current = ftell (h->fp);
}
return n;
}
static void heap_write (KdtHeap * h, long len)
{
if (ftell (h->fp) != h->current)
assert (fseek (h->fp, h->current, SEEK_SET) == 0);
if (fwrite (h->p, sizeof (KdtPoint), len, h->fp) != len) {
perror ("heap_write");
exit (1);
}
h->current = ftell (h->fp);
}
void kdt_heap_create (KdtHeap * h, FILE * fp, long start, long len, long buflen)
{
h->fp = fp;
h->start = start;
if (len > 0 && len < buflen)
buflen = len;
h->len = len;
h->buflen = buflen;
h->i = 0;
h->buf = buffer_new (buflen);
h->p = ((Buffer *) h->buf)->p;
h->current = start*sizeof (KdtPoint);
if (fp != NULL) {
assert (fseek (fp, start*sizeof (KdtPoint), SEEK_SET) == 0);
assert (ftell (fp) == h->current);
h->end = heap_read (h, buflen);
if (buflen == len)
assert (h->end == len);
}
else
h->end = 0;
}
void kdt_heap_rewind (KdtHeap * h)
{
if (h->len == h->buflen) {
h->i = 0;
assert (h->end == h->buflen);
}
else {
assert (fseek (h->fp, h->start*sizeof (KdtPoint), SEEK_SET) == 0);
h->current = ftell (h->fp);
h->end = heap_read (h, h->buflen);
h->i = 0;
}
}
int kdt_heap_get (KdtHeap * h, KdtPoint * p)
{
if (h->len == h->buflen && h->i >= h->len)
return 0;
if (h->i < h->end) {
*p = h->p[h->i++];
return 1;
}
if (h->end < h->buflen)
return 0;
h->end = heap_read (h, h->buflen);
h->i = 0;
return kdt_heap_get (h, p);
}
void kdt_heap_split (KdtHeap * h1, long len1, KdtHeap * h2)
{
assert (len1 < h1->len);
if (h1->len == h1->buflen) {
h2->fp = NULL;
h2->start = 0;
h2->len = h1->len - len1;
h2->buflen = h2->len;
h2->i = 0;
h2->p = &h1->p[len1];
h2->buf = buffer_ref (h1->buf);
h2->end = h2->len;
kdt_heap_resize (h1, len1);
}
else {
kdt_heap_create (h2, h1->fp, h1->start + len1, h1->len - len1, h1->buflen);
KdtHeap h;
kdt_heap_create (&h, NULL, 0, len1, h1->buflen);
if (len1 > h1->buflen)
h.fp = kdt_tmpfile ();
else
h.end = h.len;
kdt_heap_rewind (h1);
long i;
for (i = 0; i < len1; i++) {
KdtPoint p;
assert (kdt_heap_get (h1, &p));
kdt_heap_put (&h, &p);
}
kdt_heap_flush (&h);
h1->fp = NULL;
kdt_heap_free (h1);
*h1 = h;
}
}
void kdt_heap_put (KdtHeap * h, KdtPoint * p)
{
if (h->i == h->buflen) {
heap_write (h, h->buflen);
h->i = 0;
}
h->p[h->i++] = *p;
}
void kdt_heap_flush (KdtHeap * h)
{
if (h->i > 0 && h->fp != NULL)
heap_write (h, h->i);
}
void kdt_heap_free (KdtHeap * h)
{
buffer_unref (h->buf);
if (h->fp != NULL)
assert (fclose (h->fp) == 0);
}
/* sort */
static int put (KdtHeap * h, KdtPoint * p, KdtHeap * merged)
{
kdt_heap_put (merged, p);
return kdt_heap_get (h, p);
}
void kdt_write (KdtHeap * h, FILE * fp)
{
kdt_heap_rewind (h);
long i = 0;
KdtPoint p;
while (kdt_heap_get (h, &p)) {
fprintf (fp, "%ld %g %g\n", i, p.x, p.y);
i++;
}
}
static void merge (KdtHeap * h1, KdtHeap * h2,
int (*compar) (const void *, const void *),
long buflen)
{
KdtHeap hm;
assert (h1->len + h2->len > buflen);
kdt_heap_create (&hm, NULL, h2->start - h1->len, h1->len + h2->len, buflen);
hm.fp = h2->fp;
KdtPoint p1, p2;
kdt_heap_rewind (h1);
int r1 = kdt_heap_get (h1, &p1);
kdt_heap_rewind (h2);
int r2 = kdt_heap_get (h2, &p2);
while (r1 && r2) {
if ((* compar) (&p2, &p1))
r1 = put (h1, &p1, &hm);
else
r2 = put (h2, &p2, &hm);
}
while (r1)
r1 = put (h1, &p1, &hm);
while (r2)
r2 = put (h2, &p2, &hm);
kdt_heap_free (h1);
h2->fp = NULL;
kdt_heap_free (h2);
kdt_heap_flush (&hm);
*h1 = hm;
}
#if TIMING
static double elapsed (const struct timeval * start, const struct timeval * end)
{
return (double) (end->tv_usec - start->tv_usec) +
1e6*(double) (end->tv_sec - start->tv_sec);
}
#endif
static void kdt_heap_sort (KdtHeap * h,
int (*compar) (const void *, const void *),
void (*progress) (void *), void * data)
{
#if TIMING
struct timeval start;
gettimeofday (&start, NULL);
#endif
if (h->len == h->buflen) {
qsort (h->p, h->len, sizeof (KdtPoint), compar);
if (progress)
(* progress) (data);
}
else {
KdtHeap h2;
long buflen = h->buflen;
kdt_heap_split (h, h->len/2, &h2);
kdt_heap_sort (h, compar, progress, data);
kdt_heap_sort (&h2, compar, progress, data);
merge (h, &h2, compar, buflen);
}
#if TIMING
struct timeval end;
gettimeofday (&end, NULL);
fprintf (stderr, "kdt_heap_sort %ld %g\n", len, elapsed (&start, &end));
#endif
}
/* number of qsort() calls for a given kdt_heap_sort() */
static int kdt_heap_sort_cost (long len, long buflen)
{
int m = 1;
while (len > buflen) {
m *= 2;
len /= 2;
}
return m;
}
/* Kdt */
typedef struct {
KdtRect bound1, bound2;
long len1;
int n1;
} Node;
typedef struct {
KdtRect bound;
long len;
PADDING_32_BITS
long np;
int version;
} Header;
struct _Kdt {
Header h;
FILE * nodes, * sums, * leaves;
KdtPoint * buffer;
/* progress stuff */
void (* progress) (float complete, void * data);
void * data;
int i, m;
};
static int check_32_bits (const Kdt * kdt)
{
#if (!defined (__LP64__) && !defined (__64BIT__) && \
!defined (_LP64) && !(__WORDSIZE == 64))
long maxlen = (1 << 31)/sizeof (KdtPoint);
if (kdt->h.len > maxlen) {
fprintf (stderr, "kdt: 32-bits systems are limited to %ld data points\n",
maxlen);
return 1;
}
#endif
return 0;
}
static void sizes (const Node * n, long * nodes, long * sums, long * leaves)
{
*nodes = n->n1*sizeof (Node);
*sums = n->n1*sizeof (KdtSumCore);
*leaves = n->len1*sizeof (KdtPoint);
}
static void relative (const KdtRect rect, double * o, double * h)
{
o[0] = (((double)rect[0].l) + ((double)rect[0].h))/2.;
o[1] = (((double)rect[1].l) + ((double)rect[1].h))/2.;
*h = ((double)rect[0].h) - ((double)rect[0].l);
if (((double)rect[1].h) - ((double)rect[1].l) > *h)
*h = ((double)rect[1].h) - ((double)rect[1].l);
}
static void sum_add_point (const KdtRect parent, KdtSumCore * sum,
const KdtPoint * a, double w)
{
double p[3], o[2], h;
relative (parent, o, &h);
p[0] = (a->x - o[0])/h; p[1] = (a->y - o[1])/h; p[2] = a->z;
#if AVG_TERRAIN
sum->H0 += p[2];
sum->n++;
if (p[2] < sum->Hmin) sum->Hmin = p[2];
if (p[2] > sum->Hmax) sum->Hmax = p[2];
#else
sum->m01 += w*p[0];
sum->m02 += w*p[1];
sum->m03 += w*p[0]*p[1];
sum->m11 += w*p[0]*p[0];
sum->m13 += w*p[0]*p[0]*p[1];
sum->m22 += w*p[1]*p[1];
sum->m23 += w*p[0]*p[1]*p[1];
sum->m33 += w*p[0]*p[0]*p[1]*p[1];
sum->m44 += w*p[0]*p[0]*p[0];
sum->m55 += w*p[1]*p[1]*p[1];
sum->m66 += w*p[0]*p[0]*p[0]*p[0];
sum->m77 += w*p[1]*p[1]*p[1]*p[1];
sum->m67 += w*p[0]*p[0]*p[0]*p[1];
sum->m76 += w*p[1]*p[1]*p[1]*p[0];
sum->H0 += w*p[2];
sum->H1 += w*p[0]*p[2];
sum->H2 += w*p[1]*p[2];
sum->H3 += w*p[0]*p[1]*p[2];
sum->H4 += w*p[2]*p[2];
sum->H5 += w*p[0]*p[0]*p[2];
sum->H6 += w*p[1]*p[1]*p[2];
sum->n ++;
if (p[2] < sum->Hmin) sum->Hmin = p[2];
if (p[2] > sum->Hmax) sum->Hmax = p[2];
#endif
}
static float area (const KdtRect rect)
{
return (rect[0].h - rect[0].l)*(rect[1].h - rect[1].l);
}
static float length (const KdtRect rect)
{
float w = rect[0].h - rect[0].l, h = rect[1].h - rect[1].l;
return w > h ? w : h;
}
void kdt_rect_write (const KdtRect rect, FILE * fp)
{
fprintf (fp, "%f %f\n%f %f\n%f %f\n%f %f\n%f %f\n\n",
rect[0].l, rect[1].l, rect[0].h, rect[1].l,
rect[0].h, rect[1].h, rect[0].l, rect[1].h,
rect[0].l, rect[1].l);
}
static int sort_x (const void * p1, const void * p2)
{
return ((KdtPoint *) p1)->x > ((KdtPoint *) p2)->x ? 1 : -1;
}
static int sort_y (const void * p1, const void * p2)
{
return ((KdtPoint *) p1)->y > ((KdtPoint *) p2)->y ? 1 : -1;
}
static void kdt_sum_core_init (KdtSumCore * s)
{
memset (s, 0, sizeof (KdtSumCore));
s->Hmax = - 1e30;
s->Hmin = 1e30;
}
#define GAP 0.2
#define MINLEN 6
static int update_sum (const KdtRect rect, KdtSumCore * n, KdtHeap * h,
int index)
{
kdt_sum_core_init (n);
long i, imax = 0;
double min, x, smax = 0;
KdtPoint p;
kdt_heap_rewind (h);
assert (kdt_heap_get (h, &p));
sum_add_point (rect, n, &p, 1.);
min = x = (&p.x)[index];
for (i = 1; i < h->len; i++) {
assert (kdt_heap_get (h, &p));
sum_add_point (rect, n, &p, 1.);
double s = (&p.x)[index] - x;
if (s > smax && i > MINLEN && i < h->len - MINLEN) {
smax = s;
imax = i;
}
x = (&p.x)[index];
}
return smax/(x - min) > GAP ? imax : -1;
}
static long update_bounds (KdtRect rect, KdtHeap * h)
{
long len = 0;
rect[0].h = rect[1].h = -1e30;
rect[0].l = rect[1].l = 1e30;
kdt_heap_rewind (h);
KdtPoint p;
while (kdt_heap_get (h, &p)) {
if (p.x > rect[0].h) rect[0].h = p.x;
if (p.x < rect[0].l) rect[0].l = p.x;
if (p.y > rect[1].h) rect[1].h = p.y;
if (p.y < rect[1].l) rect[1].l = p.y;
len++;
}
return len;
}
static void progress (void * data)
{
Kdt * kdt = data;
if (kdt->progress && kdt->m > 0)
(* kdt->progress) (++kdt->i/(float) kdt->m, kdt->data);
}
static void fwrite_check (const void * ptr, size_t size, size_t nmemb,
FILE * stream)
{
if (fwrite (ptr, size, nmemb, stream) != nmemb) {
perror ("kdt_write");
exit (1);
}
}
static void union_bound (KdtRect b, const KdtRect b1, const KdtRect b2)
{
b[0].l = MIN (b1[0].l, b2[0].l);
b[1].l = MIN (b1[1].l, b2[1].l);
b[0].h = MAX (b1[0].h, b2[0].h);
b[1].h = MAX (b1[1].h, b2[1].h);
}
static int split (KdtHeap * h1, KdtRect bound, int index, Kdt * kdt,
float * coverage)
{
#if TIMING
struct timeval start;
gettimeofday (&start, NULL);
#endif
int ns = 0;
if (h1->len > kdt->h.np) {
// fprintf (stderr, " splitting: %ld \r", len);
int nindex = (bound[0].h - bound[0].l < bound[1].h - bound[1].l);
if (index != nindex) {
kdt_heap_sort (h1, nindex ? sort_y : sort_x, progress, kdt);
index = nindex;
}
else
/* update cost estimate */
kdt->m -= kdt_heap_sort_cost (h1->len, h1->buflen);
KdtSumCore s;
int imax = update_sum (bound, &s, h1, index);
long spos = ftell (kdt->sums);
fwrite_check (&s, sizeof (KdtSumCore), 1, kdt->sums);
Node n;
n.len1 = imax > 0 ? imax : h1->len/2;
#if TIMING
struct timeval s1;
gettimeofday (&s1, NULL);
#endif
KdtHeap h2;
kdt_heap_split (h1, n.len1, &h2);
#if TIMING
struct timeval end;
gettimeofday (&end, NULL);
fprintf (stderr, "half %ld %f\n", len, elapsed (&s1, &end));
#endif
update_bounds (n.bound1, h1);
update_bounds (n.bound2, &h2);
long pos = ftell (kdt->nodes);
fwrite_check (&n, sizeof (Node), 1, kdt->nodes);
float coverage1;
n.n1 = split (h1, n.bound1, index, kdt, &coverage1);
float coverage2;
int n2 = split (&h2, n.bound2, index, kdt, &coverage2);
ns = n.n1 + n2 + 1;
/* update bound */
union_bound (bound, n.bound1, n.bound2);
/* update sums */
double a = area (bound);
if (a > 0.)
s.coverage = (coverage1*area (n.bound1) + coverage2*area (n.bound2))/a;
else
s.coverage = 1.;
assert (fseek (kdt->sums, spos + ((long) &s.coverage - (long) &s),
SEEK_SET) == 0);
fwrite_check (&s.coverage, sizeof (float), 1, kdt->sums);
assert (fseek (kdt->sums, 0, SEEK_END) == 0);
*coverage = s.coverage;
/* update node */
assert (fseek (kdt->nodes, pos, SEEK_SET) == 0);
fwrite_check (&n, sizeof (Node), 1, kdt->nodes);
assert (fseek (kdt->nodes, 0, SEEK_END) == 0);
}
else {
assert (h1->len > 0);
/* half the average distance between samples */
double delta = length (bound)/sqrt (h1->len)/2.;
bound[0].l -= delta;
bound[1].l -= delta;
bound[0].h += delta;
bound[1].h += delta;
#if DEBUG
kdt_rect_write (bound, stderr);
#endif
assert (h1->len <= h1->buflen);
fwrite_check (h1->p, sizeof (KdtPoint), h1->len, kdt->leaves);
kdt_heap_free (h1);
*coverage = 1.;
}
#if TIMING
struct timeval end;
gettimeofday (&end, NULL);
fprintf (stderr, "splitfile %ld %f\n", len, elapsed (&start, &end));
#endif
return ns;
}
Kdt * kdt_new (void)
{
Kdt * kdt = calloc (1, sizeof (Kdt));
return kdt;
}
static FILE * open_ext (const char * name, const char * ext, const char * mode)
{
int len = strlen (name), len1 = strlen (ext);
char * fname = malloc (sizeof(char)*(len + len1 + 1));
strcpy (fname, name);
strcpy (&fname[len], ext);
FILE * fp = fopen (fname, mode);
free (fname);
#if BUFFERED_KDT
if (fp && strchr (mode, 'r')) {
fseek (fp, 0, SEEK_END);
long fsize = ftell (fp);
fseek (fp, 0, SEEK_SET);
char * buf = malloc (fsize);
size_t len = fread (buf, 1, fsize, fp);
fclose (fp);
fp = fmemopen (buf, len, mode);
}
#endif /* BUFFERED_KDT */
return fp;
}
static int kdt_init (Kdt * kdt, const char * name, int npmax, long len)
{
kdt->nodes = open_ext (name, ".kdt", "w");
if (!kdt->nodes)
return -1;
kdt->sums = open_ext (name, ".sum", "w");
if (!kdt->sums)
return -1;
kdt->leaves = open_ext (name, ".pts", "w");
if (!kdt->leaves)
return -1;
kdt->h.version = VERSION;
kdt->h.len = len;
kdt->h.np = npmax;
kdt->h.bound[0].l = kdt->h.bound[1].l = 1e30;
kdt->h.bound[0].h = kdt->h.bound[1].h = -1e30;
if (check_32_bits (kdt))
return -1;
return 0;
}
int kdt_create (Kdt * kdt, const char * name, int blksize,
KdtHeap * h,
void (* progress) (float complete, void * data),
void * data)
{
KdtRect bound;
long len = update_bounds (bound, h);
kdt_heap_resize (h, len);
int npmax = blksize/sizeof (KdtPoint);
if (kdt_init (kdt, name, npmax, len))
return -1;
kdt->h.bound[0] = bound[0];
kdt->h.bound[1] = bound[1];
fwrite_check (&kdt->h, sizeof (Header), 1, kdt->nodes);
/* cost estimate kdt->m (number of qsort() calls) based on balanced
binary tree */
kdt->m = kdt->i = 0;
int m2 = 1;
while (len > kdt->h.np) {
kdt->m += kdt_heap_sort_cost (len, h->buflen)*m2;
len /= 2;
m2 *= 2;
}
kdt->progress = progress;
kdt->data = data;
float coverage;
split (h, kdt->h.bound, -1, kdt, &coverage);
/* write updated header (bounds have been updated) */
rewind (kdt->nodes);
fwrite_check (&kdt->h, sizeof (Header), 1, kdt->nodes);
return 0;
}
int kdt_open (Kdt * kdt, const char * name)
{
kdt->nodes = open_ext (name, ".kdt", "r");
if (!kdt->nodes)
return -1;
kdt->sums = open_ext (name, ".sum", "r");
if (!kdt->sums)
return -1;
kdt->leaves = open_ext (name, ".pts", "r");
if (!kdt->leaves)
return -1;
if (fread (&kdt->h, sizeof (Header), 1, kdt->nodes) != 1)
return -1;
if (kdt->h.version != VERSION) {
fprintf (stderr,
"kdt: incompatible version number. Use:\n"
"%% kdt2kdt -v %s\n"
"to convert to the new format.\n",
name);
return -1;
}
kdt->buffer = malloc (sizeof (KdtPoint)*kdt->h.np);
if (check_32_bits (kdt))
return -1;
return 0;
}
void kdt_destroy (Kdt * kdt)
{
if (kdt->nodes)
fclose (kdt->nodes);
if (kdt->sums)
fclose (kdt->sums);
if (kdt->leaves)
fclose (kdt->leaves);
if (kdt->buffer)
free (kdt->buffer);
free (kdt);
}
int kdt_intersects (const KdtRect rect, const KdtRect query)
{
return (rect[0].l <= query[0].h && rect[1].l <= query[1].h &&
rect[0].h >= query[0].l && rect[1].h >= query[1].l);
}
int kdt_includes (const KdtRect rect, const KdtRect query)
{
return (rect[0].h <= query[0].h && rect[1].h <= query[1].h &&
rect[0].l >= query[0].l && rect[1].l >= query[1].l);
}
static long query (const Kdt * kdt, const KdtRect rect, long len)
{
if (len > kdt->h.np) {
Node node;
if (fread (&node, sizeof (Node), 1, kdt->nodes) != 1)
return -1;
long pos = ftell (kdt->nodes), lpos = ftell (kdt->leaves);
if (pos < 0 || lpos < 0)
return -1;
long n = 0;
if (kdt_intersects (node.bound1, rect)) {
#if DEBUG
kdt_rect_write (node.bound1, stderr);
#endif
long n1 = query (kdt, rect, node.len1);
if (n1 < 0)
return -1;
n += n1;
}
if (kdt_intersects (node.bound2, rect)) {
#if DEBUG
kdt_rect_write (node.bound2, stderr);
#endif
long snodes, ssums, sleaves;
sizes (&node, &snodes, &ssums, &sleaves);
if (fseek (kdt->nodes, pos + snodes, SEEK_SET))
return -1;
if (fseek (kdt->leaves, lpos + sleaves, SEEK_SET))
return -1;
long n1 = query (kdt, rect, len - node.len1);
if (n1 < 0)
return -1;
n += n1;
}
return n;
}
else if (len > 0) {
if (fread (kdt->buffer, sizeof (KdtPoint), len, kdt->leaves) != len)
return -1;
int i, n = 0;
for (i = 0; i < len; i++)
if (kdt->buffer[i].x >= rect[0].l && kdt->buffer[i].x <= rect[0].h &&
kdt->buffer[i].y >= rect[1].l && kdt->buffer[i].y <= rect[1].h) {
printf ("%.8f %.8f %f\n",
kdt->buffer[i].x, kdt->buffer[i].y, kdt->buffer[i].z);
n++;
}
return n;
}
return 0;
}
long kdt_query (const Kdt * kdt, const KdtRect rect)
{
rewind (kdt->nodes);
rewind (kdt->leaves);
Header h;
if (fread (&h, sizeof (Header), 1, kdt->nodes) != 1)
return -1;
if (!kdt_intersects (rect, h.bound))
return 0;
return query (kdt, rect, h.len);
}
static void intersection (const KdtRect rect1, const KdtRect rect2,
KdtRect inter)
{
inter[0].l = MAX (rect1[0].l, rect2[0].l);
inter[0].h = MIN (rect1[0].h, rect2[0].h);
inter[1].l = MAX (rect1[1].l, rect2[1].l);
inter[1].h = MIN (rect1[1].h, rect2[1].h);
assert (inter[0].h >= inter[0].l && inter[1].h >= inter[1].l);
}
static float intersection_area (const KdtRect rect1, const KdtRect rect2)
{
KdtRect inter;
intersection (rect1, rect2, inter);
return area (inter);
}
static void sum_add_sum (const KdtRect parent, KdtSum * sum,
const KdtRect rect, const KdtSumCore * a)
{
/* weigh by effective area per sample */
double w = intersection_area (rect, parent)*a->coverage/a->n;
if (w == 0.)
return;
double op[2], oa[2], hp, ha;
relative (parent, op, &hp);
relative (rect, oa, &ha);
double oap0 = oa[0] - op[0], oap1 = oa[1] - op[1];
double an = a->n;
double ha2 = ha*ha, hp2 = hp*hp;
sum->m01 += w*(an*oap0 + a->m01*ha)/hp;
sum->m02 += w*(an*oap1 + a->m02*ha)/hp;
sum->m03 += w*(oap0*(an*oap1 + a->m02*ha) + ha*(a->m01*oap1 + a->m03*ha))/hp2;
double m11 = (oap0*(an*oap0 + 2.*a->m01*ha) + a->m11*ha2)/hp2;
sum->m11 += w*m11;
double m13 = ha*(oap0*(a->m02*oap0 + 2.*a->m03*ha) + a->m13*ha2)/hp2;
sum->m13 += w*(oap1*m11 + m13)/hp;
double m22 = (oap1*(an*oap1 + 2.*a->m02*ha) + a->m22*ha2)/hp2;
sum->m22 += w*m22;
sum->m23 += w*(oap0*m22 + ha*(oap1*(oap1*a->m01 + 2.*a->m03*ha) +
a->m23*ha2)/hp2)/hp;
sum->m33 += w*(oap1*(oap1*m11 + 2.*m13) +
ha2*(oap0*(oap0*a->m22 + 2.*a->m23*ha) + ha2*a->m33)/hp2)/hp2;
double ha3 = ha2*ha, hp3 = hp2*hp;
sum->m44 += w*(oap0*(oap0*(oap0*an + 3.*ha*a->m01) + 3.*ha2*a->m11) +
ha3*a->m44)/hp3;
sum->m55 += w*(oap1*(oap1*(oap1*an + 3.*ha*a->m02) + 3.*ha2*a->m22) +
ha3*a->m55)/hp3;
double ha4 = ha3*ha, hp4 = hp3*hp;
sum->m66 += w*(oap0*(oap0*(oap0*(oap0*an + 4.*ha*a->m01) + 6.*ha2*a->m11)
+ 4.*ha3*a->m44) + ha4*a->m66)/hp4;
sum->m77 += w*(oap1*(oap1*(oap1*(oap1*an + 4.*ha*a->m02) + 6.*ha2*a->m22)
+ 4.*ha3*a->m55) + ha4*a->m77)/hp4;
sum->m67 += w*(oap1*(oap0*(oap0*(oap0*an + 3.*ha*a->m01) + 3.*ha2*a->m11) +
ha3*a->m44)
+ oap0*(oap0*(ha*a->m02*oap0 + 3.*ha2*a->m03) + 3.*ha3*a->m13)
+ ha4*a->m67)/hp4;
sum->m76 += w*(oap0*(oap1*(oap1*(oap1*an + 3.*ha*a->m02) + 3.*ha2*a->m22) +
ha3*a->m55)
+ oap1*(oap1*(ha*a->m01*oap1 + 3.*ha2*a->m03) + 3.*ha3*a->m23)
+ ha4*a->m76)/hp4;
sum->H0 += w*a->H0;
sum->H1 += w*(a->H0*oap0 + a->H1*ha)/hp;
sum->H2 += w*(a->H0*oap1 + a->H2*ha)/hp;
sum->H3 += w*(ha*(ha*a->H3 + oap0*a->H2 + oap1*a->H1) + oap0*oap1*a->H0)/hp2;
sum->H4 += w*a->H4;
sum->H5 += w*(oap0*(2.*ha*a->H1 + oap0*a->H0) + ha2*a->H5)/hp2;
sum->H6 += w*(oap1*(2.*ha*a->H2 + oap1*a->H0) + ha2*a->H6)/hp2;
sum->n += w*a->n*a->n/area (rect);
float aparent = area (parent);
if (aparent > 0.)
sum->coverage += w*a->n/aparent;
else
sum->coverage = 1.;
sum->w += w*a->n;
if (a->Hmin < sum->Hmin) sum->Hmin = a->Hmin;
if (a->Hmax > sum->Hmax) sum->Hmax = a->Hmax;
}
typedef struct {
long np, sp, lp;
} FilePointers;
static long query_sum (const Kdt * kdt,
KdtCheck includes, KdtCheck intersects, void * data,
KdtRect bound, long len,
FilePointers * f,
const KdtRect query, KdtSum * sum)
{
if (len > kdt->h.np) {
if (length (bound) <= length (query) || (* includes) (bound, data)) {
KdtSumCore s;
if (fseek (kdt->sums, f->sp, SEEK_SET))
return -1;
if (fread (&s, sizeof (KdtSumCore), 1, kdt->sums) != 1)
return -1;
#if DEBUG
fprintf (stderr, "read 1 sum %ld\n", sizeof (KdtSumCore));
#endif
f->sp += sizeof (KdtSumCore);
sum_add_sum (query, sum, bound, &s);
return len;
}
f->sp += sizeof (KdtSumCore);
Node node;
if (fseek (kdt->nodes, f->np, SEEK_SET))
return -1;
if (fread (&node, sizeof (Node), 1, kdt->nodes) != 1)
return -1;
f->np += sizeof (Node);
#if DEBUG
fprintf (stderr, "read 1 node %ld\n", sizeof (Node));
#endif
long pos = f->np, lpos = f->lp, spos = f->sp;
long n = 0;
if ((* intersects) (node.bound1, data)) {
long n1 = query_sum (kdt, includes, intersects, data,
node.bound1, node.len1, f, query, sum);
if (n1 < 0)
return -1;
n += n1;
}
if ((* intersects) (node.bound2, data)) {
long snodes, ssums, sleaves;
sizes (&node, &snodes, &ssums, &sleaves);
f->np = pos + snodes;
f->sp = spos + ssums;
f->lp = lpos + sleaves;
long n1 = query_sum (kdt, includes, intersects, data,
node.bound2, len - node.len1, f, query, sum);
if (n1 < 0)
return -1;
n += n1;
}
return n;
}
else {
float h = length (bound)/sqrt (len); /* average distance between samples */
if (h <= length (query)) {
#if DEBUG
fprintf (stderr, "# area: %f %f\n", area (query), area (bound)/len);
kdt_rect_write (bound, stderr);
#endif
if (fseek (kdt->leaves, f->lp, SEEK_SET))
return -1;
if (fread (kdt->buffer, sizeof (KdtPoint), len, kdt->leaves) != len)
return -1;
#if DEBUG
fprintf (stderr, "read %ld leaves %ld\n", len, sizeof (KdtPoint));
#endif
KdtPoint * a = kdt->buffer;
int i, n = 0;
for (i = 0; i < len; i++, a++) {
KdtRect boundp;
boundp[0].l = a->x - h/2.;
boundp[0].h = a->x + h/2.;
boundp[1].l = a->y - h/2.;
boundp[1].h = a->y + h/2.;
if ((* intersects) (boundp, data)) {
double w = intersection_area (boundp, query);
sum_add_point (query, (KdtSumCore *) sum, a, w);
sum->w += w;
sum->coverage += w/area (query);
n++;
}
}
return n;
}
}
return 0;
}
long kdt_query_sum (const Kdt * kdt,
KdtCheck includes, KdtCheck intersects, void * data,
const KdtRect query, KdtSum * sum)
{
rewind (kdt->nodes);
rewind (kdt->leaves);
Header h;
if (fread (&h, sizeof (Header), 1, kdt->nodes) != 1)
return -1;
FilePointers f;
f.np = sizeof (Header);
f.sp = f.lp = 0;
if (!(* intersects) (h.bound, data))
return 0;
return query_sum (kdt, includes, intersects, data, h.bound, h.len, &f,
query, sum);
}
void kdt_sum_init (KdtSum * s)
{
kdt_sum_core_init ((KdtSumCore *) s);
s->w = 0.;
}