The unsmoothed renderer emits a staircase: runs of identical samples are collapsed to a single horizontal segment and each value change becomes a near-vertical segment. This class replaces the straight segments with cubic Bezier segments whose tangents are chosen so that:
- Nothing overshoots. The limiting step bounds the normalized tangent magnitudes (alpha, beta) by 3, which is exactly the condition for the interpolant on an interval to stay within the two endpoint values. A curve therefore never leaves the vertical band of the data it passes through, so it cannot escape the plot area.
- Genuinely flat data stays flat. Where the secants on both sides of a vertex are equal, have opposite signs, or one of them is zero, the tangent is forced to zero. A plateau is bracketed by two horizontal tangents and so is drawn as an exact straight horizontal line; only the transitions between plateaus are curved.
- Spacing is honored. The interpolation is x-aware, so it stays correct when a
DownSamplerhas produced a non-uniform sample spacing.
The interpolation is not linear in the y values, because zeroing the tangent at a local extremum or plateau is a sign test rather than a weighted sum. Two series therefore do not interpolate additively, which is why ImageWorker only smooths plots whose fill has a flat lower boundary; see fillPolygonSmooth for the reasoning.
All methods are static and side-effect free so the interpolation can be exercised without a graphics context or a router.
Method Summary
All MethodsModifier and TypeMethodDescription(package private) static voidappendForward(GeneralPath path, int[] xv, int[] yv, double[] m) Appends the smoothed curve through the vertices, walking forward fromxv[0]toxv[n - 1].(package private) static voidappendReverse(GeneralPath path, int[] xv, int[] yv, double[] m) Appends the smoothed curve through the vertices, walking backward fromxv[n - 1]toxv[0].(package private) static booleanisInteriorTransition(int[] xv, int[] vy, int i) Tells whether the interval atiis a zero-width value change that may be widened.(package private) static double[]tangents(double[] x, double[] y, int start, int end) Computes the slope (dy/dx) at each of the verticesx[start]..(package private) static intwidenTransitions(int[] xv, int[] vy, int maxWidth) Widens zero-width transitions so they have room to be drawn as curves.
Method Details
tangents
static double[] tangents(double[] x, double[] y, int start, int end) Computes the slope (dy/dx) at each of the verticesx[start]..x[end - 1].- Parameters:
x- vertex x coordinates, strictly increasing within the rangey- vertex y coordinatesstart- first index of the range, inclusiveend- last index of the range, exclusive- Returns:
- slopes in y-units per x-unit, one per vertex; never null, empty when
end - start < 1
appendForward
Appends the smoothed curve through the vertices, walking forward fromxv[0]toxv[n - 1].The caller is responsible for having positioned the path at
(xv[0], yv[0]). Vertices with a non-positive span between them are joined with a straight line, since no curve is defined there.- Parameters:
path- path to append toxv- vertex x coordinatesyv- vertex y coordinatesm- slopes as returned bytangents(double[], double[], int, int)for this vertex range
appendReverse
Appends the smoothed curve through the vertices, walking backward fromxv[n - 1]toxv[0]. Used for the lower boundary of a filled area, which is traversed in reverse to close the outline.The caller is responsible for having positioned the path at
(xv[n - 1], yv[n - 1]).- Parameters:
path- path to append toxv- vertex x coordinatesyv- vertex y coordinatesm- slopes as returned bytangents(double[], double[], int, int)for this vertex range
widenTransitions
static int widenTransitions(int[] xv, int[] vy, int maxWidth) Widens zero-width transitions so they have room to be drawn as curves.When a graph plots more periods than it has pixels, several samples share one pixel column and a value change lands inside that column. There is then no horizontal distance across which to draw a curve, so the transition stays a vertical line and the plot keeps its staircase look however good the interpolation is. This borrows the missing width from the flat runs on either side of the transition, spreading it into an S-shaped ramp.
Only the x position of the two vertices bounding a transition moves; their y values, and every vertex of the surrounding plateaus, are left alone. A flat stretch therefore stays exactly flat and at exactly the value the data holds, it is just a little shorter. The curve still cannot overshoot, because it is bounded by the same two endpoint values as before.
Only interior transitions are widened. The first and last intervals of a run are left at their own columns, because they are the run's anchors: the first is where the trace starts and the last is where it ends, and both sit on the plot area's boundary when the series fills the window. Widening either one moves the anchor, which would start the trace inside the plot instead of at its edge or run the tail past the far edge.
Because the drawn curve is given width it does not literally pass through every sample once they are closer together than a pixel. That is the intended trade: this renders the series as a trend rather than reproducing sub-pixel steps exactly.
- Parameters:
xv- vertex x coordinates, adjusted in placevy- vertex y coordinates, read only to tell a transition from a flat runmaxWidth- the most total width, in pixels, to give one transition; must be at least 2- Returns:
- the number of transitions widened
isInteriorTransition
static boolean isInteriorTransition(int[] xv, int[] vy, int i) Tells whether the interval atiis a zero-width value change that may be widened.Three conditions, all required. The x coordinates must be equal, or the transition already spans pixels and there is nothing to widen. The y values must differ, or it is a flat run and there is no transition to draw. And
imust be neither 0 norxv.length - 2, because those intervals bound the run: moving either vertex would move the start or the end of the trace rather than a join in the middle of it.- Parameters:
xv- vertex x coordinatesvy- vertex y coordinatesi- index of the interval, whose two ends areiandi + 1- Returns:
- true if the interval may be widened