From 12fa563ebddc29b8307d9e2718173a04c6b98ced Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 11 Mar 2018 08:29:05 -0500 Subject: [PATCH 01/40] Fix app freeze when gridsteps are much larger than the actual plot range (#76) --- .../src/main/java/com/androidplot/Plot.java | 31 +- .../java/com/androidplot/util/Redrawer.java | 2 +- .../com/androidplot/util/SeriesUtils.java | 17 +- .../xy/FastLineAndPointRenderer.java | 19 +- .../com/androidplot/xy/SampledXYSeries.java | 2 +- .../com/androidplot/xy/XYGraphWidget.java | 306 ++++++++++-------- 6 files changed, 218 insertions(+), 159 deletions(-) diff --git a/androidplot-core/src/main/java/com/androidplot/Plot.java b/androidplot-core/src/main/java/com/androidplot/Plot.java index 8b436199..5e009635 100644 --- a/androidplot-core/src/main/java/com/androidplot/Plot.java +++ b/androidplot-core/src/main/java/com/androidplot/Plot.java @@ -18,28 +18,43 @@ import android.content.Context; import android.content.res.TypedArray; -import android.graphics.*; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.RectF; import android.os.Build; import android.os.Looper; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; + import com.androidplot.exception.PlotRenderException; -import com.androidplot.ui.*; +import com.androidplot.ui.Anchor; +import com.androidplot.ui.BoxModel; import com.androidplot.ui.Formatter; +import com.androidplot.ui.HorizontalPositioning; +import com.androidplot.ui.LayoutManager; +import com.androidplot.ui.Resizable; +import com.androidplot.ui.SeriesBundle; +import com.androidplot.ui.SeriesRenderer; +import com.androidplot.ui.Size; +import com.androidplot.ui.SizeMode; import com.androidplot.ui.TextOrientation; +import com.androidplot.ui.VerticalPositioning; import com.androidplot.ui.widget.TextLabelWidget; -import com.androidplot.ui.SeriesRenderer; import com.androidplot.util.AttrUtils; import com.androidplot.util.DisplayDimensions; import com.androidplot.util.PixelUtils; -import com.androidplot.ui.HorizontalPositioning; -import com.androidplot.ui.VerticalPositioning; -import com.halfhp.fig.*; +import com.halfhp.fig.Fig; +import com.halfhp.fig.FigException; import java.lang.reflect.Field; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; /** * Base class for all Plot implementations. @@ -406,7 +421,7 @@ public void run() { } pingPong.recycle(); } - }); + }, "Androidplot renderThread"); } } diff --git a/androidplot-core/src/main/java/com/androidplot/util/Redrawer.java b/androidplot-core/src/main/java/com/androidplot/util/Redrawer.java index 6cf3b4eb..4bc118e0 100644 --- a/androidplot-core/src/main/java/com/androidplot/util/Redrawer.java +++ b/androidplot-core/src/main/java/com/androidplot/util/Redrawer.java @@ -58,7 +58,7 @@ public Redrawer(List plots, float maxRefreshRate, boolean startImmediately this.plots.add(new WeakReference<>(plot)); } setMaxRefreshRate(maxRefreshRate); - thread = new Thread(this); + thread = new Thread(this, "Androidplot Redrawer"); thread.start(); if(startImmediately) { start(); diff --git a/androidplot-core/src/main/java/com/androidplot/util/SeriesUtils.java b/androidplot-core/src/main/java/com/androidplot/util/SeriesUtils.java index bd265582..b9526a24 100644 --- a/androidplot-core/src/main/java/com/androidplot/util/SeriesUtils.java +++ b/androidplot-core/src/main/java/com/androidplot/util/SeriesUtils.java @@ -85,25 +85,24 @@ public static RectRegion minMax(XYConstraints constraints, XYSeries... seriesArr for (XYSeries series : seriesArray) { // if this is an advanced xy series then minMax have already been calculated for us: - boolean isPreCalculated = false; if (series instanceof FastXYSeries) { final RectRegion b = ((FastXYSeries) series).minMax(); if(b == null) { + //this series doesn't currently have min or max region (might be empty) continue; } if(constraints == null || constraints.contains(b)) { bounds.union(b); + continue; } } - if (!isPreCalculated && series.size() > 0) { - for (int i = 0; i < series.size(); i++) { - final Number xi = series.getX(i); - final Number yi = series.getY(i); + for (int i = 0; i < series.size(); i++) { + final Number xi = series.getX(i); + final Number yi = series.getY(i); - // if constraints have been set, make sure this xy coordinate exists within them: - if (constraints == null || constraints.contains(xi, yi)) { - bounds.union(xi, yi); - } + // if constraints have been set, make sure this xy coordinate exists within them: + if (constraints == null || constraints.contains(xi, yi)) { + bounds.union(xi, yi); } } } diff --git a/androidplot-core/src/main/java/com/androidplot/xy/FastLineAndPointRenderer.java b/androidplot-core/src/main/java/com/androidplot/xy/FastLineAndPointRenderer.java index 4e117252..d1e3fc8e 100644 --- a/androidplot-core/src/main/java/com/androidplot/xy/FastLineAndPointRenderer.java +++ b/androidplot-core/src/main/java/com/androidplot/xy/FastLineAndPointRenderer.java @@ -17,8 +17,10 @@ package com.androidplot.xy; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.PointF; import android.graphics.RectF; +import android.support.annotation.NonNull; import com.androidplot.exception.PlotRenderException; import com.androidplot.ui.RenderStack; @@ -36,6 +38,11 @@ */ public class FastLineAndPointRenderer extends XYSeriesRenderer { + /** + * A line drawn by {@link Canvas#drawLines(float[], int, int, Paint)} must be defined by at + * least four points {@code x0, y0, x1, y1} + */ + private static final int MINIMUM_NUMBER_OF_POINTS_TO_DEFINE_A_LINE = 4; private float[] points; List segmentOffsets = new ArrayList<>(); List segmentLengths = new ArrayList<>(); @@ -94,10 +101,14 @@ protected void onRender(Canvas canvas, RectF plotArea, XYSeries series, Formatte } } - protected void drawSegment(Canvas canvas, float[] points, int offset, int len, Formatter formatter) { + protected void drawSegment(@NonNull Canvas canvas, + @NonNull float[] points, + int offset, + int len, + Formatter formatter) { if(formatter.linePaint != null) { // draw lines: - if (len >= 4) { + if (len >= MINIMUM_NUMBER_OF_POINTS_TO_DEFINE_A_LINE) { // optimization to avoid using 2x storage space to represent the full path: if ((len & 2) != 0) { canvas.drawLines(points, offset, len - 2, formatter.linePaint); @@ -116,7 +127,9 @@ protected void drawSegment(Canvas canvas, float[] points, int offset, int len, F } @Override - protected void doDrawLegendIcon(Canvas canvas, RectF rect, Formatter formatter) { + protected void doDrawLegendIcon(@NonNull Canvas canvas, + @NonNull RectF rect, + @NonNull Formatter formatter) { if(formatter.hasLinePaint()) { canvas.drawLine(rect.left, rect.bottom, rect.right, rect.top, formatter.getLinePaint()); } diff --git a/androidplot-core/src/main/java/com/androidplot/xy/SampledXYSeries.java b/androidplot-core/src/main/java/com/androidplot/xy/SampledXYSeries.java index b10903b1..f795536b 100644 --- a/androidplot-core/src/main/java/com/androidplot/xy/SampledXYSeries.java +++ b/androidplot-core/src/main/java/com/androidplot/xy/SampledXYSeries.java @@ -89,7 +89,7 @@ public void run() { lastResamplingException = ex; } } - }); + }, "Androidplot XY Series Sampler"); getZoomLevels().add(thisSeries); threads.add(thread); thread.start(); diff --git a/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java b/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java index 2ecd4fcb..23506f64 100644 --- a/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java +++ b/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java @@ -47,8 +47,8 @@ import java.util.Map; /** - * Displays graphical data (lines, points, etc.) annotated with domain and range tick markers. - * The inner area of the graph upon which grid lines and points are rendered is called the "grid" area. + * Displays graphical data (lines, points, etc.) annotated with domain and range tick markers. The + * inner area of the graph upon which grid lines and points are rendered is called the "grid" area. */ public class XYGraphWidget extends Widget { @@ -148,7 +148,12 @@ public class XYGraphWidget extends Widget { public static class LineLabelRenderer { - public void drawLabel(Canvas canvas, LineLabelStyle style, Number val, float x, float y, boolean isOrigin) { + public void drawLabel(Canvas canvas, + LineLabelStyle style, + Number val, + float x, + float y, + boolean isOrigin) { final int canvasState = canvas.save(); try { final String txt = style.format.format(val); @@ -159,7 +164,12 @@ public void drawLabel(Canvas canvas, LineLabelStyle style, Number val, float x, } } - protected void drawLabel(Canvas canvas, String text, Paint paint, float x, float y, boolean isOrigin) { + protected void drawLabel(Canvas canvas, + String text, + Paint paint, + float x, + float y, + boolean isOrigin) { canvas.drawText(text, x, y, paint); } } @@ -175,6 +185,7 @@ public static class LineLabelStyle { paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(DEFAULT_LINE_LABEL_TEXT_SIZE_PX); } + public Format getFormat() { return format; } @@ -203,17 +214,16 @@ public void setPaint(Paint paint) { public interface CursorLabelFormatter { /** - * * @return The Paint to be used to draw the cursor text label. */ Paint getTextPaint(); /** - * - * @return Null if no background should be drawn, - * the Paint used to draw the background otherwise. + * @return Null if no background should be drawn, the Paint used to draw the background + * otherwise. */ Paint getBackgroundPaint(); + String getLabelText(Number x, Number y); } @@ -272,18 +282,21 @@ public XYGraphWidget(LayoutManager layoutManager, XYPlot plot, Size size) { /** * Apply xml attrs + * * @param attrs */ public void processAttrs(TypedArray attrs) { setDrawGridOnTop(attrs.getBoolean(R.styleable.xy_XYPlot_drawGridOnTop, isDrawGridOnTop())); int tlp = attrs.getInt(R.styleable.xy_XYPlot_lineLabels, 0); - if(tlp != 0) { + if (tlp != 0) { setLineLabelEdges(tlp); } - setGridClippingEnabled(attrs.getBoolean(R.styleable.xy_XYPlot_gridClippingEnabled, - isGridClippingEnabled())); + setGridClippingEnabled(attrs.getBoolean( + R.styleable.xy_XYPlot_gridClippingEnabled, + isGridClippingEnabled() + )); final LineLabelStyle lineLabelStyleTop = getLineLabelStyle(Edge.TOP); final LineLabelStyle lineLabelStyleBottom = getLineLabelStyle(Edge.BOTTOM); @@ -292,19 +305,23 @@ public void processAttrs(TypedArray attrs) { lineLabelStyleTop.setRotation(attrs.getFloat( R.styleable.xy_XYPlot_lineLabelRotationTop, - lineLabelStyleTop.getRotation())); + lineLabelStyleTop.getRotation() + )); lineLabelStyleBottom.setRotation(attrs.getFloat( R.styleable.xy_XYPlot_lineLabelRotationBottom, - lineLabelStyleBottom.getRotation())); + lineLabelStyleBottom.getRotation() + )); lineLabelStyleLeft.setRotation(attrs.getFloat( R.styleable.xy_XYPlot_lineLabelRotationLeft, - lineLabelStyleLeft.getRotation())); + lineLabelStyleLeft.getRotation() + )); lineLabelStyleRight.setRotation(attrs.getFloat( R.styleable.xy_XYPlot_lineLabelRotationRight, - lineLabelStyleRight.getRotation())); + lineLabelStyleRight.getRotation() + )); setLineExtensionTop(attrs.getDimension( R.styleable.xy_XYPlot_lineExtensionTop, getLineExtensionTop())); @@ -318,34 +335,40 @@ public void processAttrs(TypedArray attrs) { AttrUtils.configureTextPaint(attrs, lineLabelStyleTop.getPaint(), R.styleable.xy_XYPlot_lineLabelTextColorTop, R.styleable.xy_XYPlot_lineLabelTextSizeTop, - R.styleable.xy_XYPlot_lineLabelAlignTop); + R.styleable.xy_XYPlot_lineLabelAlignTop + ); AttrUtils.configureTextPaint(attrs, lineLabelStyleBottom.getPaint(), R.styleable.xy_XYPlot_lineLabelTextColorBottom, R.styleable.xy_XYPlot_lineLabelTextSizeBottom, - R.styleable.xy_XYPlot_lineLabelAlignBottom); + R.styleable.xy_XYPlot_lineLabelAlignBottom + ); AttrUtils.configureTextPaint(attrs, lineLabelStyleLeft.getPaint(), R.styleable.xy_XYPlot_lineLabelTextColorLeft, R.styleable.xy_XYPlot_lineLabelTextSizeLeft, - R.styleable.xy_XYPlot_lineLabelAlignLeft); + R.styleable.xy_XYPlot_lineLabelAlignLeft + ); AttrUtils.configureTextPaint(attrs, lineLabelStyleRight.getPaint(), R.styleable.xy_XYPlot_lineLabelTextColorRight, R.styleable.xy_XYPlot_lineLabelTextSizeRight, - R.styleable.xy_XYPlot_lineLabelAlignRight); + R.styleable.xy_XYPlot_lineLabelAlignRight + ); AttrUtils.configureInsets(attrs, getGridInsets(), R.styleable.xy_XYPlot_gridInsetTop, R.styleable.xy_XYPlot_gridInsetBottom, R.styleable.xy_XYPlot_gridInsetLeft, - R.styleable.xy_XYPlot_gridInsetRight); + R.styleable.xy_XYPlot_gridInsetRight + ); AttrUtils.configureInsets(attrs, getLineLabelInsets(), R.styleable.xy_XYPlot_lineLabelInsetTop, R.styleable.xy_XYPlot_lineLabelInsetBottom, R.styleable.xy_XYPlot_lineLabelInsetLeft, - R.styleable.xy_XYPlot_lineLabelInsetRight); + R.styleable.xy_XYPlot_lineLabelInsetRight + ); // graph size & position AttrUtils.configureWidget(attrs, this, @@ -353,7 +376,8 @@ public void processAttrs(TypedArray attrs) { R.styleable.xy_XYPlot_graphWidthMode, R.styleable.xy_XYPlot_graphWidth, R.styleable.xy_XYPlot_graphHorizontalPositioning, R.styleable.xy_XYPlot_graphHorizontalPosition, R.styleable.xy_XYPlot_graphVerticalPositioning, R.styleable.xy_XYPlot_graphVerticalPosition, - R.styleable.xy_XYPlot_graphAnchor, R.styleable.xy_XYPlot_graphVisible); + R.styleable.xy_XYPlot_graphAnchor, R.styleable.xy_XYPlot_graphVisible + ); // domainLabel size & position AttrUtils.configureWidget(attrs, this, @@ -361,7 +385,8 @@ public void processAttrs(TypedArray attrs) { R.styleable.xy_XYPlot_domainTitleWidthMode, R.styleable.xy_XYPlot_domainTitleWidth, R.styleable.xy_XYPlot_domainTitleHorizontalPositioning, R.styleable.xy_XYPlot_domainTitleHorizontalPosition, R.styleable.xy_XYPlot_domainTitleVerticalPositioning, R.styleable.xy_XYPlot_domainTitleVerticalPosition, - R.styleable.xy_XYPlot_domainTitleAnchor, R.styleable.xy_XYPlot_domainTitleVisible); + R.styleable.xy_XYPlot_domainTitleAnchor, R.styleable.xy_XYPlot_domainTitleVisible + ); // rangeLabel size & position AttrUtils.configureWidget(attrs, this, @@ -369,7 +394,8 @@ public void processAttrs(TypedArray attrs) { R.styleable.xy_XYPlot_rangeTitleWidthMode, R.styleable.xy_XYPlot_rangeTitleWidth, R.styleable.xy_XYPlot_rangeTitleHorizontalPositioning, R.styleable.xy_XYPlot_rangeTitleHorizontalPosition, R.styleable.xy_XYPlot_rangeTitleVerticalPositioning, R.styleable.xy_XYPlot_rangeTitleVerticalPosition, - R.styleable.xy_XYPlot_rangeTitleAnchor, R.styleable.xy_XYPlot_rangeTitleVisible); + R.styleable.xy_XYPlot_rangeTitleAnchor, R.styleable.xy_XYPlot_rangeTitleVisible + ); // rotation AttrUtils.configureWidgetRotation(attrs, this, R.styleable.xy_XYPlot_graphRotation); @@ -379,31 +405,38 @@ public void processAttrs(TypedArray attrs) { R.styleable.xy_XYPlot_graphMarginTop, R.styleable.xy_XYPlot_graphMarginBottom, R.styleable.xy_XYPlot_graphMarginLeft, R.styleable.xy_XYPlot_graphMarginRight, R.styleable.xy_XYPlot_graphPaddingTop, R.styleable.xy_XYPlot_graphPaddingBottom, - R.styleable.xy_XYPlot_graphPaddingLeft, R.styleable.xy_XYPlot_graphPaddingRight); + R.styleable.xy_XYPlot_graphPaddingLeft, R.styleable.xy_XYPlot_graphPaddingRight + ); // domainOriginLinePaint AttrUtils.configureLinePaint(attrs, getDomainOriginLinePaint(), R.styleable.xy_XYPlot_domainOriginLineColor, - R.styleable.xy_XYPlot_domainOriginLineThickness); + R.styleable.xy_XYPlot_domainOriginLineThickness + ); // rangeOriginLinePaint AttrUtils.configureLinePaint(attrs, getRangeOriginLinePaint(), R.styleable.xy_XYPlot_rangeOriginLineColor, - R.styleable.xy_XYPlot_rangeOriginLineThickness); + R.styleable.xy_XYPlot_rangeOriginLineThickness + ); AttrUtils.configureLinePaint(attrs, getDomainGridLinePaint(), R.styleable.xy_XYPlot_domainLineColor, - R.styleable.xy_XYPlot_domainLineThickness); + R.styleable.xy_XYPlot_domainLineThickness + ); AttrUtils.configureLinePaint(attrs, getRangeGridLinePaint(), R.styleable.xy_XYPlot_rangeLineColor, - R.styleable.xy_XYPlot_rangeLineThickness); + R.styleable.xy_XYPlot_rangeLineThickness + ); AttrUtils.setColor(attrs, getBackgroundPaint(), - R.styleable.xy_XYPlot_graphBackgroundColor); + R.styleable.xy_XYPlot_graphBackgroundColor + ); AttrUtils.setColor(attrs, getGridBackgroundPaint(), - R.styleable.xy_XYPlot_gridBackgroundColor); + R.styleable.xy_XYPlot_gridBackgroundColor + ); } /** @@ -415,7 +448,7 @@ public void processAttrs(TypedArray attrs) { * @return */ protected XYCoords screenToSeries(PointF point) { - if(!plot.getBounds().isFullyDefined()) { + if (!plot.getBounds().isFullyDefined()) { return null; } return new RectRegion(gridRect) @@ -466,6 +499,7 @@ protected Number screenToSeriesX(float xPix) { * Converts a y pixel to a y value. * This is a relatively slow operation and should not be used for operations that are a part of * the main render loop of a dynamic plot. + * * @param yPix * @return */ @@ -478,7 +512,7 @@ protected Number screenToSeriesY(float yPix) { } protected PointF seriesToScreen(XYCoords xy) { - if(!plot.getBounds().isFullyDefined()) { + if (!plot.getBounds().isFullyDefined()) { return null; } return plot.getBounds().transform(xy, gridRect, false, true); @@ -511,7 +545,7 @@ protected void doOnDraw(Canvas canvas, RectF widgetRect) && bounds.getMaxX() != null && bounds.getMinY() != null && bounds.getMaxY() != null) { - if(drawGridOnTop) { + if (drawGridOnTop) { drawData(canvas); drawGrid(canvas); } else { @@ -527,12 +561,13 @@ protected void doOnDraw(Canvas canvas, RectF widgetRect) } protected void drawDomainLine(Canvas canvas, float xPix, Number xVal, - Paint linePaint, boolean isOrigin) { + Paint linePaint, boolean isOrigin) { // lines if (linePaint != null) { - canvas.drawLine(xPix, gridRect.top - lineExtensionTop, - xPix, gridRect.bottom + lineExtensionBottom, linePaint); + canvas.drawLine(xPix, gridRect.top - lineExtensionTop, + xPix, gridRect.bottom + lineExtensionBottom, linePaint + ); } // labels @@ -541,11 +576,12 @@ protected void drawDomainLine(Canvas canvas, float xPix, Number xVal, } protected void drawRangeLine(Canvas canvas, float yPix, Number yVal, - Paint linePaint, boolean isOrigin) { + Paint linePaint, boolean isOrigin) { // lines if (linePaint != null) { canvas.drawLine(gridRect.left - lineExtensionLeft, yPix, - gridRect.right + lineExtensionRight, yPix, linePaint); + gridRect.right + lineExtensionRight, yPix, linePaint + ); } // labels @@ -553,8 +589,13 @@ protected void drawRangeLine(Canvas canvas, float yPix, Number yVal, drawLineLabel(canvas, Edge.RIGHT, yVal, labelRect.right, yPix, isOrigin); } - protected void drawLineLabel(Canvas canvas, Edge edge, Number val, float x, float y, boolean isOrigin) { - if(isLineLabelEnabled(edge)) { + protected void drawLineLabel(Canvas canvas, + Edge edge, + Number val, + float x, + float y, + boolean isOrigin) { + if (isLineLabelEnabled(edge)) { getLineLabelRenderer(edge).drawLabel(canvas, getLineLabelStyle(edge), val, x, y, isOrigin); } } @@ -565,63 +606,48 @@ protected void drawLineLabel(Canvas canvas, Edge edge, Number val, float x, floa * @param canvas */ protected void drawGrid(Canvas canvas) { - if(!drawGridOnTop) { + if (!drawGridOnTop) { drawGridBackground(canvas); } Number domainOrigin = plot.getDomainOrigin(); - double domainOriginPix; + final double domainOriginPix; if (domainOrigin != null) { domainOriginPix = plot.getBounds().getxRegion().transform( plot.getDomainOrigin().doubleValue(), gridRect.left, gridRect.right, false); } else { // if no domain origin is set, use the leftmost value visible on the grid: domainOriginPix = gridRect.left; - domainOrigin=plot.getBounds().getMinX(); + domainOrigin = plot.getBounds().getMinX(); } Step domainStep = XYStepCalculator.getStep(plot, Axis.DOMAIN, gridRect); - // draw domain origin: - if (domainOriginPix >= gridRect.left - && domainOriginPix <= gridRect.right) { - drawDomainLine(canvas, (float) domainOriginPix, - domainOrigin, domainOriginLinePaint, true); - } - - // draw lines LEFT of origin: - double xPix = domainOriginPix - domainStep.getStepPix(); - for (int i = ONE; xPix >= gridRect.left - FUDGE; xPix = domainOriginPix - - (i * domainStep.getStepPix())) { - double xVal = domainOrigin.doubleValue() - i - * domainStep.getStepVal(); - - if (xPix <= gridRect.right) { - final boolean isDomainTick = i% getLinesPerDomainLabel() == ZERO; - final Paint lp = isDomainTick ? domainGridLinePaint : domainSubGridLinePaint; - drawDomainLine(canvas, (float) xPix, xVal, lp, false); + // Draw Domain Lines: + + final double domainStepPix = domainStep.getStepPix(); + final double iMin = (gridRect.left - domainOriginPix - FUDGE) / domainStepPix; + final double iMax = (gridRect.right - domainOriginPix + FUDGE) / domainStepPix; + + for (int i = (int) Math.ceil(iMin); i <= iMax; i++) { + double xVal = domainOrigin.doubleValue() + i * domainStep.getStepVal(); + double xPix = domainOriginPix + i * domainStepPix; + boolean isMajorTick = i % getLinesPerDomainLabel() == ZERO; + boolean isOrigin = i == 0; + Paint linePaint; + if (isOrigin) { + linePaint = domainOriginLinePaint; + } else if (isMajorTick) { + linePaint = domainGridLinePaint; + } else { + linePaint = domainSubGridLinePaint; } - i++; - } - - // draw lines RIGHT of origin: - xPix = domainOriginPix + domainStep.getStepPix(); - for (int i = ONE; xPix <= gridRect.right + FUDGE; xPix = domainOriginPix - + (i * domainStep.getStepPix())) { - double xVal = domainOrigin.doubleValue() + i - * domainStep.getStepVal(); - - if (xPix >= gridRect.left) { - final boolean isDomainTick = i% getLinesPerDomainLabel() == ZERO; - final Paint lp = isDomainTick ? domainGridLinePaint : domainSubGridLinePaint; - drawDomainLine(canvas, (float) xPix, xVal, lp, false); - } - i++; + drawDomainLine(canvas, (float) xPix, xVal, linePaint, isOrigin); } Number rangeOrigin = plot.getRangeOrigin(); - double rangeOriginPix; + final double rangeOriginPix; if (rangeOrigin != null) { rangeOriginPix = plot.getBounds().getyRegion().transform( rangeOrigin.doubleValue(), gridRect.top, gridRect.bottom, true); @@ -633,39 +659,29 @@ protected void drawGrid(Canvas canvas) { Step rangeStep = XYStepCalculator.getStep(plot, Axis.RANGE, gridRect); - // draw range origin: - if (rangeOriginPix >= gridRect.top && rangeOriginPix <= gridRect.bottom) { - drawRangeLine(canvas, (float) rangeOriginPix, - rangeOrigin, rangeOriginLinePaint, true); - } + // Draw Range Lines: final double rangeStepPix = rangeStep.getStepPix(); - - // draw lines ABOVE origin: - double yPix = rangeOriginPix - rangeStep.getStepPix(); - for (int i = ONE; yPix >= gridRect.top - FUDGE; yPix = rangeOriginPix - (i * rangeStepPix)) { - double yVal = rangeOrigin.doubleValue() + i - * rangeStep.getStepVal(); - - if (yPix <= gridRect.bottom) { - final boolean isRangeTick = i% getLinesPerRangeLabel() == ZERO; - final Paint lp = isRangeTick ? rangeGridLinePaint : rangeSubGridLinePaint; - drawRangeLine(canvas, (float)yPix, yVal, lp, false); + final double kMin = (gridRect.top - rangeOriginPix - FUDGE) / rangeStepPix; + final double kMax = (gridRect.bottom - rangeOriginPix + FUDGE) / rangeStepPix; + + for (int k = (int) Math.ceil(kMin); k <= kMax; k++) { + // Android vertical coordinates (zero at the top of the screen) are the opposite + // direction of default range values (lowest on bottom of screen) so we subtract when + // calculating yVal + double yVal = rangeOrigin.doubleValue() - k * rangeStep.getStepVal(); + double yPix = rangeOriginPix + k * rangeStepPix; + boolean isMajorTick = k % getLinesPerRangeLabel() == ZERO; + boolean isOrigin = k == 0; + Paint linePaint; + if (isOrigin) { + linePaint = rangeOriginLinePaint; + } else if (isMajorTick) { + linePaint = rangeGridLinePaint; + } else { + linePaint = rangeSubGridLinePaint; } - i++; - } - - // draw lines BENEATH origin: - yPix = rangeOriginPix + rangeStep.getStepPix(); - for (int i = ONE; yPix <= gridRect.bottom + FUDGE; yPix = rangeOriginPix + (i * rangeStepPix)) { - double yVal = rangeOrigin.doubleValue() - i - * rangeStep.getStepVal(); - if (yPix >= gridRect.top) { - final boolean isRangeTick = i% getLinesPerRangeLabel() == ZERO; - final Paint lp = isRangeTick ? rangeGridLinePaint : rangeSubGridLinePaint; - drawRangeLine(canvas, (float)yPix, yVal, lp, false); - } - i++; + drawRangeLine(canvas, (float) yPix, yVal, linePaint, isOrigin); } } @@ -679,11 +695,13 @@ protected void drawGrid(Canvas canvas) { * @param y */ private void drawMarkerText(Canvas canvas, String text, ValueMarker marker, - float x, float y) { + float x, float y) { x += MARKER_LABEL_SPACING; y -= MARKER_LABEL_SPACING; - RectF textRect = new RectF(FontUtils.getStringDimensions(text, - marker.getTextPaint())); + RectF textRect = new RectF(FontUtils.getStringDimensions( + text, + marker.getTextPaint() + )); textRect.offsetTo(x, y - textRect.height()); if (textRect.right > gridRect.right) { @@ -695,18 +713,20 @@ private void drawMarkerText(Canvas canvas, String text, ValueMarker marker, } canvas.drawText(text, textRect.left, textRect.bottom, - marker.getTextPaint()); + marker.getTextPaint() + ); } protected void drawMarkers(Canvas canvas) { - if(plot.getYValueMarkers() != null && plot.getYValueMarkers().size() > 0) { + if (plot.getYValueMarkers() != null && plot.getYValueMarkers().size() > 0) { for (YValueMarker marker : plot.getYValueMarkers()) { if (marker.getValue() != null) { float yPix = (float) plot.getBounds().yRegion .transform(marker.getValue() .doubleValue(), gridRect.top, gridRect.bottom, true); canvas.drawLine(gridRect.left, yPix, - gridRect.right, yPix, marker.getLinePaint()); + gridRect.right, yPix, marker.getLinePaint() + ); float xPix = marker.getTextPosition().getPixelValue( gridRect.width()); @@ -719,14 +739,15 @@ protected void drawMarkers(Canvas canvas) { } } - if(plot.getXValueMarkers() != null && plot.getXValueMarkers().size() > 0) { + if (plot.getXValueMarkers() != null && plot.getXValueMarkers().size() > 0) { for (XValueMarker marker : plot.getXValueMarkers()) { if (marker.getValue() != null) { float xPix = (float) plot.getBounds().xRegion .transform(marker.getValue() .doubleValue(), gridRect.left, gridRect.right, false); canvas.drawLine(xPix, gridRect.top, xPix, gridRect.bottom, - marker.getLinePaint()); + marker.getLinePaint() + ); float yPix = marker.getTextPosition().getPixelValue(gridRect.height()); yPix += gridRect.top; if (marker.getText() != null) { @@ -747,7 +768,8 @@ protected void drawCursors(Canvas canvas) { hasDomainCursor = true; canvas.drawLine(domainCursorPosition, gridRect.top, domainCursorPosition, gridRect.bottom, - domainCursorPaint); + domainCursorPaint + ); } boolean hasRangeCursor = false; @@ -758,10 +780,11 @@ protected void drawCursors(Canvas canvas) { && rangeCursorPosition <= gridRect.bottom) { hasRangeCursor = true; canvas.drawLine(gridRect.left, rangeCursorPosition, - gridRect.right, rangeCursorPosition, rangeCursorPaint); + gridRect.right, rangeCursorPosition, rangeCursorPaint + ); } - if(getCursorLabelFormatter() != null && hasRangeCursor && hasDomainCursor) { + if (getCursorLabelFormatter() != null && hasRangeCursor && hasDomainCursor) { drawCursorLabel(canvas); } } @@ -779,8 +802,10 @@ protected void drawCursorLabel(Canvas canvas) { // if we are too close to the right edge of the plot, we will move // the label to the left side of our cursor: if (cursorRect.right >= gridRect.right) { - cursorRect.offsetTo(domainCursorPosition - cursorRect.width(), - cursorRect.top); + cursorRect.offsetTo( + domainCursorPosition - cursorRect.width(), + cursorRect.top + ); } // same thing for the top edge of the plot: @@ -794,11 +819,12 @@ protected void drawCursorLabel(Canvas canvas) { } canvas.drawText(label, cursorRect.left, cursorRect.bottom, - getCursorLabelFormatter().getTextPaint()); + getCursorLabelFormatter().getTextPaint() + ); } protected void drawGridBackground(Canvas canvas) { - if(gridBackgroundPaint != null) { + if (gridBackgroundPaint != null) { canvas.drawRect(gridRect, gridBackgroundPaint); } } @@ -814,15 +840,15 @@ protected void drawData(Canvas canvas) throws PlotRenderException { drawGridBackground(canvas); } try { - if(isGridClippingEnabled) { + if (isGridClippingEnabled) { canvas.save(Canvas.ALL_SAVE_FLAG); canvas.clipRect(gridRect, android.graphics.Region.Op.INTERSECT); } renderStack.sync(); - for(RenderStack.StackElement thisElement : renderStack.getElements()) { - if(thisElement.isEnabled()) { + for (RenderStack.StackElement thisElement : renderStack.getElements()) { + if (thisElement.isEnabled()) { Class rendererClass = thisElement.get().getFormatter().getRendererClass(); plot.getRenderer(rendererClass).render( @@ -831,7 +857,7 @@ protected void drawData(Canvas canvas) throws PlotRenderException { } } finally { - if(isGridClippingEnabled) { + if (isGridClippingEnabled) { canvas.restore(); } } @@ -858,6 +884,7 @@ public Paint getDomainGridLinePaint() { /** * Set the paint used to draw the domain grid line. + * * @param gridLinePaint */ public void setDomainGridLinePaint(Paint gridLinePaint) { @@ -880,6 +907,7 @@ public Paint getDomainSubGridLinePaint() { /** * Set the paint used to draw the domain grid line. + * * @param gridLinePaint */ public void setDomainSubGridLinePaint(Paint gridLinePaint) { @@ -888,6 +916,7 @@ public void setDomainSubGridLinePaint(Paint gridLinePaint) { /** * Set the Paint used to draw the range grid line. + * * @param gridLinePaint */ public void setRangeGridLinePaint(Paint gridLinePaint) { @@ -903,6 +932,7 @@ public Paint getRangeSubGridLinePaint() { /** * Set the Paint used to draw the range grid line. + * * @param gridLinePaint */ public void setRangeSubGridLinePaint(Paint gridLinePaint) { @@ -943,6 +973,7 @@ public void setRangeOriginLinePaint(Paint rangeOriginLinePaint) { /** * Set domain and range cursor position using screen coordinates + * * @param x * @param y */ @@ -953,6 +984,7 @@ public void setCursorPosition(Float x, Float y) { /** * Set domain and range cursor position using screen coordinates + * * @param point */ public void setCursorPosition(PointF point) { @@ -969,6 +1001,7 @@ public Number getDomainCursorVal() { /** * Set domain cursor position using screen coordinates + * * @param domainCursorPosition */ public void setDomainCursorPosition(Float domainCursorPosition) { @@ -985,6 +1018,7 @@ public Number getRangeCursorVal() { /** * Set range cursor position using screen coordinates + * * @param rangeCursorPosition */ public void setRangeCursorPosition(Float rangeCursorPosition) { @@ -1012,9 +1046,8 @@ public Paint getDomainCursorPaint() { } /** - * - * @param domainCursorPaint The {@link Paint} used to draw the domain cursor line. - * Set to null (default) to disable. + * @param domainCursorPaint The {@link Paint} used to draw the domain cursor line. Set to null + * (default) to disable. */ public void setDomainCursorPaint(Paint domainCursorPaint) { this.domainCursorPaint = domainCursorPaint; @@ -1025,9 +1058,8 @@ public Paint getRangeCursorPaint() { } /** - * - * @param rangeCursorPaint The {@link Paint} used to draw the range cursor line. - * Set to null (default) to disable. + * @param rangeCursorPaint The {@link Paint} used to draw the range cursor line. Set to null + * (default) to disable. */ public void setRangeCursorPaint(Paint rangeCursorPaint) { this.rangeCursorPaint = rangeCursorPaint; @@ -1160,7 +1192,7 @@ public boolean isLineLabelEnabled(Edge position) { public void setLineLabelEdges(Edge... positions) { EnumSet positionSet = EnumSet.noneOf(Edge.class); - if(positions != null) { + if (positions != null) { Collections.addAll(positionSet, positions); } this.lineLabelEdges = positionSet; @@ -1171,8 +1203,8 @@ public void setLineLabelEdges(Collection positions) { } protected void setLineLabelEdges(int bitfield) { - for(Edge tp : Edge.values()) { - if((tp.value & bitfield) == tp.value) { + for (Edge tp : Edge.values()) { + if ((tp.value & bitfield) == tp.value) { lineLabelEdges.add(tp); } } From 0de08c598bb483ba9cd95b190460403f52602948 Mon Sep 17 00:00:00 2001 From: Nick Fellows Date: Sun, 11 Mar 2018 09:15:39 -0500 Subject: [PATCH 02/40] uprev dev to 1.5.5 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c45af2f9..d9bdfbfc 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ ext { theCompileSdkVersion = 26 theTargetSdkVersion = 26 theMinSdkVersion = 5 - theVersionName = '1.5.4' + theVersionName = '1.5.5' theVersionCode = 0 } From 7893160306fcba7baacd8db46745ede00b35bc0a Mon Sep 17 00:00:00 2001 From: hannesa2 Date: Sun, 22 Apr 2018 18:24:06 +0200 Subject: [PATCH 03/40] update to Android Studio 3.1.1 (#77) --- build.gradle | 6 +++--- demoapp-wearable/build.gradle | 5 +++-- demoapp/build.gradle | 8 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index d9bdfbfc..a7aa9c13 100644 --- a/build.gradle +++ b/build.gradle @@ -38,9 +38,9 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' + classpath 'com.android.tools.build:gradle:3.1.1' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0' classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.6.0' classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.6.3' } diff --git a/demoapp-wearable/build.gradle b/demoapp-wearable/build.gradle index b4afad27..22e5186d 100644 --- a/demoapp-wearable/build.gradle +++ b/demoapp-wearable/build.gradle @@ -17,9 +17,10 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.1' } } apply plugin: 'com.android.application' @@ -54,6 +55,6 @@ android { dependencies { compile project(':androidplot-core') - compile 'com.google.android.support:wearable:1.3.0' + compile 'com.google.android.support:wearable:2.3.0' compile 'com.google.android.gms:play-services-wearable:8.3.0' } diff --git a/demoapp/build.gradle b/demoapp/build.gradle index c501ed33..36a7c800 100644 --- a/demoapp/build.gradle +++ b/demoapp/build.gradle @@ -21,9 +21,9 @@ dependencies { compile project(':androidplot-core') compile 'com.crittercism:crittercism-android-agent:5.4.0' - debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1' - releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' - testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' + debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4' + releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' + testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' } buildscript { @@ -32,7 +32,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.github.triplet.gradle:play-publisher:1.1.5' + classpath 'com.github.triplet.gradle:play-publisher:1.2.0' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6bd840a3..42a64d0b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip From c53ac24397e25032c444304951c50152ff5a39ad Mon Sep 17 00:00:00 2001 From: Nick Fellows Date: Fri, 4 May 2018 13:47:45 -0500 Subject: [PATCH 04/40] include full apache 2.0 license in LICENSE.md --- LICENSE.md | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 198 insertions(+), 10 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 3fbf0a78..f49a4e16 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,13 +1,201 @@ - Copyright 2018 Androidplot.com + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - http://www.apache.org/licenses/LICENSE-2.0 + 1. Definitions. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file From f58cd6b43859886a7141d8294c16069d85baf768 Mon Sep 17 00:00:00 2001 From: Nick Fellows Date: Wed, 23 May 2018 11:18:38 -0500 Subject: [PATCH 05/40] 1.5.5 changes (#79) * remove unused button from simple xy plot * #78 Fixes issue where setting XYGraphWidget insets has no effect. * XYGraphWidget.drawMarkerText is now protected instead of private. * 1.5.5 documentation updates --- .../com/androidplot/xy/XYGraphWidget.java | 54 +++++----- .../com/androidplot/xy/XYGraphWidgetTest.java | 98 ++++++++++--------- .../demos/SimpleXYPlotActivity.java | 17 +++- .../res/layout/simple_xy_plot_example.xml | 6 -- docs/quickstart.md | 2 +- docs/release_notes.md | 6 ++ 6 files changed, 107 insertions(+), 76 deletions(-) diff --git a/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java b/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java index 23506f64..1ca3de77 100644 --- a/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java +++ b/androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java @@ -530,8 +530,15 @@ protected float seriesToScreenY(Number y) { @Override protected void onResize(@Nullable RectF oldRect, @NonNull RectF newRect) { - gridRect = RectFUtils.applyInsets(newRect, gridInsets); - labelRect = RectFUtils.applyInsets(newRect, lineLabelInsets); + recalculateSizes(newRect); + } + + protected void recalculateSizes(@Nullable RectF rect) { + if(rect == null) { + rect = getWidgetDimensions().paddedRect; + } + gridRect = RectFUtils.applyInsets(rect, gridInsets); + labelRect = RectFUtils.applyInsets(rect, lineLabelInsets); } @Override @@ -694,27 +701,29 @@ protected void drawGrid(Canvas canvas) { * @param x * @param y */ - private void drawMarkerText(Canvas canvas, String text, ValueMarker marker, + protected void drawMarkerText(Canvas canvas, String text, ValueMarker marker, float x, float y) { - x += MARKER_LABEL_SPACING; - y -= MARKER_LABEL_SPACING; - RectF textRect = new RectF(FontUtils.getStringDimensions( - text, - marker.getTextPaint() - )); - textRect.offsetTo(x, y - textRect.height()); + if (marker.getText() != null) { + x += MARKER_LABEL_SPACING; + y -= MARKER_LABEL_SPACING; + RectF textRect = new RectF(FontUtils.getStringDimensions( + text, + marker.getTextPaint() + )); + textRect.offsetTo(x, y - textRect.height()); + + if (textRect.right > gridRect.right) { + textRect.offset(-(textRect.right - gridRect.right), ZERO); + } - if (textRect.right > gridRect.right) { - textRect.offset(-(textRect.right - gridRect.right), ZERO); - } + if (textRect.top < gridRect.top) { + textRect.offset(0, gridRect.top - textRect.top); + } - if (textRect.top < gridRect.top) { - textRect.offset(0, gridRect.top - textRect.top); + canvas.drawText(text, textRect.left, textRect.bottom, + marker.getTextPaint() + ); } - - canvas.drawText(text, textRect.left, textRect.bottom, - marker.getTextPaint() - ); } protected void drawMarkers(Canvas canvas) { @@ -731,10 +740,7 @@ protected void drawMarkers(Canvas canvas) { float xPix = marker.getTextPosition().getPixelValue( gridRect.width()); xPix += gridRect.left; - - if (marker.getText() != null) { - drawMarkerText(canvas, marker.getText(), marker, xPix, yPix); - } + drawMarkerText(canvas, marker.getText(), marker, xPix, yPix); } } } @@ -1149,6 +1155,7 @@ public Insets getGridInsets() { public void setGridInsets(Insets gridInsets) { this.gridInsets = gridInsets; + recalculateSizes(null); } /** @@ -1160,6 +1167,7 @@ public Insets getLineLabelInsets() { public void setLineLabelInsets(Insets lineLabelInsets) { this.lineLabelInsets = lineLabelInsets; + recalculateSizes(null); } public RectF getGridRect() { diff --git a/androidplot-core/src/test/java/com/androidplot/xy/XYGraphWidgetTest.java b/androidplot-core/src/test/java/com/androidplot/xy/XYGraphWidgetTest.java index 9f92afa8..65d3898a 100644 --- a/androidplot-core/src/test/java/com/androidplot/xy/XYGraphWidgetTest.java +++ b/androidplot-core/src/test/java/com/androidplot/xy/XYGraphWidgetTest.java @@ -21,6 +21,7 @@ import com.androidplot.test.*; import com.androidplot.ui.*; +import com.androidplot.util.DisplayDimensions; import org.junit.*; import org.mockito.*; @@ -67,7 +68,7 @@ public class XYGraphWidgetTest extends AndroidplotTest { @Before - public void setUp() throws Exception { + public void setUp() { size = spy(new Size(100, SizeMode.ABSOLUTE, 100, SizeMode.ABSOLUTE)); xyPlot = spy(new XYPlot(getContext(), "XYPlot")); when(xyPlot.getRegistry()).thenReturn(seriesRegistry); @@ -83,13 +84,8 @@ public void setUp() throws Exception { graphWidget.setLabelRect(new RectF(0, 0, 100, 100)); } - @After - public void tearDown() throws Exception { - - } - @Test - public void testProcessAttrs() throws Exception { + public void processAttrs_withDefaults_disablesGridClipping() { XYGraphWidget graphWidget = spy(new XYGraphWidget(layoutManager, xyPlot, size)); graphWidget.processAttrs(typedArray); @@ -97,7 +93,7 @@ public void testProcessAttrs() throws Exception { } @Test - public void testDoOnDraw_drawGridOnTopFalse() throws Exception { + public void doOnDraw_drawGridOnTopFalse_drawsGridAfterData() throws Exception { XYGraphWidget graphWidget = spy(new XYGraphWidget(layoutManager, xyPlot, size)); graphWidget.setDrawGridOnTop(false); doNothing().when(graphWidget).drawGrid(canvas); @@ -113,7 +109,7 @@ public void testDoOnDraw_drawGridOnTopFalse() throws Exception { } @Test - public void testDoOnDraw_drawGridOnTopTrue() throws Exception { + public void doOnDraw_drawGridOnTopTrue_drawsGridBeforeData() throws Exception { XYGraphWidget graphWidget = spy(new XYGraphWidget(layoutManager, xyPlot, size)); graphWidget.setDrawGridOnTop(true); doNothing().when(graphWidget).drawGrid(canvas); @@ -129,7 +125,7 @@ public void testDoOnDraw_drawGridOnTopTrue() throws Exception { } @Test - public void testDrawMarkers() throws Exception { + public void drawMarkers_drawsLineForEachMarker() { xyPlot.addMarker(new XValueMarker(1, "x")); xyPlot.addMarker(new YValueMarker(-1, "y")); @@ -140,42 +136,22 @@ public void testDrawMarkers() throws Exception { .drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(), any(Paint.class)); } - protected void runDrawGridTest() { - doNothing().when(graphWidget). - drawDomainLine(any(Canvas.class), anyFloat(), any(Number.class), any(Paint.class), anyBoolean()); - - doNothing().when(graphWidget). - drawRangeLine(any(Canvas.class), anyFloat(), any(Number.class), any(Paint.class), anyBoolean()); - - xyPlot.setRangeBoundaries(0, 100, BoundaryMode.FIXED); - xyPlot.setDomainBoundaries(0, 100, BoundaryMode.FIXED); - - graphWidget.drawGrid(canvas); - - // expecting a 100x100 grid to be drawn: - verify(graphWidget, times(100)) - .drawDomainLine(eq(canvas), anyFloat(), anyFloat(), any(Paint.class), eq(false)); - - verify(graphWidget, times(100)) - .drawRangeLine(eq(canvas), anyFloat(), anyFloat(), any(Paint.class), eq(false)); - } - @Test - public void testDrawGrid_nullOrigin() throws Exception { + public void drawGrid_nullOrigin_drawsGrid() { when(xyPlot.getDomainOrigin()).thenReturn(null); when(xyPlot.getRangeOrigin()).thenReturn(null); runDrawGridTest(); } @Test - public void testDrawGrid_zeroOrigin() throws Exception { + public void drawGrid_zeroOrigin_drawsGrid() { when(xyPlot.getDomainOrigin()).thenReturn(0); when(xyPlot.getRangeOrigin()).thenReturn(0); runDrawGridTest(); } @Test - public void testDrawGrid_centeredOrigin() throws Exception { + public void drawGrid_centeredOrigin_drawsGrid() { // set origin to midpoint so we exercise // code to draw lines on both sides of the origin: @@ -185,7 +161,7 @@ public void testDrawGrid_centeredOrigin() throws Exception { } @Test - public void testDrawCursors_ifCursorPaintAndPositionAreSet() throws Exception { + public void drawCursors_withCursorPaintAndPosition_drawsCursorLines() { final Paint domainCursorPaint = new Paint(); graphWidget.setDomainCursorPaint(domainCursorPaint); @@ -201,7 +177,7 @@ public void testDrawCursors_ifCursorPaintAndPositionAreSet() throws Exception { } @Test - public void testDrawCursors_ifCursorPaintAndPositionAreNotSet() throws Exception { + public void testDrawCursors_noCursorPaintOrPosition_drawsNoCursorLines() { graphWidget.setDomainCursorPaint(null); graphWidget.setRangeCursorPaint(null); @@ -212,7 +188,7 @@ public void testDrawCursors_ifCursorPaintAndPositionAreNotSet() throws Exception } @Test - public void testDrawCursorLabel() throws Exception { + public void drawCursorLabel_drawsText() { graphWidget.setDomainCursorPosition(0f); graphWidget.setRangeCursorPosition(0f); XYGraphWidget.CursorLabelFormatter clf = mock(XYGraphWidget.CursorLabelFormatter.class); @@ -224,7 +200,7 @@ public void testDrawCursorLabel() throws Exception { } @Test - public void testSetLineLabelEdges() throws Exception { + public void setLineLabelEdges_setsEdges() { graphWidget.setLineLabelEdges(XYGraphWidget.Edge.LEFT, XYGraphWidget.Edge.BOTTOM); assertTrue(graphWidget.isLineLabelEnabled(XYGraphWidget.Edge.LEFT)); @@ -238,7 +214,7 @@ public void testSetLineLabelEdges() throws Exception { } @Test - public void testSetLineLabelEdges_bitfield() throws Exception { + public void setLineLabelEdges_bitfield_setsEdges() { graphWidget.setLineLabelEdges( XYGraphWidget.Edge.TOP.getValue() | XYGraphWidget.Edge.RIGHT.getValue()); @@ -250,7 +226,7 @@ public void testSetLineLabelEdges_bitfield() throws Exception { } @Test - public void testScreenToSeries() throws Exception { + public void screenToSeries_returnsSeriesCoords() { when(xyPlot.getBounds()).thenReturn(new RectRegion(-100, 100, -100, 100)); XYCoords coords = graphWidget.screenToSeries(new PointF(0, 0)); @@ -267,7 +243,7 @@ public void testScreenToSeries() throws Exception { } @Test - public void testSeriesToScreen() throws Exception { + public void seriesToScreen_returnsScreenPoint() { when(xyPlot.getBounds()).thenReturn(new RectRegion(-100, 100, -100, 100)); PointF point = graphWidget.seriesToScreen(new XYCoords(-100, 100)); @@ -284,7 +260,7 @@ public void testSeriesToScreen() throws Exception { } @Test - public void testScreenToSeriesX() throws Exception { + public void screenToSeriesX_returnsSeriesValue() { when(xyPlot.getBounds()).thenReturn(new RectRegion(-100, 100, -100, 100)); assertEquals(-100, graphWidget.screenToSeriesX(new PointF(0, 0)).intValue()); @@ -293,7 +269,7 @@ public void testScreenToSeriesX() throws Exception { } @Test - public void testScreenToSeriesY() throws Exception { + public void screenToSeriesY_returnsSeriesValue() { when(xyPlot.getBounds()).thenReturn(new RectRegion(-100, 100, -100, 100)); assertEquals(100, graphWidget.screenToSeriesY(new PointF(0, 0)).intValue()); @@ -302,7 +278,7 @@ public void testScreenToSeriesY() throws Exception { } @Test - public void testSeriesToScreenX() throws Exception { + public void seriesToScreenX_returnsScreenValue() { when(xyPlot.getBounds()).thenReturn(new RectRegion(-100, 100, -100, 100)); assertEquals(0f, graphWidget.seriesToScreenX(-100)); @@ -311,11 +287,45 @@ public void testSeriesToScreenX() throws Exception { } @Test - public void testSeriesToScreenY() throws Exception { + public void seriesToScreenY_returnsScreenValue() { when(xyPlot.getBounds()).thenReturn(new RectRegion(-100, 100, -100, 100)); assertEquals(100f, graphWidget.seriesToScreenY(100)); assertEquals(0f, graphWidget.seriesToScreenY(-100)); assertEquals(50f, graphWidget.seriesToScreenY(0)); } + + @Test + public void setGridInsets_updatesGridRect() { + graphWidget.setGridInsets(new Insets(0, 0, 0, 0)); + final RectF oldRect = graphWidget.getGridRect(); + + graphWidget.setGridInsets(new Insets(2, 2, 2, 2)); + final RectF newRect = graphWidget.getGridRect(); + + assertEquals(oldRect.left + 2, newRect.left); + assertEquals(oldRect.top + 2, newRect.top); + assertEquals(oldRect.right - 2, newRect.right); + assertEquals(oldRect.bottom -2, newRect.bottom); + } + + private void runDrawGridTest() { + doNothing().when(graphWidget). + drawDomainLine(any(Canvas.class), anyFloat(), any(Number.class), any(Paint.class), anyBoolean()); + + doNothing().when(graphWidget). + drawRangeLine(any(Canvas.class), anyFloat(), any(Number.class), any(Paint.class), anyBoolean()); + + xyPlot.setRangeBoundaries(0, 100, BoundaryMode.FIXED); + xyPlot.setDomainBoundaries(0, 100, BoundaryMode.FIXED); + + graphWidget.drawGrid(canvas); + + // expecting a 100x100 grid to be drawn: + verify(graphWidget, times(100)) + .drawDomainLine(eq(canvas), anyFloat(), anyFloat(), any(Paint.class), eq(false)); + + verify(graphWidget, times(100)) + .drawRangeLine(eq(canvas), anyFloat(), anyFloat(), any(Paint.class), eq(false)); + } } diff --git a/demoapp/src/main/java/com/androidplot/demos/SimpleXYPlotActivity.java b/demoapp/src/main/java/com/androidplot/demos/SimpleXYPlotActivity.java index b673f512..6238e4df 100644 --- a/demoapp/src/main/java/com/androidplot/demos/SimpleXYPlotActivity.java +++ b/demoapp/src/main/java/com/androidplot/demos/SimpleXYPlotActivity.java @@ -21,8 +21,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; -import com.androidplot.ui.Size; -import com.androidplot.ui.SizeMode; +import com.androidplot.ui.Insets; import com.androidplot.util.PixelUtils; import com.androidplot.xy.CatmullRomInterpolator; import com.androidplot.xy.LineAndPointFormatter; @@ -102,5 +101,19 @@ public Object parseObject(String source, @NonNull ParsePosition pos) { return null; } }); + + new Thread(new Runnable() { + + @Override + public void run() { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + plot.getGraph().setGridInsets(new Insets(120, 120, 120, 120)); + plot.redraw(); + } + }).start(); } } diff --git a/demoapp/src/main/res/layout/simple_xy_plot_example.xml b/demoapp/src/main/res/layout/simple_xy_plot_example.xml index 48aeb5cb..08a2c5af 100644 --- a/demoapp/src/main/res/layout/simple_xy_plot_example.xml +++ b/demoapp/src/main/res/layout/simple_xy_plot_example.xml @@ -33,10 +33,4 @@ ap:lineLabelRotationBottom="-45" android:layout_weight="1"/> -