Skip to content

Commit bae8096

Browse files
phisihalfhp
authored andcommitted
Introduce a new StepMode (halfhp#32)
* Introduce a new StepMode Why: Increment by value -> no good when zooming Increment by pixel -> no good when zooming subdivide -> depending on data chooses ticks at wired locations e.g (1.3 , 2.3, 3,3 instead of 1, 2, 3) Workaround: When you know your data: supply an array of predefined increments (by value) for StepModel to choose from to best fit the desired number of lines. For example: Start zoomed out with ticks every 100 and as you zoom in switch to 50,10,1 * Introduce a new StepMode Why: Increment by value -> no good when zooming Increment by pixel -> no good when zooming subdivide -> depending on data chooses ticks at wired locations e.g (1.3 , 2.3, 3,3 instead of 1, 2, 3) Workaround: When you know your data: supply an array of predefined increments (by value) for StepModel to choose from to best fit the desired number of lines. For example: Start zoomed out with ticks every 100 and as you zoom in switch to 50,10,1 * comments * restore messed up build files * sanity check for StepModelFit.setSteps added unit test for StepModelFit
1 parent 8e5fcc1 commit bae8096

6 files changed

Lines changed: 160 additions & 4 deletions

File tree

androidplot-core/src/main/java/com/androidplot/xy/StepMode.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
* INCREMENTAL_VALUE - (default) draw a tick every n values.
2121
* INCREMENTAL_PIXEL - draw a tick every n pixels.
2222
* SUBDIVIDE - draw n number of evenly spaced lines.
23+
* INCREMENT_BY_FIT choose increment from a list of possible values
2324
*/
2425
public enum StepMode {
2526
SUBDIVIDE, // default
2627
INCREMENT_BY_VAL,
27-
INCREMENT_BY_PIXELS
28+
INCREMENT_BY_PIXELS,
29+
INCREMENT_BY_FIT
2830
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.androidplot.xy;
2+
3+
import com.androidplot.Region;
4+
5+
import java.util.Arrays;
6+
7+
/**
8+
* Subclass of StepModel that chooses from predefined step values. Depending on the currently
9+
* displayed range (by value) choose increment so that the number of lines
10+
* is closest to StepModel.value
11+
*/
12+
public class StepModelFit extends StepModel {
13+
14+
private double[] steps; // list of steps to choose from
15+
private Region scale; // axis region on display
16+
17+
public StepModelFit(Region axisRegion, double[] increments, double numLines) {
18+
super(StepMode.INCREMENT_BY_FIT, numLines);
19+
20+
setSteps(increments);
21+
setScale(axisRegion);
22+
}
23+
24+
public double[] getSteps() {
25+
return steps;
26+
}
27+
28+
public void setSteps(double[] steps) {
29+
30+
// sanity checks: no null, 0 or negative
31+
if (steps == null || steps.length == 0)
32+
return;
33+
34+
for (double step : steps) {
35+
if (step <= 0.0d)
36+
return;
37+
}
38+
39+
this.steps = steps;
40+
}
41+
42+
public Region getScale() {
43+
return scale;
44+
}
45+
46+
public void setScale(Region scale) {
47+
this.scale = scale;
48+
}
49+
50+
// does not return StepModel.value instead calculates best fit
51+
@Override
52+
public double getValue() {
53+
54+
// no possible increments where supplied
55+
// or no region defined
56+
if (steps == null || scale == null || !scale.isDefined())
57+
return super.getValue();
58+
59+
double curStep = steps[0];
60+
double oldDistance = Math.abs((scale.length().doubleValue() / curStep)-super.getValue() );
61+
62+
// determine which step size comes closest to the desired number of steps
63+
// since steps[] is a small array brute force search is ok
64+
for (double step : steps) {
65+
66+
double newDistance = Math.abs((scale.length().doubleValue() / step)-super.getValue() );
67+
68+
// closer than previous stepping?
69+
if (newDistance < oldDistance){
70+
curStep = step;
71+
oldDistance = newDistance;
72+
}
73+
}
74+
return curStep;
75+
}
76+
77+
@Override
78+
public String toString() {
79+
return "StepModelFit{" +
80+
"steps=" + Arrays.toString(steps) +
81+
", scale=" + scale +
82+
", current stepping=" + getValue() +
83+
'}';
84+
}
85+
}

androidplot-core/src/main/java/com/androidplot/xy/XYGraphWidget.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ protected void drawLineLabel(Canvas canvas, Edge edge, Number val, float x, floa
546546
}
547547

548548
/**
549-
* Draws the drid and domain/range labels for the plot.
549+
* Draws the grid and domain/range labels for the plot.
550550
*
551551
* @param canvas
552552
*/

androidplot-core/src/main/java/com/androidplot/xy/XYStepCalculator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public static Step getStep(StepMode typeXY, double stepValue, Region realBounds,
5959
double stepCount = 0;
6060
switch(typeXY) {
6161
case INCREMENT_BY_VAL:
62+
case INCREMENT_BY_FIT:
6263
stepVal = stepValue;
6364
stepPix = stepValue / realBounds.ratio(pixelBounds).doubleValue();
6465
stepCount = pixelBounds.length().doubleValue() / stepPix;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.androidplot.xy;
2+
3+
import com.androidplot.Region;
4+
5+
import org.junit.After;
6+
import org.junit.Before;
7+
import org.junit.Test;
8+
9+
import static org.junit.Assert.*;
10+
11+
public class StepModelFitTest {
12+
13+
Region regionSmall = new Region(0,11);
14+
Region regionBig = new Region(-111,420);
15+
Region regionZero = new Region(0, 0);
16+
Region regionUndef = new Region(0, null);
17+
18+
double[] stpSmall = {1,2,5}, stpBig = {1,10,100}, nonsense = {0};
19+
20+
@Before
21+
public void setUp() throws Exception {
22+
23+
}
24+
25+
@After
26+
public void tearDown() throws Exception {
27+
28+
}
29+
30+
@Test
31+
public void getValue() throws Exception {
32+
33+
StepModelFit model = new StepModelFit(regionSmall,stpSmall,3);
34+
35+
assertEquals(5.0, model.getValue(), 0.0);
36+
model.setValue(5.0);
37+
assertEquals(2.0, model.getValue(), 0.0);
38+
model.setValue(7.0);
39+
assertEquals(2.0, model.getValue(), 0.0);
40+
41+
model.setSteps(stpBig);
42+
assertEquals(1.0, model.getValue(), 0.0);
43+
44+
model.setScale(regionBig);
45+
assertEquals(100.0, model.getValue(), 0.0);
46+
model.setValue(1000.0);
47+
assertEquals(1.0, model.getValue(), 0.0);
48+
49+
// bad parameters
50+
model.setSteps(nonsense);
51+
assertArrayEquals(stpBig,model.getSteps(), 0.0);
52+
53+
model.setScale(regionZero);
54+
assertEquals(stpBig[0], model.getValue(), 0.0);
55+
56+
model.setScale(regionUndef);
57+
model.setValue(1.1);
58+
assertEquals(1.1, model.getValue(), 0.0);
59+
}
60+
61+
}

demoapp/src/main/java/com/androidplot/demos/TouchZoomExampleActivity.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
public class TouchZoomExampleActivity extends Activity {
3232
private static final int SERIES_SIZE = 3000;
3333
private static final int SERIES_ALPHA = 255;
34+
private static final int NUM_GRIDLINES = 5;
3435
private XYPlot plot;
3536
private PanZoom panZoom;
3637
private Button resetButton;
@@ -53,8 +54,14 @@ public void onClick(View view) {
5354
// move dynamically with the data when the users pans or zooms:
5455
plot.setUserDomainOrigin(0);
5556
plot.setUserRangeOrigin(0);
56-
plot.setDomainStep(StepMode.INCREMENT_BY_VAL, 500);
57-
plot.setRangeStep(StepMode.INCREMENT_BY_VAL, 100);
57+
58+
// predefine the stepping of both axis
59+
// increment will be chosen from list to best fit NUM_GRIDLINES grid lines
60+
double[] inc_domain = new double[]{10,50,100,500};
61+
double[] inc_range = new double[]{1,5,10,20,50,100};
62+
plot.setDomainStepModel(new StepModelFit(plot.getBounds().getxRegion(),inc_domain,NUM_GRIDLINES));
63+
plot.setRangeStepModel( new StepModelFit(plot.getBounds().getyRegion(),inc_range,NUM_GRIDLINES));
64+
5865

5966
panSpinner = (Spinner) findViewById(R.id.pan_spinner);
6067
zoomSpinner = (Spinner) findViewById(R.id.zoom_spinner);

0 commit comments

Comments
 (0)