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:
parent
2e63b23921
commit
d1df3dd35f
@ -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>
|
||||
|
@ -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>
|
BIN
android/libs/achartengine-0.7.0.jar
Normal file
BIN
android/libs/achartengine-0.7.0.jar
Normal file
Binary file not shown.
@ -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>
|
@ -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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
227
src/net/sf/openrocket/android/simulation/SimulationChart.java
Normal file
227
src/net/sf/openrocket/android/simulation/SimulationChart.java
Normal 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 ));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user