From: kruland2607 Date: Thu, 28 Jun 2012 13:32:57 +0000 (+0000) Subject: Added source code for achartengine 1.0.0 in order to apply patches to make it work... X-Git-Tag: upstream/12.09^2~141 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=dfe153139b917cc91dddc1efa0298d4204616462;p=debian%2Fopenrocket Added source code for achartengine 1.0.0 in order to apply patches to make it work better. git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@818 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/android-libraries/achartengine/.classpath b/android-libraries/achartengine/.classpath new file mode 100644 index 00000000..a4f1e405 --- /dev/null +++ b/android-libraries/achartengine/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/android-libraries/achartengine/.project b/android-libraries/achartengine/.project new file mode 100644 index 00000000..fb1c7567 --- /dev/null +++ b/android-libraries/achartengine/.project @@ -0,0 +1,33 @@ + + + achartengine + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/android-libraries/achartengine/AndroidManifest.xml b/android-libraries/achartengine/AndroidManifest.xml new file mode 100644 index 00000000..083e05fc --- /dev/null +++ b/android-libraries/achartengine/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/android-libraries/achartengine/extra/LICENSE-2.0.txt b/android-libraries/achartengine/extra/LICENSE-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/android-libraries/achartengine/extra/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "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. diff --git a/android-libraries/achartengine/project.properties b/android-libraries/achartengine/project.properties new file mode 100644 index 00000000..337e8f37 --- /dev/null +++ b/android-libraries/achartengine/project.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-7 +android.library=true diff --git a/android-libraries/achartengine/src/org/achartengine/ChartFactory.java b/android-libraries/achartengine/src/org/achartengine/ChartFactory.java new file mode 100644 index 00000000..301f1a8f --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/ChartFactory.java @@ -0,0 +1,708 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine; + +import org.achartengine.chart.BarChart; +import org.achartengine.chart.BarChart.Type; +import org.achartengine.chart.BubbleChart; +import org.achartengine.chart.CombinedXYChart; +import org.achartengine.chart.CubicLineChart; +import org.achartengine.chart.DialChart; +import org.achartengine.chart.DoughnutChart; +import org.achartengine.chart.LineChart; +import org.achartengine.chart.PieChart; +import org.achartengine.chart.RangeBarChart; +import org.achartengine.chart.ScatterChart; +import org.achartengine.chart.TimeChart; +import org.achartengine.chart.XYChart; +import org.achartengine.model.CategorySeries; +import org.achartengine.model.MultipleCategorySeries; +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.DialRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; + +import android.content.Context; +import android.content.Intent; + +/** + * Utility methods for creating chart views or intents. + */ +public class ChartFactory { + /** The key for the chart data. */ + public static final String CHART = "chart"; + + /** The key for the chart graphical activity title. */ + public static final String TITLE = "title"; + + private ChartFactory() { + // empty for now + } + + /** + * Creates a line chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a line chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final GraphicalView getLineChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + checkParameters(dataset, renderer); + XYChart chart = new LineChart(dataset, renderer); + return new GraphicalView(context, chart); + } + + /** + * Creates a cubic line chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a line chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final GraphicalView getCubeLineChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, float smoothness) { + checkParameters(dataset, renderer); + XYChart chart = new CubicLineChart(dataset, renderer, smoothness); + return new GraphicalView(context, chart); + } + + /** + * Creates a scatter chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a scatter chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final GraphicalView getScatterChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + checkParameters(dataset, renderer); + XYChart chart = new ScatterChart(dataset, renderer); + return new GraphicalView(context, chart); + } + + /** + * Creates a bubble chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a scatter chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final GraphicalView getBubbleChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + checkParameters(dataset, renderer); + XYChart chart = new BubbleChart(dataset, renderer); + return new GraphicalView(context, chart); + } + + /** + * Creates a time chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param format the date format pattern to be used for displaying the X axis + * date labels. If null, a default appropriate format will be used. + * @return a time chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final GraphicalView getTimeChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, String format) { + checkParameters(dataset, renderer); + TimeChart chart = new TimeChart(dataset, renderer); + chart.setDateFormat(format); + return new GraphicalView(context, chart); + } + + /** + * Creates a bar chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param type the bar chart type + * @return a bar chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final GraphicalView getBarChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, Type type) { + checkParameters(dataset, renderer); + XYChart chart = new BarChart(dataset, renderer, type); + return new GraphicalView(context, chart); + } + + /** + * Creates a range bar chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param type the range bar chart type + * @return a bar chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final GraphicalView getRangeBarChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, Type type) { + checkParameters(dataset, renderer); + XYChart chart = new RangeBarChart(dataset, renderer, type); + return new GraphicalView(context, chart); + } + + /** + * Creates a combined XY chart view. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param types the chart types (cannot be null) + * @return a combined XY chart graphical view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if a dataset number of items is different than the number of + * series renderers or number of chart types + */ + public static final GraphicalView getCombinedXYChartView(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, String[] types) { + if (dataset == null || renderer == null || types == null + || dataset.getSeriesCount() != types.length) { + throw new IllegalArgumentException( + "Dataset, renderer and types should be not null and the datasets series count should be equal to the types length"); + } + checkParameters(dataset, renderer); + CombinedXYChart chart = new CombinedXYChart(dataset, renderer, types); + return new GraphicalView(context, chart); + } + + /** + * Creates a pie chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the category series dataset (cannot be null) + * @param renderer the series renderer (cannot be null) + * @return a pie chart view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + public static final GraphicalView getPieChartView(Context context, CategorySeries dataset, + DefaultRenderer renderer) { + checkParameters(dataset, renderer); + PieChart chart = new PieChart(dataset, renderer); + return new GraphicalView(context, chart); + } + + /** + * Creates a dial chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the category series dataset (cannot be null) + * @param renderer the dial renderer (cannot be null) + * @return a pie chart view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + public static final GraphicalView getDialChartView(Context context, CategorySeries dataset, + DialRenderer renderer) { + checkParameters(dataset, renderer); + DialChart chart = new DialChart(dataset, renderer); + return new GraphicalView(context, chart); + } + + /** + * Creates a doughnut chart intent that can be used to start the graphical + * view activity. + * + * @param context the context + * @param dataset the multiple category series dataset (cannot be null) + * @param renderer the series renderer (cannot be null) + * @return a pie chart view + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + public static final GraphicalView getDoughnutChartView(Context context, + MultipleCategorySeries dataset, DefaultRenderer renderer) { + checkParameters(dataset, renderer); + DoughnutChart chart = new DoughnutChart(dataset, renderer); + return new GraphicalView(context, chart); + } + + /** + * + * Creates a line chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a line chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getLineChartIntent(Context context, XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer) { + return getLineChartIntent(context, dataset, renderer, ""); + } + + /** + * + * Creates a cubic line chart intent that can be used to start the graphical + * view activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a line chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getCubicLineChartIntent(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, float smoothness) { + return getCubicLineChartIntent(context, dataset, renderer, smoothness, ""); + } + + /** + * Creates a scatter chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a scatter chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getScatterChartIntent(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + return getScatterChartIntent(context, dataset, renderer, ""); + } + + /** + * Creates a bubble chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @return a scatter chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getBubbleChartIntent(Context context, XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer) { + return getBubbleChartIntent(context, dataset, renderer, ""); + } + + /** + * Creates a time chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param format the date format pattern to be used for displaying the X axis + * date labels. If null, a default appropriate format will be used. + * @return a time chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getTimeChartIntent(Context context, XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer, String format) { + return getTimeChartIntent(context, dataset, renderer, format, ""); + } + + /** + * Creates a bar chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param type the bar chart type + * @return a bar chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getBarChartIntent(Context context, XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer, Type type) { + return getBarChartIntent(context, dataset, renderer, type, ""); + } + + /** + * Creates a line chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param activityTitle the graphical chart activity title. If this is null, + * then the title bar will be hidden. If a blank title is passed in, + * then the title bar will be the default. Pass in any other string + * to set a custom title. + * @return a line chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final 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(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a line chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param activityTitle the graphical chart activity title. If this is null, + * then the title bar will be hidden. If a blank title is passed in, + * then the title bar will be the default. Pass in any other string + * to set a custom title. + * @return a line chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getCubicLineChartIntent(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, float smoothness, + String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + XYChart chart = new CubicLineChart(dataset, renderer, smoothness); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a scatter chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param activityTitle the graphical chart activity title + * @return a scatter chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getScatterChartIntent(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + XYChart chart = new ScatterChart(dataset, renderer); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a bubble chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param activityTitle the graphical chart activity title + * @return a scatter chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getBubbleChartIntent(Context context, XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer, String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + XYChart chart = new BubbleChart(dataset, renderer); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a time chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param format the date format pattern to be used for displaying the X axis + * date labels. If null, a default appropriate format will be used + * @param activityTitle the graphical chart activity title + * @return a time chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getTimeChartIntent(Context context, XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer, String format, String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + TimeChart chart = new TimeChart(dataset, renderer); + chart.setDateFormat(format); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a bar chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param type the bar chart type + * @param activityTitle the graphical chart activity title + * @return a bar chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getBarChartIntent(Context context, XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer, Type type, String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + BarChart chart = new BarChart(dataset, renderer, type); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a range bar chart intent that can be used to start the graphical + * view activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param type the range bar chart type + * @param activityTitle the graphical chart activity title + * @return a range bar chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + public static final Intent getRangeBarChartIntent(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, Type type, + String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + RangeBarChart chart = new RangeBarChart(dataset, renderer, type); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a combined XY chart intent that can be used to start the graphical + * view activity. + * + * @param context the context + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @param types the chart types (cannot be null) + * @param activityTitle the graphical chart activity title + * @return a combined XY chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if a dataset number of items is different than the number of + * series renderers or number of chart types + */ + public static final Intent getCombinedXYChartIntent(Context context, + XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, String[] types, + String activityTitle) { + if (dataset == null || renderer == null || types == null + || dataset.getSeriesCount() != types.length) { + throw new IllegalArgumentException( + "Datasets, renderers and types should be not null and the datasets series count should be equal to the types length"); + } + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + CombinedXYChart chart = new CombinedXYChart(dataset, renderer, types); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a pie chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the category series dataset (cannot be null) + * @param renderer the series renderer (cannot be null) + * @param activityTitle the graphical chart activity title + * @return a pie chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + public static final Intent getPieChartIntent(Context context, CategorySeries dataset, + DefaultRenderer renderer, String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + PieChart chart = new PieChart(dataset, renderer); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a doughnut chart intent that can be used to start the graphical + * view activity. + * + * @param context the context + * @param dataset the multiple category series dataset (cannot be null) + * @param renderer the series renderer (cannot be null) + * @param activityTitle the graphical chart activity title + * @return a pie chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + public static final Intent getDoughnutChartIntent(Context context, + MultipleCategorySeries dataset, DefaultRenderer renderer, String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + DoughnutChart chart = new DoughnutChart(dataset, renderer); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Creates a dial chart intent that can be used to start the graphical view + * activity. + * + * @param context the context + * @param dataset the category series dataset (cannot be null) + * @param renderer the dial renderer (cannot be null) + * @param activityTitle the graphical chart activity title + * @return a dial chart intent + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + public static final Intent getDialChartIntent(Context context, CategorySeries dataset, + DialRenderer renderer, String activityTitle) { + checkParameters(dataset, renderer); + Intent intent = new Intent(context, GraphicalActivity.class); + DialChart chart = new DialChart(dataset, renderer); + intent.putExtra(CHART, chart); + intent.putExtra(TITLE, activityTitle); + return intent; + } + + /** + * Checks the validity of the dataset and renderer parameters. + * + * @param dataset the multiple series dataset (cannot be null) + * @param renderer the multiple series renderer (cannot be null) + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset and the renderer don't include the same number of + * series + */ + private static void checkParameters(XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer) { + if (dataset == null || renderer == null + || dataset.getSeriesCount() != renderer.getSeriesRendererCount()) { + throw new IllegalArgumentException( + "Dataset and renderer should be not null and should have the same number of series"); + } + } + + /** + * Checks the validity of the dataset and renderer parameters. + * + * @param dataset the category series dataset (cannot be null) + * @param renderer the series renderer (cannot be null) + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + private static void checkParameters(CategorySeries dataset, DefaultRenderer renderer) { + if (dataset == null || renderer == null + || dataset.getItemCount() != renderer.getSeriesRendererCount()) { + throw new IllegalArgumentException( + "Dataset and renderer should be not null and the dataset number of items should be equal to the number of series renderers"); + } + } + + /** + * Checks the validity of the dataset and renderer parameters. + * + * @param dataset the category series dataset (cannot be null) + * @param renderer the series renderer (cannot be null) + * @throws IllegalArgumentException if dataset is null or renderer is null or + * if the dataset number of items is different than the number of + * series renderers + */ + private static void checkParameters(MultipleCategorySeries dataset, DefaultRenderer renderer) { + if (dataset == null || renderer == null + || !checkMultipleSeriesItems(dataset, renderer.getSeriesRendererCount())) { + throw new IllegalArgumentException( + "Titles and values should be not null and the dataset number of items should be equal to the number of series renderers"); + } + } + + private static boolean checkMultipleSeriesItems(MultipleCategorySeries dataset, int value) { + int count = dataset.getCategoriesCount(); + boolean equal = true; + for (int k = 0; k < count && equal; k++) { + equal = dataset.getValues(k).length == dataset.getTitles(k).length; + } + return equal; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/GraphicalActivity.java b/android-libraries/achartengine/src/org/achartengine/GraphicalActivity.java new file mode 100644 index 00000000..56c190ae --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/GraphicalActivity.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine; + +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); + 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); + } + +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/GraphicalView.java b/android-libraries/achartengine/src/org/achartengine/GraphicalView.java new file mode 100644 index 00000000..e9aebff5 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/GraphicalView.java @@ -0,0 +1,337 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine; + +import org.achartengine.chart.AbstractChart; +import org.achartengine.chart.RoundChart; +import org.achartengine.chart.XYChart; +import org.achartengine.model.Point; +import org.achartengine.model.SeriesSelection; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.tools.FitZoom; +import org.achartengine.tools.PanListener; +import org.achartengine.tools.Zoom; +import org.achartengine.tools.ZoomListener; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.os.Handler; +import android.view.MotionEvent; +import android.view.View; + +/** + * The view that encapsulates the graphical chart. + */ +public class GraphicalView extends View { + /** The chart to be drawn. */ + private AbstractChart mChart; + /** The chart renderer. */ + private DefaultRenderer mRenderer; + /** The view bounds. */ + private Rect mRect = new Rect(); + /** The user interface thread handler. */ + private Handler mHandler; + /** The zoom buttons rectangle. */ + private RectF mZoomR = new RectF(); + /** The zoom in icon. */ + private Bitmap zoomInImage; + /** The zoom out icon. */ + private Bitmap zoomOutImage; + /** The fit zoom icon. */ + private Bitmap fitZoomImage; + /** The zoom area size. */ + private int zoomSize = 50; + /** The zoom buttons background color. */ + private static final int ZOOM_BUTTONS_COLOR = Color.argb(175, 150, 150, 150); + /** The zoom in tool. */ + private Zoom mZoomIn; + /** The zoom out tool. */ + private Zoom mZoomOut; + /** The fit zoom tool. */ + private FitZoom mFitZoom; + /** The paint to be used when drawing the chart. */ + private Paint mPaint = new Paint(); + /** The touch handler. */ + private ITouchHandler mTouchHandler; + /** The old x coordinate. */ + private float oldX; + /** The old y coordinate. */ + private float oldY; + + /** + * Creates a new graphical view. + * + * @param context the context + * @param chart the chart to be drawn + */ + public GraphicalView(Context context, AbstractChart chart) { + super(context); + mChart = chart; + mHandler = new Handler(); + if (mChart instanceof XYChart) { + mRenderer = ((XYChart) mChart).getRenderer(); + } else { + mRenderer = ((RoundChart) mChart).getRenderer(); + } + if (mRenderer.isZoomButtonsVisible()) { + zoomInImage = BitmapFactory.decodeStream(GraphicalView.class + .getResourceAsStream("image/zoom_in.png")); + zoomOutImage = BitmapFactory.decodeStream(GraphicalView.class + .getResourceAsStream("image/zoom_out.png")); + fitZoomImage = BitmapFactory.decodeStream(GraphicalView.class + .getResourceAsStream("image/zoom-1.png")); + } + + if (mRenderer instanceof XYMultipleSeriesRenderer + && ((XYMultipleSeriesRenderer) mRenderer).getMarginsColor() == XYMultipleSeriesRenderer.NO_COLOR) { + ((XYMultipleSeriesRenderer) mRenderer).setMarginsColor(mPaint.getColor()); + } + if (mRenderer.isZoomEnabled() && mRenderer.isZoomButtonsVisible() + || mRenderer.isExternalZoomEnabled()) { + mZoomIn = new Zoom(mChart, true, mRenderer.getZoomRate()); + mZoomOut = new Zoom(mChart, false, mRenderer.getZoomRate()); + mFitZoom = new FitZoom(mChart); + } + int version = 7; + try { + version = Integer.valueOf(Build.VERSION.SDK); + } catch (Exception e) { + // do nothing + } + if (version < 7) { + mTouchHandler = new TouchHandlerOld(this, mChart); + } else { + mTouchHandler = new TouchHandler(this, mChart); + } + } + + /** + * Returns the current series selection object. + * + * @return the series selection + */ + public SeriesSelection getCurrentSeriesAndPoint() { + return mChart.getSeriesAndPointForScreenCoordinate(new Point(oldX, oldY)); + } + + /** + * Transforms the currently selected screen point to a real point. + * + * @param scale the scale + * @return the currently selected real point + */ + public double[] toRealPoint(int scale) { + if (mChart instanceof XYChart) { + XYChart chart = (XYChart) mChart; + return chart.toRealPoint(oldX, oldY, scale); + } + return null; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.getClipBounds(mRect); + int top = mRect.top; + int left = mRect.left; + int width = mRect.width(); + int height = mRect.height(); + if (mRenderer.isInScroll()) { + top = 0; + left = 0; + width = getMeasuredWidth(); + height = getMeasuredHeight(); + } + mChart.draw(canvas, left, top, width, height, mPaint); + if (mRenderer != null && mRenderer.isZoomEnabled() && mRenderer.isZoomButtonsVisible()) { + mPaint.setColor(ZOOM_BUTTONS_COLOR); + zoomSize = Math.max(zoomSize, Math.min(width, height) / 7); + mZoomR.set(left + width - zoomSize * 3, top + height - zoomSize * 0.775f, left + width, top + + height); + canvas.drawRoundRect(mZoomR, zoomSize / 3, zoomSize / 3, mPaint); + float buttonY = top + height - zoomSize * 0.625f; + canvas.drawBitmap(zoomInImage, left + width - zoomSize * 2.75f, buttonY, null); + canvas.drawBitmap(zoomOutImage, left + width - zoomSize * 1.75f, buttonY, null); + canvas.drawBitmap(fitZoomImage, left + width - zoomSize * 0.75f, buttonY, null); + } + } + + /** + * Sets the zoom rate. + * + * @param rate the zoom rate + */ + public void setZoomRate(float rate) { + if (mZoomIn != null && mZoomOut != null) { + mZoomIn.setZoomRate(rate); + mZoomOut.setZoomRate(rate); + } + } + + /** + * Do a chart zoom in. + */ + public void zoomIn() { + if (mZoomIn != null) { + mZoomIn.apply(Zoom.ZOOM_AXIS_XY); + repaint(); + } + } + + /** + * Do a chart zoom out. + */ + public void zoomOut() { + if (mZoomOut != null) { + mZoomOut.apply(Zoom.ZOOM_AXIS_XY); + repaint(); + } + } + + + + /** + * Do a chart zoom reset / fit zoom. + */ + public void zoomReset() { + if (mFitZoom != null) { + mFitZoom.apply(); + mZoomIn.notifyZoomResetListeners(); + repaint(); + } + } + + /** + * Adds a new zoom listener. + * + * @param listener zoom listener + */ + public void addZoomListener(ZoomListener listener, boolean onButtons, boolean onPinch) { + if (onButtons) { + if (mZoomIn != null) { + mZoomIn.addZoomListener(listener); + mZoomOut.addZoomListener(listener); + } + if (onPinch) { + mTouchHandler.addZoomListener(listener); + } + } + } + + /** + * Removes a zoom listener. + * + * @param listener zoom listener + */ + public synchronized void removeZoomListener(ZoomListener listener) { + if (mZoomIn != null) { + mZoomIn.removeZoomListener(listener); + mZoomOut.removeZoomListener(listener); + } + mTouchHandler.removeZoomListener(listener); + } + + /** + * Adds a new pan listener. + * + * @param listener pan listener + */ + public void addPanListener(PanListener listener) { + mTouchHandler.addPanListener(listener); + } + + /** + * Removes a pan listener. + * + * @param listener pan listener + */ + public void removePanListener(PanListener listener) { + mTouchHandler.removePanListener(listener); + } + + protected RectF getZoomRectangle() { + return mZoomR; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + // save the x and y so they can be used in the click and long press + // listeners + oldX = event.getX(); + oldY = event.getY(); + } + if (mRenderer != null && (mRenderer.isPanEnabled() || mRenderer.isZoomEnabled())) { + if (mTouchHandler.handleTouch(event)) { + return true; + } + } + return super.onTouchEvent(event); + } + + /** + * Schedule a view content repaint. + */ + public void repaint() { + mHandler.post(new Runnable() { + public void run() { + invalidate(); + } + }); + } + + /** + * Schedule a view content repaint, in the specified rectangle area. + * + * @param left the left position of the area to be repainted + * @param top the top position of the area to be repainted + * @param right the right position of the area to be repainted + * @param bottom the bottom position of the area to be repainted + */ + public void repaint(final int left, final int top, final int right, final int bottom) { + mHandler.post(new Runnable() { + public void run() { + invalidate(left, top, right, bottom); + } + }); + } + + /** + * Saves the content of the graphical view to a bitmap. + * + * @return the bitmap + */ + public Bitmap toBitmap() { + setDrawingCacheEnabled(false); + if (!isDrawingCacheEnabled()) { + setDrawingCacheEnabled(true); + } + if (mRenderer.isApplyBackgroundColor()) { + setDrawingCacheBackgroundColor(mRenderer.getBackgroundColor()); + } + setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH); + return getDrawingCache(true); + } + +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/ITouchHandler.java b/android-libraries/achartengine/src/org/achartengine/ITouchHandler.java new file mode 100644 index 00000000..4debe964 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/ITouchHandler.java @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine; + +import org.achartengine.tools.PanListener; +import org.achartengine.tools.ZoomListener; + +import android.view.MotionEvent; + +/** + * The interface to be implemented by the touch handlers. + */ +public interface ITouchHandler { + /** + * Handles the touch event. + * + * @param event the touch event + * @return true if the event was handled + */ + boolean handleTouch(MotionEvent event); + + /** + * Adds a new zoom listener. + * + * @param listener zoom listener + */ + void addZoomListener(ZoomListener listener); + + /** + * Removes a zoom listener. + * + * @param listener zoom listener + */ + void removeZoomListener(ZoomListener listener); + + /** + * Adds a new pan listener. + * + * @param listener pan listener + */ + void addPanListener(PanListener listener); + + /** + * Removes a pan listener. + * + * @param listener pan listener + */ + void removePanListener(PanListener listener); + +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/TouchHandler.java b/android-libraries/achartengine/src/org/achartengine/TouchHandler.java new file mode 100644 index 00000000..a06d05d6 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/TouchHandler.java @@ -0,0 +1,204 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine; + +import org.achartengine.chart.AbstractChart; +import org.achartengine.chart.RoundChart; +import org.achartengine.chart.XYChart; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.tools.Pan; +import org.achartengine.tools.PanListener; +import org.achartengine.tools.Zoom; +import org.achartengine.tools.ZoomListener; + +import android.graphics.RectF; +import android.view.MotionEvent; + +/** + * The main handler of the touch events. + */ +public class TouchHandler implements ITouchHandler { + /** The chart renderer. */ + private DefaultRenderer mRenderer; + /** The old x coordinate. */ + private float oldX; + /** The old y coordinate. */ + private float oldY; + /** The old x2 coordinate. */ + private float oldX2; + /** The old y2 coordinate. */ + private float oldY2; + /** The zoom buttons rectangle. */ + private RectF zoomR = new RectF(); + /** The pan tool. */ + private Pan mPan; + /** The zoom for the pinch gesture. */ + private Zoom mPinchZoom; + /** The graphical view. */ + private GraphicalView graphicalView; + + /** + * Creates a new graphical view. + * + * @param view the graphical view + * @param chart the chart to be drawn + */ + public TouchHandler(GraphicalView view, AbstractChart chart) { + graphicalView = view; + zoomR = graphicalView.getZoomRectangle(); + if (chart instanceof XYChart) { + mRenderer = ((XYChart) chart).getRenderer(); + } else { + mRenderer = ((RoundChart) chart).getRenderer(); + } + if (mRenderer.isPanEnabled()) { + mPan = new Pan(chart); + } + if (mRenderer.isZoomEnabled()) { + mPinchZoom = new Zoom(chart, true, 1); + } + } + + /** + * Handles the touch event. + * + * @param event the touch event + */ + public boolean handleTouch(MotionEvent event) { + int action = event.getAction(); + if (mRenderer != null && action == MotionEvent.ACTION_MOVE) { + if (oldX >= 0 || oldY >= 0) { + float newX = event.getX(0); + float newY = event.getY(0); + if (event.getPointerCount() > 1 && (oldX2 >= 0 || oldY2 >= 0) && mRenderer.isZoomEnabled()) { + float newX2 = event.getX(1); + float newY2 = event.getY(1); + float newDeltaX = Math.abs(newX - newX2); + float newDeltaY = Math.abs(newY - newY2); + float oldDeltaX = Math.abs(oldX - oldX2); + float oldDeltaY = Math.abs(oldY - oldY2); + float zoomRate = 1; + + float tan1 = Math.abs(newY - oldY) / Math.abs(newX - oldX); + float tan2 = Math.abs(newY2 - oldY2) / Math.abs(newX2 - oldX2); + if ( tan1 <= 0.577 && tan2 <= 0.577) { + // horizontal pinch zoom, |deltaY| / |deltaX| is [0 ~ 0.577], 0.577 is the approximate value of tan(Pi/6) + zoomRate = newDeltaX / oldDeltaX; + if (zoomRate > 0.909 && zoomRate < 1.1) { + mPinchZoom.setZoomRate(zoomRate); + mPinchZoom.apply(Zoom.ZOOM_AXIS_X); + } + } else if ( tan1 >= 1.732 && tan2 >= 1.732 ) { + // pinch zoom vertically, |deltaY| / |deltaX| is [1.732 ~ infinity], 1.732 is the approximate value of tan(Pi/3) + zoomRate = newDeltaY / oldDeltaY; + if (zoomRate > 0.909 && zoomRate < 1.1) { + mPinchZoom.setZoomRate(zoomRate); + mPinchZoom.apply(Zoom.ZOOM_AXIS_Y); + } + } else if ( (tan1 > 0.577 && tan1 < 1.732) && (tan2 > 0.577 && tan2 < 1.732) ){ + // pinch zoom diagonally + if (Math.abs(newX - oldX) >= Math.abs(newY - oldY)) { + zoomRate = newDeltaX / oldDeltaX; + } else { + zoomRate = newDeltaY / oldDeltaY; + } + if (zoomRate > 0.909 && zoomRate < 1.1) { + mPinchZoom.setZoomRate(zoomRate); + mPinchZoom.apply(Zoom.ZOOM_AXIS_XY); + } + } + oldX2 = newX2; + oldY2 = newY2; + } else if (mRenderer.isPanEnabled()) { + mPan.apply(oldX, oldY, newX, newY); + oldX2 = 0; + oldY2 = 0; + } + oldX = newX; + oldY = newY; + graphicalView.repaint(); + return true; + } + } else if (action == MotionEvent.ACTION_DOWN) { + oldX = event.getX(0); + oldY = event.getY(0); + if (mRenderer != null && mRenderer.isZoomEnabled() && zoomR.contains(oldX, oldY)) { + if (oldX < zoomR.left + zoomR.width() / 3) { + graphicalView.zoomIn(); + } else if (oldX < zoomR.left + zoomR.width() * 2 / 3) { + graphicalView.zoomOut(); + } else { + graphicalView.zoomReset(); + } + return true; + } + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) { + oldX = 0; + oldY = 0; + oldX2 = 0; + oldY2 = 0; + if (action == MotionEvent.ACTION_POINTER_UP) { + oldX = -1; + oldY = -1; + } + } + return !mRenderer.isClickEnabled(); + } + + /** + * Adds a new zoom listener. + * + * @param listener zoom listener + */ + public void addZoomListener(ZoomListener listener) { + if (mPinchZoom != null) { + mPinchZoom.addZoomListener(listener); + } + } + + /** + * Removes a zoom listener. + * + * @param listener zoom listener + */ + public void removeZoomListener(ZoomListener listener) { + if (mPinchZoom != null) { + mPinchZoom.removeZoomListener(listener); + } + } + + /** + * Adds a new pan listener. + * + * @param listener pan listener + */ + public void addPanListener(PanListener listener) { + if (mPan != null) { + mPan.addPanListener(listener); + } + } + + /** + * Removes a pan listener. + * + * @param listener pan listener + */ + public void removePanListener(PanListener listener) { + if (mPan != null) { + mPan.removePanListener(listener); + } + } +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/TouchHandlerOld.java b/android-libraries/achartengine/src/org/achartengine/TouchHandlerOld.java new file mode 100644 index 00000000..38b4f227 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/TouchHandlerOld.java @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine; + +import org.achartengine.chart.AbstractChart; +import org.achartengine.chart.RoundChart; +import org.achartengine.chart.XYChart; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.tools.Pan; +import org.achartengine.tools.PanListener; +import org.achartengine.tools.ZoomListener; + +import android.graphics.RectF; +import android.view.MotionEvent; + +/** + * A handler implementation for touch events for older platforms. + */ +public class TouchHandlerOld implements ITouchHandler { + /** The chart renderer. */ + private DefaultRenderer mRenderer; + /** The old x coordinate. */ + private float oldX; + /** The old y coordinate. */ + private float oldY; + /** The zoom buttons rectangle. */ + private RectF zoomR = new RectF(); + /** The pan tool. */ + private Pan mPan; + /** The graphical view. */ + private GraphicalView graphicalView; + + /** + * Creates an implementation of the old version of the touch handler. + * + * @param view the graphical view + * @param chart the chart to be drawn + */ + public TouchHandlerOld(GraphicalView view, AbstractChart chart) { + graphicalView = view; + zoomR = graphicalView.getZoomRectangle(); + if (chart instanceof XYChart) { + mRenderer = ((XYChart) chart).getRenderer(); + } else { + mRenderer = ((RoundChart) chart).getRenderer(); + } + if (mRenderer.isPanEnabled()) { + mPan = new Pan(chart); + } + } + + public boolean handleTouch(MotionEvent event) { + int action = event.getAction(); + if (mRenderer != null && action == MotionEvent.ACTION_MOVE) { + if (oldX >= 0 || oldY >= 0) { + float newX = event.getX(); + float newY = event.getY(); + if (mRenderer.isPanEnabled()) { + mPan.apply(oldX, oldY, newX, newY); + } + oldX = newX; + oldY = newY; + graphicalView.repaint(); + return true; + } + } else if (action == MotionEvent.ACTION_DOWN) { + oldX = event.getX(); + oldY = event.getY(); + if (mRenderer != null && mRenderer.isZoomEnabled() && zoomR.contains(oldX, oldY)) { + if (oldX < zoomR.left + zoomR.width() / 3) { + graphicalView.zoomIn(); + } else if (oldX < zoomR.left + zoomR.width() * 2 / 3) { + graphicalView.zoomOut(); + } else { + graphicalView.zoomReset(); + } + return true; + } + } else if (action == MotionEvent.ACTION_UP) { + oldX = 0; + oldY = 0; + } + return !mRenderer.isClickEnabled(); + } + + /** + * Adds a new zoom listener. + * + * @param listener zoom listener + */ + public void addZoomListener(ZoomListener listener) { + } + + /** + * Removes a zoom listener. + * + * @param listener zoom listener + */ + public void removeZoomListener(ZoomListener listener) { + } + + /** + * Adds a new pan listener. + * + * @param listener pan listener + */ + public void addPanListener(PanListener listener) { + if (mPan != null) { + mPan.addPanListener(listener); + } + } + + /** + * Removes a pan listener. + * + * @param listener pan listener + */ + public void removePanListener(PanListener listener) { + if (mPan != null) { + mPan.removePanListener(listener); + } + } + +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/chart/AbstractChart.java b/android-libraries/achartengine/src/org/achartengine/chart/AbstractChart.java new file mode 100644 index 00000000..f99a5c2a --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/AbstractChart.java @@ -0,0 +1,475 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.io.Serializable; +import java.util.List; + +import org.achartengine.model.Point; +import org.achartengine.model.SeriesSelection; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer.Orientation; +import org.achartengine.util.MathHelper; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; + +/** + * An abstract class to be implemented by the chart rendering classes. + */ +public abstract class AbstractChart implements Serializable { + /** + * The graphical representation of the chart. + * + * @param canvas the canvas to paint to + * @param x the top left x value of the view to draw to + * @param y the top left y value of the view to draw to + * @param width the width of the view to draw to + * @param height the height of the view to draw to + * @param paint the paint + */ + public abstract void draw(Canvas canvas, int x, int y, int width, int height, Paint paint); + + /** + * Draws the chart background. + * + * @param renderer the chart renderer + * @param canvas the canvas to paint to + * @param x the top left x value of the view to draw to + * @param y the top left y value of the view to draw to + * @param width the width of the view to draw to + * @param height the height of the view to draw to + * @param paint the paint used for drawing + * @param newColor if a new color is to be used + * @param color the color to be used + */ + protected void drawBackground(DefaultRenderer renderer, Canvas canvas, int x, int y, int width, + int height, Paint paint, boolean newColor, int color) { + if (renderer.isApplyBackgroundColor() || newColor) { + if (newColor) { + paint.setColor(color); + } else { + paint.setColor(renderer.getBackgroundColor()); + } + paint.setStyle(Style.FILL); + canvas.drawRect(x, y, x + width, y + height, paint); + } + } + + /** + * Draws the chart legend. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param titles the titles to go to the legend + * @param left the left X value of the area to draw to + * @param right the right X value of the area to draw to + * @param y the y value of the area to draw to + * @param width the width of the area to draw to + * @param height the height of the area to draw to + * @param legendSize the legend size + * @param paint the paint to be used for drawing + * @param calculate if only calculating the legend size + * + * @return the legend height + */ + protected int drawLegend(Canvas canvas, DefaultRenderer renderer, String[] titles, int left, + int right, int y, int width, int height, int legendSize, Paint paint, boolean calculate) { + float size = 32; + if (renderer.isShowLegend()) { + float currentX = left; + float currentY = y + height - legendSize + size; + paint.setTextAlign(Align.LEFT); + paint.setTextSize(renderer.getLegendTextSize()); + int sLength = Math.min(titles.length, renderer.getSeriesRendererCount()); + for (int i = 0; i < sLength; i++) { + final float lineSize = getLegendShapeWidth(i); + String text = titles[i]; + if (titles.length == renderer.getSeriesRendererCount()) { + paint.setColor(renderer.getSeriesRendererAt(i).getColor()); + } else { + paint.setColor(Color.LTGRAY); + } + float[] widths = new float[text.length()]; + paint.getTextWidths(text, widths); + float sum = 0; + for (float value : widths) { + sum += value; + } + float extraSize = lineSize + 10 + sum; + float currentWidth = currentX + extraSize; + + if (i > 0 && getExceed(currentWidth, renderer, right, width)) { + currentX = left; + currentY += renderer.getLegendTextSize(); + size += renderer.getLegendTextSize(); + currentWidth = currentX + extraSize; + } + if (getExceed(currentWidth, renderer, right, width)) { + float maxWidth = right - currentX - lineSize - 10; + if (isVertical(renderer)) { + maxWidth = width - currentX - lineSize - 10; + } + int nr = paint.breakText(text, true, maxWidth, widths); + text = text.substring(0, nr) + "..."; + } + if (!calculate) { + drawLegendShape(canvas, renderer.getSeriesRendererAt(i), currentX, currentY, i, paint); + drawString(canvas, text, currentX + lineSize + 5, currentY + 5, paint); + } + currentX += extraSize; + } + } + return Math.round(size + renderer.getLegendTextSize()); + } + + /** + * Draw a multiple lines string. + * + * @param canvas the canvas to paint to + * @param text the text to be painted + * @param x the x value of the area to draw to + * @param y the y value of the area to draw to + * @param paint the paint to be used for drawing + */ + protected void drawString(Canvas canvas, String text, float x, float y, Paint paint) { + String[] lines = text.split("\n"); + Rect rect = new Rect(); + int yOff = 0; + for (int i = 0; i < lines.length; ++i) { + canvas.drawText(lines[i], x, y + yOff, paint); + paint.getTextBounds(lines[i], 0, lines[i].length(), rect); + yOff = yOff + rect.height() + 5; // space between lines is 5 + } + } + + /** + * Calculates if the current width exceeds the total width. + * + * @param currentWidth the current width + * @param renderer the renderer + * @param right the right side pixel value + * @param width the total width + * @return if the current width exceeds the total width + */ + protected boolean getExceed(float currentWidth, DefaultRenderer renderer, int right, int width) { + boolean exceed = currentWidth > right; + if (isVertical(renderer)) { + exceed = currentWidth > width; + } + return exceed; + } + + /** + * Checks if the current chart is rendered as vertical. + * + * @param renderer the renderer + * @return if the chart is rendered as a vertical one + */ + public boolean isVertical(DefaultRenderer renderer) { + return renderer instanceof XYMultipleSeriesRenderer + && ((XYMultipleSeriesRenderer) renderer).getOrientation() == Orientation.VERTICAL; + } + + /** + * Makes sure the fraction digit is not displayed, if not needed. + * + * @param label the input label value + * @return the label without the useless fraction digit + */ + protected String getLabel(double label) { + String text = ""; + if (label == Math.round(label)) { + text = Math.round(label) + ""; + } else { + text = label + ""; + } + return text; + } + + private static float[] calculateDrawPoints(float p1x, float p1y, float p2x, float p2y, + int screenHeight, int screenWidth) { + float drawP1x; + float drawP1y; + float drawP2x; + float drawP2y; + + if (p1y > screenHeight) { + // Intersection with the top of the screen + float m = (p2y - p1y) / (p2x - p1x); + drawP1x = (screenHeight - p1y + m * p1x) / m; + drawP1y = screenHeight; + + if (drawP1x < 0) { + // If Intersection is left of the screen we calculate the intersection + // with the left border + drawP1x = 0; + drawP1y = p1y - m * p1x; + } else if (drawP1x > screenWidth) { + // If Intersection is right of the screen we calculate the intersection + // with the right border + drawP1x = screenWidth; + drawP1y = m * screenWidth + p1y - m * p1x; + } + } else if (p1y < 0) { + float m = (p2y - p1y) / (p2x - p1x); + drawP1x = (-p1y + m * p1x) / m; + drawP1y = 0; + if (drawP1x < 0) { + drawP1x = 0; + drawP1y = p1y - m * p1x; + } else if (drawP1x > screenWidth) { + drawP1x = screenWidth; + drawP1y = m * screenWidth + p1y - m * p1x; + } + } else { + // If the point is in the screen use it + drawP1x = p1x; + drawP1y = p1y; + } + + if (p2y > screenHeight) { + float m = (p2y - p1y) / (p2x - p1x); + drawP2x = (screenHeight - p1y + m * p1x) / m; + drawP2y = screenHeight; + if (drawP2x < 0) { + drawP2x = 0; + drawP2y = p1y - m * p1x; + } else if (drawP2x > screenWidth) { + drawP2x = screenWidth; + drawP2y = m * screenWidth + p1y - m * p1x; + } + } else if (p2y < 0) { + float m = (p2y - p1y) / (p2x - p1x); + drawP2x = (-p1y + m * p1x) / m; + drawP2y = 0; + if (drawP2x < 0) { + drawP2x = 0; + drawP2y = p1y - m * p1x; + } else if (drawP2x > screenWidth) { + drawP2x = screenWidth; + drawP2y = m * screenWidth + p1y - m * p1x; + } + } else { + // If the point is in the screen use it + drawP2x = p2x; + drawP2y = p2y; + } + + return new float[] { drawP1x, drawP1y, drawP2x, drawP2y }; + } + + /** + * The graphical representation of a path. + * + * @param canvas the canvas to paint to + * @param points the points that are contained in the path to paint + * @param paint the paint to be used for painting + * @param circular if the path ends with the start point + */ + protected void drawPath(Canvas canvas, float[] points, Paint paint, boolean circular) { + Path path = new Path(); + int height = canvas.getHeight(); + int width = canvas.getWidth(); + + float[] tempDrawPoints; + if (points.length < 4) { + return; + } + tempDrawPoints = calculateDrawPoints(points[0], points[1], points[2], points[3], height, width); + path.moveTo(tempDrawPoints[0], tempDrawPoints[1]); + path.lineTo(tempDrawPoints[2], tempDrawPoints[3]); + + for (int i = 4; i < points.length; i += 2) { + if ((points[i - 1] < 0 && points[i + 1] < 0) + || (points[i - 1] > height && points[i + 1] > height)) { + continue; + } + tempDrawPoints = calculateDrawPoints(points[i - 2], points[i - 1], points[i], points[i + 1], + height, width); + if (!circular) { + path.moveTo(tempDrawPoints[0], tempDrawPoints[1]); + } + path.lineTo(tempDrawPoints[2], tempDrawPoints[3]); + } + if (circular) { + path.lineTo(points[0], points[1]); + } + canvas.drawPath(path, paint); + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public abstract int getLegendShapeWidth(int seriesIndex); + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public abstract void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, + float y, int seriesIndex, Paint paint); + + /** + * Calculates the best text to fit into the available space. + * + * @param text the entire text + * @param width the width to fit the text into + * @param paint the paint + * @return the text to fit into the space + */ + private String getFitText(String text, float width, Paint paint) { + String newText = text; + int length = text.length(); + int diff = 0; + while (paint.measureText(newText) > width && diff < length) { + diff++; + newText = text.substring(0, length - diff) + "..."; + } + if (diff == length) { + newText = "..."; + } + return newText; + } + + /** + * Calculates the current legend size. + * + * @param renderer the renderer + * @param defaultHeight the default height + * @param extraHeight the added extra height + * @return the legend size + */ + protected int getLegendSize(DefaultRenderer renderer, int defaultHeight, float extraHeight) { + int legendSize = renderer.getLegendHeight(); + if (renderer.isShowLegend() && legendSize == 0) { + legendSize = defaultHeight; + } + if (!renderer.isShowLegend() && renderer.isShowLabels()) { + legendSize = (int) (renderer.getLabelsTextSize() * 4 / 3 + extraHeight); + } + return legendSize; + } + + /** + * Draws a text label. + * + * @param canvas the canvas + * @param labelText the label text + * @param renderer the renderer + * @param prevLabelsBounds the previous rendered label bounds + * @param centerX the round chart center on X axis + * @param centerY the round chart center on Y axis + * @param shortRadius the short radius for the round chart + * @param longRadius the long radius for the round chart + * @param currentAngle the current angle + * @param angle the label extra angle + * @param left the left side + * @param right the right side + * @param color the label color + * @param paint the paint + * @param line if a line to the label should be drawn + */ + protected void drawLabel(Canvas canvas, String labelText, DefaultRenderer renderer, + List prevLabelsBounds, int centerX, int centerY, float shortRadius, float longRadius, + float currentAngle, float angle, int left, int right, int color, Paint paint, boolean line) { + if (renderer.isShowLabels()) { + paint.setColor(color); + double rAngle = Math.toRadians(90 - (currentAngle + angle / 2)); + double sinValue = Math.sin(rAngle); + double cosValue = Math.cos(rAngle); + int x1 = Math.round(centerX + (float) (shortRadius * sinValue)); + int y1 = Math.round(centerY + (float) (shortRadius * cosValue)); + int x2 = Math.round(centerX + (float) (longRadius * sinValue)); + int y2 = Math.round(centerY + (float) (longRadius * cosValue)); + + float size = renderer.getLabelsTextSize(); + float extra = Math.max(size / 2, 10); + paint.setTextAlign(Align.LEFT); + if (x1 > x2) { + extra = -extra; + paint.setTextAlign(Align.RIGHT); + } + float xLabel = x2 + extra; + float yLabel = y2; + float width = right - xLabel; + if (x1 > x2) { + width = xLabel - left; + } + labelText = getFitText(labelText, width, paint); + float widthLabel = paint.measureText(labelText); + boolean okBounds = false; + while (!okBounds && line) { + boolean intersects = false; + int length = prevLabelsBounds.size(); + for (int j = 0; j < length && !intersects; j++) { + RectF prevLabelBounds = prevLabelsBounds.get(j); + if (prevLabelBounds.intersects(xLabel, yLabel, xLabel + widthLabel, yLabel + size)) { + intersects = true; + yLabel = Math.max(yLabel, prevLabelBounds.bottom); + } + } + okBounds = !intersects; + } + + if (line) { + y2 = (int) (yLabel - size / 2); + canvas.drawLine(x1, y1, x2, y2, paint); + canvas.drawLine(x2, y2, x2 + extra, y2, paint); + } else { + paint.setTextAlign(Align.CENTER); + } + canvas.drawText(labelText, xLabel, yLabel, paint); + if (line) { + prevLabelsBounds.add(new RectF(xLabel, yLabel, xLabel + widthLabel, yLabel + size)); + } + } + } + + public boolean isNullValue(double value) { + return Double.isNaN(value) || Double.isInfinite(value) || value == MathHelper.NULL_VALUE; + } + + /** + * Given screen coordinates, returns the series and point indexes of a chart + * element. If there is no chart element (line, point, bar, etc) at those + * coordinates, null is returned. + * + * @param screenPoint + * @return the series and point indexes + */ + public SeriesSelection getSeriesAndPointForScreenCoordinate(Point screenPoint) { + return null; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/BarChart.java b/android-libraries/achartengine/src/org/achartengine/chart/BarChart.java new file mode 100644 index 00000000..d5d0fb23 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/BarChart.java @@ -0,0 +1,329 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.GradientDrawable.Orientation; + +/** + * The bar chart rendering class. + */ +public class BarChart extends XYChart { + /** The constant to identify this chart type. */ + public static final String TYPE = "Bar"; + /** The legend shape width. */ + private static final int SHAPE_WIDTH = 12; + /** The chart type. */ + protected Type mType = Type.DEFAULT; + + /** + * The bar chart type enum. + */ + public enum Type { + DEFAULT, STACKED; + } + + BarChart() { + } + + BarChart(Type type) { + mType = type; + } + + /** + * Builds a new bar chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + * @param type the bar chart type + */ + public BarChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, Type type) { + super(dataset, renderer); + mType = type; + } + + @Override + protected ClickableArea[] clickableAreasForPoints(float[] points, double[] values, + float yAxisValue, int seriesIndex, int startIndex) { + int seriesNr = mDataset.getSeriesCount(); + int length = points.length; + ClickableArea[] ret = new ClickableArea[length / 2]; + float halfDiffX = getHalfDiffX(points, length, seriesNr); + for (int i = 0; i < length; i += 2) { + float x = points[i]; + float y = points[i + 1]; + if (mType == Type.STACKED) { + ret[i / 2] = new ClickableArea(new RectF(x - halfDiffX, y, x + halfDiffX, yAxisValue), + values[i], values[i + 1]); + } else { + float startX = x - seriesNr * halfDiffX + seriesIndex * 2 * halfDiffX; + ret[i / 2] = new ClickableArea(new RectF(startX, y, startX + 2 * halfDiffX, yAxisValue), + values[i], values[i + 1]); + } + } + return ret; + } + + /** + * The graphical representation of a series. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesRenderer the series renderer + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + public void drawSeries(Canvas canvas, Paint paint, float[] points, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) { + int seriesNr = mDataset.getSeriesCount(); + int length = points.length; + paint.setColor(seriesRenderer.getColor()); + paint.setStyle(Style.FILL); + float halfDiffX = getHalfDiffX(points, length, seriesNr); + for (int i = 0; i < length; i += 2) { + float x = points[i]; + float y = points[i + 1]; + drawBar(canvas, x, yAxisValue, x, y, halfDiffX, seriesNr, seriesIndex, paint); + } + paint.setColor(seriesRenderer.getColor()); + } + + /** + * Draws a bar. + * + * @param canvas the canvas + * @param xMin the X axis minimum + * @param yMin the Y axis minimum + * @param xMax the X axis maximum + * @param yMax the Y axis maximum + * @param halfDiffX half the size of a bar + * @param seriesNr the total number of series + * @param seriesIndex the current series index + * @param paint the paint + */ + protected void drawBar(Canvas canvas, float xMin, float yMin, float xMax, float yMax, + float halfDiffX, int seriesNr, int seriesIndex, Paint paint) { + int scale = mDataset.getSeriesAt(seriesIndex).getScaleNumber(); + if (mType == Type.STACKED) { + drawBar(canvas, xMin - halfDiffX, yMax, xMax + halfDiffX, yMin, scale, seriesIndex, paint); + } else { + float startX = xMin - seriesNr * halfDiffX + seriesIndex * 2 * halfDiffX; + drawBar(canvas, startX, yMax, startX + 2 * halfDiffX, yMin, scale, seriesIndex, paint); + } + } + + /** + * Draws a bar. + * + * @param canvas the canvas + * @param xMin the X axis minimum + * @param yMin the Y axis minimum + * @param xMax the X axis maximum + * @param yMax the Y axis maximum + * @param scale the scale index + * @param seriesIndex the current series index + * @param paint the paint + */ + private void drawBar(Canvas canvas, float xMin, float yMin, float xMax, float yMax, int scale, + int seriesIndex, Paint paint) { + SimpleSeriesRenderer renderer = mRenderer.getSeriesRendererAt(seriesIndex); + if (renderer.isGradientEnabled()) { + float minY = (float) toScreenPoint(new double[] { 0, renderer.getGradientStopValue() }, scale)[1]; + float maxY = (float) toScreenPoint(new double[] { 0, renderer.getGradientStartValue() }, + scale)[1]; + float gradientMinY = Math.max(minY, Math.min(yMin, yMax)); + float gradientMaxY = Math.min(maxY, Math.max(yMin, yMax)); + int gradientMinColor = renderer.getGradientStopColor(); + int gradientMaxColor = renderer.getGradientStartColor(); + int gradientStartColor = gradientMaxColor; + int gradientStopColor = gradientMinColor; + + if (yMin < minY) { + paint.setColor(gradientMinColor); + canvas.drawRect(Math.round(xMin), Math.round(yMin), Math.round(xMax), + Math.round(gradientMinY), paint); + } else { + gradientStopColor = getGradientPartialColor(gradientMinColor, gradientMaxColor, + (maxY - gradientMinY) / (maxY - minY)); + } + if (yMax > maxY) { + paint.setColor(gradientMaxColor); + canvas.drawRect(Math.round(xMin), Math.round(gradientMaxY), Math.round(xMax), + Math.round(yMax), paint); + } else { + gradientStartColor = getGradientPartialColor(gradientMaxColor, gradientMinColor, + (gradientMaxY - minY) / (maxY - minY)); + } + GradientDrawable gradient = new GradientDrawable(Orientation.BOTTOM_TOP, new int[] { + gradientStartColor, gradientStopColor }); + gradient.setBounds(Math.round(xMin), Math.round(gradientMinY), Math.round(xMax), + Math.round(gradientMaxY)); + gradient.draw(canvas); + } else { + if (Math.abs(yMin - yMax) < 1) { + if (yMin < yMax) { + yMax = yMin + 1; + } else { + yMax = yMin - 1; + } + } + canvas + .drawRect(Math.round(xMin), Math.round(yMin), Math.round(xMax), Math.round(yMax), paint); + } + } + + private int getGradientPartialColor(int minColor, int maxColor, float fraction) { + int alpha = Math.round(fraction * Color.alpha(minColor) + (1 - fraction) + * Color.alpha(maxColor)); + int r = Math.round(fraction * Color.red(minColor) + (1 - fraction) * Color.red(maxColor)); + int g = Math.round(fraction * Color.green(minColor) + (1 - fraction) * Color.green(maxColor)); + int b = Math.round(fraction * Color.blue(minColor) + (1 - fraction) * Color.blue((maxColor))); + return Color.argb(alpha, r, g, b); + } + + /** + * The graphical representation of the series values as text. + * + * @param canvas the canvas to paint to + * @param series the series to be painted + * @param renderer the series renderer + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + protected void drawChartValuesText(Canvas canvas, XYSeries series, SimpleSeriesRenderer renderer, + Paint paint, float[] points, int seriesIndex, int startIndex) { + int seriesNr = mDataset.getSeriesCount(); + float halfDiffX = getHalfDiffX(points, points.length, seriesNr); + for (int i = 0; i < points.length; i += 2) { + int index = startIndex + i / 2; + double value = series.getY(index); + if (!isNullValue(value)) { + float x = points[i]; + if (mType == Type.DEFAULT) { + x += seriesIndex * 2 * halfDiffX - (seriesNr - 1.5f) * halfDiffX; + } + if (value >= 0) { + drawText(canvas, getLabel(value), x, points[i + 1] - renderer.getChartValuesSpacing(), + paint, 0); + } else { + drawText(canvas, getLabel(value), x, points[i + 1] + renderer.getChartValuesTextSize() + + renderer.getChartValuesSpacing() - 3, paint, 0); + } + } + } + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public int getLegendShapeWidth(int seriesIndex) { + return SHAPE_WIDTH; + } + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, + int seriesIndex, Paint paint) { + float halfShapeWidth = SHAPE_WIDTH / 2; + canvas.drawRect(x, y - halfShapeWidth, x + SHAPE_WIDTH, y + halfShapeWidth, paint); + } + + /** + * Calculates and returns the half-distance in the graphical representation of + * 2 consecutive points. + * + * @param points the points + * @param length the points length + * @param seriesNr the series number + * @return the calculated half-distance value + */ + protected float getHalfDiffX(float[] points, int length, int seriesNr) { + int div = length; + if (length > 2) { + div = length - 2; + } + float halfDiffX = (points[length - 2] - points[0]) / div; + if (halfDiffX == 0) { + halfDiffX = 10; + } + + if (mType != Type.STACKED) { + halfDiffX /= seriesNr; + } + return (float) (halfDiffX / (getCoeficient() * (1 + mRenderer.getBarSpacing()))); + } + + /** + * Returns the value of a constant used to calculate the half-distance. + * + * @return the constant value + */ + protected float getCoeficient() { + return 1f; + } + + /** + * Returns if the chart should display the null values. + * + * @return if null values should be rendered + */ + protected boolean isRenderNullValues() { + return true; + } + + /** + * Returns the default axis minimum. + * + * @return the default axis minimum + */ + public double getDefaultMinimum() { + return 0; + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return TYPE; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/BubbleChart.java b/android-libraries/achartengine/src/org/achartengine/chart/BubbleChart.java new file mode 100644 index 00000000..f3125713 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/BubbleChart.java @@ -0,0 +1,146 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYValueSeries; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; + +/** + * The bubble chart rendering class. + */ +public class BubbleChart extends XYChart { + /** The constant to identify this chart type. */ + public static final String TYPE = "Bubble"; + /** The legend shape width. */ + private static final int SHAPE_WIDTH = 10; + /** The minimum bubble size. */ + private static final int MIN_BUBBLE_SIZE = 2; + /** The maximum bubble size. */ + private static final int MAX_BUBBLE_SIZE = 20; + + BubbleChart() { + } + + /** + * Builds a new bubble chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + */ + public BubbleChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + super(dataset, renderer); + } + + /** + * The graphical representation of a series. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesRenderer the series renderer + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + public void drawSeries(Canvas canvas, Paint paint, float[] points, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) { + XYSeriesRenderer renderer = (XYSeriesRenderer) seriesRenderer; + paint.setColor(renderer.getColor()); + paint.setStyle(Style.FILL); + int length = points.length; + XYValueSeries series = (XYValueSeries) mDataset.getSeriesAt(seriesIndex); + double max = series.getMaxValue(); + double coef = MAX_BUBBLE_SIZE / max; + for (int i = 0; i < length; i += 2) { + double size = series.getValue(startIndex + i / 2) * coef + MIN_BUBBLE_SIZE; + drawCircle(canvas, paint, points[i], points[i + 1], (float) size); + } + } + + @Override + protected ClickableArea[] clickableAreasForPoints(float[] points, double[] values, + float yAxisValue, int seriesIndex, int startIndex) { + int length = points.length; + XYValueSeries series = (XYValueSeries) mDataset.getSeriesAt(seriesIndex); + double max = series.getMaxValue(); + double coef = MAX_BUBBLE_SIZE / max; + ClickableArea[] ret = new ClickableArea[length / 2]; + for (int i = 0; i < length; i += 2) { + double size = series.getValue(startIndex + i / 2) * coef + MIN_BUBBLE_SIZE; + ret[i / 2] = new ClickableArea(new RectF(points[i] - (float) size, points[i + 1] + - (float) size, points[i] + (float) size, points[i + 1] + (float) size), values[i], + values[i + 1]); + } + return ret; + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public int getLegendShapeWidth(int seriesIndex) { + return SHAPE_WIDTH; + } + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, + int seriesIndex, Paint paint) { + paint.setStyle(Style.FILL); + drawCircle(canvas, paint, x + SHAPE_WIDTH, y, 3); + } + + /** + * The graphical representation of a circle point shape. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param radius the bubble radius + */ + private void drawCircle(Canvas canvas, Paint paint, float x, float y, float radius) { + canvas.drawCircle(x, y, radius, paint); + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return TYPE; + } + +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/chart/ClickableArea.java b/android-libraries/achartengine/src/org/achartengine/chart/ClickableArea.java new file mode 100644 index 00000000..d2d306ca --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/ClickableArea.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import android.graphics.RectF; + +public class ClickableArea { + private RectF rect; + private double x; + private double y; + + public ClickableArea(RectF rect, double x, double y) { + super(); + this.rect = rect; + this.x = x; + this.y = y; + } + + public RectF getRect() { + return rect; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/CombinedXYChart.java b/android-libraries/achartengine/src/org/achartengine/chart/CombinedXYChart.java new file mode 100644 index 00000000..d684c3a2 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/CombinedXYChart.java @@ -0,0 +1,177 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.util.List; + +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer.Orientation; + +import android.graphics.Canvas; +import android.graphics.Paint; + +/** + * The combined XY chart rendering class. + */ +public class CombinedXYChart extends XYChart { + /** The embedded XY charts. */ + private XYChart[] mCharts; + /** The supported charts for being combined. */ + private Class[] xyChartTypes = new Class[] { TimeChart.class, LineChart.class, + CubicLineChart.class, BarChart.class, BubbleChart.class, ScatterChart.class, + RangeBarChart.class, RangeStackedBarChart.class }; + + /** + * Builds a new combined XY chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + * @param types the XY chart types + */ + public CombinedXYChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, + String[] types) { + super(dataset, renderer); + int length = types.length; + mCharts = new XYChart[length]; + for (int i = 0; i < length; i++) { + try { + mCharts[i] = getXYChart(types[i]); + } catch (Exception e) { + // ignore + } + if (mCharts[i] == null) { + throw new IllegalArgumentException("Unknown chart type " + types[i]); + } else { + XYMultipleSeriesDataset newDataset = new XYMultipleSeriesDataset(); + newDataset.addSeries(dataset.getSeriesAt(i)); + XYMultipleSeriesRenderer newRenderer = new XYMultipleSeriesRenderer(); + // TODO: copy other parameters here + newRenderer.setBarSpacing(renderer.getBarSpacing()); + newRenderer.setPointSize(renderer.getPointSize()); + int scale = dataset.getSeriesAt(i).getScaleNumber(); + if (renderer.isMinXSet(scale)) { + newRenderer.setXAxisMin(renderer.getXAxisMin(scale)); + } + if (renderer.isMaxXSet(scale)) { + newRenderer.setXAxisMax(renderer.getXAxisMax(scale)); + } + if (renderer.isMinYSet(scale)) { + newRenderer.setYAxisMin(renderer.getYAxisMin(scale)); + } + if (renderer.isMaxYSet(scale)) { + newRenderer.setYAxisMax(renderer.getYAxisMax(scale)); + } + newRenderer.addSeriesRenderer(renderer.getSeriesRendererAt(i)); + mCharts[i].setDatasetRenderer(newDataset, newRenderer); + } + } + } + + /** + * Returns a chart instance based on the provided type. + * + * @param type the chart type + * @return an instance of a chart implementation + * @throws IllegalAccessException + * @throws InstantiationException + */ + private XYChart getXYChart(String type) throws IllegalAccessException, InstantiationException { + XYChart chart = null; + int length = xyChartTypes.length; + for (int i = 0; i < length && chart == null; i++) { + XYChart newChart = (XYChart) xyChartTypes[i].newInstance(); + if (type.equals(newChart.getChartType())) { + chart = newChart; + } + } + return chart; + } + + /** + * The graphical representation of a series. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesRenderer the series renderer + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + public void drawSeries(Canvas canvas, Paint paint, float[] points, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) { + mCharts[seriesIndex].setScreenR(getScreenR()); + mCharts[seriesIndex].setCalcRange(getCalcRange(mDataset.getSeriesAt(seriesIndex) + .getScaleNumber()), 0); + mCharts[seriesIndex].drawSeries(canvas, paint, points, seriesRenderer, yAxisValue, 0, + startIndex); + } + + @Override + protected ClickableArea[] clickableAreasForPoints(float[] points, double[] values, + float yAxisValue, int seriesIndex, int startIndex) { + return mCharts[seriesIndex].clickableAreasForPoints(points, values, yAxisValue, 0, startIndex); + } + + @Override + protected void drawSeries(XYSeries series, Canvas canvas, Paint paint, List pointsList, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, Orientation or, + int startIndex) { + mCharts[seriesIndex].setScreenR(getScreenR()); + mCharts[seriesIndex].setCalcRange(getCalcRange(mDataset.getSeriesAt(seriesIndex) + .getScaleNumber()), 0); + mCharts[seriesIndex].drawSeries(series, canvas, paint, pointsList, seriesRenderer, yAxisValue, + 0, or, startIndex); + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public int getLegendShapeWidth(int seriesIndex) { + return mCharts[seriesIndex].getLegendShapeWidth(0); + } + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, + int seriesIndex, Paint paint) { + mCharts[seriesIndex].drawLegendShape(canvas, renderer, x, y, 0, paint); + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return "Combined"; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/CubicLineChart.java b/android-libraries/achartengine/src/org/achartengine/chart/CubicLineChart.java new file mode 100644 index 00000000..2011318f --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/CubicLineChart.java @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.Point; +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.renderer.XYMultipleSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; + +/** + * The interpolated (cubic) line chart rendering class. + */ +public class CubicLineChart extends LineChart { + /** The chart type. */ + public static final String TYPE = "Cubic"; + + private float firstMultiplier; + + private float secondMultiplier; + + private Point p1 = new Point(); + + private Point p2 = new Point(); + + private Point p3 = new Point(); + + public CubicLineChart() { + // default is to have first control point at about 33% of the distance, + firstMultiplier = 0.33f; + // and the next at 66% of the distance. + secondMultiplier = 1 - firstMultiplier; + } + + /** + * Builds a cubic line chart. + * + * @param dataset the dataset + * @param renderer the renderer + * @param smoothness smoothness determines how smooth the curve should be, + * range [0->0.5] super smooth, 0.5, means that it might not get + * close to control points if you have random data // less smooth, + * (close to 0) means that it will most likely touch all control // + * points + */ + public CubicLineChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, + float smoothness) { + super(dataset, renderer); + firstMultiplier = smoothness; + secondMultiplier = 1 - firstMultiplier; + } + + @Override + protected void drawPath(Canvas canvas, float[] points, Paint paint, boolean circular) { + Path p = new Path(); + float x = points[0]; + float y = points[1]; + p.moveTo(x, y); + + int length = points.length; + if (circular) { + length -= 4; + } + + for (int i = 0; i < length; i += 2) { + int nextIndex = i + 2 < length ? i + 2 : i; + int nextNextIndex = i + 4 < length ? i + 4 : nextIndex; + calc(points, p1, i, nextIndex, secondMultiplier); + p2.setX(points[nextIndex]); + p2.setY(points[nextIndex + 1]); + calc(points, p3, nextIndex, nextNextIndex, firstMultiplier); + // From last point, approaching x1/y1 and x2/y2 and ends up at x3/y3 + p.cubicTo(p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY()); + } + if (circular) { + for (int i = length; i < length + 4; i += 2) { + p.lineTo(points[i], points[i + 1]); + } + p.lineTo(points[0], points[1]); + } + canvas.drawPath(p, paint); + } + + private void calc(float[] points, Point result, int index1, int index2, final float multiplier) { + float p1x = points[index1]; + float p1y = points[index1 + 1]; + float p2x = points[index2]; + float p2y = points[index2 + 1]; + + float diffX = p2x - p1x; // p2.x - p1.x; + float diffY = p2y - p1y; // p2.y - p1.y; + result.setX(p1x + (diffX * multiplier)); + result.setY(p1y + (diffY * multiplier)); + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return TYPE; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/DialChart.java b/android-libraries/achartengine/src/org/achartengine/chart/DialChart.java new file mode 100644 index 00000000..ebfcbbb1 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/DialChart.java @@ -0,0 +1,236 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.CategorySeries; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.DialRenderer; +import org.achartengine.renderer.DialRenderer.Type; +import org.achartengine.util.MathHelper; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; + +/** + * The dial chart rendering class. + */ +public class DialChart extends RoundChart { + /** The radius of the needle. */ + private static final int NEEDLE_RADIUS = 10; + /** The series renderer. */ + private DialRenderer mRenderer; + + /** + * Builds a new dial chart instance. + * + * @param dataset the series dataset + * @param renderer the dial renderer + */ + public DialChart(CategorySeries dataset, DialRenderer renderer) { + super(dataset, renderer); + mRenderer = renderer; + } + + /** + * The graphical representation of the dial chart. + * + * @param canvas the canvas to paint to + * @param x the top left x value of the view to draw to + * @param y the top left y value of the view to draw to + * @param width the width of the view to draw to + * @param height the height of the view to draw to + * @param paint the paint + */ + @Override + public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) { + paint.setAntiAlias(mRenderer.isAntialiasing()); + paint.setStyle(Style.FILL); + paint.setTextSize(mRenderer.getLabelsTextSize()); + int legendSize = getLegendSize(mRenderer, height / 5, 0); + int left = x; + int top = y; + int right = x + width; + + int sLength = mDataset.getItemCount(); + String[] titles = new String[sLength]; + for (int i = 0; i < sLength; i++) { + titles[i] = mDataset.getCategory(i); + } + + if (mRenderer.isFitLegend()) { + legendSize = drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, + paint, true); + } + int bottom = y + height - legendSize; + drawBackground(mRenderer, canvas, x, y, width, height, paint, false, DefaultRenderer.NO_COLOR); + + int mRadius = Math.min(Math.abs(right - left), Math.abs(bottom - top)); + int radius = (int) (mRadius * 0.35 * mRenderer.getScale()); + if (mCenterX == NO_VALUE) { + mCenterX = (left + right) / 2; + } + if (mCenterY == NO_VALUE) { + mCenterY = (bottom + top) / 2; + } + float shortRadius = radius * 0.9f; + float longRadius = radius * 1.1f; + double min = mRenderer.getMinValue(); + double max = mRenderer.getMaxValue(); + double angleMin = mRenderer.getAngleMin(); + double angleMax = mRenderer.getAngleMax(); + if (!mRenderer.isMinValueSet() || !mRenderer.isMaxValueSet()) { + int count = mRenderer.getSeriesRendererCount(); + for (int i = 0; i < count; i++) { + double value = mDataset.getValue(i); + if (!mRenderer.isMinValueSet()) { + min = Math.min(min, value); + } + if (!mRenderer.isMaxValueSet()) { + max = Math.max(max, value); + } + } + } + if (min == max) { + min = min * 0.5; + max = max * 1.5; + } + + paint.setColor(mRenderer.getLabelsColor()); + double minorTicks = mRenderer.getMinorTicksSpacing(); + double majorTicks = mRenderer.getMajorTicksSpacing(); + if (minorTicks == MathHelper.NULL_VALUE) { + minorTicks = (max - min) / 30; + } + if (majorTicks == MathHelper.NULL_VALUE) { + majorTicks = (max - min) / 10; + } + drawTicks(canvas, min, max, angleMin, angleMax, mCenterX, mCenterY, longRadius, radius, + minorTicks, paint, false); + drawTicks(canvas, min, max, angleMin, angleMax, mCenterX, mCenterY, longRadius, shortRadius, + majorTicks, paint, true); + + int count = mRenderer.getSeriesRendererCount(); + for (int i = 0; i < count; i++) { + double angle = getAngleForValue(mDataset.getValue(i), angleMin, angleMax, min, max); + paint.setColor(mRenderer.getSeriesRendererAt(i).getColor()); + boolean type = mRenderer.getVisualTypeForIndex(i) == Type.ARROW; + drawNeedle(canvas, angle, mCenterX, mCenterY, shortRadius, type, paint); + } + drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, paint, false); + drawTitle(canvas, x, y, width, paint); + } + + /** + * Returns the angle for a specific chart value. + * + * @param value the chart value + * @param minAngle the minimum chart angle value + * @param maxAngle the maximum chart angle value + * @param min the minimum chart value + * @param max the maximum chart value + * @return the angle + */ + private double getAngleForValue(double value, double minAngle, double maxAngle, double min, + double max) { + double angleDiff = maxAngle - minAngle; + double diff = max - min; + return Math.toRadians(minAngle + (value - min) * angleDiff / diff); + } + + /** + * Draws the chart tick lines. + * + * @param canvas the canvas + * @param min the minimum chart value + * @param max the maximum chart value + * @param minAngle the minimum chart angle value + * @param maxAngle the maximum chart angle value + * @param centerX the center x value + * @param centerY the center y value + * @param longRadius the long radius + * @param shortRadius the short radius + * @param ticks the tick spacing + * @param paint the paint settings + * @param labels paint the labels + * @return the angle + */ + private void drawTicks(Canvas canvas, double min, double max, double minAngle, double maxAngle, + int centerX, int centerY, double longRadius, double shortRadius, double ticks, Paint paint, + boolean labels) { + for (double i = min; i <= max; i += ticks) { + double angle = getAngleForValue(i, minAngle, maxAngle, min, max); + double sinValue = Math.sin(angle); + double cosValue = Math.cos(angle); + int x1 = Math.round(centerX + (float) (shortRadius * sinValue)); + int y1 = Math.round(centerY + (float) (shortRadius * cosValue)); + int x2 = Math.round(centerX + (float) (longRadius * sinValue)); + int y2 = Math.round(centerY + (float) (longRadius * cosValue)); + canvas.drawLine(x1, y1, x2, y2, paint); + if (labels) { + paint.setTextAlign(Align.LEFT); + if (x1 <= x2) { + paint.setTextAlign(Align.RIGHT); + } + String text = i + ""; + if (Math.round(i) == (long) i) { + text = (long) i + ""; + } + canvas.drawText(text, x1, y1, paint); + } + } + } + + /** + * Returns the angle for a specific chart value. + * + * @param canvas the canvas + * @param angle the needle angle value + * @param centerX the center x value + * @param centerY the center y value + * @param radius the radius + * @param arrow if a needle or an arrow to be painted + * @param paint the paint settings + * @return the angle + */ + private void drawNeedle(Canvas canvas, double angle, int centerX, int centerY, double radius, + boolean arrow, Paint paint) { + double diff = Math.toRadians(90); + int needleSinValue = (int) (NEEDLE_RADIUS * Math.sin(angle - diff)); + int needleCosValue = (int) (NEEDLE_RADIUS * Math.cos(angle - diff)); + int needleX = (int) (radius * Math.sin(angle)); + int needleY = (int) (radius * Math.cos(angle)); + int needleCenterX = centerX + needleX; + int needleCenterY = centerY + needleY; + float[] points; + if (arrow) { + int arrowBaseX = centerX + (int) (radius * 0.85 * Math.sin(angle)); + int arrowBaseY = centerY + (int) (radius * 0.85 * Math.cos(angle)); + points = new float[] { arrowBaseX - needleSinValue, arrowBaseY - needleCosValue, + needleCenterX, needleCenterY, arrowBaseX + needleSinValue, arrowBaseY + needleCosValue }; + float width = paint.getStrokeWidth(); + paint.setStrokeWidth(5); + canvas.drawLine(centerX, centerY, needleCenterX, needleCenterY, paint); + paint.setStrokeWidth(width); + } else { + points = new float[] { centerX - needleSinValue, centerY - needleCosValue, needleCenterX, + needleCenterY, centerX + needleSinValue, centerY + needleCosValue }; + } + drawPath(canvas, points, paint, true); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/DoughnutChart.java b/android-libraries/achartengine/src/org/achartengine/chart/DoughnutChart.java new file mode 100644 index 00000000..ad67b076 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/DoughnutChart.java @@ -0,0 +1,162 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.util.ArrayList; +import java.util.List; + +import org.achartengine.model.MultipleCategorySeries; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.SimpleSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; + +/** + * The doughnut chart rendering class. + */ +public class DoughnutChart extends RoundChart { + /** The series dataset. */ + private MultipleCategorySeries mDataset; + /** A step variable to control the size of the legend shape. */ + private int mStep; + + /** + * Builds a new doughnut chart instance. + * + * @param dataset the series dataset + * @param renderer the series renderer + */ + public DoughnutChart(MultipleCategorySeries dataset, DefaultRenderer renderer) { + super(null, renderer); + mDataset = dataset; + } + + /** + * The graphical representation of the doughnut chart. + * + * @param canvas the canvas to paint to + * @param x the top left x value of the view to draw to + * @param y the top left y value of the view to draw to + * @param width the width of the view to draw to + * @param height the height of the view to draw to + * @param paint the paint + */ + @Override + public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) { + paint.setAntiAlias(mRenderer.isAntialiasing()); + paint.setStyle(Style.FILL); + paint.setTextSize(mRenderer.getLabelsTextSize()); + int legendSize = getLegendSize(mRenderer, height / 5, 0); + int left = x; + int top = y; + int right = x + width; + int cLength = mDataset.getCategoriesCount(); + String[] categories = new String[cLength]; + for (int category = 0; category < cLength; category++) { + categories[category] = mDataset.getCategory(category); + } + if (mRenderer.isFitLegend()) { + legendSize = drawLegend(canvas, mRenderer, categories, left, right, y, width, height, + legendSize, paint, true); + } + + int bottom = y + height - legendSize; + drawBackground(mRenderer, canvas, x, y, width, height, paint, false, DefaultRenderer.NO_COLOR); + mStep = SHAPE_WIDTH * 3 / 4; + + int mRadius = Math.min(Math.abs(right - left), Math.abs(bottom - top)); + double rCoef = 0.35 * mRenderer.getScale(); + double decCoef = 0.2 / cLength; + int radius = (int) (mRadius * rCoef); + if (mCenterX == NO_VALUE) { + mCenterX = (left + right) / 2; + } + if (mCenterY == NO_VALUE) { + mCenterY = (bottom + top) / 2; + } + float shortRadius = radius * 0.9f; + float longRadius = radius * 1.1f; + List prevLabelsBounds = new ArrayList(); + for (int category = 0; category < cLength; category++) { + int sLength = mDataset.getItemCount(category); + double total = 0; + String[] titles = new String[sLength]; + for (int i = 0; i < sLength; i++) { + total += mDataset.getValues(category)[i]; + titles[i] = mDataset.getTitles(category)[i]; + } + float currentAngle = mRenderer.getStartAngle(); + RectF oval = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + + radius); + for (int i = 0; i < sLength; i++) { + paint.setColor(mRenderer.getSeriesRendererAt(i).getColor()); + float value = (float) mDataset.getValues(category)[i]; + float angle = (float) (value / total * 360); + canvas.drawArc(oval, currentAngle, angle, true, paint); + drawLabel(canvas, mDataset.getTitles(category)[i], mRenderer, prevLabelsBounds, mCenterX, + mCenterY, shortRadius, longRadius, currentAngle, angle, left, right, + mRenderer.getLabelsColor(), paint, true); + currentAngle += angle; + } + radius -= (int) mRadius * decCoef; + shortRadius -= mRadius * decCoef - 2; + if (mRenderer.getBackgroundColor() != 0) { + paint.setColor(mRenderer.getBackgroundColor()); + } else { + paint.setColor(Color.WHITE); + } + paint.setStyle(Style.FILL); + oval = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + radius); + canvas.drawArc(oval, 0, 360, true, paint); + radius -= 1; + } + prevLabelsBounds.clear(); + drawLegend(canvas, mRenderer, categories, left, right, y, width, height, legendSize, paint, + false); + drawTitle(canvas, x, y, width, paint); + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public int getLegendShapeWidth(int seriesIndex) { + return SHAPE_WIDTH; + } + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, + int seriesIndex, Paint paint) { + mStep--; + canvas.drawCircle(x + SHAPE_WIDTH - mStep, y, mStep, paint); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/LineChart.java b/android-libraries/achartengine/src/org/achartengine/chart/LineChart.java new file mode 100644 index 00000000..2c458986 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/LineChart.java @@ -0,0 +1,175 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; + +/** + * The line chart rendering class. + */ +public class LineChart extends XYChart { + /** The constant to identify this chart type. */ + public static final String TYPE = "Line"; + /** The legend shape width. */ + private static final int SHAPE_WIDTH = 30; + /** The scatter chart to be used to draw the data points. */ + private ScatterChart pointsChart; + + LineChart() { + } + + /** + * Builds a new line chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + */ + public LineChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + super(dataset, renderer); + pointsChart = new ScatterChart(dataset, renderer); + } + + /** + * Sets the series and the renderer. + * + * @param dataset the series dataset + * @param renderer the series renderer + */ + protected void setDatasetRenderer(XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer) { + super.setDatasetRenderer(dataset, renderer); + pointsChart = new ScatterChart(dataset, renderer); + } + + /** + * The graphical representation of a series. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesRenderer the series renderer + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + public void drawSeries(Canvas canvas, Paint paint, float[] points, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) { + int length = points.length; + XYSeriesRenderer renderer = (XYSeriesRenderer) seriesRenderer; + float lineWidth = paint.getStrokeWidth(); + paint.setStrokeWidth(renderer.getLineWidth()); + if (renderer.isFillBelowLine()) { + paint.setColor(renderer.getFillBelowLineColor()); + int pLength = points.length; + float[] fillPoints = new float[pLength + 4]; + System.arraycopy(points, 0, fillPoints, 0, length); + fillPoints[0] = points[0] + 1; + fillPoints[length] = fillPoints[length - 2]; + fillPoints[length + 1] = yAxisValue; + fillPoints[length + 2] = fillPoints[0]; + fillPoints[length + 3] = fillPoints[length + 1]; + for (int i = 0; i < length + 4; i += 2) { + if (fillPoints[i + 1] < 0) { + fillPoints[i + 1] = 0; + } + } + paint.setStyle(Style.FILL); + drawPath(canvas, fillPoints, paint, true); + } + paint.setColor(seriesRenderer.getColor()); + paint.setStyle(Style.STROKE); + drawPath(canvas, points, paint, false); + paint.setStrokeWidth(lineWidth); + } + + @Override + protected ClickableArea[] clickableAreasForPoints(float[] points, double[] values, + float yAxisValue, int seriesIndex, int startIndex) { + int length = points.length; + ClickableArea[] ret = new ClickableArea[length / 2]; + for (int i = 0; i < length; i += 2) { + int selectableBuffer = mRenderer.getSelectableBuffer(); + ret[i / 2] = new ClickableArea(new RectF(points[i] - selectableBuffer, points[i + 1] + - selectableBuffer, points[i] + selectableBuffer, points[i + 1] + selectableBuffer), + values[i], values[i + 1]); + } + return ret; + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public int getLegendShapeWidth(int seriesIndex) { + return SHAPE_WIDTH; + } + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, + int seriesIndex, Paint paint) { + canvas.drawLine(x, y, x + SHAPE_WIDTH, y, paint); + if (isRenderPoints(renderer)) { + pointsChart.drawLegendShape(canvas, renderer, x + 5, y, seriesIndex, paint); + } + } + + /** + * Returns if the chart should display the points as a certain shape. + * + * @param renderer the series renderer + */ + public boolean isRenderPoints(SimpleSeriesRenderer renderer) { + return ((XYSeriesRenderer) renderer).getPointStyle() != PointStyle.POINT; + } + + /** + * Returns the scatter chart to be used for drawing the data points. + * + * @return the data points scatter chart + */ + public ScatterChart getPointsChart() { + return pointsChart; + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return TYPE; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/PieChart.java b/android-libraries/achartengine/src/org/achartengine/chart/PieChart.java new file mode 100644 index 00000000..d656a95e --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/PieChart.java @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.util.ArrayList; +import java.util.List; + +import org.achartengine.model.CategorySeries; +import org.achartengine.model.Point; +import org.achartengine.model.SeriesSelection; +import org.achartengine.renderer.DefaultRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; + +/** + * The pie chart rendering class. + */ +public class PieChart extends RoundChart { + /** Handles returning values when tapping on PieChart. */ + private PieMapper mPieMapper; + + /** + * Builds a new pie chart instance. + * + * @param dataset the series dataset + * @param renderer the series renderer + */ + public PieChart(CategorySeries dataset, DefaultRenderer renderer) { + super(dataset, renderer); + mPieMapper = new PieMapper(); + } + + /** + * The graphical representation of the pie chart. + * + * @param canvas the canvas to paint to + * @param x the top left x value of the view to draw to + * @param y the top left y value of the view to draw to + * @param width the width of the view to draw to + * @param height the height of the view to draw to + * @param paint the paint + */ + @Override + public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) { + paint.setAntiAlias(mRenderer.isAntialiasing()); + paint.setStyle(Style.FILL); + paint.setTextSize(mRenderer.getLabelsTextSize()); + int legendSize = getLegendSize(mRenderer, height / 5, 0); + int left = x; + int top = y; + int right = x + width; + int sLength = mDataset.getItemCount(); + double total = 0; + String[] titles = new String[sLength]; + for (int i = 0; i < sLength; i++) { + total += mDataset.getValue(i); + titles[i] = mDataset.getCategory(i); + } + if (mRenderer.isFitLegend()) { + legendSize = drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, + paint, true); + } + int bottom = y + height - legendSize; + drawBackground(mRenderer, canvas, x, y, width, height, paint, false, DefaultRenderer.NO_COLOR); + + float currentAngle = mRenderer.getStartAngle(); + int mRadius = Math.min(Math.abs(right - left), Math.abs(bottom - top)); + int radius = (int) (mRadius * 0.35 * mRenderer.getScale()); + + if (mCenterX == NO_VALUE) { + mCenterX = (left + right) / 2; + } + if (mCenterY == NO_VALUE) { + mCenterY = (bottom + top) / 2; + } + + // Hook in clip detection after center has been calculated + mPieMapper.setDimensions(radius, mCenterX, mCenterY); + boolean loadPieCfg = !mPieMapper.areAllSegmentPresent(sLength); + if (loadPieCfg) { + mPieMapper.clearPieSegments(); + } + + float shortRadius = radius * 0.9f; + float longRadius = radius * 1.1f; + + RectF oval = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + + radius); + List prevLabelsBounds = new ArrayList(); + + for (int i = 0; i < sLength; i++) { + paint.setColor(mRenderer.getSeriesRendererAt(i).getColor()); + float value = (float) mDataset.getValue(i); + float angle = (float) (value / total * 360); + canvas.drawArc(oval, currentAngle, angle, true, paint); + drawLabel(canvas, mDataset.getCategory(i), mRenderer, prevLabelsBounds, mCenterX, mCenterY, + shortRadius, longRadius, currentAngle, angle, left, right, mRenderer.getLabelsColor(), + paint, true); + if (mRenderer.isDisplayValues()) { + drawLabel(canvas, getLabel(mDataset.getValue(i)), mRenderer, prevLabelsBounds, mCenterX, + mCenterY, shortRadius / 2, longRadius / 2, currentAngle, angle, left, right, + mRenderer.getLabelsColor(), paint, false); + } + + // Save details for getSeries functionality + if (loadPieCfg) { + mPieMapper.addPieSegment(i, value, currentAngle, angle); + } + currentAngle += angle; + } + prevLabelsBounds.clear(); + drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, paint, false); + drawTitle(canvas, x, y, width, paint); + } + + public SeriesSelection getSeriesAndPointForScreenCoordinate(Point screenPoint) { + return mPieMapper.getSeriesAndPointForScreenCoordinate(screenPoint); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/PieMapper.java b/android-libraries/achartengine/src/org/achartengine/chart/PieMapper.java new file mode 100644 index 00000000..6e4b7150 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/PieMapper.java @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.achartengine.model.Point; +import org.achartengine.model.SeriesSelection; + +/** + * PieChart Segment Selection Management. + */ +public class PieMapper implements Serializable { + + private List mPieSegmentList = new ArrayList(); + + private int mPieChartRadius; + + private int mCenterX, mCenterY; + + /** + * Set PieChart location on screen. + * + * @param pieRadius + * @param centerX + * @param centerY + */ + public void setDimensions(int pieRadius, int centerX, int centerY) { + mPieChartRadius = pieRadius; + mCenterX = centerX; + mCenterY = centerY; + } + + /** + * If we have all PieChart Config then there is no point in reloading it + * + * @param datasetSize + * @return true if cfg for each segment is present + */ + public boolean areAllSegmentPresent(int datasetSize) { + return mPieSegmentList.size() == datasetSize; + } + + /** + * Add configuration for a PieChart Segment + * + * @param dataIndex + * @param value + * @param startAngle + * @param angle + */ + public void addPieSegment(int dataIndex, float value, float startAngle, float angle) { + mPieSegmentList.add(new PieSegment(dataIndex, value, startAngle, angle)); + } + + /** + * Clears the pie segments list. + */ + public void clearPieSegments() { + mPieSegmentList.clear(); + } + + /** + * Fetches angle relative to pie chart center point where 3 O'Clock is 0 and + * 12 O'Clock is 270degrees + * + * @param screenPoint + * @return angle in degress from 0-360. + */ + public double getAngle(Point screenPoint) { + double dx = screenPoint.getX() - mCenterX; + // Minus to correct for coord re-mapping + double dy = -(screenPoint.getY() - mCenterY); + + double inRads = Math.atan2(dy, dx); + + // We need to map to coord system when 0 degree is at 3 O'clock, 270 at 12 + // O'clock + if (inRads < 0) + inRads = Math.abs(inRads); + else + inRads = 2 * Math.PI - inRads; + + return Math.toDegrees(inRads); + } + + /** + * Checks if Point falls within PieChart + * + * @param screenPoint + * @return true if in PieChart + */ + public boolean isOnPieChart(Point screenPoint) { + // Using a bit of Pythagoras + // inside circle if (x-center_x)**2 + (y-center_y)**2 <= radius**2: + + double sqValue = (Math.pow(mCenterX - screenPoint.getX(), 2) + Math.pow( + mCenterY - screenPoint.getY(), 2)); + + double radiusSquared = mPieChartRadius * mPieChartRadius; + boolean isOnPieChart = sqValue <= radiusSquared; + return isOnPieChart; + } + + /** + * Fetches the SeriesSelection for the PieSegment selected. + * + * @param screenPoint - the user tap location + * @return null if screen point is not in PieChart or its config if it is + */ + public SeriesSelection getSeriesAndPointForScreenCoordinate(Point screenPoint) { + if (isOnPieChart(screenPoint)) { + double angleFromPieCenter = getAngle(screenPoint); + + for (PieSegment pieSeg : mPieSegmentList) { + if (pieSeg.isInSegment(angleFromPieCenter)) { + return new SeriesSelection(0, pieSeg.getDataIndex(), pieSeg.getValue(), + pieSeg.getValue()); + } + } + } + return null; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/PieSegment.java b/android-libraries/achartengine/src/org/achartengine/chart/PieSegment.java new file mode 100644 index 00000000..0fb0a2e4 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/PieSegment.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.io.Serializable; + +/** + * Holds An PieChart Segment + */ +public class PieSegment implements Serializable { + private float mStartAngle; + + private float mEndAngle; + + private int mDataIndex; + + private float mValue; + + public PieSegment(int dataIndex, float value, float startAngle, float angle) { + mStartAngle = startAngle; + mEndAngle = angle + startAngle; + mDataIndex = dataIndex; + mValue = value; + } + + /** + * Checks if angle falls in segment. + * + * @param angle + * @return true if in segment, false otherwise. + */ + public boolean isInSegment(double angle) { + return angle >= mStartAngle && angle <= mEndAngle; + } + + protected float getStartAngle() { + return mStartAngle; + } + + protected float getEndAngle() { + return mEndAngle; + } + + protected int getDataIndex() { + return mDataIndex; + } + + protected float getValue() { + return mValue; + } + + public String toString() { + return "mDataIndex=" + mDataIndex + ",mValue=" + mValue + ",mStartAngle=" + mStartAngle + + ",mEndAngle=" + mEndAngle; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/PointStyle.java b/android-libraries/achartengine/src/org/achartengine/chart/PointStyle.java new file mode 100644 index 00000000..29a2311a --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/PointStyle.java @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +/** + * The chart point style enumerator. + */ +public enum PointStyle { + X("x"), CIRCLE("circle"), TRIANGLE("triangle"), SQUARE("square"), DIAMOND("diamond"), POINT( + "point"); + + /** The point shape name. */ + private String mName; + + /** + * The point style enum constructor. + * + * @param name the name + */ + private PointStyle(String name) { + mName = name; + } + + /** + * Returns the point shape name. + * + * @return the point shape name + */ + public String getName() { + return mName; + } + + /** + * Returns the point shape name. + * + * @return the point shape name + */ + public String toString() { + return getName(); + } + + /** + * Return the point shape that has the provided symbol. + * + * @param name the point style name + * @return the point shape + */ + public static PointStyle getPointStyleForName(String name) { + PointStyle pointStyle = null; + PointStyle[] styles = values(); + int length = styles.length; + for (int i = 0; i < length && pointStyle == null; i++) { + if (styles[i].mName.equals(name)) { + pointStyle = styles[i]; + } + } + return pointStyle; + } + + /** + * Returns the point shape index based on the given name. + * + * @return the point shape index + */ + public static int getIndexForName(String name) { + int index = -1; + PointStyle[] styles = values(); + int length = styles.length; + for (int i = 0; i < length && index < 0; i++) { + if (styles[i].mName.equals(name)) { + index = i; + } + } + return Math.max(0, index); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/RangeBarChart.java b/android-libraries/achartengine/src/org/achartengine/chart/RangeBarChart.java new file mode 100644 index 00000000..105f509a --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/RangeBarChart.java @@ -0,0 +1,145 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; + +/** + * The range bar chart rendering class. + */ +public class RangeBarChart extends BarChart { + /** The chart type. */ + public static final String TYPE = "RangeBar"; + + RangeBarChart() { + } + + RangeBarChart(Type type) { + super(type); + } + + /** + * Builds a new range bar chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + * @param type the range bar chart type + */ + public RangeBarChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, Type type) { + super(dataset, renderer, type); + } + + /** + * The graphical representation of a series. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesRenderer the series renderer + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + public void drawSeries(Canvas canvas, Paint paint, float[] points, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) { + int seriesNr = mDataset.getSeriesCount(); + int length = points.length; + paint.setColor(seriesRenderer.getColor()); + paint.setStyle(Style.FILL); + float halfDiffX = getHalfDiffX(points, length, seriesNr); + int start = 0; + if (startIndex > 0) { + start = 2; + } + for (int i = start; i < length; i += 4) { + if (points.length > i + 3) { + float xMin = points[i]; + float yMin = points[i + 1]; + // xMin = xMax + float xMax = points[i + 2]; + float yMax = points[i + 3]; + drawBar(canvas, xMin, yMin, xMax, yMax, halfDiffX, seriesNr, seriesIndex, paint); + } + } + paint.setColor(seriesRenderer.getColor()); + } + + /** + * The graphical representation of the series values as text. + * + * @param canvas the canvas to paint to + * @param series the series to be painted + * @param renderer the series renderer + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + protected void drawChartValuesText(Canvas canvas, XYSeries series, SimpleSeriesRenderer renderer, + Paint paint, float[] points, int seriesIndex, int startIndex) { + int seriesNr = mDataset.getSeriesCount(); + float halfDiffX = getHalfDiffX(points, points.length, seriesNr); + int start = 0; + if (startIndex > 0) { + start = 2; + } + for (int i = start; i < points.length; i += 4) { + int index = startIndex + i / 2; + float x = points[i]; + if (mType == Type.DEFAULT) { + x += seriesIndex * 2 * halfDiffX - (seriesNr - 1.5f) * halfDiffX; + } + + if (!isNullValue(series.getY(index + 1)) && points.length > i + 3) { + // draw the maximum value + drawText(canvas, getLabel(series.getY(index + 1)), x, + points[i + 3] - renderer.getChartValuesSpacing(), paint, 0); + } + if (!isNullValue(series.getY(index)) && points.length > i + 1) { + // draw the minimum value + drawText(canvas, getLabel(series.getY(index)), x, + points[i + 1] + renderer.getChartValuesTextSize() + renderer.getChartValuesSpacing() + - 3, paint, 0); + } + } + } + + /** + * Returns the value of a constant used to calculate the half-distance. + * + * @return the constant value + */ + protected float getCoeficient() { + return 0.5f; + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return TYPE; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/RangeStackedBarChart.java b/android-libraries/achartengine/src/org/achartengine/chart/RangeStackedBarChart.java new file mode 100644 index 00000000..4da4f006 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/RangeStackedBarChart.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +public class RangeStackedBarChart extends RangeBarChart { + /** The chart type. */ + public static final String TYPE = "RangeStackedBar"; + + RangeStackedBarChart() { + super(Type.STACKED); + } + + public String getChartType() { + return TYPE; + } +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/chart/RoundChart.java b/android-libraries/achartengine/src/org/achartengine/chart/RoundChart.java new file mode 100644 index 00000000..1a2121d4 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/RoundChart.java @@ -0,0 +1,143 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.CategorySeries; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.SimpleSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; + +/** + * An abstract class to be extended by round like chart rendering classes. + */ +public abstract class RoundChart extends AbstractChart { + /** The legend shape width. */ + protected static final int SHAPE_WIDTH = 10; + /** The series dataset. */ + protected CategorySeries mDataset; + /** The series renderer. */ + protected DefaultRenderer mRenderer; + /** A no value constant. */ + protected static final int NO_VALUE = Integer.MAX_VALUE; + /** The chart center X axis. */ + protected int mCenterX = NO_VALUE; + /** The chart center y axis. */ + protected int mCenterY = NO_VALUE; + + /** + * Round chart. + * + * @param dataset the series dataset + * @param renderer the series renderer + */ + public RoundChart(CategorySeries dataset, DefaultRenderer renderer) { + mDataset = dataset; + mRenderer = renderer; + } + + /** + * The graphical representation of the round chart title. + * + * @param canvas the canvas to paint to + * @param x the top left x value of the view to draw to + * @param y the top left y value of the view to draw to + * @param width the width of the view to draw to + * @param paint the paint + */ + public void drawTitle(Canvas canvas, int x, int y, int width, Paint paint) { + if (mRenderer.isShowLabels()) { + paint.setColor(mRenderer.getLabelsColor()); + paint.setTextAlign(Align.CENTER); + paint.setTextSize(mRenderer.getChartTitleTextSize()); + drawString(canvas, mRenderer.getChartTitle(), x + width / 2, + y + mRenderer.getChartTitleTextSize(), paint); + } + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public int getLegendShapeWidth(int seriesIndex) { + return SHAPE_WIDTH; + } + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, + int seriesIndex, Paint paint) { + canvas.drawRect(x, y - SHAPE_WIDTH / 2, x + SHAPE_WIDTH, y + SHAPE_WIDTH / 2, paint); + } + + /** + * Returns the renderer. + * + * @return the renderer + */ + public DefaultRenderer getRenderer() { + return mRenderer; + } + + /** + * Returns the center on X axis. + * + * @return the center on X axis + */ + public int getCenterX() { + return mCenterX; + } + + /** + * Returns the center on Y axis. + * + * @return the center on Y axis + */ + public int getCenterY() { + return mCenterY; + } + + /** + * Sets a new center on X axis. + * + * @param centerX center on X axis + */ + public void setCenterX(int centerX) { + mCenterX = centerX; + } + + /** + * Sets a new center on Y axis. + * + * @param centerY center on Y axis + */ + public void setCenterY(int centerY) { + mCenterY = centerY; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/ScatterChart.java b/android-libraries/achartengine/src/org/achartengine/chart/ScatterChart.java new file mode 100644 index 00000000..1d3b2a18 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/ScatterChart.java @@ -0,0 +1,266 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; + +/** + * The scatter chart rendering class. + */ +public class ScatterChart extends XYChart { + /** The constant to identify this chart type. */ + public static final String TYPE = "Scatter"; + /** The default point shape size. */ + private static final float SIZE = 3; + /** The legend shape width. */ + private static final int SHAPE_WIDTH = 10; + /** The point shape size. */ + private float size = SIZE; + + ScatterChart() { + } + + /** + * Builds a new scatter chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + */ + public ScatterChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + super(dataset, renderer); + size = renderer.getPointSize(); + } + + // TODO: javadoc + protected void setDatasetRenderer(XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer) { + super.setDatasetRenderer(dataset, renderer); + size = renderer.getPointSize(); + } + + /** + * The graphical representation of a series. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesRenderer the series renderer + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + public void drawSeries(Canvas canvas, Paint paint, float[] points, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) { + XYSeriesRenderer renderer = (XYSeriesRenderer) seriesRenderer; + paint.setColor(renderer.getColor()); + if (renderer.isFillPoints()) { + paint.setStyle(Style.FILL); + } else { + paint.setStyle(Style.STROKE); + } + int length = points.length; + switch (renderer.getPointStyle()) { + case X: + for (int i = 0; i < length; i += 2) { + drawX(canvas, paint, points[i], points[i + 1]); + } + break; + case CIRCLE: + for (int i = 0; i < length; i += 2) { + drawCircle(canvas, paint, points[i], points[i + 1]); + } + break; + case TRIANGLE: + float[] path = new float[6]; + for (int i = 0; i < length; i += 2) { + drawTriangle(canvas, paint, path, points[i], points[i + 1]); + } + break; + case SQUARE: + for (int i = 0; i < length; i += 2) { + drawSquare(canvas, paint, points[i], points[i + 1]); + } + break; + case DIAMOND: + path = new float[8]; + for (int i = 0; i < length; i += 2) { + drawDiamond(canvas, paint, path, points[i], points[i + 1]); + } + break; + case POINT: + canvas.drawPoints(points, paint); + break; + } + } + + @Override + protected ClickableArea[] clickableAreasForPoints(float[] points, double[] values, + float yAxisValue, int seriesIndex, int startIndex) { + int length = points.length; + ClickableArea[] ret = new ClickableArea[length / 2]; + for (int i = 0; i < length; i += 2) { + int selectableBuffer = mRenderer.getSelectableBuffer(); + ret[i / 2] = new ClickableArea(new RectF(points[i] - selectableBuffer, points[i + 1] + - selectableBuffer, points[i] + selectableBuffer, points[i + 1] + selectableBuffer), + values[i], values[i + 1]); + } + return ret; + } + + /** + * Returns the legend shape width. + * + * @param seriesIndex the series index + * @return the legend shape width + */ + public int getLegendShapeWidth(int seriesIndex) { + return SHAPE_WIDTH; + } + + /** + * The graphical representation of the legend shape. + * + * @param canvas the canvas to paint to + * @param renderer the series renderer + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + * @param seriesIndex the series index + * @param paint the paint to be used for drawing + */ + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, + int seriesIndex, Paint paint) { + if (((XYSeriesRenderer) renderer).isFillPoints()) { + paint.setStyle(Style.FILL); + } else { + paint.setStyle(Style.STROKE); + } + switch (((XYSeriesRenderer) renderer).getPointStyle()) { + case X: + drawX(canvas, paint, x + SHAPE_WIDTH, y); + break; + case CIRCLE: + drawCircle(canvas, paint, x + SHAPE_WIDTH, y); + break; + case TRIANGLE: + drawTriangle(canvas, paint, new float[6], x + SHAPE_WIDTH, y); + break; + case SQUARE: + drawSquare(canvas, paint, x + SHAPE_WIDTH, y); + break; + case DIAMOND: + drawDiamond(canvas, paint, new float[8], x + SHAPE_WIDTH, y); + break; + case POINT: + canvas.drawPoint(x + SHAPE_WIDTH, y, paint); + break; + } + } + + /** + * The graphical representation of an X point shape. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + */ + private void drawX(Canvas canvas, Paint paint, float x, float y) { + canvas.drawLine(x - size, y - size, x + size, y + size, paint); + canvas.drawLine(x + size, y - size, x - size, y + size, paint); + } + + /** + * The graphical representation of a circle point shape. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + */ + private void drawCircle(Canvas canvas, Paint paint, float x, float y) { + canvas.drawCircle(x, y, size, paint); + } + + /** + * The graphical representation of a triangle point shape. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param path the triangle path + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + */ + private void drawTriangle(Canvas canvas, Paint paint, float[] path, float x, float y) { + path[0] = x; + path[1] = y - size - size / 2; + path[2] = x - size; + path[3] = y + size; + path[4] = x + size; + path[5] = path[3]; + drawPath(canvas, path, paint, true); + } + + /** + * The graphical representation of a square point shape. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + */ + private void drawSquare(Canvas canvas, Paint paint, float x, float y) { + canvas.drawRect(x - size, y - size, x + size, y + size, paint); + } + + /** + * The graphical representation of a diamond point shape. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param path the diamond path + * @param x the x value of the point the shape should be drawn at + * @param y the y value of the point the shape should be drawn at + */ + private void drawDiamond(Canvas canvas, Paint paint, float[] path, float x, float y) { + path[0] = x; + path[1] = y - size; + path[2] = x - size; + path[3] = y; + path[4] = x; + path[5] = y + size; + path[6] = x + size; + path[7] = y; + drawPath(canvas, path, paint, true); + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return TYPE; + } + +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/chart/TimeChart.java b/android-libraries/achartengine/src/org/achartengine/chart/TimeChart.java new file mode 100644 index 00000000..ba201de7 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/TimeChart.java @@ -0,0 +1,226 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.XYMultipleSeriesRenderer; + +import android.graphics.Canvas; +import android.graphics.Paint; + +/** + * The time chart rendering class. + */ +public class TimeChart extends LineChart { + /** The constant to identify this chart type. */ + public static final String TYPE = "Time"; + /** The number of milliseconds in a day. */ + public static final long DAY = 24 * 60 * 60 * 1000; + /** The date format pattern to be used in formatting the X axis labels. */ + private String mDateFormat; + /** The starting point for labels. */ + private Double mStartPoint; + + TimeChart() { + } + + /** + * Builds a new time chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + */ + public TimeChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + super(dataset, renderer); + } + + /** + * Returns the date format pattern to be used for formatting the X axis + * labels. + * + * @return the date format pattern for the X axis labels + */ + public String getDateFormat() { + return mDateFormat; + } + + /** + * Sets the date format pattern to be used for formatting the X axis labels. + * + * @param format the date format pattern for the X axis labels. If null, an + * appropriate default format will be used. + */ + public void setDateFormat(String format) { + mDateFormat = format; + } + + /** + * The graphical representation of the labels on the X axis. + * + * @param xLabels the X labels values + * @param xTextLabelLocations the X text label locations + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param left the left value of the labels area + * @param top the top value of the labels area + * @param bottom the bottom value of the labels area + * @param xPixelsPerUnit the amount of pixels per one unit in the chart labels + * @param minX the minimum value on the X axis in the chart + * @param maxX the maximum value on the X axis in the chart + */ + @Override + protected void drawXLabels(List xLabels, Double[] xTextLabelLocations, Canvas canvas, + Paint paint, int left, int top, int bottom, double xPixelsPerUnit, double minX, double maxX) { + int length = xLabels.size(); + if (length > 0) { + boolean showLabels = mRenderer.isShowLabels(); + boolean showGridY = mRenderer.isShowGridY(); + DateFormat format = getDateFormat(xLabels.get(0), xLabels.get(length - 1)); + for (int i = 0; i < length; i++) { + long label = Math.round(xLabels.get(i)); + float xLabel = (float) (left + xPixelsPerUnit * (label - minX)); + if (showLabels) { + paint.setColor(mRenderer.getXLabelsColor()); + canvas + .drawLine(xLabel, bottom, xLabel, bottom + mRenderer.getLabelsTextSize() / 3, paint); + drawText(canvas, format.format(new Date(label)), xLabel, + bottom + mRenderer.getLabelsTextSize() * 4 / 3, paint, mRenderer.getXLabelsAngle()); + } + if (showGridY) { + paint.setColor(mRenderer.getGridColor()); + canvas.drawLine(xLabel, bottom, xLabel, top, paint); + } + } + } + drawXTextLabels(xTextLabelLocations, canvas, paint, true, left, top, bottom, xPixelsPerUnit, + minX, maxX); + } + + /** + * Returns the date format pattern to be used, based on the date range. + * + * @param start the start date in milliseconds + * @param end the end date in milliseconds + * @return the date format + */ + private DateFormat getDateFormat(double start, double end) { + if (mDateFormat != null) { + SimpleDateFormat format = null; + try { + format = new SimpleDateFormat(mDateFormat); + return format; + } catch (Exception e) { + // do nothing here + } + } + DateFormat format = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM); + double diff = end - start; + if (diff > DAY && diff < 5 * DAY) { + format = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT); + } else if (diff < DAY) { + format = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM); + } + return format; + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public String getChartType() { + return TYPE; + } + + protected List getXLabels(double min, double max, int count) { + final List result = new ArrayList(); + if (!mRenderer.isXRoundedLabels()) { + if (mDataset.getSeriesCount() > 0) { + XYSeries series = mDataset.getSeriesAt(0); + int length = series.getItemCount(); + int intervalLength = 0; + int startIndex = -1; + for (int i = 0; i < length; i++) { + double value = series.getX(i); + if (min <= value && value <= max) { + intervalLength++; + if (startIndex < 0) { + startIndex = i; + } + } + } + if (intervalLength < count) { + for (int i = startIndex; i < startIndex + intervalLength; i++) { + result.add(series.getX(i)); + } + } else { + float step = (float) intervalLength / count; + int intervalCount = 0; + for (int i = 0; i < length && intervalCount < count; i++) { + double value = series.getX(Math.round(i * step)); + if (min <= value && value <= max) { + result.add(value); + intervalCount++; + } + } + } + return result; + } else { + return super.getXLabels(min, max, count); + } + } + if (mStartPoint == null) { + mStartPoint = min - (min % DAY) + DAY + new Date(Math.round(min)).getTimezoneOffset() * 60 + * 1000; + } + if (count > 25) { + count = 25; + } + + + final double cycleMath = (max - min) / count; + if (cycleMath <= 0) { + return result; + } + double cycle = DAY; + + if (cycleMath <= DAY) { + while (cycleMath < cycle / 2) { + cycle = cycle / 2; + } + } else { + while (cycleMath > cycle) { + cycle = cycle * 2; + } + } + + double val = mStartPoint - Math.floor((mStartPoint - min) / cycle) * cycle; + int i = 0; + while (val < max && i++ <= count) { + result.add(val); + val += cycle; + } + + return result; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/XYChart.java b/android-libraries/achartengine/src/org/achartengine/chart/XYChart.java new file mode 100644 index 00000000..6b531d60 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/XYChart.java @@ -0,0 +1,905 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.chart; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; + +import org.achartengine.model.Point; +import org.achartengine.model.SeriesSelection; +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.BasicStroke; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.SimpleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYMultipleSeriesRenderer.Orientation; +import org.achartengine.util.MathHelper; + +import android.graphics.Canvas; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Cap; +import android.graphics.Paint.Join; +import android.graphics.Paint.Style; +import android.graphics.PathEffect; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Typeface; + +/** + * The XY chart rendering class. + */ +public abstract class XYChart extends AbstractChart { + /** The multiple series dataset. */ + protected XYMultipleSeriesDataset mDataset; + /** The multiple series renderer. */ + protected XYMultipleSeriesRenderer mRenderer; + /** The current scale value. */ + private float mScale; + /** The current translate value. */ + private float mTranslate; + /** The canvas center point. */ + private Point mCenter; + /** The visible chart area, in screen coordinates. */ + private Rect mScreenR; + /** The calculated range. */ + private final Map mCalcRange = new HashMap(); + + /** + * The clickable areas for all points. The array index is the series index, + * and the RectF list index is the point index in that series. + */ + private Map> clickableAreas = new HashMap>(); + + protected XYChart() { + } + + /** + * Builds a new XY chart instance. + * + * @param dataset the multiple series dataset + * @param renderer the multiple series renderer + */ + public XYChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) { + mDataset = dataset; + mRenderer = renderer; + } + + // TODO: javadoc + protected void setDatasetRenderer(XYMultipleSeriesDataset dataset, + XYMultipleSeriesRenderer renderer) { + mDataset = dataset; + mRenderer = renderer; + } + + /** + * The graphical representation of the XY chart. + * + * @param canvas the canvas to paint to + * @param x the top left x value of the view to draw to + * @param y the top left y value of the view to draw to + * @param width the width of the view to draw to + * @param height the height of the view to draw to + * @param paint the paint + */ + public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) { + paint.setAntiAlias(mRenderer.isAntialiasing()); + int legendSize = getLegendSize(mRenderer, height / 5, mRenderer.getAxisTitleTextSize()); + int[] margins = mRenderer.getMargins(); + int left = x + margins[1]; + int top = y + margins[0]; + int right = x + width - margins[3]; + int sLength = mDataset.getSeriesCount(); + String[] titles = new String[sLength]; + for (int i = 0; i < sLength; i++) { + titles[i] = mDataset.getSeriesAt(i).getTitle(); + } + if (mRenderer.isFitLegend() && mRenderer.isShowLegend()) { + legendSize = drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, + paint, true); + } + int bottom = y + height - margins[2] - legendSize; + if (mScreenR == null) { + mScreenR = new Rect(); + } + mScreenR.set(left, top, right, bottom); + drawBackground(mRenderer, canvas, x, y, width, height, paint, false, DefaultRenderer.NO_COLOR); + + if (paint.getTypeface() == null + || !paint.getTypeface().toString().equals(mRenderer.getTextTypefaceName()) + || paint.getTypeface().getStyle() != mRenderer.getTextTypefaceStyle()) { + paint.setTypeface(Typeface.create(mRenderer.getTextTypefaceName(), + mRenderer.getTextTypefaceStyle())); + } + Orientation or = mRenderer.getOrientation(); + if (or == Orientation.VERTICAL) { + right -= legendSize; + bottom += legendSize - 20; + } + int angle = or.getAngle(); + boolean rotate = angle == 90; + mScale = (float) (height) / width; + mTranslate = Math.abs(width - height) / 2; + if (mScale < 1) { + mTranslate *= -1; + } + mCenter = new Point((x + width) / 2, (y + height) / 2); + if (rotate) { + transform(canvas, angle, false); + } + + int maxScaleNumber = -Integer.MAX_VALUE; + for (int i = 0; i < sLength; i++) { + maxScaleNumber = Math.max(maxScaleNumber, mDataset.getSeriesAt(i).getScaleNumber()); + } + maxScaleNumber++; + if (maxScaleNumber < 0) { + return; + } + double[] minX = new double[maxScaleNumber]; + double[] maxX = new double[maxScaleNumber]; + double[] minY = new double[maxScaleNumber]; + double[] maxY = new double[maxScaleNumber]; + boolean[] isMinXSet = new boolean[maxScaleNumber]; + boolean[] isMaxXSet = new boolean[maxScaleNumber]; + boolean[] isMinYSet = new boolean[maxScaleNumber]; + boolean[] isMaxYSet = new boolean[maxScaleNumber]; + + for (int i = 0; i < maxScaleNumber; i++) { + minX[i] = mRenderer.getXAxisMin(i); + maxX[i] = mRenderer.getXAxisMax(i); + minY[i] = mRenderer.getYAxisMin(i); + maxY[i] = mRenderer.getYAxisMax(i); + isMinXSet[i] = mRenderer.isMinXSet(i); + isMaxXSet[i] = mRenderer.isMaxXSet(i); + isMinYSet[i] = mRenderer.isMinYSet(i); + isMaxYSet[i] = mRenderer.isMaxYSet(i); + if (mCalcRange.get(i) == null) { + mCalcRange.put(i, new double[4]); + } + } + double[] xPixelsPerUnit = new double[maxScaleNumber]; + double[] yPixelsPerUnit = new double[maxScaleNumber]; + for (int i = 0; i < sLength; i++) { + XYSeries series = mDataset.getSeriesAt(i); + int scale = series.getScaleNumber(); + if (series.getItemCount() == 0) { + continue; + } + if (!isMinXSet[scale]) { + double minimumX = series.getMinX(); + minX[scale] = Math.min(minX[scale], minimumX); + mCalcRange.get(scale)[0] = minX[scale]; + } + if (!isMaxXSet[scale]) { + double maximumX = series.getMaxX(); + maxX[scale] = Math.max(maxX[scale], maximumX); + mCalcRange.get(scale)[1] = maxX[scale]; + } + if (!isMinYSet[scale]) { + double minimumY = series.getMinY(); + minY[scale] = Math.min(minY[scale], (float) minimumY); + mCalcRange.get(scale)[2] = minY[scale]; + } + if (!isMaxYSet[scale]) { + double maximumY = series.getMaxY(); + maxY[scale] = Math.max(maxY[scale], (float) maximumY); + mCalcRange.get(scale)[3] = maxY[scale]; + } + } + for (int i = 0; i < maxScaleNumber; i++) { + if (maxX[i] - minX[i] != 0) { + xPixelsPerUnit[i] = (right - left) / (maxX[i] - minX[i]); + } + if (maxY[i] - minY[i] != 0) { + yPixelsPerUnit[i] = (float) ((bottom - top) / (maxY[i] - minY[i])); + } + } + + boolean hasValues = false; + // use a linked list for these reasons: + // 1) Avoid a large contiguous memory allocation + // 2) We don't need random seeking, only sequential reading/writing, so + // linked list makes sense + clickableAreas = new HashMap>(); + for (int i = 0; i < sLength; i++) { + XYSeries series = mDataset.getSeriesAt(i); + int scale = series.getScaleNumber(); + if (series.getItemCount() == 0) { + continue; + } + + hasValues = true; + SimpleSeriesRenderer seriesRenderer = mRenderer.getSeriesRendererAt(i); + + // int originalValuesLength = series.getItemCount(); + // int valuesLength = originalValuesLength; + // int length = valuesLength * 2; + + List points = new ArrayList(); + List values = new ArrayList(); + float yAxisValue = Math.min(bottom, (float) (bottom + yPixelsPerUnit[scale] * minY[scale])); + LinkedList clickableArea = new LinkedList(); + + clickableAreas.put(i, clickableArea); + + SortedMap range = series.getRange(minX[scale], maxX[scale], 1); + int startIndex = -1; + + for (Entry value : range.entrySet()) { + + double xValue = value.getKey(); + double yValue = value.getValue(); + if (startIndex < 0) { + startIndex = series.getIndexForKey(xValue); + } + + // points.add((float) (left + xPixelsPerUnit[scale] + // * (value.getKey().floatValue() - minX[scale]))); + // points.add((float) (bottom - yPixelsPerUnit[scale] + // * (value.getValue().floatValue() - minY[scale]))); + values.add(value.getKey()); + values.add(value.getValue()); + + if (!isNullValue(yValue)) { + points.add((float) (left + xPixelsPerUnit[scale] * (xValue - minX[scale]))); + points.add((float) (bottom - yPixelsPerUnit[scale] * (yValue - minY[scale]))); + } else if (isRenderNullValues()) { + points.add((float) (left + xPixelsPerUnit[scale] * (xValue - minX[scale]))); + points.add((float) (bottom - yPixelsPerUnit[scale] * (-minY[scale]))); + } else { + if (points.size() > 0) { + drawSeries(series, canvas, paint, points, seriesRenderer, yAxisValue, i, or, startIndex); + ClickableArea[] clickableAreasForSubSeries = clickableAreasForPoints( + MathHelper.getFloats(points), MathHelper.getDoubles(values), yAxisValue, i, + startIndex); + clickableArea.addAll(Arrays.asList(clickableAreasForSubSeries)); + points.clear(); + values.clear(); + } + clickableArea.add(null); + } + } + + if (points.size() > 0) { + drawSeries(series, canvas, paint, points, seriesRenderer, yAxisValue, i, or, startIndex); + ClickableArea[] clickableAreasForSubSeries = clickableAreasForPoints( + MathHelper.getFloats(points), MathHelper.getDoubles(values), yAxisValue, i, startIndex); + clickableArea.addAll(Arrays.asList(clickableAreasForSubSeries)); + } + } + + // draw stuff over the margins such as data doesn't render on these areas + drawBackground(mRenderer, canvas, x, bottom, width, height - bottom, paint, true, + mRenderer.getMarginsColor()); + drawBackground(mRenderer, canvas, x, y, width, margins[0], paint, true, + mRenderer.getMarginsColor()); + if (or == Orientation.HORIZONTAL) { + drawBackground(mRenderer, canvas, x, y, left - x, height - y, paint, true, + mRenderer.getMarginsColor()); + drawBackground(mRenderer, canvas, right, y, margins[3], height - y, paint, true, + mRenderer.getMarginsColor()); + } else if (or == Orientation.VERTICAL) { + drawBackground(mRenderer, canvas, right, y, width - right, height - y, paint, true, + mRenderer.getMarginsColor()); + drawBackground(mRenderer, canvas, x, y, left - x, height - y, paint, true, + mRenderer.getMarginsColor()); + } + + boolean showLabels = mRenderer.isShowLabels() && hasValues; + boolean showGridX = mRenderer.isShowGridX(); + boolean showCustomTextGrid = mRenderer.isShowCustomTextGrid(); + if (showLabels || showGridX) { + List xLabels = getValidLabels(getXLabels(minX[0], maxX[0], mRenderer.getXLabels())); + Map> allYLabels = getYLabels(minY, maxY, maxScaleNumber); + + int xLabelsLeft = left; + if (showLabels) { + paint.setColor(mRenderer.getXLabelsColor()); + paint.setTextSize(mRenderer.getLabelsTextSize()); + paint.setTextAlign(mRenderer.getXLabelsAlign()); + if (mRenderer.getXLabelsAlign() == Align.LEFT) { + xLabelsLeft += mRenderer.getLabelsTextSize() / 4; + } + } + drawXLabels(xLabels, mRenderer.getXTextLabelLocations(), canvas, paint, xLabelsLeft, top, + bottom, xPixelsPerUnit[0], minX[0], maxX[0]); + drawYLabels(allYLabels, canvas, paint, maxScaleNumber, left, right, bottom, yPixelsPerUnit, + minY); + + if (showLabels) { + paint.setColor(mRenderer.getLabelsColor()); + for (int i = 0; i < maxScaleNumber; i++) { + Align axisAlign = mRenderer.getYAxisAlign(i); + Double[] yTextLabelLocations = mRenderer.getYTextLabelLocations(i); + for (Double location : yTextLabelLocations) { + if (minY[i] <= location && location <= maxY[i]) { + float yLabel = (float) (bottom - yPixelsPerUnit[i] + * (location.doubleValue() - minY[i])); + String label = mRenderer.getYTextLabel(location, i); + paint.setColor(mRenderer.getYLabelsColor(i)); + paint.setTextAlign(mRenderer.getYLabelsAlign(i)); + if (or == Orientation.HORIZONTAL) { + if (axisAlign == Align.LEFT) { + canvas.drawLine(left + getLabelLinePos(axisAlign), yLabel, left, yLabel, paint); + drawText(canvas, label, left, yLabel - 2, paint, mRenderer.getYLabelsAngle()); + } else { + canvas.drawLine(right, yLabel, right + getLabelLinePos(axisAlign), yLabel, paint); + drawText(canvas, label, right, yLabel - 2, paint, mRenderer.getYLabelsAngle()); + } + + if (showCustomTextGrid) { + paint.setColor(mRenderer.getGridColor()); + canvas.drawLine(left, yLabel, right, yLabel, paint); + } + } else { + canvas.drawLine(right - getLabelLinePos(axisAlign), yLabel, right, yLabel, paint); + drawText(canvas, label, right + 10, yLabel - 2, paint, mRenderer.getYLabelsAngle()); + if (showCustomTextGrid) { + paint.setColor(mRenderer.getGridColor()); + canvas.drawLine(right, yLabel, left, yLabel, paint); + } + } + } + } + } + } + + if (showLabels) { + paint.setColor(mRenderer.getLabelsColor()); + float size = mRenderer.getAxisTitleTextSize(); + paint.setTextSize(size); + paint.setTextAlign(Align.CENTER); + if (or == Orientation.HORIZONTAL) { + drawText(canvas, mRenderer.getXTitle(), x + width / 2, + bottom + mRenderer.getLabelsTextSize() * 4 / 3 + size, paint, 0); + for (int i = 0; i < maxScaleNumber; i++) { + Align axisAlign = mRenderer.getYAxisAlign(i); + if (axisAlign == Align.LEFT) { + drawText(canvas, mRenderer.getYTitle(i), x + size, y + height / 2, paint, -90); + } else { + drawText(canvas, mRenderer.getYTitle(i), x + width, y + height / 2, paint, -90); + } + } + paint.setTextSize(mRenderer.getChartTitleTextSize()); + drawText(canvas, mRenderer.getChartTitle(), x + width / 2, + y + mRenderer.getChartTitleTextSize(), paint, 0); + } else if (or == Orientation.VERTICAL) { + drawText(canvas, mRenderer.getXTitle(), x + width / 2, y + height - size, paint, -90); + drawText(canvas, mRenderer.getYTitle(), right + 20, y + height / 2, paint, 0); + paint.setTextSize(mRenderer.getChartTitleTextSize()); + drawText(canvas, mRenderer.getChartTitle(), x + size, top + height / 2, paint, 0); + } + } + } + if (or == Orientation.HORIZONTAL) { + drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, paint, false); + } else if (or == Orientation.VERTICAL) { + transform(canvas, angle, true); + drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, paint, false); + transform(canvas, angle, false); + } + if (mRenderer.isShowAxes()) { + paint.setColor(mRenderer.getAxesColor()); + canvas.drawLine(left, bottom, right, bottom, paint); + boolean rightAxis = false; + for (int i = 0; i < maxScaleNumber && !rightAxis; i++) { + rightAxis = mRenderer.getYAxisAlign(i) == Align.RIGHT; + } + if (or == Orientation.HORIZONTAL) { + canvas.drawLine(left, top, left, bottom, paint); + if (rightAxis) { + canvas.drawLine(right, top, right, bottom, paint); + } + } else if (or == Orientation.VERTICAL) { + canvas.drawLine(right, top, right, bottom, paint); + } + } + if (rotate) { + transform(canvas, angle, true); + } + } + + protected List getXLabels(double min, double max, int count) { + return MathHelper.getLabels(min, max, count); + } + + protected Map> getYLabels(double[] minY, double[] maxY, int maxScaleNumber) { + Map> allYLabels = new HashMap>(); + for (int i = 0; i < maxScaleNumber; i++) { + allYLabels.put(i, + getValidLabels(MathHelper.getLabels(minY[i], maxY[i], mRenderer.getYLabels()))); + } + return allYLabels; + } + + protected Rect getScreenR() { + return mScreenR; + } + + protected void setScreenR(Rect screenR) { + mScreenR = screenR; + } + + private List getValidLabels(List labels) { + List result = new ArrayList(labels); + for (Double label : labels) { + if (label.isNaN()) { + result.remove(label); + } + } + return result; + } + + /** + * Draws the series. + * + * @param series the series + * @param canvas the canvas + * @param paint the paint object + * @param pointsList the points to be rendered + * @param seriesRenderer the series renderer + * @param yAxisValue the y axis value in pixels + * @param seriesIndex the series index + * @param or the orientation + * @param startIndex the start index of the rendering points + */ + protected void drawSeries(XYSeries series, Canvas canvas, Paint paint, List pointsList, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, Orientation or, + int startIndex) { + BasicStroke stroke = seriesRenderer.getStroke(); + Cap cap = paint.getStrokeCap(); + Join join = paint.getStrokeJoin(); + float miter = paint.getStrokeMiter(); + PathEffect pathEffect = paint.getPathEffect(); + Style style = paint.getStyle(); + if (stroke != null) { + PathEffect effect = null; + if (stroke.getIntervals() != null) { + effect = new DashPathEffect(stroke.getIntervals(), stroke.getPhase()); + } + setStroke(stroke.getCap(), stroke.getJoin(), stroke.getMiter(), Style.FILL_AND_STROKE, + effect, paint); + } + float[] points = MathHelper.getFloats(pointsList); + drawSeries(canvas, paint, points, seriesRenderer, yAxisValue, seriesIndex, startIndex); + if (isRenderPoints(seriesRenderer)) { + ScatterChart pointsChart = getPointsChart(); + if (pointsChart != null) { + pointsChart.drawSeries(canvas, paint, points, seriesRenderer, yAxisValue, seriesIndex, + startIndex); + } + } + paint.setTextSize(seriesRenderer.getChartValuesTextSize()); + if (or == Orientation.HORIZONTAL) { + paint.setTextAlign(Align.CENTER); + } else { + paint.setTextAlign(Align.LEFT); + } + if (seriesRenderer.isDisplayChartValues()) { + paint.setTextAlign(seriesRenderer.getChartValuesTextAlign()); + drawChartValuesText(canvas, series, seriesRenderer, paint, points, seriesIndex, startIndex); + } + if (stroke != null) { + setStroke(cap, join, miter, style, pathEffect, paint); + } + } + + private void setStroke(Cap cap, Join join, float miter, Style style, PathEffect pathEffect, + Paint paint) { + paint.setStrokeCap(cap); + paint.setStrokeJoin(join); + paint.setStrokeMiter(miter); + paint.setPathEffect(pathEffect); + paint.setStyle(style); + } + + /** + * The graphical representation of the series values as text. + * + * @param canvas the canvas to paint to + * @param series the series to be painted + * @param renderer the series renderer + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + protected void drawChartValuesText(Canvas canvas, XYSeries series, SimpleSeriesRenderer renderer, + Paint paint, float[] points, int seriesIndex, int startIndex) { + if (points.length > 1) { // there are more than one point + // record the first point's position + float previousPointX = points[0]; + float previousPointY = points[1]; + for (int k = 0; k < points.length; k += 2) { + if (k == 2) { // decide whether to display first two points' values or not + if (Math.abs(points[2]- points[0]) > 100 || Math.abs(points[3] - points[1]) > 100) { + // first point + drawText(canvas, getLabel(series.getY(startIndex)), points[0], points[1] + - renderer.getChartValuesSpacing(), paint, 0); + // second point + drawText(canvas, getLabel(series.getY(startIndex + 1)), points[2], points[3] + - renderer.getChartValuesSpacing(), paint, 0); + + previousPointX = points[2]; + previousPointY = points[3]; + } + } else if (k > 2) { + // compare current point's position with the previous point's, if they are not too close, display + if (Math.abs(points[k]- previousPointX) > 100 || Math.abs(points[k+1] - previousPointY) > 100) { + drawText(canvas, getLabel(series.getY(startIndex + k / 2)), points[k], points[k + 1] + - renderer.getChartValuesSpacing(), paint, 0); + previousPointX = points[k]; + previousPointY = points[k+1]; + } + } + } + } else { // if only one point, display it + for (int k = 0; k < points.length; k += 2) { + drawText(canvas, getLabel(series.getY(startIndex + k / 2)), points[k], points[k + 1] + - renderer.getChartValuesSpacing(), paint, 0); + } + } + } + + /** + * The graphical representation of a text, to handle both HORIZONTAL and + * VERTICAL orientations and extra rotation angles. + * + * @param canvas the canvas to paint to + * @param text the text to be rendered + * @param x the X axis location of the text + * @param y the Y axis location of the text + * @param paint the paint to be used for drawing + * @param extraAngle the text angle + */ + protected void drawText(Canvas canvas, String text, float x, float y, Paint paint, + float extraAngle) { + float angle = -mRenderer.getOrientation().getAngle() + extraAngle; + if (angle != 0) { + // canvas.scale(1 / mScale, mScale); + canvas.rotate(angle, x, y); + } + drawString(canvas, text, x, y, paint); + if (angle != 0) { + canvas.rotate(-angle, x, y); + // canvas.scale(mScale, 1 / mScale); + } + } + + /** + * Transform the canvas such as it can handle both HORIZONTAL and VERTICAL + * orientations. + * + * @param canvas the canvas to paint to + * @param angle the angle of rotation + * @param inverse if the inverse transform needs to be applied + */ + private void transform(Canvas canvas, float angle, boolean inverse) { + if (inverse) { + canvas.scale(1 / mScale, mScale); + canvas.translate(mTranslate, -mTranslate); + canvas.rotate(-angle, mCenter.getX(), mCenter.getY()); + } else { + canvas.rotate(angle, mCenter.getX(), mCenter.getY()); + canvas.translate(-mTranslate, mTranslate); + canvas.scale(mScale, 1 / mScale); + } + } + + /** + * The graphical representation of the labels on the X axis. + * + * @param xLabels the X labels values + * @param xTextLabelLocations the X text label locations + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param left the left value of the labels area + * @param top the top value of the labels area + * @param bottom the bottom value of the labels area + * @param xPixelsPerUnit the amount of pixels per one unit in the chart labels + * @param minX the minimum value on the X axis in the chart + * @param maxX the maximum value on the X axis in the chart + */ + protected void drawXLabels(List xLabels, Double[] xTextLabelLocations, Canvas canvas, + Paint paint, int left, int top, int bottom, double xPixelsPerUnit, double minX, double maxX) { + int length = xLabels.size(); + boolean showLabels = mRenderer.isShowLabels(); + boolean showGridY = mRenderer.isShowGridY(); + for (int i = 0; i < length; i++) { + double label = xLabels.get(i); + float xLabel = (float) (left + xPixelsPerUnit * (label - minX)); + if (showLabels) { + paint.setColor(mRenderer.getXLabelsColor()); + canvas.drawLine(xLabel, bottom, xLabel, bottom + mRenderer.getLabelsTextSize() / 3, paint); + drawText(canvas, getLabel(label), xLabel, bottom + mRenderer.getLabelsTextSize() * 4 / 3, + paint, mRenderer.getXLabelsAngle()); + } + if (showGridY) { + paint.setColor(mRenderer.getGridColor()); + canvas.drawLine(xLabel, bottom, xLabel, top, paint); + } + } + drawXTextLabels(xTextLabelLocations, canvas, paint, showLabels, left, top, bottom, + xPixelsPerUnit, minX, maxX); + } + + /** + * The graphical representation of the labels on the X axis. + * + * @param allYLabels the Y labels values + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param maxScaleNumber the maximum scale number + * @param left the left value of the labels area + * @param right the right value of the labels area + * @param bottom the bottom value of the labels area + * @param yPixelsPerUnit the amount of pixels per one unit in the chart labels + * @param minY the minimum value on the Y axis in the chart + */ + protected void drawYLabels(Map> allYLabels, Canvas canvas, Paint paint, + int maxScaleNumber, int left, int right, int bottom, double[] yPixelsPerUnit, double[] minY) { + Orientation or = mRenderer.getOrientation(); + boolean showGridX = mRenderer.isShowGridX(); + boolean showLabels = mRenderer.isShowLabels(); + for (int i = 0; i < maxScaleNumber; i++) { + paint.setTextAlign(mRenderer.getYLabelsAlign(i)); + List yLabels = allYLabels.get(i); + int length = yLabels.size(); + for (int j = 0; j < length; j++) { + double label = yLabels.get(j); + Align axisAlign = mRenderer.getYAxisAlign(i); + boolean textLabel = mRenderer.getYTextLabel(label, i) != null; + float yLabel = (float) (bottom - yPixelsPerUnit[i] * (label - minY[i])); + if (or == Orientation.HORIZONTAL) { + if (showLabels && !textLabel) { + paint.setColor(mRenderer.getYLabelsColor(i)); + if (axisAlign == Align.LEFT) { + canvas.drawLine(left + getLabelLinePos(axisAlign), yLabel, left, yLabel, paint); + drawText(canvas, getLabel(label), left, yLabel - 2, paint, + mRenderer.getYLabelsAngle()); + } else { + canvas.drawLine(right, yLabel, right + getLabelLinePos(axisAlign), yLabel, paint); + drawText(canvas, getLabel(label), right, yLabel - 2, paint, + mRenderer.getYLabelsAngle()); + } + } + if (showGridX) { + paint.setColor(mRenderer.getGridColor()); + canvas.drawLine(left, yLabel, right, yLabel, paint); + } + } else if (or == Orientation.VERTICAL) { + if (showLabels && !textLabel) { + paint.setColor(mRenderer.getYLabelsColor(i)); + canvas.drawLine(right - getLabelLinePos(axisAlign), yLabel, right, yLabel, paint); + drawText(canvas, getLabel(label), right + 10, yLabel - 2, paint, + mRenderer.getYLabelsAngle()); + } + if (showGridX) { + paint.setColor(mRenderer.getGridColor()); + canvas.drawLine(right, yLabel, left, yLabel, paint); + } + } + } + } + } + + /** + * The graphical representation of the text labels on the X axis. + * + * @param xTextLabelLocations the X text label locations + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param left the left value of the labels area + * @param top the top value of the labels area + * @param bottom the bottom value of the labels area + * @param xPixelsPerUnit the amount of pixels per one unit in the chart labels + * @param minX the minimum value on the X axis in the chart + * @param maxX the maximum value on the X axis in the chart + */ + protected void drawXTextLabels(Double[] xTextLabelLocations, Canvas canvas, Paint paint, + boolean showLabels, int left, int top, int bottom, double xPixelsPerUnit, double minX, + double maxX) { + boolean showCustomTextGrid = mRenderer.isShowCustomTextGrid(); + if (showLabels) { + paint.setColor(mRenderer.getXLabelsColor()); + for (Double location : xTextLabelLocations) { + if (minX <= location && location <= maxX) { + float xLabel = (float) (left + xPixelsPerUnit * (location.doubleValue() - minX)); + paint.setColor(mRenderer.getXLabelsColor()); + canvas + .drawLine(xLabel, bottom, xLabel, bottom + mRenderer.getLabelsTextSize() / 3, paint); + drawText(canvas, mRenderer.getXTextLabel(location), xLabel, + bottom + mRenderer.getLabelsTextSize() * 4 / 3, paint, mRenderer.getXLabelsAngle()); + if (showCustomTextGrid) { + paint.setColor(mRenderer.getGridColor()); + canvas.drawLine(xLabel, bottom, xLabel, top, paint); + } + } + } + } + } + + // TODO: docs + public XYMultipleSeriesRenderer getRenderer() { + return mRenderer; + } + + public XYMultipleSeriesDataset getDataset() { + return mDataset; + } + + public double[] getCalcRange(int scale) { + return mCalcRange.get(scale); + } + + public void setCalcRange(double[] range, int scale) { + mCalcRange.put(scale, range); + } + + public double[] toRealPoint(float screenX, float screenY) { + return toRealPoint(screenX, screenY, 0); + } + + public double[] toScreenPoint(double[] realPoint) { + return toScreenPoint(realPoint, 0); + } + + private int getLabelLinePos(Align align) { + int pos = 4; + if (align == Align.LEFT) { + pos = -pos; + } + return pos; + } + + /** + * Transforms a screen point to a real coordinates point. + * + * @param screenX the screen x axis value + * @param screenY the screen y axis value + * @return the real coordinates point + */ + public double[] toRealPoint(float screenX, float screenY, int scale) { + double realMinX = mRenderer.getXAxisMin(scale); + double realMaxX = mRenderer.getXAxisMax(scale); + double realMinY = mRenderer.getYAxisMin(scale); + double realMaxY = mRenderer.getYAxisMax(scale); + return new double[] { + (screenX - mScreenR.left) * (realMaxX - realMinX) / mScreenR.width() + realMinX, + (mScreenR.top + mScreenR.height() - screenY) * (realMaxY - realMinY) / mScreenR.height() + + realMinY }; + } + + public double[] toScreenPoint(double[] realPoint, int scale) { + double realMinX = mRenderer.getXAxisMin(scale); + double realMaxX = mRenderer.getXAxisMax(scale); + double realMinY = mRenderer.getYAxisMin(scale); + double realMaxY = mRenderer.getYAxisMax(scale); + if (!mRenderer.isMinXSet(scale) || !mRenderer.isMaxXSet(scale) || !mRenderer.isMinXSet(scale) + || !mRenderer.isMaxYSet(scale)) { + double[] calcRange = getCalcRange(scale); + realMinX = calcRange[0]; + realMaxX = calcRange[1]; + realMinY = calcRange[2]; + realMaxY = calcRange[3]; + } + return new double[] { + (realPoint[0] - realMinX) * mScreenR.width() / (realMaxX - realMinX) + mScreenR.left, + (realMaxY - realPoint[1]) * mScreenR.height() / (realMaxY - realMinY) + mScreenR.top }; + } + + public SeriesSelection getSeriesAndPointForScreenCoordinate(final Point screenPoint) { + if (clickableAreas != null) + for (int seriesIndex = clickableAreas.size() - 1; seriesIndex >= 0; seriesIndex--) { + // series 0 is drawn first. Then series 1 is drawn on top, and series 2 + // on top of that. + // we want to know what the user clicked on, so traverse them in the + // order they appear on the screen. + int pointIndex = 0; + if (clickableAreas.get(seriesIndex) != null) { + RectF rectangle; + for (ClickableArea area : clickableAreas.get(seriesIndex)) { + rectangle = area.getRect(); + if (rectangle != null && rectangle.contains(screenPoint.getX(), screenPoint.getY())) { + return new SeriesSelection(seriesIndex, pointIndex, area.getX(), area.getY()); + } + pointIndex++; + } + } + } + return super.getSeriesAndPointForScreenCoordinate(screenPoint); + } + + /** + * The graphical representation of a series. + * + * @param canvas the canvas to paint to + * @param paint the paint to be used for drawing + * @param points the array of points to be used for drawing the series + * @param seriesRenderer the series renderer + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series currently being drawn + * @param startIndex the start index of the rendering points + */ + public abstract void drawSeries(Canvas canvas, Paint paint, float[] points, + SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex); + + /** + * Returns the clickable areas for all passed points + * + * @param points the array of points + * @param values the array of values of each point + * @param yAxisValue the minimum value of the y axis + * @param seriesIndex the index of the series to which the points belong + * @return an array of rectangles with the clickable area + * @param startIndex the start index of the rendering points + */ + protected abstract ClickableArea[] clickableAreasForPoints(float[] points, double[] values, + float yAxisValue, int seriesIndex, int startIndex); + + /** + * Returns if the chart should display the null values. + * + * @return if null values should be rendered + */ + protected boolean isRenderNullValues() { + return false; + } + + /** + * Returns if the chart should display the points as a certain shape. + * + * @param renderer the series renderer + */ + public boolean isRenderPoints(SimpleSeriesRenderer renderer) { + return false; + } + + /** + * Returns the default axis minimum. + * + * @return the default axis minimum + */ + public double getDefaultMinimum() { + return MathHelper.NULL_VALUE; + } + + /** + * Returns the scatter chart to be used for drawing the data points. + * + * @return the data points scatter chart + */ + public ScatterChart getPointsChart() { + return null; + } + + /** + * Returns the chart type identifier. + * + * @return the chart type + */ + public abstract String getChartType(); + +} diff --git a/android-libraries/achartengine/src/org/achartengine/chart/package.html b/android-libraries/achartengine/src/org/achartengine/chart/package.html new file mode 100644 index 00000000..2c5fbec1 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/chart/package.html @@ -0,0 +1,6 @@ + +AChartEngine + +Provides the classes that handle the actual rendering / drawing of the charts, based on the provided model and renderer. + + \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/image/zoom-1.png b/android-libraries/achartengine/src/org/achartengine/image/zoom-1.png new file mode 100644 index 00000000..8265f278 Binary files /dev/null and b/android-libraries/achartengine/src/org/achartengine/image/zoom-1.png differ diff --git a/android-libraries/achartengine/src/org/achartengine/image/zoom_in.png b/android-libraries/achartengine/src/org/achartengine/image/zoom_in.png new file mode 100644 index 00000000..1ac4864d Binary files /dev/null and b/android-libraries/achartengine/src/org/achartengine/image/zoom_in.png differ diff --git a/android-libraries/achartengine/src/org/achartengine/image/zoom_out.png b/android-libraries/achartengine/src/org/achartengine/image/zoom_out.png new file mode 100644 index 00000000..d67a87de Binary files /dev/null and b/android-libraries/achartengine/src/org/achartengine/image/zoom_out.png differ diff --git a/android-libraries/achartengine/src/org/achartengine/model/CategorySeries.java b/android-libraries/achartengine/src/org/achartengine/model/CategorySeries.java new file mode 100644 index 00000000..ae31c7dd --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/CategorySeries.java @@ -0,0 +1,143 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * A series for the category charts like the pie ones. + */ +public class CategorySeries implements Serializable { + /** The series title. */ + private String mTitle; + /** The series categories. */ + private List mCategories = new ArrayList(); + /** The series values. */ + private List mValues = new ArrayList(); + + /** + * Builds a new category series. + * + * @param title the series title + */ + public CategorySeries(String title) { + mTitle = title; + } + + /** + * Returns the series title. + * + * @return the series title + */ + public String getTitle() { + return mTitle; + } + + /** + * Adds a new value to the series + * + * @param value the new value + */ + public synchronized void add(double value) { + add(mCategories.size() + "", value); + } + + /** + * Adds a new value to the series. + * + * @param category the category + * @param value the new value + */ + public synchronized void add(String category, double value) { + mCategories.add(category); + mValues.add(value); + } + + /** + * Replaces the value at the specific index in the series. + * + * @param index the index in the series + * @param category the category + * @param value the new value + */ + public synchronized void set(int index, String category, double value) { + mCategories.set(index, category); + mValues.set(index, value); + } + + /** + * Removes an existing value from the series. + * + * @param index the index in the series of the value to remove + */ + public synchronized void remove(int index) { + mCategories.remove(index); + mValues.remove(index); + } + + /** + * Removes all the existing values from the series. + */ + public synchronized void clear() { + mCategories.clear(); + mValues.clear(); + } + + /** + * Returns the value at the specified index. + * + * @param index the index + * @return the value at the index + */ + public synchronized double getValue(int index) { + return mValues.get(index); + } + + /** + * Returns the category name at the specified index. + * + * @param index the index + * @return the category name at the index + */ + public synchronized String getCategory(int index) { + return mCategories.get(index); + } + + /** + * Returns the series item count. + * + * @return the series item count + */ + public synchronized int getItemCount() { + return mCategories.size(); + } + + /** + * Transforms the category series to an XY series. + * + * @return the XY series + */ + public XYSeries toXYSeries() { + XYSeries xySeries = new XYSeries(mTitle); + int k = 0; + for (double value : mValues) { + xySeries.add(++k, value); + } + return xySeries; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/MultipleCategorySeries.java b/android-libraries/achartengine/src/org/achartengine/model/MultipleCategorySeries.java new file mode 100644 index 00000000..992db4c1 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/MultipleCategorySeries.java @@ -0,0 +1,145 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * A series for the multiple category charts like the doughnut. + */ +public class MultipleCategorySeries implements Serializable { + /** The series title. */ + private String mTitle; + /** The series local keys. */ + private List mCategories = new ArrayList(); + /** The series name. */ + private List mTitles = new ArrayList(); + /** The series values. */ + private List mValues = new ArrayList(); + + /** + * Builds a new category series. + * + * @param title the series title + */ + public MultipleCategorySeries(String title) { + mTitle = title; + } + + /** + * Adds a new value to the series + * + * @param titles the titles to be used as labels + * @param values the new value + */ + public void add(String[] titles, double[] values) { + add(mCategories.size() + "", titles, values); + } + + /** + * Adds a new value to the series. + * + * @param category the category name + * @param titles the titles to be used as labels + * @param values the new value + */ + public void add(String category, String[] titles, double[] values) { + mCategories.add(category); + mTitles.add(titles); + mValues.add(values); + } + + /** + * Removes an existing value from the series. + * + * @param index the index in the series of the value to remove + */ + public void remove(int index) { + mCategories.remove(index); + mTitles.remove(index); + mValues.remove(index); + } + + /** + * Removes all the existing values from the series. + */ + public void clear() { + mCategories.clear(); + mTitles.clear(); + mValues.clear(); + } + + /** + * Returns the values at the specified index. + * + * @param index the index + * @return the value at the index + */ + public double[] getValues(int index) { + return mValues.get(index); + } + + /** + * Returns the category name at the specified index. + * + * @param index the index + * @return the category name at the index + */ + public String getCategory(int index) { + return mCategories.get(index); + } + + /** + * Returns the categories count. + * + * @return the categories count + */ + public int getCategoriesCount() { + return mCategories.size(); + } + + /** + * Returns the series item count. + * + * @param index the index + * @return the series item count + */ + public int getItemCount(int index) { + return mValues.get(index).length; + } + + /** + * Returns the series titles. + * + * @param index the index + * @return the series titles + */ + public String[] getTitles(int index) { + return mTitles.get(index); + } + + /** + * Transforms the category series to an XY series. + * + * @return the XY series + */ + public XYSeries toXYSeries() { + XYSeries xySeries = new XYSeries(mTitle); + return xySeries; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/Point.java b/android-libraries/achartengine/src/org/achartengine/model/Point.java new file mode 100644 index 00000000..4d2767cf --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/Point.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.io.Serializable; + +/** + * A class to encapsulate the definition of a point. + */ +public final class Point implements Serializable { + /** The X axis coordinate value. */ + private float mX; + /** The Y axis coordinate value. */ + private float mY; + + public Point() { + } + + public Point(float x, float y) { + mX = x; + mY = y; + } + + public float getX() { + return mX; + } + + public float getY() { + return mY; + } + + public void setX(float x) { + mX = x; + } + + public void setY(float y) { + mY = y; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/RangeCategorySeries.java b/android-libraries/achartengine/src/org/achartengine/model/RangeCategorySeries.java new file mode 100644 index 00000000..c004fa10 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/RangeCategorySeries.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.util.ArrayList; +import java.util.List; +/** + * A series for the range category charts like the range bar. + */ +public class RangeCategorySeries extends CategorySeries { + /** The series values. */ + private List mMaxValues = new ArrayList(); + /** + * Builds a new category series. + * + * @param title the series title + */ + public RangeCategorySeries(String title) { + super(title); + } + /** + * Adds new values to the series + * + * @param minValue the new minimum value + * @param maxValue the new maximum value + */ + public synchronized void add(double minValue, double maxValue) { + super.add(minValue); + mMaxValues.add(maxValue); + } + + /** + * Adds new values to the series. + * + * @param category the category + * @param minValue the new minimum value + * @param maxValue the new maximum value + */ + public synchronized void add(String category, double minValue, double maxValue) { + super.add(category, minValue); + mMaxValues.add(maxValue); + } + + /** + * Removes existing values from the series. + * + * @param index the index in the series of the values to remove + */ + public synchronized void remove(int index) { + super.remove(index); + mMaxValues.remove(index); + } + + /** + * Removes all the existing values from the series. + */ + public synchronized void clear() { + super.clear(); + mMaxValues.clear(); + } + + /** + * Returns the minimum value at the specified index. + * + * @param index the index + * @return the minimum value at the index + */ + public double getMinimumValue(int index) { + return getValue(index); + } + + /** + * Returns the maximum value at the specified index. + * + * @param index the index + * @return the maximum value at the index + */ + public double getMaximumValue(int index) { + return mMaxValues.get(index); + } + + /** + * Transforms the range category series to an XY series. + * + * @return the XY series + */ + public XYSeries toXYSeries() { + XYSeries xySeries = new XYSeries(getTitle()); + int length = getItemCount(); + for (int k = 0; k < length; k++) { + xySeries.add(k + 1, getMinimumValue(k)); + // the new fast XYSeries implementation doesn't allow 2 values at the same X, + // so I had to do a hack until I find a better solution + xySeries.add(k + 1.000001, getMaximumValue(k)); + } + return xySeries; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/SeriesSelection.java b/android-libraries/achartengine/src/org/achartengine/model/SeriesSelection.java new file mode 100644 index 00000000..4319c742 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/SeriesSelection.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +public class SeriesSelection { + private int mSeriesIndex; + + private int mPointIndex; + + private double mXValue; + + private double mValue; + + public SeriesSelection(int seriesIndex, int pointIndex, double xValue, double value) { + mSeriesIndex = seriesIndex; + mPointIndex = pointIndex; + mXValue = xValue; + mValue = value; + } + + public int getSeriesIndex() { + return mSeriesIndex; + } + + public int getPointIndex() { + return mPointIndex; + } + + public double getXValue() { + return mXValue; + } + + public double getValue() { + return mValue; + } +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/model/TimeSeries.java b/android-libraries/achartengine/src/org/achartengine/model/TimeSeries.java new file mode 100644 index 00000000..6b9a18e5 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/TimeSeries.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.util.Date; + +/** + * A series for the date / time charts. + */ +public class TimeSeries extends XYSeries { + + /** + * Builds a new date / time series. + * + * @param title the series title + */ + public TimeSeries(String title) { + super(title); + } + + /** + * Adds a new value to the series. + * + * @param x the date / time value for the X axis + * @param y the value for the Y axis + */ + public synchronized void add(Date x, double y) { + super.add(x.getTime(), y); + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/XYMultipleSeriesDataset.java b/android-libraries/achartengine/src/org/achartengine/model/XYMultipleSeriesDataset.java new file mode 100644 index 00000000..6ac6a6de --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/XYMultipleSeriesDataset.java @@ -0,0 +1,94 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * A series that includes 0 to many XYSeries. + */ +public class XYMultipleSeriesDataset implements Serializable { + /** The included series. */ + private List mSeries = new ArrayList(); + + /** + * Adds a new XY series to the list. + * + * @param series the XY series to ass + */ + public synchronized void addSeries(XYSeries series) { + mSeries.add(series); + } + + /** + * Adds a new XY series to the list. + * + * @param index the index in the series list + * @param series the XY series to ass + */ + public synchronized void addSeries(int index, XYSeries series) { + mSeries.add(index, series); + } + + /** + * Removes the XY series from the list. + * + * @param index the index in the series list of the series to remove + */ + public synchronized void removeSeries(int index) { + mSeries.remove(index); + } + + /** + * Removes the XY series from the list. + * + * @param series the XY series to be removed + */ + public synchronized void removeSeries(XYSeries series) { + mSeries.remove(series); + } + + /** + * Returns the XY series at the specified index. + * + * @param index the index + * @return the XY series at the index + */ + public synchronized XYSeries getSeriesAt(int index) { + return mSeries.get(index); + } + + /** + * Returns the XY series count. + * + * @return the XY series count + */ + public synchronized int getSeriesCount() { + return mSeries.size(); + } + + /** + * Returns an array of the XY series. + * + * @return the XY series array + */ + public synchronized XYSeries[] getSeries() { + return mSeries.toArray(new XYSeries[0]); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/XYSeries.java b/android-libraries/achartengine/src/org/achartengine/model/XYSeries.java new file mode 100644 index 00000000..56063b77 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/XYSeries.java @@ -0,0 +1,255 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.SortedMap; + +import org.achartengine.util.IndexXYMap; +import org.achartengine.util.MathHelper; +import org.achartengine.util.XYEntry; + +/** + * An XY series encapsulates values for XY charts like line, time, area, + * scatter... charts. + */ +public class XYSeries implements Serializable { + /** The series title. */ + private String mTitle; + /** A map to contain values for X and Y axes and index for each bundle */ + private final IndexXYMap mXY = new IndexXYMap(); + /** The minimum value for the X axis. */ + private double mMinX = MathHelper.NULL_VALUE; + /** The maximum value for the X axis. */ + private double mMaxX = -MathHelper.NULL_VALUE; + /** The minimum value for the Y axis. */ + private double mMinY = MathHelper.NULL_VALUE; + /** The maximum value for the Y axis. */ + private double mMaxY = -MathHelper.NULL_VALUE; + /** The scale number for this series. */ + private final int mScaleNumber; + + /** + * Builds a new XY series. + * + * @param title the series title. + */ + public XYSeries(String title) { + this(title, 0); + } + + /** + * Builds a new XY series. + * + * @param title the series title. + * @param scaleNumber the series scale number + */ + public XYSeries(String title, int scaleNumber) { + mTitle = title; + mScaleNumber = scaleNumber; + initRange(); + } + + public int getScaleNumber() { + return mScaleNumber; + } + + /** + * Initializes the range for both axes. + */ + private void initRange() { + mMinX = MathHelper.NULL_VALUE; + mMaxX = -MathHelper.NULL_VALUE; + mMinY = MathHelper.NULL_VALUE; + mMaxY = -MathHelper.NULL_VALUE; + int length = getItemCount(); + for (int k = 0; k < length; k++) { + double x = getX(k); + double y = getY(k); + updateRange(x, y); + } + } + + /** + * Updates the range on both axes. + * + * @param x the new x value + * @param y the new y value + */ + private void updateRange(double x, double y) { + mMinX = Math.min(mMinX, x); + mMaxX = Math.max(mMaxX, x); + mMinY = Math.min(mMinY, y); + mMaxY = Math.max(mMaxY, y); + } + + /** + * Returns the series title. + * + * @return the series title + */ + public String getTitle() { + return mTitle; + } + + /** + * Sets the series title. + * + * @param title the series title + */ + public void setTitle(String title) { + mTitle = title; + } + + /** + * Adds a new value to the series. + * + * @param x the value for the X axis + * @param y the value for the Y axis + */ + public synchronized void add(double x, double y) { + mXY.put(x, y); + updateRange(x, y); + } + + /** + * Removes an existing value from the series. + * + * @param index the index in the series of the value to remove + */ + public synchronized void remove(int index) { + XYEntry removedEntry = mXY.removeByIndex(index); + double removedX = removedEntry.getKey(); + double removedY = removedEntry.getValue(); + if (removedX == mMinX || removedX == mMaxX || removedY == mMinY || removedY == mMaxY) { + initRange(); + } + } + + /** + * Removes all the existing values from the series. + */ + public synchronized void clear() { + mXY.clear(); + initRange(); + } + + /** + * Returns the X axis value at the specified index. + * + * @param index the index + * @return the X value + */ + public synchronized double getX(int index) { + return mXY.getXByIndex(index); + } + + /** + * Returns the Y axis value at the specified index. + * + * @param index the index + * @return the Y value + */ + public synchronized double getY(int index) { + return mXY.getYByIndex(index); + } + + /** + * Returns submap of x and y values according to the given start and end + * + * @param start start x value + * @param stop stop x value + * @return a submap of x and y values + */ + public synchronized SortedMap getRange(double start, double stop, + int beforeAfterPoints) { + // we need to add one point before the start and one point after the end (if + // there are any) + // to ensure that line doesn't end before the end of the screen + + // this would be simply: start = mXY.lowerKey(start) but NavigableMap is + // available since API 9 + SortedMap headMap = mXY.headMap(start); + if (!headMap.isEmpty()) { + start = headMap.lastKey(); + } + + // this would be simply: end = mXY.higherKey(end) but NavigableMap is + // available since API 9 + // so we have to do this hack in order to support older versions + SortedMap tailMap = mXY.tailMap(stop); + if (!tailMap.isEmpty()) { + Iterator tailIterator = tailMap.keySet().iterator(); + Double next = tailIterator.next(); + if (tailIterator.hasNext()) { + stop = tailIterator.next(); + } else { + stop += next; + } + } + return mXY.subMap(start, stop); + } + + public int getIndexForKey(double key) { + return mXY.getIndexForKey(key); + } + + /** + * Returns the series item count. + * + * @return the series item count + */ + public synchronized int getItemCount() { + return mXY.size(); + } + + /** + * Returns the minimum value on the X axis. + * + * @return the X axis minimum value + */ + public double getMinX() { + return mMinX; + } + + /** + * Returns the minimum value on the Y axis. + * + * @return the Y axis minimum value + */ + public double getMinY() { + return mMinY; + } + + /** + * Returns the maximum value on the X axis. + * + * @return the X axis maximum value + */ + public double getMaxX() { + return mMaxX; + } + + /** + * Returns the maximum value on the Y axis. + * + * @return the Y axis maximum value + */ + public double getMaxY() { + return mMaxY; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/XYValueSeries.java b/android-libraries/achartengine/src/org/achartengine/model/XYValueSeries.java new file mode 100644 index 00000000..81aff28b --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/XYValueSeries.java @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.model; + +import java.util.ArrayList; +import java.util.List; + +import org.achartengine.util.MathHelper; + +/** + * An extension of the XY series which adds a third dimension. It is used for XY + * charts like bubble. + */ +public class XYValueSeries extends XYSeries { + /** A list to contain the series values. */ + private List mValue = new ArrayList(); + /** The minimum value. */ + private double mMinValue = MathHelper.NULL_VALUE; + /** The maximum value. */ + private double mMaxValue = -MathHelper.NULL_VALUE; + + /** + * Builds a new XY value series. + * + * @param title the series title. + */ + public XYValueSeries(String title) { + super(title); + } + + /** + * Adds a new value to the series. + * + * @param x the value for the X axis + * @param y the value for the Y axis + * @param value the value + */ + public synchronized void add(double x, double y, double value) { + super.add(x, y); + mValue.add(value); + updateRange(value); + } + + /** + * Initializes the values range. + */ + private void initRange() { + mMinValue = MathHelper.NULL_VALUE; + mMaxValue = MathHelper.NULL_VALUE; + int length = getItemCount(); + for (int k = 0; k < length; k++) { + updateRange(getValue(k)); + } + } + + /** + * Updates the values range. + * + * @param value the new value + */ + private void updateRange(double value) { + mMinValue = Math.min(mMinValue, value); + mMaxValue = Math.max(mMaxValue, value); + } + + /** + * Adds a new value to the series. + * + * @param x the value for the X axis + * @param y the value for the Y axis + */ + public synchronized void add(double x, double y) { + add(x, y, 0d); + } + + /** + * Removes an existing value from the series. + * + * @param index the index in the series of the value to remove + */ + public synchronized void remove(int index) { + super.remove(index); + double removedValue = mValue.remove(index); + if (removedValue == mMinValue || removedValue == mMaxValue) { + initRange(); + } + } + + /** + * Removes all the values from the series. + */ + public synchronized void clear() { + super.clear(); + mValue.clear(); + initRange(); + } + + /** + * Returns the value at the specified index. + * + * @param index the index + * @return the value + */ + public synchronized double getValue(int index) { + return mValue.get(index); + } + + /** + * Returns the minimum value. + * + * @return the minimum value + */ + public double getMinValue() { + return mMinValue; + } + + /** + * Returns the maximum value. + * + * @return the maximum value + */ + public double getMaxValue() { + return mMaxValue; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/model/package.html b/android-libraries/achartengine/src/org/achartengine/model/package.html new file mode 100644 index 00000000..6135d29c --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/model/package.html @@ -0,0 +1,6 @@ + +AChartEngine + +Provides the classes that handle the data values (data model) to be used by displaying the charts. + + \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/package.html b/android-libraries/achartengine/src/org/achartengine/package.html new file mode 100644 index 00000000..b26cf259 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/package.html @@ -0,0 +1,6 @@ + +AChartEngine + +The main classes, including the ChartFactory, GraphicalActivity and GraphicalView. + + \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/renderer/BasicStroke.java b/android-libraries/achartengine/src/org/achartengine/renderer/BasicStroke.java new file mode 100644 index 00000000..3ac93d54 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/renderer/BasicStroke.java @@ -0,0 +1,107 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.renderer; + +import java.io.Serializable; + +import android.graphics.Paint.Cap; +import android.graphics.Paint.Join; + +/** + * A descriptor for the stroke style. + */ +public class BasicStroke implements Serializable { + /** The solid line style. */ + public static final BasicStroke SOLID = new BasicStroke(Cap.BUTT, Join.MITER, 4, null, 0); + /** The dashed line style. */ + public static final BasicStroke DASHED = new BasicStroke(Cap.ROUND, Join.BEVEL, 10, new float[] { + 10, 10 }, 1); + /** The dot line style. */ + public static final BasicStroke DOTTED = new BasicStroke(Cap.ROUND, Join.BEVEL, 5, new float[] { + 2, 10 }, 1); + /** The stroke cap. */ + private Cap mCap; + /** The stroke join. */ + private Join mJoin; + /** The stroke miter. */ + private float mMiter; + /** The path effect intervals. */ + private float[] mIntervals; + /** The path effect phase. */ + private float mPhase; + + /** + * Build a new basic stroke style. + * + * @param cap the stroke cap + * @param join the stroke join + * @param miter the stroke miter + * @param intervals the path effect intervals + * @param phase the path effect phase + */ + public BasicStroke(Cap cap, Join join, float miter, float[] intervals, float phase) { + mCap = cap; + mJoin = join; + mMiter = miter; + mIntervals = intervals; + } + + /** + * Returns the stroke cap. + * + * @return the stroke cap + */ + public Cap getCap() { + return mCap; + } + + /** + * Returns the stroke join. + * + * @return the stroke join + */ + public Join getJoin() { + return mJoin; + } + + /** + * Returns the stroke miter. + * + * @return the stroke miter + */ + public float getMiter() { + return mMiter; + } + + /** + * Returns the path effect intervals. + * + * @return the path effect intervals + */ + public float[] getIntervals() { + return mIntervals; + } + + /** + * Returns the path effect phase. + * + * @return the path effect phase + */ + public float getPhase() { + return mPhase; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/renderer/DefaultRenderer.java b/android-libraries/achartengine/src/org/achartengine/renderer/DefaultRenderer.java new file mode 100644 index 00000000..4dbb2549 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/renderer/DefaultRenderer.java @@ -0,0 +1,750 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.renderer; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import android.graphics.Color; +import android.graphics.Typeface; + +/** + * An abstract renderer to be extended by the multiple series classes. + */ +public class DefaultRenderer implements Serializable { + /** The chart title. */ + private String mChartTitle = ""; + /** The chart title text size. */ + private float mChartTitleTextSize = 15; + /** A no color constant. */ + public static final int NO_COLOR = 0; + /** The default background color. */ + public static final int BACKGROUND_COLOR = Color.BLACK; + /** The default color for text. */ + public static final int TEXT_COLOR = Color.LTGRAY; + /** A text font for regular text, like the chart labels. */ + private static final Typeface REGULAR_TEXT_FONT = Typeface + .create(Typeface.SERIF, Typeface.NORMAL); + /** The typeface name for the texts. */ + private String mTextTypefaceName = REGULAR_TEXT_FONT.toString(); + /** The typeface style for the texts. */ + private int mTextTypefaceStyle = Typeface.NORMAL; + /** The chart background color. */ + private int mBackgroundColor; + /** If the background color is applied. */ + private boolean mApplyBackgroundColor; + /** If the axes are visible. */ + private boolean mShowAxes = true; + /** The axes color. */ + private int mAxesColor = TEXT_COLOR; + /** If the labels are visible. */ + private boolean mShowLabels = true; + /** The labels color. */ + private int mLabelsColor = TEXT_COLOR; + /** The labels text size. */ + private float mLabelsTextSize = 10; + /** If the legend is visible. */ + private boolean mShowLegend = true; + /** The legend text size. */ + private float mLegendTextSize = 12; + /** If the legend should size to fit. */ + private boolean mFitLegend = false; + /** If the X axis grid should be displayed. */ + private boolean mShowGridX = false; + /** If the Y axis grid should be displayed. */ + private boolean mShowGridY = false; + /** If the custom text grid should be displayed. */ + private boolean mShowCustomTextGrid = false; + /** The simple renderers that are included in this multiple series renderer. */ + private List mRenderers = new ArrayList(); + /** The antialiasing flag. */ + private boolean mAntialiasing = true; + /** The legend height. */ + private int mLegendHeight = 0; + /** The margins size. */ + private int[] mMargins = new int[] { 20, 30, 10, 20 }; + /** A value to be used for scaling the chart. */ + private float mScale = 1; + /** A flag for enabling the pan. */ + private boolean mPanEnabled = true; + /** A flag for enabling the zoom. */ + private boolean mZoomEnabled = true; + /** A flag for enabling the visibility of the zoom buttons. */ + private boolean mZoomButtonsVisible = false; + /** The zoom rate. */ + private float mZoomRate = 1.5f; + /** A flag for enabling the external zoom. */ + private boolean mExternalZoomEnabled = false; + /** The original chart scale. */ + private float mOriginalScale = mScale; + /** A flag for enabling the click on elements. */ + private boolean mClickEnabled = false; + /** The selectable radius around a clickable point. */ + private int selectableBuffer = 15; + /** If the chart should display the values (available for pie chart). */ + private boolean mDisplayValues; + + /** + * A flag to be set if the chart is inside a scroll and doesn't need to shrink + * when not enough space. + */ + private boolean mInScroll; + /** The start angle for circular charts such as pie, doughnut, etc. */ + private float mStartAngle = 0; + + /** + * Returns the chart title. + * + * @return the chart title + */ + public String getChartTitle() { + return mChartTitle; + } + + /** + * Sets the chart title. + * + * @param title the chart title + */ + public void setChartTitle(String title) { + mChartTitle = title; + } + + /** + * Returns the chart title text size. + * + * @return the chart title text size + */ + public float getChartTitleTextSize() { + return mChartTitleTextSize; + } + + /** + * Sets the chart title text size. + * + * @param textSize the chart title text size + */ + public void setChartTitleTextSize(float textSize) { + mChartTitleTextSize = textSize; + } + + /** + * Adds a simple renderer to the multiple renderer. + * + * @param renderer the renderer to be added + */ + public void addSeriesRenderer(SimpleSeriesRenderer renderer) { + mRenderers.add(renderer); + } + + /** + * Adds a simple renderer to the multiple renderer. + * + * @param index the index in the renderers list + * @param renderer the renderer to be added + */ + public void addSeriesRenderer(int index, SimpleSeriesRenderer renderer) { + mRenderers.add(index, renderer); + } + + /** + * Removes a simple renderer from the multiple renderer. + * + * @param renderer the renderer to be removed + */ + public void removeSeriesRenderer(SimpleSeriesRenderer renderer) { + mRenderers.remove(renderer); + } + + /** + * Removes all renderers from the multiple renderer. + */ + public void removeAllRenderers() { + mRenderers.clear(); + } + + /** + * Returns the simple renderer from the multiple renderer list. + * + * @param index the index in the simple renderers list + * @return the simple renderer at the specified index + */ + public SimpleSeriesRenderer getSeriesRendererAt(int index) { + return mRenderers.get(index); + } + + /** + * Returns the simple renderers count in the multiple renderer list. + * + * @return the simple renderers count + */ + public int getSeriesRendererCount() { + return mRenderers.size(); + } + + /** + * Returns an array of the simple renderers in the multiple renderer list. + * + * @return the simple renderers array + */ + public SimpleSeriesRenderer[] getSeriesRenderers() { + return mRenderers.toArray(new SimpleSeriesRenderer[0]); + } + + /** + * Returns the background color. + * + * @return the background color + */ + public int getBackgroundColor() { + return mBackgroundColor; + } + + /** + * Sets the background color. + * + * @param color the background color + */ + public void setBackgroundColor(int color) { + mBackgroundColor = color; + } + + /** + * Returns if the background color should be applied. + * + * @return the apply flag for the background color. + */ + public boolean isApplyBackgroundColor() { + return mApplyBackgroundColor; + } + + /** + * Sets if the background color should be applied. + * + * @param apply the apply flag for the background color + */ + public void setApplyBackgroundColor(boolean apply) { + mApplyBackgroundColor = apply; + } + + /** + * Returns the axes color. + * + * @return the axes color + */ + public int getAxesColor() { + return mAxesColor; + } + + /** + * Sets the axes color. + * + * @param color the axes color + */ + public void setAxesColor(int color) { + mAxesColor = color; + } + + /** + * Returns the labels color. + * + * @return the labels color + */ + public int getLabelsColor() { + return mLabelsColor; + } + + /** + * Sets the labels color. + * + * @param color the labels color + */ + public void setLabelsColor(int color) { + mLabelsColor = color; + } + + /** + * Returns the labels text size. + * + * @return the labels text size + */ + public float getLabelsTextSize() { + return mLabelsTextSize; + } + + /** + * Sets the labels text size. + * + * @param textSize the labels text size + */ + public void setLabelsTextSize(float textSize) { + mLabelsTextSize = textSize; + } + + /** + * Returns if the axes should be visible. + * + * @return the visibility flag for the axes + */ + public boolean isShowAxes() { + return mShowAxes; + } + + /** + * Sets if the axes should be visible. + * + * @param showAxes the visibility flag for the axes + */ + public void setShowAxes(boolean showAxes) { + mShowAxes = showAxes; + } + + /** + * Returns if the labels should be visible. + * + * @return the visibility flag for the labels + */ + public boolean isShowLabels() { + return mShowLabels; + } + + /** + * Sets if the labels should be visible. + * + * @param showLabels the visibility flag for the labels + */ + public void setShowLabels(boolean showLabels) { + mShowLabels = showLabels; + } + + /** + * Returns if the X axis grid should be visible. + * + * @return the visibility flag for the X axis grid + */ + public boolean isShowGridX() { + return mShowGridX; + } + + /** + * Returns if the Y axis grid should be visible. + * + * @return the visibility flag for the Y axis grid + */ + public boolean isShowGridY() { + return mShowGridY; + } + + /** + * Sets if the X axis grid should be visible. + * + * @param showGrid the visibility flag for the X axis grid + */ + public void setShowGridX(boolean showGrid) { + mShowGridX = showGrid; + } + + /** + * Sets if the Y axis grid should be visible. + * + * @param showGrid the visibility flag for the Y axis grid + */ + public void setShowGridY(boolean showGrid) { + mShowGridY = showGrid; + } + + /** + * Sets if the grid should be visible. + * + * @param showGrid the visibility flag for the grid + */ + public void setShowGrid(boolean showGrid) { + setShowGridX(showGrid); + setShowGridY(showGrid); + } + + /** + * Returns if the grid should be visible for custom X or Y labels. + * + * @return the visibility flag for the custom text grid + */ + public boolean isShowCustomTextGrid() { + return mShowCustomTextGrid; + } + + /** + * Sets if the grid for custom X or Y labels should be visible. + * + * @param showGrid the visibility flag for the custom text grid + */ + public void setShowCustomTextGrid(boolean showGrid) { + mShowCustomTextGrid = showGrid; + } + + /** + * Returns if the legend should be visible. + * + * @return the visibility flag for the legend + */ + public boolean isShowLegend() { + return mShowLegend; + } + + /** + * Sets if the legend should be visible. + * + * @param showLegend the visibility flag for the legend + */ + public void setShowLegend(boolean showLegend) { + mShowLegend = showLegend; + } + + /** + * Returns if the legend should size to fit. + * + * @return the fit behavior + */ + public boolean isFitLegend() { + return mFitLegend; + } + + /** + * Sets if the legend should size to fit. + * + * @param fit the fit behavior + */ + public void setFitLegend(boolean fit) { + mFitLegend = fit; + } + + /** + * Returns the text typeface name. + * + * @return the text typeface name + */ + public String getTextTypefaceName() { + return mTextTypefaceName; + } + + /** + * Returns the text typeface style. + * + * @return the text typeface style + */ + public int getTextTypefaceStyle() { + return mTextTypefaceStyle; + } + + /** + * Returns the legend text size. + * + * @return the legend text size + */ + public float getLegendTextSize() { + return mLegendTextSize; + } + + /** + * Sets the legend text size. + * + * @param textSize the legend text size + */ + public void setLegendTextSize(float textSize) { + mLegendTextSize = textSize; + } + + /** + * Sets the text typeface name and style. + * + * @param typefaceName the text typeface name + * @param style the text typeface style + */ + public void setTextTypeface(String typefaceName, int style) { + mTextTypefaceName = typefaceName; + mTextTypefaceStyle = style; + } + + /** + * Returns the antialiasing flag value. + * + * @return the antialiasing value + */ + public boolean isAntialiasing() { + return mAntialiasing; + } + + /** + * Sets the antialiasing value. + * + * @param antialiasing the antialiasing + */ + public void setAntialiasing(boolean antialiasing) { + mAntialiasing = antialiasing; + } + + /** + * Returns the value to be used for scaling the chart. + * + * @return the scale value + */ + public float getScale() { + return mScale; + } + + /** + * Returns the original value to be used for scaling the chart. + * + * @return the original scale value + */ + public float getOriginalScale() { + return mOriginalScale; + } + + /** + * Sets the value to be used for scaling the chart. It works on some charts + * like pie, doughnut, dial. + * + * @param scale the scale value + */ + public void setScale(float scale) { + mScale = scale; + } + + /** + * Returns the enabled state of the zoom. + * + * @return if zoom is enabled + */ + public boolean isZoomEnabled() { + return mZoomEnabled; + } + + /** + * Sets the enabled state of the zoom. + * + * @param enabled zoom enabled + */ + public void setZoomEnabled(boolean enabled) { + mZoomEnabled = enabled; + } + + /** + * Returns the visible state of the zoom buttons. + * + * @return if zoom buttons are visible + */ + public boolean isZoomButtonsVisible() { + return mZoomButtonsVisible; + } + + /** + * Sets the visible state of the zoom buttons. + * + * @param visible if the zoom buttons are visible + */ + public void setZoomButtonsVisible(boolean visible) { + mZoomButtonsVisible = visible; + } + + /** + * Returns the enabled state of the external (application implemented) zoom. + * + * @return if external zoom is enabled + */ + public boolean isExternalZoomEnabled() { + return mExternalZoomEnabled; + } + + /** + * Sets the enabled state of the external (application implemented) zoom. + * + * @param enabled external zoom enabled + */ + public void setExternalZoomEnabled(boolean enabled) { + mExternalZoomEnabled = enabled; + } + + /** + * Returns the zoom rate. + * + * @return the zoom rate + */ + public float getZoomRate() { + return mZoomRate; + } + + /** + * Returns the enabled state of the pan. + * + * @return if pan is enabled + */ + public boolean isPanEnabled() { + return mPanEnabled; + } + + /** + * Sets the enabled state of the pan. + * + * @param enabled pan enabled + */ + public void setPanEnabled(boolean enabled) { + mPanEnabled = enabled; + } + + /** + * Sets the zoom rate. + * + * @param rate the zoom rate + */ + public void setZoomRate(float rate) { + mZoomRate = rate; + } + + /** + * Returns the enabled state of the click. + * + * @return if click is enabled + */ + public boolean isClickEnabled() { + return mClickEnabled; + } + + /** + * Sets the enabled state of the click. + * + * @param enabled click enabled + */ + public void setClickEnabled(boolean enabled) { + mClickEnabled = enabled; + } + + /** + * Returns the selectable radius value around clickable points. + * + * @return the selectable radius + */ + public int getSelectableBuffer() { + return selectableBuffer; + } + + /** + * Sets the selectable radius value around clickable points. + * + * @param buffer the selectable radius + */ + public void setSelectableBuffer(int buffer) { + selectableBuffer = buffer; + } + + /** + * Returns the legend height. + * + * @return the legend height + */ + public int getLegendHeight() { + return mLegendHeight; + } + + /** + * Sets the legend height, in pixels. + * + * @param height the legend height + */ + public void setLegendHeight(int height) { + mLegendHeight = height; + } + + /** + * Returns the margin sizes. An array containing the margins in this order: + * top, left, bottom, right + * + * @return the margin sizes + */ + public int[] getMargins() { + return mMargins; + } + + /** + * Sets the margins, in pixels. + * + * @param margins an array containing the margin size values, in this order: + * top, left, bottom, right + */ + public void setMargins(int[] margins) { + mMargins = margins; + } + + /** + * Returns if the chart is inside a scroll view and doesn't need to shrink. + * + * @return if it is inside a scroll view + */ + public boolean isInScroll() { + return mInScroll; + } + + /** + * To be set if the chart is inside a scroll view and doesn't need to shrink + * when not enough space. + * + * @param inScroll if it is inside a scroll view + */ + public void setInScroll(boolean inScroll) { + mInScroll = inScroll; + } + + /** + * Returns the start angle for circular charts such as pie, doughnut. An angle + * of 0 degrees correspond to the geometric angle of 0 degrees (3 o'clock on a + * watch.) + * + * @return the start angle in degrees + */ + public float getStartAngle() { + return mStartAngle; + } + + /** + * Sets the start angle for circular charts such as pie, doughnut, etc. An + * angle of 0 degrees correspond to the geometric angle of 0 degrees (3 + * o'clock on a watch.) + * + * @param startAngle the start angle in degrees + */ + public void setStartAngle(float startAngle) { + mStartAngle = startAngle; + } + + /** + * Returns if the values should be displayed as text. + * + * @return if the values should be displayed as text + */ + public boolean isDisplayValues() { + return mDisplayValues; + } + + /** + * Sets if the values should be displayed as text (supported by pie chart). + * + * @param display if the values should be displayed as text + */ + public void setDisplayValues(boolean display) { + mDisplayValues = display; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/renderer/DialRenderer.java b/android-libraries/achartengine/src/org/achartengine/renderer/DialRenderer.java new file mode 100644 index 00000000..1ed84619 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/renderer/DialRenderer.java @@ -0,0 +1,196 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.renderer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.achartengine.util.MathHelper; + +/** + * Dial chart renderer. + */ +public class DialRenderer extends DefaultRenderer { + /** The start angle in the dial range. */ + private double mAngleMin = 330; + /** The end angle in the dial range. */ + private double mAngleMax = 30; + /** The start value in dial range. */ + private double mMinValue = MathHelper.NULL_VALUE; + /** The end value in dial range. */ + private double mMaxValue = -MathHelper.NULL_VALUE; + /** The spacing for the minor ticks. */ + private double mMinorTickSpacing = MathHelper.NULL_VALUE; + /** The spacing for the major ticks. */ + private double mMajorTickSpacing = MathHelper.NULL_VALUE; + /** An array of the renderers types (default is NEEDLE). */ + private List mVisualTypes = new ArrayList(); + + public enum Type { + NEEDLE, ARROW; + } + + /** + * Returns the start angle value of the dial. + * + * @return the angle start value + */ + public double getAngleMin() { + return mAngleMin; + } + + /** + * Sets the start angle value of the dial. + * + * @param min the dial angle start value + */ + public void setAngleMin(double min) { + mAngleMin = min; + } + + /** + * Returns the end angle value of the dial. + * + * @return the angle end value + */ + public double getAngleMax() { + return mAngleMax; + } + + /** + * Sets the end angle value of the dial. + * + * @param max the dial angle end value + */ + public void setAngleMax(double max) { + mAngleMax = max; + } + + /** + * Returns the start value to be rendered on the dial. + * + * @return the start value on dial + */ + public double getMinValue() { + return mMinValue; + } + + /** + * Sets the start value to be rendered on the dial. + * + * @param min the start value on the dial + */ + public void setMinValue(double min) { + mMinValue = min; + } + + /** + * Returns if the minimum dial value was set. + * + * @return the minimum dial value was set or not + */ + public boolean isMinValueSet() { + return mMinValue != MathHelper.NULL_VALUE; + } + + /** + * Returns the end value to be rendered on the dial. + * + * @return the end value on the dial + */ + public double getMaxValue() { + return mMaxValue; + } + + /** + * Sets the end value to be rendered on the dial. + * + * @param max the end value on the dial + */ + public void setMaxValue(double max) { + mMaxValue = max; + } + + /** + * Returns if the maximum dial value was set. + * + * @return the maximum dial was set or not + */ + public boolean isMaxValueSet() { + return mMaxValue != -MathHelper.NULL_VALUE; + } + + /** + * Returns the minor ticks spacing. + * + * @return the minor ticks spacing + */ + public double getMinorTicksSpacing() { + return mMinorTickSpacing; + } + + /** + * Sets the minor ticks spacing. + * + * @param spacing the minor ticks spacing + */ + public void setMinorTicksSpacing(double spacing) { + mMinorTickSpacing = spacing; + } + + /** + * Returns the major ticks spacing. + * + * @return the major ticks spacing + */ + public double getMajorTicksSpacing() { + return mMajorTickSpacing; + } + + /** + * Sets the major ticks spacing. + * + * @param spacing the major ticks spacing + */ + public void setMajorTicksSpacing(double spacing) { + mMajorTickSpacing = spacing; + } + + /** + * Returns the visual type at the specified index. + * + * @param index the index + * @return the visual type + */ + public Type getVisualTypeForIndex(int index) { + if (index < mVisualTypes.size()) { + return mVisualTypes.get(index); + } + return Type.NEEDLE; + } + + /** + * Sets the visual types. + * + * @param types the visual types + */ + public void setVisualTypes(Type[] types) { + mVisualTypes.clear(); + mVisualTypes.addAll(Arrays.asList(types)); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/renderer/SimpleSeriesRenderer.java b/android-libraries/achartengine/src/org/achartengine/renderer/SimpleSeriesRenderer.java new file mode 100644 index 00000000..0763fc58 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/renderer/SimpleSeriesRenderer.java @@ -0,0 +1,235 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.renderer; + +import java.io.Serializable; + +import android.graphics.Color; +import android.graphics.Paint.Align; + +/** + * A simple series renderer. + */ +public class SimpleSeriesRenderer implements Serializable { + /** The series color. */ + private int mColor = Color.BLUE; + /** If the values should be displayed above the chart points. */ + private boolean mDisplayChartValues; + /** The chart values text size. */ + private float mChartValuesTextSize = 10; + /** The chart values text alignment. */ + private Align mChartValuesTextAlign = Align.CENTER; + /** The chart values spacing from the data point. */ + private float mChartValuesSpacing = 5f; + /** The stroke style. */ + private BasicStroke mStroke; + /** If gradient is enabled. */ + private boolean mGradientEnabled = false; + /** The gradient start value. */ + private double mGradientStartValue; + /** The gradient start color. */ + private int mGradientStartColor; + /** The gradient stop value. */ + private double mGradientStopValue; + /** The gradient stop color. */ + private int mGradientStopColor; + + /** + * Returns the series color. + * + * @return the series color + */ + public int getColor() { + return mColor; + } + + /** + * Sets the series color. + * + * @param color the series color + */ + public void setColor(int color) { + mColor = color; + } + + /** + * Returns if the chart point values should be displayed as text. + * + * @return if the chart point values should be displayed as text + */ + public boolean isDisplayChartValues() { + return mDisplayChartValues; + } + + /** + * Sets if the chart point values should be displayed as text. + * + * @param display if the chart point values should be displayed as text + */ + public void setDisplayChartValues(boolean display) { + mDisplayChartValues = display; + } + + /** + * Returns the chart values text size. + * + * @return the chart values text size + */ + public float getChartValuesTextSize() { + return mChartValuesTextSize; + } + + /** + * Sets the chart values text size. + * + * @param textSize the chart values text size + */ + public void setChartValuesTextSize(float textSize) { + mChartValuesTextSize = textSize; + } + + /** + * Returns the chart values text align. + * + * @return the chart values text align + */ + public Align getChartValuesTextAlign() { + return mChartValuesTextAlign; + } + + /** + * Sets the chart values text align. + * + * @param align the chart values text align + */ + public void setChartValuesTextAlign(Align align) { + mChartValuesTextAlign = align; + } + + /** + * Returns the chart values spacing from the data point. + * + * @return the chart values spacing + */ + public float getChartValuesSpacing() { + return mChartValuesSpacing; + } + + /** + * Sets the chart values spacing from the data point. + * + * @param spacing the chart values spacing (in pixels) from the chart data + * point + */ + public void setChartValuesSpacing(float spacing) { + mChartValuesSpacing = spacing; + } + + /** + * Returns the stroke style. + * + * @return the stroke style + */ + public BasicStroke getStroke() { + return mStroke; + } + + /** + * Sets the stroke style. + * + * @param stroke the stroke style + */ + public void setStroke(BasicStroke stroke) { + mStroke = stroke; + } + + /** + * Returns the gradient is enabled value. + * + * @return the gradient enabled + */ + public boolean isGradientEnabled() { + return mGradientEnabled; + } + + /** + * Sets the gradient enabled value. + * + * @param enabled the gradient enabled + */ + public void setGradientEnabled(boolean enabled) { + mGradientEnabled = enabled; + } + + /** + * Returns the gradient start value. + * + * @return the gradient start value + */ + public double getGradientStartValue() { + return mGradientStartValue; + } + + /** + * Returns the gradient start color. + * + * @return the gradient start color + */ + public int getGradientStartColor() { + return mGradientStartColor; + } + + /** + * Sets the gradient start value and color. + * + * @param start the gradient start value + * @param color the gradient start color + */ + public void setGradientStart(double start, int color) { + mGradientStartValue = start; + mGradientStartColor = color; + } + + /** + * Returns the gradient stop value. + * + * @return the gradient stop value + */ + public double getGradientStopValue() { + return mGradientStopValue; + } + + /** + * Returns the gradient stop color. + * + * @return the gradient stop color + */ + public int getGradientStopColor() { + return mGradientStopColor; + } + + /** + * Sets the gradient stop value and color. + * + * @param start the gradient stop value + * @param color the gradient stop color + */ + public void setGradientStop(double start, int color) { + mGradientStopValue = start; + mGradientStopColor = color; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/renderer/XYMultipleSeriesRenderer.java b/android-libraries/achartengine/src/org/achartengine/renderer/XYMultipleSeriesRenderer.java new file mode 100644 index 00000000..64b34211 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/renderer/XYMultipleSeriesRenderer.java @@ -0,0 +1,1101 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.renderer; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.achartengine.util.MathHelper; + +import android.graphics.Color; +import android.graphics.Paint.Align; + +/** + * Multiple XY series renderer. + */ +public class XYMultipleSeriesRenderer extends DefaultRenderer { + /** The X axis title. */ + private String mXTitle = ""; + /** The Y axis title. */ + private String[] mYTitle; + /** The axis title text size. */ + private float mAxisTitleTextSize = 12; + /** The start value in the X axis range. */ + private double[] mMinX; + /** The end value in the X axis range. */ + private double[] mMaxX; + /** The start value in the Y axis range. */ + private double[] mMinY; + /** The end value in the Y axis range. */ + private double[] mMaxY; + /** The approximative number of labels on the x axis. */ + private int mXLabels = 5; + /** The approximative number of labels on the y axis. */ + private int mYLabels = 5; + /** The current orientation of the chart. */ + private Orientation mOrientation = Orientation.HORIZONTAL; + /** The X axis text labels. */ + private Map mXTextLabels = new HashMap(); + /** The Y axis text labels. */ + private Map> mYTextLabels = new LinkedHashMap>(); + /** A flag for enabling or not the pan on the X axis. */ + private boolean mPanXEnabled = true; + /** A flag for enabling or not the pan on the Y axis. */ + private boolean mPanYEnabled = true; + /** A flag for enabling or not the zoom on the X axis. */ + private boolean mZoomXEnabled = true; + /** A flag for enabling or not the zoom on the Y axis . */ + private boolean mZoomYEnabled = true; + /** The spacing between bars, in bar charts. */ + private double mBarSpacing = 0; + /** The margins colors. */ + private int mMarginsColor = NO_COLOR; + /** The pan limits. */ + private double[] mPanLimits; + /** The zoom limits. */ + private double[] mZoomLimits; + /** The X axis labels rotation angle. */ + private float mXLabelsAngle; + /** The Y axis labels rotation angle. */ + private float mYLabelsAngle; + /** The initial axis range. */ + private Map initialRange = new LinkedHashMap(); + /** The point size for charts displaying points. */ + private float mPointSize = 3; + /** The grid color. */ + private int mGridColor = Color.argb(75, 200, 200, 200); + /** The number of scales. */ + private int scalesCount; + /** The X axis labels alignment. */ + private Align xLabelsAlign = Align.CENTER; + /** The Y axis labels alignment. */ + private Align[] yLabelsAlign; + /** The Y axis alignment. */ + private Align[] yAxisAlign; + /** The X axis labels color. */ + private int mXLabelsColor = TEXT_COLOR; + /** The Y axis labels color. */ + private int[] mYLabelsColor = new int[] { TEXT_COLOR }; + /** + * If X axis value selection algorithm to be used. Only used by the time + * charts. + */ + private boolean mXRoundedLabels = true; + + /** + * An enum for the XY chart orientation of the X axis. + */ + public enum Orientation { + HORIZONTAL(0), VERTICAL(90); + /** The rotate angle. */ + private int mAngle = 0; + + private Orientation(int angle) { + mAngle = angle; + } + + /** + * Return the orientation rotate angle. + * + * @return the orientaion rotate angle + */ + public int getAngle() { + return mAngle; + } + } + + public XYMultipleSeriesRenderer() { + this(1); + } + + public XYMultipleSeriesRenderer(int scaleNumber) { + scalesCount = scaleNumber; + initAxesRange(scaleNumber); + } + + public void initAxesRange(int scales) { + mYTitle = new String[scales]; + yLabelsAlign = new Align[scales]; + yAxisAlign = new Align[scales]; + mYLabelsColor = new int[scales]; + mMinX = new double[scales]; + mMaxX = new double[scales]; + mMinY = new double[scales]; + mMaxY = new double[scales]; + for (int i = 0; i < scales; i++) { + mYLabelsColor[i] = TEXT_COLOR; + initAxesRangeForScale(i); + } + } + + public void initAxesRangeForScale(int i) { + mMinX[i] = MathHelper.NULL_VALUE; + mMaxX[i] = -MathHelper.NULL_VALUE; + mMinY[i] = MathHelper.NULL_VALUE; + mMaxY[i] = -MathHelper.NULL_VALUE; + double[] range = new double[] { mMinX[i], mMaxX[i], mMinY[i], mMaxY[i] }; + initialRange.put(i, range); + mYTitle[i] = ""; + mYTextLabels.put(i, new HashMap()); + yLabelsAlign[i] = Align.CENTER; + yAxisAlign[i] = Align.LEFT; + } + + /** + * Returns the current orientation of the chart X axis. + * + * @return the chart orientation + */ + public Orientation getOrientation() { + return mOrientation; + } + + /** + * Sets the current orientation of the chart X axis. + * + * @param orientation the chart orientation + */ + public void setOrientation(Orientation orientation) { + mOrientation = orientation; + } + + /** + * Returns the title for the X axis. + * + * @return the X axis title + */ + public String getXTitle() { + return mXTitle; + } + + /** + * Sets the title for the X axis. + * + * @param title the X axis title + */ + public void setXTitle(String title) { + mXTitle = title; + } + + /** + * Returns the title for the Y axis. + * + * @return the Y axis title + */ + public String getYTitle() { + return getYTitle(0); + } + + /** + * Returns the title for the Y axis. + * + * @param scale the renderer scale + * @return the Y axis title + */ + public String getYTitle(int scale) { + return mYTitle[scale]; + } + + /** + * Sets the title for the Y axis. + * + * @param title the Y axis title + */ + public void setYTitle(String title) { + setYTitle(title, 0); + } + + /** + * Sets the title for the Y axis. + * + * @param title the Y axis title + * @param scale the renderer scale + */ + public void setYTitle(String title, int scale) { + mYTitle[scale] = title; + } + + /** + * Returns the axis title text size. + * + * @return the axis title text size + */ + public float getAxisTitleTextSize() { + return mAxisTitleTextSize; + } + + /** + * Sets the axis title text size. + * + * @param textSize the chart axis text size + */ + public void setAxisTitleTextSize(float textSize) { + mAxisTitleTextSize = textSize; + } + + /** + * Returns the start value of the X axis range. + * + * @return the X axis range start value + */ + public double getXAxisMin() { + return getXAxisMin(0); + } + + /** + * Sets the start value of the X axis range. + * + * @param min the X axis range start value + */ + public void setXAxisMin(double min) { + setXAxisMin(min, 0); + } + + /** + * Returns if the minimum X value was set. + * + * @return the minX was set or not + */ + public boolean isMinXSet() { + return isMinXSet(0); + } + + /** + * Returns the end value of the X axis range. + * + * @return the X axis range end value + */ + public double getXAxisMax() { + return getXAxisMax(0); + } + + /** + * Sets the end value of the X axis range. + * + * @param max the X axis range end value + */ + public void setXAxisMax(double max) { + setXAxisMax(max, 0); + } + + /** + * Returns if the maximum X value was set. + * + * @return the maxX was set or not + */ + public boolean isMaxXSet() { + return isMaxXSet(0); + } + + /** + * Returns the start value of the Y axis range. + * + * @return the Y axis range end value + */ + public double getYAxisMin() { + return getYAxisMin(0); + } + + /** + * Sets the start value of the Y axis range. + * + * @param min the Y axis range start value + */ + public void setYAxisMin(double min) { + setYAxisMin(min, 0); + } + + /** + * Returns if the minimum Y value was set. + * + * @return the minY was set or not + */ + public boolean isMinYSet() { + return isMinYSet(0); + } + + /** + * Returns the end value of the Y axis range. + * + * @return the Y axis range end value + */ + public double getYAxisMax() { + return getYAxisMax(0); + } + + /** + * Sets the end value of the Y axis range. + * + * @param max the Y axis range end value + */ + public void setYAxisMax(double max) { + setYAxisMax(max, 0); + } + + /** + * Returns if the maximum Y value was set. + * + * @return the maxY was set or not + */ + public boolean isMaxYSet() { + return isMaxYSet(0); + } + + /** + * Returns the start value of the X axis range. + * + * @param scale the renderer scale + * @return the X axis range start value + */ + public double getXAxisMin(int scale) { + return mMinX[scale]; + } + + /** + * Sets the start value of the X axis range. + * + * @param min the X axis range start value + * @param scale the renderer scale + */ + public void setXAxisMin(double min, int scale) { + if (!isMinXSet(scale)) { + initialRange.get(scale)[0] = min; + } + mMinX[scale] = min; + } + + /** + * Returns if the minimum X value was set. + * + * @param scale the renderer scale + * @return the minX was set or not + */ + public boolean isMinXSet(int scale) { + return mMinX[scale] != MathHelper.NULL_VALUE; + } + + /** + * Returns the end value of the X axis range. + * + * @param scale the renderer scale + * @return the X axis range end value + */ + public double getXAxisMax(int scale) { + return mMaxX[scale]; + } + + /** + * Sets the end value of the X axis range. + * + * @param max the X axis range end value + * @param scale the renderer scale + */ + public void setXAxisMax(double max, int scale) { + if (!isMaxXSet(scale)) { + initialRange.get(scale)[1] = max; + } + mMaxX[scale] = max; + } + + /** + * Returns if the maximum X value was set. + * + * @param scale the renderer scale + * @return the maxX was set or not + */ + public boolean isMaxXSet(int scale) { + return mMaxX[scale] != -MathHelper.NULL_VALUE; + } + + /** + * Returns the start value of the Y axis range. + * + * @param scale the renderer scale + * @return the Y axis range end value + */ + public double getYAxisMin(int scale) { + return mMinY[scale]; + } + + /** + * Sets the start value of the Y axis range. + * + * @param min the Y axis range start value + * @param scale the renderer scale + */ + public void setYAxisMin(double min, int scale) { + if (!isMinYSet(scale)) { + initialRange.get(scale)[2] = min; + } + mMinY[scale] = min; + } + + /** + * Returns if the minimum Y value was set. + * + * @param scale the renderer scale + * @return the minY was set or not + */ + public boolean isMinYSet(int scale) { + return mMinY[scale] != MathHelper.NULL_VALUE; + } + + /** + * Returns the end value of the Y axis range. + * + * @param scale the renderer scale + * @return the Y axis range end value + */ + public double getYAxisMax(int scale) { + return mMaxY[scale]; + } + + /** + * Sets the end value of the Y axis range. + * + * @param max the Y axis range end value + * @param scale the renderer scale + */ + public void setYAxisMax(double max, int scale) { + if (!isMaxYSet(scale)) { + initialRange.get(scale)[3] = max; + } + mMaxY[scale] = max; + } + + /** + * Returns if the maximum Y value was set. + * + * @param scale the renderer scale + * @return the maxY was set or not + */ + public boolean isMaxYSet(int scale) { + return mMaxY[scale] != -MathHelper.NULL_VALUE; + } + + /** + * Returns the approximate number of labels for the X axis. + * + * @return the approximate number of labels for the X axis + */ + public int getXLabels() { + return mXLabels; + } + + /** + * Sets the approximate number of labels for the X axis. + * + * @param xLabels the approximate number of labels for the X axis + */ + public void setXLabels(int xLabels) { + mXLabels = xLabels; + } + + /** + * Adds a new text label for the specified X axis value. + * + * @param x the X axis value + * @param text the text label + * @deprecated use addXTextLabel instead + */ + public void addTextLabel(double x, String text) { + addXTextLabel(x, text); + } + + /** + * Adds a new text label for the specified X axis value. + * + * @param x the X axis value + * @param text the text label + */ + public void addXTextLabel(double x, String text) { + mXTextLabels.put(x, text); + } + + /** + * Returns the X axis text label at the specified X axis value. + * + * @param x the X axis value + * @return the X axis text label + */ + public String getXTextLabel(Double x) { + return mXTextLabels.get(x); + } + + /** + * Returns the X text label locations. + * + * @return the X text label locations + */ + public Double[] getXTextLabelLocations() { + return mXTextLabels.keySet().toArray(new Double[0]); + } + + /** + * Clears the existing text labels. + * + * @deprecated use clearXTextLabels instead + */ + public void clearTextLabels() { + clearXTextLabels(); + } + + /** + * Clears the existing text labels on the X axis. + */ + public void clearXTextLabels() { + mXTextLabels.clear(); + } + + /** + * If X axis labels should be rounded. + * + * @return if rounded time values to be used + */ + public boolean isXRoundedLabels() { + return mXRoundedLabels; + } + + /** + * Sets if X axis rounded time values to be used. + * + * @param rounded rounded values to be used + */ + public void setXRoundedLabels(boolean rounded) { + mXRoundedLabels = rounded; + } + + /** + * Adds a new text label for the specified Y axis value. + * + * @param y the Y axis value + * @param text the text label + */ + public void addYTextLabel(double y, String text) { + addYTextLabel(y, text, 0); + } + + /** + * Adds a new text label for the specified Y axis value. + * + * @param y the Y axis value + * @param text the text label + * @param scale the renderer scale + */ + public void addYTextLabel(double y, String text, int scale) { + mYTextLabels.get(scale).put(y, text); + } + + /** + * Returns the Y axis text label at the specified Y axis value. + * + * @param y the Y axis value + * @return the Y axis text label + */ + public String getYTextLabel(Double y) { + return getYTextLabel(y, 0); + } + + /** + * Returns the Y axis text label at the specified Y axis value. + * + * @param y the Y axis value + * @param scale the renderer scale + * @return the Y axis text label + */ + public String getYTextLabel(Double y, int scale) { + return mYTextLabels.get(scale).get(y); + } + + /** + * Returns the Y text label locations. + * + * @return the Y text label locations + */ + public Double[] getYTextLabelLocations() { + return getYTextLabelLocations(0); + } + + /** + * Returns the Y text label locations. + * + * @param scale the renderer scale + * @return the Y text label locations + */ + public Double[] getYTextLabelLocations(int scale) { + return mYTextLabels.get(scale).keySet().toArray(new Double[0]); + } + + /** + * Clears the existing text labels on the Y axis. + */ + public void clearYTextLabels() { + clearYTextLabels(0); + } + + /** + * Clears the existing text labels on the Y axis. + * + * @param scale the renderer scale + */ + public void clearYTextLabels(int scale) { + mYTextLabels.get(scale).clear(); + } + + /** + * Returns the approximate number of labels for the Y axis. + * + * @return the approximate number of labels for the Y axis + */ + public int getYLabels() { + return mYLabels; + } + + /** + * Sets the approximate number of labels for the Y axis. + * + * @param yLabels the approximate number of labels for the Y axis + */ + public void setYLabels(int yLabels) { + mYLabels = yLabels; + } + + /** + * Sets if the chart point values should be displayed as text. + * + * @param display if the chart point values should be displayed as text + * @deprecated use SimpleSeriesRenderer.setDisplayChartValues() instead + */ + public void setDisplayChartValues(boolean display) { + SimpleSeriesRenderer[] renderers = getSeriesRenderers(); + for (SimpleSeriesRenderer renderer : renderers) { + renderer.setDisplayChartValues(display); + } + } + + /** + * Sets the chart values text size. + * + * @param textSize the chart values text size + * @deprecated use SimpleSeriesRenderer.setChartValuesTextSize() instead + */ + public void setChartValuesTextSize(float textSize) { + SimpleSeriesRenderer[] renderers = getSeriesRenderers(); + for (SimpleSeriesRenderer renderer : renderers) { + renderer.setChartValuesTextSize(textSize); + } + } + + /** + * Returns the enabled state of the pan on at least one axis. + * + * @return if pan is enabled + */ + public boolean isPanEnabled() { + return isPanXEnabled() || isPanYEnabled(); + } + + /** + * Returns the enabled state of the pan on X axis. + * + * @return if pan is enabled on X axis + */ + public boolean isPanXEnabled() { + return mPanXEnabled; + } + + /** + * Returns the enabled state of the pan on Y axis. + * + * @return if pan is enabled on Y axis + */ + public boolean isPanYEnabled() { + return mPanYEnabled; + } + + /** + * Sets the enabled state of the pan. + * + * @param enabledX pan enabled on X axis + * @param enabledY pan enabled on Y axis + */ + public void setPanEnabled(boolean enabledX, boolean enabledY) { + mPanXEnabled = enabledX; + mPanYEnabled = enabledY; + } + + /** + * Returns the enabled state of the zoom on at least one axis. + * + * @return if zoom is enabled + */ + public boolean isZoomEnabled() { + return isZoomXEnabled() || isZoomYEnabled(); + } + + /** + * Returns the enabled state of the zoom on X axis. + * + * @return if zoom is enabled on X axis + */ + public boolean isZoomXEnabled() { + return mZoomXEnabled; + } + + /** + * Returns the enabled state of the zoom on Y axis. + * + * @return if zoom is enabled on Y axis + */ + public boolean isZoomYEnabled() { + return mZoomYEnabled; + } + + /** + * Sets the enabled state of the zoom. + * + * @param enabledX zoom enabled on X axis + * @param enabledY zoom enabled on Y axis + */ + public void setZoomEnabled(boolean enabledX, boolean enabledY) { + mZoomXEnabled = enabledX; + mZoomYEnabled = enabledY; + } + + /** + * Returns the spacing between bars, in bar charts. + * + * @return the spacing between bars + * @deprecated use getBarSpacing instead + */ + public double getBarsSpacing() { + return getBarSpacing(); + } + + /** + * Returns the spacing between bars, in bar charts. + * + * @return the spacing between bars + */ + public double getBarSpacing() { + return mBarSpacing; + } + + /** + * Sets the spacing between bars, in bar charts. Only available for bar + * charts. This is a coefficient of the bar width. For instance, if you want + * the spacing to be a half of the bar width, set this value to 0.5. + * + * @param spacing the spacing between bars coefficient + */ + public void setBarSpacing(double spacing) { + mBarSpacing = spacing; + } + + /** + * Returns the margins color. + * + * @return the margins color + */ + public int getMarginsColor() { + return mMarginsColor; + } + + /** + * Sets the color of the margins. + * + * @param color the margins color + */ + public void setMarginsColor(int color) { + mMarginsColor = color; + } + + /** + * Returns the grid color. + * + * @return the grid color + */ + public int getGridColor() { + return mGridColor; + } + + /** + * Sets the color of the grid. + * + * @param color the grid color + */ + public void setGridColor(int color) { + mGridColor = color; + } + + /** + * Returns the pan limits. + * + * @return the pan limits + */ + public double[] getPanLimits() { + return mPanLimits; + } + + /** + * Sets the pan limits as an array of 4 values. Setting it to null or a + * different size array will disable the panning limitation. Values: + * [panMinimumX, panMaximumX, panMinimumY, panMaximumY] + * + * @param panLimits the pan limits + */ + public void setPanLimits(double[] panLimits) { + mPanLimits = panLimits; + } + + /** + * Returns the zoom limits. + * + * @return the zoom limits + */ + public double[] getZoomLimits() { + return mZoomLimits; + } + + /** + * Sets the zoom limits as an array of 4 values. Setting it to null or a + * different size array will disable the zooming limitation. Values: + * [zoomMinimumX, zoomMaximumX, zoomMinimumY, zoomMaximumY] + * + * @param zoomLimits the zoom limits + */ + public void setZoomLimits(double[] zoomLimits) { + mZoomLimits = zoomLimits; + } + + /** + * Returns the rotation angle of labels for the X axis. + * + * @return the rotation angle of labels for the X axis + */ + public float getXLabelsAngle() { + return mXLabelsAngle; + } + + /** + * Sets the rotation angle (in degrees) of labels for the X axis. + * + * @param angle the rotation angle of labels for the X axis + */ + public void setXLabelsAngle(float angle) { + mXLabelsAngle = angle; + } + + /** + * Returns the rotation angle of labels for the Y axis. + * + * @return the approximate number of labels for the Y axis + */ + public float getYLabelsAngle() { + return mYLabelsAngle; + } + + /** + * Sets the rotation angle (in degrees) of labels for the Y axis. + * + * @param angle the rotation angle of labels for the Y axis + */ + public void setYLabelsAngle(float angle) { + mYLabelsAngle = angle; + } + + /** + * Returns the size of the points, for charts displaying points. + * + * @return the point size + */ + public float getPointSize() { + return mPointSize; + } + + /** + * Sets the size of the points, for charts displaying points. + * + * @param size the point size + */ + public void setPointSize(float size) { + mPointSize = size; + } + + public void setRange(double[] range) { + setRange(range, 0); + } + + /** + * Sets the axes range values. + * + * @param range an array having the values in this order: minX, maxX, minY, + * maxY + * @param scale the renderer scale + */ + public void setRange(double[] range, int scale) { + setXAxisMin(range[0], scale); + setXAxisMax(range[1], scale); + setYAxisMin(range[2], scale); + setYAxisMax(range[3], scale); + } + + public boolean isInitialRangeSet() { + return isInitialRangeSet(0); + } + + /** + * Returns if the initial range is set. + * + * @param scale the renderer scale + * @return the initial range was set or not + */ + public boolean isInitialRangeSet(int scale) { + return initialRange.get(scale) != null; + } + + /** + * Returns the initial range. + * + * @return the initial range + */ + public double[] getInitialRange() { + return getInitialRange(0); + } + + /** + * Returns the initial range. + * + * @param scale the renderer scale + * @return the initial range + */ + public double[] getInitialRange(int scale) { + return initialRange.get(scale); + } + + /** + * Sets the axes initial range values. This will be used in the zoom fit tool. + * + * @param range an array having the values in this order: minX, maxX, minY, + * maxY + */ + public void setInitialRange(double[] range) { + setInitialRange(range, 0); + } + + /** + * Sets the axes initial range values. This will be used in the zoom fit tool. + * + * @param range an array having the values in this order: minX, maxX, minY, + * maxY + * @param scale the renderer scale + */ + public void setInitialRange(double[] range, int scale) { + initialRange.put(scale, range); + } + + /** + * Returns the X axis labels color. + * + * @return the X axis labels color + */ + public int getXLabelsColor() { + return mXLabelsColor; + } + + /** + * Returns the Y axis labels color. + * + * @return the Y axis labels color + */ + public int getYLabelsColor(int scale) { + return mYLabelsColor[scale]; + } + + /** + * Sets the X axis labels color. + * + * @param color the X axis labels color + */ + public void setXLabelsColor(int color) { + mXLabelsColor = color; + } + + /** + * Sets the Y axis labels color. + * + * @param scale the renderer scale + * @param color the Y axis labels color + */ + public void setYLabelsColor(int scale, int color) { + mYLabelsColor[scale] = color; + } + + /** + * Returns the X axis labels alignment. + * + * @return X labels alignment + */ + public Align getXLabelsAlign() { + return xLabelsAlign; + } + + /** + * Sets the X axis labels alignment. + * + * @param align the X labels alignment + */ + public void setXLabelsAlign(Align align) { + xLabelsAlign = align; + } + + /** + * Returns the Y axis labels alignment. + * + * @param scale the renderer scale + * @return Y labels alignment + */ + public Align getYLabelsAlign(int scale) { + return yLabelsAlign[scale]; + } + + public void setYLabelsAlign(Align align) { + setYLabelsAlign(align, 0); + } + + public Align getYAxisAlign(int scale) { + return yAxisAlign[scale]; + } + + public void setYAxisAlign(Align align, int scale) { + yAxisAlign[scale] = align; + } + + /** + * Sets the Y axis labels alignment. + * + * @param align the Y labels alignment + */ + public void setYLabelsAlign(Align align, int scale) { + yLabelsAlign[scale] = align; + } + + public int getScalesCount() { + return scalesCount; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/renderer/XYSeriesRenderer.java b/android-libraries/achartengine/src/org/achartengine/renderer/XYSeriesRenderer.java new file mode 100644 index 00000000..42e4808e --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/renderer/XYSeriesRenderer.java @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.renderer; + +import org.achartengine.chart.PointStyle; + +import android.graphics.Color; + +/** + * A renderer for the XY type series. + */ +public class XYSeriesRenderer extends SimpleSeriesRenderer { + /** If the chart points should be filled. */ + private boolean mFillPoints = false; + /** If the chart should be filled below its line. */ + private boolean mFillBelowLine = false; + /** The fill below the chart line color. */ + private int mFillColor = Color.argb(125, 0, 0, 200); + /** The point style. */ + private PointStyle mPointStyle = PointStyle.POINT; + /** The chart line width. */ + private float mLineWidth = 1; + + /** + * Returns if the chart should be filled below the line. + * + * @return the fill below line status + */ + public boolean isFillBelowLine() { + return mFillBelowLine; + } + + /** + * Sets if the line chart should be filled below its line. Filling below the + * line transforms a line chart into an area chart. + * + * @param fill the fill below line flag value + */ + public void setFillBelowLine(boolean fill) { + mFillBelowLine = fill; + } + + /** + * Returns if the chart points should be filled. + * + * @return the points fill status + */ + public boolean isFillPoints() { + return mFillPoints; + } + + /** + * Sets if the chart points should be filled. + * + * @param fill the points fill flag value + */ + public void setFillPoints(boolean fill) { + mFillPoints = fill; + } + + /** + * Returns the fill below line color. + * + * @return the fill below line color + */ + public int getFillBelowLineColor() { + return mFillColor; + } + + /** + * Sets the fill below the line color. + * + * @param color the fill below line color + */ + public void setFillBelowLineColor(int color) { + mFillColor = color; + } + + /** + * Returns the point style. + * + * @return the point style + */ + public PointStyle getPointStyle() { + return mPointStyle; + } + + /** + * Sets the point style. + * + * @param style the point style + */ + public void setPointStyle(PointStyle style) { + mPointStyle = style; + } + + /** + * Returns the chart line width. + * + * @return the line width + */ + public float getLineWidth() { + return mLineWidth; + } + + /** + * Sets the chart line width. + * + * @param lineWidth the line width + */ + public void setLineWidth(float lineWidth) { + mLineWidth = lineWidth; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/renderer/package.html b/android-libraries/achartengine/src/org/achartengine/renderer/package.html new file mode 100644 index 00000000..c9db0a45 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/renderer/package.html @@ -0,0 +1,6 @@ + +AChartEngine + +Provides renderer classes that keep the chart rendering / drawing styles. + + \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/tools/AbstractTool.java b/android-libraries/achartengine/src/org/achartengine/tools/AbstractTool.java new file mode 100644 index 00000000..99841ed7 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/tools/AbstractTool.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.tools; + +import org.achartengine.chart.AbstractChart; +import org.achartengine.chart.XYChart; +import org.achartengine.renderer.XYMultipleSeriesRenderer; + +/** + * Abstract class for being extended by graphical view tools. + */ +public abstract class AbstractTool { + /** The chart. */ + protected AbstractChart mChart; + /** The renderer. */ + protected XYMultipleSeriesRenderer mRenderer; + + /** + * Abstract tool constructor. + * + * @param chart the chart + */ + public AbstractTool(AbstractChart chart) { + mChart = chart; + if (chart instanceof XYChart) { + mRenderer = ((XYChart) chart).getRenderer(); + } + } + + /** + * Returns the current chart range. + * + * @param scale the scale + * @return the chart range + */ + public double[] getRange(int scale) { + double minX = mRenderer.getXAxisMin(scale); + double maxX = mRenderer.getXAxisMax(scale); + double minY = mRenderer.getYAxisMin(scale); + double maxY = mRenderer.getYAxisMax(scale); + return new double[] { minX, maxX, minY, maxY }; + } + + /** + * Sets the range to the calculated one, if not already set. + * + * @param range the range + * @param scale the scale + */ + public void checkRange(double[] range, int scale) { + if (mChart instanceof XYChart) { + double[] calcRange = ((XYChart) mChart).getCalcRange(scale); + if (calcRange != null) { + if (!mRenderer.isMinXSet(scale)) { + range[0] = calcRange[0]; + mRenderer.setXAxisMin(range[0], scale); + } + if (!mRenderer.isMaxXSet(scale)) { + range[1] = calcRange[1]; + mRenderer.setXAxisMax(range[1], scale); + } + if (!mRenderer.isMinYSet(scale)) { + range[2] = calcRange[2]; + mRenderer.setYAxisMin(range[2], scale); + } + if (!mRenderer.isMaxYSet(scale)) { + range[3] = calcRange[3]; + mRenderer.setYAxisMax(range[3], scale); + } + } + } + } + + /** + * Sets a new range on the X axis. + * + * @param min the minimum value + * @param max the maximum value + * @param scale the scale + */ + protected void setXRange(double min, double max, int scale) { + mRenderer.setXAxisMin(min, scale); + mRenderer.setXAxisMax(max, scale); + } + + /** + * Sets a new range on the Y axis. + * + * @param min the minimum value + * @param max the maximum value + * @param scale the scale + */ + protected void setYRange(double min, double max, int scale) { + mRenderer.setYAxisMin(min, scale); + mRenderer.setYAxisMax(max, scale); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/tools/FitZoom.java b/android-libraries/achartengine/src/org/achartengine/tools/FitZoom.java new file mode 100644 index 00000000..92f67b84 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/tools/FitZoom.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.tools; + +import org.achartengine.chart.AbstractChart; +import org.achartengine.chart.RoundChart; +import org.achartengine.chart.XYChart; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.util.MathHelper; + +public class FitZoom extends AbstractTool { + /** + * Builds an instance of the fit zoom tool. + * + * @param chart the XY chart + */ + public FitZoom(AbstractChart chart) { + super(chart); + } + + /** + * Apply the tool. + */ + public void apply() { + if (mChart instanceof XYChart) { + if (((XYChart) mChart).getDataset() == null) { + return; + } + int scales = mRenderer.getScalesCount(); + if (mRenderer.isInitialRangeSet()) { + for (int i = 0; i < scales; i++) { + if (mRenderer.isInitialRangeSet(i)) { + mRenderer.setRange(mRenderer.getInitialRange(i), i); + } + } + } else { + XYSeries[] series = ((XYChart) mChart).getDataset().getSeries(); + double[] range = null; + int length = series.length; + if (length > 0) { + for (int i = 0; i < scales; i++) { + range = new double[] { MathHelper.NULL_VALUE, -MathHelper.NULL_VALUE, + MathHelper.NULL_VALUE, -MathHelper.NULL_VALUE }; + for (int j = 0; j < length; j++) { + if (i == series[j].getScaleNumber()) { + range[0] = Math.min(range[0], series[j].getMinX()); + range[1] = Math.max(range[1], series[j].getMaxX()); + range[2] = Math.min(range[2], series[j].getMinY()); + range[3] = Math.max(range[3], series[j].getMaxY()); + } + } + double marginX = Math.abs(range[1] - range[0]) / 40; + double marginY = Math.abs(range[3] - range[2]) / 40; + mRenderer.setRange(new double[] { range[0] - marginX, range[1] + marginX, + range[2] - marginY, range[3] + marginY }, i); + } + } + } + } else { + DefaultRenderer renderer = ((RoundChart) mChart).getRenderer(); + renderer.setScale(renderer.getOriginalScale()); + } + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/tools/Pan.java b/android-libraries/achartengine/src/org/achartengine/tools/Pan.java new file mode 100644 index 00000000..2d4ea28e --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/tools/Pan.java @@ -0,0 +1,163 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.tools; + +import java.util.ArrayList; +import java.util.List; + +import org.achartengine.chart.AbstractChart; +import org.achartengine.chart.RoundChart; +import org.achartengine.chart.XYChart; + +/** + * The pan tool. + */ +public class Pan extends AbstractTool { + /** The pan listeners. */ + private List mPanListeners = new ArrayList(); + /** Pan limits reached on the X axis. */ + private boolean limitsReachedX = false; + /** Pan limits reached on the X axis. */ + private boolean limitsReachedY = false; + + /** + * Builds and instance of the pan tool. + * + * @param chart the XY chart + */ + public Pan(AbstractChart chart) { + super(chart); + } + + /** + * Apply the tool. + * + * @param oldX the previous location on X axis + * @param oldY the previous location on Y axis + * @param newX the current location on X axis + * @param newY the current location on the Y axis + */ + public void apply(float oldX, float oldY, float newX, float newY) { + boolean notLimitedUp = true; + boolean notLimitedBottom = true; + boolean notLimitedLeft = true; + boolean notLimitedRight = true; + if (mChart instanceof XYChart) { + int scales = mRenderer.getScalesCount(); + double[] limits = mRenderer.getPanLimits(); + boolean limited = limits != null && limits.length == 4; + XYChart chart = (XYChart) mChart; + for (int i = 0; i < scales; i++) { + double[] range = getRange(i); + double[] calcRange = chart.getCalcRange(i); + if (limitsReachedX + && limitsReachedY + && (range[0] == range[1] && calcRange[0] == calcRange[1] || range[2] == range[3] + && calcRange[2] == calcRange[3])) { + return; + } + checkRange(range, i); + + double[] realPoint = chart.toRealPoint(oldX, oldY, i); + double[] realPoint2 = chart.toRealPoint(newX, newY, i); + double deltaX = realPoint[0] - realPoint2[0]; + double deltaY = realPoint[1] - realPoint2[1]; + double ratio = getAxisRatio(range); + if (chart.isVertical(mRenderer)) { + double newDeltaX = -deltaY * ratio; + double newDeltaY = deltaX / ratio; + deltaX = newDeltaX; + deltaY = newDeltaY; + } + if (mRenderer.isPanXEnabled()) { + if (limits != null) { + if (notLimitedLeft) { + notLimitedLeft = limits[0] <= range[0] + deltaX; + } + if (notLimitedRight) { + notLimitedRight = limits[1] >= range[1] + deltaX; + } + } + if (!limited || (notLimitedLeft && notLimitedRight)) { + setXRange(range[0] + deltaX, range[1] + deltaX, i); + limitsReachedX = false; + } else { + limitsReachedX = true; + } + } + if (mRenderer.isPanYEnabled()) { + if (limits != null) { + if (notLimitedBottom) { + notLimitedBottom = limits[2] <= range[2] + deltaY; + } + if (notLimitedUp) { + notLimitedUp = limits[3] >= range[3] + deltaY; + } + } + if (!limited || (notLimitedBottom && notLimitedUp)) { + setYRange(range[2] + deltaY, range[3] + deltaY, i); + limitsReachedY = false; + } else { + limitsReachedY = true; + } + } + } + } else { + RoundChart chart = (RoundChart) mChart; + chart.setCenterX(chart.getCenterX() + (int) (newX - oldX)); + chart.setCenterY(chart.getCenterY() + (int) (newY - oldY)); + } + notifyPanListeners(); + } + + /** + * Return the X / Y axis range ratio. + * + * @param range the axis range + * @return the ratio + */ + private double getAxisRatio(double[] range) { + return Math.abs(range[1] - range[0]) / Math.abs(range[3] - range[2]); + } + + /** + * Notify the pan listeners about a pan. + */ + private synchronized void notifyPanListeners() { + for (PanListener listener : mPanListeners) { + listener.panApplied(); + } + } + + /** + * Adds a new pan listener. + * + * @param listener pan listener + */ + public synchronized void addPanListener(PanListener listener) { + mPanListeners.add(listener); + } + + /** + * Removes a pan listener. + * + * @param listener pan listener + */ + public synchronized void removePanListener(PanListener listener) { + mPanListeners.add(listener); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/tools/PanListener.java b/android-libraries/achartengine/src/org/achartengine/tools/PanListener.java new file mode 100644 index 00000000..d3d136c0 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/tools/PanListener.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.tools; + +/** + * A pan listener. + */ +public interface PanListener { + + /** + * Called when a pan change is triggered. + */ + void panApplied(); + +} diff --git a/android-libraries/achartengine/src/org/achartengine/tools/Zoom.java b/android-libraries/achartengine/src/org/achartengine/tools/Zoom.java new file mode 100644 index 00000000..0abee92c --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/tools/Zoom.java @@ -0,0 +1,189 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.tools; + +import java.util.ArrayList; +import java.util.List; + +import org.achartengine.chart.AbstractChart; +import org.achartengine.chart.RoundChart; +import org.achartengine.chart.XYChart; +import org.achartengine.renderer.DefaultRenderer; + +/** + * The zoom tool. + */ +public class Zoom extends AbstractTool { + /** A flag to be used to know if this is a zoom in or out. */ + private boolean mZoomIn; + /** The zoom rate. */ + private float mZoomRate; + /** The zoom listeners. */ + private List mZoomListeners = new ArrayList(); + /** Zoom limits reached on the X axis. */ + private boolean limitsReachedX = false; + /** Zoom limits reached on the Y axis. */ + private boolean limitsReachedY = false; + + /** Zoom on X axis and Y axis */ + public static final int ZOOM_AXIS_XY = 0; + /** Zoom on X axis independently */ + public static final int ZOOM_AXIS_X = 1; + /** Zoom on Y axis independently */ + public static final int ZOOM_AXIS_Y = 2; + + + /** + * Builds the zoom tool. + * + * @param chart the chart + * @param in zoom in or out + * @param rate the zoom rate + */ + public Zoom(AbstractChart chart, boolean in, float rate) { + super(chart); + mZoomIn = in; + setZoomRate(rate); + } + + /** + * Sets the zoom rate. + * + * @param rate + */ + public void setZoomRate(float rate) { + mZoomRate = rate; + } + + /** + * Apply the zoom. + */ + public void apply(int zoom_axis) { + if (mChart instanceof XYChart) { + int scales = mRenderer.getScalesCount(); + for (int i = 0; i < scales; i++) { + double[] range = getRange(i); + checkRange(range, i); + double[] limits = mRenderer.getZoomLimits(); + + double centerX = (range[0] + range[1]) / 2; + double centerY = (range[2] + range[3]) / 2; + double newWidth = range[1] - range[0]; + double newHeight = range[3] - range[2]; + double newXMin = centerX - newWidth / 2; + double newXMax = centerX + newWidth / 2; + double newYMin = centerY - newHeight / 2; + double newYMax = centerY + newHeight / 2; + + // if already reached last zoom, then it will always set to reached + if (i == 0) { + limitsReachedX = limits != null && (newXMin <= limits[0] || newXMax >= limits[1]); + limitsReachedY = limits != null && (newYMin <= limits[2] || newYMax >= limits[3]); + } + + if (mZoomIn) { + if (mRenderer.isZoomXEnabled() && // zoom in on X axis + (zoom_axis == ZOOM_AXIS_X || zoom_axis == ZOOM_AXIS_XY)) { + if (limitsReachedX && mZoomRate < 1) { + // ignore pinch zoom out once reached X limit + } else { + newWidth /= mZoomRate; + } + } + + if (mRenderer.isZoomYEnabled() && // zoom in on Y axis + (zoom_axis == ZOOM_AXIS_Y || zoom_axis == ZOOM_AXIS_XY)) { + if (limitsReachedY && mZoomRate < 1) { + } else { + newHeight /= mZoomRate; + } + } + } else { + if (mRenderer.isZoomXEnabled() && !limitsReachedX && // zoom out on X axis + (zoom_axis == ZOOM_AXIS_X || zoom_axis == ZOOM_AXIS_XY)) { + newWidth *= mZoomRate; + } + + if (mRenderer.isZoomYEnabled() && !limitsReachedY && // zoom out on Y axis + (zoom_axis == ZOOM_AXIS_Y || zoom_axis == ZOOM_AXIS_XY)) { + newHeight *= mZoomRate; + } + } + + if (mRenderer.isZoomXEnabled() && + (zoom_axis == ZOOM_AXIS_X || zoom_axis == ZOOM_AXIS_XY)) { + newXMin = centerX - newWidth / 2; + newXMax = centerX + newWidth / 2; + setXRange(newXMin, newXMax, i); + } + if (mRenderer.isZoomYEnabled() && + (zoom_axis == ZOOM_AXIS_Y || zoom_axis == ZOOM_AXIS_XY)) { + newYMin = centerY - newHeight / 2; + newYMax = centerY + newHeight / 2; + setYRange(newYMin, newYMax, i); + } + } + } else { + DefaultRenderer renderer = ((RoundChart) mChart).getRenderer(); + if (mZoomIn) { + renderer.setScale(renderer.getScale() * mZoomRate); + } else { + renderer.setScale(renderer.getScale() / mZoomRate); + } + } + notifyZoomListeners(new ZoomEvent(mZoomIn, mZoomRate)); + } + + + /** + * Notify the zoom listeners about a zoom change. + * + * @param e the zoom event + */ + private synchronized void notifyZoomListeners(ZoomEvent e) { + for (ZoomListener listener : mZoomListeners) { + listener.zoomApplied(e); + } + } + + /** + * Notify the zoom listeners about a zoom reset. + */ + public synchronized void notifyZoomResetListeners() { + for (ZoomListener listener : mZoomListeners) { + listener.zoomReset(); + } + } + + /** + * Adds a new zoom listener. + * + * @param listener zoom listener + */ + public synchronized void addZoomListener(ZoomListener listener) { + mZoomListeners.add(listener); + } + + /** + * Removes a zoom listener. + * + * @param listener zoom listener + */ + public synchronized void removeZoomListener(ZoomListener listener) { + mZoomListeners.add(listener); + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/tools/ZoomEvent.java b/android-libraries/achartengine/src/org/achartengine/tools/ZoomEvent.java new file mode 100644 index 00000000..bd8fb686 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/tools/ZoomEvent.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.tools; + + +/** + * A zoom event. + */ +public class ZoomEvent { + /** A flag to be used to know if this is a zoom in or out. */ + private boolean mZoomIn; + /** The zoom rate. */ + private float mZoomRate; + + /** + * Builds the zoom tool. + * + * @param in zoom in or out + * @param rate the zoom rate + */ + public ZoomEvent(boolean in, float rate) { + mZoomIn = in; + mZoomRate = rate; + } + + /** + * Returns the zoom type. + * + * @return true if zoom in, false otherwise + */ + public boolean isZoomIn() { + return mZoomIn; + } + + /** + * Returns the zoom rate. + * + * @return the zoom rate + */ + public float getZoomRate() { + return mZoomRate; + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/tools/ZoomListener.java b/android-libraries/achartengine/src/org/achartengine/tools/ZoomListener.java new file mode 100644 index 00000000..4827483e --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/tools/ZoomListener.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.tools; + +/** + * A zoom listener. + */ +public interface ZoomListener { + + /** + * Called when a zoom change is triggered. + * @param e the zoom event + */ + void zoomApplied(ZoomEvent e); + + /** + * Called when a zoom reset is done. + */ + void zoomReset(); +} diff --git a/android-libraries/achartengine/src/org/achartengine/util/IndexXYMap.java b/android-libraries/achartengine/src/org/achartengine/util/IndexXYMap.java new file mode 100644 index 00000000..f9572622 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/util/IndexXYMap.java @@ -0,0 +1,108 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.TreeMap; + +/** + * This class requires sorted x values + */ +public class IndexXYMap extends TreeMap { + private final List indexList = new ArrayList(); + + private double maxXDifference = 0; + + public IndexXYMap() { + super(); + } + + public V put(K key, V value) { + indexList.add(key); + updateMaxXDifference(); + return super.put(key, value); + } + + private void updateMaxXDifference() { + if (indexList.size() < 2) { + maxXDifference = 0; + return; + } + + if (Math.abs((Double) indexList.get(indexList.size() - 1) + - (Double) indexList.get(indexList.size() - 2)) > maxXDifference) + maxXDifference = Math.abs((Double) indexList.get(indexList.size() - 1) + - (Double) indexList.get(indexList.size() - 2)); + } + + public double getMaxXDifference() { + return maxXDifference; + } + + public void clear() { + updateMaxXDifference(); + super.clear(); + indexList.clear(); + } + + /** + * Returns X-value according to the given index + * + * @param index + * @return the X value + */ + public K getXByIndex(int index) { + return indexList.get(index); + } + + /** + * Returns Y-value according to the given index + * + * @param index + * @return the Y value + */ + public V getYByIndex(int index) { + K key = indexList.get(index); + return this.get(key); + } + + /** + * Returns XY-entry according to the given index + * + * @param index + * @return the X and Y values + */ + public XYEntry getByIndex(int index) { + K key = indexList.get(index); + return new XYEntry(key, this.get(key)); + } + + /** + * Removes entry from map by index + * + * @param index + */ + public XYEntry removeByIndex(int index) { + K key = indexList.remove(index); + return new XYEntry(key, this.remove(key)); + } + + public int getIndexForKey(K key) { + return Collections.binarySearch(indexList, key, null); + } +} diff --git a/android-libraries/achartengine/src/org/achartengine/util/MathHelper.java b/android-libraries/achartengine/src/org/achartengine/util/MathHelper.java new file mode 100644 index 00000000..f5b893bf --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/util/MathHelper.java @@ -0,0 +1,174 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.util; + +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class for math operations. + */ +public class MathHelper { + /** A value that is used a null value. */ + public static final double NULL_VALUE = Double.MAX_VALUE; + /** + * A number formatter to be used to make sure we have a maximum number of + * fraction digits in the labels. + */ + private static final NumberFormat FORMAT = NumberFormat.getNumberInstance(); + + private MathHelper() { + // empty constructor + } + + /** + * Calculate the minimum and maximum values out of a list of doubles. + * + * @param values the input values + * @return an array with the minimum and maximum values + */ + public static double[] minmax(List values) { + if (values.size() == 0) { + return new double[2]; + } + double min = values.get(0); + double max = min; + int length = values.size(); + for (int i = 1; i < length; i++) { + double value = values.get(i); + min = Math.min(min, value); + max = Math.max(max, value); + } + return new double[] { min, max }; + } + + /** + * Computes a reasonable set of labels for a data interval and number of + * labels. + * + * @param start start value + * @param end final value + * @param approxNumLabels desired number of labels + * @return collection containing {start value, end value, increment} + */ + public static List getLabels(final double start, final double end, + final int approxNumLabels) { + FORMAT.setMaximumFractionDigits(5); + List labels = new ArrayList(); + double[] labelParams = computeLabels(start, end, approxNumLabels); + // when the start > end the inc will be negative so it will still work + int numLabels = 1 + (int) ((labelParams[1] - labelParams[0]) / labelParams[2]); + // we want the range to be inclusive but we don't want to blow up when + // looping for the case where the min and max are the same. So we loop + // on + // numLabels not on the values. + for (int i = 0; i < numLabels; i++) { + double z = labelParams[0] + i * labelParams[2]; + try { + // this way, we avoid a label value like 0.4000000000000000001 instead + // of 0.4 + z = FORMAT.parse(FORMAT.format(z)).doubleValue(); + } catch (ParseException e) { + // do nothing here + } + labels.add(z); + } + return labels; + } + + /** + * Computes a reasonable number of labels for a data range. + * + * @param start start value + * @param end final value + * @param approxNumLabels desired number of labels + * @return double[] array containing {start value, end value, increment} + */ + private static double[] computeLabels(final double start, final double end, + final int approxNumLabels) { + if (Math.abs(start - end) < 0.0000001f) { + return new double[] { start, start, 0 }; + } + double s = start; + double e = end; + boolean switched = false; + if (s > e) { + switched = true; + double tmp = s; + s = e; + e = tmp; + } + double xStep = roundUp(Math.abs(s - e) / approxNumLabels); + // Compute x starting point so it is a multiple of xStep. + double xStart = xStep * Math.ceil(s / xStep); + double xEnd = xStep * Math.floor(e / xStep); + if (switched) { + return new double[] { xEnd, xStart, -1.0 * xStep }; + } + return new double[] { xStart, xEnd, xStep }; + } + + /** + * Given a number, round up to the nearest power of ten times 1, 2, or 5. The + * argument must be strictly positive. + */ + private static double roundUp(final double val) { + int exponent = (int) Math.floor(Math.log10(val)); + double rval = val * Math.pow(10, -exponent); + if (rval > 5.0) { + rval = 10.0; + } else if (rval > 2.0) { + rval = 5.0; + } else if (rval > 1.0) { + rval = 2.0; + } + rval *= Math.pow(10, exponent); + return rval; + } + + /** + * Transforms a list of Float values into an array of float. + * + * @param values the list of Float + * @return the array of floats + */ + public static float[] getFloats(List values) { + int length = values.size(); + float[] result = new float[length]; + for (int i = 0; i < length; i++) { + result[i] = values.get(i).floatValue(); + } + return result; + } + + /** + * Transforms a list of Double values into an array of double. + * + * @param values the list of Double + * @return the array of doubles + */ + public static double[] getDoubles(List values) { + int length = values.size(); + double[] result = new double[length]; + for (int i = 0; i < length; i++) { + result[i] = values.get(i).doubleValue(); + } + return result; + } + +} diff --git a/android-libraries/achartengine/src/org/achartengine/util/XYEntry.java b/android-libraries/achartengine/src/org/achartengine/util/XYEntry.java new file mode 100644 index 00000000..53761a4d --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/util/XYEntry.java @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2009 - 2012 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 org.achartengine.util; + +import java.util.Map.Entry; + +/** + * A map entry value encapsulating an XY point. + */ +public class XYEntry implements Entry { + private final K key; + + private V value; + + public XYEntry(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public V setValue(V object) { + this.value = object; + return this.value; + } +} \ No newline at end of file diff --git a/android-libraries/achartengine/src/org/achartengine/util/package.html b/android-libraries/achartengine/src/org/achartengine/util/package.html new file mode 100644 index 00000000..da92e913 --- /dev/null +++ b/android-libraries/achartengine/src/org/achartengine/util/package.html @@ -0,0 +1,6 @@ + +AChartEngine + +Utility classes that provide helper methods used by most of the other packages. + + \ No newline at end of file diff --git a/android/.classpath b/android/.classpath index 3288939d..1772b2ef 100644 --- a/android/.classpath +++ b/android/.classpath @@ -8,6 +8,5 @@ - diff --git a/android/libs/achartengine-1.0.0.jar b/android/libs/achartengine-1.0.0.jar deleted file mode 100644 index fe75dc34..00000000 Binary files a/android/libs/achartengine-1.0.0.jar and /dev/null differ diff --git a/android/project.properties b/android/project.properties index 0c951984..8202ea55 100644 --- a/android/project.properties +++ b/android/project.properties @@ -11,3 +11,4 @@ target=android-15 android.library.reference.1=../ActionBarSherlock android.library.reference.2=../TreeViewList +android.library.reference.3=../achartengine