diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java deleted file mode 100644 index 70170f2e81..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java +++ /dev/null @@ -1,99 +0,0 @@ - -package com.github.mikephil.charting.data.filter; - -import android.annotation.TargetApi; -import android.os.Build; - -import java.util.Arrays; - -/** - * Implemented according to Wiki-Pseudocode {@link} - * ...�Douglas�Peucker_algorithm - * - * @author Philipp Baldauf & Phliipp Jahoda - */ -public class Approximator { - - @TargetApi(Build.VERSION_CODES.GINGERBREAD) - public float[] reduceWithDouglasPeucker(float[] points, float tolerance) { - - int greatestIndex = 0; - float greatestDistance = 0f; - - Line line = new Line(points[0], points[1], points[points.length - 2], points[points.length - 1]); - - for (int i = 2; i < points.length - 2; i += 2) { - - float distance = line.distance(points[i], points[i + 1]); - - if (distance > greatestDistance) { - greatestDistance = distance; - greatestIndex = i; - } - } - - if (greatestDistance > tolerance) { - - float[] reduced1 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, 0, greatestIndex + 2), tolerance); - float[] reduced2 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, greatestIndex, points.length), - tolerance); - - float[] result1 = reduced1; - float[] result2 = Arrays.copyOfRange(reduced2, 2, reduced2.length); - - return concat(result1, result2); - } else { - return line.getPoints(); - } - } - - /** - * Combine arrays. - */ - float[] concat(float[]... arrays) { - int length = 0; - for (float[] array : arrays) { - length += array.length; - } - float[] result = new float[length]; - int pos = 0; - for (float[] array : arrays) { - for (float element : array) { - result[pos] = element; - pos++; - } - } - return result; - } - - private class Line { - - private final float[] points; - - private final float sxey; - private final float exsy; - - private final float dx; - private final float dy; - - private final float length; - - public Line(float x1, float y1, float x2, float y2) { - dx = x1 - x2; - dy = y1 - y2; - sxey = x1 * y2; - exsy = x2 * y1; - length = (float) Math.sqrt(dx * dx + dy * dy); - - points = new float[]{x1, y1, x2, y2}; - } - - public float distance(float x, float y) { - return Math.abs(dy * x - dx * y + sxey - exsy) / length; - } - - public float[] getPoints() { - return points; - } - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.kt new file mode 100644 index 0000000000..32f58bc9bb --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.kt @@ -0,0 +1,80 @@ +package com.github.mikephil.charting.data.filter + +import android.annotation.TargetApi +import android.os.Build +import kotlin.math.abs +import kotlin.math.sqrt + +/** + * Implemented according to Wiki-Pseudocode [] + * [...](http://en.wikipedia.org/wiki/Ramer)�Douglas�Peucker_algorithm + */ +class Approximator { + @TargetApi(Build.VERSION_CODES.GINGERBREAD) + fun reduceWithDouglasPeucker(points: FloatArray, tolerance: Float): FloatArray { + var greatestIndex = 0 + var greatestDistance = 0f + + val line = Line(points[0], points[1], points[points.size - 2], points[points.size - 1]) + + var i = 2 + while (i < points.size - 2) { + val distance = line.distance(points[i], points[i + 1]) + + if (distance > greatestDistance) { + greatestDistance = distance + greatestIndex = i + } + i += 2 + } + + if (greatestDistance > tolerance) { + val reduced1 = reduceWithDouglasPeucker(points.copyOfRange(0, greatestIndex + 2), tolerance) + val reduced2 = reduceWithDouglasPeucker( + points.copyOfRange(greatestIndex, points.size), + tolerance + ) + + val result2 = reduced2.copyOfRange(2, reduced2.size) + + return concat(reduced1, result2) + } else { + return line.points + } + } + + /** + * Combine arrays. + */ + fun concat(vararg arrays: FloatArray): FloatArray { + var length = 0 + for (array in arrays) { + length += array.size + } + val result = FloatArray(length) + var pos = 0 + for (array in arrays) { + for (element in array) { + result[pos] = element + pos++ + } + } + return result + } + + private class Line(x1: Float, y1: Float, x2: Float, y2: Float) { + val points: FloatArray = floatArrayOf(x1, y1, x2, y2) + + private val sxeY: Float = x1 * y2 + private val exsY: Float = x2 * y1 + + private val dx: Float = x1 - x2 + private val dy: Float = y1 - y2 + + private val length: Float = sqrt((dx * dx + dy * dy).toDouble()).toFloat() + + fun distance(x: Float, y: Float): Float { + return abs(dy * x - dx * y + sxeY - exsY) / length + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java deleted file mode 100644 index 9351341c76..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java +++ /dev/null @@ -1,146 +0,0 @@ - -package com.github.mikephil.charting.data.filter; - -import java.util.ArrayList; - -/** - * Implemented according to modified Douglas Peucker {@link} - * http://psimpl.sourceforge.net/douglas-peucker.html - */ -public class ApproximatorN -{ - public float[] reduceWithDouglasPeucker(float[] points, float resultCount) { - - int pointCount = points.length / 2; - - // if a shape has 2 or less points it cannot be reduced - if (resultCount <= 2 || resultCount >= pointCount) - return points; - - boolean[] keep = new boolean[pointCount]; - - // first and last always stay - keep[0] = true; - keep[pointCount - 1] = true; - - int currentStoredPoints = 2; - - ArrayList queue = new ArrayList<>(); - Line line = new Line(0, pointCount - 1, points); - queue.add(line); - - do { - line = queue.remove(queue.size() - 1); - - // store the key - keep[line.index] = true; - - // check point count tolerance - currentStoredPoints += 1; - - if (currentStoredPoints == resultCount) - break; - - // split the polyline at the key and recurse - Line left = new Line(line.start, line.index, points); - if (left.index > 0) { - int insertionIndex = insertionIndex(left, queue); - queue.add(insertionIndex, left); - } - - Line right = new Line(line.index, line.end, points); - if (right.index > 0) { - int insertionIndex = insertionIndex(right, queue); - queue.add(insertionIndex, right); - } - } while (queue.isEmpty()); - - float[] reducedEntries = new float[currentStoredPoints * 2]; - - for (int i = 0, i2 = 0, r2 = 0; i < currentStoredPoints; i++, r2 += 2) { - if (keep[i]) { - reducedEntries[i2++] = points[r2]; - reducedEntries[i2++] = points[r2 + 1]; - } - } - - return reducedEntries; - } - - private static float distanceToLine( - float ptX, float ptY, float[] - fromLinePoint1, float[] fromLinePoint2) { - float dx = fromLinePoint2[0] - fromLinePoint1[0]; - float dy = fromLinePoint2[1] - fromLinePoint1[1]; - - float dividend = Math.abs( - dy * ptX - - dx * ptY - - fromLinePoint1[0] * fromLinePoint2[1] + - fromLinePoint2[0] * fromLinePoint1[1]); - double divisor = Math.sqrt(dx * dx + dy * dy); - - return (float)(dividend / divisor); - } - - private static class Line { - int start; - int end; - - float distance = 0; - int index = 0; - - Line(int start, int end, float[] points) { - this.start = start; - this.end = end; - - float[] startPoint = new float[]{points[start * 2], points[start * 2 + 1]}; - float[] endPoint = new float[]{points[end * 2], points[end * 2 + 1]}; - - if (end <= start + 1) return; - - for (int i = start + 1, i2 = i * 2; i < end; i++, i2 += 2) { - float distance = distanceToLine( - points[i2], points[i2 + 1], - startPoint, endPoint); - - if (distance > this.distance) { - this.index = i; - this.distance = distance; - } - } - } - - boolean equals(final Line rhs) { - return (start == rhs.start) && (end == rhs.end) && index == rhs.index; - } - - boolean lessThan(final Line rhs) { - return distance < rhs.distance; - } - } - - private static int insertionIndex(Line line, ArrayList queue) { - int min = 0; - int max = queue.size(); - - while (!queue.isEmpty()) { - int midIndex = min + (max - min) / 2; - Line midLine = queue.get(midIndex); - - if (midLine.equals(line)) { - return midIndex; - } - else if (line.lessThan(midLine)) { - // perform search in left half - max = midIndex; - } - else { - // perform search in right half - min = midIndex + 1; - } - } - - return min; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.kt new file mode 100644 index 0000000000..390203445b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.kt @@ -0,0 +1,146 @@ +package com.github.mikephil.charting.data.filter + +import kotlin.math.abs +import kotlin.math.sqrt + +/** + * Implemented according to modified Douglas Peucker [] + * [...](http://psimpl.sourceforge.net/douglas-peucker.html) + */ +class ApproximatorN { + fun reduceWithDouglasPeucker(points: FloatArray, resultCount: Float): FloatArray { + val pointCount = points.size / 2 + + // if a shape has 2 or less points it cannot be reduced + if (resultCount <= 2 || resultCount >= pointCount) return points + + val keep = BooleanArray(pointCount) + + // first and last always stay + keep[0] = true + keep[pointCount - 1] = true + + var currentStoredPoints = 2 + + val queue = ArrayList() + var line = Line(0, pointCount - 1, points) + queue.add(line) + + do { + line = queue.removeAt(queue.size - 1) + + // store the key + keep[line.index] = true + + // check point count tolerance + currentStoredPoints += 1 + + if (currentStoredPoints.toFloat() == resultCount) break + + // split the polyline at the key and recurse + val left = Line(line.start, line.index, points) + if (left.index > 0) { + val insertionIndex: Int = insertionIndex(left, queue) + queue.add(insertionIndex, left) + } + + val right = Line(line.index, line.end, points) + if (right.index > 0) { + val insertionIndex: Int = insertionIndex(right, queue) + queue.add(insertionIndex, right) + } + } while (queue.isEmpty()) + + val reducedEntries = FloatArray(currentStoredPoints * 2) + + var i = 0 + var i2 = 0 + var r2 = 0 + while (i < currentStoredPoints) { + if (keep[i]) { + reducedEntries[i2++] = points[r2] + reducedEntries[i2++] = points[r2 + 1] + } + i++ + r2 += 2 + } + + return reducedEntries + } + + private class Line(var start: Int, var end: Int, points: FloatArray) { + var distance: Float = 0f + var index: Int = 0 + + init { + val startPoint = floatArrayOf(points[start * 2], points[start * 2 + 1]) + val endPoint = floatArrayOf(points[end * 2], points[end * 2 + 1]) + + if (end > start + 1) { + + var i = start + 1 + var i2 = i * 2 + while (i < end) { + val distance: Float = distanceToLine( + points[i2], points[i2 + 1], + startPoint, endPoint + ) + + if (distance > this.distance) { + this.index = i + this.distance = distance + } + i++ + i2 += 2 + } + } + } + + fun equals(rhs: Line): Boolean { + return (start == rhs.start) && (end == rhs.end) && index == rhs.index + } + + fun lessThan(rhs: Line): Boolean { + return distance < rhs.distance + } + } + + companion object { + private fun distanceToLine( + ptX: Float, ptY: Float, fromLinePoint1: FloatArray, fromLinePoint2: FloatArray + ): Float { + val dx = fromLinePoint2[0] - fromLinePoint1[0] + val dy = fromLinePoint2[1] - fromLinePoint1[1] + + val dividend = abs( + dy * ptX - dx * ptY - fromLinePoint1[0] * fromLinePoint2[1] + + fromLinePoint2[0] * fromLinePoint1[1] + ) + val divisor = sqrt((dx * dx + dy * dy).toDouble()) + + return (dividend / divisor).toFloat() + } + + private fun insertionIndex(line: Line, queue: ArrayList): Int { + var min = 0 + var max = queue.size + + while (!queue.isEmpty()) { + val midIndex = min + (max - min) / 2 + val midLine = queue.get(midIndex) + + if (midLine.equals(line)) { + return midIndex + } else if (line.lessThan(midLine)) { + // perform search in left half + max = midIndex + } else { + // perform search in right half + min = midIndex + 1 + } + } + + return min + } + } +}