2019-07-14 20:37:26 +02:00
|
|
|
#include <stdlib.h>
|
2019-07-16 17:31:20 +02:00
|
|
|
// #include "debug.h"
|
2019-07-14 20:37:26 +02:00
|
|
|
#define DIM 2
|
|
|
|
|
|
|
|
void copyCoordinate(double* from, double* to) {
|
|
|
|
for (int i = 0; i < DIM; i++) {
|
|
|
|
to[i] = from[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// extends array and copies point on the end. Returns new length
|
|
|
|
int push(double* array, int length, double* point) {
|
|
|
|
int newLength = length + DIM;
|
|
|
|
// realloc(array, newLength * sizeof(double));
|
|
|
|
copyCoordinate(point, array + length);
|
|
|
|
return newLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
// square distance between 2 points
|
|
|
|
double getSqDist(double* p1, double* p2) {
|
|
|
|
double dx = p1[0] - p2[0],
|
|
|
|
dy = p1[1] - p2[1];
|
|
|
|
return dx * dx + dy * dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// square distance from a point to a segment
|
|
|
|
double getSqSegDist(double* p, double* p1, double* p2) {
|
|
|
|
double x = p1[0],
|
|
|
|
y = p1[1],
|
|
|
|
dx = p2[0] - x,
|
|
|
|
dy = p2[1] - y;
|
|
|
|
|
|
|
|
if (dx != 0 || dy != 0) {
|
|
|
|
double t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);
|
|
|
|
|
|
|
|
if (t > 1) {
|
|
|
|
x = p2[0];
|
|
|
|
y = p2[1];
|
|
|
|
|
|
|
|
} else if (t > 0) {
|
|
|
|
x += dx * t;
|
|
|
|
y += dy * t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dx = p[0] - x;
|
|
|
|
dy = p[1] - y;
|
|
|
|
|
|
|
|
return dx * dx + dy * dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
// basic distance-based simplification
|
2019-07-16 17:31:20 +02:00
|
|
|
int simplifyRadialDist(double* points, int length, double sqTolerance, double* result) {
|
2019-07-14 20:37:26 +02:00
|
|
|
double* prevPoint = points;
|
|
|
|
double* point;
|
2019-07-16 17:31:20 +02:00
|
|
|
int resultLength = push(result, 0, points);
|
2019-07-14 20:37:26 +02:00
|
|
|
|
2019-07-16 17:31:20 +02:00
|
|
|
for (int i = DIM; i < length; i += DIM) {
|
2019-07-14 20:37:26 +02:00
|
|
|
point = points + i;
|
|
|
|
if (getSqDist(point, prevPoint) > sqTolerance) {
|
2019-07-16 17:31:20 +02:00
|
|
|
resultLength = push(result, resultLength, point);
|
2019-07-14 20:37:26 +02:00
|
|
|
prevPoint = point;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prevPoint != point) {
|
2019-07-16 17:31:20 +02:00
|
|
|
resultLength = push(result, resultLength, point);
|
2019-07-14 20:37:26 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 17:31:20 +02:00
|
|
|
return resultLength;
|
2019-07-14 20:37:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int simplifyDPStep(double* points, int first, int last, double sqTolerance, double* simplified, int lengthOfSimplified) {
|
|
|
|
double maxSqDist = sqTolerance;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
for (int i = first + DIM; i < last; i += DIM) {
|
|
|
|
double sqDist = getSqSegDist(points + i, points + first, points + last);
|
|
|
|
|
|
|
|
if (sqDist > maxSqDist) {
|
|
|
|
index = i;
|
|
|
|
maxSqDist = sqDist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (maxSqDist > sqTolerance) {
|
|
|
|
if (index - first > DIM) lengthOfSimplified = simplifyDPStep(points, first, index, sqTolerance, simplified, lengthOfSimplified);
|
|
|
|
lengthOfSimplified = push(simplified, lengthOfSimplified, points + index);
|
|
|
|
if (last - index > DIM) lengthOfSimplified = simplifyDPStep(points, index, last, sqTolerance, simplified, lengthOfSimplified);
|
|
|
|
}
|
|
|
|
return lengthOfSimplified;
|
|
|
|
}
|
|
|
|
|
|
|
|
// simplification using Ramer-Douglas-Peucker algorithm
|
|
|
|
int simplifyDouglasPeucker(double* points, int length, double sqTolerance, double* simplified) {
|
|
|
|
int lengthOfSimplified;
|
|
|
|
lengthOfSimplified = push(simplified, 0, points);
|
|
|
|
lengthOfSimplified = simplifyDPStep(points, 0, length - DIM, sqTolerance, simplified, lengthOfSimplified);
|
|
|
|
lengthOfSimplified = push(simplified, lengthOfSimplified, points + length - DIM);
|
|
|
|
return lengthOfSimplified;
|
|
|
|
}
|
|
|
|
|
2019-07-16 17:31:20 +02:00
|
|
|
int* simplify(double * points, int length, double tolerance, int highestQuality) {
|
2019-07-14 20:37:26 +02:00
|
|
|
double sqTolerance = tolerance * tolerance;
|
|
|
|
double* resultRdDistance = NULL;
|
2019-07-16 17:31:20 +02:00
|
|
|
double* result = NULL;
|
|
|
|
int resultLength;
|
2019-07-14 20:37:26 +02:00
|
|
|
|
|
|
|
if (!highestQuality) {
|
|
|
|
resultRdDistance = malloc(length * sizeof(double));
|
|
|
|
length = simplifyRadialDist(points, length, sqTolerance, resultRdDistance);
|
|
|
|
points = resultRdDistance;
|
|
|
|
}
|
2019-07-16 17:31:20 +02:00
|
|
|
|
|
|
|
result = malloc(length * sizeof(double));
|
|
|
|
resultLength = simplifyDouglasPeucker(points, length, sqTolerance, result);
|
2019-07-14 20:37:26 +02:00
|
|
|
free(resultRdDistance);
|
|
|
|
|
|
|
|
int* resultInfo = malloc(2);
|
|
|
|
resultInfo[0] = (int) result;
|
|
|
|
resultInfo[1] = resultLength;
|
|
|
|
return resultInfo;
|
|
|
|
}
|