Skip to content

Commit

Permalink
version 0.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Dahl Jacobsen authored and cran-robot committed Jul 5, 2023
1 parent 45b9391 commit 4b6332d
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 48 deletions.
8 changes: 4 additions & 4 deletions DESCRIPTION
@@ -1,20 +1,20 @@
Package: RDP
Title: The Ramer-Douglas-Peucker Algorithm
Version: 0.2.3
Version: 0.3.0
Authors@R: person("Robert", "Dahl Jacobsen", role = c("aut", "cre"), email = "cran@dahl-jacobsen.dk")
Description: Pretty fast implementation of the Ramer-Douglas-Peucker algorithm for reducing the number of points on a 2D curve.
Urs Ramer (1972), "An iterative procedure for the polygonal approximation of plane curves" <doi:10.1016/S0146-664X(72)80017-0>.
David H. Douglas and Thomas K. Peucker (1973), "Algorithms for the Reduction of the Number of Points Required to Represent a Digitized Line or its Caricature" <doi:10.3138/FM57-6770-U75U-7727>.
License: GPL-3
URL: https://github.com/robertdj/RDP
Encoding: UTF-8
RoxygenNote: 7.1.2
RoxygenNote: 7.2.1
LinkingTo: Rcpp
Imports: Rcpp
Suggests: testthat, withr, zeallot
NeedsCompilation: yes
Packaged: 2022-03-17 19:33:52 UTC; robert
Packaged: 2023-07-05 21:57:55 UTC; robert
Author: Robert Dahl Jacobsen [aut, cre]
Maintainer: Robert Dahl Jacobsen <cran@dahl-jacobsen.dk>
Repository: CRAN
Date/Publication: 2022-03-17 20:00:02 UTC
Date/Publication: 2023-07-05 22:13:04 UTC
18 changes: 9 additions & 9 deletions MD5
@@ -1,16 +1,16 @@
65121eeca10f7955916a9cb86033d11b *DESCRIPTION
307eea5b522c518f3280336a284b13c0 *DESCRIPTION
a6892181c742d67c91932b7a9ae9a1b1 *NAMESPACE
94346c4beb59f83a249106424ee7124d *R/RDP-package.R
0c1d68865f8d1fd242be2afd2091e969 *R/RcppExports.R
3da93c72d3d6fdfc2deb8dd51ff28bd9 *build/partial.rdb
15d2f288101747e10c53c61e3decd507 *R/RcppExports.R
0c6ad5b3446721162c89589e8077ab1c *build/partial.rdb
537e8955e966c4fcaa06a6403b734a66 *man/RDP-package.Rd
e64af47a563757357d5238467d7bb2f3 *man/RamerDouglasPeucker.Rd
5bd1528059d024806f80b6d56c6ef48f *man/RamerDouglasPeucker.Rd
f83ff26c302dc4420b1b693e49719072 *man/figures/README-example-1.png
daf246d2559a7df53698b1714f020b7a *src/Makevars
daf246d2559a7df53698b1714f020b7a *src/Makevars.win
e337a01300ab4bf35b015446439090f2 *src/RamerDouglasPeucker.cpp
561f9103c77931ac481416cdec955565 *src/RamerDouglasPeucker.h
717ec3dc1097c9e3ba69583eb232b940 *src/RcppExports.cpp
796697c13da38f09a8b87ed424015e50 *src/wrapper.cpp
a6a086927824b68da0ac55607f7c2c32 *src/RamerDouglasPeucker.cpp
d8ae92e21747cce3bdc3af0ccb344dbc *src/RamerDouglasPeucker.h
de56ffc5ab7fd9bf89c22c53f2cf6030 *src/RcppExports.cpp
e6aca099b9b0b57001bed47704555043 *src/wrapper.cpp
be8c94f200a61296bfa381fb42c8f65e *tests/testthat.R
1abbbeb5341fb797ac8bca4b2f7f0368 *tests/testthat/test-RamerDouglasPeucker.R
e6e1b02af05aa76515b04acf8849507e *tests/testthat/test-RamerDouglasPeucker.R
17 changes: 10 additions & 7 deletions R/RcppExports.R
@@ -1,25 +1,28 @@
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#' Ramer-Douglas-Peucker
#' Simplify a curve using the Ramer-Douglas-Peucker algorithm.
#'
#' The [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm) for reducing the number of points on a curve.
#' Implements the [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm) for reducing the number of points on a curve.
#'
#' @details If there are no more than two points it does not make sense to simplify.
#' In this case the input is returned without further checks of `x` and `y`.
#' In particular, the input is not checked for `NA` values.
#'
#' @param x The `x` values of the curve as a vector.
#' @param y The `y` values of the curve as a vector.
#' @param epsilon The threshold for filtering outliers from the simplified curve.
#' @param x `[numeric]` The `x` values of the curve as a vector without `NA` values.
#' @param y `[numeric]` The `y` values of the curve as a vector without `NA` values.
#' @param epsilon `[positive numeric(1)]` The threshold for filtering outliers from the simplified curve.
#' @param keep_index `[logical]` If `TRUE`, returns a column called `index` with the index locations of points that are kept.
#'
#' @return A `data.frame` with `x` and `y` values of the simplified curve.
#'
#' @examples
#' RDP::RamerDouglasPeucker(x = c(0, 1, 3, 5), y = c(2, 1, 0, 1), epsilon = 0.5)
#' RDP::RamerDouglasPeucker(x = c(0, 1, 3, 5), y = c(2, 1, 0, 1), epsilon = 0.5, keep_index = TRUE)
#'
#' @export
#'
RamerDouglasPeucker <- function(x, y, epsilon) {
.Call(`_RDP_RamerDouglasPeucker`, x, y, epsilon)
RamerDouglasPeucker <- function(x, y, epsilon, keep_index = FALSE) {
.Call(`_RDP_RamerDouglasPeucker`, x, y, epsilon, keep_index)
}

Binary file modified build/partial.rdb
Binary file not shown.
16 changes: 10 additions & 6 deletions man/RamerDouglasPeucker.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions src/RamerDouglasPeucker.cpp
Expand Up @@ -23,9 +23,9 @@ double abs2(Point2D p)
}


// From reference points[startIndex] == points[endIndex], find the point furthest away.
// Find the point furthest away from reference (points[startIndex] == points[endIndex])
std::pair<double, std::size_t> findMostDistantPoint(const std::vector<Point2D> &points,
std::size_t startIndex, std::size_t endIndex)
std::size_t startIndex, std::size_t endIndex)
{
assert(startIndex < endIndex && "Start index must be smaller than end index");
assert(endIndex < points.size() && "End index is larger than the number of points");
Expand Down Expand Up @@ -55,7 +55,8 @@ std::pair<double, std::size_t> findMostDistantPoint(const std::vector<Point2D> &
// Rearranging this formula to avoid recomputing constants:
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
std::pair<double, std::size_t> findMostDistantPointFromLine(const std::vector<Point2D> &points,
std::size_t startIndex, std::size_t endIndex)
std::size_t startIndex,
std::size_t endIndex)
{
assert(startIndex < endIndex && "Start index must be smaller than end index");
assert(endIndex < points.size() && "End index is larger than the number of points");
Expand Down Expand Up @@ -93,8 +94,9 @@ std::pair<double, std::size_t> findMostDistantPointFromLine(const std::vector<Po
}


void RamerDouglasPeucker(const std::vector<Point2D> &points, std::size_t startIndex, std::size_t endIndex,
double epsilonSquared, std::vector<std::size_t> &indicesToKeep)
void RamerDouglasPeucker(const std::vector<Point2D> &points, std::size_t startIndex,
std::size_t endIndex, double epsilonSquared,
std::vector<std::size_t> &indicesToKeep)
{
assert(startIndex < endIndex && "Start index must be smaller than end index");
assert(endIndex < points.size() && "End index is larger than the number of points");
Expand All @@ -106,7 +108,8 @@ void RamerDouglasPeucker(const std::vector<Point2D> &points, std::size_t startIn
assert(indicesToKeep.size() >= 1 && "indicesToKeep should be non-empty");
assert(indicesToKeep[0] == 0 && "indicesToKeep should be initialized with a 0");

auto [maxDistanceSquared, maxDistanceIndex] = findMostDistantPointFromLine(points, startIndex, endIndex);
auto [maxDistanceSquared, maxDistanceIndex] =
findMostDistantPointFromLine(points, startIndex, endIndex);

if (maxDistanceSquared > epsilonSquared)
{
Expand Down
5 changes: 3 additions & 2 deletions src/RamerDouglasPeucker.h
Expand Up @@ -8,8 +8,9 @@ struct Point2D
double y;
};

void RamerDouglasPeucker(const std::vector<Point2D> &pointList, std::size_t startIndex, std::size_t endIndex,
double epsilonSquared, std::vector<std::size_t> &indicesToKeep);
void RamerDouglasPeucker(const std::vector<Point2D> &pointList, std::size_t startIndex,
std::size_t endIndex, double epsilonSquared,
std::vector<std::size_t> &indicesToKeep);
}

#endif
9 changes: 5 additions & 4 deletions src/RcppExports.cpp
Expand Up @@ -11,21 +11,22 @@ Rcpp::Rostream<false>& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
#endif

// RamerDouglasPeucker
Rcpp::DataFrame RamerDouglasPeucker(Rcpp::NumericVector x, Rcpp::NumericVector y, double epsilon);
RcppExport SEXP _RDP_RamerDouglasPeucker(SEXP xSEXP, SEXP ySEXP, SEXP epsilonSEXP) {
Rcpp::DataFrame RamerDouglasPeucker(Rcpp::NumericVector x, Rcpp::NumericVector y, double epsilon, bool keep_index);
RcppExport SEXP _RDP_RamerDouglasPeucker(SEXP xSEXP, SEXP ySEXP, SEXP epsilonSEXP, SEXP keep_indexSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::NumericVector >::type x(xSEXP);
Rcpp::traits::input_parameter< Rcpp::NumericVector >::type y(ySEXP);
Rcpp::traits::input_parameter< double >::type epsilon(epsilonSEXP);
rcpp_result_gen = Rcpp::wrap(RamerDouglasPeucker(x, y, epsilon));
Rcpp::traits::input_parameter< bool >::type keep_index(keep_indexSEXP);
rcpp_result_gen = Rcpp::wrap(RamerDouglasPeucker(x, y, epsilon, keep_index));
return rcpp_result_gen;
END_RCPP
}

static const R_CallMethodDef CallEntries[] = {
{"_RDP_RamerDouglasPeucker", (DL_FUNC) &_RDP_RamerDouglasPeucker, 3},
{"_RDP_RamerDouglasPeucker", (DL_FUNC) &_RDP_RamerDouglasPeucker, 4},
{NULL, NULL, 0}
};

Expand Down
32 changes: 22 additions & 10 deletions src/wrapper.cpp
@@ -1,37 +1,41 @@
#include <Rcpp.h>
#include "RamerDouglasPeucker.h"

//' Ramer-Douglas-Peucker
//' Simplify a curve using the Ramer-Douglas-Peucker algorithm.
//'
//' The [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm) for reducing the number of points on a curve.
//' Implements the [Ramer-Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm) for reducing the number of points on a curve.
//'
//' @details If there are no more than two points it does not make sense to simplify.
//' In this case the input is returned without further checks of `x` and `y`.
//' In particular, the input is not checked for `NA` values.
//'
//' @param x The `x` values of the curve as a vector.
//' @param y The `y` values of the curve as a vector.
//' @param epsilon The threshold for filtering outliers from the simplified curve.
//' @param x `[numeric]` The `x` values of the curve as a vector without `NA` values.
//' @param y `[numeric]` The `y` values of the curve as a vector without `NA` values.
//' @param epsilon `[positive numeric(1)]` The threshold for filtering outliers from the simplified curve.
//' @param keep_index `[logical]` If `TRUE`, returns a column called `index` with the index locations of points that are kept.
//'
//' @return A `data.frame` with `x` and `y` values of the simplified curve.
//'
//' @examples
//' RDP::RamerDouglasPeucker(x = c(0, 1, 3, 5), y = c(2, 1, 0, 1), epsilon = 0.5)
//' RDP::RamerDouglasPeucker(x = c(0, 1, 3, 5), y = c(2, 1, 0, 1), epsilon = 0.5, keep_index = TRUE)
//'
//' @export
//'
// [[Rcpp::export]]
Rcpp::DataFrame RamerDouglasPeucker(Rcpp::NumericVector x, Rcpp::NumericVector y, double epsilon)
Rcpp::DataFrame RamerDouglasPeucker(Rcpp::NumericVector x, Rcpp::NumericVector y, double epsilon,
bool keep_index = false)
{
if (epsilon < 0 || Rcpp::NumericVector::is_na(epsilon))
throw std::domain_error("epsilon must be a non-negative number");

R_xlen_t nPoints = x.length();
if (nPoints != y.length())
throw std::invalid_argument("x and y vectors must be of equal length");

if (nPoints <= 2)
return Rcpp::DataFrame::create(Rcpp::Named("x") = x, Rcpp::Named("y") = y);

if (epsilon < 0 || Rcpp::NumericVector::is_na(epsilon))
throw std::domain_error("epsilon must be a non-negative number");

std::vector<rdp::Point2D> points;
points.reserve(nPoints);
for (R_xlen_t i = 0; i != nPoints; ++i)
Expand Down Expand Up @@ -59,7 +63,15 @@ Rcpp::DataFrame RamerDouglasPeucker(Rcpp::NumericVector x, Rcpp::NumericVector y
std::size_t index = indicesToKeep[i];
xOut[i] = x[index];
yOut[i] = y[index];
// Add 1 to index before returning to R:
indicesToKeep[i] += 1;
}

if (keep_index) {
return Rcpp::DataFrame::create(Rcpp::Named("x") = xOut, Rcpp::Named("y") = yOut,
Rcpp::Named("index") = indicesToKeep);
} else {
return Rcpp::DataFrame::create(Rcpp::Named("x") = xOut, Rcpp::Named("y") = yOut);
}

return Rcpp::DataFrame::create(Rcpp::Named("x") = xOut, Rcpp::Named("y") = yOut);
}
11 changes: 11 additions & 0 deletions tests/testthat/test-RamerDouglasPeucker.R
Expand Up @@ -76,6 +76,17 @@ test_that("Bigger example", {
)
})

test_that("Keeps indices", {
x <- c(3.5, 7.3, 23.2, 37.2, 54.6, 62.2, 71.5, 101.3)
y <- c(21.25, 12.0, 3.1, 12.07, 18.15, 16.45, 9.7, 21.1)

expect_equal(
RamerDouglasPeucker(x, y, 5, keep_index = TRUE),
data.frame(x = c(3.5, 23.2, 54.6, 71.5, 101.3), y = c(21.25, 3.1, 18.15, 9.7, 21.1),
index = c(1, 3, 5, 7, 8))
)
})


test_that("Input returned when there are too few points", {
expect_equal(RamerDouglasPeucker(c(1, 2), c(1, 2), 5), data.frame(x = c(1, 2), y = c(1, 2)))
Expand Down

0 comments on commit 4b6332d

Please sign in to comment.