Skip to content

Commit f55660c

Browse files
authored
1.3.0 Changes (halfhp#14)
* * Cleaned up PanZoom logic and improved zoom functionality. Resolves Issue halfhp#11. * Sampling support, including LTTB estimating algorithm implementation, LTTBSampler. * documentation tweak
1 parent 3de8649 commit f55660c

File tree

72 files changed

+2541
-843
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+2541
-843
lines changed

androidplot-core/src/main/java/com/androidplot/Plot.java

Lines changed: 56 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
/**
4444
* Base class for all Plot implementations.
4545
*/
46-
public abstract class Plot<SeriesType extends Series, FormatterType extends Formatter, RendererType extends SeriesRenderer>
46+
public abstract class Plot<SeriesType extends Series, FormatterType extends Formatter,
47+
RendererType extends SeriesRenderer, BundleType extends SeriesBundle<SeriesType, FormatterType>,
48+
RegistryType extends SeriesRegistry<BundleType, SeriesType, FormatterType>>
4749
extends View implements Resizable {
4850
private static final String TAG = Plot.class.getName();
4951
private static final String XML_ATTR_PREFIX = "androidplot";
@@ -66,10 +68,23 @@ public HashMap<Class<? extends RendererType>, RendererType> getRenderers() {
6668
/**
6769
* Associates lists series and getFormatter pairs with the class of the Renderer used to render them.
6870
*/
69-
public SeriesRegistry<SeriesType, FormatterType> getSeriesRegistry() {
70-
return seriesRegistry;
71+
public RegistryType getRegistry() {
72+
return registry;
7173
}
7274

75+
public void setRegistry(RegistryType registry) {
76+
this.registry = registry;
77+
for(BundleType bundle : registry.getSeriesAndFormatterList()) {
78+
attachSeries(bundle.getSeries(), bundle.getFormatter());
79+
}
80+
}
81+
82+
/**
83+
*
84+
* @return A new instance of RegistryType
85+
*/
86+
protected abstract RegistryType getRegistryInstance();
87+
7388
public TextLabelWidget getTitle() {
7489
return title;
7590
}
@@ -152,7 +167,8 @@ public enum RenderMode {
152167
private final Object renderSynch = new Object();
153168

154169
private HashMap<Class<? extends RendererType>, RendererType> renderers;
155-
private SeriesRegistry<SeriesType, FormatterType> seriesRegistry;
170+
171+
private RegistryType registry;
156172
private final ArrayList<PlotListener> listeners;
157173

158174
private Thread renderThread;
@@ -161,7 +177,7 @@ public enum RenderMode {
161177

162178
{
163179
listeners = new ArrayList<>();
164-
seriesRegistry = new SeriesRegistry<>();
180+
registry = getRegistryInstance();
165181
renderers = new HashMap<>();
166182

167183
borderPaint = new Paint();
@@ -459,8 +475,8 @@ private void loadAttrs(AttributeSet attrs, int defStyle) {
459475
styleableName = styleableName.replace('.', '_');
460476
try {
461477
/**
462-
* Use reflection to safely check for the existence of styleable defs for Plot
463-
* and it's derivatives. This safety check is necessary to avoid runtime exceptions
478+
* Use reflection to safely run for the existence of styleable defs for Plot
479+
* and it's derivatives. This safety run is necessary to avoid runtime exceptions
464480
* in apps that don't include Androidplot as a .aar and won't have access to
465481
* the resources defined in the core library.
466482
*/
@@ -507,7 +523,7 @@ private void loadAttrs(AttributeSet attrs, int defStyle) {
507523
for (int i = 0; i < attrs.getAttributeCount(); i++) {
508524
String attrName = attrs.getAttributeName(i);
509525

510-
// case insensitive check to see if this attr begins with our prefix:
526+
// case insensitive run to see if this attr begins with our prefix:
511527
if (attrName != null && attrName.toUpperCase().startsWith(XML_ATTR_PREFIX.toUpperCase())) {
512528
attrHash.put(attrName.substring(XML_ATTR_PREFIX.length() + 1), attrs.getAttributeValue(i));
513529
}
@@ -569,11 +585,14 @@ public synchronized boolean addSeries(FormatterType formatter, SeriesType... ser
569585
* @return True if the series was added or false if the series / formatter pair already exists in the registry.
570586
*/
571587
public synchronized boolean addSeries(SeriesType series, FormatterType formatter) {
572-
Class rendererClass = formatter.getRendererClass();
588+
final boolean result = getRegistry().add(series, formatter);
589+
attachSeries(series, formatter);
590+
return result;
591+
}
573592

574-
// if(getSeries(series, rendererClass) != null) {
575-
// return false;
576-
// }
593+
protected void attachSeries(SeriesType series, FormatterType formatter) {
594+
595+
Class rendererClass = formatter.getRendererClass();
577596

578597
// initialize the Renderer if necessary:
579598
if(!getRenderers().containsKey(rendererClass)) {
@@ -584,20 +603,17 @@ public synchronized boolean addSeries(SeriesType series, FormatterType formatter
584603
if(series instanceof PlotListener) {
585604
addListener((PlotListener)series);
586605
}
587-
588-
getSeriesRegistry().add(new SeriesAndFormatter<>(series, formatter));
589-
return true;
590606
}
591607

592608
/**
593609
*
594610
* @param series
595611
* @param rendererClass
596-
* @return The {@link SeriesAndFormatter} that matches the series and rendererClass params, or null if one is not found.
612+
* @return The {@link SeriesBundle} that matches the series and rendererClass params, or null if one is not found.
597613
*/
598-
protected SeriesAndFormatter<SeriesType, FormatterType> getSeries(SeriesType series, Class<? extends RendererType> rendererClass) {
599-
for(SeriesAndFormatter<SeriesType, FormatterType> thisPair : seriesRegistry) {
600-
if(thisPair.getSeries() == series && thisPair.getFormatter().getRendererClass() == rendererClass) {
614+
protected SeriesBundle<SeriesType, FormatterType> getSeries(SeriesType series, Class<? extends RendererType> rendererClass) {
615+
for(SeriesBundle<SeriesType, FormatterType> thisPair : getSeries(series)) {
616+
if(thisPair.getFormatter().getRendererClass() == rendererClass) {
601617
return thisPair;
602618
}
603619
}
@@ -607,17 +623,10 @@ protected SeriesAndFormatter<SeriesType, FormatterType> getSeries(SeriesType ser
607623
/**
608624
*
609625
* @param series
610-
* @return A List of {@link SeriesAndFormatter} instances that reference series.
611-
*/
612-
protected List<SeriesAndFormatter<SeriesType, FormatterType>> getSeries(SeriesType series) {
613-
List<SeriesAndFormatter<SeriesType, FormatterType>> results =
614-
new ArrayList<>();
615-
for(SeriesAndFormatter<SeriesType, FormatterType> thisPair : seriesRegistry) {
616-
if(thisPair.getSeries() == series) {
617-
results.add(thisPair);
618-
}
619-
}
620-
return results;
626+
* @return A List of {@link SeriesBundle} instances that reference series.
627+
*/
628+
protected List<SeriesBundle<SeriesType, FormatterType>> getSeries(SeriesType series) {
629+
return getRegistry().get(series);
621630
}
622631

623632
/**
@@ -626,58 +635,47 @@ protected List<SeriesAndFormatter<SeriesType, FormatterType>> getSeries(SeriesTy
626635
* from the plot completely.
627636
* @param series
628637
* @param rendererClass
629-
* @return The SeriesAndFormatterPair that was removed or null if nothing was removed.
638+
* @return True if anything was removed, false otherwise
630639
*/
631-
public synchronized SeriesAndFormatter<SeriesType, FormatterType> removeSeries(SeriesType series, Class<? extends RendererType> rendererClass) {
640+
public synchronized boolean removeSeries(SeriesType series, Class<? extends RendererType> rendererClass) {
632641

633-
List<SeriesAndFormatter<SeriesType, FormatterType>> results = getSeries(series);
634-
SeriesAndFormatter<SeriesType, FormatterType> result = null;
635-
for(SeriesAndFormatter<SeriesType, FormatterType> thisPair : results) {
636-
if(thisPair.getFormatter().getRendererClass() == rendererClass) {
637-
result = thisPair;
638-
getSeriesRegistry().remove(result);
639-
break;
640-
}
641-
}
642+
List removedItems = getRegistry().remove(series, rendererClass);
642643

643644
// if series implements PlotListener and is not assigned to any other renderers remove it as a listener:
644-
if(series instanceof PlotListener && results.size() == 1) {
645+
if (removedItems.size() == 1 && series instanceof PlotListener) {
645646
removeListener((PlotListener) series);
647+
return true;
646648
}
647-
648-
return result;
649+
return false;
649650
}
650651

651652
/**
652653
* Remove all occurrences of series regardless of the associated Renderer.
653654
* @param series
654655
*/
655656
public synchronized void removeSeries(SeriesType series) {
656-
657-
for(Iterator<SeriesAndFormatter<SeriesType, FormatterType>> it = getSeriesRegistry().iterator(); it.hasNext();) {
658-
if(it.next().getSeries() == series) {
659-
it.remove();
660-
}
661-
}
662-
663657
// if series implements PlotListener, remove it from listeners:
664658
if (series instanceof PlotListener) {
665659
removeListener((PlotListener) series);
666660
}
661+
662+
getRegistry().remove(series);
667663
}
668664

669665
/**
670666
* Remove all series from the plot.
671667
*/
672668
public void clear() {
673-
for(Iterator<SeriesAndFormatter<SeriesType, FormatterType>> it = getSeriesRegistry().iterator(); it.hasNext();) {
674-
it.next();
675-
it.remove();
669+
for(SeriesType series : getRegistry().getSeriesList()) {
670+
if(series instanceof PlotListener) {
671+
removeListener((PlotListener) series);
672+
}
676673
}
674+
getRegistry().clear();
677675
}
678676

679677
public boolean isEmpty() {
680-
return getSeriesRegistry().isEmpty();
678+
return getRegistry().isEmpty();
681679
}
682680

683681
/**
@@ -754,7 +752,7 @@ protected synchronized void onSizeChanged (int w, int h, int oldw, int oldh) {
754752
PixelUtils.init(getContext());
755753

756754
// disable hardware acceleration if it's not explicitly supported
757-
// by the current Plot implementation. this check only applies to
755+
// by the current Plot implementation. this run only applies to
758756
// honeycomb and later environments.
759757
if (Build.VERSION.SDK_INT >= 11) {
760758
if (!isHwAccelerationSupported() && isHardwareAccelerated()) {
@@ -831,12 +829,13 @@ protected synchronized void renderOnCanvas(Canvas canvas) {
831829
} catch (Exception e) {
832830
Log.e(TAG, "Exception while rendering Plot.", e);
833831
}
834-
} finally {
832+
835833
isIdle = true;
836834
// any series interested in synchronizing with plot should
837835
// implement PlotListener.onAfterDraw(...) and do a read unlock from within that
838836
// invocation. This is the entry point for that invocation.
839837
notifyListenersAfterDraw(canvas);
838+
} finally {
840839
}
841840
}
842841

androidplot-core/src/main/java/com/androidplot/Region.java

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717

1818
package com.androidplot;
1919

20+
import com.androidplot.util.*;
21+
2022
/**
2123
* A one dimensional region represented by a starting and ending value.
2224
*/
2325
public class Region {
24-
private Number min;
25-
private Number max;
26+
private FastNumber min;
27+
private FastNumber max;
28+
private FastNumber cachedLength;
29+
2630
private Region defaults = this;
2731

2832
public Region() {}
@@ -46,6 +50,11 @@ public Region(Number v1, Number v2) {
4650
}
4751
}
4852

53+
public void setMinMax(Region region) {
54+
setMin(region.getMin());
55+
setMax(region.getMax());
56+
}
57+
4958
/**
5059
*
5160
* @param v1
@@ -58,8 +67,14 @@ public static Number measure(Number v1, Number v2) {
5867
}
5968

6069
public Number length() {
61-
return getMax() == null || getMin() == null ?
62-
null : getMax().doubleValue() - getMin().doubleValue();
70+
if(cachedLength == null) {
71+
Number l = getMax() == null || getMin() == null ?
72+
null : getMax().doubleValue() - getMin().doubleValue();
73+
if(l != null) {
74+
cachedLength = new FastNumber(l);
75+
}
76+
}
77+
return cachedLength;
6378
}
6479

6580
/**
@@ -95,7 +110,7 @@ public Number transform(double value, Region region2) {
95110
}
96111

97112
public Number transform(double value, Region region2, boolean flip) {
98-
return transform(value, region2.min.doubleValue(), region2.max.doubleValue(), flip);
113+
return transform(value, region2.getMin().doubleValue(), region2.getMax().doubleValue(), flip);
99114
}
100115

101116
public double transform(double value, double min, double max, boolean flip) {
@@ -112,13 +127,35 @@ public double transform(double value, double min, double max, boolean flip) {
112127
}
113128

114129
public Number ratio(Region r2) {
115-
return ratio(r2.min.doubleValue(), r2.max.doubleValue());
130+
return ratio(r2.getMin().doubleValue(), r2.getMax().doubleValue());
116131
}
117132

133+
/**
134+
*
135+
* @param min
136+
* @param max
137+
* @return length of this series divided by the length of the distance between min and max.
138+
*/
118139
public double ratio(double min, double max) {
119140
return length().doubleValue() / (max - min);
120141
}
121142

143+
144+
public void union(Number value) {
145+
if(value == null) {
146+
return;
147+
}
148+
double val = value.doubleValue();
149+
if(getMin() == null ||
150+
val < getMin().doubleValue()) {
151+
setMin(value);
152+
}
153+
if(getMax() == null || val >
154+
getMax().doubleValue()) {
155+
setMax(value);
156+
}
157+
}
158+
122159
/**
123160
* Compares the input bounds min/max against this instance's current min/max.
124161
* If the input.min is less than this.min then this.min will be set to input.min.
@@ -128,14 +165,8 @@ public double ratio(double min, double max) {
128165
* @param input
129166
*/
130167
public void union(Region input) {
131-
if(getMin() == null || input.min != null &&
132-
input.min.doubleValue() < getMin().doubleValue()) {
133-
setMin(input.min);
134-
}
135-
if(getMax() == null || input.max != null && input.max.doubleValue() >
136-
getMax().doubleValue()) {
137-
setMax(input.max);
138-
}
168+
union(input.getMin());
169+
union(input.getMax());
139170
}
140171

141172
/**
@@ -176,10 +207,17 @@ public Number getMin() {
176207
}
177208

178209
public void setMin(Number min) {
179-
if(min == null && defaults == null) {
180-
throw new NullPointerException("Region values cannot be null unless defaults have been set.");
210+
cachedLength = null;
211+
if(min == null) {
212+
if(defaults == null) {
213+
throw new NullPointerException(
214+
"Region values cannot be null unless defaults have been set.");
215+
} else {
216+
this.min = null;
217+
}
218+
} else {
219+
this.min = new FastNumber(min);
181220
}
182-
this.min = min;
183221
}
184222

185223
public boolean isMaxSet() {
@@ -191,10 +229,17 @@ public Number getMax() {
191229
}
192230

193231
public void setMax(Number max) {
194-
if(max == null && defaults == null) {
195-
throw new NullPointerException("Region values can never be null unless defaults have been set.");
232+
cachedLength = null;
233+
if(max == null) {
234+
if(defaults == null) {
235+
throw new NullPointerException(
236+
"Region values can never be null unless defaults have been set.");
237+
} else {
238+
this.max = null;
239+
}
240+
} else {
241+
this.max = new FastNumber(max);
196242
}
197-
this.max = max;
198243
}
199244

200245
/**

0 commit comments

Comments
 (0)