Change Simulation plotting functionality to use AChartEngine instead of AndroidPlot. AChartEngine has built in zoom and pan though they are currently not used (because they are a little buggy). AChartEngine supports mulitple series plotted on same graph. However, it does not support the plotting of events.

Removed the sliding drawer from the configuration.  Instead a new activity is launched containing the plot.
This commit is contained in:
Kevin Ruland 2012-01-06 03:12:27 +00:00
parent 2e63b23921
commit d1df3dd35f
9 changed files with 407 additions and 421 deletions

View File

@ -7,5 +7,6 @@
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="lib" path="libs/android-support-v4.jar"/>
<classpathentry kind="lib" path="libs/Androidplot-core-0.4.4-release.jar"/>
<classpathentry kind="lib" path="libs/achartengine-0.7.0.jar" sourcepath="/AChartEngine/src"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@ -1,79 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.sf.openrocket"
android:versionCode="1"
android:versionName="1.0" >
package="net.sf.openrocket" android:versionCode="1"
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:debuggable="true"
android:icon="@drawable/or_launcher"
android:killAfterRestore="true"
android:label="@string/app_name"
android:name=".android.Application" >
<activity android:name=".android.Main" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<application android:debuggable="true" android:icon="@drawable/or_launcher"
android:killAfterRestore="true" android:label="@string/app_name"
android:name=".android.Application">
<activity android:name=".android.Main">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:label="@string/app_name"
android:name=".android.rocket.OpenRocketViewer" >
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:label="@string/app_name" android:name=".android.rocket.OpenRocketViewer">
<!--
I don't understand why I need to have two different intent filters. Combining the <data> elements
into a single field did not result in a working application.
The first intent-filter (with mimeType wildcard) convinces the file browser to associate the correct launcher
icon.
the second intent-filter is actually invoked when a file is selected.
-->
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<!-- I don't understand why I need to have two different intent filters.
Combining the <data> elements into a single field did not result in a working
application. The first intent-filter (with mimeType wildcard) convinces the
file browser to associate the correct launcher icon. the second intent-filter
is actually invoked when a file is selected. -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.ork"
android:scheme="file" />
</intent-filter>
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<data android:host="*" android:mimeType="*/*"
android:pathPattern=".*\\.ork" android:scheme="file" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="*"
android:pathPattern=".*\\.ork"
android:scheme="file" />
</intent-filter>
</activity>
<activity android:name=".android.PreferencesActivity" >
<intent-filter >
<action android:name="net.sf.openrocket.android.PreferencesActivity" />
<data android:host="*" android:pathPattern=".*\\.ork"
android:scheme="file" />
</intent-filter>
</activity>
<activity android:name=".android.PreferencesActivity">
<intent-filter>
<action android:name="net.sf.openrocket.android.PreferencesActivity" />
<category android:name="android.intent.category.PREFERENCE" />
</intent-filter>
</activity>
<activity
android:label="@string/MotorListTitle"
android:name=".android.motor.MotorHierarchicalBrowser" >
</activity>
<activity android:name=".android.motor.MotorDetails" />
<activity
android:label="@string/MotorListTitle"
android:name=".android.thrustcurve.TCQueryActivity" >
</activity>
<activity android:name=".android.simulation.SimulationViewer" />
</application>
<category android:name="android.intent.category.PREFERENCE" />
</intent-filter>
</activity>
<activity android:label="@string/MotorListTitle"
android:name=".android.motor.MotorHierarchicalBrowser" />
<activity android:name=".android.motor.MotorDetails" />
<activity android:label="@string/MotorListTitle"
android:name=".android.thrustcurve.TCQueryActivity" />
<activity android:name=".android.simulation.SimulationViewer" />
<activity android:name=".android.simulation.GraphicalActivity" />
</application>
</manifest>

Binary file not shown.

View File

@ -1,67 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/simulationPlotFragment"
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="draw"
android:text="Draw" />
<TabHost
android:id="@+id/simulationConfigurationForm"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="0px"
android:layout_marginRight="0px"
android:layout_marginTop="5px"
class="net.sf.openrocket.android.simulation.SimulationPlotFragment"
title="Simulation" />
android:background="@android:color/black" >
<SlidingDrawer
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.4"
android:content="@+id/simulationConfigurationForm"
android:handle="@+id/handle" >
<ImageView
android:id="@+id/handle"
android:layout_width="match_parent"
android:layout_height="30px"
android:src="@drawable/arrow_up_float"
android:text="" />
<TabHost
android:id="@+id/simulationConfigurationForm"
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/black" >
android:orientation="vertical" >
<LinearLayout
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
android:layout_height="wrap_content" />
<TabWidget
android:id="@android:id/tabs"
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="@+id/simulationEventsList"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/tabcontent"
<LinearLayout
android:id="@+id/simulationSeriesSelection"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
android:layout_height="wrap_content"
android:orientation="vertical" >
<ListView
android:id="@+id/simulationEventsList"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/simulationSeries1Label" />
<ListView
android:id="@+id/simulationSeriesList"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
</LinearLayout>
</TabHost>
</SlidingDrawer>
<Spinner
android:id="@+id/simulationSeries1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="true" />
</FrameLayout>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/simulationSeries2Label" />
<Spinner
android:id="@+id/simulationSeries2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="true" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</TabHost>
</LinearLayout>

View File

@ -93,6 +93,8 @@
</string-array>
<string name="TCMotorSearchFormSubmit">Submit</string>
<string name="tcdownload">Download from ThrustCurve</string>
<string name="tcdownload">Download from ThrustCurve</string>
<string name="simulationSeries1Label">Series 1</string>
<string name="simulationSeries2Label">Series 2</string>
</resources>

View File

@ -0,0 +1,51 @@
/**
* Copyright (C) 2009, 2010 SC 4ViewSoft SRL
*
* 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.
*/
package net.sf.openrocket.android.simulation;
import org.achartengine.ChartFactory;
import org.achartengine.GraphicalView;
import org.achartengine.chart.AbstractChart;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
/**
* An activity that encapsulates a graphical view of the chart.
*/
public class GraphicalActivity extends Activity {
/** The encapsulated graphical view. */
private GraphicalView mView;
/** The chart to be drawn. */
private AbstractChart mChart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Bundle extras = getIntent().getExtras();
mChart = (AbstractChart) extras.getSerializable(ChartFactory.CHART);
mView = new GraphicalView(this, mChart);
String title = extras.getString(ChartFactory.TITLE);
if (title == null) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
} else if (title.length() > 0) {
setTitle(title);
}
setContentView(mView);
}
}

View File

@ -0,0 +1,227 @@
/**
* Copyright (C) 2009, 2010 SC 4ViewSoft SRL
*
* 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.
*/
package net.sf.openrocket.android.simulation;
import java.util.List;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.FlightEvent;
import org.achartengine.ChartFactory;
import org.achartengine.chart.LineChart;
import org.achartengine.chart.PointStyle;
import org.achartengine.chart.XYChart;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint.Align;
import android.util.Log;
/**
* Multiple temperature demo chart.
*/
public class SimulationChart {
private final static String TAG = "SimulationChart";
private FlightDataBranch flightDataBranch;
private FlightDataType series1;
private FlightDataType series2;
private final FlightDataType time = FlightDataType.TYPE_TIME;
private List<FlightEvent> flightEvents;
// Define 4 different colors and point styles to use for the series.
// For now only 2 series are supported though.
private final static int[] colors = new int[] { Color.BLUE, Color.YELLOW, Color.GREEN, Color.RED };
private final static PointStyle[] styles = new PointStyle[] { PointStyle.CIRCLE, PointStyle.DIAMOND,
PointStyle.TRIANGLE, PointStyle.SQUARE };
/**
* @param flightDataBranch the flightDataBranch to set
*/
public void setFlightDataBranch(FlightDataBranch flightDataBranch) {
this.flightDataBranch = flightDataBranch;
}
/**
* @param series1 the series1 to set
*/
public void setSeries1(FlightDataType series1) {
this.series1 = series1;
}
/**
* @param series2 the series2 to set
*/
public void setSeries2(FlightDataType series2) {
this.series2 = series2;
}
/**
* @param flightEvents the flightEvents to set
*/
public void setFlightEvents(List<FlightEvent> flightEvents) {
this.flightEvents = flightEvents;
}
private static String formatFlightDataTypeAxisLabel( FlightDataType fdt ) {
return fdt.getName() + " (" + fdt.getUnitGroup().getDefaultUnit().toString() + ")";
}
/**
* Executes the chart demo.
*
* @param context the context
* @return the built intent
*/
public Intent execute(Context context) {
int seriesCount = 2;
// if the same series is selected twice, only plot it once.
if ( series1 == series2 ) {
seriesCount = 1;
}
XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(seriesCount);
renderer.setAxisTitleTextSize(16);
renderer.setChartTitleTextSize(20);
renderer.setLabelsTextSize(15);
renderer.setLegendTextSize(15);
renderer.setPointSize(5f);
renderer.setXLabels(10);
renderer.setYLabels(10);
renderer.setShowGrid(true);
//renderer.setZoomButtonsVisible(true);
renderer.setChartTitle("Simulation");
renderer.setMargins(new int[] { 50, 30, 0, 20 });
{
for (int i = 0; i < seriesCount; i++) {
XYSeriesRenderer r = new XYSeriesRenderer();
r.setColor(colors[i]);
r.setPointStyle(styles[i]);
r.setFillPoints(true);
renderer.addSeriesRenderer(r);
}
}
renderer.setXTitle(formatFlightDataTypeAxisLabel(time));
renderer.setXLabelsAlign(Align.RIGHT);
renderer.setYTitle(formatFlightDataTypeAxisLabel(series1),0);
renderer.setYLabelsAlign(Align.RIGHT,0);
if ( seriesCount > 1 ) {
renderer.setYTitle(formatFlightDataTypeAxisLabel(series2), 1);
renderer.setYAxisAlign(Align.RIGHT, 1);
renderer.setYLabelsAlign(Align.LEFT, 1);
}
renderer.setAxesColor(Color.LTGRAY);
renderer.setLabelsColor(Color.LTGRAY);
XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
List<Double> timevalues = flightDataBranch.get(time);
List<Double> series1values = flightDataBranch.get(series1);
// compute the axis limits using timevalues and series1values.
double xmin = 0;
double ymin = 0;
renderer.setXAxisMin(xmin);
renderer.setYAxisMin(ymin);
double ymax = computeMaxValueWithPadding( series1values );
double xmax = Math.ceil( timevalues.get( timevalues.size()-1));
Log.d(TAG,"ymax = " + ymax);
renderer.setXAxisMax(xmax);
renderer.setYAxisMax(ymax);
// Don't allow pan & zoom just yet.
renderer.setPanEnabled(false,false);
renderer.setZoomEnabled(false,false);
//renderer.setPanLimits(new double[] { xmin, xmax, ymin, ymax });
//renderer.setZoomLimits(new double[] { xmin, xmax, ymin, ymax });
// Add first series
addXYSeries(dataset, series1.getName(), timevalues, series1values, 0);
if ( seriesCount > 1 ) {
// Add second series
addXYSeries(dataset, series2.getName(), timevalues, flightDataBranch.get(series2), 1);
}
Intent intent = getLineChartIntent(context, dataset, renderer,"Simulation");
return intent;
}
private static void addXYSeries(XYMultipleSeriesDataset dataset, String titles, List<Double> xValues, List<Double> yValues, int scale) {
XYSeries series = new XYSeries(titles, scale);
int datasize = xValues.size();
for( int i = 0; i<datasize; i++ ) {
series.add(xValues.get(i), yValues.get(i));
}
dataset.addSeries(series);
}
private static Intent getLineChartIntent(Context context, XYMultipleSeriesDataset dataset,
XYMultipleSeriesRenderer renderer, String activityTitle) {
// checkParameters(dataset, renderer);
Intent intent = new Intent(context, GraphicalActivity.class);
XYChart chart = new LineChart(dataset, renderer);
intent.putExtra(ChartFactory.CHART, chart);
intent.putExtra(ChartFactory.TITLE, activityTitle);
return intent;
}
private static double computeMaxValueWithPadding( List<Double> list ) {
double max = list.get(0);
for( double v : list ) {
if ( v > max ) {
max = v;
}
}
if ( max <= 0 ) return 1.0;
// Do something stupid.
// return:
// 10 if max <= 10
// next 10 if 10 < max < 1000
// next 100 if 1000 < max < 10,000
// next 1000 if max >= 10,000
double numdigits = Math.floor(Math.log10(max));
if ( numdigits <= 1.0 ) {
return 10.0;
} else if ( numdigits <= 3.0 ) {
return 10.0 * ( Math.ceil( max/10.0));
} else if ( numdigits <= 4.0 ) {
return 100.0 * ( Math.ceil( max/ 100.0) );
} else {
return 1000.0 * ( Math.ceil( max / 1000.0 ));
}
}
}

View File

@ -1,255 +0,0 @@
package net.sf.openrocket.android.simulation;
import java.util.List;
import java.util.Vector;
import net.sf.openrocket.R;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.FlightEvent;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import com.androidplot.xy.BoundaryMode;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.LineAndPointRenderer;
import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.ValueMarker;
import com.androidplot.xy.XValueMarker;
import com.androidplot.xy.XYPlot;
public class SimulationPlotFragment extends Fragment implements OnTouchListener {
private final static String TAG = "SimulationPlot";
private XYPlot mySimpleXYPlot;
private SimpleXYSeries mySeries;
private PointF minXY;
private PointF maxXY;
private float absMinX;
private float absMaxX;
private float minNoError;
private float maxNoError;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d(TAG,"onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG,"onCreate");
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG,"onCreateView");
View v = inflater.inflate(R.layout.motor_burn, container, false);
mySimpleXYPlot = (XYPlot) v.findViewById(R.id.xyplot);
mySimpleXYPlot.setOnTouchListener(this);
mScaleDetector = new ScaleGestureDetector(v.getContext(), new ScaleListener());
// Motor motor = getMotor();
// init(motor);
return v;
}
void init( FlightDataBranch data, FlightDataType selectedSeries, List<FlightEvent> eventsToShow ) {
mySimpleXYPlot.clear();
if ( data == null || selectedSeries == null || eventsToShow == null ) {
return;
}
mySimpleXYPlot.setUserDomainOrigin(0);
mySimpleXYPlot.setUserRangeOrigin(0);
mySimpleXYPlot.setRangeLabel("");
mySimpleXYPlot.setDomainLabel(FlightDataType.TYPE_TIME.getName() + " (" + FlightDataType.TYPE_TIME.getUnitGroup().getDefaultUnit().toString() + ")");
mySimpleXYPlot.setRangeLabel( selectedSeries.getName() + " (" + selectedSeries.getUnitGroup().getDefaultUnit().toString() + ")");
mySimpleXYPlot.disableAllMarkup();
for ( FlightEvent event : eventsToShow ) {
XValueMarker xmarker = new XValueMarker( event.getTime(), event.getType().toString() );
xmarker.setTextOrientation( ValueMarker.TextOrientation.VERTICAL );
mySimpleXYPlot.addMarker( xmarker );
}
List<Double> yvals = null;
List<Double> xvals = null;
try {
yvals = data.get(selectedSeries);
xvals = data.get(FlightDataType.TYPE_TIME);
Log.d("plot","data = " + yvals);
} catch ( Exception ex ) {
Log.d(TAG, "Exception: " + ex);
}
if ( yvals == null || yvals.size() == 0 ) {
yvals = new Vector<Double>();
yvals.add(0.0);
yvals.add(0.0);
yvals.add(1.0);
yvals.add(1.0);
}
Log.d("plot","data = " + yvals.toString());
mySeries = new SimpleXYSeries(xvals, yvals, selectedSeries.toString());
mySimpleXYPlot.addSeries(mySeries, LineAndPointRenderer.class,
new LineAndPointFormatter(Color.rgb(0, 255, 0), Color.rgb(200, 0, 0), null));
//Set of internal variables for keeping track of the boundaries
mySimpleXYPlot.calculateMinMaxVals();
mySimpleXYPlot.redraw();
minXY=new PointF(mySimpleXYPlot.getCalculatedMinX().floatValue(),mySimpleXYPlot.getCalculatedMinY().floatValue());
maxXY=new PointF(mySimpleXYPlot.getCalculatedMaxX().floatValue(),mySimpleXYPlot.getCalculatedMaxY().floatValue());
absMinX = minXY.x;
absMaxX = maxXY.x;
minNoError = Math.round(mySeries.getX(1).floatValue() +2);
maxNoError = Math.round(mySeries.getX(mySeries.size() -1).floatValue()) - 2.0f;
}
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = -1;
@Override
public boolean onTouch(View arg0, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
final int action = event.getAction();
switch ( action & MotionEvent.ACTION_MASK ) {
case MotionEvent.ACTION_DOWN: {
final float x = event.getX();
final float y = event.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = event.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
scroll(dx);
// do scroll.
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = -1;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = -1;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. choose a new active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex ==0 ? 1:0;
mLastTouchX = event.getX(newPointerIndex);
mLastTouchY = event.getY(newPointerIndex);
mActivePointerId = event.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
private void zoom(float scale) {
Log.d(TAG,"zoom by " + scale);
float domainSpan = absMaxX - absMinX;
Log.d(TAG,"domainSpan = " + domainSpan);
float domainMidPoint = absMaxX - domainSpan / 2.0f;
Log.d(TAG,"domainMidPoint = " + domainMidPoint);
float offset = domainSpan / scale;
Log.d(TAG,"offset " + offset);
minXY.x=domainMidPoint- offset;
Log.d(TAG,"min X " + minXY.x);
maxXY.x=domainMidPoint+offset;
Log.d(TAG,"max X " + maxXY.x);
checkBoundaries();
mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);
mySimpleXYPlot.redraw();
}
private void scroll(float pan) {
float domainSpan = maxXY.x - minXY.x;
float step = domainSpan / mySimpleXYPlot.getWidth();
float offset = pan * step;
minXY.x+= offset;
maxXY.x+= offset;
checkBoundaries();
mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.AUTO);
mySimpleXYPlot.redraw();
}
private void checkBoundaries() {
if ( minXY.x < absMinX)
minXY.x = absMinX;
// else if ( minXY.x > maxNoError )
// minXY.x = maxNoError;
if ( maxXY.x > absMaxX)
maxXY.x = absMaxX;
// else if ( maxXY.x < minNoError)
// maxXY.x = minNoError;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale( ScaleGestureDetector detector ) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 5.0f));
zoom(mScaleFactor);
return true;
}
}
}

View File

@ -9,36 +9,27 @@ import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.FlightEvent;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SlidingDrawer;
import android.widget.Spinner;
import android.widget.TabHost;
import android.widget.TextView;
public class SimulationViewer extends FragmentActivity
implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListener {
public class SimulationViewer extends Activity {
private final static String TAG = "MotorDetails";
private SlidingDrawer slidingDrawer;
private ImageView handle;
private final static String TAG = "SimulationViewer";
private ListView eventList;
private ListView seriesList;
private SimulationPlotFragment simPlot;
private Spinner series1Spinner;
private Spinner series2Spinner;
private Simulation sim;
private FlightDataBranch data;
@ -54,15 +45,6 @@ implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListen
sim = ((Application)this.getApplication()).getRocketDocument().getSimulation(simnumber);
data = sim.getSimulatedData().getBranch(0);
simPlot = (SimulationPlotFragment) getSupportFragmentManager().findFragmentById(R.id.simulationPlotFragment);
slidingDrawer = (SlidingDrawer) findViewById(R.id.drawer);
slidingDrawer.setOnDrawerOpenListener(this);
slidingDrawer.setOnDrawerCloseListener(this);
handle = (ImageView) findViewById(R.id.handle);
TabHost tabs=(TabHost)findViewById(R.id.simulationConfigurationForm);
tabs.setup();
@ -74,14 +56,12 @@ implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListen
tabs.addTab(spec);
spec=tabs.newTabSpec("tag2");
spec.setContent(R.id.simulationSeriesList);
spec.setContent(R.id.simulationSeriesSelection);
spec.setIndicator("Series");
tabs.addTab(spec);
eventList = (ListView) findViewById(R.id.simulationEventsList);
seriesList = (ListView) findViewById(R.id.simulationSeriesList);
// Initialize the eventList
ArrayAdapter<FlightEvent> events = new ArrayAdapter<FlightEvent>(this,android.R.layout.simple_list_item_multiple_choice,data.getEvents()) {
@ -102,6 +82,9 @@ implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListen
eventList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
eventList.setAdapter(events);
series1Spinner = (Spinner) findViewById(R.id.simulationSeries1);
series2Spinner = (Spinner) findViewById(R.id.simulationSeries2);
List<FlightDataType> selectableSeries = new ArrayList<FlightDataType>();
for( FlightDataType fdt : data.getTypes() ) {
if ( fdt == FlightDataType.TYPE_TIME ) {
@ -110,7 +93,7 @@ implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListen
selectableSeries.add(fdt);
}
}
ArrayAdapter<FlightDataType> serieses = new ArrayAdapter<FlightDataType>(this,android.R.layout.simple_list_item_multiple_choice,selectableSeries) {
ArrayAdapter<FlightDataType> serieses = new ArrayAdapter<FlightDataType>(this,android.R.layout.simple_spinner_item,selectableSeries) {
@Override
public View getView(int position, View convertView,
@ -118,7 +101,7 @@ implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListen
View v = convertView;
if ( v == null ) {
LayoutInflater li = getLayoutInflater();
v = li.inflate(android.R.layout.simple_list_item_multiple_choice,null);
v = li.inflate(android.R.layout.simple_spinner_item,null);
}
FlightDataType fdt = this.getItem(position);
((TextView)v.findViewById(android.R.id.text1)).setText( fdt.toString() );
@ -126,24 +109,12 @@ implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListen
}
};
seriesList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
seriesList.setAdapter(serieses);
redraw();
series1Spinner.setAdapter(serieses);
series2Spinner.setAdapter(serieses);
}
@Override
public void onDrawerOpened() {
handle.setImageResource(R.drawable.arrow_down_float);
}
@Override
public void onDrawerClosed() {
handle.setImageResource(R.drawable.arrow_up_float);
redraw();
}
private void redraw() {
public void draw( View v ) {
List<FlightEvent> eventsToShow = new ArrayList<FlightEvent>();
{
SparseBooleanArray eventsSelected = eventList.getCheckedItemPositions();
@ -154,16 +125,18 @@ implements SlidingDrawer.OnDrawerCloseListener, SlidingDrawer.OnDrawerOpenListen
}
}
}
FlightDataType selectedSeries = null;
{
int selected = seriesList.getCheckedItemPosition();
if ( selected >= 0 ) {
selectedSeries = (FlightDataType) seriesList.getAdapter().getItem(selected);
}
}
simPlot.init(data, selectedSeries, eventsToShow );
FlightDataType series1 = (FlightDataType) series1Spinner.getSelectedItem();
Log.d(TAG,"sereis1 = " + series1.toString());
FlightDataType series2 = (FlightDataType) series2Spinner.getSelectedItem();
Log.d(TAG,"series2 = " + series2.toString());
SimulationChart chart = new SimulationChart();
chart.setFlightDataBranch(data);
chart.setSeries1(series1);
chart.setSeries2(series2);
chart.setFlightEvents(eventsToShow);
startActivity(chart.execute(this));
}
}