Initial checkin of androidplot v0.6.0

This commit is a snapshot of androidplot at tag 0.6.0:

  commit 24dfe5d708dd409611fff1aa803ce3324a00db85
  Author: bamboo <[email protected]>
  Date:   Sat Jul 20 20:06:07 2013 +0000

      [maven-release-plugin] prepare release 0.6.0

Change-Id: I84fb6af3e0c96b1d3d14e21f518a9b49e1126cef
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..01ce2d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/.metadata/
+/AndroidPlot-Core/target/
+/AndroidPlot-Core/.settings/
+/Examples/DemoApp/.settings/
+/Examples/DemoApp/bin/
+/Examples/DemoApp/gen/
+/Examples/DemoApp/target/
\ No newline at end of file
diff --git a/.idea/ant.xml b/.idea/ant.xml
new file mode 100644
index 0000000..8dcb7fc
--- /dev/null
+++ b/.idea/ant.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4" />

+

diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
new file mode 100644
index 0000000..d63a15e
--- /dev/null
+++ b/.idea/codeStyleSettings.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4">

+  <component name="ProjectCodeStyleSettingsManager">

+    <option name="PER_PROJECT_SETTINGS">

+      <value>

+        <XML>

+          <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />

+        </XML>

+      </value>

+    </option>

+  </component>

+</project>

+

diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..4802278
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4">

+  <component name="CompilerConfiguration">

+    <option name="DEFAULT_COMPILER" value="Javac" />

+    <excludeFromCompile>

+      <directory url="file://$PROJECT_DIR$/Examples/StepChartExample/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/SimpleXYPlotExample/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/TimeSeriesExample/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/OrientationSensorExample/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/DynamicXYPlotExample/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/HelperExample/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/DemoApp/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/TimedXYPlotExample/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/Quickstart/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/DemoApp/target/generated-sources/annotations" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/Examples/DemoApp/target/generated-test-sources/test-annotations" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/AndroidPlot-Core/target/generated-sources/annotations" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/AndroidPlot-Core/target/generated-test-sources/test-annotations" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/AndroidPlot-Core/gen" includeSubdirectories="true" />

+      <directory url="file://$PROJECT_DIR$/gen" includeSubdirectories="true" />

+    </excludeFromCompile>

+    <resourceExtensions>

+      <entry name=".+\.(properties|xml|html|dtd|tld)" />

+      <entry name=".+\.(gif|png|jpeg|jpg)" />

+    </resourceExtensions>

+    <wildcardResourcePatterns>

+      <entry name="?*.properties" />

+      <entry name="?*.xml" />

+      <entry name="?*.gif" />

+      <entry name="?*.png" />

+      <entry name="?*.jpeg" />

+      <entry name="?*.jpg" />

+      <entry name="?*.html" />

+      <entry name="?*.dtd" />

+      <entry name="?*.tld" />

+      <entry name="?*.ftl" />

+    </wildcardResourcePatterns>

+    <annotationProcessing>

+      <profile default="true" name="Default" enabled="false">

+        <processorPath useClasspath="true" />

+      </profile>

+      <profile default="false" name="Maven default annotation processors profile" enabled="true">

+        <sourceOutputDir name="target/generated-sources/annotations" />

+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />

+        <outputRelativeToContentRoot value="true" />

+        <processorPath useClasspath="true" />

+        <module name="androidplot-core" />

+        <module name="DemoApp" />

+      </profile>

+    </annotationProcessing>

+    <bytecodeTargetLevel>

+      <module name="androidplot-core" target="1.6" />

+    </bytecodeTargetLevel>

+  </component>

+</project>

+

diff --git a/.idea/copyright/AndroidPlot_Apache_2_0.xml b/.idea/copyright/AndroidPlot_Apache_2_0.xml
new file mode 100644
index 0000000..af0b58d
--- /dev/null
+++ b/.idea/copyright/AndroidPlot_Apache_2_0.xml
@@ -0,0 +1,9 @@
+<component name="CopyrightManager">
+  <copyright>
+    <option name="notice" value="Copyright &amp;#36;today.year AndroidPlot.com&#10;&#10;   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;   you may not use this file except in compliance with the License.&#10;   You may obtain a copy of the License at&#10;&#10;       http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;   Unless required by applicable law or agreed to in writing, software&#10;   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;   See the License for the specific language governing permissions and&#10;   limitations under the License." />
+    <option name="keyword" value="Copyright" />
+    <option name="allowReplaceKeyword" value="" />
+    <option name="myName" value="AndroidPlot Apache 2.0" />
+    <option name="myLocal" value="true" />
+  </copyright>
+</component>
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..a8fb46f
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="CopyrightManager">

+  <settings default="AndroidPlot Apache 2.0">

+    <module2copyright>

+      <element module="All" copyright="AndroidPlot Apache 2.0" />

+    </module2copyright>

+  </settings>

+</component>
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..b7e4cd4
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4">

+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">

+    <file url="file://$PROJECT_DIR$" charset="UTF-8" />

+    <file url="file://$PROJECT_DIR$/AndroidPlot-Core" charset="UTF-8" />

+    <file url="file://$PROJECT_DIR$/Examples/DemoApp" charset="UTF-8" />

+  </component>

+</project>

+

diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..bfdc003
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,153 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0" is_locked="false">
+    <option name="myName" value="Project Default" />
+    <option name="myLocal" value="false" />
+    <inspection_tool class="AbstractBeanReferencesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="AssertEqualsBetweenInconvertibleTypesTestNG" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="AutowiredDependenciesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="ComponentNotRegistered" enabled="false" level="WARNING" enabled_by_default="false">
+      <option name="CHECK_ACTIONS" value="true" />
+      <option name="IGNORE_NON_PUBLIC" value="true" />
+    </inspection_tool>
+    <inspection_tool class="ComponentRegistrationProblems" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="ContextComponentScanInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="ContextJavaBeanUnresolvedMethodsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="DialogTitleCapitalization" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="DuplicatedBeanNamesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="ELDeferredExpressionsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="ELMethodSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="ELSpecValidationInJSP" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="ELValidationInJSP" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="EjbClassBasicInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbClassWarningsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="EjbDomInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbEntityClassInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbEntityHomeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbEntityInterfaceInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbEnvironmentInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbInterceptorInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbInterceptorWarningsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="EjbInterfaceMethodInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbInterfaceSignatureInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbProhibitedPackageUsageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="EjbQlInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbRemoteRequirementsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbSessionHomeInterfaceInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="EjbStaticAccessInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="EjbThisExpressionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="ExtensionPointBeanClass" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="FlowRequiredBeanTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="HardcodedActionUrl" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="InjectionValueTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="InspectionDescriptionNotFoundInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="InspectionMappingConsistency" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="InspectionUsingGrayColors" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="IntentionDescriptionNotFoundInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="JavaeeApplicationDomInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="JdkProxiedBeanTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="JsfJamExtendsClassInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="JspAbsolutePathInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="JspDirectiveInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="JspPropertiesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="MVCPathVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="ManagedBeanClassInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="MimeType" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="MissedExecutable" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="PlayCustomTagNameInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="PlayPropertyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="PluginXmlValidity" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="ReferencesToClassesFromDefaultPackagesInJSPFile" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="RequiredBeanTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SassScssResolvedByNameOnly" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
+    <inspection_tool class="SassUnresolvedFunction" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SassUnresolvedImport" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SassUnresolvedMixin" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SassUnresolvedPlaceholderSelector" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SassUnresolvedVariable" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SelfIncludingJspFiles" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="ServletWithoutMappingInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringBatchModel" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringBeanAutowiringInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringBeanConstructorArgInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringBeanDepedencyCheckInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringBeanInstantiationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringBeanLookupMethodInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringBeanNameConventionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringContextConfigurationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringDataJpaMethodInconsistencyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringElInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringFacetInspection" enabled="false" level="WARNING" enabled_by_default="false">
+      <option name="checkTestFiles" value="false" />
+    </inspection_tool>
+    <inspection_tool class="SpringFactoryMethodInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringHandlersSchemasHighlighting" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringIncorrectResourceTypeInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringInjectionValueConsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringInjectionValueStyleInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringIntegrationDeprecations21" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringIntegrationModel" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringJavaAutowiringInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringJavaConfigExternalBeansErrorInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringJavaConfigInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringMVCInitBinder" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringMVCViewInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringMessageDispatcherWebXmlInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringOsgiElementsInconsistencyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringOsgiListenerInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringOsgiServiceCommonInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringPlaceholdersInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringPublicFactoryMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringRequiredAnnotationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringRequiredPropertyInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringScopesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringSecurityDebugActivatedInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringSecurityFiltersConfiguredInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringSecurityModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringStaticMembersAutowiringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringTransactionalComponentInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SpringWebServiceAnnotationsInconsistencyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SpringWebServicesConfigurationsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlDeliverTableNameInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="Struts2ModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="StrutsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="StrutsTilesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="StrutsValidatorFormInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="StrutsValidatorInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="TaglibDomModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="TelReferencesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="UnparsedCustomBeanInspection" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="UnresolvedMessageChannel" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="UseJBColor" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="UtilSchemaInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="ValidatorConfigModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="ValidatorModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="WebProperties" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="WebWarnings" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="WebflowConfigModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="WebflowModelInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="WebflowSetupInspection" enabled="false" level="ERROR" enabled_by_default="false" />
+    <inspection_tool class="dependsOnMethodTestNG" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="groupsTestNG" enabled="false" level="WARNING" enabled_by_default="false">
+      <option name="groups">
+        <value>
+          <list size="0" />
+        </value>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..3b31283
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="PROJECT_PROFILE" value="Project Default" />
+    <option name="USE_PROJECT_PROFILE" value="true" />
+    <version value="1.0" />
+  </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__com_google_android_android_4_1_1_4.xml b/.idea/libraries/Maven__com_google_android_android_4_1_1_4.xml
new file mode 100644
index 0000000..fca0234
--- /dev/null
+++ b/.idea/libraries/Maven__com_google_android_android_4_1_1_4.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: com.google.android:android:4.1.1.4">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/com/google/android/android/4.1.1.4/android-4.1.1.4.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/com/google/android/android/4.1.1.4/android-4.1.1.4-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/com/google/android/android/4.1.1.4/android-4.1.1.4-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__commons_codec_commons_codec_1_3.xml b/.idea/libraries/Maven__commons_codec_commons_codec_1_3.xml
new file mode 100644
index 0000000..3688001
--- /dev/null
+++ b/.idea/libraries/Maven__commons_codec_commons_codec_1_3.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: commons-codec:commons-codec:1.3">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/commons-codec/commons-codec/1.3/commons-codec-1.3.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/commons-codec/commons-codec/1.3/commons-codec-1.3-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/commons-codec/commons-codec/1.3/commons-codec-1.3-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml b/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml
new file mode 100644
index 0000000..b770f56
--- /dev/null
+++ b/.idea/libraries/Maven__commons_logging_commons_logging_1_1_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: commons-logging:commons-logging:1.1.1">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__junit_junit_4_8_1.xml b/.idea/libraries/Maven__junit_junit_4_8_1.xml
new file mode 100644
index 0000000..bb76199
--- /dev/null
+++ b/.idea/libraries/Maven__junit_junit_4_8_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: junit:junit:4.8.1">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.8.1/junit-4.8.1.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.8.1/junit-4.8.1-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.8.1/junit-4.8.1-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__mockit_jmockit_0_999_3.xml b/.idea/libraries/Maven__mockit_jmockit_0_999_3.xml
new file mode 100644
index 0000000..99058c9
--- /dev/null
+++ b/.idea/libraries/Maven__mockit_jmockit_0_999_3.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: mockit:jmockit:0.999.3">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/mockit/jmockit/0.999.3/jmockit-0.999.3.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/mockit/jmockit/0.999.3/jmockit-0.999.3-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/mockit/jmockit/0.999.3/jmockit-0.999.3-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_0_1.xml b/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_0_1.xml
new file mode 100644
index 0000000..a9ca4c6
--- /dev/null
+++ b/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_0_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: org.apache.httpcomponents:httpclient:4.0.1">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_0_1.xml b/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_0_1.xml
new file mode 100644
index 0000000..eee5c06
--- /dev/null
+++ b/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_0_1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: org.apache.httpcomponents:httpcore:4.0.1">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_json_json_20080701.xml b/.idea/libraries/Maven__org_json_json_20080701.xml
new file mode 100644
index 0000000..b86a8bf
--- /dev/null
+++ b/.idea/libraries/Maven__org_json_json_20080701.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: org.json:json:20080701">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/json/json/20080701/json-20080701.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/org/json/json/20080701/json-20080701-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/json/json/20080701/json-20080701-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_khronos_opengl_api_gl1_1_android_2_1_r1.xml b/.idea/libraries/Maven__org_khronos_opengl_api_gl1_1_android_2_1_r1.xml
new file mode 100644
index 0000000..2fe6214
--- /dev/null
+++ b/.idea/libraries/Maven__org_khronos_opengl_api_gl1_1_android_2_1_r1.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: org.khronos:opengl-api:gl1.1-android-2.1_r1">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/khronos/opengl-api/gl1.1-android-2.1_r1/opengl-api-gl1.1-android-2.1_r1.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/org/khronos/opengl-api/gl1.1-android-2.1_r1/opengl-api-gl1.1-android-2.1_r1-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/khronos/opengl-api/gl1.1-android-2.1_r1/opengl-api-gl1.1-android-2.1_r1-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__xerces_xmlParserAPIs_2_6_2.xml b/.idea/libraries/Maven__xerces_xmlParserAPIs_2_6_2.xml
new file mode 100644
index 0000000..96cae4c
--- /dev/null
+++ b/.idea/libraries/Maven__xerces_xmlParserAPIs_2_6_2.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: xerces:xmlParserAPIs:2.6.2">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/xerces/xmlParserAPIs/2.6.2/xmlParserAPIs-2.6.2.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/xerces/xmlParserAPIs/2.6.2/xmlParserAPIs-2.6.2-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/xerces/xmlParserAPIs/2.6.2/xmlParserAPIs-2.6.2-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/Maven__xpp3_xpp3_1_1_4c.xml b/.idea/libraries/Maven__xpp3_xpp3_1_1_4c.xml
new file mode 100644
index 0000000..26694b3
--- /dev/null
+++ b/.idea/libraries/Maven__xpp3_xpp3_1_1_4c.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+  <library name="Maven: xpp3:xpp3:1.1.4c">
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/xpp3/xpp3/1.1.4c/xpp3-1.1.4c.jar!/" />
+    </CLASSES>
+    <JAVADOC>
+      <root url="jar://$MAVEN_REPOSITORY$/xpp3/xpp3/1.1.4c/xpp3-1.1.4c-javadoc.jar!/" />
+    </JAVADOC>
+    <SOURCES>
+      <root url="jar://$MAVEN_REPOSITORY$/xpp3/xpp3/1.1.4c/xpp3-1.1.4c-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..689ddbb
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4">

+  <component name="EntryPointsManager">

+    <entry_points version="2.0" />

+  </component>

+  <component name="FrameworkDetectionExcludesConfiguration">

+    <file type="android" url="file://$PROJECT_DIR$/AndroidPlot-Core/src/AndroidManifest.xml" />

+    <file type="android" url="file://$PROJECT_DIR$/Examples/DemoApp" />

+    <file type="android" url="file://$PROJECT_DIR$/Tests/AndroidManifest.xml" />

+  </component>

+  <component name="IdProvider" IDEtalkID="96A70927FCB4C9DA89E754C51C667B37" />

+  <component name="JavadocGenerationManager">

+    <option name="OUTPUT_DIRECTORY" />

+    <option name="OPTION_SCOPE" value="protected" />

+    <option name="OPTION_HIERARCHY" value="true" />

+    <option name="OPTION_NAVIGATOR" value="true" />

+    <option name="OPTION_INDEX" value="true" />

+    <option name="OPTION_SEPARATE_INDEX" value="true" />

+    <option name="OPTION_DOCUMENT_TAG_USE" value="false" />

+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />

+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />

+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />

+    <option name="OPTION_DEPRECATED_LIST" value="true" />

+    <option name="OTHER_OPTIONS" value="" />

+    <option name="HEAP_SIZE" />

+    <option name="LOCALE" />

+    <option name="OPEN_IN_BROWSER" value="true" />

+  </component>

+  <component name="MavenProjectsManager">

+    <option name="originalFiles">

+      <list>

+        <option value="$PROJECT_DIR$/AndroidPlot-Lib/pom.xml" />

+        <option value="$PROJECT_DIR$/pom.xml" />

+        <option value="$PROJECT_DIR$/Examples/DemoApp/pom.xml" />

+      </list>

+    </option>

+  </component>

+  <component name="ProjectResources">

+    <default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>

+  </component>

+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="Maven Android 4.1 Platform" project-jdk-type="Android SDK">

+    <output url="file://$PROJECT_DIR$/out" />

+  </component>

+  <component name="RegexUtilComponent" text="1900-01-01 2007/08/13 1900.01.01 1900 01 01 1900-01.01 1900 13 01 1900 02 31" flags="0" regex="(19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])" mode="0" />

+  <component name="SvnBranchConfigurationManager">

+    <option name="myConfigurationMap">

+      <map>

+        <entry key="$PROJECT_DIR$">

+          <value>

+            <SvnBranchConfiguration>

+              <option name="branchUrls">

+                <list>

+                  <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/branches" />

+                  <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/tags" />

+                </list>

+              </option>

+              <option name="trunkUrl" value="https://bridalveil.jira.com/svn/ANDROIDPLOT/trunk" />

+            </SvnBranchConfiguration>

+          </value>

+        </entry>

+        <entry key="$PROJECT_DIR$/AndroidPlot-Core">

+          <value>

+            <SvnBranchConfiguration>

+              <option name="branchUrls">

+                <list>

+                  <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/branches" />

+                  <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/tags" />

+                </list>

+              </option>

+              <option name="trunkUrl" value="https://bridalveil.jira.com/svn/ANDROIDPLOT/trunk" />

+            </SvnBranchConfiguration>

+          </value>

+        </entry>

+        <entry key="$PROJECT_DIR$/Examples/DemoApp">

+          <value>

+            <SvnBranchConfiguration>

+              <option name="trunkUrl" value="https://androidplot.jira.com/svn/ANDROIDPLOT/trunk/Examples/DemoApp" />

+            </SvnBranchConfiguration>

+          </value>

+        </entry>

+        <entry key="$PROJECT_DIR$/Examples/OrientationSensorExample">

+          <value>

+            <SvnBranchConfiguration>

+              <option name="branchUrls">

+                <list>

+                  <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/branches" />

+                  <option value="https://bridalveil.jira.com/svn/ANDROIDPLOT/tags" />

+                </list>

+              </option>

+              <option name="trunkUrl" value="https://bridalveil.jira.com/svn/ANDROIDPLOT/trunk" />

+            </SvnBranchConfiguration>

+          </value>

+        </entry>

+      </map>

+    </option>

+    <option name="mySupportsUserInfoFilter" value="true" />

+  </component>

+  <component name="WebServicesPlugin" addRequiredLibraries="true" />

+</project>

+

diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..a79b83b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4">

+  <component name="ProjectModuleManager">

+    <modules>

+      <module fileurl="file://$PROJECT_DIR$/Examples/DemoApp/DemoApp.iml" filepath="$PROJECT_DIR$/Examples/DemoApp/DemoApp.iml" />

+      <module fileurl="file://$PROJECT_DIR$/androidplot.iml" filepath="$PROJECT_DIR$/androidplot.iml" />

+      <module fileurl="file://$PROJECT_DIR$/AndroidPlot-Core/androidplot-core.iml" filepath="$PROJECT_DIR$/AndroidPlot-Core/androidplot-core.iml" />

+    </modules>

+  </component>

+</project>

+

diff --git a/.idea/runConfigurations/DemoApp.xml b/.idea/runConfigurations/DemoApp.xml
new file mode 100644
index 0000000..733ea80
--- /dev/null
+++ b/.idea/runConfigurations/DemoApp.xml
@@ -0,0 +1,22 @@
+<component name="ProjectRunConfigurationManager">

+  <configuration default="false" name="DemoApp" type="AndroidRunConfigurationType" factoryName="Android Application">

+    <module name="DemoApp" />

+    <option name="ACTIVITY_CLASS" value="" />

+    <option name="MODE" value="default_activity" />

+    <option name="DEPLOY" value="true" />

+    <option name="TARGET_SELECTION_MODE" value="USB_DEVICE" />

+    <option name="PREFERRED_AVD" value="" />

+    <option name="USE_COMMAND_LINE" value="true" />

+    <option name="COMMAND_LINE" value="" />

+    <option name="WIPE_USER_DATA" value="false" />

+    <option name="DISABLE_BOOT_ANIMATION" value="false" />

+    <option name="NETWORK_SPEED" value="full" />

+    <option name="NETWORK_LATENCY" value="none" />

+    <option name="CLEAR_LOGCAT" value="true" />

+    <RunnerSettings RunnerId="AndroidDebugRunner" />

+    <RunnerSettings RunnerId="Run" />

+    <ConfigurationWrapper RunnerId="AndroidDebugRunner" />

+    <ConfigurationWrapper RunnerId="Run" />

+    <method />

+  </configuration>

+</component>
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..1e7cce4
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4">

+  <component name="Palette2">

+    <group name="Swing">

+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />

+      </item>

+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />

+      </item>

+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />

+      </item>

+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">

+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />

+      </item>

+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />

+        <initial-values>

+          <property name="text" value="Button" />

+        </initial-values>

+      </item>

+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />

+        <initial-values>

+          <property name="text" value="RadioButton" />

+        </initial-values>

+      </item>

+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />

+        <initial-values>

+          <property name="text" value="CheckBox" />

+        </initial-values>

+      </item>

+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />

+        <initial-values>

+          <property name="text" value="Label" />

+        </initial-values>

+      </item>

+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">

+          <preferred-size width="150" height="-1" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">

+          <preferred-size width="150" height="-1" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">

+          <preferred-size width="150" height="-1" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">

+          <preferred-size width="150" height="50" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">

+          <preferred-size width="150" height="50" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">

+          <preferred-size width="150" height="50" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />

+      </item>

+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">

+          <preferred-size width="150" height="50" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">

+          <preferred-size width="150" height="50" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">

+          <preferred-size width="150" height="50" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">

+          <preferred-size width="200" height="200" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">

+          <preferred-size width="200" height="200" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">

+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />

+      </item>

+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />

+      </item>

+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />

+      </item>

+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />

+      </item>

+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">

+          <preferred-size width="-1" height="20" />

+        </default-constraints>

+      </item>

+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">

+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />

+      </item>

+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">

+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />

+      </item>

+    </group>

+  </component>

+</project>

+

diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..ab55cf1
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project version="4">

+  <component name="VcsDirectoryMappings">

+    <mapping directory="$PROJECT_DIR$" vcs="Git" />

+  </component>

+</project>

+

diff --git a/AndroidPlot-Core/.classpath b/AndroidPlot-Core/.classpath
new file mode 100644
index 0000000..dcd1f46
--- /dev/null
+++ b/AndroidPlot-Core/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/AndroidPlot-Core/.project b/AndroidPlot-Core/.project
new file mode 100644
index 0000000..1cb89ea
--- /dev/null
+++ b/AndroidPlot-Core/.project
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>AndroidPlot-Core</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>
diff --git a/AndroidPlot-Core/androidplot-core.iml b/AndroidPlot-Core/androidplot-core.iml
new file mode 100644
index 0000000..53f8e0a
--- /dev/null
+++ b/AndroidPlot-Core/androidplot-core.iml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">

+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="false">

+    <output url="file://$MODULE_DIR$/target/classes" />

+    <output-test url="file://$MODULE_DIR$/target/test-classes" />

+    <content url="file://$MODULE_DIR$">

+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />

+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />

+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />

+      <excludeFolder url="file://$MODULE_DIR$/target" />

+    </content>

+    <orderEntry type="inheritedJdk" />

+    <orderEntry type="sourceFolder" forTests="false" />

+    <orderEntry type="library" scope="TEST" name="Maven: mockit:jmockit:0.999.3" level="project" />

+    <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.android:android:4.1.1.4" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.1.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpclient:4.0.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpcore:4.0.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: commons-codec:commons-codec:1.3" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.khronos:opengl-api:gl1.1-android-2.1_r1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: xpp3:xpp3:1.1.4c" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.json:json:20080701" level="project" />

+  </component>

+</module>

+

diff --git a/AndroidPlot-Core/pom.xml b/AndroidPlot-Core/pom.xml
new file mode 100644
index 0000000..ffdb40b
--- /dev/null
+++ b/AndroidPlot-Core/pom.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2012 AndroidPlot.com
+  ~
+  ~    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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.androidplot</groupId>
+        <artifactId>androidplot</artifactId>
+        <version>0.6.0</version>
+    </parent>
+    <artifactId>androidplot-core</artifactId>
+    <!--<version>${applicationVersion}</version>-->
+    <name>AndroidPlot-Core</name>
+    <description>AndroidPlot core library</description>
+
+    <profiles>
+        <profile>
+            <id>default</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <path.to.rt.jar>${java.home}/lib/rt.jar</path.to.rt.jar>
+            </properties>
+        </profile>
+        <profile>
+            <id>osx</id>
+            <activation>
+                <os>
+                    <family>mac</family>
+                </os>
+            </activation>
+            <properties>
+                <path.to.rt.jar>${java.home}/../Classes/classes.jar</path.to.rt.jar>
+            </properties>
+        </profile>
+    </profiles>
+
+    <!--<repositories>
+        <repository>
+            <id>central</id>
+            <name>Maven Central</name>
+            <url>http://repo1.maven.org/maven2/</url>
+        </repository></repositories>
+-->
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-gpg-plugin</artifactId>
+                <version>1.4</version>
+                <executions>
+                    <execution>
+                        <id>sign-artifacts</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>sign</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.7</version>
+                <configuration>
+                    <show>public</show>                    
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+                <version>2.2</version>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.0.2</version>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+
+            
+            
+            <plugin>
+                <groupId>com.pyx4me</groupId>
+                <artifactId>proguard-maven-plugin</artifactId>
+                <version>2.0.4</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>proguard</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <obfuscate>true</obfuscate>
+                    <options>
+                        <option>-allowaccessmodification</option>
+                        <option>-keep public class * {public *;}</option>
+                        <option>-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);}</option>
+                        <option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>
+                    </options>
+                    <!--<injar>${project.build.finalName}.jar</injar>
+                    <outjar>obfuscated/${project.build.finalName}-release.jar</outjar>-->
+                    <outputDirectory>${project.build.directory}</outputDirectory>
+                    <libs>
+                        <!--<lib>${java.home}/lib/rt.jar</lib>-->
+                        <lib>${path.to.rt.jar}</lib>
+                    </libs>
+                    <addMavenDescriptor>false</addMavenDescriptor>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <version>2.7</version>
+                <inherited>false</inherited>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+     
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>mockit</groupId>
+            <artifactId>jmockit</artifactId>
+            <version>0.999.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+        </dependency>
+        <!--<dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+            <version>4.1.1.4</version>
+            <scope>provided</scope>
+        </dependency>-->
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/LineRegion.java b/AndroidPlot-Core/src/main/java/com/androidplot/LineRegion.java
new file mode 100644
index 0000000..0f53711
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/LineRegion.java
@@ -0,0 +1,101 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot;

+

+/**

+ * A one dimensional region represented by a starting and ending value.

+ */

+public class LineRegion {

+    private Number minVal;

+    private Number maxVal;

+

+    public LineRegion(Number val1, Number v2) {

+        if (val1.doubleValue() < v2.doubleValue()) {

+            this.setMinVal(val1);

+            this.setMaxVal(v2);

+        } else {

+            this.setMinVal(v2);

+            this.setMaxVal(val1);

+        }

+    }

+

+    public static Number measure(Number val1, Number val2) {

+        return new LineRegion(val1, val2).length();

+    }

+

+    public Number length() {

+        return maxVal.doubleValue() - minVal.doubleValue();

+    }

+

+    /**

+     * Tests whether a value is within the given range

+     * @param value

+     * @return

+     */

+    public boolean contains(Number value) {

+        return value.doubleValue() >= minVal.doubleValue() && value.doubleValue() <= maxVal.doubleValue();

+    }

+

+    public boolean intersects(LineRegion lineRegion) {

+        return intersects(lineRegion.getMinVal(), lineRegion.getMaxVal());

+    }

+

+     /**

+     * Tests whether this segment intersects another

+     * @param line2Min

+     * @param line2Max

+     * @return

+     */

+    public  boolean intersects(Number line2Min, Number line2Max) {

+

+        //double l1min = getMinVal() == null ? Double.NEGATIVE_INFINITY : getMinVal().doubleValue();

+        //double l1max = getMaxVal() == null ? Double.POSITIVE_INFINITY : getMaxVal().doubleValue();

+

+        //double l2min = line2Min == null ? Double.NEGATIVE_INFINITY : line2Min.doubleValue();

+        //double l2max = line2Max == null ? Double.POSITIVE_INFINITY : line2Max.doubleValue();

+

+

+        // is this line completely within line2?

+        if(line2Min.doubleValue() <= this.minVal.doubleValue() && line2Max.doubleValue() >= this.maxVal.doubleValue()) {

+            return true;

+        // is line1 partially within line2

+        } else return contains(line2Min) || contains(line2Max);

+    }

+

+    public Number getMinVal() {

+        return minVal;

+    }

+

+    public void setMinVal(Number minVal) {

+        if(minVal == null) {

+            throw new NullPointerException("Region values can never be null.");

+        }

+        this.minVal = minVal;

+    }

+

+    public Number getMaxVal() {

+        return maxVal;

+    }

+

+    public void setMaxVal(Number maxVal) {

+        if(maxVal == null) {

+            throw new NullPointerException("Region values can never be null.");

+        }

+        this.maxVal = maxVal;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/Plot.java b/AndroidPlot-Core/src/main/java/com/androidplot/Plot.java
new file mode 100644
index 0000000..e47e568
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/Plot.java
@@ -0,0 +1,868 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot;

+

+import android.content.Context;

+import android.graphics.*;

+import android.os.Build;

+import android.os.Looper;

+import android.util.AttributeSet;

+import android.util.Log;

+import android.view.View;

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.ui.*;

+import com.androidplot.ui.Formatter;

+import com.androidplot.ui.TextOrientationType;

+import com.androidplot.ui.widget.TextLabelWidget;

+import com.androidplot.ui.widget.Widget;

+import com.androidplot.ui.SeriesRenderer;

+import com.androidplot.util.Configurator;

+import com.androidplot.util.DisplayDimensions;

+import com.androidplot.util.PixelUtils;

+import com.androidplot.ui.XLayoutStyle;

+import com.androidplot.ui.YLayoutStyle;

+

+import java.util.*;

+

+/**

+ * Base class for all other Plot implementations..

+ */

+public abstract class Plot<SeriesType extends Series, FormatterType extends Formatter, RendererType extends SeriesRenderer>

+        extends View implements Resizable{

+    private static final String TAG = Plot.class.getName();

+    private static final String XML_ATTR_PREFIX      = "androidplot";

+

+    private static final String ATTR_TITLE           = "title";

+    private static final String ATTR_RENDER_MODE     = "renderMode";

+

+    public DisplayDimensions getDisplayDimensions() {

+        return displayDims;

+    }

+

+    public enum BorderStyle {

+        ROUNDED,

+        SQUARE,

+        NONE

+    }

+

+    /**

+     * The RenderMode used by a Plot to draw it's self onto the screen.  The RenderMode can be set

+     * in two ways.

+     *

+     * In an xml layout:

+     *

+     * <code>

+     * <com.androidplot.xy.XYPlot

+     * android:id="@+id/mySimpleXYPlot"

+     * android:layout_width="fill_parent"

+     * android:layout_height="fill_parent"

+     * title="@string/sxy_title"

+     * renderMode="useBackgroundThread"/>

+     * </code>

+     *

+     * Programatically:

+     *

+     * <code>

+     * XYPlot myPlot = new XYPlot(context "MyPlot", Plot.RenderMode.USE_MAIN_THREAD);

+     * </code>

+     *

+     * A Plot's  RenderMode cannot be changed after the plot has been initialized.

+     * @since 0.5.1

+     */

+    public enum RenderMode {

+        /**

+         * Use a second thread and an off-screen buffer to do drawing.  This is the preferred method

+         * of drawing dynamic data and static data that consists of a large number of points.  This mode

+         * provides more efficient CPU utilization at the cost of increased memory usage.  As of

+         * version 0.5.1 this is the default RenderMode.

+         *

+         * XML value: use_background_thread

+         * @since 0.5.1

+         */

+        USE_BACKGROUND_THREAD,

+

+        /**

+         * Do everything in the primary thread.  This is the preferred method of drawing static charts

+         * and dynamic data that consists of a small number of points. This mode uses less memory at

+         * the cost of poor CPU utilization.

+         *

+         * XML value: use_main_thread

+         * @since 0.5.1

+         */

+        USE_MAIN_THREAD

+    }

+    private BoxModel boxModel = new BoxModel(3, 3, 3, 3, 3, 3, 3, 3);

+    private BorderStyle borderStyle = Plot.BorderStyle.SQUARE;

+    private float borderRadiusX = 15;

+    private float borderRadiusY = 15;

+    private boolean drawBorderEnabled = true;

+    private Paint borderPaint;

+    private Paint backgroundPaint;

+    private LayoutManager layoutManager;

+    private TextLabelWidget titleWidget;

+    private DisplayDimensions displayDims = new DisplayDimensions();

+    private RenderMode renderMode = RenderMode.USE_MAIN_THREAD;

+    private final BufferedCanvas pingPong = new BufferedCanvas();

+

+    // used to get rid of flickering when drawing offScreenBitmap to the visible Canvas.

+    private final Object renderSynch = new Object();

+

+    /**

+     * Used for caching renderer instances.  Note that once a renderer is initialized it remains initialized

+     * for the life of the application; does not and should not be destroyed until the application exits.

+     */

+    private LinkedList<RendererType> renderers;

+

+    /**

+     * Associates lists series and formatter pairs with the class of the Renderer used to render them.

+     */

+    private LinkedHashMap<Class, SeriesAndFormatterList<SeriesType,FormatterType>> seriesRegistry;

+

+    private final ArrayList<PlotListener> listeners;

+

+    private Thread renderThread;

+    private boolean keepRunning = false;

+    private boolean isIdle = true;

+

+    {

+        listeners = new ArrayList<PlotListener>();

+        seriesRegistry = new LinkedHashMap<Class, SeriesAndFormatterList<SeriesType,FormatterType>>();

+        renderers = new LinkedList<RendererType>();

+        borderPaint = new Paint();

+        borderPaint.setColor(Color.rgb(150, 150, 150));

+        borderPaint.setStyle(Paint.Style.STROKE);

+        borderPaint.setStrokeWidth(1.0f);

+        borderPaint.setAntiAlias(true);

+        backgroundPaint = new Paint();

+        backgroundPaint.setColor(Color.DKGRAY);

+        backgroundPaint.setStyle(Paint.Style.FILL);

+    }

+

+

+    /**

+     *  Any rendering that utilizes a buffer from this class should synchronize rendering on the instance of this class

+     *  that is being used.

+     */

+    private class BufferedCanvas {

+        private volatile Bitmap bgBuffer;  // all drawing is done on this buffer.

+        private volatile Bitmap fgBuffer;

+        private Canvas canvas = new Canvas();

+

+        /**

+         * Call this method once drawing on a Canvas retrieved by {@link #getCanvas()} to mark

+         * the buffer as fully rendered.  Failure to call this method will result in nothing being drawn.

+         */

+        public synchronized void swap() {

+            Bitmap tmp = bgBuffer;

+            bgBuffer = fgBuffer;

+            fgBuffer = tmp;

+        }

+

+        public synchronized void resize(int h, int w) {

+            if (w <= 0 || h <= 0) {

+                bgBuffer = null;

+                fgBuffer = null;

+            } else {

+                bgBuffer = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);

+                fgBuffer = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);

+            }

+        }

+

+

+        /**

+         * Get a Canvas for drawing.  Actual drawing should be synchronized on the instance

+         * of BufferedCanvas being used.

+         * @return The Canvas instance to draw onto.  Returns null if drawing buffers have not

+         *         been initialized a la {@link #resize(int, int)}.

+         */

+        public synchronized Canvas getCanvas() {

+            if(bgBuffer != null) {

+                canvas.setBitmap(bgBuffer);

+                return canvas;

+            } else {

+                return null;

+            }

+        }

+

+        /**

+         * @return The most recent fully rendered Bitmsp

+         */

+        public Bitmap getBitmap() {

+            return fgBuffer;

+        }

+    }

+

+    /**

+     * Convenience constructor - wraps {@link #Plot(android.content.Context, String, com.androidplot.Plot.RenderMode)}.

+     * RenderMode is set to {@link RenderMode#USE_BACKGROUND_THREAD}.

+     * @param context

+     * @param title The display title of this Plot.

+     */

+    public Plot(Context context, String title) {

+        this(context, title, RenderMode.USE_MAIN_THREAD);

+    }

+

+    /**

+     * Used for programmatic instantiation.

+     * @param context

+     * @param title The display title of this Plot.

+     */

+    public Plot(Context context, String title, RenderMode mode) {

+        super(context);

+        this.renderMode = mode;

+        init(null, null);

+        setTitle(title);

+    }

+

+

+    /**

+     * Required by super-class. Extending class' implementations should add

+     * the following code immediately before exiting to ensure that loadAttrs

+     * is called only once by the derived class:

+     * <code>

+     * if(getClass().equals(DerivedPlot.class) {

+     *     loadAttrs(context, attrs);

+     * }

+     * </code>

+     *

+     * See {@link com.androidplot.xy.XYPlot#XYPlot(android.content.Context, android.util.AttributeSet)}

+     * for an example.

+     * @param context

+     * @param attrs

+     */

+    public Plot(Context context, AttributeSet attrs) {

+        super(context, attrs);

+        init(context, attrs);

+    }

+

+    /**

+     * Required by super-class. Extending class' implementations should add

+     * the following code immediately before exiting to ensure that loadAttrs

+     * is called only once by the derived class:

+     * <code>

+     * if(getClass().equals(DerivedPlot.class) {

+     *     loadAttrs(context, attrs);

+     * }

+     * </code>

+     *

+     * See {@link com.androidplot.xy.XYPlot#XYPlot(android.content.Context, android.util.AttributeSet, int)}

+     * for an example.

+     * @param context

+     * @param attrs

+     * @param defStyle

+     */

+    public Plot(Context context, AttributeSet attrs, int defStyle) {

+        super(context, attrs, defStyle);

+        init(context, attrs);

+    }

+

+    /**

+     * Can be overridden by derived classes to control hardware acceleration state.

+     * Note that this setting is only used on Honeycomb and later environments.

+     * @return True if hardware acceleration is allowed, false otherwise.

+     * @since 0.5.1

+     */

+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")

+    protected boolean isHwAccelerationSupported() {

+        return false;

+    }

+

+    /**

+     * Sets the render mode used by the Plot.

+     * WARNING: This method is not currently designed for general use outside of Configurator.

+     * Attempting to reassign the render mode at runtime will result in unexpected behavior.

+     * @param mode

+     */

+    public void setRenderMode(RenderMode mode) {

+        this.renderMode = mode;

+    }

+

+    /**

+     * Concrete implementations should do any final setup / initialization

+     * here.  Immediately following this method's invocation, AndroidPlot assumes

+     * that the Plot instance is ready for final configuration via the Configurator.

+     */

+    protected abstract void onPreInit();

+

+

+    private void init(Context context, AttributeSet attrs) {

+        PixelUtils.init(getContext());

+        layoutManager = new LayoutManager();

+        titleWidget = new TextLabelWidget(layoutManager, new SizeMetrics(25,

+                SizeLayoutType.ABSOLUTE, 100,

+                SizeLayoutType.ABSOLUTE),

+                TextOrientationType.HORIZONTAL);

+        titleWidget.position(0, XLayoutStyle.RELATIVE_TO_CENTER, 0,

+                YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.TOP_MIDDLE);

+

+        onPreInit();

+        // make sure the title widget is always the topmost widget:

+        layoutManager.moveToTop(titleWidget);

+        if(context != null && attrs != null) {

+            loadAttrs(attrs);

+        }

+

+        layoutManager.onPostInit();

+        Log.d(TAG, "AndroidPlot RenderMode: " + renderMode);

+        if (renderMode == RenderMode.USE_BACKGROUND_THREAD) {

+            renderThread = new Thread(new Runnable() {

+                @Override

+                public void run() {

+

+                    keepRunning = true;

+                    while (keepRunning) {

+                        isIdle = false;

+                        synchronized (pingPong) {

+                            Canvas c = pingPong.getCanvas();

+                            renderOnCanvas(c);

+                            pingPong.swap();

+                        }

+                        synchronized (renderSynch) {

+                            postInvalidate();

+                            // prevent this thread from becoming an orphan

+                            // after the view is destroyed

+                            if (keepRunning) {

+                                try {

+                                    renderSynch.wait();

+                                } catch (InterruptedException e) {

+                                    keepRunning = false;

+                                }

+                            }

+                        }

+                    }

+                    System.out.println("AndroidPlot render thread finished.");

+                }

+            });

+        }

+    }

+

+    /**

+     * Parse XML Attributes.  Should only be called once and at the end of the base class constructor.

+     *

+     * @param attrs

+     */

+    private void loadAttrs(AttributeSet attrs) {

+

+        if (attrs != null) {

+            // filter out androidplot prefixed attrs:

+            HashMap<String, String> attrHash = new HashMap<String, String>();

+            for (int i = 0; i < attrs.getAttributeCount(); i++) {

+                String attrName = attrs.getAttributeName(i);

+

+                // case insensitive check to see if this attr begins with our prefix:

+                if (attrName.toUpperCase().startsWith(XML_ATTR_PREFIX.toUpperCase())) {

+                    attrHash.put(attrName.substring(XML_ATTR_PREFIX.length() + 1), attrs.getAttributeValue(i));

+                }

+            }

+            Configurator.configure(getContext(), this, attrHash);

+        }

+    }

+

+    public RenderMode getRenderMode() {

+        return renderMode;

+    }

+

+    public synchronized boolean addListener(PlotListener listener) {

+        return !listeners.contains(listener) && listeners.add(listener);

+    }

+

+    public synchronized boolean removeListener(PlotListener listener) {

+        return listeners.remove(listener);

+    }

+

+    protected void notifyListenersBeforeDraw(Canvas canvas) {

+        for (PlotListener listener : listeners) {

+            listener.onBeforeDraw(this, canvas);

+        }

+    }

+

+    protected void notifyListenersAfterDraw(Canvas canvas) {

+        for (PlotListener listener : listeners) {

+            listener.onAfterDraw(this, canvas);

+        }

+    }

+

+    /**

+     * @param series

+     */

+    public synchronized boolean addSeries(SeriesType series, FormatterType formatter) {

+        Class rendererClass = formatter.getRendererClass();

+        SeriesAndFormatterList<SeriesType, FormatterType> sfList = seriesRegistry.get(rendererClass);

+        

+        // if there is no list for this renderer type, we need to getInstance one:

+        if(sfList == null) {

+            // make sure there is not already an instance of this renderer available:

+            if(getRenderer(rendererClass) == null) {

+                renderers.add((RendererType) formatter.getRendererInstance(this));

+            }

+            sfList = new SeriesAndFormatterList<SeriesType,FormatterType>();

+            seriesRegistry.put(rendererClass, sfList);

+        }

+

+        // if this series implements PlotListener, add it as a listener:

+        if(series instanceof PlotListener) {

+            addListener((PlotListener)series);

+        }

+

+        // do nothing if this series already associated with the renderer:

+        if(sfList.contains(series)) {

+            return false;

+        } else {

+            sfList.add(series, formatter);

+            return true;

+        }

+    }

+

+    public synchronized boolean removeSeries(SeriesType series, Class rendererClass) {

+        boolean result = seriesRegistry.get(rendererClass).remove(series);

+        if(seriesRegistry.get(rendererClass).size() <= 0) {

+            seriesRegistry.remove(rendererClass);

+        }

+

+        // if series implements PlotListener, remove it from listeners:

+        if(series instanceof PlotListener) {

+            removeListener((PlotListener) series);

+        }

+        return result;

+    }

+

+    /**

+     * Remove all occorrences of series from all renderers

+     * @param series

+     */

+    public synchronized void removeSeries(SeriesType series) {

+

+        // remove all occurrences of series from all renderers:

+        for(Class rendererClass : seriesRegistry.keySet()) {

+            seriesRegistry.get(rendererClass).remove(series);

+        }       

+

+        // remove empty SeriesAndFormatterList instances from the registry:

+        for(Iterator<SeriesAndFormatterList<SeriesType,FormatterType>> it = seriesRegistry.values().iterator(); it.hasNext();) {

+            if(it.next().size() <= 0) {

+                it.remove();

+            }

+        }

+

+        // if series implements PlotListener, remove it from listeners:

+        if (series instanceof PlotListener) {

+            removeListener((PlotListener) series);

+        }

+    }

+

+    /**

+     * Remove all series from all renderers

+     */

+    public void clear() {

+        for(Iterator<SeriesAndFormatterList<SeriesType,FormatterType>> it = seriesRegistry.values().iterator(); it.hasNext();) {

+            it.next();

+            it.remove();

+        }

+    }

+

+    public boolean isEmpty() {

+        return seriesRegistry.isEmpty();

+    }

+

+    public FormatterType getFormatter(SeriesType series, Class rendererClass) {

+        return seriesRegistry.get(rendererClass).getFormatter(series);

+    }

+

+    public SeriesAndFormatterList<SeriesType,FormatterType> getSeriesAndFormatterListForRenderer(Class rendererClass) {

+        return seriesRegistry.get(rendererClass);

+    }

+

+    /**

+     * Returns a list of all series assigned to the various renderers within the Plot.

+     * The returned List will contain no duplicates.

+     * @return

+     */

+    public Set<SeriesType> getSeriesSet() {

+        Set<SeriesType> seriesSet = new LinkedHashSet<SeriesType>();

+        for (SeriesRenderer renderer : getRendererList()) {

+            List<SeriesType> seriesList = getSeriesListForRenderer(renderer.getClass());

+            if (seriesList != null) {

+                for (SeriesType series : seriesList) {

+                    seriesSet.add(series);

+                }

+            }

+        }

+        return seriesSet;

+    }

+

+    public List<SeriesType> getSeriesListForRenderer(Class rendererClass) {

+        SeriesAndFormatterList<SeriesType,FormatterType> lst = seriesRegistry.get(rendererClass);

+        if(lst == null) {

+            return null;

+        } else {

+            return lst.getSeriesList();

+        }

+    }

+

+    public RendererType getRenderer(Class rendererClass) {

+        for(RendererType renderer : renderers) {

+            if(renderer.getClass() == rendererClass) {

+                return renderer;

+            }

+        }

+        return null;

+    }

+

+    public List<RendererType> getRendererList() {

+        return renderers;

+    }

+

+    public void setMarkupEnabled(boolean enabled) {

+        this.layoutManager.setMarkupEnabled(enabled);

+    }

+

+    /**

+     * Causes the plot to be redrawn.

+     * @since 0.5.1

+     */

+    public void redraw() {

+

+        if (renderMode == RenderMode.USE_BACKGROUND_THREAD) {

+

+            // only enter synchronized block if the call is expected to block OR

+            // if the render thread is idle, so we know that we won't have to wait to

+            // obtain a lock.

+            if (isIdle) {

+                synchronized (renderSynch) {

+                    renderSynch.notify();

+                }

+            }

+        } else if(renderMode == RenderMode.USE_MAIN_THREAD) {

+

+            // are we on the UI thread?

+            if (Looper.myLooper() == Looper.getMainLooper()) {

+                invalidate();

+            } else {

+                postInvalidate();

+            }

+        } else {

+            throw new IllegalArgumentException("Unsupported Render Mode: " + renderMode);

+        }

+    }

+

+    @Override

+    public synchronized void layout(final DisplayDimensions dims) {

+        displayDims = dims;

+        layoutManager.layout(displayDims);

+    }

+

+    @Override

+    protected void onDetachedFromWindow() {

+        synchronized(renderSynch) {

+            keepRunning = false;

+            renderSynch.notify();

+        }

+    }

+

+

+    @Override

+    protected synchronized void onSizeChanged (int w, int h, int oldw, int oldh) {

+

+        // update pixel conversion values

+        PixelUtils.init(getContext());

+

+        // disable hardware acceleration if it's not explicitly supported

+        // by the current Plot implementation. this check only applies to

+        // honeycomb and later environments.

+        if (Build.VERSION.SDK_INT >= 11) {

+            if (!isHwAccelerationSupported() && isHardwareAccelerated()) {

+                setLayerType(View.LAYER_TYPE_SOFTWARE, null);

+            }

+        }

+

+        // pingPong is only used in background rendering mode.

+        if(renderMode == RenderMode.USE_BACKGROUND_THREAD) {

+            pingPong.resize(h, w);

+        }

+

+        RectF cRect = new RectF(0, 0, w, h);

+        RectF mRect = boxModel.getMarginatedRect(cRect);

+        RectF pRect = boxModel.getPaddedRect(mRect);

+

+        layout(new DisplayDimensions(cRect, mRect, pRect));

+        super.onSizeChanged(w, h, oldw, oldh);

+        if(renderThread != null && !renderThread.isAlive()) {

+            renderThread.start();

+        }

+    }

+

+    /**

+     * Called whenever the plot needs to be drawn via the Handler, which invokes invalidate().

+     * Should never be called directly; use {@link #redraw()} instead.

+     * @param canvas

+     */

+    @Override

+    protected void onDraw(Canvas canvas) {

+        if (renderMode == RenderMode.USE_BACKGROUND_THREAD) {

+            synchronized(pingPong) {

+                Bitmap bmp = pingPong.getBitmap();

+                if(bmp != null) {

+                    canvas.drawBitmap(bmp, 0, 0, null);

+                }

+            }

+        } else if (renderMode == RenderMode.USE_MAIN_THREAD) {

+            renderOnCanvas(canvas);

+        } else {

+            throw new IllegalArgumentException("Unsupported Render Mode: " + renderMode);

+        }

+    }

+

+    /**

+     * Renders the plot onto a canvas.  Used by both main thread to draw directly

+     * onto the View's canvas as well as by background draw to render onto a

+     * Bitmap buffer.  At the end of the day this is the main entry for a plot's

+     * "heavy lifting".

+     * @param canvas

+     */

+    protected synchronized void renderOnCanvas(Canvas canvas) {

+        try {

+            // any series interested in synchronizing with plot should

+            // implement PlotListener.onBeforeDraw(...) and do a read lock from within its

+            // invocation.  This is the entry point into that call:

+            notifyListenersBeforeDraw(canvas);

+            try {

+                // need to completely erase what was on the canvas before redrawing, otherwise

+                // some odd aliasing artifacts begin to build up around the edges of aa'd entities

+                // over time.

+                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

+                if (backgroundPaint != null) {

+                    drawBackground(canvas, displayDims.marginatedRect);

+                }

+

+                layoutManager.draw(canvas);

+

+                if (getBorderPaint() != null) {

+                    drawBorder(canvas, displayDims.marginatedRect);

+                }

+            } catch (PlotRenderException e) {

+                Log.e(TAG, "Exception while rendering Plot.", e);

+                e.printStackTrace();

+            } catch (Exception e) {

+                Log.e(TAG, "Exception while rendering Plot.", e);

+            }

+        } finally {

+            isIdle = true;

+            // any series interested in synchronizing with plot should

+            // implement PlotListener.onAfterDraw(...) and do a read unlock from within that

+            // invocation. This is the entry point for that invocation.

+            notifyListenersAfterDraw(canvas);

+        }

+    }

+

+

+    /**

+     * Sets the visual style of the plot's border.

+     * @param style

+     * @param radiusX Sets the X radius for BorderStyle.ROUNDED.  Use null for all other styles.

+     * @param radiusY Sets the Y radius for BorderStyle.ROUNDED.  Use null for all other styles.

+     */

+    public void setBorderStyle(BorderStyle style, Float radiusX, Float radiusY) {

+        if (style == Plot.BorderStyle.ROUNDED) {

+            if (radiusX == null || radiusY == null){

+                throw new IllegalArgumentException("radiusX and radiusY cannot be null when using BorderStyle.ROUNDED");

+            }

+            this.borderRadiusX = radiusX;

+            this.borderRadiusY = radiusY;

+        }

+        this.borderStyle = style;

+    }

+

+    /**

+     * Draws the plot's outer border.

+     * @param canvas

+     * @throws PlotRenderException

+     */

+    protected void drawBorder(Canvas canvas, RectF dims) {

+        switch (borderStyle) {

+            case ROUNDED:

+                canvas.drawRoundRect(dims, borderRadiusX, borderRadiusY, borderPaint);

+                break;

+            case SQUARE:

+                canvas.drawRect(dims, borderPaint);

+                break;

+            default:

+        }

+    }

+

+    protected void drawBackground(Canvas canvas, RectF dims) {

+        switch (borderStyle) {

+            case ROUNDED:

+                canvas.drawRoundRect(dims, borderRadiusX, borderRadiusY, backgroundPaint);

+                break;

+            case SQUARE:

+                canvas.drawRect(dims, backgroundPaint);

+                break;

+            default:

+        }

+    }

+

+    /**

+     *

+     * @return The displayed title of this Plot.

+     */

+    public String getTitle() {

+        return getTitleWidget().getText();

+    }

+

+    /**

+     *

+     * @param title  The title to display on this Plot.

+     */

+    public void setTitle(String title) {

+        titleWidget.setText(title);

+    }

+

+    public LayoutManager getLayoutManager() {

+        return layoutManager;

+    }

+

+    public void setLayoutManager(LayoutManager layoutManager) {

+        this.layoutManager = layoutManager;

+    }

+

+    public TextLabelWidget getTitleWidget() {

+        return titleWidget;

+    }

+

+    public void setTitleWidget(TextLabelWidget titleWidget) {

+        this.titleWidget = titleWidget;

+    }

+

+    public Paint getBackgroundPaint() {

+        return backgroundPaint;

+    }

+

+    public void setBackgroundPaint(Paint backgroundPaint) {

+        this.backgroundPaint = backgroundPaint;

+    }

+

+    /**

+     * Convenience method - wraps the individual setMarginXXX methods into a single method.

+     * @param left

+     * @param top

+     * @param right

+     * @param bottom

+     */

+    public void setPlotMargins(float left, float top, float right, float bottom) {

+        setPlotMarginLeft(left);

+        setPlotMarginTop(top);

+        setPlotMarginRight(right);

+        setPlotMarginBottom(bottom);

+    }

+

+    /**

+     * Convenience method - wraps the individual setPaddingXXX methods into a single method.

+     * @param left

+     * @param top

+     * @param right

+     * @param bottom

+     */

+    public void setPlotPadding(float left, float top, float right, float bottom) {

+        setPlotPaddingLeft(left);

+        setPlotPaddingTop(top);

+        setPlotPaddingRight(right);

+        setPlotPaddingBottom(bottom);

+    }

+

+    public float getPlotMarginTop() {

+        return boxModel.getMarginTop();

+    }

+

+    public void setPlotMarginTop(float plotMarginTop) {

+        boxModel.setMarginTop(plotMarginTop);

+    }

+

+    public float getPlotMarginBottom() {

+        return boxModel.getMarginBottom();

+    }

+

+    public void setPlotMarginBottom(float plotMarginBottom) {

+        boxModel.setMarginBottom(plotMarginBottom);

+    }

+

+    public float getPlotMarginLeft() {

+        return boxModel.getMarginLeft();

+    }

+

+    public void setPlotMarginLeft(float plotMarginLeft) {

+        boxModel.setMarginLeft(plotMarginLeft);

+    }

+

+    public float getPlotMarginRight() {

+        return boxModel.getMarginRight();

+    }

+

+    public void setPlotMarginRight(float plotMarginRight) {

+        boxModel.setMarginRight(plotMarginRight);

+    }

+

+    public float getPlotPaddingTop() {

+        return boxModel.getPaddingTop();

+    }

+

+    public void setPlotPaddingTop(float plotPaddingTop) {

+        boxModel.setPaddingTop(plotPaddingTop);

+    }

+

+    public float getPlotPaddingBottom() {

+        return boxModel.getPaddingBottom();

+    }

+

+    public void setPlotPaddingBottom(float plotPaddingBottom) {

+        boxModel.setPaddingBottom(plotPaddingBottom);

+    }

+

+    public float getPlotPaddingLeft() {

+        return boxModel.getPaddingLeft();

+    }

+

+    public void setPlotPaddingLeft(float plotPaddingLeft) {

+        boxModel.setPaddingLeft(plotPaddingLeft);

+    }

+

+    public float getPlotPaddingRight() {

+        return boxModel.getPaddingRight();

+    }

+

+    public void setPlotPaddingRight(float plotPaddingRight) {

+        boxModel.setPaddingRight(plotPaddingRight);

+    }

+

+    public Paint getBorderPaint() {

+        return borderPaint;

+    }

+

+    /**

+     * Set's the paint used to draw the border.  Note that this method

+     * copies borderPaint and set's the copy's Paint.Style attribute to

+     * Paint.Style.STROKE.

+     * @param borderPaint

+     */

+    public void setBorderPaint(Paint borderPaint) {

+        if(borderPaint == null) {

+            this.borderPaint = null;

+        } else {

+            this.borderPaint = new Paint(borderPaint);

+            this.borderPaint.setStyle(Paint.Style.STROKE);

+        }

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/PlotListener.java b/AndroidPlot-Core/src/main/java/com/androidplot/PlotListener.java
new file mode 100644
index 0000000..e622215
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/PlotListener.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot;

+

+import android.graphics.Canvas;

+

+/**

+ * Defines methods used for monitoring events generated by a Plot.

+ */

+public interface PlotListener {

+

+    /**

+     * Fired immediately before the Plot "source" is drawn onto canvas.

+     * Commonly used by implementing Series instances to activate a read

+     * lock on it's self in preparation for the Plot's imminent reading

+     * of that series.

+     * @param source

+     * @param canvas

+     */

+    public void onBeforeDraw(Plot source, Canvas canvas);

+

+    /**

+     * Fired immediately after the Plot "source" is drawn onto canvas.

+     * Just as onBeforeDraw(...) is commonly used by Series implementations

+     * to activate a read lock, this method is commonly used to release that

+     * same lock.

+     * @param source

+     * @param canvas

+     */

+    public void onAfterDraw(Plot source, Canvas canvas);

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/Series.java b/AndroidPlot-Core/src/main/java/com/androidplot/Series.java
new file mode 100644
index 0000000..7fc90f5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/Series.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot;
+
+/**
+ * Base interface for all Series implementations
+ */
+public interface Series<T> {
+
+    /**
+     *
+     * @return The title of this Series.
+     */
+    public String getTitle();
+
+
+
+    // used primarily for synchronization.  can also be used to hang a condition on updates.
+
+    /**
+     * Called whenever the plot initiates a read of a Series.  In most cases this means that
+     * a complete read of the Series contents will proceed.
+     *//*
+    public void onReadBegin();
+
+    *//**
+     * Called when a Plot concludes reading of a Series.
+     *//*
+    public void onReadEnd();*/
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/exception/PlotRenderException.java b/AndroidPlot-Core/src/main/java/com/androidplot/exception/PlotRenderException.java
new file mode 100644
index 0000000..0515e2a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/exception/PlotRenderException.java
@@ -0,0 +1,23 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.exception;

+

+public class PlotRenderException extends Exception {

+    public PlotRenderException(String message) {

+        super(message);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieChart.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieChart.java
new file mode 100644
index 0000000..56f609d
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieChart.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ *    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 com.androidplot.pie;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import com.androidplot.Plot;
+import com.androidplot.ui.AnchorPosition;
+import com.androidplot.ui.SizeLayoutType;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.ui.YLayoutStyle;
+
+public class PieChart extends Plot<Segment, SegmentFormatter, PieRenderer> {
+
+    private static final int DEFAULT_PIE_WIDGET_H_DP = 18;
+    private static final int DEFAULT_PIE_WIDGET_W_DP = 10;
+
+    private static final int DEFAULT_PIE_WIDGET_Y_OFFSET_DP = 0;
+    private static final int DEFAULT_PIE_WIDGET_X_OFFSET_DP = 0;
+
+    public void setPieWidget(PieWidget pieWidget) {
+        this.pieWidget = pieWidget;
+    }
+
+    @SuppressWarnings("FieldCanBeLocal")
+    private PieWidget pieWidget;
+
+    public PieChart(Context context, String title) {
+        super(context, title);
+    }
+
+    public PieChart(Context context, String title, RenderMode mode) {
+        super(context, title, mode);
+    }
+
+    public PieChart(Context context, AttributeSet attributes) {
+        super(context, attributes);
+    }
+
+    @Override
+    protected void onPreInit() {
+        pieWidget = new PieWidget(
+                getLayoutManager(),
+                this,
+                new SizeMetrics(
+                        PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_H_DP),
+                        SizeLayoutType.FILL,
+                        PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_W_DP),
+                        SizeLayoutType.FILL));
+
+        pieWidget.position(
+                PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_X_OFFSET_DP),
+                XLayoutStyle.ABSOLUTE_FROM_CENTER,
+                PixelUtils.dpToPix(DEFAULT_PIE_WIDGET_Y_OFFSET_DP),
+                YLayoutStyle.ABSOLUTE_FROM_CENTER,
+                AnchorPosition.CENTER);
+
+        pieWidget.setPadding(10, 10, 10, 10);
+
+        // TODO: can't remember why this getClass() check is neccessary.  test if it actually is...
+        /*if (getClass().equals(PieChart.class) && attrs != null) {
+            loadAttrs(context, attrs);
+        }*/
+    }
+    
+    public PieWidget getPieWidget() {
+        return pieWidget;
+    }
+
+    public void addSegment(Segment segment, SegmentFormatter formatter) {
+        addSeries(segment, formatter);
+    }
+
+    public void removeSegment(Segment segment) {
+        removeSeries(segment);
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieRenderer.java
new file mode 100644
index 0000000..86364aa
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieRenderer.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ *    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 com.androidplot.pie;
+
+import android.graphics.*;
+
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.SeriesRenderer;
+
+import java.util.Set;
+
+public class PieRenderer extends SeriesRenderer<PieChart, Segment, SegmentFormatter> {
+
+    // starting angle to use when drawing the first radial line of the first segment.
+    @SuppressWarnings("FieldCanBeLocal")
+    private float startDeg = 0;
+    private float endDeg = 360;
+
+    // TODO: express donut in units other than px.
+    private float donutSize = 0.5f;
+    private DonutMode donutMode = DonutMode.PERCENT;
+
+    public enum DonutMode {
+        PERCENT,
+        DP,
+        PIXELS
+    }
+
+    public PieRenderer(PieChart plot) {
+        super(plot);
+    }
+    
+    public float getRadius(RectF rect) {
+    	return  rect.width() < rect.height() ? rect.width() / 2 : rect.height() / 2;
+    }
+
+    @Override
+    public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
+
+        float radius = getRadius(plotArea);
+        PointF origin = new PointF(plotArea.centerX(), plotArea.centerY());
+        
+        double[] values = getValues();
+        double scale = calculateScale(values);
+        float offset = startDeg;
+        Set<Segment> segments = getPlot().getSeriesSet();
+
+        //PointF lastRadial = calculateLineEnd(origin, radius, offset);
+
+        RectF rec = new RectF(origin.x - radius, origin.y - radius, origin.x + radius, origin.y + radius);
+        
+        int i = 0;
+        for (Segment segment : segments) {
+            float lastOffset = offset;
+            float sweep = (float) (scale * (values[i]) * 360);
+            offset += sweep;
+            //PointF radial = calculateLineEnd(origin, radius, offset);
+            drawSegment(canvas, rec, segment, getPlot().getFormatter(segment, PieRenderer.class),
+                    radius, lastOffset, sweep);
+            //lastRadial = radial;
+            i++;
+        }
+    }
+
+    protected void drawSegment(Canvas canvas, RectF bounds, Segment seg, SegmentFormatter f,
+                               float rad, float startAngle, float sweep) {
+        canvas.save();
+
+        float cx = bounds.centerX();
+        float cy = bounds.centerY();
+
+        float donutSizePx;
+        switch(donutMode) {
+            case PERCENT:
+                donutSizePx = donutSize * rad;
+                break;
+            case PIXELS:
+                donutSizePx = (donutSize > 0)?donutSize:(rad + donutSize);
+                break;
+            default:
+                throw new UnsupportedOperationException("Not yet implemented.");
+        }
+
+        // vertices of the first radial:
+        PointF r1Outer = calculateLineEnd(cx, cy, rad, startAngle);
+        PointF r1Inner = calculateLineEnd(cx, cy, donutSizePx, startAngle);
+
+        // vertices of the second radial:
+        PointF r2Outer = calculateLineEnd(cx, cy, rad, startAngle + sweep);
+        PointF r2Inner = calculateLineEnd(cx, cy, donutSizePx, startAngle + sweep);
+
+        Path clip = new Path();
+
+        //float outerStroke = f.getOuterEdgePaint().getStrokeWidth();
+        //float halfOuterStroke = outerStroke / 2;
+
+        // leave plenty of room on the outside for stroked borders;
+        // necessary because the clipping border is ugly
+        // and cannot be easily anti aliased.  Really we only care about masking off the
+        // radial edges.
+        clip.arcTo(new RectF(bounds.left - rad,
+                bounds.top - rad,
+                bounds.right + rad,
+                bounds.bottom + rad),
+                startAngle, sweep);
+        clip.lineTo(cx, cy);
+        clip.close();
+        canvas.clipPath(clip);
+
+        Path p = new Path();
+        p.arcTo(bounds, startAngle, sweep);
+        p.lineTo(r2Inner.x, r2Inner.y);
+
+        // sweep back to original angle:
+        p.arcTo(new RectF(
+                cx - donutSizePx,
+                cy - donutSizePx,
+                cx + donutSizePx,
+                cy + donutSizePx),
+                startAngle + sweep, -sweep);
+
+        p.close();
+
+        // fill segment:
+        canvas.drawPath(p, f.getFillPaint());
+
+        // draw radial lines
+        canvas.drawLine(r1Inner.x, r1Inner.y, r1Outer.x, r1Outer.y, f.getRadialEdgePaint());
+        canvas.drawLine(r2Inner.x, r2Inner.y, r2Outer.x, r2Outer.y, f.getRadialEdgePaint());
+
+        // draw inner line:
+        canvas.drawCircle(cx, cy, donutSizePx, f.getInnerEdgePaint());
+
+        // draw outer line:
+        canvas.drawCircle(cx, cy, rad, f.getOuterEdgePaint());
+        canvas.restore();
+
+        PointF labelOrigin = calculateLineEnd(cx, cy,
+                (rad-((rad- donutSizePx)/2)), startAngle + (sweep/2));
+
+        // TODO: move segment labelling outside the segment drawing loop
+        // TODO: so that the labels will not be clipped by the edge of the next
+        // TODO: segment being drawn.
+        drawSegmentLabel(canvas, labelOrigin, seg, f);
+    }
+
+    protected void drawSegmentLabel(Canvas canvas, PointF origin,
+                                    Segment seg, SegmentFormatter f) {
+        canvas.drawText(seg.getTitle(), origin.x, origin.y, f.getLabelPaint());
+
+    }
+
+    @Override
+    protected void doDrawLegendIcon(Canvas canvas, RectF rect, SegmentFormatter formatter) {
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Determines how many counts there are per cent of whatever the
+     * pie chart is displaying as a fraction, 1 being 100%.
+     */
+    private double calculateScale(double[] values) {
+        double total = 0;
+        for (int i = 0; i < values.length; i++) {
+			total += values[i];
+		}
+
+        return (1d / total);
+    }
+    
+	private double[] getValues() {
+		Set<Segment> segments = getPlot().getSeriesSet();
+		double[] result = new double[segments.size()];
+		int i = 0;
+		for (Segment seg : getPlot().getSeriesSet()) {
+			result[i] = seg.getValue().doubleValue();
+			i++;
+		}
+		return result;
+	}
+
+    private PointF calculateLineEnd(float x, float y, float rad, float deg) {
+        return calculateLineEnd(new PointF(x, y), rad, deg);
+    }
+
+    private PointF calculateLineEnd(PointF origin, float rad, float deg) {
+
+        double radians = deg * Math.PI / 180F;
+        double x = rad * Math.cos(radians);
+        double y = rad * Math.sin(radians);
+
+        // convert to screen space:
+        return new PointF(origin.x + (float) x, origin.y + (float) y);
+    }
+
+    public void setDonutSize(float size, DonutMode mode) {
+        switch(mode) {
+            case PERCENT:
+                if(size < 0 || size > 1) {
+                    throw new IllegalArgumentException(
+                            "Size parameter must be between 0 and 1 when operating in PERCENT mode.");
+                }
+                break;
+            case PIXELS:
+            	break;
+            default:
+                throw new UnsupportedOperationException("Not yet implemented.");
+        }
+        donutMode = mode;
+        donutSize = size;
+    }
+    
+    public void setStartDeg(float deg) {
+        startDeg = deg;
+    }
+    
+    public void setEndDeg(float deg) {
+        endDeg = deg;
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieWidget.java
new file mode 100644
index 0000000..ebfa8f8
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/PieWidget.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ *    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 com.androidplot.pie;
+
+import android.graphics.*;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.ui.LayoutManager;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.widget.Widget;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Visualizes data as a pie chart.
+ */
+public class PieWidget extends Widget {
+
+    private PieChart pieChart;
+
+    public PieWidget(LayoutManager layoutManager, PieChart pieChart, SizeMetrics metrics) {
+        super(layoutManager, metrics);
+        this.pieChart = pieChart;
+    }
+
+    @Override
+    protected void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException {
+
+        for(PieRenderer renderer : pieChart.getRendererList()) {
+            renderer.render(canvas, widgetRect);
+        }
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/Segment.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/Segment.java
new file mode 100644
index 0000000..1b18f15
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/Segment.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ *    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 com.androidplot.pie;
+
+import com.androidplot.Series;
+
+public class Segment implements Series<Number> {
+
+    private String title;
+
+    private Number value;
+
+    public Segment(String title, Number value) {
+        this.title = title;
+        this.setValue(value);
+    }
+
+    @Override
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Number getValue() {
+        return value;
+    }
+
+    public void setValue(Number value) {
+        this.value = value;
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/pie/SegmentFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/pie/SegmentFormatter.java
new file mode 100644
index 0000000..5c75950
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/pie/SegmentFormatter.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ *    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 com.androidplot.pie;
+
+import android.graphics.Color;
+import android.graphics.Paint;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.Formatter;
+
+public class SegmentFormatter extends Formatter<PieChart> {
+
+    private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
+    private static final int DEFAULT_EDGE_COLOR = Color.BLACK;
+    private static final int DEFAULT_LABEL_COLOR = Color.WHITE;
+    private static final float DEFAULT_EDGE_THICKNESS = 3;
+    private static final float DEFAULT_LABEL_MARKER_THICKNESS = 3;
+    private static final float DEFAULT_LABEL_FONT_SIZE = 18;
+
+    private Paint innerEdgePaint;
+    private Paint outerEdgePaint;
+    private Paint radialEdgePaint;
+    private Paint fillPaint;
+
+    private Paint labelPaint;
+    private Paint labelMarkerPaint;
+
+    {
+        setFillPaint(new Paint());
+        // outer edge:
+        setOuterEdgePaint(new Paint());
+        getOuterEdgePaint().setStyle(Paint.Style.STROKE);
+        getOuterEdgePaint().setStrokeWidth(DEFAULT_EDGE_THICKNESS);
+        getOuterEdgePaint().setAntiAlias(true);
+
+        // inner edge:
+        setInnerEdgePaint(new Paint());
+        getInnerEdgePaint().setStyle(Paint.Style.STROKE);
+        getInnerEdgePaint().setStrokeWidth(DEFAULT_EDGE_THICKNESS);
+        getInnerEdgePaint().setAntiAlias(true);
+
+        // radial edge:
+        setRadialEdgePaint(new Paint());
+        getRadialEdgePaint().setStyle(Paint.Style.STROKE);
+        getRadialEdgePaint().setStrokeWidth(DEFAULT_EDGE_THICKNESS);
+        getRadialEdgePaint().setAntiAlias(true);
+
+        // label paint:
+        setLabelPaint(new Paint());
+        getLabelPaint().setColor(DEFAULT_LABEL_COLOR);
+        getLabelPaint().setTextSize(DEFAULT_LABEL_FONT_SIZE);
+        getLabelPaint().setAntiAlias(true);
+        getLabelPaint().setTextAlign(Paint.Align.CENTER);
+        //getLabelPaint().setShadowLayer(5, 4, 4, Color.BLACK);
+
+        // label marker paint:
+        setLabelMarkerPaint(new Paint());
+        getLabelMarkerPaint().setColor(DEFAULT_LABEL_COLOR);
+        getLabelMarkerPaint().setStrokeWidth(DEFAULT_LABEL_MARKER_THICKNESS);
+    }
+
+    /**
+     * Should only be used in conjunction with calls to configure()...
+     */
+    public SegmentFormatter() {}
+
+    public SegmentFormatter(Integer fillColor) {
+        if(fillColor != null) {
+            getFillPaint().setColor(fillColor);
+        } else {
+            getFillPaint().setColor(DEFAULT_FILL_COLOR);
+        }
+    }
+
+    public SegmentFormatter(Integer fillColor, Integer borderColor) {
+        this(fillColor);
+        getInnerEdgePaint().setColor(borderColor);
+        getOuterEdgePaint().setColor(borderColor);
+        getRadialEdgePaint().setColor(borderColor);
+    }
+
+    public SegmentFormatter(Integer fillColor, Integer outerEdgeColor,
+                            Integer innerEdgeColor, Integer radialEdgeColor) {
+        this(fillColor);
+
+
+        if(getOuterEdgePaint() != null) {
+            getOuterEdgePaint().setColor(outerEdgeColor);
+        } else {
+            outerEdgePaint = new Paint();
+            getOuterEdgePaint().setColor(DEFAULT_EDGE_COLOR);
+        }
+
+        if (getInnerEdgePaint() != null) {
+            getInnerEdgePaint().setColor(innerEdgeColor);
+        } else {
+            outerEdgePaint = new Paint();
+            getInnerEdgePaint().setColor(DEFAULT_EDGE_COLOR);
+        }
+
+        if (getRadialEdgePaint() != null) {
+            getRadialEdgePaint().setColor(radialEdgeColor);
+        } else {
+            radialEdgePaint = new Paint();
+            getRadialEdgePaint().setColor(DEFAULT_EDGE_COLOR);
+        }
+    }
+
+    @Override
+    public Class<? extends SeriesRenderer> getRendererClass() {
+        return PieRenderer.class;
+    }
+
+    @Override
+    public SeriesRenderer getRendererInstance(PieChart plot) {
+        return new PieRenderer(plot);
+    }
+
+    public Paint getInnerEdgePaint() {
+        return innerEdgePaint;
+    }
+
+    public void setInnerEdgePaint(Paint innerEdgePaint) {
+        this.innerEdgePaint = innerEdgePaint;
+    }
+
+    public Paint getOuterEdgePaint() {
+        return outerEdgePaint;
+    }
+
+    public void setOuterEdgePaint(Paint outerEdgePaint) {
+        this.outerEdgePaint = outerEdgePaint;
+    }
+
+    public Paint getRadialEdgePaint() {
+        return radialEdgePaint;
+    }
+
+    public void setRadialEdgePaint(Paint radialEdgePaint) {
+        this.radialEdgePaint = radialEdgePaint;
+    }
+
+    public Paint getFillPaint() {
+        return fillPaint;
+    }
+
+    public void setFillPaint(Paint fillPaint) {
+        this.fillPaint = fillPaint;
+    }
+
+    public Paint getLabelPaint() {
+        return labelPaint;
+    }
+
+    public void setLabelPaint(Paint labelPaint) {
+        this.labelPaint = labelPaint;
+    }
+
+    public Paint getLabelMarkerPaint() {
+        return labelMarkerPaint;
+    }
+
+    public void setLabelMarkerPaint(Paint labelMarkerPaint) {
+        this.labelMarkerPaint = labelMarkerPaint;
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/AnchorPosition.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/AnchorPosition.java
new file mode 100644
index 0000000..e1f0b2e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/AnchorPosition.java
@@ -0,0 +1,33 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+/**

+ * Enumeration of possible anchor positions that a {@link com.androidplot.ui.widget.Widget} can use.  There are a total

+ * 8 possible anchor positions representing each corner of the Widget and the point exactly between each corner.

+ */

+public enum AnchorPosition {

+    TOP_MIDDLE,

+    LEFT_TOP,    // default

+    LEFT_MIDDLE,

+    LEFT_BOTTOM,

+    RIGHT_TOP,

+    RIGHT_MIDDLE,

+    RIGHT_BOTTOM,

+    BOTTOM_MIDDLE,

+    CENTER

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModel.java
new file mode 100644
index 0000000..e80728c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModel.java
@@ -0,0 +1,161 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+

+/**

+ * Convenience implementation of {@link BoxModelable}.

+ */

+public class BoxModel implements BoxModelable{

+

+    private float marginLeft;

+    private float marginTop;

+    private float marginRight;

+    private float marginBottom;

+    

+

+    private float paddingLeft;

+    private float paddingTop;

+    private float paddingRight;

+    private float paddingBottom;

+    //private RectF marginRect;

+    //private RectF paddingRect;

+

+    public BoxModel() {

+        

+    }

+

+    @SuppressWarnings("SameParameterValue")

+    public BoxModel(float marginLeft, float marginTop, float marginRight, float marginBottom,

+                    float paddingLeft, float paddingTop, float paddingRight, float paddingBottom) {

+        this.marginLeft = marginLeft;

+        this.marginTop = marginTop;

+        this.marginRight = marginRight;

+        this.marginBottom = marginBottom;

+

+        this.paddingLeft = paddingLeft;

+        this.paddingTop = paddingTop;

+        this.paddingRight = paddingRight;

+        this.paddingBottom = paddingBottom;

+    }

+

+    /**

+     * Returns a RectF instance describing the inner edge of the margin layer.

+     * @param boundsRect

+     * @return

+     */

+    public RectF getMarginatedRect(RectF boundsRect) {

+        return new RectF( boundsRect.left + getMarginLeft(),

+                boundsRect.top + getMarginTop(),

+                boundsRect.right - getMarginRight(),

+                boundsRect.bottom - getMarginBottom());

+    }

+

+    /**

+     * Returns a RectF instance describing the inner edge of the padding layer.

+     * @param marginRect

+     * @return

+     */

+    public RectF getPaddedRect(RectF marginRect) {

+        return new RectF(marginRect.left + getPaddingLeft(),

+                marginRect.top+getPaddingTop(),

+                marginRect.right - getPaddingRight(),

+                marginRect.bottom - getPaddingBottom());

+    }

+

+    @Override

+    public void setMargins(float left, float top, float right, float bottom) {

+        setMarginLeft(left);

+        setMarginTop(top);

+        setMarginRight(right);

+        setMarginBottom(bottom);

+    }

+

+    @Override

+    public void setPadding(float left, float top, float right, float bottom) {

+        setPaddingLeft(left);

+        setPaddingTop(top);

+        setPaddingRight(right);

+        setPaddingBottom(bottom);

+    }

+

+

+    public float getMarginLeft() {

+        return marginLeft;

+    }

+

+    public void setMarginLeft(float marginLeft) {

+        this.marginLeft = marginLeft;

+    }

+

+    public float getMarginTop() {

+        return marginTop;

+    }

+

+    public void setMarginTop(float marginTop) {

+        this.marginTop = marginTop;

+    }

+

+    public float getMarginRight() {

+        return marginRight;

+    }

+

+    public void setMarginRight(float marginRight) {

+        this.marginRight = marginRight;

+    }

+

+    public float getMarginBottom() {

+        return marginBottom;

+    }

+

+    public void setMarginBottom(float marginBottom) {

+        this.marginBottom = marginBottom;

+    }

+

+    public float getPaddingLeft() {

+        return paddingLeft;

+    }

+

+    public void setPaddingLeft(float paddingLeft) {

+        this.paddingLeft = paddingLeft;

+    }

+

+    public float getPaddingTop() {

+        return paddingTop;

+    }

+

+    public void setPaddingTop(float paddingTop) {

+        this.paddingTop = paddingTop;

+    }

+

+    public float getPaddingRight() {

+        return paddingRight;

+    }

+

+    public void setPaddingRight(float paddingRight) {

+        this.paddingRight = paddingRight;

+    }

+

+    public float getPaddingBottom() {

+        return paddingBottom;

+    }

+

+    public void setPaddingBottom(float paddingBottom) {

+        this.paddingBottom = paddingBottom;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModelable.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModelable.java
new file mode 100644
index 0000000..7389e85
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/BoxModelable.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+

+/**

+ * Encapsulates the functionality of a BoxModel.

+ * See http://www.w3.org/TR/CSS21/box.html for a good explanation of how

+ * the box model works.

+ */

+public interface BoxModelable {

+    /**

+     * Returns a RectF instance describing the inner edge of the margin layer.

+     * @param boundsRect

+     * @return

+     */

+    public RectF getMarginatedRect(RectF boundsRect);

+

+    /**

+     * Returns a RectF instance describing the inner edge of the padding layer.

+     * @param marginRect

+     * @return

+     */

+    public RectF getPaddedRect(RectF marginRect);

+

+

+    public void setMargins(float left, float top, float right, float bottom);

+

+    public void setPadding(float left, float top, float right, float bottom);

+

+    public float getMarginLeft();

+

+    public void setMarginLeft(float marginLeft);

+

+    public float getMarginTop();

+

+    public void setMarginTop(float marginTop);

+

+    public float getMarginRight();

+

+    public void setMarginRight(float marginRight);

+

+    public float getMarginBottom();

+

+    public float getPaddingLeft();

+

+    public void setPaddingLeft(float paddingLeft);

+

+    public float getPaddingTop();

+

+    public void setPaddingTop(float paddingTop);

+

+    public float getPaddingRight();

+

+    public void setPaddingRight(float paddingRight);

+

+    public float getPaddingBottom();

+

+    public void setPaddingBottom(float paddingBottom);

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/DynamicTableModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/DynamicTableModel.java
new file mode 100644
index 0000000..1654054
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/DynamicTableModel.java
@@ -0,0 +1,286 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+

+import java.util.Iterator;

+

+/**

+ * Encapsulates the visual aspects of a table; number of rows and columns

+ * and the height and width in pixels of each element within the table.

+ * There is no support (yet) for variable size cells within a table;  all

+ * cells within a table share the same dimensions.

+ *

+ * The DynamicTableModel provides an Iterator implementation which returns a RectF

+ * of each subsequent cell, based on the order of the plot.  Tables with

+ * an order of COLUMN_MAJOR are traversed left to right column by column until

+ * the end of the row is reached, then proceeding to the next row.

+ * Tables with an order of ROW_MAJOR are traversed top to bottom row by row

+ * until the end of the row is reached, then proceeding to the next column.

+ */

+public class DynamicTableModel extends TableModel {

+

+    //private float cellWidth;

+    //private float cellHeight;

+    //private TableSizingMethod rowSizingMethod;

+    //private TableSizingMethod columnSizingMethod;

+

+

+    private int numRows;

+    private int numColumns;

+

+    private Float cellWidth;

+    private Float cellHeight;

+

+    private CellSizingMethod rowSizingMethod;

+    private CellSizingMethod columnSizingMethod;

+

+    /**

+     * Convenience method.  Sets order to ROW_MAJOR.

+     * @param numColumns

+     * @param numRows

+     */

+    public DynamicTableModel(int numColumns, int numRows) {

+        this(numColumns, numRows, TableOrder.ROW_MAJOR);

+

+    }

+

+    public DynamicTableModel(int numColumns, int numRows, TableOrder order) {

+        super(order);

+        this.numColumns = numColumns;

+        //this.cellWidth = cellWidth;

+        //this.rowSizingMethod = rowSizingMethod;

+        this.numRows = numRows;

+        //this.cellHeight = cellHeight;

+        //this.columnSizingMethod = columnSizingMethod;

+        //this.order = order;

+    }

+

+    /*public DynamicTableModel(Number colVal, CellSizingMethod colSzMethod, Number rowVal, CellSizingMethod rowSzMethod, TableOrder order) {

+        if(colVal == null || rowVal == null) {

+            throw new NullPointerException();

+        }

+        columnSizingMethod = colSzMethod;

+        switch(columnSizingMethod) {

+            case FILL:

+                numColumns = colVal.intValue();

+                break;

+            case FIXED:

+                cellWidth = colVal.floatValue();

+                break;

+        }

+        rowSzMethod = rowSzMethod;

+    }*/

+

+    @Override

+    public TableModelIterator getIterator(RectF tableRect, int totalElements) {

+        return new TableModelIterator(this, tableRect, totalElements);

+    }

+

+    /**

+     * Calculates the dimensions of a single element of this table with

+     * tableRect representing the overall dimensions of the table.

+     * @param tableRect Dimensions/position of the table

+     * @return a RectF representing the first (top-left) element in

+     * the tableRect passed in.

+     */

+    public RectF getCellRect(RectF tableRect, int numElements) {

+        RectF cellRect = new RectF();

+        cellRect.left = tableRect.left;

+        cellRect.top = tableRect.top;

+        //cellRect.bottom = getElementHeightPix(tableRect);

+        cellRect.bottom = tableRect.top + calculateCellSize(tableRect, TableModel.Axis.ROW, numElements);

+        //cellRect.right = getElementWidthPix(tableRect);

+        cellRect.right = tableRect.left + calculateCellSize(tableRect, TableModel.Axis.COLUMN, numElements);

+        return cellRect;

+    }

+

+    /**

+     * Figure out the size of a single cell across the specified axis.

+     * @param tableRect

+     * @param axis

+     * @param numElementsInTable

+     * @return

+     */

+    private float calculateCellSize(RectF tableRect,

+                                    Axis axis,

+                                    int numElementsInTable) {

+        //float elementSizeInPix = 0;

+        int axisElements = 0;

+        

+        float axisSizePix = 0;

+        switch (axis) {

+            case ROW:

+                //elementSizeInPix = cellHeight;

+                axisElements = numRows;

+                axisSizePix = tableRect.height();

+                break;

+            case COLUMN:

+                //elementSizeInPix = cellWidth;

+                axisElements = numColumns;

+                axisSizePix = tableRect.width();

+                break;

+        }

+        //if (elementSizeInPix != 0) {

+        //    return elementSizeInPix;

+        if(axisElements != 0) {

+            return axisSizePix / axisElements;

+        } else {

+            return axisSizePix / numElementsInTable;

+        }

+    }

+

+

+

+    public int getNumRows() {

+        return numRows;

+    }

+

+    public void setNumRows(int numRows) {

+        this.numRows = numRows;

+    }

+

+    public int getNumColumns() {

+        return numColumns;

+    }

+

+    public void setNumColumns(int numColumns) {

+        this.numColumns = numColumns;

+    }

+

+/*    public void setCellWidth(Float cellWidth) {

+        this.cellWidth = cellWidth;

+    }

+

+    public Float getCellWidth() {

+        return cellWidth;

+    }

+

+    public Float getCellHeight() {

+        return cellHeight;

+    }

+

+    public void setCellHeight(Float cellHeight) {

+        this.cellHeight = cellHeight;

+    }*/

+

+    private class TableModelIterator implements Iterator<RectF> {

+        private boolean isOk = true;

+        int lastColumn = 0;                     // most recent column iterated

+        int lastRow = 0;                        // most recent row iterated

+        int lastElement = 0;                    // last element index iterated

+        private DynamicTableModel dynamicTableModel;

+        private RectF tableRect;

+        private RectF lastElementRect;

+        private int totalElements;

+        private TableOrder order;

+

+        private int calculatedNumElements;

+        private int calculatedRows;             // number of rows to be iterated

+        private int calculatedColumns;          // number of columns to be iterated

+

+        public TableModelIterator(DynamicTableModel dynamicTableModel, RectF tableRect, int totalElements) {

+            this.dynamicTableModel = dynamicTableModel;

+            this.tableRect = tableRect;

+            this.totalElements = totalElements;

+            order = dynamicTableModel.getOrder();

+

+            // unlimited columns:

+            if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() >= 1) {

+                calculatedRows = dynamicTableModel.getNumRows();

+

+                // round up:

+                calculatedColumns = new Float((totalElements / (float) calculatedRows) + 0.5).intValue();

+            } else if(dynamicTableModel.getNumRows() == 0 && dynamicTableModel.getNumColumns() >= 1) {

+                //order = TableOrder.ROW_MAJOR;

+                calculatedColumns = dynamicTableModel.getNumColumns();

+                calculatedRows = new Float((totalElements / (float) calculatedColumns) + 0.5).intValue();

+            // unlimited rows and columns (impossible) so default a single row with n columns:

+            }else if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() == 0) {

+                calculatedRows = 1;

+                calculatedColumns = totalElements;

+            } else {

+                //order = dynamicTableModel.getOrder();

+                calculatedRows = dynamicTableModel.getNumRows();

+                calculatedColumns = dynamicTableModel.getNumColumns();

+            }

+            calculatedNumElements = calculatedRows * calculatedColumns;

+            lastElementRect = dynamicTableModel.getCellRect(tableRect, totalElements);

+        }

+

+        @Override

+        public boolean hasNext() {

+            return isOk && lastElement < calculatedNumElements;

+        }

+

+        @Override

+        public RectF next() {

+            if(!hasNext()) {

+                isOk = false;

+                throw new IndexOutOfBoundsException();

+            }

+

+            if (lastElement == 0) {

+                lastElement++;

+                return lastElementRect;

+            }

+

+            RectF nextElementRect = new RectF(lastElementRect);

+

+            switch (order) {

+                case ROW_MAJOR:

+                    if (dynamicTableModel.getNumColumns() > 0 && lastColumn >= (dynamicTableModel.getNumColumns() - 1)) {

+                        // move to the begining of the next row down:// move to the begining of the next row down:

+                        nextElementRect.offsetTo(tableRect.left, lastElementRect.bottom);

+                        lastColumn = 0;

+                        lastRow++;

+                    } else {

+                        // move to the next column over:

+                        nextElementRect.offsetTo(lastElementRect.right, lastElementRect.top);

+                        lastColumn++;

+                    }

+                    break;

+                case COLUMN_MAJOR:

+                    if (dynamicTableModel.getNumRows() > 0 && lastRow >= (dynamicTableModel.getNumRows() - 1)) {

+                        // move to the top of the next column over:

+                        nextElementRect.offsetTo(lastElementRect.right, tableRect.top);

+                        lastRow = 0;

+                        lastColumn++;

+                    } else {

+                        // move to the next row down:

+                        nextElementRect.offsetTo(lastElementRect.left, lastElementRect.bottom);

+                        lastRow++;

+                    }

+                    break;

+                // unknown/unsupported enum val:

+                default:

+                    isOk = false;

+                    throw new IllegalArgumentException();

+            }

+            lastElement++;

+            lastElementRect = nextElementRect;

+            return nextElementRect;

+

+        }

+

+        @Override

+        public void remove() {

+            throw new UnsupportedOperationException();

+        }

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/FixedTableModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/FixedTableModel.java
new file mode 100644
index 0000000..4c7c265
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/FixedTableModel.java
@@ -0,0 +1,148 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+

+import java.util.Iterator;

+

+public class FixedTableModel extends TableModel {

+    private float cellWidth;

+    private float cellHeight;

+    protected FixedTableModel(float cellWidth, float cellHeight, TableOrder order) {

+        super(order);

+        setCellWidth(cellWidth);

+        setCellHeight(cellHeight);

+    }

+

+    @Override

+    public Iterator<RectF> getIterator(RectF tableRect, int totalElements) {

+        return new FixedTableModelIterator(this, tableRect, totalElements);

+    }

+

+    public float getCellWidth() {

+        return cellWidth;

+    }

+

+    public void setCellWidth(float cellWidth) {

+        this.cellWidth = cellWidth;

+    }

+

+    public float getCellHeight() {

+        return cellHeight;

+    }

+

+    public void setCellHeight(float cellHeight) {

+        this.cellHeight = cellHeight;

+    }

+

+    private class FixedTableModelIterator implements Iterator<RectF> {

+

+        private FixedTableModel model;

+        private RectF tableRect;

+        private RectF lastRect;

+        private int numElements;

+        private int lastElement;

+        protected FixedTableModelIterator(FixedTableModel model, RectF tableRect, int numElements) {

+            this.model = model;

+            this.tableRect = tableRect;

+            this.numElements = numElements;

+            lastRect = new RectF(

+                    tableRect.left,

+                    tableRect.top,

+                    tableRect.left + model.getCellWidth(),

+                    tableRect.top + model.getCellHeight());

+        }

+

+        @Override

+        public boolean hasNext() {

+            // was this the last element or is there no room in either axis for another cell?

+            return !(lastElement >= numElements || (isColumnFinished() && isRowFinished()));

+        }

+

+        private boolean isColumnFinished() {

+            return lastRect.bottom + model.getCellHeight() > tableRect.height();

+            }

+

+        private boolean isRowFinished() {

+            return lastRect.right + model.getCellWidth() > tableRect.width();

+            }

+

+        @Override

+        public RectF next() {

+            try {

+                if (lastElement == 0) {

+                    return lastRect;

+                }

+

+                if (lastElement >= numElements) {

+                    throw new IndexOutOfBoundsException();

+                }

+                switch (model.getOrder()) {

+                    case ROW_MAJOR:

+                        if (isColumnFinished()) {

+                            moveOverAndUp();

+                        } else {

+                            moveDown();

+                        }

+                        break;

+                    case COLUMN_MAJOR:

+                        if (isRowFinished()) {

+                            moveDownAndBack();

+                        } else {

+                            moveOver();

+                        }

+                        break;

+                    default:

+                        throw new UnsupportedOperationException();

+                }

+                return lastRect;

+            } finally {

+                lastElement++;

+            }

+        }

+

+        private void moveDownAndBack() {

+            //RectF rect = new RectF(lastRect);

+            lastRect.offsetTo(tableRect.left, lastRect.bottom);

+            //return rect;

+        }

+

+        private void moveOverAndUp() {

+            //RectF rect = new RectF(lastRect);

+            lastRect.offsetTo(lastRect.right, tableRect.top);

+            //return rect;

+        }

+

+        private void moveOver() {

+            //RectF rect = new RectF(lastRect);

+            lastRect.offsetTo(lastRect.right, lastRect.top);

+            //return rect;

+        }

+

+        private void moveDown() {

+            //RectF rect = new RectF(lastRect);

+            lastRect.offsetTo(lastRect.left, lastRect.bottom);

+            //return rect;

+        }

+

+        @Override

+        public void remove() {

+            throw new UnsupportedOperationException();

+        }

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/Formatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Formatter.java
new file mode 100644
index 0000000..dbebaa2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Formatter.java
@@ -0,0 +1,61 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.content.Context;

+import com.androidplot.Plot;

+import com.androidplot.util.Configurator;

+

+/**

+ * Base class of all Formatters.  Encapsulates visual elements of a series; line style, color etc.

+ * Implementors of this class should include both a default constructor and a one argument

+ * constructor in the following form:

+ *

+ * <pre>

+ * {@code

+ * // provided as a convenience to users; allows instantiation and

+ * // xml configuration in a single line.

+ * public MyFormatter(Context ctx, int xmlCfgId) {

+ *     // prevent configuration of classes derived from this one:

+ *     if (getClass().equals(MyFormatter.class)) {

+ *         Configurator.configure(ctx, this, xmlCfgId);

+ *     }

+ * }

+ * </pre>

+ */

+public abstract class Formatter<PlotType extends Plot> {

+

+    public Formatter<PlotType> configure(Context ctx, int xmlCfgId) {

+        Configurator.configure(ctx, this, xmlCfgId);

+        return this;

+    }

+

+

+

+    /**

+     *

+     * @return The Class of SeriesRenderer that should be used.

+     */

+    public abstract Class<? extends SeriesRenderer> getRendererClass();

+

+    /**

+     *

+     * @return An instance of SeriesRenderer that took plot as an argument to its constructor.

+     */

+    public abstract SeriesRenderer getRendererInstance(PlotType plot);

+

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutManager.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutManager.java
new file mode 100644
index 0000000..1983ca7
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutManager.java
@@ -0,0 +1,268 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.*;

+import android.view.MotionEvent;

+import android.view.View;

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.ui.widget.Widget;

+import com.androidplot.util.DisplayDimensions;

+import com.androidplot.util.ZLinkedList;

+

+public class LayoutManager extends ZLinkedList<Widget>

+        implements View.OnTouchListener, Resizable {

+    private boolean drawAnchorsEnabled = false;

+    private Paint anchorPaint;

+    private boolean drawOutlinesEnabled = false;

+    private Paint outlinePaint;

+    private boolean drawOutlineShadowsEnabled = false;

+    private Paint outlineShadowPaint;

+    private boolean drawMarginsEnabled = false;

+    private Paint marginPaint;

+    private boolean drawPaddingEnabled = false;

+    private Paint paddingPaint;

+    private DisplayDimensions displayDims = new DisplayDimensions();

+

+    // cache of widget rects

+    //private HashMap<Widget, DisplayDimensions> widgetRects;

+

+    {

+        //widgetRects = new HashMap<Widget, DisplayDimensions>();

+        anchorPaint = new Paint();

+        anchorPaint.setStyle(Paint.Style.FILL);

+        anchorPaint.setColor(Color.GREEN);

+        outlinePaint = new Paint();

+        outlinePaint.setColor(Color.GREEN);

+        outlinePaint.setStyle(Paint.Style.STROKE);

+        marginPaint = new Paint();

+        marginPaint.setColor(Color.YELLOW);

+        marginPaint.setStyle(Paint.Style.FILL);

+        marginPaint.setAlpha(200);

+        paddingPaint= new Paint();

+        paddingPaint.setColor(Color.BLUE);

+        paddingPaint.setStyle(Paint.Style.FILL);

+        paddingPaint.setAlpha(200);

+    }

+

+    /**

+     * Invoked immediately following XML configuration.

+     */

+    public synchronized void onPostInit() {

+        for(Widget w : elements()) {

+            w.onPostInit();

+        }

+    }

+

+    public LayoutManager() {

+    }

+

+    public void setMarkupEnabled(boolean enabled) {

+        setDrawOutlinesEnabled(enabled);

+        setDrawAnchorsEnabled(enabled);

+        setDrawMarginsEnabled(enabled);

+        setDrawPaddingEnabled(enabled);

+        setDrawOutlineShadowsEnabled(enabled);

+    }

+

+    public void draw(Canvas canvas) throws PlotRenderException {

+        if(isDrawMarginsEnabled()) {

+            drawSpacing(canvas, displayDims.canvasRect, displayDims.marginatedRect, marginPaint);

+        }

+        if (isDrawPaddingEnabled()) {

+            drawSpacing(canvas, displayDims.marginatedRect, displayDims.paddedRect, paddingPaint);

+        }

+        for (Widget widget : elements()) {

+            //int canvasState = canvas.save(Canvas.ALL_SAVE_FLAG); // preserve clipping etc

+            try {

+                canvas.save(Canvas.ALL_SAVE_FLAG);

+                PositionMetrics metrics = widget.getPositionMetrics();

+                float elementWidth = widget.getWidthPix(displayDims.paddedRect.width());

+                float elementHeight = widget.getHeightPix(displayDims.paddedRect.height());

+                PointF coords = widget.getElementCoordinates(elementHeight,

+                        elementWidth, displayDims.paddedRect, metrics);

+

+                //RectF widgetRect = new RectF(coords.x, coords.y, coords.x + elementWidth, coords.y + elementHeight);

+                //DisplayDimensions dims = widgetRects.get(widget);

+                DisplayDimensions dims = widget.getWidgetDimensions();

+                //RectF widgetRect = widgetRects.get(widget);

+

+                if (drawOutlineShadowsEnabled) {

+                    canvas.drawRect(dims.canvasRect, outlineShadowPaint);

+                }

+

+                // not positive why this is, but the rect clipped by clipRect is 1 less than the one drawn by drawRect.

+                // so this is necessary to avoid clipping borders.  I suspect that its a floating point

+                // jitter issue.

+                if (widget.isClippingEnabled()) {

+                    //RectF clipRect = new RectF(l-1, t-1, r + 1, b + 1);

+                    //canvas.clipRect(clipRect, Region.Op.REPLACE);

+                    canvas.clipRect(dims.canvasRect, Region.Op.INTERSECT);

+                }

+                widget.draw(canvas, dims.canvasRect);

+

+                //RectF marginatedWidgetRect = widget.getMarginatedRect(dims.canvasRect);

+                //RectF paddedWidgetRect = widget.getPaddedRect(marginatedWidgetRect);

+

+                if (drawMarginsEnabled) {

+                    drawSpacing(canvas, dims.canvasRect, dims.marginatedRect, getMarginPaint());

+                }

+

+                if (drawPaddingEnabled) {

+                    drawSpacing(canvas, dims.marginatedRect, dims.paddedRect, getPaddingPaint());

+                }

+

+                if (drawAnchorsEnabled) {

+                    PointF anchorCoords =

+                            Widget.getAnchorCoordinates(coords.x, coords.y, elementWidth,

+                                    elementHeight, metrics.getAnchor());

+                    drawAnchor(canvas, anchorCoords);

+                }

+

+

+                if (drawOutlinesEnabled) {

+                    outlinePaint.setAntiAlias(true);

+                    canvas.drawRect(dims.canvasRect, outlinePaint);

+                }

+            } finally {

+                //canvas.restoreToCount(canvasState);  // restore clipping etc.

+                canvas.restore();

+            }

+        }

+    }

+

+    private void drawSpacing(Canvas canvas, RectF outer, RectF inner, Paint paint) {

+        //int saved = canvas.save(Canvas.ALL_SAVE_FLAG);

+        try {

+            canvas.save(Canvas.ALL_SAVE_FLAG);

+            canvas.clipRect(inner, Region.Op.DIFFERENCE);

+            canvas.drawRect(outer, paint);

+            //canvas.restoreToCount(saved);

+        } finally {

+            canvas.restore();

+        }

+    }

+

+    protected void drawAnchor(Canvas canvas, PointF coords) {

+        float anchorSize = 4;

+        canvas.drawRect(coords.x-anchorSize, coords.y-anchorSize, coords.x+anchorSize, coords.y+anchorSize, anchorPaint);

+

+    }

+

+    public boolean isDrawOutlinesEnabled() {

+        return drawOutlinesEnabled;

+    }

+

+    public void setDrawOutlinesEnabled(boolean drawOutlinesEnabled) {

+        this.drawOutlinesEnabled = drawOutlinesEnabled;

+    }

+

+    public Paint getOutlinePaint() {

+        return outlinePaint;

+    }

+

+    public void setOutlinePaint(Paint outlinePaint) {

+        this.outlinePaint = outlinePaint;

+    }

+

+    public boolean isDrawAnchorsEnabled() {

+        return drawAnchorsEnabled;

+    }

+

+    public void setDrawAnchorsEnabled(boolean drawAnchorsEnabled) {

+        this.drawAnchorsEnabled = drawAnchorsEnabled;

+    }

+

+    public boolean isDrawMarginsEnabled() {

+        return drawMarginsEnabled;

+    }

+

+    public void setDrawMarginsEnabled(boolean drawMarginsEnabled) {

+        this.drawMarginsEnabled = drawMarginsEnabled;

+    }

+

+    public Paint getMarginPaint() {

+        return marginPaint;

+    }

+

+    public void setMarginPaint(Paint marginPaint) {

+        this.marginPaint = marginPaint;

+    }

+

+    public boolean isDrawPaddingEnabled() {

+        return drawPaddingEnabled;

+    }

+

+    public void setDrawPaddingEnabled(boolean drawPaddingEnabled) {

+        this.drawPaddingEnabled = drawPaddingEnabled;

+    }

+

+    public Paint getPaddingPaint() {

+        return paddingPaint;

+    }

+

+    public void setPaddingPaint(Paint paddingPaint) {

+        this.paddingPaint = paddingPaint;

+    }

+

+    public boolean isDrawOutlineShadowsEnabled() {

+        return drawOutlineShadowsEnabled;

+    }

+

+    public void setDrawOutlineShadowsEnabled(boolean drawOutlineShadowsEnabled) {

+        this.drawOutlineShadowsEnabled = drawOutlineShadowsEnabled;

+        if(drawOutlineShadowsEnabled && outlineShadowPaint == null) {

+            // use a default shadow effect in the case where none has been set:

+            outlineShadowPaint = new Paint();

+            outlineShadowPaint.setColor(Color.DKGRAY);

+            outlineShadowPaint.setStyle(Paint.Style.FILL);

+            outlineShadowPaint.setShadowLayer(3, 5, 5, Color.BLACK);

+        }

+    }

+

+    public Paint getOutlineShadowPaint() {

+        return outlineShadowPaint;

+    }

+

+    public void setOutlineShadowPaint(Paint outlineShadowPaint) {

+        this.outlineShadowPaint = outlineShadowPaint;

+    }

+

+    @Override

+    public boolean onTouch(View v, MotionEvent event) {

+        return false;

+    }

+

+    /**

+     * Recalculates layouts for all widgets using last set

+     * DisplayDimensions.  Care should be excersized when choosing when

+     * to call this method as it is a relatively slow operation.

+     */

+    public void refreshLayout() {

+        //widgetRects.clear();

+        for (Widget widget : elements()) {

+            widget.layout(displayDims);

+        }

+    }

+

+    @Override

+    public void layout(final DisplayDimensions dims) {

+        this.displayDims = dims;

+

+        refreshLayout();

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutMetric.java
new file mode 100644
index 0000000..9061913
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/LayoutMetric.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+

+

+abstract class LayoutMetric<LayoutType extends Enum> {

+

+    private LayoutType layoutType;

+

+    //private LayoutType layoutType;

+    private float value;

+    //private float lastRow;

+

+    public LayoutMetric(float value, LayoutType layoutType) {

+        validatePair(value, layoutType);

+        set(value, layoutType);

+        //setLayoutType(layoutType);

+        //setValue(value);

+        //setLayoutType(layoutType);

+    }

+

+    /**

+     * Verifies that the values passed in are valid for the layout algorithm being used.

+     * @param value 

+     * @param layoutType

+     */

+    protected abstract void validatePair(float value, LayoutType layoutType);

+

+    public void set(float value, LayoutType layoutType) {

+        validatePair(value, layoutType);

+        this.value = value;

+        this.layoutType = layoutType;

+    }

+

+    public float getValue() {

+        return value;

+    }

+

+    public void setValue(float value) {

+        validatePair(value, layoutType);

+        this.value = value;

+    }

+

+    public abstract float getPixelValue(float size);

+

+    public LayoutType getLayoutType() {

+        return layoutType;

+    }

+

+    public void setLayoutType(LayoutType layoutType) {

+        validatePair(value, layoutType);

+        this.layoutType = layoutType;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetric.java
new file mode 100644
index 0000000..7183760
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetric.java
@@ -0,0 +1,87 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+public abstract class PositionMetric<LayoutType extends Enum> extends LayoutMetric<LayoutType> {

+

+    protected enum Origin {

+        FROM_BEGINING,

+        FROM_CENTER,

+        FROM_END

+    }

+

+    protected enum LayoutMode {

+        ABSOLUTE,

+        RELATIVE

+    }

+

+    public PositionMetric(float value, LayoutType layoutType) {

+        super(value, layoutType);

+    }

+

+    /**

+     * Throws IllegalArgumentException if there is a problem.

+     * @param value

+     * @param layoutMode

+     * @throws IllegalArgumentException

+     */

+    protected static void validateValue(float value, LayoutMode layoutMode) throws IllegalArgumentException {

+        switch(layoutMode) {

+            case ABSOLUTE:

+                break;

+            case RELATIVE:

+                if(value < -1 || value > 1) {

+                    throw new IllegalArgumentException("Relative layout values must be within the range of -1 to 1.");

+                }

+                break;

+            default:

+                throw new IllegalArgumentException("Unknown LayoutMode: " + layoutMode);

+        }

+

+    }

+

+    protected float getAbsolutePosition(float size, Origin origin) {

+        switch(origin) {

+            case FROM_BEGINING:

+                return getValue();

+            case FROM_CENTER:

+                return (size/2f) + getValue();

+            case FROM_END:

+                return size - getValue();

+            default:

+                 throw new IllegalArgumentException("Unsupported Origin: " + origin);

+        }

+    }

+

+    protected float getRelativePosition(float size, Origin origin) {

+        //throw new UnsupportedOperationException("Not yet implemented.");

+

+        switch(origin) {

+            case FROM_BEGINING:

+                return size * getValue();

+            case FROM_CENTER:

+                return (size/2f) + ((size/2f) * getValue());

+            case FROM_END:

+                return size + (size*getValue());

+            default:

+                 throw new IllegalArgumentException("Unsupported Origin: " + origin);

+        }

+

+    }

+

+

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetrics.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetrics.java
new file mode 100644
index 0000000..34d06a3
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/PositionMetrics.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+public class PositionMetrics implements Comparable<PositionMetrics> {

+

+    private XPositionMetric xPositionMetric;

+    private YPositionMetric yPositionMetric;

+    private AnchorPosition anchor;

+    private float layerDepth;

+

+    public PositionMetrics(float x, XLayoutStyle xLayoutStyle, float y, YLayoutStyle yLayoutStyle, AnchorPosition anchor) {

+        setXPositionMetric(new XPositionMetric(x, xLayoutStyle));

+        setYPositionMetric(new YPositionMetric(y, yLayoutStyle));

+        setAnchor(anchor);

+

+    }

+

+    public YPositionMetric getYPositionMetric() {

+        return yPositionMetric;

+    }

+

+    public void setYPositionMetric(YPositionMetric yPositionMetric) {

+        this.yPositionMetric = yPositionMetric;

+    }

+

+    public AnchorPosition getAnchor() {

+        return anchor;

+    }

+

+    public void setAnchor(AnchorPosition anchor) {

+        this.anchor = anchor;

+    }

+

+    @Override

+    public int compareTo(PositionMetrics o) {

+        if(this.layerDepth < o.layerDepth) {

+            return -1;

+        } else if(this.layerDepth == o.layerDepth) {

+            return 0;

+        } else {

+            return 1;

+        }

+    }

+

+    public XPositionMetric getXPositionMetric() {

+        return xPositionMetric;

+    }

+

+    public void setXPositionMetric(XPositionMetric xPositionMetric) {

+        this.xPositionMetric = xPositionMetric;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/RenderBundle.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/RenderBundle.java
new file mode 100644
index 0000000..75303ac
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/RenderBundle.java
@@ -0,0 +1,47 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import com.androidplot.Series;

+import com.androidplot.xy.XYSeriesFormatter;

+

+public abstract class RenderBundle<RenderBundleType extends RenderBundle, SeriesType extends Series, SeriesFormatterType extends XYSeriesFormatter> {

+    //private XYDataset series;

+    private Series series;

+    private SeriesFormatterType formatter;

+

+    public RenderBundle(SeriesType series, SeriesFormatterType formatter) {

+        this.formatter = formatter;

+        this.series = series;

+    }

+

+    public Series getSeries() {

+        return series;

+    }

+

+    public void setSeries(Series series) {

+        this.series = series;

+    }

+

+    public SeriesFormatterType getFormatter() {

+        return formatter;

+    }

+

+    public void setFormatter(SeriesFormatterType formatter) {

+        this.formatter = formatter;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/Resizable.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Resizable.java
new file mode 100644
index 0000000..34a1c89
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/Resizable.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.Canvas;

+import android.graphics.RectF;

+import com.androidplot.util.DisplayDimensions;

+

+/**

+ * Used by classes that depend on dimensional values to lay themselves out and draw.

+ * Consideration should be given to synchronizing with any draw routines that also

+ * exist within the class.

+ */

+public interface Resizable {

+

+    /**

+     * Called when a change to the class' dimensions is made.  This method is responsible

+     * for cascading calls to update for any logical children of this class, for example

+     * the Plot class is responsible for updating the LayoutManager.  Note that while dims

+     * is marked final in this interface, the compiler will not enforce it.  Implementors of

+     * this method should take care not to make changes to dims as this will affect parent

+     * Resizables in likely undesired ways.

+     * @param dims

+     */

+    public void layout(final DisplayDimensions dims);

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesAndFormatterList.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesAndFormatterList.java
new file mode 100644
index 0000000..7d90e32
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesAndFormatterList.java
@@ -0,0 +1,91 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import com.androidplot.Series;

+

+import java.util.LinkedList;

+import java.util.List;

+

+/**

+ * Associates a Series with a Formatter.

+ * @param <SeriesType>

+ * @param <FormatterType>

+ */

+public class SeriesAndFormatterList<SeriesType extends Series, FormatterType> {

+    private LinkedList<SeriesType> seriesList;

+    private LinkedList<FormatterType> formatterList;

+    {

+        seriesList = new LinkedList<SeriesType>();

+        formatterList = new LinkedList<FormatterType>();

+    }

+

+    public boolean contains(SeriesType series) {

+        return seriesList.contains(series);

+    }

+

+    public int size() {

+        return seriesList.size();

+    }

+

+    public List<SeriesType> getSeriesList() {

+        return seriesList;

+    }

+

+    public List<FormatterType> getFormatterList() {

+        return formatterList;

+    }

+

+    public boolean add(SeriesType series, FormatterType formatter) {

+        if(series == null || formatter == null) {

+            throw new IllegalArgumentException("series and formatter must not be null.");

+        }

+        if(seriesList.contains(series)) {

+            return false;

+        }

+        seriesList.add(series);

+        formatterList.add(formatter);

+        return true;

+    }

+

+    public boolean remove(SeriesType series) {

+        int index = seriesList.indexOf(series);

+        if(index < 0) {

+            return false;

+        }

+        seriesList.remove(index);

+        formatterList.remove(index);

+        return true;

+    }

+

+

+    public FormatterType getFormatter(SeriesType series) {

+        return formatterList.get(seriesList.indexOf(series));

+    }

+

+    public FormatterType getFormatter(int index) {

+        return formatterList.get(index);

+    }

+

+    public SeriesType getSeries(int index) {

+        return seriesList.get(index);

+    }

+

+    public FormatterType setFormatter(SeriesType series, FormatterType formatter) {

+        return formatterList.set(seriesList.indexOf(series), formatter);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesRenderer.java
new file mode 100644
index 0000000..b32f12d
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SeriesRenderer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.ui;
+
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.graphics.Region;
+import com.androidplot.Series;
+import com.androidplot.exception.PlotRenderException;
+import com.androidplot.Plot;
+
+public abstract class SeriesRenderer
+        <PlotType extends Plot, SeriesType extends Series, SeriesFormatterType extends Formatter> {
+    private PlotType plot;
+
+    public SeriesRenderer(PlotType plot) {
+        this.plot = plot;
+    }
+
+    public PlotType getPlot() {
+        return plot;
+    }
+
+    public void setPlot(PlotType plot) {
+        this.plot = plot;
+    }
+
+    public SeriesAndFormatterList<SeriesType,SeriesFormatterType> getSeriesAndFormatterList() {
+        return plot.getSeriesAndFormatterListForRenderer(getClass());
+    }
+
+    public SeriesFormatterType getFormatter(SeriesType series) {
+        return (SeriesFormatterType) plot.getFormatter(series, getClass());
+    }
+
+    public void render(Canvas canvas, RectF plotArea) throws PlotRenderException {
+        onRender(canvas, plotArea);
+    }
+    public abstract void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException;
+
+    /**
+     * Draw the legend icon in the rect passed in.
+     * @param canvas
+     * @param rect
+     */
+    protected abstract void doDrawLegendIcon(Canvas canvas, RectF rect, SeriesFormatterType formatter);
+
+    public void drawSeriesLegendIcon(Canvas canvas, RectF rect, SeriesFormatterType formatter) {
+        //int state = canvas.save(Canvas.CLIP_SAVE_FLAG);
+        try {
+            canvas.save(Canvas.ALL_SAVE_FLAG);
+            canvas.clipRect(rect, Region.Op.INTERSECT);
+            doDrawLegendIcon(canvas, rect, formatter);
+            //canvas.restoreToCount(state);
+        } finally {
+            canvas.restore();
+        }
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeLayoutType.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeLayoutType.java
new file mode 100644
index 0000000..7cb6ff3
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeLayoutType.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+/**

+ * SizeLayoutType is an enumeration of algorithms available for calculating an arbitrary dimension of a widget.

+ * Each algorithm also takes a single value called "val" in this doc.

+ * ABSOLUTE - Val is treated as absolute.  If val is 5 then the size of the widget along the associated axis is 5 pixels.

+ *

+ * RELATIVE - Val represents the percentage of the display that the widget should fill along the associated axis.  For example,

+ * if the total size of the owning plot is 120 pixels and val is set to 50 then the size of the widget along the associated axis

+ * is 60; 50% of 120 = 60.

+ *

+ * FILL - Widget completely fills along the associated axis, minus

+ */

+public enum SizeLayoutType {

+    ABSOLUTE,

+    RELATIVE,

+    FILL

+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetric.java
new file mode 100644
index 0000000..57e57c0
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetric.java
@@ -0,0 +1,59 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+/**

+ * Encapsulates a sizing algorithm and an associated value.

+ *

+ * The available algorithms list are stored in the {@link SizeLayoutType} enumeration.

+ *

+ */

+public class SizeMetric extends LayoutMetric<SizeLayoutType> {

+

+    public SizeMetric(float value, SizeLayoutType layoutType) {

+        super(value, layoutType);

+    }

+

+    protected void validatePair(float value, SizeLayoutType layoutType) {

+        switch(layoutType) {

+            case RELATIVE:

+                if(value < 0 || value > 1) {

+                    throw new IllegalArgumentException("SizeMetric Relative and Hybrid layout values must be within the range of 0 to 1.");

+                }

+            case ABSOLUTE:

+            case FILL:

+            default:

+                break;

+        }

+    }

+

+    @Override

+    public float getPixelValue(float size) {

+        //switch(layoutType)

+        switch(getLayoutType()) {

+            case ABSOLUTE:

+                return getValue();

+            case RELATIVE:

+                return getValue() * size;

+            case FILL:

+                return size - getValue();

+            default:

+                throw new IllegalArgumentException("Unsupported LayoutType: " + this.getLayoutType());

+        }

+    }

+

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetrics.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetrics.java
new file mode 100644
index 0000000..28f0ce5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/SizeMetrics.java
@@ -0,0 +1,90 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+import com.androidplot.util.PixelUtils;

+

+/**

+ * Encapsulates sizing preferences associated with a Widget; how/if it scales etc.

+ */

+public class SizeMetrics {

+    private SizeMetric heightMetric;

+    private SizeMetric widthMetric;

+

+    /**

+     * Convenience constructor.  Wraps {@link #SizeMetrics(SizeMetric, SizeMetric)}.

+     * @param height Height value used algorithm to calculate the height of the associated widget(s).

+     * @param heightLayoutType Algorithm used to calculate the height of the associated widget(s).

+     * @param width Width value used algorithm to calculate the width of the associated widget(s).

+     * @param widthLayoutType Algorithm used to calculate the width of the associated widget(s).

+     */

+    public SizeMetrics(float height, SizeLayoutType heightLayoutType, float width, SizeLayoutType widthLayoutType) {

+        heightMetric = new SizeMetric(height, heightLayoutType);

+        widthMetric = new SizeMetric(width, widthLayoutType);

+    }

+

+    /**

+     * Creates a new SizeMetrics instance using the specified size layout algorithm and value.

+     * See {@link SizeMetric} for details on what can be passed in.

+     * @param heightMetric

+     * @param widthMetric

+     */

+    public SizeMetrics(SizeMetric heightMetric, SizeMetric widthMetric) {

+        this.heightMetric = heightMetric;

+        this.widthMetric = widthMetric;

+    }

+

+    public SizeMetric getHeightMetric() {

+        return heightMetric;

+    }

+

+    public void setHeightMetric(SizeMetric heightMetric) {

+        this.heightMetric = heightMetric;

+    }

+

+    public SizeMetric getWidthMetric() {

+        return widthMetric;

+    }

+

+    /**

+     * Calculates a RectF with calculated width and height.  The top-left corner is set to 0,0.

+     * @param canvasRect

+     * @return

+     */

+    public RectF getRectF(RectF canvasRect) {

+        return new RectF(

+                0,

+                0,

+                widthMetric.getPixelValue(canvasRect.width()),

+                heightMetric.getPixelValue(canvasRect.height()));

+    }

+

+    /**

+     * Same as getRectF but with edges rounded to the nearest full pixel.

+     * @param canvasRect

+     * @return

+     */

+    public RectF getRoundedRect(RectF canvasRect) {

+        return PixelUtils.nearestPixRect(0, 0, widthMetric.getPixelValue(canvasRect.width()),

+                heightMetric.getPixelValue(canvasRect.height()));

+    }

+

+    public void setWidthMetric(SizeMetric widthMetric) {

+        this.widthMetric = widthMetric;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableModel.java
new file mode 100644
index 0000000..96eaa7a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableModel.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+

+import java.util.Iterator;

+

+public abstract class TableModel {

+    private TableOrder order;

+

+    protected TableModel(TableOrder order) {

+        setOrder(order);

+    }

+

+    public abstract Iterator<RectF> getIterator(RectF tableRect, int totalElements);

+

+    //public abstract RectF getCellRect(RectF tableRect, int numElements);

+

+    public TableOrder getOrder() {

+        return order;

+    }

+

+    public void setOrder(TableOrder order) {

+        this.order = order;

+    }

+

+    public enum Axis {

+        ROW,

+        COLUMN

+    }

+

+    public enum CellSizingMethod {

+        FIXED,

+        FILL

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableOrder.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableOrder.java
new file mode 100644
index 0000000..5af231e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableOrder.java
@@ -0,0 +1,22 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+public enum TableOrder {

+    ROW_MAJOR,    // standard c-style

+    COLUMN_MAJOR

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableSizingMethod.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableSizingMethod.java
new file mode 100644
index 0000000..501fb92
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TableSizingMethod.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+/**

+ * The sizing methods available to a table.

+ * AUTO:  The table is divided evenly int tableSize/numElements sections.

+ * FIXED:  Each element in the table has a predefined number of pixels

+ * regardless of what the table's dimensions actually are.

+ */

+public enum TableSizingMethod {

+    AUTO,

+    FIXED

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/TextOrientationType.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TextOrientationType.java
new file mode 100644
index 0000000..0b78ddd
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/TextOrientationType.java
@@ -0,0 +1,23 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+public enum TextOrientationType {

+    HORIZONTAL,

+    VERTICAL_ASCENDING,

+    VERTICAL_DESCENDING

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/XLayoutStyle.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XLayoutStyle.java
new file mode 100644
index 0000000..9264bf0
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XLayoutStyle.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.ui;
+
+public enum XLayoutStyle {
+    ABSOLUTE_FROM_LEFT,
+    ABSOLUTE_FROM_RIGHT,
+    ABSOLUTE_FROM_CENTER,
+    RELATIVE_TO_LEFT,
+    RELATIVE_TO_RIGHT,
+    RELATIVE_TO_CENTER
+
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/XPositionMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XPositionMetric.java
new file mode 100644
index 0000000..fac02d2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/XPositionMetric.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.ui;
+
+
+import com.androidplot.ui.PositionMetric;
+import com.androidplot.ui.XLayoutStyle;
+
+public class XPositionMetric extends PositionMetric<XLayoutStyle> {
+
+    //private XLayoutStyle layoutType;
+
+    public XPositionMetric(float value, XLayoutStyle layoutStyle) {
+        super(value, layoutStyle);
+        validatePair(value, layoutStyle);
+        //this.layoutStyle = layoutStyle;
+    }
+
+    /**
+     * Throws IllegalArgumentException if there is a problem.
+     * @param value
+     */
+    protected void validatePair(float value, XLayoutStyle layoutStyle) {
+        switch(layoutStyle) {
+            case ABSOLUTE_FROM_LEFT:
+            case ABSOLUTE_FROM_RIGHT:
+            case ABSOLUTE_FROM_CENTER:
+                validateValue(value, PositionMetric.LayoutMode.ABSOLUTE);
+                break;
+            case RELATIVE_TO_LEFT:
+            case RELATIVE_TO_RIGHT:
+            case RELATIVE_TO_CENTER:
+                validateValue(value, PositionMetric.LayoutMode.RELATIVE);
+        }
+    }
+
+    @Override
+    public float getPixelValue(float size) {
+        switch(getLayoutType()) {
+            case ABSOLUTE_FROM_LEFT:
+                return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_BEGINING);
+            case ABSOLUTE_FROM_RIGHT:
+                return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_END);
+            case ABSOLUTE_FROM_CENTER:
+                return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_CENTER);
+            case RELATIVE_TO_LEFT:
+                return this.getRelativePosition(size, PositionMetric.Origin.FROM_BEGINING);
+            case RELATIVE_TO_RIGHT:
+                return this.getRelativePosition(size, PositionMetric.Origin.FROM_END);
+            case RELATIVE_TO_CENTER:
+                return this.getRelativePosition(size, PositionMetric.Origin.FROM_CENTER);
+            default:
+                throw new IllegalArgumentException("Unsupported LayoutType: " + this.getLayoutType());
+        }
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/YLayoutStyle.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YLayoutStyle.java
new file mode 100644
index 0000000..64fb467
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YLayoutStyle.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.ui;
+
+public enum YLayoutStyle {
+    ABSOLUTE_FROM_TOP,
+    ABSOLUTE_FROM_BOTTOM,
+    ABSOLUTE_FROM_CENTER,
+    RELATIVE_TO_TOP,
+    RELATIVE_TO_BOTTOM,
+    RELATIVE_TO_CENTER
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/YPositionMetric.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YPositionMetric.java
new file mode 100644
index 0000000..1101c1e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/YPositionMetric.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.ui;
+
+import com.androidplot.ui.PositionMetric;
+import com.androidplot.ui.YLayoutStyle;
+
+public class YPositionMetric extends PositionMetric<YLayoutStyle> {
+    /*
+    public enum YLayoutStyle {
+        ABSOLUTE_FROM_TOP,
+        ABSOLUTE_FROM_BOTTOM,
+        ABSOLUTE_FROM_CENTER,
+        RELATIVE_TO_TOP,
+        RELATIVE_TO_BOTTOM,
+        RELATIVE_TO_CENTER
+    }
+    */
+
+    //private YLayoutStyle layoutType;
+
+    public YPositionMetric(float value, YLayoutStyle layoutStyle) {
+        super(value, layoutStyle);
+        //this.layoutStyle = layoutStyle;
+
+
+    }
+
+    /*
+    @Override
+    public void set(float value, YLayoutStyle layoutType) {
+        validatePair(value, layoutType);
+        super.set(value, layoutType);
+    }
+
+    @Override
+    public void setLayoutType(YLayoutStyle layoutType) {
+        validatePair(getValue(), layoutType);
+        super.setLayoutType(layoutType);
+    }
+
+    @Override
+    public void setValue(float value) {
+        validatePair(value, getLayoutType());
+        super.setValue(value);
+    }
+    */
+
+    /**
+     * Throws IllegalArgumentException if there is a problem.
+     * @param value
+     */
+    protected void validatePair(float value, YLayoutStyle layoutStyle) {
+        switch(layoutStyle) {
+            case ABSOLUTE_FROM_TOP:
+            case ABSOLUTE_FROM_BOTTOM:
+            case ABSOLUTE_FROM_CENTER:
+                validateValue(value, PositionMetric.LayoutMode.ABSOLUTE);
+                break;
+            case RELATIVE_TO_TOP:
+            case RELATIVE_TO_BOTTOM:
+            case RELATIVE_TO_CENTER:
+                validateValue(value, PositionMetric.LayoutMode.RELATIVE);
+        }
+    }
+
+    @Override
+    public float getPixelValue(float size) {
+        switch(getLayoutType()) {
+            case ABSOLUTE_FROM_TOP:
+                return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_BEGINING);
+            case ABSOLUTE_FROM_BOTTOM:
+                return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_END);
+            case ABSOLUTE_FROM_CENTER:
+                return this.getAbsolutePosition(size, PositionMetric.Origin.FROM_CENTER);
+            case RELATIVE_TO_TOP:
+                return this.getRelativePosition(size, PositionMetric.Origin.FROM_BEGINING);
+            case RELATIVE_TO_BOTTOM:
+                return this.getRelativePosition(size, PositionMetric.Origin.FROM_END);
+            case RELATIVE_TO_CENTER:
+                return this.getRelativePosition(size, PositionMetric.Origin.FROM_CENTER);
+            default:
+                throw new IllegalArgumentException("Unsupported LayoutType: " + this.getLayoutType());
+        }
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/EmptyWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/EmptyWidget.java
new file mode 100644
index 0000000..f3da9fc
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/EmptyWidget.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui.widget;

+

+import android.graphics.Canvas;

+import android.graphics.RectF;

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.ui.LayoutManager;

+import com.androidplot.ui.SizeMetrics;

+

+public class EmptyWidget extends Widget {

+

+    public EmptyWidget(LayoutManager layoutManager, SizeMetrics sizeMetrics) {

+        super(layoutManager, sizeMetrics);

+    }

+    @Override

+    protected void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException {

+        // nothing to do

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/TextLabelWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/TextLabelWidget.java
new file mode 100644
index 0000000..96615b8
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/TextLabelWidget.java
@@ -0,0 +1,189 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui.widget;

+

+import android.graphics.*;

+import android.util.Log;

+import com.androidplot.ui.*;

+import com.androidplot.util.FontUtils;

+

+public class TextLabelWidget extends Widget {

+    private static final String TAG = TextLabelWidget.class.getName();

+

+    private String text;

+    private Paint labelPaint;

+

+    private TextOrientationType orientation;

+

+    private boolean autoPackEnabled = true;

+

+    {

+        labelPaint = new Paint();

+        labelPaint.setColor(Color.WHITE);

+        labelPaint.setAntiAlias(true);

+        labelPaint.setTextAlign(Paint.Align.CENTER);

+    }

+

+    public TextLabelWidget(LayoutManager layoutManager, SizeMetrics sizeMetrics) {

+        this(layoutManager, sizeMetrics, TextOrientationType.HORIZONTAL);

+    }

+

+    public TextLabelWidget(LayoutManager layoutManager, String title, SizeMetrics sizeMetrics, TextOrientationType orientation) {

+        this(layoutManager, sizeMetrics, orientation);

+        setText(title);

+    }

+

+    public TextLabelWidget(LayoutManager layoutManager, SizeMetrics sizeMetrics, TextOrientationType orientation) {

+        super(layoutManager, new SizeMetrics(0, SizeLayoutType.ABSOLUTE, 0, SizeLayoutType.ABSOLUTE));

+        //this.plot = plot;

+        //this.setWidth(labelPaint.measureText(plot.getTitle()));

+        //this.setHeight(labelPaint.getFontMetrics().top);

+        setSize(sizeMetrics);

+        this.orientation = orientation;

+    }

+

+    @Override

+    protected void onMetricsChanged(SizeMetrics olds, SizeMetrics news) {

+        if(autoPackEnabled) {

+            pack();

+        }

+    }

+

+    @Override

+    public void onPostInit() {

+       if(autoPackEnabled) {

+           pack();

+       }

+    }

+

+    //protected abstract String getText();

+

+    /**

+     * Sets the dimensions of the widget to exactly contain the text contents

+     */

+    public void pack() {

+        Log.d(TAG, "Packing...");

+        Rect size = FontUtils.getStringDimensions(text, getLabelPaint());

+        if(size == null) {

+            Log.w(TAG, "Attempt to pack empty text.");

+            return;

+        }

+        switch(orientation) {

+            case HORIZONTAL:

+                setSize(new SizeMetrics(size.height(), SizeLayoutType.ABSOLUTE, size.width()+2, SizeLayoutType.ABSOLUTE));

+                break;

+            case VERTICAL_ASCENDING:

+            case VERTICAL_DESCENDING:

+                setSize(new SizeMetrics(size.width(), SizeLayoutType.ABSOLUTE, size.height()+2, SizeLayoutType.ABSOLUTE));

+                break;

+        }

+        refreshLayout();

+

+    }

+

+    /**

+     * Do not call this method directly.  It is indirectly invoked every time a plot is

+     * redrawn.

+     * @param canvas The Canvas to draw onto

+     * @param widgetRect the size and coordinates of this widget

+     */

+    @Override

+    public void doOnDraw(Canvas canvas, RectF widgetRect) {

+        if(text == null || text.length() == 0) {

+            return;

+        }

+        //FontUtils.getStringDimensions(text, labelPaint);

+        float vOffset = labelPaint.getFontMetrics().descent;

+        PointF start = getAnchorCoordinates(widgetRect,

+                AnchorPosition.CENTER);

+

+        // BEGIN ROTATION CALCULATION

+        //int canvasState = canvas.save(Canvas.ALL_SAVE_FLAG);

+

+        try {

+            canvas.save(Canvas.ALL_SAVE_FLAG);

+            canvas.translate(start.x, start.y);

+            switch (orientation) {

+                case HORIZONTAL:

+                    break;

+                case VERTICAL_ASCENDING:

+                    canvas.rotate(-90);

+                    break;

+                case VERTICAL_DESCENDING:

+                    canvas.rotate(90);

+                    break;

+                default:

+

+                    throw new UnsupportedOperationException("Orientation " + orientation + " not yet implemented for TextLabelWidget.");

+            }

+            canvas.drawText(text, 0, vOffset, labelPaint);

+        } finally {

+            //canvas.restoreToCount(canvasState);

+            canvas.restore();

+        }

+

+        // END ROTATION CALCULATION

+    }

+

+    public Paint getLabelPaint() {

+        return labelPaint;

+    }

+

+    public void setLabelPaint(Paint labelPaint) {

+        this.labelPaint = labelPaint;

+

+        // when paint changes, packing params change too so check

+        // to see if we need to resize:

+        if(autoPackEnabled) {

+            pack();

+        }

+    }

+

+    public TextOrientationType getOrientation() {

+        return orientation;

+    }

+

+    public void setOrientation(TextOrientationType orientation) {

+        this.orientation = orientation;

+        if(autoPackEnabled) {

+            pack();

+        }

+    }

+

+    public boolean isAutoPackEnabled() {

+        return autoPackEnabled;

+    }

+

+    public void setAutoPackEnabled(boolean autoPackEnabled) {

+        this.autoPackEnabled = autoPackEnabled;

+        if(autoPackEnabled) {

+            pack();

+        }

+    }

+

+    public void setText(String text) {

+        Log.d(TAG, "Setting textLabel to: " + text);

+        this.text = text;

+        if(autoPackEnabled) {

+            pack();

+        }

+    }

+

+    public String getText() {

+        return text;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/Widget.java b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/Widget.java
new file mode 100644
index 0000000..1e6e663
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/ui/widget/Widget.java
@@ -0,0 +1,403 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui.widget;

+

+import android.graphics.*;

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.ui.*;

+import com.androidplot.util.DisplayDimensions;

+import com.androidplot.ui.XLayoutStyle;

+import com.androidplot.ui.YLayoutStyle;

+import com.androidplot.util.PixelUtils;

+

+/**

+ * A Widget is a graphical sub-element of a Plot that can be positioned relative

+ * to the bounds of the Plot.

+ */

+public abstract class Widget implements BoxModelable, Resizable {

+

+    private Paint borderPaint;

+    private Paint backgroundPaint;

+    private boolean clippingEnabled = true;

+    private BoxModel boxModel = new BoxModel();

+    private SizeMetrics sizeMetrics;

+    private DisplayDimensions plotDimensions = new DisplayDimensions();

+    private DisplayDimensions widgetDimensions = new DisplayDimensions();

+    private boolean isVisible = true;

+    private PositionMetrics positionMetrics;

+    private LayoutManager layoutManager;

+

+    public Widget(LayoutManager layoutManager, SizeMetric heightMetric, SizeMetric widthMetric) {

+        this(layoutManager, new SizeMetrics(heightMetric, widthMetric));

+    }

+

+    public Widget(LayoutManager layoutManager, SizeMetrics sizeMetrics) {

+        this.layoutManager = layoutManager;

+        SizeMetrics oldSize = this.sizeMetrics;

+        setSize(sizeMetrics);

+        onMetricsChanged(oldSize, sizeMetrics);

+    }

+

+    public DisplayDimensions getWidgetDimensions() {

+        return widgetDimensions;

+    }

+

+    public AnchorPosition getAnchor() {

+        return getPositionMetrics().getAnchor();

+    }

+

+    public void setAnchor(AnchorPosition anchor) {

+        getPositionMetrics().setAnchor(anchor);

+    }

+

+

+    /**

+     * Same as {@link #position(float, com.androidplot.ui.XLayoutStyle, float, com.androidplot.ui.YLayoutStyle, com.androidplot.ui.AnchorPosition)}

+     * but with the anchor parameter defaulted to the upper left corner.

+     * @param x

+     * @param xLayoutStyle

+     * @param y

+     * @param yLayoutStyle

+     */

+    public void position(float x, XLayoutStyle xLayoutStyle, float y, YLayoutStyle yLayoutStyle) {

+        position(x, xLayoutStyle, y, yLayoutStyle, AnchorPosition.LEFT_TOP);

+    }

+

+    /**

+     * @param x            X-Coordinate of the top left corner of element.  When using RELATIVE, must be a value between 0 and 1.

+     * @param xLayoutStyle LayoutType to use when orienting this element's X-Coordinate.

+     * @param y            Y_VALS_ONLY-Coordinate of the top-left corner of element.  When using RELATIVE, must be a value between 0 and 1.

+     * @param yLayoutStyle LayoutType to use when orienting this element's Y_VALS_ONLY-Coordinate.

+     * @param anchor       The point of reference used by this positioning call.

+     */

+    public void position(float x, XLayoutStyle xLayoutStyle, float y,

+                         YLayoutStyle yLayoutStyle, AnchorPosition anchor) {

+        setPositionMetrics(new PositionMetrics(x, xLayoutStyle, y, yLayoutStyle, anchor));

+        layoutManager.addToTop(this);

+    }

+

+    /**

+     * Can be overridden by subclasses to respond to resizing events.

+     *

+     * @param oldSize

+     * @param newSize

+     */

+    protected void onMetricsChanged(SizeMetrics oldSize, SizeMetrics newSize) {

+    }

+

+    /**

+     * Can be overridden by subclasses to handle any final resizing etc. that

+     * can only be done after XML configuration etc. has completed.

+     */

+    public void onPostInit() {

+    }

+

+    /**

+     * Determines whether or not point lies within this Widget.

+     *

+     * @param point

+     * @return

+     */

+    public boolean containsPoint(PointF point) {

+        //return outlineRect != null && outlineRect.contains(point.x, point.y);

+        return widgetDimensions.canvasRect.contains(point.x, point.y);

+    }

+

+    public void setSize(SizeMetrics sizeMetrics) {

+        this.sizeMetrics = sizeMetrics;

+    }

+

+

+    public void setWidth(float width) {

+        sizeMetrics.getWidthMetric().setValue(width);

+    }

+

+    public void setWidth(float width, SizeLayoutType layoutType) {

+        sizeMetrics.getWidthMetric().set(width, layoutType);

+    }

+

+    public void setHeight(float height) {

+        sizeMetrics.getHeightMetric().setValue(height);

+    }

+

+    public void setHeight(float height, SizeLayoutType layoutType) {

+        sizeMetrics.getHeightMetric().set(height, layoutType);

+    }

+

+    public SizeMetric getWidthMetric() {

+        return sizeMetrics.getWidthMetric();

+    }

+

+    public SizeMetric getHeightMetric() {

+        return sizeMetrics.getHeightMetric();

+    }

+

+    public float getWidthPix(float size) {

+        return sizeMetrics.getWidthMetric().getPixelValue(size);

+    }

+

+    public float getHeightPix(float size) {

+        return sizeMetrics.getHeightMetric().getPixelValue(size);

+    }

+

+    public RectF getMarginatedRect(RectF widgetRect) {

+        return boxModel.getMarginatedRect(widgetRect);

+    }

+

+    public RectF getPaddedRect(RectF widgetMarginRect) {

+        return boxModel.getPaddedRect(widgetMarginRect);

+    }

+

+    public void setMarginRight(float marginRight) {

+        boxModel.setMarginRight(marginRight);

+    }

+

+    public void setMargins(float left, float top, float right, float bottom) {

+        boxModel.setMargins(left, top, right, bottom);

+    }

+

+    public void setPadding(float left, float top, float right, float bottom) {

+        boxModel.setPadding(left, top, right, bottom);

+    }

+

+    public float getMarginTop() {

+        return boxModel.getMarginTop();

+    }

+

+    public void setMarginTop(float marginTop) {

+        boxModel.setMarginTop(marginTop);

+    }

+

+    public float getMarginBottom() {

+        return boxModel.getMarginBottom();

+    }

+

+    @Override

+    public float getPaddingLeft() {

+        return boxModel.getPaddingLeft();

+    }

+

+    @Override

+    public void setPaddingLeft(float paddingLeft) {

+        boxModel.setPaddingLeft(paddingLeft);

+    }

+

+    @Override

+    public float getPaddingTop() {

+        return boxModel.getPaddingTop();

+    }

+

+    @Override

+    public void setPaddingTop(float paddingTop) {

+        boxModel.setPaddingTop(paddingTop);

+    }

+

+    @Override

+    public float getPaddingRight() {

+        return boxModel.getPaddingRight();

+    }

+

+    @Override

+    public void setPaddingRight(float paddingRight) {

+        boxModel.setPaddingRight(paddingRight);

+    }

+

+    @Override

+    public float getPaddingBottom() {

+        return boxModel.getPaddingBottom();

+    }

+

+    @Override

+    public void setPaddingBottom(float paddingBottom) {

+        boxModel.setPaddingBottom(paddingBottom);

+    }

+

+    @SuppressWarnings("SameParameterValue")

+    public void setMarginBottom(float marginBottom) {

+        boxModel.setMarginBottom(marginBottom);

+    }

+

+    public float getMarginLeft() {

+        return boxModel.getMarginLeft();

+    }

+

+    public void setMarginLeft(float marginLeft) {

+        boxModel.setMarginLeft(marginLeft);

+    }

+

+    public float getMarginRight() {

+        return boxModel.getMarginRight();

+    }

+

+    /**

+     * Causes the pixel dimensions used for rendering this Widget

+     * to be recalculated.  Should be called any time a parameter that factors

+     * into this Widget's size or position is altered.

+     */

+    public synchronized void refreshLayout() {

+        if(positionMetrics == null) {

+            // make sure positionMetrics have been set.  this method can be

+            // automatically called during xml configuration of certain params

+            // before the widget is fully configured.

+            return;

+        }

+        float elementWidth = getWidthPix(plotDimensions.paddedRect.width());

+        float elementHeight = getHeightPix(plotDimensions.paddedRect.height());

+        PointF coords = getElementCoordinates(elementHeight,

+                elementWidth, plotDimensions.paddedRect, positionMetrics);

+

+        RectF widgetRect = new RectF(coords.x, coords.y,

+                coords.x + elementWidth, coords.y + elementHeight);

+        RectF marginatedWidgetRect = getMarginatedRect(widgetRect);

+        RectF paddedWidgetRect = getPaddedRect(marginatedWidgetRect);

+        widgetDimensions = new DisplayDimensions(widgetRect,

+                marginatedWidgetRect, paddedWidgetRect);

+    }

+

+    @Override

+    public synchronized void layout(final DisplayDimensions plotDimensions) {

+        this.plotDimensions = plotDimensions;

+        refreshLayout();

+    }

+

+    public PointF getElementCoordinates(float height, float width, RectF viewRect, PositionMetrics metrics) {

+            float x = metrics.getXPositionMetric().getPixelValue(viewRect.width()) + viewRect.left;

+            float y = metrics.getYPositionMetric().getPixelValue(viewRect.height()) + viewRect.top;

+            PointF point = new PointF(x, y);

+            return PixelUtils.sub(point, getAnchorOffset(width, height, metrics.getAnchor()));

+        }

+

+    public static PointF getAnchorOffset(float width, float height, AnchorPosition anchorPosition) {

+            PointF point = new PointF();

+            switch (anchorPosition) {

+                case LEFT_TOP:

+                    break;

+                case LEFT_MIDDLE:

+                    point.set(0, height / 2);

+                    break;

+                case LEFT_BOTTOM:

+                    point.set(0, height);

+                    break;

+                case RIGHT_TOP:

+                    point.set(width, 0);

+                    break;

+                case RIGHT_BOTTOM:

+                    point.set(width, height);

+                    break;

+                case RIGHT_MIDDLE:

+                    point.set(width, height / 2);

+                    break;

+                case TOP_MIDDLE:

+                    point.set(width / 2, 0);

+                    break;

+                case BOTTOM_MIDDLE:

+                    point.set(width / 2, height);

+                    break;

+                case CENTER:

+                    point.set(width / 2, height / 2);

+                    break;

+                default:

+                    throw new IllegalArgumentException("Unsupported anchor location: " + anchorPosition);

+            }

+            return point;

+        }

+

+    public static PointF getAnchorCoordinates(RectF widgetRect, AnchorPosition anchorPosition) {

+            return PixelUtils.add(new PointF(widgetRect.left, widgetRect.top),

+                    getAnchorOffset(widgetRect.width(), widgetRect.height(), anchorPosition));

+        }

+

+        public static PointF getAnchorCoordinates(float x, float y, float width, float height, AnchorPosition anchorPosition) {

+            return getAnchorCoordinates(new RectF(x, y, x+width, y+height), anchorPosition);

+        }

+

+    public void draw(Canvas canvas, RectF widgetRect) throws PlotRenderException {

+        //outlineRect = widgetRect;

+        if (isVisible()) {

+            if (backgroundPaint != null) {

+                drawBackground(canvas, widgetDimensions.canvasRect);

+            }

+

+            /* RectF marginatedRect = new RectF(outlineRect.left + marginLeft,

+          outlineRect.top + marginTop,

+          outlineRect.right - marginRight,

+          outlineRect.bottom - marginBottom);*/

+

+            /*RectF marginatedRect = boxModel.getMarginatedRect(widgetRect);

+            RectF paddedRect = boxModel.getPaddedRect(marginatedRect);*/

+            doOnDraw(canvas, widgetDimensions.paddedRect);

+

+            if (borderPaint != null) {

+                drawBorder(canvas, widgetDimensions.paddedRect);

+            }

+        }

+    }

+

+    protected void drawBorder(Canvas canvas, RectF paddedRect) {

+        canvas.drawRect(paddedRect, borderPaint);

+    }

+

+    protected void drawBackground(Canvas canvas, RectF widgetRect) {

+        canvas.drawRect(widgetRect, backgroundPaint);

+    }

+

+    /**

+     * @param canvas     The Canvas to draw onto

+     * @param widgetRect the size and coordinates of this widget

+     */

+    protected abstract void doOnDraw(Canvas canvas, RectF widgetRect) throws PlotRenderException;

+

+    public Paint getBorderPaint() {

+        return borderPaint;

+    }

+

+    public void setBorderPaint(Paint borderPaint) {

+        this.borderPaint = borderPaint;

+    }

+

+    public Paint getBackgroundPaint() {

+        return backgroundPaint;

+    }

+

+    public void setBackgroundPaint(Paint backgroundPaint) {

+        this.backgroundPaint = backgroundPaint;

+    }

+

+    public boolean isClippingEnabled() {

+        return clippingEnabled;

+    }

+

+    public void setClippingEnabled(boolean clippingEnabled) {

+        this.clippingEnabled = clippingEnabled;

+    }

+

+    public boolean isVisible() {

+        return isVisible;

+    }

+

+    public void setVisible(boolean visible) {

+        isVisible = visible;

+    }

+

+    public PositionMetrics getPositionMetrics() {

+        return positionMetrics;

+    }

+

+    public void setPositionMetrics(PositionMetrics positionMetrics) {

+        this.positionMetrics = positionMetrics;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/Configurator.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/Configurator.java
new file mode 100644
index 0000000..a4f7ae0
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/Configurator.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.util;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.util.Log;
+import android.util.TypedValue;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+/**
+ * Utility class for "configuring" objects via XML config files.  Supports the following field types:
+ * String
+ * Enum
+ * int
+ * float
+ * boolean
+ * <p/>
+ * Config files should be stored in /res/xml.  Given the XML configuration /res/xml/myConfig.xml, one can apply the
+ * configuration to an Object instance as follows:
+ * <p/>
+ * MyObject obj = new MyObject();
+ * Configurator.configure(obj, R.xml.myConfig);
+ * <p/>
+ * WHAT IT DOES:
+ * Given a series of parameters stored in an XML file, Configurator iterates through each parameter, using the name
+ * as a map to the field within a given object.  For example:
+ * <p/>
+ * <pre>
+ * {@code
+ * <config car.engine.sparkPlug.condition="poor"/>
+ * }
+ * </pre>
+ * <p/>
+ * Given a Car instance car and assuming the method setCondition(String) exists within the SparkPlug class,
+ * Configurator does the following:
+ * <p/>
+ * <pre>
+ * {@code
+ * car.getEngine().getSparkPlug().setCondition("poor");
+ * }
+ * </pre>
+ * <p/>
+ * Now let's pretend that setCondition takes an instance of the Condition enum as it's argument.
+ * Configurator then does the following:
+ * <p/>
+ * car.getEngine().getSparkPlug().setCondition(Condition.valueOf("poor");
+ * <p/>
+ * Now let's look at how ints are handled.  Given the following xml:
+ * <p/>
+ * <config car.engine.miles="100000"/>
+ * <p/>
+ * would result in:
+ * car.getEngine.setMiles(Integer.ParseInt("100000");
+ * <p/>
+ * That's pretty straight forward.  But colors are expressed as ints too in Android
+ * but can be defined using hex values or even names of colors.  When Configurator
+ * attempts to parse a parameter for a method that it knows takes an int as it's argument,
+ * Configurator will first attempt to parse the parameter as a color.  Only after this
+ * attempt fails will Configurator resort to Integer.ParseInt.  So:
+ * <p/>
+ * <config car.hood.paint.color="Red"/>
+ * <p/>
+ * would result in:
+ * car.getHood().getPaint().setColor(Color.parseColor("Red");
+ * <p/>
+ * Next lets talk about float.  Floats can appear in XML a few different ways in Android,
+ * especially when it comes to defining dimensions:
+ * <p/>
+ * <config height="10dp" depth="2mm" width="5em"/>
+ * <p/>
+ * Configurator will correctly parse each of these into their corresponding real pixel value expressed as a float.
+ * <p/>
+ * One last thing to keep in mind when using Configurator:
+ * Values for Strings and ints can be assigned to localized values, allowing
+ * a cleaner solution for those developing apps to run on multiple form factors
+ * or in multiple languages:
+ * <p/>
+ * <config thingy.description="@string/thingyDescription"
+ * thingy.titlePaint.textSize=""/>
+ */
+@SuppressWarnings("WeakerAccess")
+public abstract class Configurator {
+
+    private static final String TAG = Configurator.class.getName();
+    protected static final String CFG_ELEMENT_NAME = "config";
+
+    protected static int parseResId(Context ctx, String prefix, String value) {
+        String[] split = value.split("/");
+        // is this a localized resource?
+        if (split.length > 1 && split[0].equalsIgnoreCase(prefix)) {
+            String pack = split[0].replace("@", "");
+            String name = split[1];
+            return ctx.getResources().getIdentifier(name, pack, ctx.getPackageName());
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    protected static int parseIntAttr(Context ctx, String value) {
+        try {
+            return ctx.getResources().getColor(parseResId(ctx, "@color", value));
+        } catch (IllegalArgumentException e1) {
+            try {
+                return Color.parseColor(value);
+            } catch (IllegalArgumentException e2) {
+                // wasn't a color so try parsing as a plain old int:
+                return Integer.parseInt(value);
+            }
+        }
+    }
+
+    /**
+     * Treats value as a float parameter.  First value is tested to see whether
+     * it contains a resource identifier.  Failing that, it is tested to see whether
+     * a dimension suffix (dp, em, mm etc.) exists.  Failing that, it is evaluated as
+     * a plain old float.
+     * @param ctx
+     * @param value
+     * @return
+     */
+    protected static float parseFloatAttr(Context ctx, String value) {
+        try {
+            return ctx.getResources().getDimension(parseResId(ctx, "@dimen", value));
+        } catch (IllegalArgumentException e1) {
+            try {
+                return PixelUtils.stringToDimension(value);
+            } catch (Exception e2) {
+                return Float.parseFloat(value);
+            }
+        }
+    }
+
+    protected static String parseStringAttr(Context ctx, String value) {
+        try {
+            return ctx.getResources().getString(parseResId(ctx, "@string", value));
+        } catch (IllegalArgumentException e1) {
+            return value;
+        }
+    }
+
+
+    protected static Method getSetter(Class clazz, final String fieldId) throws NoSuchMethodException {
+        Method[] methods = clazz.getMethods();
+
+        String methodName = "set" + fieldId;
+        for (Method method : methods) {
+            if (method.getName().equalsIgnoreCase(methodName)) {
+                return method;
+            }
+        }
+        throw new NoSuchMethodException("No such public method (case insensitive): " +
+                methodName + " in " + clazz);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected static Method getGetter(Class clazz, final String fieldId) throws NoSuchMethodException {
+        Log.d(TAG, "Attempting to find getter for " + fieldId + " in class " + clazz.getName());
+        String firstLetter = fieldId.substring(0, 1);
+        String methodName = "get" + firstLetter.toUpperCase() + fieldId.substring(1, fieldId.length());
+        return clazz.getMethod(methodName);
+    }
+
+    /**
+     * Returns the object containing the field specified by path.
+     * @param obj
+     * @param path Path through member hierarchy to the destination field.
+     * @return null if the object at path cannot be found.
+     * @throws java.lang.reflect.InvocationTargetException
+     *
+     * @throws IllegalAccessException
+     */
+    protected static Object getObjectContaining(Object obj, String path) throws
+            InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+        if(obj == null) {
+            throw new NullPointerException("Attempt to call getObjectContaining(Object obj, String path) " +
+                    "on a null Object instance.  Path was: " + path);
+        }
+        Log.d(TAG, "Looking up object containing: " + path);
+        int separatorIndex = path.indexOf(".");
+
+        // not there yet, descend deeper:
+        if (separatorIndex > 0) {
+            String lhs = path.substring(0, separatorIndex);
+            String rhs = path.substring(separatorIndex + 1, path.length());
+
+            // use getter to retrieve the instance
+            Method m = getGetter(obj.getClass(), lhs);
+            if(m == null) {
+                throw new NullPointerException("No getter found for field: " + lhs + " within " + obj.getClass());
+            }
+            Log.d(TAG, "Invoking " + m.getName() + " on instance of " + obj.getClass().getName());
+            Object o = m.invoke(obj);
+            // delve into o
+            return getObjectContaining(o, rhs);
+            //} catch (NoSuchMethodException e) {
+            // TODO: log a warning
+            //    return null;
+            //}
+        } else {
+            // found it!
+            return obj;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Object[] inflateParams(Context ctx, Class[] params, String[] vals) throws NoSuchMethodException,
+            InvocationTargetException, IllegalAccessException {
+        Object[] out = new Object[params.length];
+        int i = 0;
+        for (Class param : params) {
+            if (Enum.class.isAssignableFrom(param)) {
+                out[i] = param.getMethod("valueOf", String.class).invoke(null, vals[i].toUpperCase());
+            } else if (param.equals(Float.TYPE)) {
+                out[i] = parseFloatAttr(ctx, vals[i]);
+            } else if (param.equals(Integer.TYPE)) {
+                out[i] = parseIntAttr(ctx, vals[i]);
+            } else if (param.equals(Boolean.TYPE)) {
+                out[i] = Boolean.valueOf(vals[i]);
+            } else if (param.equals(String.class)) {
+                out[i] = parseStringAttr(ctx, vals[i]);
+            } else {
+                throw new IllegalArgumentException(
+                        "Error inflating XML: Setter requires param of unsupported type: " + param);
+            }
+            i++;
+        }
+        return out;
+    }
+
+    /**
+     *
+     * @param ctx
+     * @param obj
+     * @param xmlFileId ID of the XML config file within /res/xml
+     */
+    public static void configure(Context ctx, Object obj, int xmlFileId) {
+        XmlResourceParser xrp = ctx.getResources().getXml(xmlFileId);
+        try {
+            HashMap<String, String> params = new HashMap<String, String>();
+            while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) {
+                xrp.next();
+                String name = xrp.getName();
+                if (xrp.getEventType() == XmlResourceParser.START_TAG) {
+                    if (name.equalsIgnoreCase(CFG_ELEMENT_NAME))
+                        for (int i = 0; i < xrp.getAttributeCount(); i++) {
+                            params.put(xrp.getAttributeName(i), xrp.getAttributeValue(i));
+                        }
+                    break;
+                }
+            }
+            configure(ctx, obj, params);
+        } catch (XmlPullParserException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            xrp.close();
+        }
+    }
+
+    public static void configure(Context ctx, Object obj, HashMap<String, String> params) {
+        for (String key : params.keySet()) {
+            try {
+                configure(ctx, obj, key, params.get(key));
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (NoSuchMethodException e) {
+                Log.w(TAG, "Error inflating XML: Setter for field \"" + key + "\" does not exist. ");
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Recursively descend into an object using key as the pathway and invoking the corresponding setter
+     * if one exists.
+     *
+     * @param key
+     * @param value
+     */
+    protected static void configure(Context ctx, Object obj, String key, String value)
+            throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+        Object o = getObjectContaining(obj, key);
+        if (o != null) {
+            int idx = key.lastIndexOf(".");
+            String fieldId = idx > 0 ? key.substring(idx + 1, key.length()) : key;
+
+            Method m = getSetter(o.getClass(), fieldId);
+            Class[] paramTypes = m.getParameterTypes();
+            // TODO: add support for generic type params
+            if (paramTypes.length >= 1) {
+
+                // split on "|"
+                // TODO: add support for String args containing a |
+                String[] paramStrs = value.split("\\|");
+                if (paramStrs.length == paramTypes.length) {
+
+                    Object[] oa = inflateParams(ctx, paramTypes, paramStrs);
+                    Log.d(TAG, "Invoking " + m.getName() + " with arg(s) " + argArrToString(oa));
+                    m.invoke(o, oa);
+                } else {
+                    throw new IllegalArgumentException("Error inflating XML: Unexpected number of argments passed to \""
+                            + m.getName() + "\".  Expected: " + paramTypes.length + " Got: " + paramStrs.length);
+                }
+            } else {
+                // Obvious this is not a setter
+                throw new IllegalArgumentException("Error inflating XML: no setter method found for param \"" +
+                        fieldId + "\".");
+            }
+        }
+    }
+
+    protected static String argArrToString(Object[] args) {
+        String out = "";
+        for(Object obj : args) {
+            out += (obj == null ? (out += "[null] ") :
+                    ("[" + obj.getClass() + ": " + obj + "] "));
+        }
+        return out;
+    }
+}
+
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/DisplayDimensions.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/DisplayDimensions.java
new file mode 100644
index 0000000..0ae5a7a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/DisplayDimensions.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import android.graphics.RectF;

+

+/**

+ * Convenience class for managing BoxModel data

+ */

+public class DisplayDimensions {

+

+    public final RectF canvasRect;

+    public final RectF marginatedRect;

+    public final RectF paddedRect;

+

+    // init to 1 to avoid potential divide by zero errors (yet to be observed in practice)

+    private static final RectF initRect;

+

+    static {

+        initRect = new RectF(1, 1, 1, 1);

+    }

+

+    public DisplayDimensions() {

+        this(initRect, initRect, initRect);

+    }

+    public DisplayDimensions(RectF canvasRect, RectF marginatedRect, RectF paddedRect) {

+        this.canvasRect = canvasRect;

+        this.marginatedRect = marginatedRect;

+        this.paddedRect = paddedRect;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/FontUtils.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/FontUtils.java
new file mode 100644
index 0000000..d4bbfd2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/FontUtils.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import android.graphics.Paint;

+import android.graphics.Rect;

+

+public class FontUtils {

+

+    /**

+     * Determines the height of the tallest character that can be drawn by paint.

+     * @param paint

+     * @return

+     */

+    public static float getFontHeight(Paint paint) {

+        Paint.FontMetrics metrics = paint.getFontMetrics();

+        return (-metrics.ascent) + metrics.descent;

+        //return (-metrics.top) + metrics.bottom;

+    }

+

+    /**

+     * Get the smallest rect that ecompasses the text to be drawn using paint.

+     * @param text

+     * @param paint

+     * @return

+     */

+    public static Rect getPackedStringDimensions(String text, Paint paint) {

+        Rect size = new Rect();

+        paint.getTextBounds(text, 0, text.length(), size);

+        return size;

+    }

+

+    /**

+     * Like getPackedStringDimensions except adds extra space to accommodate all

+     * characters that can be drawn regardless of whether or not they exist in text.

+     * This ensures a more uniform appearance for things that have dynamic text.

+     * @param text

+     * @param paint

+     * @return

+     */

+    public static Rect getStringDimensions(String text, Paint paint) {

+        Rect size = new Rect();

+        if(text == null || text.length() == 0) {

+            return null;

+        }

+        paint.getTextBounds(text, 0, text.length(), size);

+        size.bottom = size.top + (int) getFontHeight(paint);

+        return size;

+    }

+

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ListOrganizer.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ListOrganizer.java
new file mode 100644
index 0000000..ea2cea6
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ListOrganizer.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import java.util.List;

+

+/**

+ * Utility class providing additional element organization operations.

+ * @param <ElementType>

+ */

+public class ListOrganizer<ElementType> implements ZIndexable<ElementType> {

+    private List<ElementType> list;

+

+    public ListOrganizer(List<ElementType> list) {

+        this.list = list;

+    }

+

+

+    public boolean moveToTop(ElementType element) {

+            if(list.remove(element)) {

+                list.add(list.size(), element);

+                return true;

+            } else {

+                return false;

+            }

+    }

+

+    public boolean moveAbove(ElementType objectToMove, ElementType reference) {

+        if(objectToMove == reference) {

+            throw new IllegalArgumentException("Illegal argument to moveAbove(A, B); A cannot be equal to B.");

+        }

+

+

+        list.remove(objectToMove);

+        int refIndex = list.indexOf(reference);

+        list.add(refIndex + 1, objectToMove);

+        return true;

+        //widgetOrder.remove(element);

+

+    }

+

+    public boolean moveBeneath(ElementType objectToMove, ElementType reference) {

+        if (objectToMove == reference) {

+            throw new IllegalArgumentException("Illegal argument to moveBeaneath(A, B); A cannot be equal to B.");

+        }

+

+        list.remove(objectToMove);

+        int refIndex = list.indexOf(reference);

+        list.add(refIndex, objectToMove);

+        return true;

+

+    }

+

+    public boolean moveToBottom(ElementType key) {

+

+        //int widgetIndex = widgetOrder.indexOf(key);

+        list.remove(key);

+        //list.add(list.size(), key);

+        list.add(0, key);

+        return true;

+        //widgetOrder.remove(key);

+    }

+

+    public boolean moveUp(ElementType key) {

+        int widgetIndex = list.indexOf(key);

+        if(widgetIndex == -1) {

+            // key not found:

+            return false;

+        }

+        if(widgetIndex >= list.size()-1) {

+            // already at the top:

+            return true;

+        }

+

+        ElementType widgetAbove = list.get(widgetIndex+1);

+        return moveAbove(key, widgetAbove);

+    }

+

+    public boolean moveDown(ElementType key) {

+        int widgetIndex = list.indexOf(key);

+        if(widgetIndex == -1) {

+            // key not found:

+            return false;

+        }

+        if(widgetIndex <= 0) {

+            // already at the bottom:

+            return true;

+        }

+

+        ElementType widgetBeneath = list.get(widgetIndex-1);

+        return moveBeneath(key, widgetBeneath);

+    }

+

+    @Override

+    public List<ElementType> elements() {

+        return list;

+    }

+

+    public void addToBottom(ElementType element) {

+        list.add(0, element);

+    }

+

+    public void addToTop(ElementType element) {

+        list.add(list.size(), element);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/MultiSynch.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/MultiSynch.java
new file mode 100644
index 0000000..2e2fa88
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/MultiSynch.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ *    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 com.androidplot.util;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility class for obtaining synch lock across multiple objects.
+ */
+public abstract class MultiSynch {
+
+    /**
+     * Callback class for doing work from within a MultiSynch.
+     */
+    public interface Action {
+
+        /**
+         * Invoked by MultiSynch.run(...)
+         * @param params
+         */
+        public void run(Object[] params);
+    }
+
+
+    /**
+     *
+     * @param params
+     * @param synchSet Set of objects to be synchronized upon
+     * @param action Action to be invoked once  full synchronization has been obtained.
+     */
+    public static void run(Object[] params, Set synchSet,  Action action) {
+        run(params, synchSet.toArray(), action, 0);
+    }
+
+    /**
+     * @param params
+     * @param synchList List of objects to be synchronized upon
+     * @param action   Action to be invoked once  full synchronization has been obtained.
+     */
+    public static void run(Object[] params, List synchList, Action action) {
+        run(params, synchList.toArray(), action, 0);
+    }
+
+    /**
+     * @param params
+     * @param synchArr Array of objects to be synchronized upon
+     * @param action   Action to be invoked once  full synchronization has been obtained.
+     */
+    public static void run(Object[] params, Object[] synchArr, Action action) {
+        run(params, synchArr, action, 0);
+    }
+
+    /**
+     * Recursively synchs on each item in SynchList
+     * @param params
+     * @param synchArr
+     * @param action
+     * @param depth
+     */
+    private static void run(Object[] params, Object[] synchArr, Action action, int depth) {
+        if (synchArr != null) {
+            synchronized (synchArr[depth]) {
+                if (depth < synchArr.length - 1) {
+                    run(params, synchArr, action, ++depth);
+                } else {
+                    action.run(params);
+                }
+            }
+        }
+        action.run(params);
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/PaintUtils.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/PaintUtils.java
new file mode 100644
index 0000000..b2cd713
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/PaintUtils.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+import android.graphics.Paint;

+

+/**

+ * Convenience methods that operate on Paint instances.  These methods primarily deal with

+ * converting pixel values to/from dp values.

+ */

+public class PaintUtils {

+

+    /*public static Paint getPaint() {

+        Paint p = new Paint();

+        return p;

+    }*/

+

+    /**

+     * Sets a paint instance's line stroke size in dp

+     * @param paint

+     * @param lineSizeDp

+     */

+    public static void setLineSizeDp(Paint paint, float lineSizeDp){

+        paint.setStrokeWidth(PixelUtils.dpToPix(lineSizeDp));

+    }

+

+    /**

+     * Sets a paint instance's font size in dp

+     * @param paint

+     * @param fontSizeDp

+     */

+    public static void setFontSizeDp(Paint paint, float fontSizeDp){

+        paint.setTextSize(PixelUtils.dpToPix(fontSizeDp));

+    }

+

+    /**

+     * Set a paint instance's shadowing using dp values

+     * @param paint

+     * @param radiusDp

+     * @param dxDp

+     * @param dyDp

+     * @param color

+     */

+    public static void setShadowDp(Paint paint, float radiusDp, float dxDp, float dyDp, int color) {

+        float radius = PixelUtils.dpToPix(radiusDp);

+        float dx = PixelUtils.dpToPix(dxDp);

+        float dy = PixelUtils.dpToPix(dyDp);

+        paint.setShadowLayer(radius, dx, dy, color);

+    }

+

+

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/PixelUtils.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/PixelUtils.java
new file mode 100644
index 0000000..ea6efed
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/PixelUtils.java
@@ -0,0 +1,217 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import android.content.Context;

+import android.graphics.PointF;

+import android.graphics.Rect;

+import android.graphics.RectF;

+import android.util.DisplayMetrics;

+import android.util.TypedValue;

+

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+public class PixelUtils {

+    private static DisplayMetrics metrics;

+    private static final float FLOAT_INT_AVG_NUDGE = 0.5f;

+    //private static float SCALE = 1;   //  pix per dp

+    //private static int X_PIX = 1;     // total display horizontal pix

+    //private static int Y_PIX = 1;     // total display vertical pix

+

+    /**

+     * Recalculates scale value etc.  Should be called when an application starts or

+     * whenever the screen is rotated.

+     */

+    public static void init(Context ctx) {

+        //DisplayMetrics dm = ctx.getResources().getDisplayMetrics();

+        //SCALE = dm.density;

+        //X_PIX = dm.widthPixels;

+        //Y_PIX = dm.heightPixels;

+        metrics = ctx.getResources().getDisplayMetrics();

+

+    }

+

+    public static PointF add(PointF lhs, PointF rhs) {

+        return new PointF(lhs.x + rhs.x, lhs.y + rhs.y);

+    }

+

+    public static PointF sub(PointF lhs, PointF rhs) {

+        return new PointF(lhs.x - rhs.x, lhs.y - rhs.y);

+    }

+

+    /**

+     * Converts a sub-pixel accurate RectF to a Rect

+     * using the closest matching full pixel vals.  This is

+     * useful for clipping operations etc.

+     * @param rectIn The rect to be converted

+     * @return

+     */

+    /*public static Rect toRect(RectF rectIn) {

+        return new Rect(

+                (int) (rectIn.left + FLOAT_INT_AVG_NUDGE),

+                (int) (rectIn.top + FLOAT_INT_AVG_NUDGE),

+                (int) (rectIn.right + FLOAT_INT_AVG_NUDGE),

+                (int) (rectIn.bottom + FLOAT_INT_AVG_NUDGE));

+    }*/

+

+    /**

+     * Converts a sub-pixel accurate RectF to

+     * a single pixel accurate rect.  This is helpful

+     * for clipping operations which dont do a good job with

+     * subpixel vals.

+     * @param in

+     * @return

+     */

+    public static RectF sink(RectF in) {

+        return nearestPixRect(in.left, in.top, in.right, in.bottom);

+    }

+

+    public static RectF nearestPixRect(float left, float top, float right, float bottom) {

+        return new RectF(

+                (int) (left + FLOAT_INT_AVG_NUDGE),

+                (int) (top + FLOAT_INT_AVG_NUDGE),

+                (int) (right + FLOAT_INT_AVG_NUDGE),

+                (int) (bottom + FLOAT_INT_AVG_NUDGE));

+    }

+

+    /**

+     * Converts a dp value to pixels.

+     * @param dp

+     * @return Pixel value of dp.

+     */

+    public static float dpToPix(float dp) {

+        //return SCALE * dp + FLOAT_INT_AVG_NUDGE;

+        //InternalDimension id = new InternalDimension(dp, TypedValue.COMPLEX_UNIT_DIP);

+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);

+

+    }

+

+    /**

+     * Converts an sp value to pixels.

+     * @param sp

+     * @return Pixel value of sp.

+     */

+    @SuppressWarnings("SameParameterValue")

+    public static float spToPix(float sp) {

+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, metrics);

+    }

+

+

+    /**

+     *

+     * @param fraction A float value between 0 and 1.

+     * @return Number of pixels fraction represents on the current device's display.

+     */

+    public static float fractionToPixH(float fraction) {

+        return metrics.heightPixels * fraction;

+

+    }

+

+    /**

+     *

+     * @param fraction A float value between 0 and 1.

+     * @return Number of pixels fraction represents on the current device's display.

+     */

+    public static float fractionToPixW(float fraction) {

+        return metrics.widthPixels * fraction;

+    }

+

+

+    /**

+     *

+     * CODE BELOW IS ADAPTED IN PART FROM MINDRIOT'S SAMPLE CODE HERE:

+     * http://stackoverflow.com/questions/8343971/how-to-parse-a-dimension-string-and-convert-it-to-a-dimension-value

+     */

+    // -- Initialize dimension string to constant lookup.

+    public static final Map<String, Integer> dimensionConstantLookup = initDimensionConstantLookup();

+

+    private static Map<String, Integer> initDimensionConstantLookup() {

+        Map<String, Integer> m = new HashMap<String, Integer>();

+        m.put("px", TypedValue.COMPLEX_UNIT_PX);

+        m.put("dip", TypedValue.COMPLEX_UNIT_DIP);

+        m.put("dp", TypedValue.COMPLEX_UNIT_DIP);

+        m.put("sp", TypedValue.COMPLEX_UNIT_SP);

+        m.put("pt", TypedValue.COMPLEX_UNIT_PT);

+        m.put("in", TypedValue.COMPLEX_UNIT_IN);

+        m.put("mm", TypedValue.COMPLEX_UNIT_MM);

+        return Collections.unmodifiableMap(m);

+    }

+

+    // -- Initialize pattern for dimension string.

+    private static final Pattern DIMENSION_PATTERN = Pattern.compile("^\\s*(\\d+(\\.\\d+)*)\\s*([a-zA-Z]+)\\s*$");

+

+    /*public static int stringToDimensionPixelSize(String dimension, DisplayMetrics metrics) {

+        // -- Mimics TypedValue.complexToDimensionPixelSize(int data, DisplayMetrics metrics).

+        InternalDimension internalDimension = stringToInternalDimension(dimension);

+        final float value = internalDimension.value;

+        final float f = TypedValue.applyDimension(internalDimension.unit, value, metrics);

+        final int res = (int) (f + 0.5f);

+        if (res != 0) return res;

+        if (value == 0) return 0;

+        if (value > 0) return 1;

+        return -1;

+    }*/

+

+    public static float stringToDimension(String dimension) {

+        // -- Mimics TypedValue.complexToDimension(int data, DisplayMetrics metrics).

+        InternalDimension internalDimension = stringToInternalDimension(dimension);

+        return TypedValue.applyDimension(internalDimension.unit, internalDimension.value, metrics);

+    }

+

+    private static InternalDimension stringToInternalDimension(String dimension) {

+        // -- Match target against pattern.

+        Matcher matcher = DIMENSION_PATTERN.matcher(dimension);

+        if (matcher.matches()) {

+            // -- Match found.

+            // -- Extract value.

+            float value = Float.valueOf(matcher.group(1));

+            // -- Extract dimension units.

+            String unit = matcher.group(3).toLowerCase();

+            // -- Get Android dimension constant.

+            Integer dimensionUnit = dimensionConstantLookup.get(unit);

+            if (dimensionUnit == null) {

+                // -- Invalid format.

+                throw new NumberFormatException();

+            } else {

+                // -- Return valid dimension.

+                return new InternalDimension(value, dimensionUnit);

+            }

+        } else {

+            // -- Invalid format.

+            throw new NumberFormatException();

+        }

+    }

+

+    private static class InternalDimension {

+        float value;

+        int unit;

+

+        public InternalDimension(float value, int unit) {

+            this.value = value;

+            this.unit = unit;

+        }

+    }

+

+

+}

+

+

+

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/PlotStatistics.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/PlotStatistics.java
new file mode 100644
index 0000000..9703aee
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/PlotStatistics.java
@@ -0,0 +1,118 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import android.graphics.Canvas;

+import android.graphics.Color;

+import android.graphics.Paint;

+import android.graphics.RectF;

+import com.androidplot.Plot;

+import com.androidplot.PlotListener;

+

+/**

+ * !!! THIS CLASS IS STILL UNDER DEVELOPMENT AND MAY CONTAIN BUGS !!!

+ * Gathers performance statistics from a Plot.  Instances of PlotStatistics

+ * should never be added to more than one Plot, otherwise the statiscs will

+ * be invalid.

+ */

+public class PlotStatistics implements PlotListener {

+    long minRenderTimeMs;

+    long maxRenderTimeMs;

+    long avgRenderTimeMs;

+    long fps;

+    long updateDelayMs;

+

+

+    long longestRenderMs = 0;

+    long shortestRenderMs = 0;

+    long lastStart = 0;

+    long lastLatency = 0;

+    long lastAnnotation;

+    long latencySamples = 0;

+    long latencySum = 0;

+    String annotationString = "";

+

+    private Paint paint;

+    {

+        paint = new Paint();

+        paint.setTextAlign(Paint.Align.CENTER);

+        paint.setColor(Color.WHITE);

+        paint.setTextSize(30);

+        resetCounters();

+    }

+

+

+    private boolean annotatePlotEnabled;

+

+

+

+    public PlotStatistics(long updateDelayMs, boolean annotatePlotEnabled) {

+        this.updateDelayMs = updateDelayMs;

+        this.annotatePlotEnabled = annotatePlotEnabled;

+    }

+

+    public void setAnnotatePlotEnabled(boolean enabled) {

+        this.annotatePlotEnabled = enabled;

+    }

+

+    private void resetCounters() {

+        longestRenderMs = 0;

+        shortestRenderMs = 999999999;

+        latencySamples = 0;

+        latencySum = 0;

+    }

+

+    private void annotatePlot(Plot source, Canvas canvas) {

+        long nowMs = System.currentTimeMillis();

+        // throttle the update frequency:

+        long msSinceUpdate = (nowMs - lastAnnotation);

+        if(msSinceUpdate >= updateDelayMs) {

+

+            float avgLatency = latencySamples > 0 ?  latencySum/latencySamples : 0;

+            String overallFPS = String.format("%.2f", latencySamples > 0 ?  (1000f/msSinceUpdate) * latencySamples : 0);

+            String potentialFPS = String.format("%.2f", latencySamples > 0 ? 1000f/avgLatency : 0);

+            annotationString = "FPS (potential): " + potentialFPS + " FPS (actual): " + overallFPS + " Latency (ms) Avg: " + lastLatency + " \nMin: " + shortestRenderMs +

+                    " Max: " + longestRenderMs;

+            lastAnnotation = nowMs;

+            resetCounters();

+        }

+        RectF r = source.getDisplayDimensions().canvasRect;

+        if(annotatePlotEnabled) {

+            canvas.drawText(annotationString, r.centerX(),  r.centerY(), paint);

+        }

+    }

+

+    @Override

+    public void onBeforeDraw(Plot source, Canvas canvas) {

+        lastStart = System.currentTimeMillis();

+    }

+

+    @Override

+    public void onAfterDraw(Plot source, Canvas canvas) {

+        lastLatency = System.currentTimeMillis() - lastStart;

+        if(lastLatency < shortestRenderMs) {

+            shortestRenderMs = lastLatency;

+        }

+

+        if(lastLatency > longestRenderMs) {

+            longestRenderMs = lastLatency;

+        }

+        latencySum += lastLatency;

+        latencySamples++;

+        annotatePlot(source, canvas);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/Redrawer.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/Redrawer.java
new file mode 100644
index 0000000..6602dbb
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/Redrawer.java
@@ -0,0 +1,112 @@
+package com.androidplot.util;
+
+import android.util.Log;
+import com.androidplot.Plot;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility class for invoking Plot.redraw() on a backgorund thread
+ * at a set frequency.
+ */
+public class Redrawer implements Runnable {
+
+    private static final String TAG = Redrawer.class.getName();
+
+    private List<Plot> plots;
+    private long sleepTime;
+    private boolean keepRunning;
+    private boolean keepAlive;
+
+    /**
+     *
+     * @param plots List of Plot instances to be redrawn
+     * @param maxRefreshRate Desired frequency at which to redraw plots.
+     * @param startImmediately If true, invokes run() immediately after construction.
+     */
+    public Redrawer(List<Plot> plots, float maxRefreshRate, boolean startImmediately) {
+        this.plots = plots;
+        setMaxRefreshRate(maxRefreshRate);
+        new Thread(this).start();
+        if(startImmediately) {
+            run();
+        }
+    }
+
+    public Redrawer(Plot plot, float maxRefreshRate, boolean startImmediately) {
+        this(Arrays.asList(new Plot[]{plot}), maxRefreshRate, startImmediately);
+    }
+
+    /**
+     * Temporarily stop redrawing the plot.
+     */
+    public synchronized void pause() {
+        keepRunning = false;
+        notify();
+        Log.d(TAG, "Redrawer paused.");
+    }
+
+    /**
+     * Start/resume redrawing the plot.
+     */
+    public synchronized void start() {
+        keepRunning = true;
+        notify();
+        Log.d(TAG, "Redrawer started.");
+    }
+
+    /**
+     * Internally, this causes
+     * the refresh thread to exit.  Should always be called
+     * before exiting the application.
+     */
+    public synchronized void finish() {
+        keepRunning = false;
+        keepAlive = false;
+        notify();
+    }
+
+    @Override
+    public void run() {
+        keepAlive = true;
+        try {
+        while(keepAlive) {
+            if(keepRunning) {
+                // redraw plot(s) and sleep in an interruptible state for a
+                // max of sleepTime ms.
+                // TODO: record start and end timestamps and
+                // TODO: calculate sleepTime from that, in order to more accurately
+                // TODO: meet desired refresh rate.
+                for(Plot plot : plots) {
+                    plot.redraw();
+                }
+                synchronized (this) {
+                    wait(sleepTime);
+                }
+            } else {
+                // sleep until notified
+                synchronized (this) {
+                    wait();
+                }
+            }
+        }
+        } catch(InterruptedException e) {
+
+        } finally {
+            Log.d(TAG, "Redrawer thread exited.");
+        }
+    }
+
+    /**
+     * Set the maximum refresh rate that Redrawer should use.  Actual
+     * refresh rate could be slower.
+     * @param refreshRate Refresh rate in Hz.
+     */
+    public void setMaxRefreshRate(float refreshRate) {
+        sleepTime = (long)(1000 / refreshRate);
+        Log.d(TAG, "Set Redrawer refresh rate to " +
+                refreshRate + "( " + sleepTime + " ms)");
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ValPixConverter.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ValPixConverter.java
new file mode 100644
index 0000000..d7a0d5a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ValPixConverter.java
@@ -0,0 +1,96 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import android.graphics.PointF;

+import android.graphics.RectF;

+

+/**

+ * Utility methods for converting pixel coordinates into real values and vice versa.

+ */

+public class ValPixConverter {

+    private static final int ZERO = 0;

+

+

+    public static float valToPix(double val, double min, double max, float lengthPix, boolean flip) {

+        if(lengthPix <= ZERO) {

+            throw new IllegalArgumentException("Length in pixels must be greater than 0.");

+        }

+        double range = range(min, max);

+        double scale = lengthPix / range;

+        double raw = val - min;

+        float pix = (float)(raw * scale);

+

+        if(flip) {

+            pix = (lengthPix - pix);

+        }

+        return pix;

+    }

+

+    public static double range(double min, double max) {

+        return (max-min);

+    }

+

+    

+    public static double valPerPix(double min, double max, float lengthPix) {

+        double valRange = range(min, max);

+        return valRange/lengthPix;

+    }

+

+    /**

+     * Convert a value in pixels to the type passed into min/max

+     * @param pix

+     * @param min

+     * @param max

+     * @param lengthPix

+     * @param flip True if the axis should be reversed before calculated. This is the case

+     * with the y axis for screen coords.

+     * @return

+     */

+    public static double pixToVal(float pix, double min, double max, float lengthPix, boolean flip) {

+        if(pix < ZERO) {

+            throw new IllegalArgumentException("pixel values cannot be negative.");

+        }

+

+        if(lengthPix <= ZERO) {

+            throw new IllegalArgumentException("Length in pixels must be greater than 0.");

+        }

+        float pMult = pix;

+        if(flip) {

+            pMult = lengthPix - pix;

+        }

+        double range = range(min, max);

+        return ((range / lengthPix) * pMult) + min;

+    }

+

+    /**

+     * Converts a real value into a pixel value.

+     * @param x Real d (domain) component of the point to convert.

+     * @param y Real y (range) component of the point to convert.

+     * @param plotArea

+     * @param minX Minimum visible real value on the d (domain) axis.

+     * @param maxX Maximum visible real value on the y (domain) axis.

+     * @param minY Minimum visible real value on the y (range) axis.

+     * @param maxY Maximum visible real value on the y (range axis.

+     * @return

+     */

+    public static PointF valToPix(Number x, Number y, RectF plotArea, Number minX, Number maxX, Number minY, Number maxY) {

+        float pixX = ValPixConverter.valToPix(x.doubleValue(), minX.doubleValue(), maxX.doubleValue(), plotArea.width(), false) + (plotArea.left);

+        float pixY = ValPixConverter.valToPix(y.doubleValue(), minY.doubleValue(), maxY.doubleValue(), plotArea.height(), true) + plotArea.top;

+        return new PointF(pixX, pixY);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ZHash.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZHash.java
new file mode 100644
index 0000000..56d4665
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZHash.java
@@ -0,0 +1,163 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import java.util.HashMap;

+import java.util.List;

+

+/**

+ * Concrete implementation of ZIndexable.  Provides fast element retrieval via hash key.  Also provides

+ * mutable ordering (z indexing) of elements.

+ */

+public class ZHash<KeyType, ValueType> implements ZIndexable<KeyType> {

+

+    private HashMap<KeyType, ValueType> hash;

+    //private LinkedList<KeyType> zlist;

+    //private ListOrganizer<KeyType> listOrganizer;

+    private ZLinkedList<KeyType> zlist;

+

+    {

+        hash = new HashMap<KeyType, ValueType>();

+        zlist = new ZLinkedList<KeyType>();

+        //listOrganizer = new ListOrganizer<KeyType>(zlist);

+    }

+

+    public int size() {

+        return zlist.size();

+    }

+

+

+    public ValueType get(KeyType key) {

+        return hash.get(key);

+    }

+

+    public List<KeyType> getKeysAsList() {

+        return zlist;

+    }

+

+    /**

+     * If key already exists within the structure, it's value is replaced with the new value and

+     * it's existing order is maintained.

+     * @param key

+     * @param value

+     */

+    public synchronized void addToTop(KeyType key, ValueType value) {

+        if(hash.containsKey(key)) {

+            hash.put(key, value);

+           //throw new IllegalArgumentException("Key already exists in series structure...duplicates not permitted.");

+        } else {

+            hash.put(key, value);

+            zlist.addToTop(key);

+            //zlist.addToTop(key);

+        }

+    }

+

+    /**

+     * If key already exists within the structure, it's value is replaced with the new value and

+     * it's existing order is maintained.

+     * @param key

+     * @param value

+     */

+    public synchronized void addToBottom(KeyType key, ValueType value) {

+        if(hash.containsKey(key)) {

+            hash.put(key, value);

+           //throw new IllegalArgumentException("Key already exists in series structure...duplicates not permitted.");

+        } else {

+            hash.put(key, value);

+            zlist.addToBottom(key);

+            //zlist.addToBottom(key);

+        }

+    }

+

+    public synchronized boolean moveToTop(KeyType element) {

+        if(!hash.containsKey(element)) {

+            return false;

+        } else {

+            return zlist.moveToTop(element);

+        }

+    }

+

+    public synchronized boolean moveAbove(KeyType objectToMove, KeyType reference) {

+        if(objectToMove == reference) {

+            throw new IllegalArgumentException("Illegal argument to moveAbove(A, B); A cannot be equal to B.");

+        }

+        if(!hash.containsKey(reference) || !hash.containsKey(objectToMove)) {

+            return false;

+        } else {

+            return zlist.moveAbove(objectToMove, reference);

+        }

+    }

+

+    public synchronized boolean moveBeneath(KeyType objectToMove, KeyType reference) {

+        if(objectToMove == reference) {

+            throw new IllegalArgumentException("Illegal argument to moveBeaneath(A, B); A cannot be equal to B.");

+        }

+        if(!hash.containsKey(reference) || !hash.containsKey(objectToMove)) {

+            return false;

+        } else {

+            return zlist.moveBeneath(objectToMove, reference);

+        }

+    }

+

+    public synchronized boolean moveToBottom(KeyType key) {

+        if(!hash.containsKey(key)) {

+            return false;

+        } else {

+            return zlist.moveToBottom(key);

+        }

+    }

+

+    public synchronized boolean moveUp(KeyType key) {

+        if (!hash.containsKey(key)) {

+            return false;

+        } else {

+            return zlist.moveUp(key);

+        }

+    }

+

+    public synchronized boolean moveDown(KeyType key) {

+        if (!hash.containsKey(key)) {

+            return false;

+        } else {

+            return zlist.moveDown(key);

+        }

+    }

+

+    @Override

+    public List<KeyType> elements() {

+        return zlist;

+    }

+

+    /**

+     *

+     * @return Ordered list of keys.

+     */

+    public List<KeyType> keys() {

+        return elements();

+    }

+

+

+    public synchronized boolean remove(KeyType key) {

+        if(hash.containsKey(key)) {

+            hash.remove(key);

+            zlist.remove(key);

+            return true;

+        } else {

+            return false;

+        }

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ZIndexable.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZIndexable.java
new file mode 100644
index 0000000..44e9f88
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZIndexable.java
@@ -0,0 +1,93 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import java.util.List;

+

+/**

+ * Encapsulates the concept of z-indexable objects;  Each object is stored above or below each other object and may

+ * be moved up and down in the queue relative to other elements in the hash or absolutely to the front or back of the queue.

+ *

+ * Note that the method names correspond to the order of items drawn directly on top of one another using an iterator;

+ * the first element drawn (lowest z-index) is effectively the "bottom" element.

+ * @param <ElementType>

+ */

+public interface ZIndexable<ElementType> {

+

+    /**

+     * Move above all other elements

+     * @param element

+     * @return

+     */

+    public boolean moveToTop(ElementType element);

+

+

+    /**

+     * Move above the specified element

+     * @param objectToMove

+     * @param reference

+     * @return

+     */

+    public boolean moveAbove(ElementType objectToMove, ElementType reference);

+

+

+    /**

+     * Move beneath the specified element

+     *

+     * @param objectToMove

+     * @param reference

+     * @return

+     */

+    public boolean moveBeneath(ElementType objectToMove, ElementType reference);

+

+    /**

+     * Move beneath all other elements

+     * @param key

+     * @return

+     */

+    public boolean moveToBottom(ElementType key);

+

+

+    /**

+     * Move up by one element

+     * @param key

+     * @return

+     */

+    public boolean moveUp(ElementType key);

+

+    /**

+     * Move down by one element

+     * @param key

+     * @return

+     */

+    public boolean moveDown(ElementType key);

+

+    public List<ElementType> elements();

+

+

+    /**

+     * Add beneath all other elements

+     * @param element

+     */

+    //public void addToBottom(ElementType element);

+

+    /**

+     * Add above all other elements

+     * @param element

+     */

+    //public void addToTop(ElementType element);

+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/util/ZLinkedList.java b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZLinkedList.java
new file mode 100644
index 0000000..8924a25
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/util/ZLinkedList.java
@@ -0,0 +1,78 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import java.util.LinkedList;

+import java.util.List;

+

+public class ZLinkedList<Type> extends LinkedList<Type> implements ZIndexable<Type> {

+

+    //private LinkedList<Type> list;

+    private ListOrganizer<Type> organizer;

+

+    {

+        //list = new LinkedList<Type>();

+        organizer = new ListOrganizer<Type>(this);

+    }

+

+

+    @Override

+    public boolean moveToTop(Type element) {

+        return organizer.moveToTop(element);

+    }

+

+    @Override

+    public boolean moveAbove(Type objectToMove, Type reference) {

+        return organizer.moveAbove(objectToMove, reference);

+    }

+

+    @Override

+    public boolean moveBeneath(Type objectToMove, Type reference) {

+        return organizer.moveBeneath(objectToMove, reference);

+    }

+

+    @Override

+    public boolean moveToBottom(Type key) {

+        return organizer.moveToBottom(key);

+    }

+

+    @Override

+    public boolean moveUp(Type key) {

+        return organizer.moveUp(key);

+    }

+

+    @Override

+    public boolean moveDown(Type key) {

+        return organizer.moveDown(key);

+    }

+

+    @Override

+    public List<Type> elements() {

+        return organizer.elements();

+    }

+

+    public void addToBottom(Type element) {

+        organizer.addToBottom(element);

+    }

+

+    public void addToTop(Type element) {

+        organizer.addToTop(element);

+    }

+

+

+

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/AxisValueLabelFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/AxisValueLabelFormatter.java
new file mode 100644
index 0000000..d61d74b
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/AxisValueLabelFormatter.java
@@ -0,0 +1,34 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+public class AxisValueLabelFormatter {

+    //private Paint textPaint;

+    private int color;

+

+    public AxisValueLabelFormatter(int color) {

+        this.color = color;

+    }

+

+    public int getColor() {

+        return color;

+    }

+

+    public void setColor(int color) {

+        this.color = color;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarFormatter.java
new file mode 100644
index 0000000..fff7720
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarFormatter.java
@@ -0,0 +1,72 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+import android.graphics.Paint;

+import com.androidplot.ui.SeriesRenderer;

+

+public class BarFormatter extends LineAndPointFormatter {

+

+    public Paint getFillPaint() {

+        return fillPaint;

+    }

+

+    public void setFillPaint(Paint fillPaint) {

+        this.fillPaint = fillPaint;

+    }

+

+    public Paint getBorderPaint() {

+        return borderPaint;

+    }

+

+    public void setBorderPaint(Paint borderPaint) {

+        this.borderPaint = borderPaint;

+    }

+

+    private Paint fillPaint;

+    private Paint borderPaint;

+

+    {

+        fillPaint = new Paint();

+        //fillPaint.setColor(Color.RED);

+        fillPaint.setStyle(Paint.Style.FILL);

+        fillPaint.setAlpha(100);

+        borderPaint = new Paint();

+        borderPaint.setStyle(Paint.Style.STROKE);

+        borderPaint.setAlpha(100);

+    }

+

+    /**

+     * Should only be used in conjunction with calls to configure()...

+     */

+    public BarFormatter() {

+    }

+

+    public BarFormatter(int fillColor, int borderColor) {

+        fillPaint.setColor(fillColor);

+        borderPaint.setColor(borderColor);

+    }

+

+    @Override

+    public Class<? extends SeriesRenderer> getRendererClass() {

+        return BarRenderer.class;

+    }

+

+    @Override

+    public SeriesRenderer getRendererInstance(XYPlot plot) {

+        return new BarRenderer(plot);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarRenderer.java
new file mode 100644
index 0000000..22fc38e
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BarRenderer.java
@@ -0,0 +1,369 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.Comparator;

+import java.util.List;

+import java.util.Map.Entry;

+import java.util.TreeMap;

+

+import android.graphics.Canvas;

+import android.graphics.RectF;

+

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.util.ValPixConverter;

+

+/**

+ * Renders a point as a Bar

+ */

+public class BarRenderer<T extends BarFormatter> extends XYSeriesRenderer<T> {

+

+    private BarRenderStyle renderStyle = BarRenderStyle.OVERLAID;  // default Render Style

+	private BarWidthStyle widthStyle = BarWidthStyle.FIXED_WIDTH;  // default Width Style

+    private float barWidth = 5;

+    private float barGap = 1;

+

+    public enum BarRenderStyle {

+        OVERLAID,           // bars are overlaid in descending y-val order (largest val in back)

+        STACKED,            // bars are drawn stacked vertically on top of each other

+        SIDE_BY_SIDE        // bars are drawn horizontally next to each-other

+    }

+

+    public enum BarWidthStyle {

+        FIXED_WIDTH,        // bar width is always barWidth

+        VARIABLE_WIDTH      // bar width is calculated so that there is only barGap between each bar

+    }

+

+    public BarRenderer(XYPlot plot) {

+        super(plot);

+    }

+

+    /**

+     * Sets the width of the bars when using the FIXED_WIDTH render style

+     * @param barWidth

+     */

+    public void setBarWidth(float barWidth) {

+        this.barWidth = barWidth;

+    }

+

+    /**

+     * Sets the size of the gap between the bar (or bar groups) when using the VARIABLE_WIDTH render style

+     * @param barGap

+     */

+    public void setBarGap(float barGap) {

+        this.barGap = barGap;

+    }

+

+    public void setBarRenderStyle(BarRenderStyle renderStyle) {

+        this.renderStyle = renderStyle;

+    }

+    

+    public void setBarWidthStyle(BarWidthStyle widthStyle) {

+        this.widthStyle = widthStyle;

+    }

+    

+    public void setBarWidthStyle(BarWidthStyle style, float value) {

+    	setBarWidthStyle(style);

+        switch (style) {

+        	case FIXED_WIDTH:

+        		setBarWidth(value);

+                break;

+        	case VARIABLE_WIDTH:

+        		setBarGap(value);

+        		break;

+		default:

+			break;

+        }

+    }

+

+    @Override

+    public void doDrawLegendIcon(Canvas canvas, RectF rect, BarFormatter formatter) {

+        canvas.drawRect(rect, formatter.getFillPaint());

+        canvas.drawRect(rect, formatter.getBorderPaint());

+    }

+

+    /**

+     * Retrieves the BarFormatter instance that corresponds with the series passed in.

+     * Can be overridden to return other BarFormatters as a result of touch events etc.

+     * @param index index of the point being rendered.

+     * @param series XYSeries to which the point being rendered belongs.

+     * @return

+     */

+    @SuppressWarnings("UnusedParameters")

+    protected T getFormatter(int index, XYSeries series) {

+        return getFormatter(series);

+    }

+    

+    public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {

+        

+    	List<XYSeries> sl = getPlot().getSeriesListForRenderer(this.getClass());

+    	

+    	TreeMap<Number, BarGroup> axisMap = new TreeMap<Number, BarGroup>();

+    	

+        // dont try to render anything if there's nothing to render.

+        if(sl == null) return;

+

+        /* 

+         * Build the axisMap (yVal,BarGroup)... a TreeMap of BarGroups

+         * BarGroups represent a point on the X axis where a single or group of bars need to be drawn.

+         */

+        

+        // For each Series

+        for(XYSeries series : sl) { 

+        	BarGroup barGroup;

+        	

+            // For each value in the series

+            for(int i = 0; i < series.size(); i++) {

+            	

+               	if (series.getX(i) != null) {

+

+            		// get a new bar object

+            		Bar b = new Bar(series,i,plotArea);

+	            	

+            		// Find or create the barGroup

+	            	if (axisMap.containsKey(b.intX)) {

+	            		barGroup = axisMap.get(b.intX);

+	            	} else {

+	            		barGroup = new BarGroup(b.intX,plotArea);

+	            		axisMap.put(b.intX, barGroup);

+	            	}

+	            	barGroup.addBar(b);

+            	}

+            	

+            }

+        }

+

+		// Loop through the axisMap linking up prev pointers

+		BarGroup prev, current;

+		prev = null;

+		for(Entry<Number, BarGroup> mapEntry : axisMap.entrySet()) {

+			current = mapEntry.getValue(); 

+    		current.prev = prev;

+    		prev = current;

+		}

+

+		

+		// The default gap between each bar section

+		int gap  = (int) barGap;

+

+		// Determine roughly how wide (rough_width) this bar should be. This is then used as a default width

+		// when there are gaps in the data or for the first/last bars.

+		float f_rough_width = ((plotArea.width() - ((axisMap.size() - 1) * gap)) / (axisMap.size() - 1));

+		int rough_width = (int) f_rough_width;

+		if (rough_width < 0) rough_width = 0;

+		if (gap > rough_width) {

+			gap = rough_width / 2;

+		}

+

+		//Log.d("PARAMTER","PLOT_WIDTH=" + plotArea.width());

+		//Log.d("PARAMTER","BAR_GROUPS=" + axisMap.size());

+		//Log.d("PARAMTER","ROUGH_WIDTH=" + rough_width);

+		//Log.d("PARAMTER","GAP=" + gap);

+

+		/* 

+		 * Calculate the dimensions of each barGroup and then draw each bar within it according to

+		 * the Render Style and Width Style. 

+		 */

+		

+		for(Number key : axisMap.keySet()) {

+

+			BarGroup barGroup = axisMap.get(key);

+

+			// Determine the exact left and right X for the Bar Group

+			switch (widthStyle) {

+			case FIXED_WIDTH:

+    			// use intX and go halfwidth either side.

+    			barGroup.leftX = barGroup.intX - (int) (barWidth / 2);

+    			barGroup.width = (int) barWidth;

+    			barGroup.rightX = barGroup.leftX + barGroup.width;

+				break;

+			case VARIABLE_WIDTH:

+	    		if (barGroup.prev != null) {

+	    			if (barGroup.intX - barGroup.prev.intX - gap - 1 > (int)(rough_width * 1.5)) {

+	    				// use intX and go halfwidth either side.

+	        			barGroup.leftX = barGroup.intX - (rough_width / 2);

+	        			barGroup.width = rough_width;

+	        			barGroup.rightX = barGroup.leftX + barGroup.width;

+	    			} else {

+	    				// base left off prev right to get the gap correct.

+	    				barGroup.leftX = barGroup.prev.rightX + gap + 1;

+	    				if (barGroup.leftX > barGroup.intX) barGroup.leftX = barGroup.intX;

+	    				// base right off intX + halfwidth.

+	    				barGroup.rightX = barGroup.intX + (rough_width / 2);

+	    				// calculate the width

+	    				barGroup.width = barGroup.rightX - barGroup.leftX;

+	    			}

+	    		} else {

+	    			// use intX and go halfwidth either side.

+	    			barGroup.leftX = barGroup.intX - (rough_width / 2);

+	    			barGroup.width = rough_width;

+	    			barGroup.rightX = barGroup.leftX + barGroup.width;

+	    		}

+				break;

+			default:

+				break;

+			}

+    		

+    		//Log.d("BAR_GROUP", "rough_width=" + rough_width + " width=" + barGroup.width + " <" + barGroup.leftX + "|" + barGroup.intX + "|" + barGroup.rightX + ">"); 

+    		

+    		/*

+    		 * Draw the bars within the barGroup area.

+    		 */

+			switch (renderStyle) {

+			case OVERLAID:

+				Collections.sort(barGroup.bars, new BarComparator());

+				for (Bar b : barGroup.bars) {

+					BarFormatter formatter = b.formatter();

+			        PointLabelFormatter plf = formatter.getPointLabelFormatter();

+			        PointLabeler pointLabeler = null;

+                	if (formatter != null) {

+                		pointLabeler = formatter.getPointLabeler();

+                	}

+	        		//Log.d("BAR", b.series.getTitle() + " <" + b.barGroup.leftX + "|" + b.barGroup.intX + "|" + b.barGroup.rightX + "> " + b.intY); 

+	    			if (b.barGroup.width >= 2) {

+	        			canvas.drawRect(b.barGroup.leftX, b.intY, b.barGroup.rightX, b.barGroup.plotArea.bottom, formatter.getFillPaint());

+	        		}

+	        		canvas.drawRect(b.barGroup.leftX, b.intY, b.barGroup.rightX, b.barGroup.plotArea.bottom, formatter.getBorderPaint());

+	        		if(plf != null && pointLabeler != null) {

+	                    canvas.drawText(pointLabeler.getLabel(b.series, b.seriesIndex), b.intX + plf.hOffset, b.intY + plf.vOffset, plf.getTextPaint());

+	                }

+	        	}

+				break;

+			case SIDE_BY_SIDE:

+				int width = (int) barGroup.width / barGroup.bars.size();

+				int leftX = barGroup.leftX;

+				Collections.sort(barGroup.bars, new BarComparator());

+				for (Bar b : barGroup.bars) {

+					BarFormatter formatter = b.formatter();

+			        PointLabelFormatter plf = formatter.getPointLabelFormatter();

+			        PointLabeler pointLabeler = null;

+                	if (formatter != null) {

+                		pointLabeler = formatter.getPointLabeler();

+                	}

+	        		//Log.d("BAR", "width=" + width + " <" + leftX + "|" + b.intX + "|" + (leftX + width) + "> " + b.intY); 

+	        		if (b.barGroup.width >= 2) {

+	        			canvas.drawRect(leftX, b.intY, leftX + width, b.barGroup.plotArea.bottom, formatter.getFillPaint());

+	        		}

+	        		canvas.drawRect(leftX, b.intY, leftX + width, b.barGroup.plotArea.bottom, formatter.getBorderPaint());

+	        		if(plf != null && pointLabeler != null) {

+	                    canvas.drawText(pointLabeler.getLabel(b.series, b.seriesIndex), leftX + width/2 + plf.hOffset, b.intY + plf.vOffset, plf.getTextPaint());

+	                }

+	        		leftX = leftX + width;

+	        	}

+				break;

+			case STACKED:

+				int bottom = (int) barGroup.plotArea.bottom;

+				Collections.sort(barGroup.bars, new BarComparator());

+				for (Bar b : barGroup.bars) {

+					BarFormatter formatter = b.formatter();

+			        PointLabelFormatter plf = formatter.getPointLabelFormatter();

+			        PointLabeler pointLabeler = null;

+                	if (formatter != null) {

+                		pointLabeler = formatter.getPointLabeler();

+                	}

+	        		int height = (int) b.barGroup.plotArea.bottom - b.intY;

+	        		int top = bottom - height;

+	        		//Log.d("BAR", "top=" + top + " bottom=" + bottom + " height=" + height); 

+	    			if (b.barGroup.width >= 2) {

+	        			canvas.drawRect(b.barGroup.leftX, top, b.barGroup.rightX, bottom, formatter.getFillPaint());

+	        		}

+	        		canvas.drawRect(b.barGroup.leftX, top, b.barGroup.rightX, bottom, formatter.getBorderPaint());

+	        		if(plf != null && pointLabeler != null) {

+	                    canvas.drawText(pointLabeler.getLabel(b.series, b.seriesIndex), b.intX + plf.hOffset, b.intY + plf.vOffset, plf.getTextPaint());

+	                }

+		        	bottom = top;

+	        	}

+				break;

+			default:

+				break;

+			}

+			

+		}

+    		

+    }

+    

+    private class Bar {

+		public XYSeries series;

+		public int seriesIndex;

+		public double yVal, xVal;

+		public int intX, intY;

+		public float pixX, pixY;

+		public BarGroup barGroup;

+    	

+    	public Bar(XYSeries series, int seriesIndex, RectF plotArea) {

+			this.series = series;

+			this.seriesIndex = seriesIndex;

+			

+			this.xVal = series.getX(seriesIndex).doubleValue();

+			this.pixX = ValPixConverter.valToPix(xVal, getPlot().getCalculatedMinX().doubleValue(), getPlot().getCalculatedMaxX().doubleValue(), plotArea.width(), false) + (plotArea.left);

+			this.intX = (int) pixX;

+			

+			if (series.getY(seriesIndex) != null) {

+				this.yVal = series.getY(seriesIndex).doubleValue();

+				this.pixY = ValPixConverter.valToPix(yVal, getPlot().getCalculatedMinY().doubleValue(), getPlot().getCalculatedMaxY().doubleValue(), plotArea.height(), true) + plotArea.top;

+				this.intY = (int) pixY;

+			} else {

+				this.yVal = 0;

+				this.pixY = plotArea.bottom;

+				this.intY = (int) pixY;

+			}

+		}

+    	public BarFormatter formatter() {

+    		return getFormatter(seriesIndex, series);

+    	}

+    }

+    

+    private class BarGroup {

+    	public ArrayList<Bar> bars;

+    	public int intX;

+    	public int width, leftX, rightX;

+    	public RectF plotArea;

+    	public BarGroup prev;

+		

+    	public BarGroup(int intX, RectF plotArea) {

+    		// Setup the TreeMap with the required comparator

+   			this.bars = new ArrayList<Bar>(); // create a comparator that compares series title given the index.

+    		this.intX = intX;

+			this.plotArea = plotArea;

+		}

+    	

+    	public void addBar(Bar bar) {

+    		bar.barGroup = this;

+   			this.bars.add(bar);

+    	}

+    }

+

+    @SuppressWarnings("WeakerAccess")

+    public class BarComparator implements Comparator<Bar>{

+        @Override

+        public int compare(Bar bar1, Bar bar2) {

+			switch (renderStyle) {

+			case OVERLAID:

+				return Integer.valueOf(bar1.intY).compareTo(bar2.intY);

+			case SIDE_BY_SIDE:

+				return bar1.series.getTitle().compareToIgnoreCase(bar2.series.getTitle());

+			case STACKED:

+				return bar1.series.getTitle().compareToIgnoreCase(bar2.series.getTitle());

+			default:

+	            return 0;

+			}

+        }

+    }        

+    

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointFormatter.java
new file mode 100644
index 0000000..1935a83
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointFormatter.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import com.androidplot.ui.SeriesRenderer;

+

+/**

+ * This is an experimental class and should not currently be used in production apps.

+ */

+public class BezierLineAndPointFormatter extends LineAndPointFormatter {

+

+    /**

+     * Should only be used in conjunction with calls to configure()...

+     */

+    public BezierLineAndPointFormatter() {

+    }

+

+    public BezierLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor) {

+        super(lineColor, vertexColor, fillColor, null);

+    }

+

+    public BezierLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, FillDirection fillDir) {

+        super(lineColor, vertexColor, fillColor, null, fillDir);

+    }

+

+    @Override

+    public Class<? extends SeriesRenderer> getRendererClass() {

+        return BezierLineAndPointRenderer.class;

+    }

+

+    @Override

+    public SeriesRenderer getRendererInstance(XYPlot plot) {

+        return new BezierLineAndPointRenderer(plot);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointRenderer.java
new file mode 100644
index 0000000..432abe5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BezierLineAndPointRenderer.java
@@ -0,0 +1,43 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Path;

+import android.graphics.PointF;

+

+/**

+ * WARNING: This is an unfinished class.  Series drawn by this implementation may look nice

+ * but they are not yet accurate representations of the control points from which they are derived.

+ */

+public class BezierLineAndPointRenderer extends LineAndPointRenderer<BezierLineAndPointFormatter> {

+    public BezierLineAndPointRenderer(XYPlot plot) {

+        super(plot);

+    }

+

+    @Override

+    protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {

+        //path.lineTo(thisPoint.x, thisPoint.y);

+

+        // bezier curve version:

+        PointF mid = new PointF();

+        mid.set((lastPoint.x + thisPoint.x) / 2, (lastPoint.y + thisPoint.y) / 2);

+        path.quadTo((lastPoint.x + mid.x) / 2, lastPoint.y, mid.x, mid.y);

+        //path.quadTo((mid.x + thisPoint.x) / 2, thisPoint.y, thisPoint.x, thisPoint.y);

+        path.quadTo((mid.x + thisPoint.x) / 2, lastPoint.y, thisPoint.x, thisPoint.y);

+

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/BoundaryMode.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BoundaryMode.java
new file mode 100644
index 0000000..4d717f8
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/BoundaryMode.java
@@ -0,0 +1,26 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+public enum BoundaryMode {

+    FIXED,

+    AUTO,

+    GROW,

+    SHRINNK

+}

+

+

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/FillDirection.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/FillDirection.java
new file mode 100644
index 0000000..e1e1845
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/FillDirection.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+/**

+ * Defines which edge is used to close a fill path for drawing lines.

+ *

+ * TOP - Use the top edge of the plot.

+ * BOTTOM - Use the bottom edge of the plot.

+ * LEFT - (Not implemented) Use the left edge of the plot.

+ * RIGHT - (Not implemented) Use the right edge of the plot.

+ * DOMAIN_ORIGIN - (Not implemented) Use the domain origin line.

+ * RANGE_ORIGIN - Use the range origin line.

+ */

+public enum FillDirection {

+    TOP,

+    BOTTOM,

+    LEFT,

+    RIGHT,

+    DOMAIN_ORIGIN,

+    RANGE_ORIGIN

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointFormatter.java
new file mode 100644
index 0000000..9f9b377
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointFormatter.java
@@ -0,0 +1,182 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import android.graphics.Color;

+import android.graphics.Paint;

+import com.androidplot.ui.SeriesRenderer;

+import com.androidplot.util.Configurator;

+import com.androidplot.util.PixelUtils;

+

+/**

+ * Defines the visual aesthetics of an XYSeries; outline color and width, fill style,

+ * vertex size and color, shadowing, etc.

+ */

+public class LineAndPointFormatter extends XYSeriesFormatter<XYRegionFormatter> {

+

+    private static final float DEFAULT_LINE_STROKE_WIDTH_DP   = 1.5f;

+    private static final float DEFAULT_VERTEX_STROKE_WIDTH_DP = 4.5f;

+

+     // default implementation prints point's yVal:

+    private PointLabeler pointLabeler = new PointLabeler() {

+        @Override

+        public String getLabel(XYSeries series, int index) {

+            return series.getY(index) + "";

+        }

+    };

+

+    public FillDirection getFillDirection() {

+        return fillDirection;

+    }

+

+    /**

+     * Sets which edge to use to close the line's path for filling purposes.

+     * See {@link FillDirection}.

+     * @param fillDirection

+     */

+    public void setFillDirection(FillDirection fillDirection) {

+        this.fillDirection = fillDirection;

+    }

+

+    protected FillDirection fillDirection = FillDirection.BOTTOM;

+    protected Paint linePaint;

+    protected Paint vertexPaint;

+    protected Paint fillPaint;

+    private PointLabelFormatter pointLabelFormatter;

+

+    {

+        initLinePaint(Color.BLACK);

+    }

+

+    /**

+     * Should only be used in conjunction with calls to configure()...

+     */

+    public LineAndPointFormatter() {

+        this(Color.RED, Color.GREEN, Color.BLUE, null);

+    }

+

+    public LineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, PointLabelFormatter plf) {

+        this(lineColor, vertexColor, fillColor, plf, FillDirection.BOTTOM);

+    }

+

+    public LineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, PointLabelFormatter plf, FillDirection fillDir) {

+        initLinePaint(lineColor);

+        initVertexPaint(vertexColor);

+        initFillPaint(fillColor);

+        setFillDirection(fillDir);

+        this.setPointLabelFormatter(plf);

+    }

+

+    @Override

+    public Class<? extends SeriesRenderer> getRendererClass() {

+        return LineAndPointRenderer.class;

+    }

+

+    @Override

+    public SeriesRenderer getRendererInstance(XYPlot plot) {

+        return new LineAndPointRenderer(plot);

+    }

+

+    protected void initLinePaint(Integer lineColor) {

+        if (lineColor == null) {

+            linePaint = null;

+        } else {

+            linePaint = new Paint();

+            linePaint.setAntiAlias(true);

+            linePaint.setStrokeWidth(PixelUtils.dpToPix(DEFAULT_LINE_STROKE_WIDTH_DP));

+            linePaint.setColor(lineColor);

+            linePaint.setStyle(Paint.Style.STROKE);

+        }

+    }

+

+    protected void initVertexPaint(Integer vertexColor) {

+        if (vertexColor == null) {

+            vertexPaint = null;

+        } else {

+            vertexPaint = new Paint();

+            vertexPaint.setAntiAlias(true);

+            vertexPaint.setStrokeWidth(PixelUtils.dpToPix(DEFAULT_VERTEX_STROKE_WIDTH_DP));

+            vertexPaint.setColor(vertexColor);

+            vertexPaint.setStrokeCap(Paint.Cap.ROUND);

+        }

+    }

+

+    protected void initFillPaint(Integer fillColor) {

+        if (fillColor == null) {

+            fillPaint = null;

+        } else {

+            fillPaint = new Paint();

+            fillPaint.setAntiAlias(true);

+            fillPaint.setColor(fillColor);

+        }

+    }

+

+    /**

+     * Enables the shadow layer on linePaint and shadowPaint by calling

+     * setShadowLayer() with preset values.

+     */

+    public void enableShadows() {

+        linePaint.setShadowLayer(1, 3, 3, Color.BLACK);

+        vertexPaint.setShadowLayer(1, 3, 3, Color.BLACK);

+    }

+

+    public void disableShadows() {

+        linePaint.setShadowLayer(0, 0, 0, Color.BLACK);

+        vertexPaint.setShadowLayer(0, 0, 0, Color.BLACK);

+    }

+

+    public Paint getLinePaint() {

+        return linePaint;

+    }

+

+    public void setLinePaint(Paint linePaint) {

+        this.linePaint = linePaint;

+    }

+

+    public Paint getVertexPaint() {

+        return vertexPaint;

+    }

+

+    public void setVertexPaint(Paint vertexPaint) {

+        this.vertexPaint = vertexPaint;

+    }

+

+    public Paint getFillPaint() {

+        return fillPaint;

+    }

+

+    public void setFillPaint(Paint fillPaint) {

+        this.fillPaint = fillPaint;

+    }

+

+    public PointLabelFormatter getPointLabelFormatter() {

+        return pointLabelFormatter;

+    }

+

+    public void setPointLabelFormatter(PointLabelFormatter pointLabelFormatter) {

+        this.pointLabelFormatter = pointLabelFormatter;

+    }

+    

+    public PointLabeler getPointLabeler() {

+        return pointLabeler;

+    }

+

+    public void setPointLabeler(PointLabeler pointLabeler) {

+        this.pointLabeler = pointLabeler;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointRenderer.java
new file mode 100644
index 0000000..14595c4
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/LineAndPointRenderer.java
@@ -0,0 +1,224 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.*;

+import android.util.Pair;

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.util.ValPixConverter;

+

+import java.util.ArrayList;

+import java.util.List;

+

+/**

+ * Renders a point as a line with the vertices marked.  Requires 2 or more points to

+ * be rendered.

+ */

+public class LineAndPointRenderer<FormatterType extends LineAndPointFormatter> extends XYSeriesRenderer<FormatterType> {

+

+    public LineAndPointRenderer(XYPlot plot) {

+        super(plot);

+    }

+

+    @Override

+    public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {

+

+

+        List<XYSeries> seriesList = getPlot().getSeriesListForRenderer(this.getClass());

+        if (seriesList != null) {

+            for (XYSeries series : seriesList) {

+                //synchronized(series) {

+                    drawSeries(canvas, plotArea, series, getFormatter(series));

+                //}

+            }

+        }

+    }

+

+    @Override

+    public void doDrawLegendIcon(Canvas canvas, RectF rect, LineAndPointFormatter formatter) {

+        // horizontal icon:

+        float centerY = rect.centerY();

+        float centerX = rect.centerX();

+

+        if(formatter.getFillPaint() != null) {

+            canvas.drawRect(rect, formatter.getFillPaint());

+        }

+        if(formatter.getLinePaint() != null) {

+            canvas.drawLine(rect.left, rect.bottom, rect.right, rect.top, formatter.getLinePaint());

+        }

+

+        if(formatter.getVertexPaint() != null) {

+            canvas.drawPoint(centerX, centerY, formatter.getVertexPaint());

+        }

+    }

+

+    /**

+     * This method exists for StepRenderer to override without having to duplicate any

+     * additional code.

+     */

+    protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {

+

+        path.lineTo(thisPoint.x, thisPoint.y);

+    }

+

+

+    protected void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {

+        PointF thisPoint;

+        PointF lastPoint = null;

+        PointF firstPoint = null;

+        Paint  linePaint = formatter.getLinePaint();

+

+        //PointF lastDrawn = null;

+        Path path = null;

+        ArrayList<Pair<PointF, Integer>> points = new ArrayList<Pair<PointF, Integer>>(series.size());

+        for (int i = 0; i < series.size(); i++) {

+            Number y = series.getY(i);

+            Number x = series.getX(i);

+

+            if (y != null && x != null) {

+                thisPoint = ValPixConverter.valToPix(

+                        x,

+                        y,

+                        plotArea,

+                        getPlot().getCalculatedMinX(),

+                        getPlot().getCalculatedMaxX(),

+                        getPlot().getCalculatedMinY(),

+                        getPlot().getCalculatedMaxY());

+                points.add(new Pair<PointF, Integer>(thisPoint, i));

+                //appendToPath(path, thisPoint, lastPoint);

+            } else {

+                thisPoint = null;

+            }

+

+            if(linePaint != null && thisPoint != null) {

+

+                // record the first point of the new Path

+                if(firstPoint == null) {

+                    path = new Path();

+                    firstPoint = thisPoint;

+                    // create our first point at the bottom/x position so filling

+                    // will look good

+                    path.moveTo(firstPoint.x, firstPoint.y);

+                }

+

+                if(lastPoint != null) {

+                    appendToPath(path, thisPoint, lastPoint);

+                }

+

+                lastPoint = thisPoint;

+            } else {

+                if(lastPoint != null) {

+                    renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);

+                }

+                firstPoint = null;

+                lastPoint = null;

+            }

+        }

+        if(linePaint != null && firstPoint != null) {

+            renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);

+        }

+

+        // TODO: benchmark this against drawPoints(float[]);

+        Paint vertexPaint = formatter.getVertexPaint();

+        PointLabelFormatter plf = formatter.getPointLabelFormatter();

+        if (vertexPaint != null || plf != null) {

+            for (Pair<PointF, Integer> p : points) {

+            	PointLabeler pointLabeler = formatter.getPointLabeler();

+ 

+                // if vertexPaint is available, draw vertex:

+                if(vertexPaint != null) {

+                    canvas.drawPoint(p.first.x, p.first.y, formatter.getVertexPaint());

+                }

+

+                // if textPaint and pointLabeler are available, draw point's text label:

+                if(plf != null && pointLabeler != null) {

+                    canvas.drawText(pointLabeler.getLabel(series, p.second), p.first.x + plf.hOffset, p.first.y + plf.vOffset, plf.getTextPaint());

+                }

+            }

+        }

+    }

+

+    protected void renderPath(Canvas canvas, RectF plotArea, Path path, PointF firstPoint, PointF lastPoint, LineAndPointFormatter formatter) {

+        Path outlinePath = new Path(path);

+

+        // determine how to close the path for filling purposes:

+        // We always need to calculate this path because it is also used for

+        // masking off for region highlighting.

+        switch (formatter.getFillDirection()) {

+            case BOTTOM:

+                path.lineTo(lastPoint.x, plotArea.bottom);

+                path.lineTo(firstPoint.x, plotArea.bottom);

+                path.close();

+                break;

+            case TOP:

+                path.lineTo(lastPoint.x, plotArea.top);

+                path.lineTo(firstPoint.x, plotArea.top);

+                path.close();

+                break;

+            case RANGE_ORIGIN:

+                float originPix = ValPixConverter.valToPix(

+                        getPlot().getRangeOrigin().doubleValue(),

+                        getPlot().getCalculatedMinY().doubleValue(),

+                        getPlot().getCalculatedMaxY().doubleValue(),

+                        plotArea.height(),

+                        true);

+                originPix += plotArea.top;

+

+                path.lineTo(lastPoint.x, originPix);

+                path.lineTo(firstPoint.x, originPix);

+                path.close();

+                break;

+            default:

+                throw new UnsupportedOperationException("Fill direction not yet implemented: " + formatter.getFillDirection());

+        }

+

+        if (formatter.getFillPaint() != null) {

+            canvas.drawPath(path, formatter.getFillPaint());

+        }

+

+

+        //}

+

+        // draw any visible regions on top of the base region:

+        double minX = getPlot().getCalculatedMinX().doubleValue();

+        double maxX = getPlot().getCalculatedMaxX().doubleValue();

+        double minY = getPlot().getCalculatedMinY().doubleValue();

+        double maxY = getPlot().getCalculatedMaxY().doubleValue();

+

+        // draw each region:

+        for (RectRegion r : RectRegion.regionsWithin(formatter.getRegions().elements(), minX, maxX, minY, maxY)) {

+            XYRegionFormatter f = formatter.getRegionFormatter(r);

+            RectF regionRect = r.getRectF(plotArea, minX, maxX, minY, maxY);

+            if (regionRect != null) {

+                try {

+                canvas.save(Canvas.ALL_SAVE_FLAG);

+                canvas.clipPath(path);

+                canvas.drawRect(regionRect, f.getPaint());

+                } finally {

+                    canvas.restore();

+                }

+            }

+        }

+

+        // finally we draw the outline path on top of everything else:

+        if(formatter.getLinePaint() != null) {

+            canvas.drawPath(outlinePath, formatter.getLinePaint());

+        }

+

+        path.rewind();

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabelFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabelFormatter.java
new file mode 100644
index 0000000..411a8fe
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabelFormatter.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Canvas;

+import android.graphics.Color;

+import android.graphics.Paint;

+import android.graphics.PointF;

+import com.androidplot.util.PixelUtils;

+

+public class PointLabelFormatter {

+    private static final float DEFAULT_H_OFFSET_DP = 0;

+    private static final float DEFAULT_V_OFFSET_DP = -4;

+    private static final float DEFAULT_TEXT_SIZE_SP = 12;

+    private Paint textPaint;

+    public float hOffset;

+    public float vOffset;

+

+    public PointLabelFormatter() {

+        this(Color.WHITE);

+    }

+

+    public PointLabelFormatter(int textColor) {

+        this(textColor, PixelUtils.dpToPix(DEFAULT_H_OFFSET_DP),

+                PixelUtils.dpToPix(DEFAULT_V_OFFSET_DP));

+    }

+

+    /**

+     *

+     * @param textColor

+     * @param hOffset Horizontal offset of text in pixels.

+     * @param vOffset Vertical offset of text in pixels.  Offset is in screen coordinates;

+     *                positive values shift the text further down the screen.

+     */

+    public PointLabelFormatter(int textColor, float hOffset, float vOffset) {

+        initTextPaint(textColor);

+        this.hOffset = hOffset;

+        this.vOffset = vOffset;

+    }

+

+    public Paint getTextPaint() {

+        return textPaint;

+    }

+

+    public void setTextPaint(Paint textPaint) {

+        this.textPaint = textPaint;

+    }

+

+    protected void initTextPaint(Integer textColor) {

+        if (textColor == null) {

+            setTextPaint(null);

+        } else {

+            setTextPaint(new Paint());

+            getTextPaint().setAntiAlias(true);

+            getTextPaint().setColor(textColor);

+            getTextPaint().setTextAlign(Paint.Align.CENTER);

+            getTextPaint().setTextSize(PixelUtils.spToPix(DEFAULT_TEXT_SIZE_SP));

+            //textPaint.setStyle(Paint.Style.STROKE);

+        }

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabeler.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabeler.java
new file mode 100644
index 0000000..8814ba7
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/PointLabeler.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.xy;
+
+public interface PointLabeler {
+
+    public String getLabel(XYSeries series, int index);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/RectRegion.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/RectRegion.java
new file mode 100644
index 0000000..bee4a4f
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/RectRegion.java
@@ -0,0 +1,181 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.PointF;

+import android.graphics.RectF;

+import com.androidplot.LineRegion;

+import com.androidplot.util.ValPixConverter;

+

+import java.util.ArrayList;

+import java.util.List;

+

+/**

+ * RectRegion is just a rectangle with additional methods for determining

+ * intersections with other RectRegion instances.

+ */

+public class RectRegion {

+

+    LineRegion xLineRegion;

+    LineRegion yLineRegion;

+    private String label;

+

+    /**

+     *

+     * @param minX

+     * @param maxX

+     * @param minY

+     * @param maxY

+     */

+    public RectRegion(Number minX, Number maxX, Number minY, Number maxY, String label) {

+        xLineRegion = new LineRegion(minX, maxX);

+        yLineRegion = new LineRegion(minY, maxY);

+        this.setLabel(label);

+    }

+

+    @SuppressWarnings("SameParameterValue")

+    public RectRegion(Number minX, Number maxX, Number minY, Number maxY) {

+        this(minX, maxX, minY, maxY, null);

+    }

+

+    public boolean containsPoint(PointF point) {

+        throw new UnsupportedOperationException("Not yet implemented.");

+    }

+

+    public boolean containsValue(Number x, Number y) {

+        throw new UnsupportedOperationException("Not yet implemented.");

+    }

+

+    public boolean containsDomainValue(Number value) {

+        //return RectRegion.isBetween(value, minX, maxX);

+        return xLineRegion.contains(value);

+    }

+

+    public boolean containsRangeValue(Number value) {

+        //return RectRegion.isBetween(value, minY, maxY);

+        return yLineRegion.contains(value);

+    }

+

+    public boolean intersects(RectRegion region) {

+        return intersects(region.getMinX(), region.getMaxX(), region.getMinY(), region.getMaxY());

+    }

+

+

+    /**

+     * Tests whether this region intersects the region defined by params.  Use

+     * null to represent infinity.  Negative and positive infinity is implied by

+     * the boundary edge, ie. a maxX of null equals positive infinity while a

+     * minX of null equals negative infinity.

+     * @param minX

+     * @param maxX

+     * @param minY

+     * @param maxY

+     * @return

+     */

+    public boolean intersects(Number minX, Number maxX, Number minY, Number maxY) {

+        return xLineRegion.intersects(minX, maxX) && yLineRegion.intersects(minY, maxY);

+    }

+

+    public boolean intersects(RectF region, Number visMinX, Number visMaxX, Number visMinY, Number visMaxY) {

+

+        RectF thisRegion = getRectF(region, visMinX.doubleValue(), visMaxX.doubleValue(),

+                visMinY.doubleValue(), visMaxY.doubleValue());

+        return RectF.intersects(thisRegion, region);

+    }

+

+    public RectF getRectF(RectF plotRect, Number visMinX, Number visMaxX, Number visMinY, Number visMaxY) {

+        PointF topLeftPoint = ValPixConverter.valToPix(

+                xLineRegion.getMinVal().doubleValue() != Double.NEGATIVE_INFINITY ? xLineRegion.getMinVal() : visMinX,

+                yLineRegion.getMaxVal().doubleValue() != Double.POSITIVE_INFINITY ? yLineRegion.getMaxVal() : visMaxY,

+                plotRect,

+                visMinX,

+                visMaxX,

+                visMinY,

+                visMaxY);

+        PointF bottomRightPoint = ValPixConverter.valToPix(

+                xLineRegion.getMaxVal().doubleValue() != Double.POSITIVE_INFINITY ? xLineRegion.getMaxVal() : visMaxX,

+                yLineRegion.getMinVal().doubleValue() != Double.NEGATIVE_INFINITY ? yLineRegion.getMinVal() : visMinY,

+                plotRect,

+                visMinX,

+                visMaxX,

+                visMinY,

+                visMaxY);

+        // TODO: figure out why the y-values are inverted

+        return new RectF(topLeftPoint.x, topLeftPoint.y, bottomRightPoint.x, bottomRightPoint.y);

+    }

+

+    /**

+     * Returns a list of XYRegions that either completely or partially intersect the area

+     * defined by params. A null value for any parameter represents infinity / no boundary.

+     * @param regions The list of regions to search through

+     * @param minX

+     * @param maxX

+     * @param minY

+     * @param maxY

+     * @return

+     */

+    public static List<RectRegion> regionsWithin(List<RectRegion> regions, Number minX, Number maxX, Number minY, Number maxY) {

+        ArrayList<RectRegion> intersectingRegions = new ArrayList<RectRegion>();

+        for(RectRegion r : regions) {

+            if(r.intersects(minX, maxX, minY, maxY)) {

+                intersectingRegions.add(r);

+            }

+        }

+        return intersectingRegions;

+    }

+

+

+    public Number getMinX() {

+        return xLineRegion.getMinVal();

+    }

+

+    public void setMinX(double minX) {

+        xLineRegion.setMinVal(minX);

+    }

+

+    public Number getMaxX() {

+        return xLineRegion.getMaxVal();

+    }

+

+    public void setMaxX(Number maxX) {

+        xLineRegion.setMaxVal(maxX);

+    }

+

+    public Number getMinY() {

+        return yLineRegion.getMinVal();

+    }

+

+    public void setMinY(Number minY) {

+        yLineRegion.setMinVal(minY);

+    }

+

+    public Number getMaxY() {

+        return yLineRegion.getMaxVal();

+    }

+

+    public void setMaxY(Number maxY) {

+        yLineRegion.setMaxVal(maxY);

+    }

+

+    public String getLabel() {

+        return label;

+    }

+

+    public void setLabel(String label) {

+        this.label = label;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/SimpleXYSeries.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/SimpleXYSeries.java
new file mode 100644
index 0000000..0416103
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/SimpleXYSeries.java
@@ -0,0 +1,283 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Canvas;

+import android.util.Log;

+import android.util.Pair;

+import com.androidplot.Plot;

+import com.androidplot.PlotListener;

+

+import java.util.LinkedList;

+import java.util.List;

+import java.util.NoSuchElementException;

+import java.util.concurrent.locks.ReentrantReadWriteLock;

+

+

+/**

+ * A convenience class used to create instances of XYPlot generated from Lists of Numbers.

+ */

+public class SimpleXYSeries implements XYSeries, PlotListener {

+

+    private static final String TAG = SimpleXYSeries.class.getName();

+

+    @Override

+    public void onBeforeDraw(Plot source, Canvas canvas) {

+        lock.readLock().lock();

+    }

+

+    @Override

+    public void onAfterDraw(Plot source, Canvas canvas) {

+        lock.readLock().unlock();

+    }

+

+    public enum ArrayFormat {

+        Y_VALS_ONLY,

+        XY_VALS_INTERLEAVED

+    }

+

+    private volatile LinkedList<Number> xVals = new LinkedList<Number>();

+    private volatile LinkedList<Number> yVals = new LinkedList<Number>();

+    private volatile String title = null;

+    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);

+

+

+    public SimpleXYSeries(String title) {

+        this.title = title;

+    }

+    /**

+     * Generates an XYSeries instance from the List of numbers passed in.  This is a convenience class

+     * and should only be used for static data models; it is not suitable for representing dynamically

+     * changing data.

+     *

+     * @param model  A List of Number elements comprising the data model.

+     * @param format Format of the model.  A format of Y_VALS_ONLY means that the array only contains y-values.

+     *               For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model.

+     * @param title  Title of the series

+     */

+    public SimpleXYSeries(List<? extends Number> model, ArrayFormat format, String title) {

+        this(title);

+        setModel(model, format);

+    }

+

+    public SimpleXYSeries(List<? extends Number> xVals, List<? extends Number> yVals, String title) {

+        this(title);

+        if(xVals == null || yVals == null) {

+            throw new IllegalArgumentException("Neither the xVals nor the yVals parameters may be null.");

+        }

+

+        if(xVals.size() != yVals.size()) {

+            throw new IllegalArgumentException("xVals and yVals List parameters must be of the same size.");

+        }

+

+        this.xVals.addAll(xVals);

+        this.yVals.addAll(yVals);

+    }

+

+    /**

+     * Use index value as xVal, instead of explicit, user provided xVals.

+     */

+    public void useImplicitXVals() {

+        lock.writeLock().lock();

+        try {

+            xVals = null;

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    /**

+     * Use the provided list of Numbers as yVals and their corresponding indexes as xVals.

+     * @param model A List of Number elements comprising the data model.

+     * @param format Format of the model.  A format of Y_VALS_ONLY means that the array only contains y-values.

+     *               For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model.

+     */

+    public void setModel(List<? extends Number> model, ArrayFormat format) {

+

+        lock.writeLock().lock();

+        try {

+            // empty the current values:

+            //xVals.clear();

+            xVals = null;

+            yVals.clear();

+

+            // make sure the new model has data:

+            if (model == null || model.size() == 0) {

+                return;

+            }

+

+            switch (format) {

+

+                // array containing only y-vals. assume x = index:

+                case Y_VALS_ONLY:

+                    for(Number n : model) {

+                        yVals.add(n);

+                    }

+                    /*for (int i = 0; i < model.size(); i++) {

+                        //xVals.add(i);

+                        yVals.add(model.get(i));

+                    }*/

+                    break;

+

+                // xy interleaved array:

+                case XY_VALS_INTERLEAVED:

+                    if (xVals == null) {

+                        xVals = new LinkedList<Number>();

+                    }

+                    if (model.size() % 2 != 0) {

+                        throw new IndexOutOfBoundsException("Cannot auto-generate series from odd-sized xy List.");

+                    }

+                    // always need an x and y array so init them now:

+                    int sz = model.size() / 2;

+                    for (int i = 0, j = 0; i < sz; i++, j += 2) {

+                        xVals.add(model.get(j));

+                        yVals.add(model.get(j + 1));

+                    }

+                    break;

+                default:

+                    throw new IllegalArgumentException("Unexpected enum value: " + format);

+            }

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    /**

+     * Sets individual x value based on index

+     * @param value

+     * @param index

+     */

+    public void setX(Number value, int index) {

+        lock.writeLock().lock();

+        try {

+            xVals.set(index, value);

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    /**

+     * Sets individual y value based on index

+     * @param value

+     * @param index

+     */

+    public void setY(Number value, int index) {

+        lock.writeLock().lock();

+        try {

+            yVals.set(index, value);

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    /**

+     * Sets xy values based on index

+     * @param xVal

+     * @param yVal

+     * @param index

+     */

+    public void setXY(Number xVal, Number yVal, int index) {

+        lock.writeLock().lock();

+        try {

+            yVals.set(index, yVal);

+            xVals.set(index, xVal);

+        } finally {lock.writeLock().unlock();}

+    }

+

+    public void addFirst(Number x, Number y) {

+        lock.writeLock().lock();

+        try {

+            if (xVals != null) {

+                xVals.addFirst(x);

+            }

+            yVals.addFirst(y);

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    /**

+     *

+     * @return Pair<Number, Number> with first equal to x-val and second equal to y-val.

+     */

+    public Pair<Number, Number> removeFirst() {

+        lock.writeLock().lock();

+        try {

+            if (size() <= 0) {

+                throw new NoSuchElementException();

+            }

+            return new Pair<Number, Number>(xVals != null ? xVals.removeFirst() : 0, yVals.removeFirst());

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    public void addLast(Number x, Number y) {

+        lock.writeLock().lock();

+        try {

+            if (xVals != null) {

+                xVals.addLast(x);

+            }

+            yVals.addLast(y);

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    /**

+     *

+     * @return Pair<Number, Number> with first equal to x-val and second equal to y-val.

+     */

+    public Pair<Number, Number> removeLast() {

+        lock.writeLock().lock();

+        try {

+            if (size() <= 0) {

+                throw new NoSuchElementException();

+            }

+            return new Pair<Number, Number>(xVals != null ? xVals.removeLast() : yVals.size() - 1, yVals.removeLast());

+        } finally {

+            lock.writeLock().unlock();

+        }

+    }

+

+    @Override

+    public String getTitle() {

+        return title;

+    }

+

+    public void setTitle(String title) {

+        lock.writeLock().lock();

+        try {

+            this.title = title;

+        } finally {lock.writeLock().unlock();}

+    }

+

+    @Override

+    public int size() {

+        return yVals != null ? yVals.size() : 0;

+    }

+

+    @Override

+    public Number getX(int index) {

+        return xVals != null ? xVals.get(index) : index;

+    }

+

+    @Override

+    public Number getY(int index) {

+        return yVals.get(index);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepFormatter.java
new file mode 100644
index 0000000..8c29277
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepFormatter.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import com.androidplot.ui.SeriesRenderer;

+import com.androidplot.util.Configurator;

+

+public class StepFormatter extends LineAndPointFormatter {

+

+    /**

+     * Should only be used in conjunction with calls to configure()...

+     */

+    public StepFormatter() {}

+

+    public StepFormatter(Integer lineColor, Integer fillColor) {

+        initLinePaint(lineColor);

+        initFillPaint(fillColor);

+    }

+

+    @Override

+    public Class<? extends SeriesRenderer> getRendererClass() {

+        return StepRenderer.class;

+    }

+

+    @Override

+    public SeriesRenderer getRendererInstance(XYPlot plot) {

+        return new StepRenderer(plot);

+    }

+

+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepRenderer.java
new file mode 100644
index 0000000..17f01c3
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/StepRenderer.java
@@ -0,0 +1,164 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Path;

+import android.graphics.PointF;

+

+/**

+ * Renders a point as a line with the vertices marked.  Requires 2 or more points to

+ * be rendered.

+ */

+

+public class StepRenderer extends LineAndPointRenderer<StepFormatter> {

+

+    public StepRenderer(XYPlot plot) {

+        super(plot);

+    }

+

+    @Override

+    protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {

+        //path.lineTo(thisPoint.x, thisPoint.y);

+

+        path.lineTo(thisPoint.x, lastPoint.y);

+        path.lineTo(thisPoint.x, thisPoint.y);

+                        //canvas.drawPoint(point.x, lastPoint.y, format.getVertexPaint());

+                        // next the vertical:

+                        //canvas.drawLine(point.x, lastPoint.y, point.x, point.y, format.getLinePaint());

+    }

+}

+/*

+public class StepRenderer extends XYSeriesRenderer<StepFormatter> {

+

+    private PointF lastPoint;

+

+    private boolean drawLinesEnabled = true;

+    private boolean drawPointsEnabled = true;

+

+    private XYAxisType stepAxis = XYAxisType.DOMAIN;

+

+

+

+    public StepRenderer(XYPlot plot) {

+        super(plot);

+    }

+

+    @Override

+    public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {

+

+

+

+        for(XYSeries series : getPlot().getSeriesListForRenderer(this.getClass())) {

+

+            drawSeries(canvas, plotArea, series, getFormatter(series));

+        }

+        //foreach(this.)

+        //foreach()

+    }

+

+    @Override

+    public void doDrawLegendIcon(Canvas canvas, RectF rect, String text, StepFormatter formatter) {

+        // horizontal icon:

+        float centerY = rect.centerY();

+        float centerX = rect.centerX();

+        canvas.drawLine(rect.left, rect.top, rect.right, rect.bottom, formatter.getLinePaint());

+        canvas.drawPoint(centerX, centerY, formatter.getVertexPaint());

+        //canvas.drawRect(rect, formatter.getLinePaint());

+

+    }

+

+

+    private void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, StepFormatter formatter) throws PlotRenderException {

+        beginSeries(canvas, plotArea, formatter);

+        //XYDataset series = bundle.getDataset();

+        //int seriesIndex = bundle.getSeriesIndex();

+        PointF thisPoint;

+        for (int i = 0; i < series.size(); i++) {

+            Number y = series.getY(i);

+            Number x = series.getD(i);

+

+            if (y != null && x != null) {

+

+                thisPoint = ValPixConverter.valToPix(

+                        x,

+                        y,

+                        plotArea,

+                        getPlot().getCalculatedMinX(),

+                        getPlot().getCalculatedMaxX(),

+                        getPlot().getCalculatedMinY(),

+                        getPlot().getCalculatedMaxY());

+                //float pixX = ValPixConverter.valToPix(x.doubleValue(), getPlot().getCalculatedMinX().doubleValue(), getPlot().getCalculatedMaxX().doubleValue(), plotArea.width(), false) + (plotArea.left);

+                //float pixY = ValPixConverter.valToPix(y.doubleValue(), getPlot().getCalculatedMinY().doubleValue(), getPlot().getCalculatedMaxY().doubleValue(), plotArea.height(), true) + plotArea.top;

+

+                //thisPoint = new PointF(pixX, pixY);

+            } else {

+                thisPoint = null;

+            }

+            drawPoint(canvas, thisPoint, plotArea, formatter);

+        }

+        endSeries(canvas, plotArea, formatter);

+    }

+

+    private void beginSeries(Canvas canvas, RectF plotArea, StepFormatter format) throws PlotRenderException {

+        lastPoint = null;

+    }

+

+    private void drawPoint(Canvas canvas, PointF point, RectF plotArea, StepFormatter format) throws PlotRenderException {

+        if (lastPoint != null) {

+            if (point != null) {

+

+                switch(stepAxis) {

+                    case DOMAIN:

+                        // first draw the horizontal line:

+                        canvas.drawLine(lastPoint.x, lastPoint.y, point.x, lastPoint.y, format.getLinePaint());

+                        canvas.drawPoint(point.x, lastPoint.y, format.getVertexPaint());

+                        // next the vertical:

+                        canvas.drawLine(point.x, lastPoint.y, point.x, point.y, format.getLinePaint());

+                        break;

+                    case RANGE:

+                        break;

+                }

+                //doDrawLine(canvas, lastPoint, point, plotArea, format);

+

+

+            }

+            drawLastPoint(canvas, plotArea, format);

+        }

+

+        lastPoint = point;

+    }

+

+    private void endSeries(Canvas canvas, RectF plotArea, StepFormatter format) throws PlotRenderException {

+        if(lastPoint != null) {

+            drawLastPoint(canvas, plotArea, format);

+        }

+    }

+

+    protected void drawLastPoint(Canvas canvas, RectF plotArea, StepFormatter format) throws PlotRenderException {

+        canvas.drawPoint(lastPoint.x, lastPoint.y, format.getVertexPaint());

+    }

+

+

+    public XYAxisType getStepAxis() {

+        return stepAxis;

+    }

+

+    public void setStepAxis(XYAxisType stepAxis) {

+        this.stepAxis = stepAxis;

+    }

+}

+*/

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/ValueMarker.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/ValueMarker.java
new file mode 100644
index 0000000..648cf64
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/ValueMarker.java
@@ -0,0 +1,156 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Color;

+import android.graphics.Paint;

+import com.androidplot.ui.PositionMetric;

+

+/**

+ * Encapsulates a single axis line marker drawn onto an XYPlot at a specified value.

+ * @param <PositionMetricType>

+ */

+public abstract class ValueMarker<PositionMetricType extends PositionMetric> {

+

+    public String getText() {

+        return text;

+    }

+

+    public void setText(String text) {

+        this.text = text;

+    }

+

+    public enum TextOrientation {

+        HORIZONTAL,

+        VERTICAL

+    }

+    private Number value;

+    private Paint linePaint;

+    private Paint textPaint;

+    //private Paint backgroundPaint;

+    private TextOrientation textOrientation;

+    private int textMargin = 2;

+    private PositionMetricType textPosition;

+    private String text;

+

+    {

+        linePaint = new Paint();

+        linePaint.setColor(Color.RED);

+        linePaint.setAntiAlias(true);

+        linePaint.setStyle(Paint.Style.STROKE);

+        textPaint = new Paint();

+        textPaint.setAntiAlias(true);

+        textPaint.setColor(Color.RED);

+        //backgroundPaint = new Paint();

+        //backgroundPaint.setColor(Color.argb(100, 100, 100, 100));

+        //backgroundPaint.setColor(Color.DKGRAY);

+

+    }

+

+    public ValueMarker(Number value, String text, PositionMetricType textPosition) {

+        this.value = value;

+        this.textPosition = textPosition;

+        this.text = text;

+    }

+

+    /**

+     *

+     * @param value

+     * @param text

+     * @param textPosition

+     * @param linePaint

+     * @param textPaint

+     */

+    public ValueMarker(Number value, String text, PositionMetricType textPosition, Paint linePaint, Paint textPaint) {

+        this(value, text, textPosition);

+

+        this.linePaint = linePaint;

+        this.textPaint = textPaint;

+        //this.backgroundPaint = backgroundPaint;

+    }

+

+    public ValueMarker(Number value, String text, PositionMetricType textPosition, int linePaint, int textPaint) {

+        this(value, text, textPosition);

+        this.linePaint.setColor(linePaint);

+        this.textPaint.setColor(textPaint);

+    }

+

+    public Number getValue() {

+        return value;

+    }

+

+    public void setValue(Number value) {

+        this.value = value;

+    }

+

+    public Paint getLinePaint() {

+        return linePaint;

+    }

+

+    public void setLinePaint(Paint linePaint) {

+        this.linePaint = linePaint;

+    }

+

+    public Paint getTextPaint() {

+        return textPaint;

+    }

+

+    public void setTextPaint(Paint textPaint) {

+        this.textPaint = textPaint;

+    }

+

+    /*public Paint getBackgroundPaint() {

+        return backgroundPaint;

+    }

+

+    public void setBackgroundPaint(Paint backgroundPaint) {

+        this.backgroundPaint = backgroundPaint;

+    }*/

+

+    public TextOrientation getTextOrientation() {

+        return textOrientation;

+    }

+

+    /**

+     * Currently not implemented.  Sets the orientation of the text portion of this

+     * ValueMarker.

+     * @param textOrientation

+     */

+    public void setTextOrientation(TextOrientation textOrientation) {

+        this.textOrientation = textOrientation;

+    }

+

+    /**

+     * Currently not implemented.

+     * @return

+     */

+    public int getTextMargin() {

+        return textMargin;

+    }

+

+    public void setTextMargin(int textMargin) {

+        this.textMargin = textMargin;

+    }

+

+    public PositionMetricType getTextPosition() {

+        return textPosition;

+    }

+

+    public void setTextPosition(PositionMetricType textPosition) {

+        this.textPosition = textPosition;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XValueMarker.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XValueMarker.java
new file mode 100644
index 0000000..84ddea4
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XValueMarker.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Paint;

+import com.androidplot.ui.YLayoutStyle;

+import com.androidplot.ui.YPositionMetric;

+

+public class XValueMarker extends ValueMarker<YPositionMetric> {

+

+    /**

+     *

+     * @param value

+     * @param text Set to null to use the plot's default formatter.

+     */

+    public XValueMarker(Number value, String text) {

+        super(value, text, new YPositionMetric(3, YLayoutStyle.ABSOLUTE_FROM_TOP));

+    }

+

+    /**

+     *

+     * @param value

+     * @param text Set to null to use the plot's default formatter.

+     * @param textPosition

+     * @param linePaint

+     * @param textPaint

+     */

+    public XValueMarker(Number value, String text, YPositionMetric textPosition, Paint linePaint, Paint textPaint) {

+        super(value, text, textPosition, linePaint, textPaint);

+    }

+

+

+    /**

+     *

+     * @param value

+     * @param text Set to null to use the plot's default formatter.

+     * @param textPosition

+     * @param linePaint

+     * @param textPaint

+     */

+    public XValueMarker(Number value, String text, YPositionMetric textPosition, int linePaint, int textPaint) {

+        super(value, text, textPosition, linePaint, textPaint);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYAxisType.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYAxisType.java
new file mode 100644
index 0000000..2077d6c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYAxisType.java
@@ -0,0 +1,22 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+public enum XYAxisType {

+    DOMAIN,

+    RANGE

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYFramingModel.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYFramingModel.java
new file mode 100644
index 0000000..7d1220c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYFramingModel.java
@@ -0,0 +1,22 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+public enum XYFramingModel {

+    ORIGIN,

+    EDGE,

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphBounds.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphBounds.java
new file mode 100644
index 0000000..8089185
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphBounds.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+public class XYGraphBounds {

+

+

+    private Number minX;

+    private Number maxX;

+    private Number minY;

+    private Number maxY;

+

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphWidget.java
new file mode 100644
index 0000000..c6ce620
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYGraphWidget.java
@@ -0,0 +1,1252 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.*;

+

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.ui.LayoutManager;

+import com.androidplot.ui.SizeMetrics;

+import com.androidplot.ui.widget.Widget;

+import com.androidplot.util.FontUtils;

+import com.androidplot.util.ValPixConverter;

+import com.androidplot.util.ZHash;

+import com.androidplot.util.ZIndexable;

+

+import java.text.DecimalFormat;

+import java.text.Format;

+

+/**

+ * Displays graphical data annotated with domain and range tick markers.

+ */

+public class XYGraphWidget extends Widget {

+

+    public float getRangeLabelOrientation() {

+        return rangeLabelOrientation;

+    }

+

+    public void setRangeLabelOrientation(float rangeLabelOrientation) {

+        this.rangeLabelOrientation = rangeLabelOrientation;

+    }

+

+    public float getDomainLabelOrientation() {

+        return domainLabelOrientation;

+    }

+

+    public void setDomainLabelOrientation(float domainLabelOrientation) {

+        this.domainLabelOrientation = domainLabelOrientation;

+    }

+

+    /**

+     * Will be used in a future version.

+     */

+    public enum XYPlotOrientation {

+        HORIZONTAL, VERTICAL

+    }

+

+    private static final int MARKER_LABEL_SPACING = 2;

+    private static final int CURSOR_LABEL_SPACING = 2; // space between cursor

+    private static final String TAG = "AndroidPlot";

+                                                       // lines and label in

+                                                       // pixels

+    private float domainLabelWidth = 15; // how many pixels is the area

+                                         // allocated for domain labels

+    private float rangeLabelWidth = 41; // ...

+    private float domainLabelVerticalOffset = -5;

+    private float domainLabelHorizontalOffset = 0.0f;

+    private float rangeLabelHorizontalOffset = 1.0f;   // allows tweaking of text position

+    private float rangeLabelVerticalOffset = 0.0f;  // allows tweaking of text position

+    

+    private int ticksPerRangeLabel = 1;

+    private int ticksPerDomainLabel = 1;

+    private float gridPaddingTop = 0;

+    private float gridPaddingBottom = 0;

+    private float gridPaddingLeft = 0;

+    private float gridPaddingRight = 0;

+    private int domainLabelTickExtension = 5;

+    private int rangeLabelTickExtension = 5;

+    private Paint gridBackgroundPaint;

+    private Paint rangeGridLinePaint;

+    private Paint rangeSubGridLinePaint;

+    private Paint domainGridLinePaint;

+    private Paint domainSubGridLinePaint;

+    private Paint domainLabelPaint;

+    private Paint rangeLabelPaint;

+    private Paint domainCursorPaint;

+    private Paint rangeCursorPaint;

+    private Paint cursorLabelPaint;

+    private Paint cursorLabelBackgroundPaint;

+    private XYPlot plot;

+    private Format rangeValueFormat;

+    private Format domainValueFormat;

+    private Paint domainOriginLinePaint;

+    private Paint rangeOriginLinePaint;

+    private Paint domainOriginLabelPaint;

+    private Paint rangeOriginLabelPaint;

+    private RectF gridRect;

+    private RectF paddedGridRect;

+    private float domainCursorPosition;

+    private float rangeCursorPosition;

+    @SuppressWarnings("FieldCanBeLocal")

+    private boolean drawCursorLabelEnabled = true;

+    private boolean drawMarkersEnabled = true;

+    

+    private boolean rangeAxisLeft = true;

+    private boolean domainAxisBottom = true;

+

+    private float rangeLabelOrientation;

+    private float domainLabelOrientation;

+

+    // TODO: consider typing this manager with a special

+    // axisLabelRegionFormatter

+    // private ZHash<LineRegion, AxisValueLabelFormatter> domainLabelRegions;

+    // private ZHash<LineRegion, AxisValueLabelFormatter> rangeLabelRegions;

+    private ZHash<RectRegion, AxisValueLabelFormatter> axisValueLabelRegions;

+

+    {

+        gridBackgroundPaint = new Paint();

+        gridBackgroundPaint.setColor(Color.rgb(140, 140, 140));

+        gridBackgroundPaint.setStyle(Paint.Style.FILL);

+        rangeGridLinePaint = new Paint();

+        rangeGridLinePaint.setColor(Color.rgb(180, 180, 180));

+        rangeGridLinePaint.setAntiAlias(true);

+        rangeGridLinePaint.setStyle(Paint.Style.STROKE);

+        domainGridLinePaint = new Paint(rangeGridLinePaint);

+        domainSubGridLinePaint = new Paint(domainGridLinePaint);

+        rangeSubGridLinePaint = new Paint(rangeGridLinePaint);

+        domainOriginLinePaint = new Paint();

+        domainOriginLinePaint.setColor(Color.WHITE);

+        domainOriginLinePaint.setAntiAlias(true);

+        rangeOriginLinePaint = new Paint();

+        rangeOriginLinePaint.setColor(Color.WHITE);

+        rangeOriginLinePaint.setAntiAlias(true);

+        domainOriginLabelPaint = new Paint();

+        domainOriginLabelPaint.setColor(Color.WHITE);

+        domainOriginLabelPaint.setAntiAlias(true);

+        domainOriginLabelPaint.setTextAlign(Paint.Align.CENTER);

+        rangeOriginLabelPaint = new Paint();

+        rangeOriginLabelPaint.setColor(Color.WHITE);

+        rangeOriginLabelPaint.setAntiAlias(true);

+        rangeOriginLabelPaint.setTextAlign(Paint.Align.RIGHT);

+        domainLabelPaint = new Paint();

+        domainLabelPaint.setColor(Color.LTGRAY);

+        domainLabelPaint.setAntiAlias(true);

+        domainLabelPaint.setTextAlign(Paint.Align.CENTER);

+        rangeLabelPaint = new Paint();

+        rangeLabelPaint.setColor(Color.LTGRAY);

+        rangeLabelPaint.setAntiAlias(true);

+        rangeLabelPaint.setTextAlign(Paint.Align.RIGHT);

+        domainCursorPaint = new Paint();

+        domainCursorPaint.setColor(Color.YELLOW);

+        rangeCursorPaint = new Paint();

+        rangeCursorPaint.setColor(Color.YELLOW);

+        cursorLabelPaint = new Paint();

+        cursorLabelPaint.setColor(Color.YELLOW);

+        cursorLabelBackgroundPaint = new Paint();

+        cursorLabelBackgroundPaint.setColor(Color.argb(100, 50, 50, 50));

+        setMarginTop(7);

+        setMarginRight(4);

+        setMarginBottom(4);

+        rangeValueFormat = new DecimalFormat("0.0");

+        domainValueFormat = new DecimalFormat("0.0");

+        // domainLabelRegions = new ZHash<LineRegion,

+        // AxisValueLabelFormatter>();

+        // rangeLabelRegions = new ZHash<LineRegion, AxisValueLabelFormatter>();

+        axisValueLabelRegions = new ZHash<RectRegion, AxisValueLabelFormatter>();

+    }

+

+    public XYGraphWidget(LayoutManager layoutManager, XYPlot plot, SizeMetrics sizeMetrics) {

+        super(layoutManager, sizeMetrics);

+        this.plot = plot;

+    }

+

+    public ZIndexable<RectRegion> getAxisValueLabelRegions() {

+        return axisValueLabelRegions;

+    }

+

+    /**

+     * Add a new Region used for rendering axis valuelabels. Note that it is

+     * possible to add multiple Region instances which overlap, in which cast

+     * the last region to be added will be used. It is up to the developer to

+     * guard against this often undesireable situation.

+     * 

+     * @param region

+     * @param formatter

+     */

+    public void addAxisValueLabelRegion(RectRegion region,

+            AxisValueLabelFormatter formatter) {

+        axisValueLabelRegions.addToTop(region, formatter);

+    }

+

+    /**

+     * Convenience method - wraps addAxisValueLabelRegion, using

+     * Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY to mask off range

+     * axis value labels.

+     * 

+     * @param min

+     * @param max

+     * @param formatter

+     * 

+     */

+    public void addDomainAxisValueLabelRegion(double min, double max,

+            AxisValueLabelFormatter formatter) {

+        addAxisValueLabelRegion(new RectRegion(min, max,

+                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, null),

+                formatter);

+    }

+

+    /**

+     * Convenience method - wraps addAxisValueLabelRegion, using

+     * Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY to mask off domain

+     * axis value labels.

+     * 

+     * @param min

+     * @param max

+     * @param formatter

+     */

+    public void addRangeAxisValueLabelRegion(double min, double max,

+            AxisValueLabelFormatter formatter) {

+        addAxisValueLabelRegion(new RectRegion(Double.POSITIVE_INFINITY,

+                Double.NEGATIVE_INFINITY, min, max, null), formatter);

+    }

+

+    /*

+     * public void addRangeLabelRegion(LineRegion region,

+     * AxisValueLabelFormatter formatter) { rangeLabelRegions.addToTop(region,

+     * formatter); }

+     * 

+     * public boolean removeRangeLabelRegion(LineRegion region) { return

+     * rangeLabelRegions.remove(region); }

+     */

+

+    /**

+     * Returns the formatter associated with the first (bottom) Region

+     * containing x and y.

+     * 

+     * @param x

+     * @param y

+     * @return the formatter associated with the first (bottom) region

+     *         containing x and y. null otherwise.

+     */

+    public AxisValueLabelFormatter getAxisValueLabelFormatterForVal(double x,

+            double y) {

+        for (RectRegion r : axisValueLabelRegions.elements()) {

+            if (r.containsValue(x, y)) {

+                return axisValueLabelRegions.get(r);

+            }

+        }

+        return null;

+    }

+

+    public AxisValueLabelFormatter getAxisValueLabelFormatterForDomainVal(

+            double val) {

+        for (RectRegion r : axisValueLabelRegions.elements()) {

+            if (r.containsDomainValue(val)) {

+                return axisValueLabelRegions.get(r);

+            }

+        }

+        return null;

+    }

+

+    public AxisValueLabelFormatter getAxisValueLabelFormatterForRangeVal(

+            double val) {

+        for (RectRegion r : axisValueLabelRegions.elements()) {

+            if (r.containsRangeValue(val)) {

+                return axisValueLabelRegions.get(r);

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Returns the formatter associated with the first (bottom-most) Region

+     * containing value.

+     * 

+     * @param value

+     * @return

+     */

+    /*

+     * public AxisValueLabelFormatter getXYAxisFormatterForRangeVal(double

+     * value) { return getRegionContainingVal(rangeLabelRegions, value); }

+     *//**

+     * Returns the formatter associated with the first (bottom-most) Region

+     * containing value.

+     * 

+     * @param value

+     * @return

+     */

+    /*

+     * public AxisValueLabelFormatter getXYAxisFormatterForDomainVal(double

+     * value) { return getRegionContainingVal(domainLabelRegions, value); }

+     */

+

+    /*

+     * private AxisValueLabelFormatter getRegionContainingVal(ZHash<LineRegion,

+     * AxisValueLabelFormatter> zhash, double val) { for (LineRegion r :

+     * zhash.elements()) { if (r.contains(val)) { return

+     * rangeLabelRegions.get(r); } } // nothing found return null; }

+     */

+

+    /**

+     * Returns a RectF representing the grid area last drawn by this plot.

+     * 

+     * @return

+     */

+    public RectF getGridRect() {

+        return paddedGridRect;

+    }

+    private String getFormattedRangeValue(Number value) {

+        return rangeValueFormat.format(value);

+    }

+

+    private String getFormattedDomainValue(Number value) {

+        return domainValueFormat.format(value);

+    }

+

+    /**

+     * Convenience method. Wraps getYVal(float)

+     * 

+     * @param point

+     * @return

+     */

+    public Double getYVal(PointF point) {

+        return getYVal(point.y);

+    }

+

+    /**

+     * Converts a y pixel to a y value.

+     * 

+     * @param yPix

+     * @return

+     */

+    public Double getYVal(float yPix) {

+        if (plot.getCalculatedMinY() == null

+                || plot.getCalculatedMaxY() == null) {

+            return null;

+        }

+        return ValPixConverter.pixToVal(yPix - paddedGridRect.top, plot

+                .getCalculatedMinY().doubleValue(), plot.getCalculatedMaxY()

+                .doubleValue(), paddedGridRect.height(), true);

+    }

+

+    /**

+     * Convenience method. Wraps getXVal(float)

+     * 

+     * @param point

+     * @return

+     */

+    public Double getXVal(PointF point) {

+        return getXVal(point.x);

+    }

+

+    /**

+     * Converts an x pixel into an x value.

+     * 

+     * @param xPix

+     * @return

+     */

+    public Double getXVal(float xPix) {

+        if (plot.getCalculatedMinX() == null

+                || plot.getCalculatedMaxX() == null) {

+            return null;

+        }

+        return ValPixConverter.pixToVal(xPix - paddedGridRect.left, plot

+                .getCalculatedMinX().doubleValue(), plot.getCalculatedMaxX()

+                .doubleValue(), paddedGridRect.width(), false);

+    }

+

+    @Override

+    protected void doOnDraw(Canvas canvas, RectF widgetRect)

+            throws PlotRenderException {

+        gridRect = getGridRect(widgetRect); // used for drawing the background

+                                            // of the grid

+        paddedGridRect = getPaddedGridRect(gridRect); // used for drawing lines

+                                                      // etc.

+        //Log.v(TAG, "gridRect :" + gridRect);

+        //Log.v(TAG, "paddedGridRect :" + paddedGridRect);

+        // if (!plot.isEmpty()) {

+        // don't draw if we have no space to draw into

+        if ((paddedGridRect.height() > 0.0f) && (paddedGridRect.width() > 0.0f)) {

+            if (plot.getCalculatedMinX() != null

+                    && plot.getCalculatedMaxX() != null

+                    && plot.getCalculatedMinY() != null

+                    && plot.getCalculatedMaxY() != null) {

+                drawGrid(canvas);

+                drawData(canvas);

+                drawCursors(canvas);

+                if (isDrawMarkersEnabled()) {

+                    drawMarkers(canvas);

+                }

+            }

+        }

+        // }

+    }

+

+    private RectF getGridRect(RectF widgetRect) {

+        return new RectF(widgetRect.left + ((rangeAxisLeft)?rangeLabelWidth:1),

+                widgetRect.top + ((domainAxisBottom)?1:domainLabelWidth),

+                widgetRect.right - ((rangeAxisLeft)?1:rangeLabelWidth),

+                widgetRect.bottom - ((domainAxisBottom)?domainLabelWidth:1));

+    }

+

+    private RectF getPaddedGridRect(RectF gridRect) {

+        return new RectF(gridRect.left + gridPaddingLeft, gridRect.top

+                + gridPaddingTop, gridRect.right - gridPaddingRight,

+                gridRect.bottom - gridPaddingBottom);

+    }

+

+    private void drawTickText(Canvas canvas, XYAxisType axis, Number value,

+            float xPix, float yPix, Paint labelPaint) {

+        AxisValueLabelFormatter rf = null;

+        String txt = null;

+        double v = value.doubleValue();

+

+        int canvasState = canvas.save();

+        try {

+            switch (axis) {

+                case DOMAIN:

+                    rf = getAxisValueLabelFormatterForDomainVal(v);

+                    txt = getFormattedDomainValue(value);

+                    canvas.rotate(getDomainLabelOrientation(), xPix, yPix);

+                    break;

+                case RANGE:

+                    rf = getAxisValueLabelFormatterForRangeVal(v);

+                    txt = getFormattedRangeValue(value);

+                    canvas.rotate(getRangeLabelOrientation(), xPix, yPix);

+                    break;

+            }

+

+            // if a matching region formatter was found, create a clone

+            // of labelPaint and use the formatter's color. Otherwise

+            // just use labelPaint:

+            Paint p;

+            if (rf != null) {

+                // p = rf.getPaint();

+                p = new Paint(labelPaint);

+                p.setColor(rf.getColor());

+                // p.setColor(Color.RED);

+            } else {

+                p = labelPaint;

+            }

+            canvas.drawText(txt, xPix, yPix, p);

+        } finally {

+            canvas.restoreToCount(canvasState);

+        }

+    }

+

+    private void drawDomainTick(Canvas canvas, float xPix, Number xVal,

+            Paint labelPaint, Paint linePaint, boolean drawLineOnly) {

+        if (!drawLineOnly) {

+            if (linePaint != null) {

+                if (domainAxisBottom){

+                canvas.drawLine(xPix, gridRect.top, xPix, gridRect.bottom

+                        + domainLabelTickExtension, linePaint);

+                } else {

+                    canvas.drawLine(xPix, gridRect.top - domainLabelTickExtension, xPix,

+                            gridRect.bottom , linePaint);

+                }

+            }

+            if (labelPaint != null) {

+                float fontHeight = FontUtils.getFontHeight(labelPaint);

+                float yPix;

+                if (domainAxisBottom){

+                    yPix = gridRect.bottom + domainLabelTickExtension

+                            + domainLabelVerticalOffset + fontHeight;

+                } else {

+                    yPix = gridRect.top - domainLabelTickExtension

+                            - domainLabelVerticalOffset;

+                }

+                drawTickText(canvas, XYAxisType.DOMAIN, xVal, xPix + domainLabelHorizontalOffset, yPix,

+                        labelPaint);

+            }

+        } else if (linePaint != null) {

+

+            canvas.drawLine(xPix, gridRect.top, xPix, gridRect.bottom,

+                    linePaint);

+

+        }

+    }

+

+    public void drawRangeTick(Canvas canvas, float yPix, Number yVal,

+            Paint labelPaint, Paint linePaint, boolean drawLineOnly) {

+        if (!drawLineOnly) {

+            if (linePaint != null) {

+                if (rangeAxisLeft){

+                canvas.drawLine(gridRect.left - rangeLabelTickExtension, yPix,

+                        gridRect.right, yPix, linePaint);

+                } else {

+                    canvas.drawLine(gridRect.left, yPix,

+                            gridRect.right + rangeLabelTickExtension, yPix, linePaint);

+                }

+            }

+            if (labelPaint != null) {

+                float xPix;

+                if (rangeAxisLeft){

+                    xPix = gridRect.left

+                            - (rangeLabelTickExtension + rangeLabelHorizontalOffset);

+                } else {

+                    xPix = gridRect.right

+                            + (rangeLabelTickExtension + rangeLabelHorizontalOffset);

+                }

+                drawTickText(canvas, XYAxisType.RANGE, yVal, xPix, yPix - rangeLabelVerticalOffset,

+                        labelPaint);

+            }

+        } else if (linePaint != null) {

+            canvas.drawLine(gridRect.left, yPix, gridRect.right, yPix,

+                    linePaint);

+        }

+    }

+

+    /**

+     * Draws the drid and domain/range labels for the plot.

+     * 

+     * @param canvas

+     */

+    protected void drawGrid(Canvas canvas) {

+

+        if (gridBackgroundPaint != null) {

+            canvas.drawRect(gridRect, gridBackgroundPaint);

+        }

+

+        float domainOriginF;

+        if (plot.getDomainOrigin() != null) {

+            double domainOriginVal = plot.getDomainOrigin().doubleValue();

+            domainOriginF = ValPixConverter.valToPix(domainOriginVal, plot

+                    .getCalculatedMinX().doubleValue(), plot

+                    .getCalculatedMaxX().doubleValue(), paddedGridRect.width(),

+                    false);

+            domainOriginF += paddedGridRect.left;

+            // if no origin is set, use the leftmost value visible on the grid:

+        } else {

+            domainOriginF = paddedGridRect.left;

+        }

+

+        XYStep domainStep = XYStepCalculator.getStep(plot, XYAxisType.DOMAIN,

+                paddedGridRect, plot.getCalculatedMinX().doubleValue(), plot

+                        .getCalculatedMaxX().doubleValue());

+

+        // draw domain origin:

+        if (domainOriginF >= paddedGridRect.left

+                && domainOriginF <= paddedGridRect.right) {

+            if (domainOriginLinePaint != null){

+                domainOriginLinePaint.setTextAlign(Paint.Align.CENTER);

+            }

+            drawDomainTick(canvas, domainOriginF, plot.getDomainOrigin()

+                    .doubleValue(), domainOriginLabelPaint,

+                    domainOriginLinePaint, false);

+        }

+

+        // draw ticks LEFT of origin:

+        {

+            int i = 1;

+            double xVal;

+            float xPix = domainOriginF - domainStep.getStepPix();

+            for (; xPix >= paddedGridRect.left; xPix = domainOriginF

+                    - (i * domainStep.getStepPix())) {

+                xVal = plot.getDomainOrigin().doubleValue() - i

+                        * domainStep.getStepVal();

+                if (xPix >= paddedGridRect.left && xPix <= paddedGridRect.right) {

+                    if (i % getTicksPerDomainLabel() == 0) {

+                        drawDomainTick(canvas, xPix, xVal, domainLabelPaint,

+                                domainGridLinePaint, false);

+                    } else {

+                        drawDomainTick(canvas, xPix, xVal, domainLabelPaint,

+                                domainSubGridLinePaint, true);

+                    }

+                }

+                i++;

+            }

+        }

+

+        // draw ticks RIGHT of origin:

+        {

+            int i = 1;

+            double xVal;

+            float xPix = domainOriginF + domainStep.getStepPix();

+            for (; xPix <= paddedGridRect.right; xPix = domainOriginF

+                    + (i * domainStep.getStepPix())) {

+                xVal = plot.getDomainOrigin().doubleValue() + i

+                        * domainStep.getStepVal();

+                if (xPix >= paddedGridRect.left && xPix <= paddedGridRect.right) {

+

+                    if (i % getTicksPerDomainLabel() == 0) {

+                        drawDomainTick(canvas, xPix, xVal, domainLabelPaint,

+                                domainGridLinePaint, false);

+                    } else {

+                        drawDomainTick(canvas, xPix, xVal, domainLabelPaint,

+                                domainSubGridLinePaint, true);

+                    }

+                }

+                i++;

+            }

+        }

+

+        // draw range origin:

+

+        float rangeOriginF;

+        if (plot.getRangeOrigin() != null) {

+            // --------- NEW WAY ------

+            double rangeOriginD = plot.getRangeOrigin().doubleValue();

+            rangeOriginF = ValPixConverter.valToPix(rangeOriginD, plot

+                    .getCalculatedMinY().doubleValue(), plot

+                    .getCalculatedMaxY().doubleValue(),

+                    paddedGridRect.height(), true);

+            rangeOriginF += paddedGridRect.top;

+            // if no origin is set, use the leftmost value visible on the grid

+        } else {

+            rangeOriginF = paddedGridRect.bottom;

+        }

+

+        XYStep rangeStep = XYStepCalculator.getStep(plot, XYAxisType.RANGE,

+                paddedGridRect, plot.getCalculatedMinY().doubleValue(), plot

+                        .getCalculatedMaxY().doubleValue());

+

+        // draw range origin:

+        if (rangeOriginF >= paddedGridRect.top

+                && rangeOriginF <= paddedGridRect.bottom) {

+            if (rangeOriginLinePaint != null){

+                rangeOriginLinePaint.setTextAlign(Paint.Align.RIGHT);

+            }

+            drawRangeTick(canvas, rangeOriginF, plot.getRangeOrigin()

+                    .doubleValue(), rangeOriginLabelPaint,

+                    rangeOriginLinePaint, false);

+        }

+        // draw ticks ABOVE origin:

+        {

+            int i = 1;

+            double yVal;

+            float yPix = rangeOriginF - rangeStep.getStepPix();

+            for (; yPix >= paddedGridRect.top; yPix = rangeOriginF

+                    - (i * rangeStep.getStepPix())) {

+                yVal = plot.getRangeOrigin().doubleValue() + i

+                        * rangeStep.getStepVal();

+                if (yPix >= paddedGridRect.top && yPix <= paddedGridRect.bottom) {

+                    if (i % getTicksPerRangeLabel() == 0) {

+                        drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,

+                                rangeGridLinePaint, false);

+                    } else {

+                        drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,

+                                rangeSubGridLinePaint, true);

+                    }

+                }

+                i++;

+            }

+        }

+

+        // draw ticks BENEATH origin:

+        {

+            int i = 1;

+            double yVal;

+            float yPix = rangeOriginF + rangeStep.getStepPix();

+            for (; yPix <= paddedGridRect.bottom; yPix = rangeOriginF

+                    + (i * rangeStep.getStepPix())) {

+                yVal = plot.getRangeOrigin().doubleValue() - i

+                        * rangeStep.getStepVal();

+                if (yPix >= paddedGridRect.top && yPix <= paddedGridRect.bottom) {

+                    if (i % getTicksPerRangeLabel() == 0) {

+                        drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,

+                                rangeGridLinePaint, false);

+                    } else {

+                        drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,

+                                rangeSubGridLinePaint, true);

+                    }

+                }

+                i++;

+            }

+        }

+    }

+

+    /**

+     * Renders the text associated with user defined markers

+     * 

+     * @param canvas

+     * @param text

+     * @param marker

+     * @param x

+     * @param y

+     */

+    private void drawMarkerText(Canvas canvas, String text, ValueMarker marker,

+            float x, float y) {

+        x += MARKER_LABEL_SPACING;

+        y -= MARKER_LABEL_SPACING;

+        RectF textRect = new RectF(FontUtils.getStringDimensions(text,

+                marker.getTextPaint()));

+        textRect.offsetTo(x, y - textRect.height());

+

+        if (textRect.right > paddedGridRect.right) {

+            textRect.offset(-(textRect.right - paddedGridRect.right), 0);

+        }

+

+        if (textRect.top < paddedGridRect.top) {

+            textRect.offset(0, paddedGridRect.top - textRect.top);

+        }

+

+        canvas.drawText(text, textRect.left, textRect.bottom,

+                marker.getTextPaint());

+

+    }

+

+    protected void drawMarkers(Canvas canvas) {

+        for (YValueMarker marker : plot.getYValueMarkers()) {

+

+            if (marker.getValue() != null) {

+                double yVal = marker.getValue().doubleValue();

+                float yPix = ValPixConverter.valToPix(yVal, plot

+                        .getCalculatedMinY().doubleValue(), plot

+                        .getCalculatedMaxY().doubleValue(), paddedGridRect

+                        .height(), true);

+                yPix += paddedGridRect.top;

+                canvas.drawLine(paddedGridRect.left, yPix,

+                        paddedGridRect.right, yPix, marker.getLinePaint());

+

+                // String text = getFormattedRangeValue(yVal);

+                float xPix = marker.getTextPosition().getPixelValue(

+                        paddedGridRect.width());

+                xPix += paddedGridRect.left;

+

+                if (marker.getText() != null) {

+                    drawMarkerText(canvas, marker.getText(), marker, xPix, yPix);

+                } else {

+                    drawMarkerText(canvas,

+                            getFormattedRangeValue(marker.getValue()), marker,

+                            xPix, yPix);

+                }

+            }

+        }

+

+        for (XValueMarker marker : plot.getXValueMarkers()) {

+            if (marker.getValue() != null) {

+                double xVal = marker.getValue().doubleValue();

+                float xPix = ValPixConverter.valToPix(xVal, plot

+                        .getCalculatedMinX().doubleValue(), plot

+                        .getCalculatedMaxX().doubleValue(), paddedGridRect

+                        .width(), false);

+                xPix += paddedGridRect.left;

+                canvas.drawLine(xPix, paddedGridRect.top, xPix,

+                        paddedGridRect.bottom, marker.getLinePaint());

+

+                // String text = getFormattedDomainValue(xVal);

+                float yPix = marker.getTextPosition().getPixelValue(

+                        paddedGridRect.height());

+                yPix += paddedGridRect.top;

+                if (marker.getText() != null) {

+                    drawMarkerText(canvas, marker.getText(), marker, xPix, yPix);

+                } else {

+                    drawMarkerText(canvas,

+                            getFormattedDomainValue(marker.getValue()), marker,

+                            xPix, yPix);

+                }

+            }

+        }

+    }

+

+    protected void drawCursors(Canvas canvas) {

+        boolean hasDomainCursor = false;

+        // draw the domain cursor:

+        if (domainCursorPaint != null

+                && domainCursorPosition <= paddedGridRect.right

+                && domainCursorPosition >= paddedGridRect.left) {

+            hasDomainCursor = true;

+            canvas.drawLine(domainCursorPosition, paddedGridRect.top,

+                    domainCursorPosition, paddedGridRect.bottom,

+                    domainCursorPaint);

+        }

+

+        boolean hasRangeCursor = false;

+        // draw the range cursor:

+        if (rangeCursorPaint != null

+                && rangeCursorPosition >= paddedGridRect.top

+                && rangeCursorPosition <= paddedGridRect.bottom) {

+            hasRangeCursor = true;

+            canvas.drawLine(paddedGridRect.left, rangeCursorPosition,

+                    paddedGridRect.right, rangeCursorPosition, rangeCursorPaint);

+        }

+

+        if (drawCursorLabelEnabled && cursorLabelPaint != null

+                && hasRangeCursor && hasDomainCursor) {

+

+            String label = "X="

+                    + getDomainValueFormat().format(getDomainCursorVal());

+            label += " Y=" + getRangeValueFormat().format(getRangeCursorVal());

+

+            // convert the label dimensions rect into floating-point:

+            RectF cursorRect = new RectF(FontUtils.getPackedStringDimensions(

+                    label, cursorLabelPaint));

+            cursorRect.offsetTo(domainCursorPosition, rangeCursorPosition

+                    - cursorRect.height());

+

+            // if we are too close to the right edge of the plot, we will move

+            // the

+            // label to the left side of our cursor:

+            if (cursorRect.right >= paddedGridRect.right) {

+                cursorRect.offsetTo(domainCursorPosition - cursorRect.width(),

+                        cursorRect.top);

+            }

+

+            // same thing for the top edge of the plot:

+            // dunno why but these rects can have negative values for top and

+            // bottom.

+            if (cursorRect.top <= paddedGridRect.top) {

+                cursorRect.offsetTo(cursorRect.left, rangeCursorPosition);

+            }

+

+            if (cursorLabelBackgroundPaint != null) {

+                canvas.drawRect(cursorRect, cursorLabelBackgroundPaint);

+            }

+

+            canvas.drawText(label, cursorRect.left, cursorRect.bottom,

+                    cursorLabelPaint);

+        }

+    }

+

+    /**

+     * Draws lines and points for each element in the series.

+     * 

+     * @param canvas

+     * @throws PlotRenderException

+     */

+    protected void drawData(Canvas canvas) throws PlotRenderException {

+        // TODO: iterate through a XYSeriesRenderer list

+

+        // int canvasState = canvas.save();

+        try {

+            canvas.save(Canvas.ALL_SAVE_FLAG);

+            canvas.clipRect(gridRect, android.graphics.Region.Op.INTERSECT);

+            for (XYSeriesRenderer renderer : plot.getRendererList()) {

+                renderer.render(canvas, paddedGridRect);

+            }

+            // canvas.restoreToCount(canvasState);

+        } finally {

+            canvas.restore();

+        }

+    }

+

+    protected void drawPoint(Canvas canvas, PointF point, Paint paint) {

+        canvas.drawPoint(point.x, point.y, paint);

+    }

+

+    public float getDomainLabelWidth() {

+        return domainLabelWidth;

+    }

+

+    public void setDomainLabelWidth(float domainLabelWidth) {

+        this.domainLabelWidth = domainLabelWidth;

+    }

+

+    public float getRangeLabelWidth() {

+        return rangeLabelWidth;

+    }

+

+    public void setRangeLabelWidth(float rangeLabelWidth) {

+        this.rangeLabelWidth = rangeLabelWidth;

+    }

+

+    public float getDomainLabelVerticalOffset() {

+        return domainLabelVerticalOffset;

+    }

+

+    public void setDomainLabelVerticalOffset(float domainLabelVerticalOffset) {

+        this.domainLabelVerticalOffset = domainLabelVerticalOffset;

+    }

+

+    public float getDomainLabelHorizontalOffset() {

+        return domainLabelHorizontalOffset;

+    }

+

+    public void setDomainLabelHorizontalOffset(float domainLabelHorizontalOffset) {

+        this.domainLabelHorizontalOffset = domainLabelHorizontalOffset;

+    }

+

+    public float getRangeLabelHorizontalOffset() {

+        return rangeLabelHorizontalOffset;

+    }

+

+    public void setRangeLabelHorizontalOffset(float rangeLabelHorizontalOffset) {

+        this.rangeLabelHorizontalOffset = rangeLabelHorizontalOffset;

+    }

+

+    public float getRangeLabelVerticalOffset() {

+        return rangeLabelVerticalOffset;

+    }

+

+    public void setRangeLabelVerticalOffset(float rangeLabelVerticalOffset) {

+        this.rangeLabelVerticalOffset = rangeLabelVerticalOffset;

+    }

+

+    public Paint getGridBackgroundPaint() {

+        return gridBackgroundPaint;

+    }

+

+    public void setGridBackgroundPaint(Paint gridBackgroundPaint) {

+        this.gridBackgroundPaint = gridBackgroundPaint;

+    }

+

+    public Paint getDomainLabelPaint() {

+        return domainLabelPaint;

+    }

+

+    public void setDomainLabelPaint(Paint domainLabelPaint) {

+        this.domainLabelPaint = domainLabelPaint;

+    }

+

+    public Paint getRangeLabelPaint() {

+        return rangeLabelPaint;

+    }

+

+    public void setRangeLabelPaint(Paint rangeLabelPaint) {

+        this.rangeLabelPaint = rangeLabelPaint;

+    }

+

+    /**

+     * Get the paint used to draw the domain grid line.

+     */

+    public Paint getDomainGridLinePaint() {

+        return domainGridLinePaint;

+    }

+

+    /**

+     * Set the paint used to draw the domain grid line.

+     * @param gridLinePaint

+     */

+    public void setDomainGridLinePaint(Paint gridLinePaint) {

+        this.domainGridLinePaint = gridLinePaint;

+    }

+

+    /**

+     * Get the paint used to draw the range grid line.

+     */

+    public Paint getRangeGridLinePaint() {

+        return rangeGridLinePaint;

+    }

+

+    /**

+     * Get the paint used to draw the domain grid line.

+     */

+    public Paint getDomainSubGridLinePaint() {

+        return domainSubGridLinePaint;

+    }

+    

+    /**

+     * Set the paint used to draw the domain grid line.

+     * @param gridLinePaint

+     */

+    public void setDomainSubGridLinePaint(Paint gridLinePaint) {

+        this.domainSubGridLinePaint = gridLinePaint;

+    }

+

+    /**

+     * Set the Paint used to draw the range grid line.

+     * @param gridLinePaint

+     */

+    public void setRangeGridLinePaint(Paint gridLinePaint) {

+        this.rangeGridLinePaint = gridLinePaint;

+    }

+

+    /**

+     * Get the paint used to draw the range grid line.

+     */

+    public Paint getRangeSubGridLinePaint() {

+        return rangeSubGridLinePaint;

+    }

+

+    /**

+     * Set the Paint used to draw the range grid line.

+     * @param gridLinePaint

+     */

+    public void setRangeSubGridLinePaint(Paint gridLinePaint) {

+        this.rangeSubGridLinePaint = gridLinePaint;

+    }

+    

+    // TODO: make a generic renderer queue.

+

+    public Format getRangeValueFormat() {

+        return rangeValueFormat;

+    }

+

+    public void setRangeValueFormat(Format rangeValueFormat) {

+        this.rangeValueFormat = rangeValueFormat;

+    }

+

+    public Format getDomainValueFormat() {

+        return domainValueFormat;

+    }

+

+    public void setDomainValueFormat(Format domainValueFormat) {

+        this.domainValueFormat = domainValueFormat;

+    }

+

+    public int getDomainLabelTickExtension() {

+        return domainLabelTickExtension;

+    }

+

+    public void setDomainLabelTickExtension(int domainLabelTickExtension) {

+        this.domainLabelTickExtension = domainLabelTickExtension;

+    }

+

+    public int getRangeLabelTickExtension() {

+        return rangeLabelTickExtension;

+    }

+

+    public void setRangeLabelTickExtension(int rangeLabelTickExtension) {

+        this.rangeLabelTickExtension = rangeLabelTickExtension;

+    }

+

+    public int getTicksPerRangeLabel() {

+        return ticksPerRangeLabel;

+    }

+

+    public void setTicksPerRangeLabel(int ticksPerRangeLabel) {

+        this.ticksPerRangeLabel = ticksPerRangeLabel;

+    }

+

+    public int getTicksPerDomainLabel() {

+        return ticksPerDomainLabel;

+    }

+

+    public void setTicksPerDomainLabel(int ticksPerDomainLabel) {

+        this.ticksPerDomainLabel = ticksPerDomainLabel;

+    }

+

+    public void setGridPaddingTop(float gridPaddingTop) {

+        this.gridPaddingTop = gridPaddingTop;

+    }

+

+    public float getGridPaddingBottom() {

+        return gridPaddingBottom;

+    }

+

+    public void setGridPaddingBottom(float gridPaddingBottom) {

+        this.gridPaddingBottom = gridPaddingBottom;

+    }

+

+    public float getGridPaddingLeft() {

+        return gridPaddingLeft;

+    }

+

+    public void setGridPaddingLeft(float gridPaddingLeft) {

+        this.gridPaddingLeft = gridPaddingLeft;

+    }

+

+    public float getGridPaddingRight() {

+        return gridPaddingRight;

+    }

+

+    public void setGridPaddingRight(float gridPaddingRight) {

+        this.gridPaddingRight = gridPaddingRight;

+    }

+

+    public float getGridPaddingTop() {

+        return gridPaddingTop;

+    }

+

+    public void setGridPadding(float left, float top, float right, float bottom) {

+        setGridPaddingLeft(left);

+        setGridPaddingTop(top);

+        setGridPaddingRight(right);

+        setGridPaddingBottom(bottom);

+    }

+

+    public Paint getDomainOriginLinePaint() {

+        return domainOriginLinePaint;

+    }

+

+    public void setDomainOriginLinePaint(Paint domainOriginLinePaint) {

+        this.domainOriginLinePaint = domainOriginLinePaint;

+    }

+

+    public Paint getRangeOriginLinePaint() {

+        return rangeOriginLinePaint;

+    }

+

+    public void setRangeOriginLinePaint(Paint rangeOriginLinePaint) {

+        this.rangeOriginLinePaint = rangeOriginLinePaint;

+    }

+

+    public Paint getDomainOriginLabelPaint() {

+        return domainOriginLabelPaint;

+    }

+

+    public void setDomainOriginLabelPaint(Paint domainOriginLabelPaint) {

+        this.domainOriginLabelPaint = domainOriginLabelPaint;

+    }

+

+    public Paint getRangeOriginLabelPaint() {

+        return rangeOriginLabelPaint;

+    }

+

+    public void setRangeOriginLabelPaint(Paint rangeOriginLabelPaint) {

+        this.rangeOriginLabelPaint = rangeOriginLabelPaint;

+    }

+

+    public void setCursorPosition(float x, float y) {

+        setDomainCursorPosition(x);

+        setRangeCursorPosition(y);

+    }

+

+    public void setCursorPosition(PointF point) {

+        setCursorPosition(point.x, point.y);

+    }

+

+    public float getDomainCursorPosition() {

+        return domainCursorPosition;

+    }

+

+    public Double getDomainCursorVal() {

+        return getXVal(getDomainCursorPosition());

+    }

+

+    public void setDomainCursorPosition(float domainCursorPosition) {

+        this.domainCursorPosition = domainCursorPosition;

+    }

+

+    public float getRangeCursorPosition() {

+        return rangeCursorPosition;

+    }

+

+    public Double getRangeCursorVal() {

+        return getYVal(getRangeCursorPosition());

+    }

+

+    public void setRangeCursorPosition(float rangeCursorPosition) {

+        this.rangeCursorPosition = rangeCursorPosition;

+    }

+

+    public Paint getCursorLabelPaint() {

+        return cursorLabelPaint;

+    }

+

+    public void setCursorLabelPaint(Paint cursorLabelPaint) {

+        this.cursorLabelPaint = cursorLabelPaint;

+    }

+

+    public Paint getCursorLabelBackgroundPaint() {

+        return cursorLabelBackgroundPaint;

+    }

+

+    public void setCursorLabelBackgroundPaint(Paint cursorLabelBackgroundPaint) {

+        this.cursorLabelBackgroundPaint = cursorLabelBackgroundPaint;

+    }

+

+    public boolean isDrawMarkersEnabled() {

+        return drawMarkersEnabled;

+    }

+

+    public void setDrawMarkersEnabled(boolean drawMarkersEnabled) {

+        this.drawMarkersEnabled = drawMarkersEnabled;

+    }

+

+    public boolean isRangeAxisLeft() {

+        return rangeAxisLeft;

+    }

+

+    public void setRangeAxisLeft(boolean rangeAxisLeft) {

+        this.rangeAxisLeft = rangeAxisLeft;

+    }

+

+    public boolean isDomainAxisBottom() {

+        return domainAxisBottom;

+    }

+

+    public void setDomainAxisBottom(boolean domainAxisBottom) {

+        this.domainAxisBottom = domainAxisBottom;

+    }

+    

+    /*

+     * set the position of the range axis labels.  Set the labelPaint textSizes before setting this.

+     * This call sets the various vertical and horizontal offsets and widths to good defaults.

+     * 

+     * @param rangeAxisLeft axis labels are on the left hand side not the right hand side.

+     * @param rangeAxisOverlay axis labels are overlaid on the plot, not external to it.

+     * @param tickSize the size of the tick extensions for none overlaid axis.

+     * @param maxLableString Sample label representing the biggest size space needs to be allocated for.

+     */

+    public void setRangeAxisPosition(boolean rangeAxisLeft, boolean rangeAxisOverlay, int tickSize, String maxLableString){

+        setRangeAxisLeft(rangeAxisLeft);

+        

+        if (rangeAxisOverlay) {

+            setRangeLabelWidth(1);    // needs to be at least 1 to display grid line.

+            setRangeLabelHorizontalOffset(-2.0f);

+            setRangeLabelVerticalOffset(2.0f);    // get above the line

+            Paint p = getRangeLabelPaint();

+            if (p != null) {

+                p.setTextAlign(((rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));

+            }

+            Paint po = getRangeOriginLabelPaint();

+            if (po != null) {

+                po.setTextAlign(((rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));

+            }

+            setRangeLabelTickExtension(0); 

+        } else {

+            setRangeLabelWidth(1);    // needs to be at least 1 to display grid line.

+                                      // if we have a paint this gets bigger.

+            setRangeLabelHorizontalOffset(1.0f);

+            setRangeLabelTickExtension(tickSize);

+            Paint p = getRangeLabelPaint();

+            if (p != null) {

+                p.setTextAlign(((!rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));

+                Rect r = FontUtils.getPackedStringDimensions(maxLableString,p);

+                setRangeLabelVerticalOffset(r.top/2);

+                setRangeLabelWidth(r.right + getRangeLabelTickExtension());

+            }

+            Paint po = getRangeOriginLabelPaint();

+            if (po != null) {

+                po.setTextAlign(((!rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));

+            }

+        }

+    }

+    

+    /*

+     * set the position of the domain axis labels.  Set the labelPaint textSizes before setting this.

+     * This call sets the various vertical and horizontal offsets and widths to good defaults.

+     * 

+     * @param domainAxisBottom axis labels are on the bottom not the top of the plot.

+     * @param domainAxisOverlay axis labels are overlaid on the plot, not external to it.

+     * @param tickSize the size of the tick extensions for non overlaid axis.

+     * @param maxLableString Sample label representing the biggest size space needs to be allocated for.

+     */

+    public void setDomainAxisPosition(boolean domainAxisBottom, boolean domainAxisOverlay, int tickSize, String maxLabelString){

+        setDomainAxisBottom(domainAxisBottom);

+        if (domainAxisOverlay) {

+            setDomainLabelWidth(1);    // needs to be at least 1 to display grid line.

+            setDomainLabelVerticalOffset(2.0f);    // get above the line

+            setDomainLabelTickExtension(0);

+            Paint p = getDomainLabelPaint();

+            if (p != null) {

+                Rect r = FontUtils.getPackedStringDimensions(maxLabelString,p);

+                if (domainAxisBottom){

+                    setDomainLabelVerticalOffset(2 * r.top);

+                } else {

+                    setDomainLabelVerticalOffset(r.top - 1.0f);

+                }

+            }

+        } else {

+            setDomainLabelWidth(1);    // needs to be at least 1 to display grid line.

+                                       // if we have a paint this gets bigger.

+            setDomainLabelTickExtension(tickSize);

+            Paint p = getDomainLabelPaint();

+            if (p != null) {

+                float fontHeight = FontUtils.getFontHeight(p);

+                if (domainAxisBottom){

+                    setDomainLabelVerticalOffset(-4.0f);

+                } else {

+                    setDomainLabelVerticalOffset(+1.0f);

+                }

+                setDomainLabelWidth(fontHeight + getDomainLabelTickExtension());

+            }

+        }

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYLegendWidget.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYLegendWidget.java
new file mode 100644
index 0000000..3350406
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYLegendWidget.java
@@ -0,0 +1,248 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.*;

+import com.androidplot.ui.LayoutManager;

+import com.androidplot.ui.SeriesAndFormatterList;

+import com.androidplot.ui.SizeMetrics;

+import com.androidplot.ui.TableModel;

+import com.androidplot.ui.widget.Widget;

+import com.androidplot.util.FontUtils;

+

+import java.util.*;

+

+public class XYLegendWidget extends Widget {

+

+    /**

+     * This class is of no use outside of XYLegendWidget.  It's just used to alphabetically sort

+     * Region legend entries.

+     */

+    private static class RegionEntryComparator implements Comparator<Map.Entry<XYRegionFormatter, String>> {

+        @Override

+        public int compare(Map.Entry<XYRegionFormatter, String> o1, Map.Entry<XYRegionFormatter, String> o2) {

+            return o1.getValue().compareTo(o2.getValue());

+        }

+    }

+

+    private enum CellType {

+        SERIES,

+        REGION

+    }

+

+    private XYPlot plot;

+    //private float iconWidth = 12;

+    private Paint textPaint;

+    private Paint iconBorderPaint;

+    private TableModel tableModel;

+    private boolean drawIconBackgroundEnabled = true;

+    private boolean drawIconBorderEnabled = true;

+

+    private SizeMetrics iconSizeMetrics;

+    private static final RegionEntryComparator regionEntryComparator = new RegionEntryComparator();

+    //private RectF iconRect = new RectF(0, 0, ICON_WIDTH_DEFAULT, ICON_HEIGHT_DEFAULT);

+

+    {

+        textPaint = new Paint();

+        textPaint.setColor(Color.LTGRAY);

+        textPaint.setAntiAlias(true);

+

+        iconBorderPaint = new Paint();

+        iconBorderPaint.setStyle(Paint.Style.STROKE);

+        //regionEntryComparator = new RegionEntryComparator();

+    }

+

+    public XYLegendWidget(LayoutManager layoutManager, XYPlot plot,

+                          SizeMetrics widgetSizeMetrics,

+                          TableModel tableModel,

+                          SizeMetrics iconSizeMetrics) {

+        super(layoutManager, widgetSizeMetrics);

+        this.plot = plot;

+        setTableModel(tableModel);

+        this.iconSizeMetrics = iconSizeMetrics;

+    }

+

+    public synchronized void setTableModel(TableModel tableModel) {

+        this.tableModel = tableModel;

+    }

+

+    private RectF getIconRect(RectF cellRect) {

+        float cellRectCenterY = cellRect.top + (cellRect.height()/2);

+        RectF iconRect = iconSizeMetrics.getRectF(cellRect);

+

+        // center the icon rect vertically

+        float centeredIconOriginY = cellRectCenterY - (iconRect.height()/2);

+        iconRect.offsetTo(cellRect.left + 1, centeredIconOriginY);

+        return iconRect;

+    }

+

+    private static float getRectCenterY(RectF cellRect) {

+        return cellRect.top + (cellRect.height()/2);

+    }

+

+    private void beginDrawingCell(Canvas canvas, RectF iconRect) {

+

+        Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint();

+        if(drawIconBackgroundEnabled && bgPaint != null) {

+            canvas.drawRect(iconRect, bgPaint);

+        }

+    }

+

+    private void finishDrawingCell(Canvas canvas, RectF cellRect, RectF iconRect, String text) {

+

+        Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint();

+        if(drawIconBorderEnabled && bgPaint != null) {

+            iconBorderPaint.setColor(bgPaint.getColor());

+            canvas.drawRect(iconRect, iconBorderPaint);

+        }

+

+        float centeredTextOriginY = getRectCenterY(cellRect) + (FontUtils.getFontHeight(textPaint)/2);

+                canvas.drawText(text, iconRect.right + 2, centeredTextOriginY, textPaint);

+    }

+

+    protected void drawRegionLegendIcon(Canvas canvas, RectF rect, XYRegionFormatter formatter) {

+            canvas.drawRect(rect, formatter.getPaint());

+        }

+

+    private void drawRegionLegendCell(Canvas canvas, XYRegionFormatter formatter, RectF cellRect, String text) {

+        RectF iconRect = getIconRect(cellRect);

+        beginDrawingCell(canvas, iconRect);

+

+                drawRegionLegendIcon(

+                        canvas,

+                        iconRect,

+                        formatter

+                        );

+        finishDrawingCell(canvas, cellRect, iconRect, text);

+    }

+

+    private void drawSeriesLegendCell(Canvas canvas, XYSeriesRenderer renderer, XYSeriesFormatter formatter, RectF cellRect, String seriesTitle) {

+        RectF iconRect = getIconRect(cellRect);

+        beginDrawingCell(canvas, iconRect);

+

+                renderer.drawSeriesLegendIcon(

+                        canvas,

+                        iconRect,

+                        formatter);

+        finishDrawingCell(canvas, cellRect, iconRect, seriesTitle);

+    }

+

+    @Override

+    protected synchronized void doOnDraw(Canvas canvas, RectF widgetRect) {

+        // TODO: a good amount of iterating could be avoided if

+        // we create a temporary list of all the legend items up here.

+        if(plot.isEmpty()) {

+            return;

+        }

+

+        //Hashtable<XYRegionFormatter, XYSeriesRenderer> regionRendererLookup = new Hashtable<XYRegionFormatter, XYSeriesRenderer>();

+

+        // Keep an alphabetically sorted list of regions:

+        TreeSet<Map.Entry<XYRegionFormatter, String>> sortedRegions = new TreeSet<Map.Entry<XYRegionFormatter, String>>(new RegionEntryComparator());

+

+        // Calculate the number of cells needed to draw the Legend:

+        int seriesCount = 0;

+        for(XYSeriesRenderer renderer : plot.getRendererList()) {

+

+            SeriesAndFormatterList sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass());

+            if(sfl != null) {

+                seriesCount += sfl.size();

+            }

+

+            // Figure out how many regions need to be added to the legend:

+            Hashtable<XYRegionFormatter, String> urf = renderer.getUniqueRegionFormatters();

+            /*for(XYRegionFormatter xyf : urf.keySet()) {

+                regionRendererLookup.put(xyf, renderer);

+            }*/

+            sortedRegions.addAll(urf.entrySet());

+            //sortedRegions.addAll(renderer.getUniqueRegionFormatters().entrySet());

+        }

+        seriesCount += sortedRegions.size();

+

+        // Create an iterator specially created to draw the number of cells we calculated:

+        Iterator<RectF> it = tableModel.getIterator(widgetRect, seriesCount);

+

+        RectF cellRect;

+

+        // draw each series legend item:

+        for(XYSeriesRenderer renderer : plot.getRendererList()) {

+            SeriesAndFormatterList<XYSeries,XYSeriesFormatter> sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass());

+

+            if (sfl != null) {

+                // maxIndex is only used if it has been determined.

+                // if it is 0 then it could not be determined.

+                for (int i = 0; i < sfl.size() && it.hasNext(); i++) {

+                    cellRect = it.next();

+                    XYSeriesFormatter formatter = sfl.getFormatter(i);

+                    drawSeriesLegendCell(canvas, renderer, formatter, cellRect, sfl.getSeries(i).getTitle());

+                }

+            }

+        }

+

+        // draw each region legend item:

+        for(Map.Entry<XYRegionFormatter, String> entry : sortedRegions) {

+            if(!it.hasNext()) {

+                break;

+            }

+            cellRect = it.next();

+            XYRegionFormatter formatter = entry.getKey();

+            drawRegionLegendCell(canvas, formatter, cellRect, entry.getValue());

+        }

+    }

+

+

+    public Paint getTextPaint() {

+        return textPaint;

+    }

+

+    public void setTextPaint(Paint textPaint) {

+        this.textPaint = textPaint;

+    }

+

+    public boolean isDrawIconBackgroundEnabled() {

+        return drawIconBackgroundEnabled;

+    }

+

+    public void setDrawIconBackgroundEnabled(boolean drawIconBackgroundEnabled) {

+        this.drawIconBackgroundEnabled = drawIconBackgroundEnabled;

+    }

+

+    public boolean isDrawIconBorderEnabled() {

+        return drawIconBorderEnabled;

+    }

+

+    public void setDrawIconBorderEnabled(boolean drawIconBorderEnabled) {

+        this.drawIconBorderEnabled = drawIconBorderEnabled;

+    }

+

+    public TableModel getTableModel() {

+        return tableModel;

+    }

+

+    public SizeMetrics getIconSizeMetrics() {

+        return iconSizeMetrics;

+    }

+

+    /**

+     * Set the size of each legend's icon.  Note that when using relative sizing,

+     * the size is calculated against the countaining cell's size, not the plot's size.

+     * @param iconSizeMetrics

+     */

+    public void setIconSizeMetrics(SizeMetrics iconSizeMetrics) {

+        this.iconSizeMetrics = iconSizeMetrics;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlot.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlot.java
new file mode 100644
index 0000000..d3c286c
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlot.java
@@ -0,0 +1,1344 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import android.graphics.Canvas;

+import android.graphics.Color;

+import android.graphics.Paint;

+import android.graphics.PointF;

+import android.util.AttributeSet;

+import com.androidplot.Plot;

+import com.androidplot.ui.*;

+import com.androidplot.ui.TextOrientationType;

+import com.androidplot.ui.widget.TextLabelWidget;

+import com.androidplot.util.PixelUtils;

+

+import java.text.Format;

+import java.util.ArrayList;

+import java.util.List;

+

+/**

+ * A View to graphically display x/y coordinates.

+ */

+public class XYPlot extends Plot<XYSeries, XYSeriesFormatter, XYSeriesRenderer> {

+

+    private BoundaryMode domainOriginBoundaryMode;

+    private BoundaryMode rangeOriginBoundaryMode;

+

+    // widgets

+    private XYLegendWidget legendWidget;

+    private XYGraphWidget graphWidget;

+    private TextLabelWidget domainLabelWidget;

+    private TextLabelWidget rangeLabelWidget;

+

+    private XYStepMode domainStepMode = XYStepMode.SUBDIVIDE;

+    private double domainStepValue = 10;

+

+    private XYStepMode rangeStepMode = XYStepMode.SUBDIVIDE;

+    private double rangeStepValue = 10;

+

+    // user settable min/max values

+    private Number userMinX;

+    private Number userMaxX;

+    private Number userMinY;

+    private Number userMaxY;

+

+    // these are the final min/max used for dispplaying data

+    private Number calculatedMinX;

+    private Number calculatedMaxX;

+    private Number calculatedMinY;

+    private Number calculatedMaxY;

+

+    // previous calculated min/max vals.

+    // primarily used for GROW/SHRINK operations.

+    private Number prevMinX;

+    private Number prevMaxX;

+    private Number prevMinY;

+    private Number prevMaxY;

+

+    // uses set boundary min and max values

+    // should be null if not used.

+    private Number rangeTopMin = null;

+    private Number rangeTopMax = null;

+    private Number rangeBottomMin = null;

+    private Number rangeBottomMax = null;

+    private Number domainLeftMin = null;

+    private Number domainLeftMax = null;

+    private Number domainRightMin = null;

+    private Number domainRightMax = null;

+

+    // used for  calculating the domain/range extents that will be displayed on the plot.

+    // using boundaries and origins are mutually exclusive.  because of this,

+    // setting one will disable the other.  when only setting the FramingModel,

+    // the origin or boundary is set to the current value of the plot.

+    private XYFramingModel domainFramingModel = XYFramingModel.EDGE;

+    private XYFramingModel rangeFramingModel = XYFramingModel.EDGE;

+

+    private Number userDomainOrigin;

+    private Number userRangeOrigin;

+

+    private Number calculatedDomainOrigin;

+    private Number calculatedRangeOrigin;

+

+    @SuppressWarnings("FieldCanBeLocal")

+    private Number domainOriginExtent = null;

+    @SuppressWarnings("FieldCanBeLocal")

+    private Number rangeOriginExtent = null;

+

+    private BoundaryMode domainUpperBoundaryMode = BoundaryMode.AUTO;

+    private BoundaryMode domainLowerBoundaryMode = BoundaryMode.AUTO;

+    private BoundaryMode rangeUpperBoundaryMode = BoundaryMode.AUTO;

+    private BoundaryMode rangeLowerBoundaryMode = BoundaryMode.AUTO;

+

+    private boolean drawDomainOriginEnabled = true;

+    private boolean drawRangeOriginEnabled = true;

+

+    private ArrayList<YValueMarker> yValueMarkers;

+    private ArrayList<XValueMarker> xValueMarkers;

+

+    private RectRegion defaultBounds;

+

+

+    private static final int DEFAULT_LEGEND_WIDGET_H_DP = 10;

+    private static final int DEFAULT_LEGEND_WIDGET_ICON_SIZE_DP = 7;

+    private static final int DEFAULT_GRAPH_WIDGET_H_DP = 18;

+    private static final int DEFAULT_GRAPH_WIDGET_W_DP = 10;

+    private static final int DEFAULT_DOMAIN_LABEL_WIDGET_H_DP = 10;

+    private static final int DEFAULT_DOMAIN_LABEL_WIDGET_W_DP = 80;

+    private static final int DEFAULT_RANGE_LABEL_WIDGET_H_DP = 50;

+    private static final int DEFAULT_RANGE_LABEL_WIDGET_W_DP = 10;

+

+    private static final int DEFAULT_LEGEND_WIDGET_Y_OFFSET_DP = 0;

+    private static final int DEFAULT_LEGEND_WIDGET_X_OFFSET_DP = 40;

+    private static final int DEFAULT_GRAPH_WIDGET_Y_OFFSET_DP = 0;

+    private static final int DEFAULT_GRAPH_WIDGET_X_OFFSET_DP = 0;

+    private static final int DEFAULT_DOMAIN_LABEL_WIDGET_Y_OFFSET_DP = 0;

+    private static final int DEFAULT_DOMAIN_LABEL_WIDGET_X_OFFSET_DP = 20;

+    private static final int DEFAULT_RANGE_LABEL_WIDGET_Y_OFFSET_DP = 0;

+    private static final int DEFAULT_RANGE_LABEL_WIDGET_X_OFFSET_DP = 0;

+

+    private static final int DEFAULT_GRAPH_WIDGET_TOP_MARGIN_DP = 3;

+    private static final int DEFAULT_GRAPH_WIDGET_RIGHT_MARGIN_DP = 3;

+    private static final int DEFAULT_PLOT_LEFT_MARGIN_DP = 2;

+    private static final int DEFAULT_PLOT_RIGHT_MARGIN_DP = 2;

+    private static final int DEFAULT_PLOT_BOTTOM_MARGIN_DP = 2;

+

+    public XYPlot(Context context, String title) {

+        super(context, title);

+    }

+

+    public XYPlot(Context context, String title, RenderMode mode) {

+        super(context, title, mode);

+    }

+

+    public XYPlot(Context context, AttributeSet attributes) {

+        super(context, attributes);

+    }

+

+    public XYPlot(Context context, AttributeSet attrs, int defStyle) {

+        super(context, attrs, defStyle);

+

+    }

+

+    @Override

+    protected void onPreInit() {

+        legendWidget = new XYLegendWidget(

+                getLayoutManager(),

+                this,

+                new SizeMetrics(

+                        PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_H_DP),

+                        SizeLayoutType.ABSOLUTE, 0.5f, SizeLayoutType.RELATIVE),

+                new DynamicTableModel(0, 1),

+                new SizeMetrics(

+                        PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_ICON_SIZE_DP),

+                        SizeLayoutType.ABSOLUTE,

+                        PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_ICON_SIZE_DP),

+                        SizeLayoutType.ABSOLUTE));

+

+        graphWidget = new XYGraphWidget(

+                getLayoutManager(),

+                this,

+                new SizeMetrics(

+                        PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_H_DP),

+                        SizeLayoutType.FILL,

+                        PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_W_DP),

+                        SizeLayoutType.FILL));

+

+        Paint backgroundPaint = new Paint();

+        backgroundPaint.setColor(Color.DKGRAY);

+        backgroundPaint.setStyle(Paint.Style.FILL);

+        graphWidget.setBackgroundPaint(backgroundPaint);

+

+

+        domainLabelWidget = new TextLabelWidget(

+                getLayoutManager(),

+                new SizeMetrics(

+                        PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_H_DP),

+                        SizeLayoutType.ABSOLUTE,

+                        PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_W_DP),

+                        SizeLayoutType.ABSOLUTE),

+                TextOrientationType.HORIZONTAL);

+        rangeLabelWidget = new TextLabelWidget(

+                getLayoutManager(),

+                new SizeMetrics(

+                        PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_H_DP),

+                        SizeLayoutType.ABSOLUTE,

+                        PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_W_DP),

+                        SizeLayoutType.ABSOLUTE),

+                TextOrientationType.VERTICAL_ASCENDING);

+

+        legendWidget.position(

+                PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_X_OFFSET_DP),

+                XLayoutStyle.ABSOLUTE_FROM_RIGHT,

+                PixelUtils.dpToPix(DEFAULT_LEGEND_WIDGET_Y_OFFSET_DP),

+                YLayoutStyle.ABSOLUTE_FROM_BOTTOM,

+                AnchorPosition.RIGHT_BOTTOM);

+

+        graphWidget.position(

+                PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_X_OFFSET_DP),

+                XLayoutStyle.ABSOLUTE_FROM_RIGHT,

+                PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_Y_OFFSET_DP),

+                YLayoutStyle.ABSOLUTE_FROM_CENTER,

+                AnchorPosition.RIGHT_MIDDLE);

+

+        domainLabelWidget.position(

+                PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_X_OFFSET_DP),

+                XLayoutStyle.ABSOLUTE_FROM_LEFT,

+                PixelUtils.dpToPix(DEFAULT_DOMAIN_LABEL_WIDGET_Y_OFFSET_DP),

+                YLayoutStyle.ABSOLUTE_FROM_BOTTOM,

+                AnchorPosition.LEFT_BOTTOM);

+

+        rangeLabelWidget.position(

+                PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_X_OFFSET_DP),

+                XLayoutStyle.ABSOLUTE_FROM_LEFT,

+                PixelUtils.dpToPix(DEFAULT_RANGE_LABEL_WIDGET_Y_OFFSET_DP),

+                YLayoutStyle.ABSOLUTE_FROM_CENTER,

+                AnchorPosition.LEFT_MIDDLE);

+

+        getLayoutManager().moveToTop(getTitleWidget());

+        getLayoutManager().moveToTop(getLegendWidget());

+        graphWidget.setMarginTop(PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_TOP_MARGIN_DP));

+        graphWidget.setMarginRight(PixelUtils.dpToPix(DEFAULT_GRAPH_WIDGET_RIGHT_MARGIN_DP));

+

+        getDomainLabelWidget().pack();

+        getRangeLabelWidget().pack();

+        setPlotMarginLeft(PixelUtils.dpToPix(DEFAULT_PLOT_LEFT_MARGIN_DP));

+        setPlotMarginRight(PixelUtils.dpToPix(DEFAULT_PLOT_RIGHT_MARGIN_DP));

+        setPlotMarginBottom(PixelUtils.dpToPix(DEFAULT_PLOT_BOTTOM_MARGIN_DP));

+

+        xValueMarkers = new ArrayList<XValueMarker>();

+        yValueMarkers = new ArrayList<YValueMarker>();

+

+        setDefaultBounds(new RectRegion(-1, 1, -1, 1));

+    }

+

+

+    public void setGridPadding(float left, float top, float right, float bottom) {

+        getGraphWidget().setGridPaddingTop(top);

+        getGraphWidget().setGridPaddingBottom(bottom);

+        getGraphWidget().setGridPaddingLeft(left);

+        getGraphWidget().setGridPaddingRight(right);

+    }

+

+    @Override

+    protected void notifyListenersBeforeDraw(Canvas canvas) {

+        super.notifyListenersBeforeDraw(canvas);

+

+        // this call must be AFTER the notify so that if the listener

+        // is a synchronized series, it has the opportunity to

+        // place a read lock on it's data.

+        calculateMinMaxVals();

+    }

+

+    /**

+     * Checks whether the point is within the plot's graph area.

+     *

+     * @param x

+     * @param y

+     * @return

+     */

+    public boolean containsPoint(float x, float y) {

+        if (getGraphWidget().getGridRect() != null) {

+            return getGraphWidget().getGridRect().contains(x, y);

+        }

+        return false;

+    }

+

+

+    /**

+     * Convenience method - wraps containsPoint(PointF).

+     *

+     * @param point

+     * @return

+     */

+    public boolean containsPoint(PointF point) {

+        return containsPoint(point.x, point.y);

+    }

+

+    public void setCursorPosition(PointF point) {

+        getGraphWidget().setCursorPosition(point);

+    }

+

+    public void setCursorPosition(float x, float y) {

+        getGraphWidget().setCursorPosition(x, y);

+    }

+

+    public Number getYVal(PointF point) {

+        return getGraphWidget().getYVal(point);

+    }

+

+    public Number getXVal(PointF point) {

+        return getGraphWidget().getXVal(point);

+    }

+

+    private boolean isXValWithinView(double xVal) {

+        return (userMinY == null || xVal >= userMinY.doubleValue()) &&

+                userMaxY == null || xVal <= userMaxY.doubleValue();

+    }

+

+    private boolean isPointVisible(Number x, Number y) {

+        // values without both an x and y val arent visible

+        if (x == null || y == null) {

+            return false;

+        }

+        return isValWithinRange(y.doubleValue(), userMinY, userMaxY) &&

+                isValWithinRange(x.doubleValue(), userMinX, userMaxX);

+    }

+

+    private boolean isValWithinRange(double val, Number min, Number max) {

+        boolean isAboveMinThreshold = min == null || val >= min.doubleValue();

+        boolean isBelowMaxThreshold = max == null || val <= max.doubleValue();

+        return isAboveMinThreshold &&

+                isBelowMaxThreshold;

+    }

+

+    public void calculateMinMaxVals() {

+        prevMinX = calculatedMinX;

+        prevMaxX = calculatedMaxX;

+        prevMinY = calculatedMinY;

+        prevMaxY = calculatedMaxY;

+

+        calculatedMinX = userMinX;

+        calculatedMaxX = userMaxX;

+        calculatedMinY = userMinY;

+        calculatedMaxY = userMaxY;

+

+        // next we go through each series to update our min/max values:

+        for (final XYSeries series : getSeriesSet()) {

+            // step through each point in each series:

+            for (int i = 0; i < series.size(); i++) {

+                Number thisX = series.getX(i);

+                Number thisY = series.getY(i);

+                if (isPointVisible(thisX, thisY)) {

+                    // only calculate if a static value has not been set:

+                    if (userMinX == null) {

+                        if (thisX != null && (calculatedMinX == null ||

+                                thisX.doubleValue() < calculatedMinX.doubleValue())) {

+                            calculatedMinX = thisX;

+                        }

+                    }

+

+                    if (userMaxX == null) {

+                        if (thisX != null && (calculatedMaxX == null ||

+                                thisX.doubleValue() > calculatedMaxX.doubleValue())) {

+                            calculatedMaxX = thisX;

+                        }

+                    }

+

+                    if (userMinY == null) {

+                        if (thisY != null && (calculatedMinY == null ||

+                                thisY.doubleValue() < calculatedMinY.doubleValue())) {

+                            calculatedMinY = thisY;

+                        }

+                    }

+

+                    if (userMaxY == null) {

+                        if (thisY != null && (calculatedMaxY == null || thisY.doubleValue() > calculatedMaxY.doubleValue())) {

+                            calculatedMaxY = thisY;

+                        }

+                    }

+                }

+            }

+        }

+

+        // at this point we now know what points are going to be visible on our

+        // plot, but we still need to make corrections based on modes being used:

+        // (grow, shrink etc.)

+        switch (domainFramingModel) {

+            case ORIGIN:

+                updateDomainMinMaxForOriginModel();

+                break;

+            case EDGE:

+                updateDomainMinMaxForEdgeModel();

+                calculatedMinX = ApplyUserMinMax(calculatedMinX, domainLeftMin,

+                        domainLeftMax);

+                calculatedMaxX = ApplyUserMinMax(calculatedMaxX,

+                        domainRightMin, domainRightMax);

+                break;

+            default:

+                throw new UnsupportedOperationException(

+                        "Domain Framing Model not yet supported: " + domainFramingModel);

+        }

+

+        switch (rangeFramingModel) {

+            case ORIGIN:

+                updateRangeMinMaxForOriginModel();

+                break;

+            case EDGE:

+            	if (getSeriesSet().size() > 0) {

+	                updateRangeMinMaxForEdgeModel();

+	                calculatedMinY = ApplyUserMinMax(calculatedMinY,

+	                        rangeBottomMin, rangeBottomMax);

+	                calculatedMaxY = ApplyUserMinMax(calculatedMaxY, rangeTopMin,

+	                        rangeTopMax);

+            	}

+                break;

+            default:

+                throw new UnsupportedOperationException(

+                        "Range Framing Model not yet supported: " + domainFramingModel);

+        }

+

+        calculatedDomainOrigin = userDomainOrigin != null ? userDomainOrigin : getCalculatedMinX();

+        calculatedRangeOrigin = this.userRangeOrigin != null ? userRangeOrigin : getCalculatedMinY();

+    }

+

+    /**

+     * Should ONLY be called from updateMinMax.

+     * Results are undefined otherwise.

+     */

+    private void updateDomainMinMaxForEdgeModel() {

+        switch (domainUpperBoundaryMode) {

+            case FIXED:

+                break;

+            case AUTO:

+                break;

+            case GROW:

+                if (!(prevMaxX == null || (calculatedMaxX.doubleValue() > prevMaxX.doubleValue()))) {

+                    calculatedMaxX = prevMaxX;

+                }

+                break;

+            case SHRINNK:

+                if (!(prevMaxX == null || calculatedMaxX.doubleValue() < prevMaxX.doubleValue())) {

+                    calculatedMaxX = prevMaxX;

+                }

+                break;

+            default:

+                throw new UnsupportedOperationException(

+                        "DomainUpperBoundaryMode not yet implemented: " + domainUpperBoundaryMode);

+        }

+

+        switch (domainLowerBoundaryMode) {

+            case FIXED:

+                break;

+            case AUTO:

+                break;

+            case GROW:

+                if (!(prevMinX == null || calculatedMinX.doubleValue() < prevMinX.doubleValue())) {

+                    calculatedMinX = prevMinX;

+                }

+                break;

+            case SHRINNK:

+                if (!(prevMinX == null || calculatedMinX.doubleValue() > prevMinX.doubleValue())) {

+                    calculatedMinX = prevMinX;

+                }

+                break;

+            default:

+                throw new UnsupportedOperationException(

+                        "DomainLowerBoundaryMode not supported: " + domainLowerBoundaryMode);

+        }

+    }

+

+    public void updateRangeMinMaxForEdgeModel() {

+        switch (rangeUpperBoundaryMode) {

+            case FIXED:

+                break;

+            case AUTO:

+                break;

+            case GROW:

+                if (!(prevMaxY == null || calculatedMaxY.doubleValue() > prevMaxY.doubleValue())) {

+                    calculatedMaxY = prevMaxY;

+                }

+                break;

+            case SHRINNK:

+                if (!(prevMaxY == null || calculatedMaxY.doubleValue() < prevMaxY.doubleValue())) {

+                    calculatedMaxY = prevMaxY;

+                }

+                break;

+            default:

+                throw new UnsupportedOperationException(

+                        "RangeUpperBoundaryMode not supported: " + rangeUpperBoundaryMode);

+        }

+

+        switch (rangeLowerBoundaryMode) {

+            case FIXED:

+                break;

+            case AUTO:

+                break;

+            case GROW:

+                if (!(prevMinY == null || calculatedMinY.doubleValue() < prevMinY.doubleValue())) {

+                    calculatedMinY = prevMinY;

+                }

+                break;

+            case SHRINNK:

+                if (!(prevMinY == null || calculatedMinY.doubleValue() > prevMinY.doubleValue())) {

+                    calculatedMinY = prevMinY;

+                }

+                break;

+            default:

+                throw new UnsupportedOperationException(

+                        "RangeLowerBoundaryMode not supported: " + rangeLowerBoundaryMode);

+        }

+    }

+

+    /**

+     * Apply user supplied min and max to the calculated boundary value.

+     *

+     * @param value

+     * @param min

+     * @param max

+     */

+    private Number ApplyUserMinMax(Number value, Number min, Number max) {

+        value = (((min == null) || (value.doubleValue() > min.doubleValue()))

+                ? value

+                : min);

+        value = (((max == null) || (value.doubleValue() < max.doubleValue()))

+                ? value

+                : max);

+        return value;

+    }

+

+    /**

+     * Centers the domain axis on origin.

+     *

+     * @param origin

+     */

+    public void centerOnDomainOrigin(Number origin) {

+        centerOnDomainOrigin(origin, null, BoundaryMode.AUTO);

+    }

+

+    /**

+     * Centers the domain on origin, calculating the upper and lower boundaries of the axis

+     * using mode and extent.

+     *

+     * @param origin

+     * @param extent

+     * @param mode

+     */

+    public void centerOnDomainOrigin(Number origin, Number extent, BoundaryMode mode) {

+        if (origin == null) {

+            throw new NullPointerException("Origin param cannot be null.");

+        }

+        domainFramingModel = XYFramingModel.ORIGIN;

+        setUserDomainOrigin(origin);

+        domainOriginExtent = extent;

+        domainOriginBoundaryMode = mode;

+

+        if (domainOriginBoundaryMode == BoundaryMode.FIXED) {

+            double domO = userDomainOrigin.doubleValue();

+            double domE = domainOriginExtent.doubleValue();

+            userMaxX = domO + domE;

+            userMinX = domO - domE;

+        } else {

+            userMaxX = null;

+            userMinX = null;

+        }

+    }

+

+    /**

+     * Centers the range axis on origin.

+     *

+     * @param origin

+     */

+    public void centerOnRangeOrigin(Number origin) {

+        centerOnRangeOrigin(origin, null, BoundaryMode.AUTO);

+    }

+

+    /**

+     * Centers the domain on origin, calculating the upper and lower boundaries of the axis

+     * using mode and extent.

+     *

+     * @param origin

+     * @param extent

+     * @param mode

+     */

+    @SuppressWarnings("SameParameterValue")

+    public void centerOnRangeOrigin(Number origin, Number extent, BoundaryMode mode) {

+        if (origin == null) {

+            throw new NullPointerException("Origin param cannot be null.");

+        }

+        rangeFramingModel = XYFramingModel.ORIGIN;

+        setUserRangeOrigin(origin);

+        rangeOriginExtent = extent;

+        rangeOriginBoundaryMode = mode;

+

+        if (rangeOriginBoundaryMode == BoundaryMode.FIXED) {

+            double raO = userRangeOrigin.doubleValue();

+            double raE = rangeOriginExtent.doubleValue();

+            userMaxY = raO + raE;

+            userMinY = raO - raE;

+        } else {

+            userMaxY = null;

+            userMinY = null;

+        }

+    }

+

+    /**

+     * Returns the distance between x and y.

+     * Result is never a negative number.

+     *

+     * @param x

+     * @param y

+     * @return

+     */

+    private double distance(double x, double y) {

+        if (x > y) {

+            return x - y;

+        } else {

+            return y - x;

+        }

+    }

+

+    public void updateDomainMinMaxForOriginModel() {

+        double origin = userDomainOrigin.doubleValue();

+        double maxXDelta = distance(calculatedMaxX.doubleValue(), origin);

+        double minXDelta = distance(calculatedMinX.doubleValue(), origin);

+        double delta = maxXDelta > minXDelta ? maxXDelta : minXDelta;

+        double dlb = origin - delta;

+        double dub = origin + delta;

+        switch (domainOriginBoundaryMode) {

+            case AUTO:

+                calculatedMinX = dlb;

+                calculatedMaxX = dub;

+

+                break;

+            // if fixed, then the value already exists within "user" vals.

+            case FIXED:

+                break;

+            case GROW: {

+

+                if (prevMinX == null || dlb < prevMinX.doubleValue()) {

+                    calculatedMinX = dlb;

+                } else {

+                    calculatedMinX = prevMinX;

+                }

+

+                if (prevMaxX == null || dub > prevMaxX.doubleValue()) {

+                    calculatedMaxX = dub;

+                } else {

+                    calculatedMaxX = prevMaxX;

+                }

+            }

+            break;

+            case SHRINNK:

+                if (prevMinX == null || dlb > prevMinX.doubleValue()) {

+                    calculatedMinX = dlb;

+                } else {

+                    calculatedMinX = prevMinX;

+                }

+

+                if (prevMaxX == null || dub < prevMaxX.doubleValue()) {

+                    calculatedMaxX = dub;

+                } else {

+                    calculatedMaxX = prevMaxX;

+                }

+                break;

+            default:

+                throw new UnsupportedOperationException("Domain Origin Boundary Mode not yet supported: " + domainOriginBoundaryMode);

+        }

+    }

+

+    public void updateRangeMinMaxForOriginModel() {

+        switch (rangeOriginBoundaryMode) {

+            case AUTO:

+                double origin = userRangeOrigin.doubleValue();

+                double maxYDelta = distance(calculatedMaxY.doubleValue(), origin);

+                double minYDelta = distance(calculatedMinY.doubleValue(), origin);

+                if (maxYDelta > minYDelta) {

+                    calculatedMinY = origin - maxYDelta;

+                    calculatedMaxY = origin + maxYDelta;

+                } else {

+                    calculatedMinY = origin - minYDelta;

+                    calculatedMaxY = origin + minYDelta;

+                }

+                break;

+            case FIXED:

+            case GROW:

+            case SHRINNK:

+            default:

+                throw new UnsupportedOperationException(

+                        "Range Origin Boundary Mode not yet supported: " + rangeOriginBoundaryMode);

+        }

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.getTicksPerRangeLabel().

+     * Equivalent to getGraphWidget().getTicksPerRangeLabel().

+     *

+     * @return

+     */

+    public int getTicksPerRangeLabel() {

+        return graphWidget.getTicksPerRangeLabel();

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.setTicksPerRangeLabel().

+     * Equivalent to getGraphWidget().setTicksPerRangeLabel().

+     *

+     * @param ticksPerRangeLabel

+     */

+    public void setTicksPerRangeLabel(int ticksPerRangeLabel) {

+        graphWidget.setTicksPerRangeLabel(ticksPerRangeLabel);

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.getTicksPerDomainLabel().

+     * Equivalent to getGraphWidget().getTicksPerDomainLabel().

+     *

+     * @return

+     */

+    public int getTicksPerDomainLabel() {

+        return graphWidget.getTicksPerDomainLabel();

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.setTicksPerDomainLabel().

+     * Equivalent to getGraphWidget().setTicksPerDomainLabel().

+     *

+     * @param ticksPerDomainLabel

+     */

+    public void setTicksPerDomainLabel(int ticksPerDomainLabel) {

+        graphWidget.setTicksPerDomainLabel(ticksPerDomainLabel);

+    }

+

+    public XYStepMode getDomainStepMode() {

+        return domainStepMode;

+    }

+

+    public void setDomainStepMode(XYStepMode domainStepMode) {

+        this.domainStepMode = domainStepMode;

+    }

+

+    public double getDomainStepValue() {

+        return domainStepValue;

+    }

+

+    public void setDomainStepValue(double domainStepValue) {

+        this.domainStepValue = domainStepValue;

+    }

+

+    public void setDomainStep(XYStepMode mode, double value) {

+        setDomainStepMode(mode);

+        setDomainStepValue(value);

+    }

+

+    public XYStepMode getRangeStepMode() {

+        return rangeStepMode;

+    }

+

+    public void setRangeStepMode(XYStepMode rangeStepMode) {

+        this.rangeStepMode = rangeStepMode;

+    }

+

+    public double getRangeStepValue() {

+        return rangeStepValue;

+    }

+

+    public void setRangeStepValue(double rangeStepValue) {

+        this.rangeStepValue = rangeStepValue;

+    }

+

+    public void setRangeStep(XYStepMode mode, double value) {

+        setRangeStepMode(mode);

+        setRangeStepValue(value);

+    }

+

+    public String getDomainLabel() {

+        return getDomainLabelWidget().getText();

+    }

+

+    public void setDomainLabel(String domainLabel) {

+        getDomainLabelWidget().setText(domainLabel);

+    }

+

+    public String getRangeLabel() {

+        return getRangeLabelWidget().getText();

+    }

+

+    public void setRangeLabel(String rangeLabel) {

+        getRangeLabelWidget().setText(rangeLabel);

+    }

+

+    public XYLegendWidget getLegendWidget() {

+        return legendWidget;

+    }

+

+    public void setLegendWidget(XYLegendWidget legendWidget) {

+        this.legendWidget = legendWidget;

+    }

+

+    public XYGraphWidget getGraphWidget() {

+        return graphWidget;

+    }

+

+    public void setGraphWidget(XYGraphWidget graphWidget) {

+        this.graphWidget = graphWidget;

+    }

+

+    public TextLabelWidget getDomainLabelWidget() {

+        return domainLabelWidget;

+    }

+

+    public void setDomainLabelWidget(TextLabelWidget domainLabelWidget) {

+        this.domainLabelWidget = domainLabelWidget;

+    }

+

+    public TextLabelWidget getRangeLabelWidget() {

+        return rangeLabelWidget;

+    }

+

+    public void setRangeLabelWidget(TextLabelWidget rangeLabelWidget) {

+        this.rangeLabelWidget = rangeLabelWidget;

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.getRangeValueFormat().

+     *

+     * @return

+     */

+    public Format getRangeValueFormat() {

+        return graphWidget.getRangeValueFormat();

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.setRangeValueFormat().

+     *

+     * @param rangeValueFormat

+     */

+    public void setRangeValueFormat(Format rangeValueFormat) {

+        graphWidget.setRangeValueFormat(rangeValueFormat);

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.getDomainValueFormat().

+     *

+     * @return

+     */

+    public Format getDomainValueFormat() {

+        return graphWidget.getDomainValueFormat();

+    }

+

+    /**

+     * Convenience method - wraps XYGraphWidget.setDomainValueFormat().

+     *

+     * @param domainValueFormat

+     */

+    public void setDomainValueFormat(Format domainValueFormat) {

+        graphWidget.setDomainValueFormat(domainValueFormat);

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param lowerBoundary

+     * @param upperBoundary

+     * @param mode

+     */

+    public synchronized void setDomainBoundaries(Number lowerBoundary, Number upperBoundary, BoundaryMode mode) {

+        setDomainBoundaries(lowerBoundary, mode, upperBoundary, mode);

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param lowerBoundary

+     * @param lowerBoundaryMode

+     * @param upperBoundary

+     * @param upperBoundaryMode

+     */

+    public synchronized void setDomainBoundaries(Number lowerBoundary, BoundaryMode lowerBoundaryMode,

+                                                 Number upperBoundary, BoundaryMode upperBoundaryMode) {

+        setDomainLowerBoundary(lowerBoundary, lowerBoundaryMode);

+        setDomainUpperBoundary(upperBoundary, upperBoundaryMode);

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param lowerBoundary

+     * @param upperBoundary

+     * @param mode

+     */

+    public synchronized void setRangeBoundaries(Number lowerBoundary, Number upperBoundary, BoundaryMode mode) {

+        setRangeBoundaries(lowerBoundary, mode, upperBoundary, mode);

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param lowerBoundary

+     * @param lowerBoundaryMode

+     * @param upperBoundary

+     * @param upperBoundaryMode

+     */

+    public synchronized void setRangeBoundaries(Number lowerBoundary, BoundaryMode lowerBoundaryMode,

+                                                Number upperBoundary, BoundaryMode upperBoundaryMode) {

+        setRangeLowerBoundary(lowerBoundary, lowerBoundaryMode);

+        setRangeUpperBoundary(upperBoundary, upperBoundaryMode);

+    }

+

+    protected synchronized void setDomainUpperBoundaryMode(BoundaryMode mode) {

+        this.domainUpperBoundaryMode = mode;

+    }

+

+    protected synchronized void setUserMaxX(Number boundary) {

+        // Ifor 12/10/2011

+        // you want null for auto grow and shrink

+        //if(boundary == null) {

+        //    throw new NullPointerException("Boundary value cannot be null.");

+        //}

+        this.userMaxX = boundary;

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param boundary

+     * @param mode

+     */

+    public synchronized void setDomainUpperBoundary(Number boundary, BoundaryMode mode) {

+        setUserMaxX((mode == BoundaryMode.FIXED) ? boundary : null);

+        setDomainUpperBoundaryMode(mode);

+        setDomainFramingModel(XYFramingModel.EDGE);

+    }

+

+    protected synchronized void setDomainLowerBoundaryMode(BoundaryMode mode) {

+        this.domainLowerBoundaryMode = mode;

+    }

+

+    protected synchronized void setUserMinX(Number boundary) {

+        // Ifor 12/10/2011

+        // you want null for auto grow and shrink

+        //if(boundary == null) {

+        //    throw new NullPointerException("Boundary value cannot be null.");

+        //}

+        this.userMinX = boundary;

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param boundary

+     * @param mode

+     */

+    public synchronized void setDomainLowerBoundary(Number boundary, BoundaryMode mode) {

+        setUserMinX((mode == BoundaryMode.FIXED) ? boundary : null);

+        setDomainLowerBoundaryMode(mode);

+        setDomainFramingModel(XYFramingModel.EDGE);

+        //updateMinMaxVals();

+    }

+

+    protected synchronized void setRangeUpperBoundaryMode(BoundaryMode mode) {

+        this.rangeUpperBoundaryMode = mode;

+    }

+

+    protected synchronized void setUserMaxY(Number boundary) {

+        // Ifor 12/10/2011

+        // you want null for auto grow and shrink

+        //if(boundary == null) {

+        //    throw new NullPointerException("Boundary value cannot be null.");

+        //}

+        this.userMaxY = boundary;

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param boundary

+     * @param mode

+     */

+    public synchronized void setRangeUpperBoundary(Number boundary, BoundaryMode mode) {

+        setUserMaxY((mode == BoundaryMode.FIXED) ? boundary : null);

+        setRangeUpperBoundaryMode(mode);

+        setRangeFramingModel(XYFramingModel.EDGE);

+    }

+

+    protected synchronized void setRangeLowerBoundaryMode(BoundaryMode mode) {

+        this.rangeLowerBoundaryMode = mode;

+    }

+

+    protected synchronized void setUserMinY(Number boundary) {

+        // Ifor 12/10/2011

+        // you want null for auto grow and shrink

+        //if(boundary == null) {

+        //    throw new NullPointerException("Boundary value cannot be null.");

+        //}

+        this.userMinY = boundary;

+    }

+

+    /**

+     * Setup the boundary mode, boundary values only applicable in FIXED mode.

+     *

+     * @param boundary

+     * @param mode

+     */

+    public synchronized void setRangeLowerBoundary(Number boundary, BoundaryMode mode) {

+        setUserMinY((mode == BoundaryMode.FIXED) ? boundary : null);

+        setRangeLowerBoundaryMode(mode);

+        setRangeFramingModel(XYFramingModel.EDGE);

+    }

+

+    private Number getUserMinX() {

+        return userMinX;

+    }

+

+    private Number getUserMaxX() {

+        return userMaxX;

+    }

+

+    private Number getUserMinY() {

+        return userMinY;

+    }

+

+    private Number getUserMaxY() {

+        return userMaxY;

+    }

+

+    public Number getDomainOrigin() {

+        return calculatedDomainOrigin;

+    }

+

+    public Number getRangeOrigin() {

+        return calculatedRangeOrigin;

+    }

+

+    protected BoundaryMode getDomainUpperBoundaryMode() {

+        return domainUpperBoundaryMode;

+    }

+

+    protected BoundaryMode getDomainLowerBoundaryMode() {

+        return domainLowerBoundaryMode;

+    }

+

+    protected BoundaryMode getRangeUpperBoundaryMode() {

+        return rangeUpperBoundaryMode;

+    }

+

+    protected BoundaryMode getRangeLowerBoundaryMode() {

+        return rangeLowerBoundaryMode;

+    }

+

+    public synchronized void setUserDomainOrigin(Number origin) {

+        if (origin == null) {

+            throw new NullPointerException("Origin value cannot be null.");

+        }

+        this.userDomainOrigin = origin;

+    }

+

+    public synchronized void setUserRangeOrigin(Number origin) {

+        if (origin == null) {

+            throw new NullPointerException("Origin value cannot be null.");

+        }

+        this.userRangeOrigin = origin;

+    }

+

+    public XYFramingModel getDomainFramingModel() {

+        return domainFramingModel;

+    }

+

+    @SuppressWarnings("SameParameterValue")

+    protected void setDomainFramingModel(XYFramingModel domainFramingModel) {

+        this.domainFramingModel = domainFramingModel;

+    }

+

+    public XYFramingModel getRangeFramingModel() {

+

+        return rangeFramingModel;

+    }

+

+    @SuppressWarnings("SameParameterValue")

+    protected void setRangeFramingModel(XYFramingModel rangeFramingModel) {

+        this.rangeFramingModel = rangeFramingModel;

+    }

+

+    /**

+     * CalculatedMinX value after the the framing model has been applied.

+     *

+     * @return

+     */

+    public Number getCalculatedMinX() {

+        return calculatedMinX != null ? calculatedMinX : getDefaultBounds().getMinX();

+    }

+

+    /**

+     * CalculatedMaxX value after the the framing model has been applied.

+     *

+     * @return

+     */

+    public Number getCalculatedMaxX() {

+        return calculatedMaxX != null ? calculatedMaxX : getDefaultBounds().getMaxX();

+    }

+

+    /**

+     * CalculatedMinY value after the the framing model has been applied.

+     *

+     * @return

+     */

+    public Number getCalculatedMinY() {

+        return calculatedMinY != null ? calculatedMinY : getDefaultBounds().getMinY();

+    }

+

+    /**

+     * CalculatedMaxY value after the the framing model has been applied.

+     *

+     * @return

+     */

+    public Number getCalculatedMaxY() {

+        return calculatedMaxY != null ? calculatedMaxY : getDefaultBounds().getMaxY();

+    }

+

+    public boolean isDrawDomainOriginEnabled() {

+        return drawDomainOriginEnabled;

+    }

+

+    public void setDrawDomainOriginEnabled(boolean drawDomainOriginEnabled) {

+        this.drawDomainOriginEnabled = drawDomainOriginEnabled;

+    }

+

+    public boolean isDrawRangeOriginEnabled() {

+        return drawRangeOriginEnabled;

+    }

+

+    public void setDrawRangeOriginEnabled(boolean drawRangeOriginEnabled) {

+        this.drawRangeOriginEnabled = drawRangeOriginEnabled;

+    }

+

+    /**

+     * Appends the specified marker to the end of plot's yValueMarkers list.

+     *

+     * @param marker The YValueMarker to be added.

+     * @return true if the object was successfully added, false otherwise.

+     */

+    public boolean addMarker(YValueMarker marker) {

+        if (yValueMarkers.contains(marker)) {

+            return false;

+        } else {

+            return yValueMarkers.add(marker);

+        }

+    }

+

+    /**

+     * Removes the specified marker from the plot.

+     *

+     * @param marker

+     * @return The YValueMarker removed if successfull,  null otherwise.

+     */

+    public YValueMarker removeMarker(YValueMarker marker) {

+        int markerIndex = yValueMarkers.indexOf(marker);

+        if (markerIndex == -1) {

+            return null;

+        } else {

+            return yValueMarkers.remove(markerIndex);

+        }

+    }

+

+    /**

+     * Convenience method - combines removeYMarkers() and removeXMarkers().

+     *

+     * @return

+     */

+    public int removeMarkers() {

+        int removed = removeXMarkers();

+        removed += removeYMarkers();

+        return removed;

+    }

+

+    /**

+     * Removes all YValueMarker instances from the plot.

+     *

+     * @return

+     */

+    public int removeYMarkers() {

+        int numMarkersRemoved = yValueMarkers.size();

+        yValueMarkers.clear();

+        return numMarkersRemoved;

+    }

+

+    /**

+     * Appends the specified marker to the end of plot's xValueMarkers list.

+     *

+     * @param marker The XValueMarker to be added.

+     * @return true if the object was successfully added, false otherwise.

+     */

+    public boolean addMarker(XValueMarker marker) {

+        return !xValueMarkers.contains(marker) && xValueMarkers.add(marker);

+    }

+

+    /**

+     * Removes the specified marker from the plot.

+     *

+     * @param marker

+     * @return The XValueMarker removed if successfull,  null otherwise.

+     */

+    public XValueMarker removeMarker(XValueMarker marker) {

+        int markerIndex = xValueMarkers.indexOf(marker);

+        if (markerIndex == -1) {

+            return null;

+        } else {

+            return xValueMarkers.remove(markerIndex);

+        }

+    }

+

+    /**

+     * Removes all XValueMarker instances from the plot.

+     *

+     * @return

+     */

+    public int removeXMarkers() {

+        int numMarkersRemoved = xValueMarkers.size();

+        xValueMarkers.clear();

+        return numMarkersRemoved;

+    }

+

+    protected List<YValueMarker> getYValueMarkers() {

+        return yValueMarkers;

+    }

+

+    protected List<XValueMarker> getXValueMarkers() {

+        return xValueMarkers;

+    }

+

+    public RectRegion getDefaultBounds() {

+        return defaultBounds;

+    }

+

+    public void setDefaultBounds(RectRegion defaultBounds) {

+        this.defaultBounds = defaultBounds;

+    }

+

+    /**

+     * @return the rangeTopMin

+     */

+    public Number getRangeTopMin() {

+        return rangeTopMin;

+    }

+

+    /**

+     * @param rangeTopMin the rangeTopMin to set

+     */

+    public synchronized void setRangeTopMin(Number rangeTopMin) {

+        this.rangeTopMin = rangeTopMin;

+    }

+

+    /**

+     * @return the rangeTopMax

+     */

+    public Number getRangeTopMax() {

+        return rangeTopMax;

+    }

+

+    /**

+     * @param rangeTopMax the rangeTopMax to set

+     */

+    public synchronized void setRangeTopMax(Number rangeTopMax) {

+        this.rangeTopMax = rangeTopMax;

+    }

+

+    /**

+     * @return the rangeBottomMin

+     */

+    public Number getRangeBottomMin() {

+        return rangeBottomMin;

+    }

+

+    /**

+     * @param rangeBottomMin the rangeBottomMin to set

+     */

+    public synchronized void setRangeBottomMin(Number rangeBottomMin) {

+        this.rangeBottomMin = rangeBottomMin;

+    }

+

+    /**

+     * @return the rangeBottomMax

+     */

+    public Number getRangeBottomMax() {

+        return rangeBottomMax;

+    }

+

+    /**

+     * @param rangeBottomMax the rangeBottomMax to set

+     */

+    public synchronized void setRangeBottomMax(Number rangeBottomMax) {

+        this.rangeBottomMax = rangeBottomMax;

+    }

+

+    /**

+     * @return the domainLeftMin

+     */

+    public Number getDomainLeftMin() {

+        return domainLeftMin;

+    }

+

+    /**

+     * @param domainLeftMin the domainLeftMin to set

+     */

+    public synchronized void setDomainLeftMin(Number domainLeftMin) {

+        this.domainLeftMin = domainLeftMin;

+    }

+

+    /**

+     * @return the domainLeftMax

+     */

+    public Number getDomainLeftMax() {

+        return domainLeftMax;

+    }

+

+    /**

+     * @param domainLeftMax the domainLeftMax to set

+     */

+    public synchronized void setDomainLeftMax(Number domainLeftMax) {

+        this.domainLeftMax = domainLeftMax;

+    }

+

+    /**

+     * @return the domainRightMin

+     */

+    public Number getDomainRightMin() {

+        return domainRightMin;

+    }

+

+    /**

+     * @param domainRightMin the domainRightMin to set

+     */

+    public synchronized void setDomainRightMin(Number domainRightMin) {

+        this.domainRightMin = domainRightMin;

+    }

+

+    /**

+     * @return the domainRightMax

+     */

+    public Number getDomainRightMax() {

+        return domainRightMax;

+    }

+

+    /**

+     * @param domainRightMax the domainRightMax to set

+     */

+    public synchronized void setDomainRightMax(Number domainRightMax) {

+        this.domainRightMax = domainRightMax;

+    }

+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlotZoomPan.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlotZoomPan.java
new file mode 100644
index 0000000..e1064dc
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYPlotZoomPan.java
@@ -0,0 +1,384 @@
+package com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+public class XYPlotZoomPan extends XYPlot implements OnTouchListener {
+    private static final float MIN_DIST_2_FING = 5f;
+
+    // Definition of the touch states
+    private enum State
+    {
+        NONE,
+        ONE_FINGER_DRAG,
+        TWO_FINGERS_DRAG
+    }
+
+    private State mode = State.NONE;
+    private float minXLimit = Float.MAX_VALUE;
+    private float maxXLimit = Float.MAX_VALUE;
+    private float minYLimit = Float.MAX_VALUE;
+    private float maxYLimit = Float.MAX_VALUE;
+    private float lastMinX = Float.MAX_VALUE;
+    private float lastMaxX = Float.MAX_VALUE;
+    private float lastMinY = Float.MAX_VALUE;
+    private float lastMaxY = Float.MAX_VALUE;
+    private PointF firstFingerPos;
+    private float mDistX;
+    private boolean mZoomEnabled; //default is enabled
+    private boolean mZoomVertically;
+    private boolean mZoomHorizontally;
+    private boolean mCalledBySelf;
+    private boolean mZoomEnabledInit;
+    private boolean mZoomVerticallyInit;
+    private boolean mZoomHorizontallyInit;
+
+    public XYPlotZoomPan(Context context, String title, RenderMode mode) {
+        super(context, title, mode);
+        setZoomEnabled(true); //Default is ZoomEnabled if instantiated programmatically
+    }
+
+    public XYPlotZoomPan(final Context context, final AttributeSet attrs) {
+        super(context, attrs);
+        if(mZoomEnabled || !mZoomEnabledInit) {
+            setZoomEnabled(true);
+        }
+        if(!mZoomHorizontallyInit) {
+            mZoomHorizontally = true;
+        }
+        if(!mZoomVerticallyInit) {
+            mZoomVertically = true;
+        }
+    }
+
+    public XYPlotZoomPan(final Context context, final AttributeSet attrs, final int defStyle) {
+        super(context, attrs, defStyle);
+        if(mZoomEnabled || !mZoomEnabledInit) {
+            setZoomEnabled(true);
+        }
+        if(!mZoomHorizontallyInit) {
+            mZoomHorizontally = true;
+        }
+        if(!mZoomVerticallyInit) {
+            mZoomVertically = true;
+        }
+    }
+
+    public XYPlotZoomPan(final Context context, final String title) {
+        super(context, title);
+    }
+
+    @Override
+    public void setOnTouchListener(OnTouchListener l) {
+        if(l != this) {
+            mZoomEnabled = false;
+        }
+        super.setOnTouchListener(l);
+    }
+
+    public boolean getZoomVertically() {
+        return mZoomVertically;
+    }
+
+    public void setZoomVertically(boolean zoomVertically) {
+        mZoomVertically = zoomVertically;
+        mZoomVerticallyInit = true;
+    }
+
+    public boolean getZoomHorizontally() {
+        return mZoomHorizontally;
+    }
+
+    public void setZoomHorizontally(boolean zoomHorizontally) {
+        mZoomHorizontally = zoomHorizontally;
+        mZoomHorizontallyInit = true;
+    }
+
+    public void setZoomEnabled(boolean enabled) {
+        if(enabled) {
+            setOnTouchListener(this);
+        } else {
+            setOnTouchListener(null);
+        }
+        mZoomEnabled = enabled;
+        mZoomEnabledInit = true;
+    }
+
+    public boolean getZoomEnabled() {
+        return mZoomEnabled;
+    }
+
+    private float getMinXLimit() {
+        if(minXLimit == Float.MAX_VALUE) {
+            minXLimit = getCalculatedMinX().floatValue();
+            lastMinX = minXLimit;
+        }
+        return minXLimit;
+    }
+
+    private float getMaxXLimit() {
+        if(maxXLimit == Float.MAX_VALUE) {
+            maxXLimit = getCalculatedMaxX().floatValue();
+            lastMaxX = maxXLimit;
+        }
+        return maxXLimit;
+    }
+
+    private float getMinYLimit() {
+        if(minYLimit == Float.MAX_VALUE) {
+            minYLimit = getCalculatedMinY().floatValue();
+            lastMinY = minYLimit;
+        }
+        return minYLimit;
+    }
+
+    private float getMaxYLimit() {
+        if(maxYLimit == Float.MAX_VALUE) {
+            maxYLimit = getCalculatedMaxY().floatValue();
+            lastMaxY = maxYLimit;
+        }
+        return maxYLimit;
+    }
+
+    private float getLastMinX() {
+        if(lastMinX == Float.MAX_VALUE) {
+            lastMinX = getCalculatedMinX().floatValue();
+        }
+        return lastMinX;
+    }
+
+    private float getLastMaxX() {
+        if(lastMaxX == Float.MAX_VALUE) {
+            lastMaxX = getCalculatedMaxX().floatValue();
+        }
+        return lastMaxX;
+    }
+
+    private float getLastMinY() {
+        if(lastMinY == Float.MAX_VALUE) {
+            lastMinY = getCalculatedMinY().floatValue();
+        }
+        return lastMinY;
+    }
+
+    private float getLastMaxY() {
+        if(lastMaxY == Float.MAX_VALUE) {
+            lastMaxY = getCalculatedMaxY().floatValue();
+        }
+        return lastMaxY;
+    }
+
+    @Override
+    public synchronized void setDomainBoundaries(final Number lowerBoundary, final BoundaryMode lowerBoundaryMode, final Number upperBoundary, final BoundaryMode upperBoundaryMode) {
+        super.setDomainBoundaries(lowerBoundary, lowerBoundaryMode, upperBoundary, upperBoundaryMode);
+        if(mCalledBySelf) {
+            mCalledBySelf = false;
+        } else {
+            minXLimit = lowerBoundaryMode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinX().floatValue();
+            maxXLimit = upperBoundaryMode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxX().floatValue();
+            lastMinX = minXLimit;
+            lastMaxX = maxXLimit;
+        }
+    }
+
+    @Override
+    public synchronized void setRangeBoundaries(final Number lowerBoundary, final BoundaryMode lowerBoundaryMode, final Number upperBoundary, final BoundaryMode upperBoundaryMode) {
+        super.setRangeBoundaries(lowerBoundary, lowerBoundaryMode, upperBoundary, upperBoundaryMode);
+        if(mCalledBySelf) {
+            mCalledBySelf = false;
+        } else {
+            minYLimit = lowerBoundaryMode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinY().floatValue();
+            maxYLimit = upperBoundaryMode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxY().floatValue();
+            lastMinY = minYLimit;
+            lastMaxY = maxYLimit;
+        }
+    }
+
+    @Override
+    public synchronized void setDomainBoundaries(final Number lowerBoundary, final Number upperBoundary, final BoundaryMode mode) {
+        super.setDomainBoundaries(lowerBoundary, upperBoundary, mode);
+        if(mCalledBySelf) {
+            mCalledBySelf = false;
+        } else {
+            minXLimit = mode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinX().floatValue();
+            maxXLimit = mode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxX().floatValue();
+            lastMinX = minXLimit;
+            lastMaxX = maxXLimit;
+        }
+    }
+
+    @Override
+    public synchronized void setRangeBoundaries(final Number lowerBoundary, final Number upperBoundary, final BoundaryMode mode) {
+        super.setRangeBoundaries(lowerBoundary, upperBoundary, mode);
+        if(mCalledBySelf) {
+            mCalledBySelf = false;
+        } else {
+            minYLimit = mode == BoundaryMode.FIXED ? lowerBoundary.floatValue() : getCalculatedMinY().floatValue();
+            maxYLimit = mode == BoundaryMode.FIXED ? upperBoundary.floatValue() : getCalculatedMaxY().floatValue();
+            lastMinY = minYLimit;
+            lastMaxY = maxYLimit;
+        }
+    }
+
+    @Override
+    public boolean onTouch(final View view, final MotionEvent event) {
+        switch (event.getAction() & MotionEvent.ACTION_MASK)
+        {
+            case MotionEvent.ACTION_DOWN: // start gesture
+                firstFingerPos = new PointF(event.getX(), event.getY());
+                mode = State.ONE_FINGER_DRAG;
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN: // second finger
+            {
+                mDistX = getXDistance(event);
+                // the distance check is done to avoid false alarms
+                if(mDistX > MIN_DIST_2_FING || mDistX < -MIN_DIST_2_FING) {
+                    mode = State.TWO_FINGERS_DRAG;
+                }
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP: // end zoom
+                mode = State.NONE;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if(mode == State.ONE_FINGER_DRAG) {
+                    pan(event);
+                } else if(mode == State.TWO_FINGERS_DRAG) {
+                    zoom(event);
+                }
+                break;
+        }
+        return true;
+    }
+
+    private float getXDistance(final MotionEvent event) {
+        return event.getX(0) - event.getX(1);
+    }
+
+    private void pan(final MotionEvent motionEvent) {
+        final PointF oldFirstFinger = firstFingerPos; //save old position of finger
+        firstFingerPos = new PointF(motionEvent.getX(), motionEvent.getY()); //update finger position
+        PointF newX = new PointF();
+        if(mZoomHorizontally) {
+            calculatePan(oldFirstFinger, newX, true);
+            mCalledBySelf = true;
+            super.setDomainBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+            lastMinX = newX.x;
+            lastMaxX = newX.y;
+        }
+        if(mZoomVertically) {
+            calculatePan(oldFirstFinger, newX, false);
+            mCalledBySelf = true;
+            super.setRangeBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+            lastMinY = newX.x;
+            lastMaxY = newX.y;
+        }
+        redraw();
+    }
+
+    private void calculatePan(final PointF oldFirstFinger, PointF newX, final boolean horizontal) {
+        final float offset;
+        // multiply the absolute finger movement for a factor.
+        // the factor is dependent on the calculated min and max
+        if(horizontal) {
+            newX.x = getLastMinX();
+            newX.y = getLastMaxX();
+            offset = (oldFirstFinger.x - firstFingerPos.x) * ((newX.y - newX.x) / getWidth());
+        } else {
+            newX.x = getLastMinY();
+            newX.y = getLastMaxY();
+            offset = -(oldFirstFinger.y - firstFingerPos.y) * ((newX.y - newX.x) / getHeight());
+        }
+        // move the calculated offset
+        newX.x = newX.x + offset;
+        newX.y = newX.y + offset;
+        //get the distance between max and min
+        final float diff = newX.y - newX.x;
+        //check if we reached the limit of panning
+        if(horizontal) {
+            if(newX.x < getMinXLimit()) {
+                newX.x = getMinXLimit();
+                newX.y = newX.x + diff;
+            }
+            if(newX.y > getMaxXLimit()) {
+                newX.y = getMaxXLimit();
+                newX.x = newX.y - diff;
+            }
+        } else {
+            if(newX.x < getMinYLimit()) {
+                newX.x = getMinYLimit();
+                newX.y = newX.x + diff;
+            }
+            if(newX.y > getMaxYLimit()) {
+                newX.y = getMaxYLimit();
+                newX.x = newX.y - diff;
+            }
+        }
+    }
+
+    private void zoom(final MotionEvent motionEvent) {
+        final float oldDist = mDistX;
+        final float newDist = getXDistance(motionEvent);
+        // sign change! Fingers have crossed ;-)
+        if(oldDist > 0 && newDist < 0 || oldDist < 0 && newDist > 0) {
+            return;
+        }
+        mDistX = newDist;
+        float scale = (oldDist / mDistX);
+        // sanity check
+        if(Float.isInfinite(scale) || Float.isNaN(scale) || scale > -0.001 && scale < 0.001) {
+            return;
+        }
+        PointF newX = new PointF();
+        if(mZoomHorizontally) {
+            calculateZoom(scale, newX, true);
+            mCalledBySelf = true;
+            super.setDomainBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+            lastMinX = newX.x;
+            lastMaxX = newX.y;
+        }
+        if(mZoomVertically) {
+            calculateZoom(scale, newX, false);
+            mCalledBySelf = true;
+            super.setRangeBoundaries(newX.x, newX.y, BoundaryMode.FIXED);
+            lastMinY = newX.x;
+            lastMaxY = newX.y;
+        }
+        redraw();
+    }
+
+    private void calculateZoom(float scale, PointF newX, final boolean horizontal) {
+        final float calcMax;
+        final float span;
+        if(horizontal) {
+            calcMax = getLastMaxX();
+            span = calcMax - getLastMinX();
+        } else {
+            calcMax = getLastMaxY();
+            span = calcMax - getLastMinY();
+        }
+        final float midPoint = calcMax - (span / 2.0f);
+        final float offset = span * scale / 2.0f;
+        newX.x = midPoint - offset;
+        newX.y = midPoint + offset;
+        if(horizontal) {
+            if(newX.x < getMinXLimit()) {
+                newX.x = getMinXLimit();
+            }
+            if(newX.y > getMaxXLimit()) {
+                newX.y = getMaxXLimit();
+            }
+        } else {
+            if(newX.x < getMinYLimit()) {
+                newX.x = getMinYLimit();
+            }
+            if(newX.y > getMaxYLimit()) {
+                newX.y = getMaxYLimit();
+            }
+        }
+    }
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYRegionFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYRegionFormatter.java
new file mode 100644
index 0000000..9566d5a
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYRegionFormatter.java
@@ -0,0 +1,73 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import android.graphics.Paint;

+import com.androidplot.util.Configurator;

+

+/**

+ * Base class of all XYRegionFormatters.

+ */

+public class XYRegionFormatter {

+

+    //private int color;

+    private Paint paint = new Paint();

+

+    {

+        paint.setStyle(Paint.Style.FILL);

+        paint.setAntiAlias(true);

+    }

+

+    /**

+     * Provided as a convenience to users; allows instantiation and xml configuration

+     * to take place in a single line

+     *

+     * @param ctx

+     * @param xmlCfgId Id of the xml config file within /res/xml

+     */

+    public XYRegionFormatter(Context ctx, int xmlCfgId) {

+        // prevent configuration of classes derived from this one:

+        if (getClass().equals(XYRegionFormatter.class)) {

+            Configurator.configure(ctx, this, xmlCfgId);

+        }

+    }

+

+    public XYRegionFormatter(int color) {

+        //paint = new Paint();

+        paint.setColor(color);

+        //paint.setStyle(Paint.Style.FILL);

+        //paint.setAntiAlias(true);

+        //this.color = color;

+    }

+

+    public int getColor() {

+        return paint.getColor();

+    }

+

+    public void setColor(int color) {

+        paint.setColor(color);

+    }

+

+    /**

+     * Advanced users can use this method to access the Paint instance to add transparency etc.

+     * @return

+     */

+    public Paint getPaint() {

+        return paint;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeries.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeries.java
new file mode 100644
index 0000000..22b7c19
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeries.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.xy;
+
+import android.util.Pair;
+import com.androidplot.Series;
+
+/**
+ * Represents a two dimensional series of data represented as xy values.
+ */
+public interface XYSeries extends Series<Pair<Number, Number>> {
+
+    /**
+     * @return Number of elements in this Series.
+     */
+    public int size();
+
+    /**
+     * Returns the x-value for an index within a series.
+     *
+     * @param index  the index index (in the range <code>0</code> to
+     *     <code>size()-1</code>).
+     *
+     * @return The x-value.
+     */
+    public Number getX(int index);
+
+    /**
+     * Returns the y-value for an index within a series.
+     *
+     * @param index  the index index (in the range <code>0</code> to
+     *     <code>size()-1</code>).
+     *
+     * @return The y-value.
+     */
+    public Number getY(int index);
+}
diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesFormatter.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesFormatter.java
new file mode 100644
index 0000000..465f5bd
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesFormatter.java
@@ -0,0 +1,65 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import com.androidplot.ui.SeriesRenderer;

+import com.androidplot.ui.Formatter;

+import com.androidplot.util.ZHash;

+import com.androidplot.util.ZIndexable;

+

+public abstract class XYSeriesFormatter<XYRegionFormatterType extends XYRegionFormatter> extends Formatter<XYPlot> {

+    ZHash<RectRegion, XYRegionFormatterType>  regions;

+

+    {

+        regions = new ZHash<RectRegion, XYRegionFormatterType>();

+    }

+

+    public void addRegion(RectRegion region, XYRegionFormatterType regionFormatter) {

+        regions.addToBottom(region, regionFormatter);

+    }

+

+    public void removeRegion(RectRegion region) {

+        regions.remove(region);

+    }

+

+    /**

+     * Can be used to access z-index manipulation methods of ZIndexable.

+     * @return

+     */

+    public ZIndexable<RectRegion> getRegions() {

+        return regions;

+    }

+

+    /**

+     * @param region

+     * @return

+     */

+    public XYRegionFormatterType getRegionFormatter(RectRegion region) {

+        return regions.get(region);

+    }

+

+    /**

+     * Not completely sure why this is necessary, but if it's not here then

+     * subclasses are forced to take a Plot instead of an XYPlot as a parameter,

+     * which in turn breaks the pattern.

+     * @param plot

+     * @return

+     */

+    @Override

+    public abstract SeriesRenderer getRendererInstance(XYPlot plot);

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesRenderer.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesRenderer.java
new file mode 100644
index 0000000..14f1ca9
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYSeriesRenderer.java
@@ -0,0 +1,52 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Canvas;

+import android.graphics.RectF;

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.ui.SeriesAndFormatterList;

+import com.androidplot.ui.SeriesRenderer;

+import com.androidplot.util.ZIndexable;

+

+import java.util.Hashtable;

+

+public abstract class XYSeriesRenderer<XYFormatterType extends XYSeriesFormatter>

+        extends SeriesRenderer<XYPlot, XYSeries, XYFormatterType> {

+

+    public XYSeriesRenderer(XYPlot plot) {

+        super(plot);

+    }

+

+    public Hashtable<XYRegionFormatter, String> getUniqueRegionFormatters() {

+

+        Hashtable<XYRegionFormatter, String> found = new Hashtable<XYRegionFormatter, String>();

+        SeriesAndFormatterList<XYSeries, XYFormatterType> sfl = getSeriesAndFormatterList();

+

+        if (sfl != null) {

+            for (XYFormatterType xyf : sfl.getFormatterList()) {

+                ZIndexable<RectRegion> regionIndexer = xyf.getRegions();

+                for (RectRegion region : regionIndexer.elements()) {

+                    XYRegionFormatter f = xyf.getRegionFormatter(region);

+                    found.put(f, region.getLabel());

+                }

+            }

+        }

+

+        return found;

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStep.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStep.java
new file mode 100644
index 0000000..a4dacd5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStep.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+/**

+ * An immutable object generated by XYStepCalculator representing

+ * a stepping model to be used by an XYPlot.

+ */

+public class XYStep {

+

+    private final float stepCount;

+    private final float stepPix;

+    private final double stepVal;

+

+    //public XYStep() {}

+

+    public XYStep(float stepCount, float stepPix, double stepVal) {

+        this.stepCount = stepCount;

+        this.stepPix = stepPix;

+        this.stepVal = stepVal;

+    }

+

+    public double getStepCount() {

+        return stepCount;

+    }

+

+    /*public void setStepCount(double stepCount) {

+        this.stepCount = stepCount;

+    }*/

+

+    public float getStepPix() {

+        return stepPix;

+    }

+

+    /*public void setStepPix(float stepPix) {

+        this.stepPix = stepPix;

+    }*/

+

+    public double getStepVal() {

+        return stepVal;

+    }

+

+    /*public void setStepVal(double stepVal) {

+        this.stepVal = stepVal;

+    }*/

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepCalculator.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepCalculator.java
new file mode 100644
index 0000000..fedc201
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepCalculator.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.RectF;

+import com.androidplot.util.ValPixConverter;

+

+/**

+ * Calculates "stepping" values for a plot.  These values are most commonly used for

+ * drawing grid lines on a graph.

+ */

+public class XYStepCalculator {

+

+

+    /**

+     * Convenience method - wraps other form of getStep().

+     * @param plot

+     * @param axisType

+     * @param rect

+     * @param minVal

+     * @param maxVal

+     * @return

+     */

+    public static XYStep getStep(XYPlot plot, XYAxisType axisType, RectF rect, Number minVal, Number maxVal) {

+        XYStep step = null;

+        switch(axisType) {

+            case DOMAIN:

+                step = getStep(plot.getDomainStepMode(), rect.width(), plot.getDomainStepValue(), minVal, maxVal);

+                break;

+            case RANGE:

+                step = getStep(plot.getRangeStepMode(), rect.height(), plot.getRangeStepValue(), minVal, maxVal);

+                break;

+        }

+        return step;

+    }

+

+    public static XYStep getStep(XYStepMode typeXY, float plotPixelSize, double stepValue, Number minVal, Number maxVal) {

+        //XYStep step = new XYStep();

+        double stepVal = 0;

+        float stepPix = 0;

+        float stepCount = 0;

+        switch(typeXY) {

+            case INCREMENT_BY_VAL:

+                stepVal = stepValue;

+                stepPix = (float)(stepValue/ ValPixConverter.valPerPix(minVal.doubleValue(), maxVal.doubleValue(), plotPixelSize));

+                stepCount = plotPixelSize /stepPix;

+                break;

+            case INCREMENT_BY_PIXELS:

+                stepPix = new Double(stepValue).floatValue();

+                stepCount = plotPixelSize /stepPix;

+                stepVal = ValPixConverter.valPerPix(minVal.doubleValue(), maxVal.doubleValue(), plotPixelSize)*stepPix;

+                break;

+            case SUBDIVIDE:

+                stepCount = new Double(stepValue).floatValue();

+                stepPix = (plotPixelSize /(stepCount-1));

+                stepVal = ValPixConverter.valPerPix(minVal.doubleValue(), maxVal.doubleValue(), plotPixelSize)*stepPix;

+                break;

+        }

+        return new XYStep(stepCount, stepPix, stepVal);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepMode.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepMode.java
new file mode 100644
index 0000000..005ec5f
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/XYStepMode.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+/**

+ * INCREMENTAL_VALUE - (default) draw a tick every n values.

+ * INCREMENTAL_PIXEL - draw a tick every n pixels.

+ * SUBDIVIDE - draw n number of evenly spaced ticks.

+ */

+public enum XYStepMode {

+    SUBDIVIDE,           // default

+    INCREMENT_BY_VAL,

+    INCREMENT_BY_PIXELS

+}

diff --git a/AndroidPlot-Core/src/main/java/com/androidplot/xy/YValueMarker.java b/AndroidPlot-Core/src/main/java/com/androidplot/xy/YValueMarker.java
new file mode 100644
index 0000000..d1fbde5
--- /dev/null
+++ b/AndroidPlot-Core/src/main/java/com/androidplot/xy/YValueMarker.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.graphics.Paint;

+import com.androidplot.ui.XLayoutStyle;

+import com.androidplot.ui.XPositionMetric;

+

+public class YValueMarker extends ValueMarker<XPositionMetric> {

+

+

+    /**

+     *

+     * @param value

+     * @param text Set to null to use the plot's default formatter.

+     */

+    public YValueMarker(Number value, String text) {

+        super(value, text, new XPositionMetric(3, XLayoutStyle.ABSOLUTE_FROM_LEFT));

+    }

+

+    /**

+     *

+     * @param value

+     * @param text Set to null to use the plot's default formatter.

+     * @param textPosition

+     * @param linePaint

+     * @param textPaint

+     */

+    public YValueMarker(Number value, String text, XPositionMetric textPosition, Paint linePaint, Paint textPaint) {

+        super(value, text, textPosition, linePaint, textPaint);

+    }

+

+    /**

+     *

+     * @param value

+     * @param text Set to null to use the plot's default formatter.

+     * @param textPosition

+     * @param linePaint

+     * @param textPaint

+     */

+    public YValueMarker(Number value, String text, XPositionMetric textPosition, int linePaint, int textPaint) {

+        super(value, text, textPosition, linePaint, textPaint);

+    }

+}

diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/series/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/series/package.html
new file mode 100644
index 0000000..1dc055f
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/series/package.html
@@ -0,0 +1,26 @@
+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

+        "http://www.w3.org/TR/html4/loose.dtd">

+<html>

+<head>

+    <title></title>

+</head>

+<body>

+Series interface definitions.

+</body>

+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/ui/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/ui/package.html
new file mode 100644
index 0000000..dc47c84
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/ui/package.html
@@ -0,0 +1,26 @@
+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

+        "http://www.w3.org/TR/html4/loose.dtd">

+<html>

+<head>

+    <title></title>

+</head>

+<body>

+Visual components of AndroidPlot

+</body>

+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/util/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/util/package.html
new file mode 100644
index 0000000..95a1a39
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/util/package.html
@@ -0,0 +1,26 @@
+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

+        "http://www.w3.org/TR/html4/loose.dtd">

+<html>

+<head>

+    <title></title>

+</head>

+<body>

+General use classes and interfaces.

+</body>

+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/com/androidplot/xy/package.html b/AndroidPlot-Core/src/main/javadoc/com/androidplot/xy/package.html
new file mode 100644
index 0000000..c894bab
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/com/androidplot/xy/package.html
@@ -0,0 +1,26 @@
+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

+        "http://www.w3.org/TR/html4/loose.dtd">

+<html>

+<head>

+    <title></title>

+</head>

+<body>

+Classes for creating XYPlots.

+</body>

+</html>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/main/javadoc/doc-files/barplot1.jpg b/AndroidPlot-Core/src/main/javadoc/doc-files/barplot1.jpg
new file mode 100644
index 0000000..c2f4fa9
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/doc-files/barplot1.jpg
Binary files differ
diff --git a/AndroidPlot-Core/src/main/javadoc/doc-files/lineplot1.jpg b/AndroidPlot-Core/src/main/javadoc/doc-files/lineplot1.jpg
new file mode 100644
index 0000000..f1917b2
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/doc-files/lineplot1.jpg
Binary files differ
diff --git a/AndroidPlot-Core/src/main/javadoc/overview.html b/AndroidPlot-Core/src/main/javadoc/overview.html
new file mode 100644
index 0000000..d882141
--- /dev/null
+++ b/AndroidPlot-Core/src/main/javadoc/overview.html
@@ -0,0 +1,20 @@
+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<body>

+A library for drawing plots on the Android platform.

+Visit <a href="http://androidplot.com/wiki/Docs">http://androidplot.com/wiki/Docs</a> for documentation and tutorials.

+</body>
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/LineRegionTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/LineRegionTest.java
new file mode 100644
index 0000000..a524313
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/LineRegionTest.java
@@ -0,0 +1,84 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot;

+

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertTrue;

+

+public class LineRegionTest {

+    @Before

+    public void setUp() throws Exception {

+

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+    @Test

+    public void testConstructor() throws Exception {

+        LineRegion lr = new LineRegion(0d, 0d);

+        assertEquals(0d, lr.getMinVal());

+        assertEquals(0d, lr.getMaxVal());

+

+        lr = new LineRegion(1.5d, -2d);

+        assertEquals(-2d, lr.getMinVal());

+        assertEquals(1.5d, lr.getMaxVal());

+

+        lr = new LineRegion(10d, 20d);

+        assertEquals(10d, lr.getMinVal());

+        assertEquals(20d, lr.getMaxVal());

+    }

+

+

+    @Test

+    public void testContains() throws Exception {

+

+    }

+

+    @Test

+    public void testIntersects() throws Exception {

+        LineRegion line1 = new LineRegion(1, 10);

+        LineRegion line2 = new LineRegion(11, 20);

+        assertFalse(line1.intersects(line2));

+

+        line1.setMaxVal(15);

+        assertTrue(line1.intersects(line2));

+

+        //l1end = 30;

+        line1.setMaxVal(30);

+        assertTrue(line1.intersects(line2));

+

+        //l1start = 21;

+        line1.setMinVal(21);

+        assertFalse(line1.intersects(line2));

+    }

+

+    @Test

+    public void testLength() throws Exception {

+        LineRegion lr = new LineRegion(0, 10);

+        assertEquals(10d, lr.length().doubleValue(), 0);

+

+        lr = new LineRegion(-5, 5);

+        assertEquals(10d, lr.length().doubleValue(), 0);

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/PlotTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/PlotTest.java
new file mode 100644
index 0000000..3dc8aeb
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/PlotTest.java
@@ -0,0 +1,483 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot;

+

+import android.content.Context;

+import android.graphics.*;

+import android.os.Handler;

+import android.util.Log;

+import android.view.View;

+import com.androidplot.mock.MockContext;

+import com.androidplot.mock.MockPaint;

+import com.androidplot.ui.SeriesAndFormatterList;

+import com.androidplot.exception.PlotRenderException;

+import com.androidplot.ui.SeriesRenderer;

+import com.androidplot.ui.Formatter;

+//import mockit.*;

+import com.androidplot.ui.widget.TextLabelWidget;

+import com.androidplot.util.Configurator;

+import com.androidplot.util.FontUtils;

+import com.androidplot.util.PixelUtils;

+import mockit.*;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.LinkedHashMap;

+import java.util.List;

+

+import static junit.framework.Assert.assertEquals;

+import static junit.framework.Assert.assertNotSame;

+import static junit.framework.Assert.assertNull;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertNotNull;

+import static org.junit.Assert.assertTrue;

+

+@UsingMocksAndStubs({Log.class, View.class,Handler.class,Paint.class,Color.class,

+        RectF.class, Rect.class, FontUtils.class, Canvas.class,

+        PixelUtils.class,Context.class})

+

+public class PlotTest {

+

+    static class MockPlotListener implements PlotListener {

+

+        @Override

+        public void onBeforeDraw(Plot source, Canvas canvas) {}

+

+        @Override

+        public void onAfterDraw(Plot source, Canvas canvas) {}

+    }

+

+    static class MockSeries implements Series {

+        @Override

+        public String getTitle() {

+            return null;

+        }

+

+    }

+

+    static class MockSeries2 implements Series {

+        @Override

+        public String getTitle() {

+            return null;

+        }

+    }

+

+    static class MockSeries3 implements Series {

+        @Override

+        public String getTitle() {

+            return null;

+        }

+    }

+

+    static class MockRenderer1 extends SeriesRenderer {

+

+        public MockRenderer1(Plot plot) {

+            super(plot);

+        }

+

+        @Override

+        public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {

+

+        }

+

+        @Override

+        public void doDrawLegendIcon(Canvas canvas, RectF rect, Formatter formatter) {

+

+        }

+    }

+    static class MockRenderer2 extends SeriesRenderer {

+

+        public MockRenderer2(Plot plot) {

+            super(plot);

+        }

+

+        @Override

+        public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {

+

+        }

+

+        @Override

+        public void doDrawLegendIcon(Canvas canvas, RectF rect, Formatter formatter) {

+

+        }

+    }

+

+    static class MockFormatter1 extends Formatter<MockPlot> {

+

+        @Override

+        public Class<? extends SeriesRenderer> getRendererClass() {

+            return MockRenderer1.class;

+        }

+

+        @Override

+        public SeriesRenderer getRendererInstance(MockPlot plot) {

+            return new MockRenderer1(plot);

+        }

+    }

+

+    static class MockFormatter2 extends Formatter<MockPlot> {

+

+        @Override

+        public Class<? extends SeriesRenderer> getRendererClass() {

+            return MockRenderer2.class;

+        }

+

+        @Override

+        public SeriesRenderer getRendererInstance(MockPlot plot) {

+            return new MockRenderer2(plot);

+        }

+    }

+

+    //@MockClass(realClass = Plot.class)

+    public static class MockPlot extends Plot<MockSeries, Formatter, SeriesRenderer> {

+        public MockPlot(Context context, String title) {

+            super(context, title);

+        }

+

+        @Override

+        protected void onPreInit() {

+

+        }

+

+        /*@Override

+        protected SeriesRenderer doGetRendererInstance(Class clazz) {

+            if(clazz == MockRenderer1.class) {

+                return new MockRenderer1(this);

+            } else if(clazz == MockRenderer2.class) {

+                return new MockRenderer2(this);

+            } else {

+                return null;

+            }

+        }*/

+    }

+

+    @Before

+    public void setUp() throws Exception {

+        Mockit.setUpMocks(MockPaint.class,MockContext.class);

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+    @Test

+    public void testAddSeries() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        //Plot plot = Mockit.setUpMock(Plot.class, new MockPlot(context, "MockPlot"));

+        //Plot plot = Mockit.setUpMock(new MockPlot());

+        Plot plot = new MockPlot(context, "MockPlot");

+

+        MockSeries m1 = new MockSeries();

+        Class cl = MockRenderer1.class;

+

+

+

+        plot.addSeries(m1, new MockFormatter1());

+

+        LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");

+        assertEquals(1, registry.size());

+        assertEquals(1, registry.get(cl).size());

+

+        plot.addSeries(m1, new MockFormatter1());

+

+        // duplicate Renderer added, registry size should not grow:

+        assertEquals(1, registry.size());

+        assertEquals(1, registry.get(cl).size());

+

+        MockSeries m2 = new MockSeries();

+

+        plot.addSeries(m2, new MockFormatter1());

+

+        // still should only be one renderer type:

+        assertEquals(1, registry.size());

+

+        // we added a new instance of cl to the renderer so there should be 2 in the subregistry:

+        assertEquals(2, registry.get(cl).size());

+

+

+        // lets add another renderer:

+        plot.addSeries(m1, new MockFormatter2());

+

+        assertEquals(2, registry.size());

+    }

+

+    @Test

+    public void testRemoveSeries() throws Exception {

+

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+        LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");

+

+        MockSeries m1 = new MockSeries();

+        MockSeries m2 = new MockSeries();

+        MockSeries m3 = new MockSeries();

+

+        plot.addSeries(m1, new MockFormatter1());

+        plot.addSeries(m2, new MockFormatter1());

+        plot.addSeries(m3, new MockFormatter1());

+

+        plot.addSeries(m1, new MockFormatter2());

+        plot.addSeries(m2, new MockFormatter2());

+        plot.addSeries(m3, new MockFormatter2());

+

+

+        // a quick sanity check:

+        assertEquals(2, registry.size());

+        assertEquals(3, registry.get(MockRenderer1.class).size());

+        assertEquals(3, registry.get(MockRenderer2.class).size());

+

+        plot.removeSeries(m1, MockRenderer1.class);

+        assertEquals(2, registry.get(MockRenderer1.class).size());

+

+        plot.removeSeries(m2, MockRenderer1.class);

+        assertEquals(1, registry.get(MockRenderer1.class).size());

+

+        plot.removeSeries(m2, MockRenderer1.class);

+        assertEquals(1, registry.get(MockRenderer1.class).size());

+

+        plot.removeSeries(m3, MockRenderer1.class);

+

+        // all the elements should be gone from MockRenderer1, thus the renderer should

+        // also be gone:

+        assertNull(registry.get(MockRenderer1.class));

+

+

+        // add em all back

+        plot.addSeries(m1, new MockFormatter1());

+        plot.addSeries(m2, new MockFormatter1());

+        plot.addSeries(m3, new MockFormatter1());

+

+        plot.addSeries(m1, new MockFormatter1());

+        plot.addSeries(m2, new MockFormatter1());

+        plot.addSeries(m3, new MockFormatter1());

+

+

+        // a quick sanity check:

+        assertEquals(2, registry.size());

+        assertEquals(3, registry.get(MockRenderer1.class).size());

+        assertEquals(3, registry.get(MockRenderer2.class).size());

+

+        // now lets try removing a series from all renderers:

+        plot.removeSeries(m1);

+        assertEquals(2, registry.get(MockRenderer1.class).size());

+        assertEquals(2, registry.get(MockRenderer2.class).size());

+

+        // and now lets remove the remaining series:

+        plot.removeSeries(m2);

+        plot.removeSeries(m3);

+

+        // nothing should be left:

+        assertNull(registry.get(MockRenderer1.class));

+        assertNull(registry.get(MockRenderer2.class));

+    }

+

+

+    @Test

+    public void testGetFormatter() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+        LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");

+

+        MockSeries m1 = new MockSeries();

+        MockSeries m2 = new MockSeries();

+        MockSeries m3 = new MockSeries();

+

+        MockFormatter1 f1 = new MockFormatter1();

+        MockFormatter1 f2 = new MockFormatter1();

+        MockFormatter2 f3 = new MockFormatter2();

+

+        plot.addSeries(m1, f1);

+        plot.addSeries(m2, f2);

+        plot.addSeries(m3, new MockFormatter1());

+

+        plot.addSeries(m1, new MockFormatter1());

+        plot.addSeries(m2, f3);

+        plot.addSeries(m3, new MockFormatter1());

+

+        assertEquals(registry.get(MockRenderer1.class).getFormatter(m1), f1);

+        assertEquals(registry.get(MockRenderer1.class).getFormatter(m2), f2);

+        assertEquals(registry.get(MockRenderer2.class).getFormatter(m2), f3);

+

+        assertNotSame(registry.get(MockRenderer2.class).getFormatter(m2), f1);

+

+    }

+

+    @Test

+    public void testGetSeriesListForRenderer() throws Exception {

+

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+        //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");

+

+        MockSeries m1 = new MockSeries();

+        MockSeries m2 = new MockSeries();

+        MockSeries m3 = new MockSeries();

+

+        plot.addSeries(m1, new MockFormatter1());

+        plot.addSeries(m2, new MockFormatter1());

+        plot.addSeries(m3, new MockFormatter1());

+

+        plot.addSeries(m1, new MockFormatter1());

+        plot.addSeries(m2, new MockFormatter1());

+        plot.addSeries(m3, new MockFormatter1());

+

+        List<MockSeries> m1List = plot.getSeriesListForRenderer(MockRenderer1.class);

+        assertEquals(3, m1List.size());

+        assertEquals(m1, m1List.get(0));

+        assertNotSame(m2, m1List.get(0));

+        assertEquals(m2, m1List.get(1));

+        assertEquals(m3, m1List.get(2));

+    }

+

+    @Test

+    public void testGetRendererList() throws Exception {

+

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+        //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");

+

+        MockSeries m1 = new MockSeries();

+        MockSeries m2 = new MockSeries();

+        MockSeries m3 = new MockSeries();

+

+        plot.addSeries(m1, new MockFormatter1());

+        plot.addSeries(m2, new MockFormatter1());

+        plot.addSeries(m3, new MockFormatter1());

+

+        plot.addSeries(m1, new MockFormatter2());

+        plot.addSeries(m2, new MockFormatter2());

+        plot.addSeries(m3, new MockFormatter2());

+

+        List<SeriesRenderer> rList = plot.getRendererList();

+        assertEquals(2, rList.size());

+

+        assertEquals(MockRenderer1.class, rList.get(0).getClass());

+        assertEquals(MockRenderer2.class, rList.get(1).getClass());

+    }

+

+    @Test

+    public void testAddListener() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+        ArrayList<PlotListener> listeners = Deencapsulation.getField(plot, "listeners");

+        //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");

+

+        assertEquals(0, listeners.size());

+

+        MockPlotListener pl1 = new MockPlotListener();

+        MockPlotListener pl2 = new MockPlotListener();

+

+        plot.addListener(pl1);

+

+        assertEquals(1, listeners.size());

+

+        // should return false on a double entry attempt

+        assertFalse(plot.addListener(pl1));

+

+        // make sure the listener wasnt added anyway:

+        assertEquals(1, listeners.size());

+

+        plot.addListener(pl2);

+

+        assertEquals(2, listeners.size());

+                

+    }

+

+    @Test

+    public void testRemoveListener() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+        ArrayList<PlotListener> listeners = Deencapsulation.getField(plot, "listeners");

+        //LinkedHashMap<Class<SeriesRenderer>, SeriesAndFormatterList<MockSeries,MockFormatter1>> registry = Deencapsulation.getField(plot, "seriesRegistry");

+

+        assertEquals(0, listeners.size());

+

+        MockPlotListener pl1 = new MockPlotListener();

+        MockPlotListener pl2 = new MockPlotListener();

+        MockPlotListener pl3 = new MockPlotListener();

+

+        plot.addListener(pl1);

+        plot.addListener(pl2);

+

+        assertEquals(2, listeners.size());

+

+        assertFalse(plot.removeListener(pl3));

+

+        assertTrue(plot.removeListener(pl1));

+

+        assertEquals(1, listeners.size());

+

+        assertFalse(plot.removeListener(pl1));

+

+        assertEquals(1, listeners.size());

+

+        assertTrue(plot.removeListener(pl2));

+

+        assertEquals(0, listeners.size());

+

+    }

+

+    /*@Test

+    public void testGuessGetterName() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+

+        Method m = Plot.class.getDeclaredMethod("guessGetterMethod", Object.class, String.class);

+        assertNotNull(m);

+    }

+

+    @Test

+    public void testGuessSetterName() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        Plot plot = new MockPlot(context, "MockPlot");

+

+        Method m = Plot.class.getDeclaredMethod("guessSetterMethod", Object.class, String.class, Class.class);

+        assertNotNull(m);

+    }*/

+

+

+

+    @Test

+    public void testConfigure() throws Exception {

+        //Context context = Mockit.setUpMock(new MockContext.MockContext2());

+        Context context = new MockContext.MockContext2();

+        Plot plot = new MockPlot(context, "MockPlot");

+

+        HashMap<String, String> params = new HashMap<String, String>();

+        String param1 = "this is a test.";

+        //String param2 = Plot.RenderMode.USE_BACKGROUND_THREAD.toString();

+        String param2 = "use_background_thread";

+        String param3 = "#FF0000";

+        params.put("title", param1);

+        params.put("renderMode", param2);

+        params.put("backgroundPaint.color", param3);

+

+

+        //Method m = Plot.class.getDeclaredMethod("configure", params.getClass());

+        //m.setAccessible(true);

+        //m.invoke(plot, params);

+        Configurator.configure(context, plot, params);

+

+        assertEquals(param1, plot.getTitle());

+        assertEquals(Plot.RenderMode.USE_BACKGROUND_THREAD, plot.getRenderMode());

+        assertEquals(Color.parseColor(param3), plot.getBackgroundPaint().getColor());

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/RegionTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/RegionTest.java
new file mode 100644
index 0000000..dcf4148
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/RegionTest.java
@@ -0,0 +1,59 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot;

+

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertTrue;

+

+public class RegionTest {

+    @Before

+    public void setUp() throws Exception {

+

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+    @Test

+    public void testContains() throws Exception {

+

+    }

+

+    @Test

+    public void testIntersects() throws Exception {

+        LineRegion line1 = new LineRegion(1, 10);

+        LineRegion line2 = new LineRegion(11, 20);

+        assertFalse(line1.intersects(line2));

+

+        line1.setMaxVal(15);

+        assertTrue(line1.intersects(line2));

+

+        //l1end = 30;

+        line1.setMaxVal(30);

+        assertTrue(line1.intersects(line2));

+

+        //l1start = 21;

+        line1.setMinVal(21);

+        assertFalse(line1.intersects(line2));

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockCanvas.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockCanvas.java
new file mode 100644
index 0000000..44fca82
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockCanvas.java
@@ -0,0 +1,34 @@
+package com.androidplot.mock;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import mockit.Mock;
+import mockit.MockClass;
+import mockit.MockUp;
+import mockit.Mocked;
+
+@MockClass(realClass = Canvas.class, stubs="", inverse=true)
+public class MockCanvas {
+
+    @Mock
+    public int getHeight() {
+        return 100;
+    }
+
+    @Mock
+    public int getWidth() {
+        return 100;
+    }
+
+    @Mock
+    public void restore() {}
+
+    @Mock
+    public int save(int flags) {
+        return 1;
+    }
+
+    @Mock
+    public void drawPoint(float x, float y, Paint paint) {}
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockContext.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockContext.java
new file mode 100644
index 0000000..0251c77
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockContext.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.mock;
+
+import android.content.*;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import mockit.Instantiation;
+import mockit.Mock;
+import mockit.MockClass;
+
+import java.io.*;
+
+@MockClass(realClass = Context.class)
+public class MockContext {
+
+    /**
+     * Useful for when methods are going to actually be called on a Context instance.
+     * See {@link com.androidplot.PlotTest#testConfigure()} for an example.
+     */
+    public static final class MockContext2 extends Context {
+
+        public MockContext2() {}
+
+        @Override
+        public AssetManager getAssets() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Mock
+        public android.content.res.Resources getResources() { throw new IllegalArgumentException();}
+
+        @Override
+        public PackageManager getPackageManager() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Looper getMainLooper() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Context getApplicationContext() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void setTheme(int i) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Resources.Theme getTheme() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public ClassLoader getClassLoader() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public String getPackageName() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public ApplicationInfo getApplicationInfo() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public String getPackageResourcePath() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public String getPackageCodePath() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public SharedPreferences getSharedPreferences(String s, int i) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public FileInputStream openFileInput(String s) throws FileNotFoundException {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public FileOutputStream openFileOutput(String s, int i) throws FileNotFoundException {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public boolean deleteFile(String s) {
+            return false;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getFileStreamPath(String s) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getFilesDir() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getExternalFilesDir(String s) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getObbDir() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getCacheDir() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getExternalCacheDir() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public String[] fileList() {
+            return new String[0];  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getDir(String s, int i) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public SQLiteDatabase openOrCreateDatabase(String s, int i, SQLiteDatabase.CursorFactory cursorFactory) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public SQLiteDatabase openOrCreateDatabase(String s, int i, SQLiteDatabase.CursorFactory cursorFactory, DatabaseErrorHandler databaseErrorHandler) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public boolean deleteDatabase(String s) {
+            return false;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public File getDatabasePath(String s) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public String[] databaseList() {
+            return new String[0];  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Drawable getWallpaper() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Drawable peekWallpaper() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int getWallpaperDesiredMinimumWidth() {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int getWallpaperDesiredMinimumHeight() {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void setWallpaper(Bitmap bitmap) throws IOException {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void setWallpaper(InputStream inputStream) throws IOException {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void clearWallpaper() throws IOException {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void startActivity(Intent intent) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        //@Override
+        public void startActivity(Intent intent, Bundle bundle) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void startActivities(Intent[] intents) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        //@Override
+        public void startActivities(Intent[] intents, Bundle bundle) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void startIntentSender(IntentSender intentSender, Intent intent, int i, int i1, int i2) throws IntentSender.SendIntentException {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        //@Override
+        public void startIntentSender(IntentSender intentSender, Intent intent, int i, int i1, int i2, Bundle bundle) throws IntentSender.SendIntentException {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void sendBroadcast(Intent intent) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void sendBroadcast(Intent intent, String s) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void sendOrderedBroadcast(Intent intent, String s) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void sendOrderedBroadcast(Intent intent, String s, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s1, Bundle bundle) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void sendStickyBroadcast(Intent intent) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver broadcastReceiver, Handler handler, int i, String s, Bundle bundle) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void removeStickyBroadcast(Intent intent) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Intent registerReceiver(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Intent registerReceiver(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter, String s, Handler handler) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void unregisterReceiver(BroadcastReceiver broadcastReceiver) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public ComponentName startService(Intent intent) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public boolean stopService(Intent intent) {
+            return false;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
+            return false;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void unbindService(ServiceConnection serviceConnection) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public boolean startInstrumentation(ComponentName componentName, String s, Bundle bundle) {
+            return false;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Object getSystemService(String s) {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int checkPermission(String s, int i, int i1) {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int checkCallingPermission(String s) {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int checkCallingOrSelfPermission(String s) {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void enforcePermission(String s, int i, int i1, String s1) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void enforceCallingPermission(String s, String s1) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void enforceCallingOrSelfPermission(String s, String s1) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void grantUriPermission(String s, Uri uri, int i) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void revokeUriPermission(Uri uri, int i) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int checkUriPermission(Uri uri, int i, int i1, int i2) {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int checkCallingUriPermission(Uri uri, int i) {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int checkCallingOrSelfUriPermission(Uri uri, int i) {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public int checkUriPermission(Uri uri, String s, String s1, int i, int i1, int i2) {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void enforceUriPermission(Uri uri, int i, int i1, int i2, String s) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void enforceCallingUriPermission(Uri uri, int i, String s) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void enforceCallingOrSelfUriPermission(Uri uri, int i, String s) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public void enforceUriPermission(Uri uri, String s, String s1, int i, int i1, int i2, String s2) {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        @Override
+        public Context createPackageContext(String s, int i) throws PackageManager.NameNotFoundException {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+    }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockLooper.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockLooper.java
new file mode 100644
index 0000000..5a4649e
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockLooper.java
@@ -0,0 +1,25 @@
+package com.androidplot.mock;
+
+import android.os.Looper;
+import mockit.Mock;
+import mockit.MockClass;
+
+
+/**
+ * myLooper and getMainLooper will always be equal.  The implication is that any calling
+ * entity will assume that it is running on the main thread.  Simulation of background mode
+ * is not supported.
+ */
+@MockClass(realClass = Looper.class)
+public class MockLooper {
+
+    @Mock
+    public static Looper myLooper() {
+        return null;
+    }
+
+    @Mock
+    public static Looper getMainLooper() {
+        return null;
+    }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPaint.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPaint.java
new file mode 100644
index 0000000..f0209ee
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPaint.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.mock;
+
+import android.graphics.Paint;
+import android.graphics.RectF;
+import mockit.Instantiation;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = Paint.class)
+public final class MockPaint {
+    int color = 0;
+
+    @Mock
+    public void setColor(int color) {
+        this.color = color;
+    }
+
+    @Mock
+    public int getColor() {
+        return color;
+    }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPixelUtils.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPixelUtils.java
new file mode 100644
index 0000000..45cb25c
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPixelUtils.java
@@ -0,0 +1,22 @@
+package com.androidplot.mock;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import com.androidplot.util.PixelUtils;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = PixelUtils.class, stubs="", inverse=true)
+public class MockPixelUtils {
+
+    @Mock
+    public static void init(Context ctx) {
+
+    }
+
+    @Mock
+    public static PointF sub(PointF lhs, PointF rhs) {
+        return new PointF(0, 0);
+    }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPointF.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPointF.java
new file mode 100644
index 0000000..0970c35
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockPointF.java
@@ -0,0 +1,25 @@
+package com.androidplot.mock;
+
+import android.graphics.PointF;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = PointF.class)
+public class MockPointF {
+    float x;
+    float y;
+
+    @Mock
+    public void $init() {}
+
+    @Mock
+    public void $init(float x, float y) {
+        set(x, y);
+    }
+
+    @Mock
+    public void set(float x, float y) {
+        this.x = x;
+        this.y = y;
+    }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockRectF.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockRectF.java
new file mode 100644
index 0000000..022788c
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockRectF.java
@@ -0,0 +1,94 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.mock;

+

+import android.graphics.RectF;

+import mockit.Instantiation;

+import mockit.Mock;

+import mockit.MockClass;

+

+@MockClass(realClass = RectF.class, stubs="", inverse=true)

+public final class MockRectF {

+    //public float left;

+    //public float top;

+    //public float right;

+    //public float bottom;

+

+    public RectF it;

+

+    @Mock

+    public void $init() {

+

+    }

+

+    @Mock

+    public void $init(RectF rhs) {

+        it.left = rhs.left;

+        it.top = rhs.top;

+        it.right = rhs.right;

+        it.bottom = rhs.bottom;

+    }

+

+    @Mock

+    public void $init(float left, float top, float right, float bottom) {

+        it.left = left;

+        it.top = top;

+        it.right = right;

+        it.bottom = bottom;

+        // do anything here

+

+    }

+

+    @Mock

+    public void offset(float dx, float dy) {

+        float w = width();

+        float h = height();

+

+        it.left = it.left + dx;

+        it.right = it.right + dx;

+

+        it.top = it.top + dy;

+        it.bottom = it.bottom + dy;

+    }

+

+    @Mock

+    public void offsetTo(float left, float top) {

+

+        it.right = left + width();

+        it.bottom = top + height();

+

+        it.left = left;

+        it.top = top;

+

+        // do anything here

+    }

+

+    @Mock

+    public float height() {

+        return it.bottom - it.top;

+    }

+

+    @Mock

+    public float width() {

+        return it.right - it.left;

+    }

+

+    @Mock

+    public String toString() {

+        return null;

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockSizeMetrics.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockSizeMetrics.java
new file mode 100644
index 0000000..389b1d9
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockSizeMetrics.java
@@ -0,0 +1,21 @@
+package com.androidplot.mock;
+
+import com.androidplot.ui.SizeLayoutType;
+import com.androidplot.ui.SizeMetric;
+import com.androidplot.ui.SizeMetrics;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = SizeMetrics.class)
+public class MockSizeMetrics {
+
+    @Mock
+    public SizeMetric getWidthMetric() {
+        return new SizeMetric(0, SizeLayoutType.ABSOLUTE);
+    }
+
+    @Mock
+    public SizeMetric getHeightMetric() {
+        return new SizeMetric(0, SizeLayoutType.ABSOLUTE);
+    }
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockWidget.java b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockWidget.java
new file mode 100644
index 0000000..53dc6b8
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/mock/MockWidget.java
@@ -0,0 +1,20 @@
+package com.androidplot.mock;
+
+import com.androidplot.ui.widget.Widget;
+import mockit.Mock;
+import mockit.MockClass;
+
+@MockClass(realClass = Widget.class, inverse = false)
+public class MockWidget {
+
+    @Mock
+    public float getWidthPix(float f) {
+        return 100;
+    }
+
+    @Mock
+    public float getHeightPix(float f) {
+        return 100;
+    }
+
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/ui/DynamicTableModelTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/ui/DynamicTableModelTest.java
new file mode 100644
index 0000000..2585ec8
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/ui/DynamicTableModelTest.java
@@ -0,0 +1,245 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+import com.androidplot.mock.MockRectF;

+import com.androidplot.ui.DynamicTableModel;

+import com.androidplot.ui.TableModel;

+import com.androidplot.ui.TableOrder;

+import mockit.*;

+import org.junit.Before;

+import org.junit.Test;

+

+import java.util.Iterator;

+

+import static junit.framework.Assert.assertEquals;

+

+public class DynamicTableModelTest {

+    @Before

+    public void setUp() throws Exception {

+        Mockit.setUpMocks(MockRectF.class);

+    }

+

+    @Test

+    public void testConstructor() throws Exception {

+        TableModel model = new DynamicTableModel(5, 5, TableOrder.COLUMN_MAJOR);

+        // TODO

+    }

+

+    @Test

+    public void testGetCellRect() throws Exception {

+

+        // square table, both rows and columns defined:

+        DynamicTableModel model = new DynamicTableModel(5, 5);

+        RectF tableRect = new RectF(0, 0, 1000, 2000);

+        RectF cellRect = model.getCellRect(tableRect, 10);

+        assertEquals(200f, cellRect.width());

+

+        // only rows defined:

+        model = new DynamicTableModel(5, 0);

+        cellRect = model.getCellRect(tableRect, 10);

+        assertEquals(200f, cellRect.width());

+

+        // only columns defined:

+        model = new DynamicTableModel(0, 5);

+        cellRect = model.getCellRect(tableRect, 10);

+        assertEquals(400f, cellRect.height());

+    }

+

+    @Test public void testIterator() throws Exception {

+        TableModel model = new DynamicTableModel(2, 2);

+

+        RectF tableRect = new RectF(0, 0, 1000, 2000);

+

+        // should stop at 4 iterations since the table can only hold that many:

+        Iterator<RectF> it = model.getIterator(tableRect, 10);

+        int iterations = 0;

+        while(it.hasNext()) {

+            it.next();

+            iterations++;

+        }

+        assertEquals(4, iterations);

+

+        // now set a dynamic number of columns.  iterations should equal however

+        // many elements we throw at it:

+        model = new DynamicTableModel(2, 0);

+        it = model.getIterator(tableRect, 10);

+        iterations = 0;

+        while(it.hasNext()) {

+            it.next();

+            iterations++;

+        }

+        assertEquals(10, iterations);

+

+

+    }

+

+    @Test

+    public void testRowMajorIteration() throws Exception {

+

+        // square table, both rows and columns defined:

+        TableModel model = new DynamicTableModel(2, 2);

+        RectF tableRect = new RectF(0, 0, 1000, 2000);

+        int createdCells = 4;

+        Iterator<RectF> it = model.getIterator(tableRect, createdCells);

+        

+

+        // 2x2:

+        // cell 0 (top-left

+        RectF cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(0f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(500f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        // cell 1 (top-right)

+        cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(500f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(1000f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        // cell 2 (bottom-left)

+        cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(0f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(500f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+

+        // cell 3 (bottom-right)

+        cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(500f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(1000f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+

+        // 2xN:

+        /*model = new DynamicTableModel(2, 0);

+        tableRect = new RectF(0, 0, 1000, 2000);

+        createdCells = 4;

+        it = model.getIterator(tableRect, createdCells);*/

+

+

+

+    }

+

+

+    @Test

+    public void testColumnMajorIteration() throws Exception {

+

+        // square table, both rows and columns defined:

+        TableModel model = new DynamicTableModel(2, 2, TableOrder.COLUMN_MAJOR);

+        RectF tableRect = new RectF(0, 0, 1000, 2000);

+        int createdCells = 4;

+        Iterator<RectF> it = model.getIterator(tableRect, createdCells);

+

+

+        // 2x2 fixed:

+        // cell 0 (top-left

+        RectF cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(0f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(500f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        // cell 1 (bottom-left)

+        cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(0f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(500f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+

+        // cell 2 (bottom-left)

+        cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(500f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(1000f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        // cell 3 (bottom-right)

+        cellRect = it.next();

+        assertEquals(500f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(500f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(1000f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+    }

+

+    @Test

+    public void testSingleRowIteration() throws Exception {

+        // square table, both rows and columns defined:

+        TableModel model = new DynamicTableModel(0, 1);

+        RectF tableRect = new RectF(0, 0, 1000, 1000);

+        int createdCells = 4;

+        Iterator<RectF> it = model.getIterator(tableRect, createdCells);

+

+

+

+        // 2x2 fixed:

+        // cell 0 (top-left

+        RectF cellRect = it.next();

+        assertEquals(250f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(0f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(250f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        // cell 1

+        cellRect = it.next();

+        assertEquals(250f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(250f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(500f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        // cell 2

+        cellRect = it.next();

+        assertEquals(250f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(500f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(750f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        // cell 3

+        cellRect = it.next();

+        assertEquals(250f, cellRect.width());

+        assertEquals(1000f, cellRect.height());

+        assertEquals(750f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(1000f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/ui/FixedTableModelTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/ui/FixedTableModelTest.java
new file mode 100644
index 0000000..42453b6
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/ui/FixedTableModelTest.java
@@ -0,0 +1,254 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.ui;

+

+import android.graphics.RectF;

+import com.androidplot.mock.MockRectF;

+import com.androidplot.ui.FixedTableModel;

+import com.androidplot.ui.TableOrder;

+import mockit.*;

+import org.junit.Before;

+import org.junit.Test;

+

+import java.util.Iterator;

+

+import static junit.framework.Assert.*;

+

+public class FixedTableModelTest {

+    @Before

+    public void setUp() throws Exception {

+        Mockit.setUpMocks(MockRectF.class);

+    }

+

+    @Test

+    public void testConstructor() throws Exception {

+        FixedTableModel model = new FixedTableModel(100, 100, null);

+        // TODO

+    }

+

+    @Test

+    public void testIterator() throws Exception {

+        FixedTableModel model = new FixedTableModel(100, 100, TableOrder.COLUMN_MAJOR);

+

+        RectF tableRect = new RectF(0, 0, 1000, 2000);

+

+        Iterator<RectF> it = model.getIterator(tableRect, 5);

+

+        assertTrue(it.hasNext());

+        RectF cellRect = it.next();

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+

+        assertFalse(it.hasNext());

+        try {

+            cellRect = it.next();

+            fail("Expected IndexOutOfBoundsException");

+        } catch(IndexOutOfBoundsException ex) {

+            // this was expected

+        }

+    }

+

+    @Test

+    public void testColumnMajor() throws Exception {

+        FixedTableModel model = new FixedTableModel(300, 500, TableOrder.COLUMN_MAJOR);

+

+        RectF tableRect = new RectF(0, 0, 1000, 2000);

+

+        // test the numElement limit:

+        Iterator<RectF> it = model.getIterator(tableRect, 5);

+        assertTrue(it.hasNext());

+        RectF cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(300f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(600f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(600f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(900f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(500f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(300f, cellRect.left);

+        assertEquals(500f, cellRect.top);

+        assertEquals(600f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        assertFalse(it.hasNext());

+

+        // test border limit:

+        it = model.getIterator(tableRect, 25);

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(300f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(600f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(600f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(900f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(500f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(300f, cellRect.left);

+        assertEquals(500f, cellRect.top);

+        assertEquals(600f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(600f, cellRect.left);

+        assertEquals(500f, cellRect.top);

+        assertEquals(900f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(1500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(300f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(600f, cellRect.right);

+        assertEquals(1500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(600f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(900f, cellRect.right);

+        assertEquals(1500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(1500f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(300f, cellRect.left);

+        assertEquals(1500f, cellRect.top);

+        assertEquals(600f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(600f, cellRect.left);

+        assertEquals(1500f, cellRect.top);

+        assertEquals(900f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+

+        //we've reached the limit

+        assertFalse(it.hasNext());

+    }

+

+    @Test

+    public void testRowMajor() throws Exception {

+        FixedTableModel model = new FixedTableModel(300, 500, TableOrder.ROW_MAJOR);

+

+        RectF tableRect = new RectF(0, 0, 1000, 2000);

+

+        // test the numElement limit:

+        Iterator<RectF> it = model.getIterator(tableRect, 5);

+        assertTrue(it.hasNext());

+        RectF cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(500f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(1000f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(1000f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(1500f, cellRect.bottom);

+

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(0f, cellRect.left);

+        assertEquals(1500f, cellRect.top);

+        assertEquals(300f, cellRect.right);

+        assertEquals(2000f, cellRect.bottom);

+

+        // next column over

+        assertTrue(it.hasNext());

+        cellRect = it.next();

+        assertEquals(300f, cellRect.left);

+        assertEquals(0f, cellRect.top);

+        assertEquals(600f, cellRect.right);

+        assertEquals(500f, cellRect.bottom);

+    }

+}
\ No newline at end of file
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/util/ConfiguratorTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/util/ConfiguratorTest.java
new file mode 100644
index 0000000..b5790e8
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/util/ConfiguratorTest.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import android.content.Context;

+import android.util.Log;

+import com.androidplot.mock.MockContext;

+import mockit.Mockit;

+import mockit.UsingMocksAndStubs;

+import org.junit.Test;

+

+import java.lang.reflect.Method;

+

+import static junit.framework.Assert.assertEquals;

+import static org.junit.Assert.fail;

+

+@UsingMocksAndStubs({Log.class})

+public class ConfiguratorTest {

+

+    class A {

+        int d = 0;

+

+        public int getD() {

+            return d;

+        }

+

+        public void setD(int d) {

+            this.d = d;

+        }

+    }

+

+    class B {

+        A a = new A();

+

+        public A getA() {

+            return a;

+        }

+

+        public void setA(A a) {

+            this.a = a;

+        }

+    }

+

+    class C {

+        B b = new B();

+

+        public B getB() {

+            return b;

+        }

+

+        public void setB(B a) {

+            this.b = b;

+        }

+    }

+

+    @org.junit.Before

+    public void setUp() throws Exception {

+

+    }

+

+    @org.junit.After

+    public void tearDown() throws Exception {

+

+    }

+

+

+    @Test

+    public void testGetFieldAt() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        C c = new C();

+        assertEquals(c, Configurator.getObjectContaining(c, "b"));

+        assertEquals(c.getB(), Configurator.getObjectContaining(c, "b.a"));

+        assertEquals(c.getB().getA(), Configurator.getObjectContaining(c, "b.a.d"));

+    }

+

+    @Test

+    public void testGetSetter() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        C c = new C();

+

+        Method m = Configurator.getSetter(c.getClass(), "b");

+        assertEquals(1, m.getParameterTypes().length);

+        assertEquals(B.class, m.getParameterTypes()[0]);

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/util/ListOrganizerTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/util/ListOrganizerTest.java
new file mode 100644
index 0000000..5f4e240
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/util/ListOrganizerTest.java
@@ -0,0 +1,95 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import java.util.LinkedList;

+

+import static junit.framework.Assert.assertEquals;

+

+public class ListOrganizerTest {

+    @Before

+    public void setUp() throws Exception {

+

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+    @Test

+    public void testMoveToTop() throws Exception {

+

+    }

+

+    @Test

+    public void testMoveAbove() throws Exception {

+

+    }

+

+    @Test

+    public void testMoveBeneath() throws Exception {

+

+    }

+

+    @Test

+    public void testMoveToBottom() throws Exception {

+        Object obj1 = new Object();

+        Object obj2 = new Object();

+        Object obj3 = new Object();

+        LinkedList list = new LinkedList();

+

+        list.add(obj1);

+        list.add(obj2);

+        list.add(obj3);

+

+        assertEquals(obj1, list.getFirst());

+        assertEquals(obj3, list.getLast());

+

+        ListOrganizer organizer = new ListOrganizer(list);

+

+        organizer.moveToBottom(obj3);

+

+        assertEquals(obj2, list.getLast());

+        assertEquals(obj3, list.getFirst());

+

+    }

+

+    @Test

+    public void testMoveUp() throws Exception {

+

+    }

+

+    @Test

+    public void testMoveDown() throws Exception {

+

+    }

+

+    @Test

+    public void testAddFirst() throws Exception {

+

+    }

+

+    @Test

+    public void testAddLast() throws Exception {

+

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/util/ValPixConverterTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/util/ValPixConverterTest.java
new file mode 100644
index 0000000..030e30c
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/util/ValPixConverterTest.java
@@ -0,0 +1,149 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.util;

+

+import org.junit.Ignore;

+import org.junit.Test;

+

+import static junit.framework.Assert.assertEquals;

+import static org.junit.Assert.fail;

+

+public class ValPixConverterTest {

+    @org.junit.Before

+    public void setUp() throws Exception {

+

+    }

+

+    @org.junit.After

+    public void tearDown() throws Exception {

+

+    }

+

+    /*

+    @org.junit.Test

+    public void testIndexToPix() throws Exception {

+        int sizeInPix = 100;

+        int itemCount = 10;

+        assertEquals(10.0f, ValPixConverter.indexToPix(1, itemCount, sizeInPix));

+

+        try {

+           ValPixConverter.indexToPix(100, 10, 100);

+            fail("IndexOutOfBoundsException expected.");

+        } catch(IndexOutOfBoundsException ex) {

+

+        }

+

+    }

+    */

+

+    @org.junit.Test

+    public void testValToPix() throws Exception {

+        int sizeInPix = 100;

+        int min = 0;

+        int max = 100;

+

+        int value = 50;

+

+        assertEquals(50.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));

+

+        // should be closer to the top:

+        // (remember that 0,0 is the top left pixel)

+        value = 75;

+        assertEquals(25.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));

+

+        // should be at the very top:

+        value = 100;

+        assertEquals(0.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));

+

+        // should be at the very top:

+        value = 0;

+        assertEquals(100.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));

+

+        // should be smack in the middle:

+        min = -100;

+        value = 0;

+        assertEquals(50.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));

+

+        // should be at the very bottom:

+        value = 100;

+        assertEquals(0.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));

+

+        // should be in the middle:

+        value = 0;

+        assertEquals(50.0f, ValPixConverter.valToPix(value, min, max, sizeInPix, true));

+

+        min = -100;

+        max = 100;

+        sizeInPix = 200;

+        assertEquals(0f, ValPixConverter.valToPix(-100, min, max, sizeInPix, false));

+        assertEquals(200f, ValPixConverter.valToPix(100, min, max, sizeInPix, false));

+    }

+

+    @org.junit.Test

+    public void testpixToVal() throws Exception {

+        int sizeInPix = 100;

+        int min = 0;

+        int max = 100;

+

+        double value = 50;

+

+        float pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);

+        assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));

+

+        value = 75;

+        pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);

+        assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));

+

+

+        min = -100;

+        value = 50;

+        pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);

+        assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));

+

+        min = -100;

+        value = 60;

+        pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);

+        assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));

+

+        min =  20;

+        value = 50;

+        pixel = ValPixConverter.valToPix(value, min, max, sizeInPix, true);

+        assertEquals(value, ValPixConverter.pixToVal(pixel, min, max, sizeInPix, true));

+

+        try {

+            ValPixConverter.pixToVal(-5, 0, 0, 0, true);

+            fail("IllegalArgumentException expected.");

+        } catch(IllegalArgumentException ex) {

+            

+        }

+        

+

+    }

+

+    

+    @Test

+    public void testValPerPix() {

+        //double result = ;

+        assertEquals(1.0, ValPixConverter.valPerPix(0, 100, 100));

+        double expected = 200d/100;

+        assertEquals(expected, ValPixConverter.valPerPix(100, 300, 100));

+        expected = 50d/100;

+        assertEquals(expected, ValPixConverter.valPerPix(0, 50, 100));

+        expected = 200d/100;

+        assertEquals(expected, ValPixConverter.valPerPix(-100, 100, 100));

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/RectRegionTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/RectRegionTest.java
new file mode 100644
index 0000000..9ae3877
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/RectRegionTest.java
@@ -0,0 +1,82 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertTrue;

+

+public class RectRegionTest {

+    @Before

+    public void setUp() throws Exception {

+

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+    @Test

+    public void testContainsPoint() throws Exception {

+

+    }

+

+    @Test

+    public void testContainsValue() throws Exception {

+

+    }

+

+    @Test

+    public void testContainsDomainValue() throws Exception {

+

+    }

+

+    @Test

+    public void testContainsRangeValue() throws Exception {

+

+    }

+

+

+

+    @Test

+    public void testIsWithin() throws Exception {

+

+        RectRegion region1 = new RectRegion(0, 100, 0, 100, "");

+

+        RectRegion region2 = new RectRegion(5, 10, 5, 10, "");

+

+        assertTrue(region1.intersects(region2));

+        assertTrue(region2.intersects(region1));

+

+        RectRegion region3 = new RectRegion(101, 200, 101, 200, "");

+        assertFalse(region1.intersects(region3));

+        assertFalse(region3.intersects(region1));

+

+        RectRegion region4 = new RectRegion(99, 109, 99, 109, "");

+        assertTrue(region1.intersects(region4));

+        assertTrue(region4.intersects(region1));

+

+        // negative numbers:

+        RectRegion region5 = new RectRegion(-100, 1, -100, 1, "");

+        assertTrue(region1.intersects(region5));

+        assertTrue(region5.intersects(region1));

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/SimpleXYSeriesTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/SimpleXYSeriesTest.java
new file mode 100644
index 0000000..3179f6d
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/SimpleXYSeriesTest.java
@@ -0,0 +1,134 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.util.Pair;

+import mockit.UsingMocksAndStubs;

+import org.junit.Test;

+

+import java.util.Arrays;

+

+import static junit.framework.Assert.assertEquals;

+

+@UsingMocksAndStubs({Pair.class})

+

+public class SimpleXYSeriesTest {

+

+    @Test

+    public void testYValsOnlyConstructor() throws Exception {

+        Number[] yVals = {5, 6, 7, 8, 9};

+        SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(yVals), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "test");

+

+        assertEquals(yVals[0], series.getY(0));

+        assertEquals(yVals[1], series.getY(1));

+        assertEquals(yVals[2], series.getY(2));

+        assertEquals(yVals[3], series.getY(3));

+        assertEquals(yVals[4], series.getY(4));

+

+        assertEquals(0, series.getX(0));

+        assertEquals(1, series.getX(1));

+        assertEquals(2, series.getX(2));

+        assertEquals(3, series.getX(3));

+        assertEquals(4, series.getX(4));

+    }

+

+    @Test

+    public void testXYInterleavedConstructor() throws Exception {

+        Number[] yVals = {55, 5, 66, 6, 77, 7, 88, 8, 99, 9};

+        SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(yVals), SimpleXYSeries.ArrayFormat.XY_VALS_INTERLEAVED, "test");

+

+        assertEquals(5, series.getY(0));

+        assertEquals(6, series.getY(1));

+        assertEquals(7, series.getY(2));

+        assertEquals(8, series.getY(3));

+        assertEquals(9, series.getY(4));

+

+        assertEquals(55, series.getX(0));

+        assertEquals(66, series.getX(1));

+        assertEquals(77, series.getX(2));

+        assertEquals(88, series.getX(3));

+        assertEquals(99, series.getX(4));

+    }

+

+    @Test

+    public void testTwoListConstructor() throws Exception {

+        Number[] yVals = {5, 6, 7, 8, 9};

+        Number[] xVals = {1, 2, 3, 4, 5};

+        SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(xVals), Arrays.asList(yVals), "test");

+

+        assertEquals(5, series.getY(0));

+        assertEquals(6, series.getY(1));

+        assertEquals(7, series.getY(2));

+        assertEquals(8, series.getY(3));

+        assertEquals(9, series.getY(4));

+

+        assertEquals(1, series.getX(0));

+        assertEquals(2, series.getX(1));

+        assertEquals(3, series.getX(2));

+        assertEquals(4, series.getX(3));

+        assertEquals(5, series.getX(4));

+    }

+

+    @Test

+    public void testPushPopStuff() throws Exception {

+        Number[] yVals = {5, 6, 7, 8, 9};

+        Number[] xVals = {1, 2, 3, 4, 5};

+        SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(xVals), Arrays.asList(yVals), "test");

+

+        // chop off the tail:

+        series.removeLast();

+        assertEquals(8, series.getY(series.size()-1));

+        assertEquals(4, series.getX(series.size()-1));

+

+        // chop off the head:

+        series.removeFirst();

+        assertEquals(6, series.getY(0));

+        assertEquals(2, series.getX(0));

+

+        // add to the tail:

+        series.addLast(22, 33);

+        assertEquals(33, series.getY(series.size()-1));

+        assertEquals(22, series.getX(series.size()-1));

+

+        // add to the head:

+        series.addFirst(55, 66);

+        assertEquals(66, series.getY(0));

+        assertEquals(55, series.getX(0));

+    }

+

+    @Test

+    public void testSet() throws Exception {

+        Number[] yVals = {5, 6, 7, 8, 9};

+        Number[] xVals = {1, 2, 3, 4, 5};

+        SimpleXYSeries series = new SimpleXYSeries(Arrays.asList(xVals), Arrays.asList(yVals), "test");

+

+        int size = series.size();

+

+        series.setX(22, 2);

+        assertEquals(22, series.getX(2));

+

+        // make sure size has not changed:

+        assertEquals(size, series.size());

+

+        series.setY(23, 2);

+        assertEquals(23, series.getY(2));

+

+        // make sure size has not changed:

+        assertEquals(size, series.size());

+    }

+

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYLegendWidgetTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYLegendWidgetTest.java
new file mode 100644
index 0000000..35f091d
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYLegendWidgetTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.xy;
+
+import android.content.Context;
+import android.graphics.*;
+import android.os.Handler;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import com.androidplot.Plot;
+import com.androidplot.mock.*;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.SizeMetric;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.ui.widget.Widget;
+import com.androidplot.util.FontUtils;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.util.ValPixConverter;
+import mockit.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@UsingMocksAndStubs({Log.class,View.class,Handler.class,Paint.class,Color.class, Rect.class,
+        FontUtils.class, Paint.FontMetrics.class,Bitmap.class, Pair.class})
+
+public class XYLegendWidgetTest {
+
+    @MockClass(realClass = Context.class)
+    public static class MockContext {}
+
+    @MockClass(realClass = TextLabelWidget.class)
+    public static class MockTextLabelWidget {
+        @Mock
+        public void doOnDraw(Canvas canvas, RectF widgetRect) {}
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        Mockit.setUpMocks(MockCanvas.class, MockRectF.class, MockSizeMetrics.class,
+                MockPointF.class, MockLooper.class, MockPixelUtils.class, MockTextLabelWidget.class);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+
+    }
+
+
+    @Test
+    public void testDoOnDraw() throws Exception {
+        Context context = Mockit.setUpMock(new MockContext());
+        XYPlot plot = new XYPlot(context, "Test", Plot.RenderMode.USE_MAIN_THREAD);
+
+        SimpleXYSeries s1 = new SimpleXYSeries((Arrays.asList(1, 2, 3)),
+                SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "s1");
+
+        plot.addSeries(s1, new LineAndPointFormatter(
+                Color.RED, Color.GREEN, Color.BLUE, (PointLabelFormatter)null));
+
+        assertEquals(1, plot.getSeriesSet().size());
+
+        Deencapsulation.invoke(plot, "onSizeChanged", 100, 100, 100, 100);
+        plot.redraw();
+        // have to manually invoke this because the invalidate()
+        // invoked by redraw() is a stub and will not result in onDraw being called.
+        Deencapsulation.invoke(plot, "onDraw", new Canvas());
+
+        plot.removeSeries(s1);
+        assertEquals(0, plot.getSeriesSet().size());
+        plot.addSeries(s1, new BarFormatter(Color.RED, Color.GREEN));
+        plot.redraw();
+
+        // throws NullPointerException before fix
+        // for ANDROIDPLOT-166 was applied.
+        Deencapsulation.invoke(plot, "onDraw", new Canvas());
+
+    }
+
+}
diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYPlotTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYPlotTest.java
new file mode 100644
index 0000000..04d5945
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYPlotTest.java
@@ -0,0 +1,478 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import android.graphics.*;

+import android.os.Handler;

+import android.util.Log;

+import android.view.View;

+import com.androidplot.Plot;

+import com.androidplot.PlotTest;

+import com.androidplot.mock.MockContext;

+import com.androidplot.mock.MockPaint;

+import com.androidplot.ui.widget.TextLabelWidget;

+import com.androidplot.util.Configurator;

+import com.androidplot.util.FontUtils;

+import com.androidplot.util.PixelUtils;

+import mockit.*;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+

+import java.lang.reflect.Method;

+import java.util.Arrays;

+import java.util.HashMap;

+import java.util.List;

+

+import static junit.framework.Assert.assertEquals;

+

+@UsingMocksAndStubs({Log.class,View.class,Context.class,Handler.class,Paint.class,Color.class,

+        Rect.class, RectF.class,FontUtils.class, PixelUtils.class, Canvas.class})

+

+public class XYPlotTest {

+

+    XYPlot plot;  // testing

+    

+    List<Integer> numList1;

+    List<Integer> numList2;

+    SimpleXYSeries series1;

+

+    @Before

+    public void setUp() throws Exception {

+        Mockit.setUpMocks(MockPaint.class,MockContext.class);

+        new MockUp<View>() {

+            @Mock int getWidth() { return 100;}

+            @Mock int getHeight() { return 100;}

+

+        };

+

+        plot = new XYPlot(null, "test");

+        numList1 = Arrays.asList(0, 1, 3, 5, 10, 15, 25, 50, 75, 100); // 10 elements

+        numList2 = Arrays.asList(-100, 0, 1, 3, 5, 10, 15, 25, 50, 75, 100, 200); // 12 elements

+        series1 = new SimpleXYSeries(numList1, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "");

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+    @Test

+    public void testOriginFixedMode() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.centerOnDomainOrigin(5, 2, BoundaryMode.FIXED);

+        plot.calculateMinMaxVals();

+

+

+        assertEquals(3.0, plot.getCalculatedMinX());

+        assertEquals(7.0, plot.getCalculatedMaxX());

+    }

+

+    @Test

+    public void testOriginAutoMode() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.centerOnDomainOrigin(5);

+        plot.calculateMinMaxVals();

+        //plot.updateMinMaxVals();

+

+        assertEquals(10.0, plot.getCalculatedMaxX()); // symmetry is @ 10, not 9

+        assertEquals(0.0, plot.getCalculatedMinX());

+

+        plot.centerOnRangeOrigin(50);

+        plot.calculateMinMaxVals();

+

+        assertEquals(100.0, plot.getCalculatedMaxY());

+        assertEquals(0.0, plot.getCalculatedMinY());

+

+    }

+

+    @Test

+    public void testOriginGrowMode() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.centerOnDomainOrigin(5, null, BoundaryMode.GROW);

+        plot.calculateMinMaxVals();

+

+        assertEquals(0.0, plot.getCalculatedMinX());

+        assertEquals(10.0, plot.getCalculatedMaxX());

+

+        // introduce a larger domain set.  boundaries should change

+        series1.setModel(numList2, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        assertEquals(-1.0, plot.getCalculatedMinX());

+        assertEquals(11.0, plot.getCalculatedMaxX());

+

+        // revert series model back to the previous set.  boundaries should remain the same

+        series1.setModel(numList1, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        assertEquals(-1.0, plot.getCalculatedMinX());

+        assertEquals(11.0, plot.getCalculatedMaxX());

+    }

+

+    @Test

+    public void testOriginShrinkMode() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.centerOnDomainOrigin(5, null, BoundaryMode.SHRINNK);

+        plot.calculateMinMaxVals();

+

+        assertEquals(0.0, plot.getCalculatedMinX());

+        assertEquals(10.0, plot.getCalculatedMaxX());

+

+        // update with more extreme values...nothing should change in shrink mode:

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+

+        assertEquals(0.0, plot.getCalculatedMinX());

+        assertEquals(10.0, plot.getCalculatedMaxX());

+                

+    }

+

+

+ // Ifor not sure about filling in test stubs just going to do my own stuff instead.

+    @Test

+    public void testsetDomainBoundaries() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.calculateMinMaxVals();

+

+        // default to auto so check them

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+

+        plot.setDomainBoundaries(2, BoundaryMode.FIXED, 8, BoundaryMode.FIXED);

+        plot.calculateMinMaxVals();

+

+        // fixed

+        assertEquals(2, plot.getCalculatedMinX());

+        assertEquals(8, plot.getCalculatedMaxX());

+

+        // back to auto

+        plot.setDomainBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);

+        plot.calculateMinMaxVals();

+

+        // check again

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+        

+        // we are not testing MinY well with this dataset.

+        // try grow

+        plot.setDomainBoundaries(2, BoundaryMode.GROW, 8, BoundaryMode.GROW);

+        plot.calculateMinMaxVals();

+

+        // check inital

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+        

+        // update with more extreme values...

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        // after growing

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(11, plot.getCalculatedMaxX());

+

+        // back to previous

+        series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+        

+        // should not of changed.

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(11, plot.getCalculatedMaxX());

+

+        // back to big

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        

+        plot.setDomainBoundaries(2, BoundaryMode.SHRINNK, 8, BoundaryMode.SHRINNK);

+        plot.calculateMinMaxVals();

+

+        // check inital

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(11, plot.getCalculatedMaxX());

+        

+        // now small

+        series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        // after shrinking

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+

+        // back to previous

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+        

+        // should not of changed.

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+

+        // back to auto

+        plot.setDomainBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);

+        plot.calculateMinMaxVals();

+        

+        // should of changed.

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(11, plot.getCalculatedMaxX());

+    }

+    

+    @Test

+    public void testsetRangeBoundaries() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.calculateMinMaxVals();

+

+        // default to auto so check them

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+

+        plot.setRangeBoundaries(5, BoundaryMode.FIXED, 80, BoundaryMode.FIXED);

+        plot.calculateMinMaxVals();

+

+        // fixed

+        assertEquals(5, plot.getCalculatedMinY());

+        assertEquals(80, plot.getCalculatedMaxY());

+

+        // back to auto

+        plot.setRangeBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);

+        plot.calculateMinMaxVals();

+

+        // check again

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+        

+        // try grow

+        plot.setRangeBoundaries(2, BoundaryMode.GROW, 8, BoundaryMode.GROW);

+        plot.calculateMinMaxVals();

+

+        // check inital

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+        

+        // update with more extreme values...

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        // after growing

+        assertEquals(-100, plot.getCalculatedMinY());

+        assertEquals(200, plot.getCalculatedMaxY());

+

+        // back to previous

+        series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+        

+        // should not of changed.

+        assertEquals(-100, plot.getCalculatedMinY());

+        assertEquals(200, plot.getCalculatedMaxY());

+

+        // back to big

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        

+        plot.setRangeBoundaries(2, BoundaryMode.SHRINNK, 8, BoundaryMode.SHRINNK);

+        plot.calculateMinMaxVals();

+

+        // check inital

+        assertEquals(-100, plot.getCalculatedMinY());

+        assertEquals(200, plot.getCalculatedMaxY());

+        

+        // now small

+        series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        // after shrinking

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+

+        // back to previous

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+        

+        // should not of changed.

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+

+        // back to auto

+        plot.setRangeBoundaries(2, BoundaryMode.AUTO, 8, BoundaryMode.AUTO);

+        plot.calculateMinMaxVals();

+        

+        // should of changed.

+        assertEquals(-100, plot.getCalculatedMinY());

+        assertEquals(200, plot.getCalculatedMaxY());

+    }

+    

+    @Test

+    public void testSetDomainRightMinMax() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.calculateMinMaxVals();

+

+        // default to auto so check them

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+        

+        plot.setDomainRightMax(10);

+        plot.calculateMinMaxVals();

+

+        // same values.

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        // on RightMax

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(10, plot.getCalculatedMaxX());

+

+        plot.setDomainRightMax(null);

+        plot.calculateMinMaxVals();

+

+        // back to full

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(11, plot.getCalculatedMaxX());

+        

+        // now the RightMin

+        plot.setDomainRightMin(10);

+        plot.calculateMinMaxVals();

+

+        // still to full

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(11, plot.getCalculatedMaxX());

+

+        // small list

+        series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+        

+        // on RightMin

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(10, plot.getCalculatedMaxX());

+

+        // now off again

+        plot.setDomainRightMin(null);

+        plot.calculateMinMaxVals();

+

+        // small values.

+        assertEquals(0, plot.getCalculatedMinX());

+        assertEquals(9, plot.getCalculatedMaxX());

+    }

+    

+    @Test

+    public void testSetRangeTopBottomMinMax() throws Exception {

+        plot.addSeries(series1, new LineAndPointFormatter());

+        plot.calculateMinMaxVals();

+

+        // default to auto so check them

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+        

+        plot.setRangeTopMax(110);

+        plot.setRangeBottomMin(-50);

+        plot.calculateMinMaxVals();

+

+        // same values.

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+

+        series1.setModel(numList2,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+

+        // on Limits

+        assertEquals(-50, plot.getCalculatedMinY());

+        assertEquals(110, plot.getCalculatedMaxY());

+

+        plot.setRangeTopMax(null);

+        plot.setRangeBottomMin(null);

+        plot.calculateMinMaxVals();

+

+        // back to full

+        assertEquals(-100, plot.getCalculatedMinY());

+        assertEquals(200, plot.getCalculatedMaxY());

+        

+        // now the Min

+        plot.setRangeTopMin(150);

+        plot.setRangeBottomMax(-60);

+        plot.calculateMinMaxVals();

+

+        // still to full

+        assertEquals(-100, plot.getCalculatedMinY());

+        assertEquals(200, plot.getCalculatedMaxY());

+

+        // small list

+        series1.setModel(numList1,SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+        plot.calculateMinMaxVals();

+        

+        // on Limits

+        assertEquals(-60, plot.getCalculatedMinY());

+        assertEquals(150, plot.getCalculatedMaxY());

+

+        // now off again

+        plot.setRangeTopMin(null);

+        plot.setRangeBottomMax(null);

+        plot.calculateMinMaxVals();

+

+        // small values.

+        assertEquals(0, plot.getCalculatedMinY());

+        assertEquals(100, plot.getCalculatedMaxY());

+    }

+    

+    @Test

+    public void testSetDomainUpperBoundary() throws Exception {

+

+    }

+

+    @Test

+    public void testSetDomainLowerBoundary() throws Exception {

+

+    }

+

+    @Test

+    public void testSetRangeUpperBoundary() throws Exception {

+

+    }

+

+    @Test

+    public void testSetRangeLowerBoundary() throws Exception {

+

+    }

+

+    @Test

+    public void testSetDomainOrigin() throws Exception {

+

+    }

+

+    @Test

+    public void testSetRangeOrigin() throws Exception {

+

+    }

+

+    @Test

+    public void testConfigure() throws Exception {

+        //Context context = Mockit.setUpMock(new MockContext());

+        Context context = new MockContext.MockContext2();

+        HashMap<String, String> params = new HashMap<String, String>();

+        String param1 = "this is a test.";

+        String param2 = Plot.RenderMode.USE_BACKGROUND_THREAD.toString();

+        String param3 = "#FF0000";

+        params.put("title", param1);

+        params.put("renderMode", param2);

+        params.put("backgroundPaint.color", param3);

+        params.put("graphWidget.domainLabelPaint.color", param3);

+

+        Configurator.configure(context, plot, params);

+        assertEquals(param1, plot.getTitle());

+        assertEquals(Plot.RenderMode.USE_BACKGROUND_THREAD, plot.getRenderMode());

+        assertEquals(Color.parseColor(param3), plot.getBackgroundPaint().getColor());

+        assertEquals(Color.parseColor(param3), plot.getGraphWidget().getDomainLabelPaint().getColor());

+    }

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYSeriesRendererTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYSeriesRendererTest.java
new file mode 100644
index 0000000..c412e08
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYSeriesRendererTest.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import android.content.Context;

+import android.graphics.*;

+import android.os.Handler;

+import android.util.Log;

+import android.view.View;

+import com.androidplot.util.FontUtils;

+import com.androidplot.util.PixelUtils;

+import com.androidplot.util.ValPixConverter;

+import mockit.*;

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import static junit.framework.Assert.assertEquals;

+

+@UsingMocksAndStubs({Log.class,View.class,Handler.class,Paint.class,Color.class, Rect.class, RectF.class,

+        FontUtils.class, PixelUtils.class, Canvas.class})

+

+public class XYSeriesRendererTest {

+

+    @MockClass(realClass = Context.class)

+    public static class MockContext {}

+

+    @Before

+    public void setUp() throws Exception {

+

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+

+    @Test

+    public void testDataToGridCorrelation() throws Exception {

+        Context context = Mockit.setUpMock(new MockContext());

+        //RectF gridRect = Mockit.setUpMock(new MockRectF());

+        //Mockit.setUpMock(RectF.class, new MockRectF());

+        //@MockClass(realClass = RectF.class)

+        new MockUp<RectF>()

+        {

+            final float left = 5;

+            final float top = 5;

+            final float right = 104;

+            final float bottom = 104;

+

+            @Mock void $init() {}

+

+            @Mock float width() {return 100;}

+            @Mock float height() {return 100;}

+

+        };

+        RectF gridRect = new RectF();

+        XYPlot plot = new XYPlot(context, "Test");

+        plot.setDomainStepMode(XYStepMode.SUBDIVIDE);

+        plot.setDomainStepValue(10);

+        plot.setDomainBoundaries(0, 100, BoundaryMode.FIXED);

+        plot.setRangeBoundaries(0, 100, BoundaryMode.FIXED);

+        plot.calculateMinMaxVals();

+        XYStep domainStep = XYStepCalculator.getStep(plot, XYAxisType.DOMAIN, gridRect, plot.getCalculatedMinX().doubleValue(), plot.getCalculatedMaxX().doubleValue());

+

+        float width = gridRect.width();

+

+        int x = 0;

+        float val = ValPixConverter.valToPix(x, 0, 9, gridRect.width(), false) + (gridRect.left);

+

+        assertEquals(val, domainStep.getStepPix()*x);

+

+        x = 1;

+        val = ValPixConverter.valToPix(x, 0, 9, gridRect.width(), false) + (gridRect.left);

+

+        assertEquals(val, domainStep.getStepPix()*x);

+

+        x = 9;

+        val = ValPixConverter.valToPix(x, 0, 9, gridRect.width(), false) + (gridRect.left);

+

+        assertEquals(val, domainStep.getStepPix()*x);

+    }

+

+}

diff --git a/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYStepCalculatorTest.java b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYStepCalculatorTest.java
new file mode 100644
index 0000000..1f93977
--- /dev/null
+++ b/AndroidPlot-Core/src/test/java/com/androidplot/xy/XYStepCalculatorTest.java
@@ -0,0 +1,69 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.xy;

+

+import org.junit.After;

+import org.junit.Before;

+import org.junit.Test;

+

+import static junit.framework.Assert.assertEquals;

+

+public class XYStepCalculatorTest {

+    @Before

+    public void setUp() throws Exception {

+

+    }

+

+    @After

+    public void tearDown() throws Exception {

+

+    }

+

+    @Test

+    public void testGetStep() throws Exception {

+

+    }

+

+    @Test

+    public void testSubdivide() throws Exception {

+        int numSegments = 10;

+        float plotSize = 100;

+        double minVal = 0;

+        double maxVal = 100;

+        XYStep step = XYStepCalculator.getStep(XYStepMode.SUBDIVIDE, plotSize, numSegments, minVal, maxVal);

+

+        assertEquals(plotSize/(numSegments-1), step.getStepPix());

+        //assertEquals(10, step.getStepVal());

+

+        // make sure large values dont break anything:

+        minVal = 1000000000;

+        maxVal = 2000000000;

+        step = XYStepCalculator.getStep(XYStepMode.SUBDIVIDE, plotSize, numSegments, minVal, maxVal);

+        assertEquals(plotSize/(numSegments-1), step.getStepPix());

+

+    }

+    

+    @Test

+    public void testIncrementByVal() throws Exception {

+

+    }

+

+    @Test

+    public void testIncrementByPixels() throws Exception {

+

+    }

+}

diff --git a/DynamicXYPlotExample.iml b/DynamicXYPlotExample.iml
new file mode 100644
index 0000000..cde54cf
--- /dev/null
+++ b/DynamicXYPlotExample.iml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">

+  <component name="FacetManager">

+    <facet type="android" name="Android">

+      <configuration>

+        <option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/target/generated-sources/r" />

+        <option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/target/generated-sources/aidl" />

+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/AndroidManifest.xml" />

+        <option name="RES_FOLDER_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/res" />

+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/assets" />

+        <option name="LIBS_FOLDER_RELATIVE_PATH" value="/Examples/DynamicXYPlotExample/libs" />

+        <option name="REGENERATE_R_JAVA" value="true" />

+        <option name="REGENERATE_JAVA_BY_AIDL" value="true" />

+        <option name="USE_CUSTOM_APK_RESOURCE_FOLDER" value="false" />

+        <option name="CUSTOM_APK_RESOURCE_FOLDER" value="" />

+        <option name="USE_CUSTOM_COMPILER_MANIFEST" value="false" />

+        <option name="CUSTOM_COMPILER_MANIFEST" value="" />

+        <option name="APK_PATH" value="/target/AndroidPlot.apk" />

+        <option name="LIBRARY_PROJECT" value="false" />

+        <option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="true" />

+        <option name="GENERATE_UNSIGNED_APK" value="false" />

+      </configuration>

+    </facet>

+  </component>

+  <component name="NewModuleRootManager" inherit-compiler-output="true">

+    <output url="file://$MODULE_DIR$/Examples/DynamicXYPlotExample" />

+    <output-test url="file://$MODULE_DIR$/target/test-classes" />

+    <content url="file://$MODULE_DIR$">

+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />

+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />

+      <excludeFolder url="file://$MODULE_DIR$/target" />

+    </content>

+    <orderEntry type="inheritedJdk" />

+    <orderEntry type="sourceFolder" forTests="false" />

+  </component>

+</module>

+

diff --git a/Examples/AndroidPlot-Examples.iml b/Examples/AndroidPlot-Examples.iml
new file mode 100644
index 0000000..0cfe50e
--- /dev/null
+++ b/Examples/AndroidPlot-Examples.iml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<module type="JAVA_MODULE" version="4">

+  <component name="NewModuleRootManager" inherit-compiler-output="false">

+    <output url="file://$MODULE_DIR$/target/classes" />

+    <output-test url="file://$MODULE_DIR$/target/test-classes" />

+    <content url="file://$MODULE_DIR$">

+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />

+      <excludeFolder url="file://$MODULE_DIR$/target" />

+    </content>

+    <orderEntry type="inheritedJdk" />

+    <orderEntry type="sourceFolder" forTests="false" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.1.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpclient:4.0.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpcore:4.0.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: commons-codec:commons-codec:1.3" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: xpp3:xpp3:1.1.4c" level="project" />

+  </component>

+</module>

+

diff --git a/Examples/DemoApp/.classpath b/Examples/DemoApp/.classpath
new file mode 100644
index 0000000..c64ee78
--- /dev/null
+++ b/Examples/DemoApp/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry kind="src" output="bin/classes" path="src"/>
+	<classpathentry kind="src" output="bin/classes" path="gen"/>
+	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/AndroidPlot-Core"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/Examples/DemoApp/.project b/Examples/DemoApp/.project
new file mode 100644
index 0000000..185fcd4
--- /dev/null
+++ b/Examples/DemoApp/.project
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>AndroidPlotDemo</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>
diff --git a/Examples/DemoApp/AndroidManifest.xml b/Examples/DemoApp/AndroidManifest.xml
new file mode 100644
index 0000000..765336b
--- /dev/null
+++ b/Examples/DemoApp/AndroidManifest.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>

+<manifest xmlns:android="http://schemas.android.com/apk/res/android"

+      package="com.androidplot.demos"

+      android:versionCode="8"

+      android:versionName="0.6.0.1">

+    <uses-sdk android:minSdkVersion="6" android:targetSdkVersion="16"/>

+

+    <!-- We must disable hardware acceleration otherwise some lines will not appear in our plots. -->

+    <application android:label="AndroidPlot API DemoApp"

+                 android:icon="@drawable/icon">

+        <activity android:name=".MainActivity"

+                  android:label="@string/app_name">

+            <intent-filter>

+                <action android:name="android.intent.action.MAIN" />

+                <category android:name="android.intent.category.LAUNCHER" />

+            </intent-filter>

+        </activity>

+        <activity android:name=".SimplePieChartActivity"

+                  android:label="@string/app_name">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER"/>

+            </intent-filter>

+        </activity>

+        <activity android:name=".SimpleXYPlotActivity"

+                  android:label="@string/app_name">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER" />

+            </intent-filter>

+        </activity>

+        <activity android:name=".BarPlotExampleActivity"

+                  android:label="@string/app_name">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER"/>

+            </intent-filter>

+        </activity>

+        <activity android:name=".DynamicXYPlotActivity"

+                  android:label="@string/app_name">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER"/>

+            </intent-filter>

+        </activity>

+        <activity android:name=".OrientationSensorExampleActivity"

+                  android:label="@string/app_name"

+                  android:screenOrientation="landscape">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER" />

+            </intent-filter>

+        </activity>

+        <activity android:name=".StepChartExampleActivity"

+                  android:label="@string/app_name">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER" />

+            </intent-filter>

+        </activity>

+        <activity android:name=".TouchZoomExampleActivity"

+                  android:label="@string/app_name">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER" />

+            </intent-filter>

+        </activity>

+        <activity android:name=".ListViewActivity" android:label="ListView Example"/>

+        <activity android:name=".XYRegionExampleActivity" android:label="XYRegion Example">

+            <intent-filter>

+                <category android:name="android.intent.category.LAUNCHER" />

+            </intent-filter>

+        </activity>

+        <activity android:name=".TimeSeriesActivity"/>

+		<activity android:name=".DualScaleXYPlotExampleActivity"/>

+        <activity android:name=".XYPlotWithBgImgActivity"/>

+        

+        <!-- receiver for demo app widget -->

+        <receiver

+                android:icon="@drawable/icon"

+                android:label="Example Widget"

+                android:name="com.androidplot.demos.widget.DemoAppWidgetProvider" >

+            <intent-filter >

+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

+            </intent-filter>

+

+            <meta-data

+                    android:name="android.appwidget.provider"

+                    android:resource="@xml/demo_app_widget_provider_info" />

+        </receiver>

+    </application>

+</manifest> 

diff --git a/Examples/DemoApp/DemoApp.iml b/Examples/DemoApp/DemoApp.iml
new file mode 100644
index 0000000..c4b2db2
--- /dev/null
+++ b/Examples/DemoApp/DemoApp.iml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">

+  <component name="FacetManager">

+    <facet type="android" name="Android">

+      <configuration>

+        <option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="false" />

+        <includeSystemProguardFile>false</includeSystemProguardFile>

+        <includeAssetsFromLibraries>true</includeAssetsFromLibraries>

+        <resOverlayFolders />

+      </configuration>

+    </facet>

+  </component>

+  <component name="NewModuleRootManager" inherit-compiler-output="false">

+    <output url="file://$MODULE_DIR$/target/classes" />

+    <output-test url="file://$MODULE_DIR$/target/test-classes" />

+    <content url="file://$MODULE_DIR$">

+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />

+      <sourceFolder url="file://$MODULE_DIR$/target/generated-sources/r" isTestSource="false" />

+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />

+      <excludeFolder url="file://$MODULE_DIR$/target/classes" />

+      <excludeFolder url="file://$MODULE_DIR$/target/generated-sources/combined-assets" />

+      <excludeFolder url="file://$MODULE_DIR$/target/generated-sources/combined-resources" />

+      <excludeFolder url="file://$MODULE_DIR$/target/generated-sources/extracted-dependencies" />

+    </content>

+    <orderEntry type="sourceFolder" forTests="false" />

+    <orderEntry type="inheritedJdk" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.android:android:4.1.1.4" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.1.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpclient:4.0.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.httpcomponents:httpcore:4.0.1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: commons-codec:commons-codec:1.3" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.khronos:opengl-api:gl1.1-android-2.1_r1" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: xpp3:xpp3:1.1.4c" level="project" />

+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.json:json:20080701" level="project" />

+    <orderEntry type="module" module-name="androidplot-core" />

+  </component>

+</module>

+

diff --git a/Examples/DemoApp/ant.properties b/Examples/DemoApp/ant.properties
new file mode 100644
index 0000000..87c820a
--- /dev/null
+++ b/Examples/DemoApp/ant.properties
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2012 AndroidPlot.com. All rights reserved.
+#
+# Redistribution and use of source without modification and derived binaries with or without modification,
+# are permitted provided that the following conditions are met:
+#
+#    1. Redistributions of source code must retain the above copyright notice, this list of
+#       conditions and the following disclaimer.
+#
+#    2. Redistributions in binary form must reproduce the above copyright notice, this list
+#       of conditions and the following disclaimer in the documentation and/or other materials
+#       provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY ANDROIDPLOT.COM ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDROIDPLOT.COM OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are those of the
+# authors and should not be interpreted as representing official policies, either expressed
+# or implied, of AndroidPlot.com.
+#
+
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked into Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+#  'source.dir' for the location of your java source folder and
+#  'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+#  'key.store' for the location of your keystore and
+#  'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+jar.libs.dir=..\\..\\AndroidPlot-Core\\target\\obfuscated
+
diff --git a/Examples/DemoApp/build.xml b/Examples/DemoApp/build.xml
new file mode 100644
index 0000000..45987d1
--- /dev/null
+++ b/Examples/DemoApp/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project name="testapp" default="help">

+

+    <!-- The local.properties file is created and updated by the 'android' tool.

+         It contains the path to the SDK. It should *NOT* be checked into

+         Version Control Systems. -->

+    <property file="local.properties" />

+

+    <!-- The ant.properties file can be created by you. It is only edited by the

+         'android' tool to add properties to it.

+         This is the place to change some Ant specific build properties.

+         Here are some properties you may want to change/update:

+

+         source.dir

+             The name of the source directory. Default is 'src'.

+         out.dir

+             The name of the output directory. Default is 'bin'.

+

+         For other overridable properties, look at the beginning of the rules

+         files in the SDK, at tools/ant/build.xml

+

+         Properties related to the SDK location or the project target should

+         be updated using the 'android' tool with the 'update' action.

+

+         This file is an integral part of the build system for your

+         application and should be checked into Version Control Systems.

+

+         -->

+    <property file="ant.properties" />

+

+    <!-- if sdk.dir was not set from one of the property file, then

+         get it from the ANDROID_HOME env var.

+         This must be done before we load project.properties since

+         the proguard config can use sdk.dir -->

+    <property environment="env" />

+    <condition property="sdk.dir" value="${env.ANDROID_HOME}">

+        <isset property="env.ANDROID_HOME" />

+    </condition>

+

+    <!-- The project.properties file is created and updated by the 'android'

+         tool, as well as ADT.

+

+         This contains project specific properties such as project target, and library

+         dependencies. Lower level build properties are stored in ant.properties

+         (or in .classpath for Eclipse projects).

+

+         This file is an integral part of the build system for your

+         application and should be checked into Version Control Systems. -->

+    <loadproperties srcFile="project.properties" />

+

+    <!-- quick check on sdk.dir -->

+    <fail

+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."

+            unless="sdk.dir"

+            />

+

+    <!--

+        Import per project custom build rules if present at the root of the project.

+        This is the place to put custom intermediary targets such as:

+            -pre-build

+            -pre-compile

+            -post-compile (This is typically used for code obfuscation.

+                           Compiled code location: ${out.classes.absolute.dir}

+                           If this is not done in place, override ${out.dex.input.absolute.dir})

+            -post-package

+            -post-build

+            -pre-clean

+    -->

+    <import file="custom_rules.xml" optional="true" />

+

+    <!-- Import the actual build file.

+

+         To customize existing targets, there are two options:

+         - Customize only one target:

+             - copy/paste the target into this file, *before* the

+               <import> task.

+             - customize it to your needs.

+         - Customize the whole content of build.xml

+             - copy/paste the content of the rules files (minus the top node)

+               into this file, replacing the <import> task.

+             - customize to your needs.

+

+         ***********************

+         ****** IMPORTANT ******

+         ***********************

+         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,

+         in order to avoid having your file be overridden by tools such as "android update project"

+    -->

+    <!-- version-tag: 1 -->

+    <import file="${sdk.dir}/tools/ant/build.xml" />

+

+</project>

diff --git a/Examples/DemoApp/pom.xml b/Examples/DemoApp/pom.xml
new file mode 100644
index 0000000..3ce1b3f
--- /dev/null
+++ b/Examples/DemoApp/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.androidplot</groupId>
+        <artifactId>androidplot</artifactId>
+        <version>0.6.0</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <groupId>com.androidplot</groupId>
+    <artifactId>androidplot-demoapp</artifactId>
+    <packaging>apk</packaging>
+    <name>AndroidPlot-DemoApp</name>
+    <!--<version>1.0-SNAPSHOT</version>-->
+
+    <dependencies>
+        <!--<dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+            <version>4.1.1.4</version>
+            <scope>provided</scope>
+        </dependency>-->
+        <dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.androidplot</groupId>
+            <artifactId>androidplot-core</artifactId>
+            <version>0.6.0</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <sourceDirectory>src</sourceDirectory>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+                    <artifactId>android-maven-plugin</artifactId>
+                    <version>3.6.0</version>
+                    <extensions>true</extensions>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+                <artifactId>android-maven-plugin</artifactId>
+                <configuration>
+                    <sdk>
+                        <!-- platform or api level (api level 4 = platform 1.6)-->
+                        <platform>16</platform>
+                    </sdk>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/Examples/DemoApp/proguard-project.txt b/Examples/DemoApp/proguard-project.txt
new file mode 100644
index 0000000..2c0da18
--- /dev/null
+++ b/Examples/DemoApp/proguard-project.txt
@@ -0,0 +1,26 @@
+# To enable ProGuard in your project, edit project.properties

+# to define the proguard.config property as described in that file.

+#

+# Add project specific ProGuard rules here.

+# By default, the flags in this file are appended to flags specified

+# in ${sdk.dir}/tools/proguard/proguard-android.txt

+# You can edit the include path and order by changing the ProGuard

+# include property in project.properties.

+#

+# For more details, see

+#   http://developer.android.com/guide/developing/tools/proguard.html

+

+# Add any project specific keep options here:

+

+# If your project uses WebView with JS, uncomment the following

+# and specify the fully qualified class name to the JavaScript interface

+# class:

+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {

+#   public *;

+#}

+

+# remove all debug and verbose level logging statements:

+-assumenosideeffects class android.util.Log {

+  public static *** d(...);

+  public static *** v(...);

+}

diff --git a/Examples/DemoApp/project.properties b/Examples/DemoApp/project.properties
new file mode 100644
index 0000000..29656f2
--- /dev/null
+++ b/Examples/DemoApp/project.properties
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2012 AndroidPlot.com. All rights reserved.
+#
+# Redistribution and use of source without modification and derived binaries with or without modification,
+# are permitted provided that the following conditions are met:
+#
+#    1. Redistributions of source code must retain the above copyright notice, this list of
+#       conditions and the following disclaimer.
+#
+#    2. Redistributions in binary form must reproduce the above copyright notice, this list
+#       of conditions and the following disclaimer in the documentation and/or other materials
+#       provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY ANDROIDPLOT.COM ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDROIDPLOT.COM OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are those of the
+# authors and should not be interpreted as representing official policies, either expressed
+# or implied, of AndroidPlot.com.
+#
+
+# 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 edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt
+# using config with optimization enabled so that things like log statement removal will work:
+proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
+# Project target.
+target=android-16
diff --git a/Examples/DemoApp/res/drawable/graph_background.png b/Examples/DemoApp/res/drawable/graph_background.png
new file mode 100755
index 0000000..47523c2
--- /dev/null
+++ b/Examples/DemoApp/res/drawable/graph_background.png
Binary files differ
diff --git a/Examples/DemoApp/res/drawable/icon.png b/Examples/DemoApp/res/drawable/icon.png
new file mode 100644
index 0000000..6557d3a
--- /dev/null
+++ b/Examples/DemoApp/res/drawable/icon.png
Binary files differ
diff --git a/Examples/DemoApp/res/layout/bar_plot_example.xml b/Examples/DemoApp/res/layout/bar_plot_example.xml
new file mode 100644
index 0000000..76ac666
--- /dev/null
+++ b/Examples/DemoApp/res/layout/bar_plot_example.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2012 AndroidPlot.com
+  ~
+  ~    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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              style="@style/sample_activity"
+              android:orientation="vertical">
+
+    <com.androidplot.xy.XYPlot
+            android:id="@+id/mySimpleXYPlot"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            androidPlot.title="Growth"
+            androidPlot.domainLabel="Month"
+            androidPlot.rangeLabel="Revenue (millions)"
+            androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+            androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+            androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+            androidPlot.graphWidget.marginTop="20dp"
+            androidPlot.graphWidget.marginLeft="15dp"
+            androidPlot.graphWidget.marginBottom="25dp"
+            androidPlot.graphWidget.marginRight="10dp"
+            androidPlot.graphWidget.gridBackgroundPaint.color="#000000"
+            androidPlot.graphWidget.domainGridLinePaint.alpha="0"
+            androidPlot.graphWidget.domainOriginLinePaint.alpha="0"
+            androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+            androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+            androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+            androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+            androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+            androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+            androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+            androidPlot.legendWidget.heightMetric.value="25dp"
+            androidPlot.legendWidget.positionMetrics.anchor="right_bottom"
+            android:layout_weight="1"
+            androidPlot.graphWidget.gridLinePaint.color="#000000"/>
+
+    <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="fill_parent"
+            android:layout_gravity="center"
+            android:orientation="horizontal">
+        <Spinner
+                android:id="@+id/spRenderStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+        <Spinner
+                android:id="@+id/spSeriesSize"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="fill_parent"
+            android:layout_gravity="center"
+            android:orientation="horizontal">
+        <Spinner
+                android:id="@+id/spWidthStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+        <RelativeLayout
+                android:id="@+id/sectionGraph"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content">
+            <SeekBar
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingLeft="10dp"
+                    android:paddingRight="10dp"
+                    android:max="300"
+                    android:progress="10"
+                    android:id="@+id/sbFixed"/>
+            <SeekBar
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingLeft="10dp"
+                    android:paddingRight="10dp"
+                    android:max="50"
+                    android:progress="1"
+                    android:id="@+id/sbVariable"/>
+        </RelativeLayout>
+    </LinearLayout>
+    <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center"
+            android:orientation="horizontal">
+        <CheckBox android:id="@+id/s1CheckBox"
+                  android:text="Series 1"
+                  android:checked="true"
+                  android:layout_height="wrap_content"
+                  android:layout_width="fill_parent"/>
+        <CheckBox android:id="@+id/s2CheckBox"
+                  android:text="Series 2"
+                  android:checked="true"
+                  android:layout_height="wrap_content"
+                  android:layout_width="fill_parent"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/demo_app_widget.xml b/Examples/DemoApp/res/layout/demo_app_widget.xml
new file mode 100644
index 0000000..817e0a4
--- /dev/null
+++ b/Examples/DemoApp/res/layout/demo_app_widget.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>

+

+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              style="@style/sample_activity"

+              android:orientation="vertical">

+

+    <ImageView

+            xmlns:android="http://schemas.android.com/apk/res/android"

+            android:id="@+id/imgView"

+            android:background="@android:drawable/alert_dark_frame"

+            android:scaleType="centerInside"

+            android:layout_width="fill_parent"

+            android:layout_height="fill_parent">

+    </ImageView>

+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/dual_scale_xy_plot_example.xml b/Examples/DemoApp/res/layout/dual_scale_xy_plot_example.xml
new file mode 100644
index 0000000..f21eac2
--- /dev/null
+++ b/Examples/DemoApp/res/layout/dual_scale_xy_plot_example.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              style="@style/sample_activity"

+              android:orientation="vertical">

+

+    <RelativeLayout 

+           android:id="@+id/sectionGraph" 

+	       android:layout_width="fill_parent"

+	       android:layout_height="fill_parent"

+	       android:layout_weight="1"

+	       android:layout_marginTop="0px"

+	       android:layout_marginBottom="0px"

+	       android:layout_marginLeft="0px"

+	       android:layout_marginRight="0px">

+	       

+				<com.androidplot.xy.XYPlot

+			    	android:id="@+id/mySimpleXYPlot_L"

+			        android:layout_width="fill_parent"

+			        android:layout_height="fill_parent"

+			        androidPlot.title="A Simple XY Plot"

+			        androidPlot.graphWidget.gridLinePaint.color="#000000"/>

+				<com.androidplot.xy.XYPlot

+			    	android:id="@+id/mySimpleXYPlot_R"

+			        android:layout_width="fill_parent"

+			        android:layout_height="fill_parent"

+			        androidPlot.title="A Simple XY Plot"

+			        androidPlot.graphWidget.gridLinePaint.color="#000000"/>

+   	

+   	</RelativeLayout>

+    

+    <Button android:id="@+id/toggleSeries2"

+            style="@style/toc_button"

+            android:text="Toggle position of Series2"

+            android:enabled="true"/>

+

+</LinearLayout>

diff --git a/Examples/DemoApp/res/layout/dynamicxyplot_example.xml b/Examples/DemoApp/res/layout/dynamicxyplot_example.xml
new file mode 100644
index 0000000..0276fce
--- /dev/null
+++ b/Examples/DemoApp/res/layout/dynamicxyplot_example.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2012 AndroidPlot.com
+  ~
+  ~    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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              style="@style/sample_activity">
+
+    <com.androidplot.xy.XYPlot
+    android:id="@+id/dynamicXYPlot"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    androidplot.renderMode="use_background_thread"
+    androidPlot.title="A Dynamic XY Plot"
+    androidPlot.domainLabel="Domain"
+    androidPlot.rangeLabel="Range"
+    androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+    androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+    androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+    androidPlot.graphWidget.marginTop="20dp"
+    androidPlot.graphWidget.marginLeft="15dp"
+    androidPlot.graphWidget.marginBottom="25dp"
+    androidPlot.graphWidget.marginRight="10dp"
+    androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+    androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+    androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+    androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+    androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+    androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+    androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+    androidPlot.legendWidget.heightMetric.value="25dp"
+    androidPlot.legendWidget.positionMetrics.anchor="right_bottom"/>
+
+</LinearLayout>
diff --git a/Examples/DemoApp/res/layout/listview_example.xml b/Examples/DemoApp/res/layout/listview_example.xml
new file mode 100644
index 0000000..5ba50bc
--- /dev/null
+++ b/Examples/DemoApp/res/layout/listview_example.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>

+<LinearLayout android:id="@+id/LinearLayout01"

+              android:layout_width="fill_parent"

+              android:layout_height="fill_parent"

+              xmlns:android="http://schemas.android.com/apk/res/android">

+    <ListView android:id="@+id/listView1"

+              android:layout_width="fill_parent"

+              android:layout_height="fill_parent"/>

+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/listview_example_item.xml b/Examples/DemoApp/res/layout/listview_example_item.xml
new file mode 100644
index 0000000..d1aa023
--- /dev/null
+++ b/Examples/DemoApp/res/layout/listview_example_item.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>

+

+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              android:layout_width="fill_parent"

+              android:layout_height="fill_parent">

+    <com.androidplot.xy.XYPlot

+            android:id="@+id/xyplot"

+            android:layout_width="fill_parent"

+            android:layout_height="250dp"

+            title="an xy plot"/>

+

+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/main.xml b/Examples/DemoApp/res/layout/main.xml
new file mode 100644
index 0000000..779b2f3
--- /dev/null
+++ b/Examples/DemoApp/res/layout/main.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              android:orientation="vertical"

+              android:layout_width="fill_parent"

+              android:layout_height="fill_parent">

+

+    <ScrollView android:layout_width="fill_parent"

+                android:layout_height="wrap_content"

+                android:layout_weight="1">

+        <LinearLayout 

+                      android:orientation="vertical"

+                      android:layout_width="fill_parent"

+                      android:layout_height="wrap_content">

+            <Button android:id="@+id/startSimplePieExButton"

+                    style="@style/toc_button"

+                    android:text="A Pie Chart"/>

+            <Button android:id="@+id/startSimpleXYExButton"

+                    style="@style/toc_button"

+                    android:text="A Simple XY Plot"/>

+            <Button android:id="@+id/startDynamicXYExButton"

+                    style="@style/toc_button"

+                    android:text="A Dynamic XY Plot"/>

+            <Button android:id="@+id/startOrSensorExButton"

+                    style="@style/toc_button"

+                    android:text="Realtime Orientation Sensor Plot"/>

+            <Button android:id="@+id/startTimeSeriesExButton"

+                    style="@style/toc_button"

+                    android:text="@string/ts_title"/>

+            <Button android:id="@+id/startStepChartExButton"

+                    style="@style/toc_button"

+                    android:text="Step Chart"

+                    />

+            <Button android:id="@+id/startScrollZoomButton"

+                    style="@style/toc_button"

+                    android:text="Scroll and Zoom"

+                    />

+            <Button android:id="@+id/startBarPlotExButton"

+                    style="@style/toc_button"

+                    android:text="Bar Plot"/>

+            <Button android:id="@+id/startXyScatterExButton"

+                    style="@style/toc_button"

+                    android:text="XY Scatter"

+                    android:enabled="false"/>

+            <Button android:id="@+id/startXyScatterLineExButton"

+                    style="@style/toc_button"

+                    android:text="XY Scatter With Lines"

+                    android:enabled="false"/>

+            <Button android:id="@+id/startXyRegionExampleButton"

+                    style="@style/toc_button"

+                    android:text="XYRegion Example"

+                    android:enabled="true"/>

+            <Button android:id="@+id/startXyListViewExButton"

+                    style="@style/toc_button"

+                    android:text="ListView of XYPlots"

+                    android:enabled="true"/>

+            <Button android:id="@+id/startDualScaleExampleButton"

+                    style="@style/toc_button"

+                    android:text="Dual Scale Example"

+                    android:enabled="true"/>

+            <Button android:id="@+id/startXYPlotWithBgImgExample"

+                    style="@style/toc_button"

+                    android:text="XY Background Example"

+                    android:enabled="true"/>

+            </LinearLayout>

+    </ScrollView>

+    <TextView android:id="@+id/text"

+              android:layout_width="fill_parent"

+              android:layout_height="wrap_content"

+              android:autoLink="web"

+              android:gravity="center"

+              android:text="http://androidplot.com"/>

+</LinearLayout>

+

diff --git a/Examples/DemoApp/res/layout/orientation_sensor_example.xml b/Examples/DemoApp/res/layout/orientation_sensor_example.xml
new file mode 100644
index 0000000..85fa45f
--- /dev/null
+++ b/Examples/DemoApp/res/layout/orientation_sensor_example.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              android:orientation="vertical"

+              android:layout_width="fill_parent"

+              android:layout_height="fill_parent">

+

+    <LinearLayout android:orientation="horizontal"

+                  android:gravity="center"

+                  android:layout_weight="1"

+                  android:layout_width="fill_parent"

+                  android:layout_height="wrap_content">

+

+        <com.androidplot.xy.XYPlot

+                android:id="@+id/aprLevelsPlot"

+                android:layout_width="fill_parent"

+                android:layout_height="fill_parent"

+                android:layout_margin="0dp"

+                android:layout_weight="3"

+                android:layout_marginTop="10px"

+                android:layout_marginLeft="10px"

+                android:layout_marginRight="10px"

+                androidPlot.backgroundPaint.color="#000000"

+                androidPlot.borderPaint.color="#000000"

+                androidplot.renderMode="use_background_thread"

+                androidPlot.title="Levels"

+                androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"

+                androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"

+                androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"

+                androidPlot.graphWidget.backgroundPaint.color="#000000"

+                androidPlot.graphWidget.gridBackgroundPaint.color="#000000"

+                androidPlot.graphWidget.domainGridLinePaint.color="#00000000"

+                androidPlot.graphWidget.domainOriginLinePaint.color="#00000000"

+                androidPlot.graphWidget.domainOriginLabelPaint.color="#00000000"

+                androidPlot.graphWidget.marginTop="20dp"

+                androidPlot.graphWidget.marginLeft="15dp"

+                androidPlot.graphWidget.marginBottom="25dp"

+                androidPlot.graphWidget.marginRight="10dp"

+                androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"

+                androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"

+                androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+                androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+                androidPlot.legendWidget.textPaint.textSize="10dp"

+                androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"

+                androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"

+                androidPlot.legendWidget.heightMetric.value="25dp"

+                />

+        <com.androidplot.xy.XYPlot

+                android:id="@+id/aprHistoryPlot"

+                android:layout_width="fill_parent"

+                android:layout_height="fill_parent"

+                android:layout_margin="0dp"

+                android:layout_weight="1"

+                android:layout_marginTop="10px"

+                android:layout_marginLeft="10px"

+                android:layout_marginRight="10px"

+                androidPlot.backgroundPaint.color="#000000"

+                androidPlot.borderPaint.color="#000000"

+                androidplot.renderMode="use_background_thread"

+                androidPlot.title="History"

+                androidPlot.domainLabel="Domain"

+                androidPlot.rangeLabel="Range"

+                androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"

+                androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"

+                androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"

+                androidPlot.graphWidget.backgroundPaint.color="#000000"

+                androidPlot.graphWidget.gridBackgroundPaint.color="#000000"

+                androidPlot.graphWidget.marginTop="20dp"

+                androidPlot.graphWidget.marginLeft="15dp"

+                androidPlot.graphWidget.marginBottom="25dp"

+                androidPlot.graphWidget.marginRight="10dp"

+                androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"

+                androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"

+                androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+                androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+                androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"

+                androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"

+                androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"

+                androidPlot.legendWidget.heightMetric.value="25dp"

+                />

+

+    </LinearLayout>

+

+    <LinearLayout android:orientation="horizontal"

+                  android:gravity="center"

+                  android:layout_width="fill_parent"

+                  android:layout_height="wrap_content">

+        <CheckBox android:id="@+id/hwAccelerationCb"

+                  android:visibility="gone"

+                  android:text="HW Acceleration"

+                  android:layout_width="wrap_content"

+                  android:layout_height="wrap_content"/>

+

+        <CheckBox android:id="@+id/showFpsCb"

+                  android:text="Show FPS"

+                  android:layout_width="wrap_content"

+                  android:layout_height="wrap_content"/>

+    </LinearLayout>

+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/pie_chart.xml b/Examples/DemoApp/res/layout/pie_chart.xml
new file mode 100644
index 0000000..010bcfc
--- /dev/null
+++ b/Examples/DemoApp/res/layout/pie_chart.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2012 AndroidPlot.com
+  ~
+  ~    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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              style="@style/sample_activity"
+              android:orientation="vertical">
+
+    <com.androidplot.pie.PieChart
+            android:id="@+id/mySimplePieChart"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            androidPlot.title="A Simple Pie Chart"
+            androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"/>
+    <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="fill_parent"
+            android:layout_gravity="center"
+            android:orientation="vertical">
+
+        <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="fill_parent"
+                android:layout_gravity="center"
+                android:text="Donut Size"
+                android:id="@+id/donutSizeSeekLabel"/>
+        <SeekBar
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:paddingLeft="10dp"
+                android:paddingRight="10dp"
+                android:max="90"
+                android:progress="50"
+                android:id="@+id/donutSizeSeekBar"/>
+        <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="fill_parent"
+                android:layout_gravity="center"
+                android:text="unknown"
+                android:id="@+id/donutSizeTextView"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/simple_xy_plot_example.xml b/Examples/DemoApp/res/layout/simple_xy_plot_example.xml
new file mode 100644
index 0000000..fe54c57
--- /dev/null
+++ b/Examples/DemoApp/res/layout/simple_xy_plot_example.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              style="@style/sample_activity">

+

+    <com.androidplot.xy.XYPlot

+            android:id="@+id/mySimpleXYPlot"

+            android:layout_width="fill_parent"

+            android:layout_height="fill_parent"

+            androidPlot.title="A Simple XY Plot"

+            androidPlot.domainLabel="Domain"

+            androidPlot.rangeLabel="Range"

+            androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"

+            androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"

+            androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"

+            androidPlot.graphWidget.marginTop="20dp"

+            androidPlot.graphWidget.marginLeft="15dp"

+            androidPlot.graphWidget.marginBottom="25dp"

+            androidPlot.graphWidget.marginRight="10dp"

+            androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"

+            androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"

+            androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+            androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+            androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"

+            androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"

+            androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"

+            androidPlot.legendWidget.heightMetric.value="25dp"

+            androidPlot.legendWidget.positionMetrics.anchor="right_bottom"

+            androidPlot.graphWidget.gridLinePaint.color="#000000"/>

+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/step_chart_example.xml b/Examples/DemoApp/res/layout/step_chart_example.xml
new file mode 100644
index 0000000..fadc79b
--- /dev/null
+++ b/Examples/DemoApp/res/layout/step_chart_example.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              android:orientation="vertical"

+              style="@style/sample_activity"

+    >

+

+    <com.androidplot.xy.XYPlot

+    android:id="@+id/stepChartExamplePlot"

+    style="@style/plotStyle1"

+    android:layout_width="fill_parent"

+    android:layout_height="fill_parent"

+    androidplot.title="HTTP Server State (15  Sec)"

+    androidplot.domainLabel="time (secs)"

+    androidplot.rangeLabel="server state"

+    androidplot.markupEnabled="false"

+    renderMode="use_main_thread"

+    />

+</LinearLayout>

+

diff --git a/Examples/DemoApp/res/layout/time_series_example.xml b/Examples/DemoApp/res/layout/time_series_example.xml
new file mode 100644
index 0000000..847b381
--- /dev/null
+++ b/Examples/DemoApp/res/layout/time_series_example.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>

+

+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              style="@style/sample_activity"

+              android:orientation="vertical">

+

+    <com.androidplot.xy.XYPlot

+            android:id="@+id/plot1"

+            android:layout_width="fill_parent"

+            android:layout_height="fill_parent"

+            title="@string/ts_plot1_title"

+            renderMode="use_main_thread"/>

+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/touch_zoom_example.xml b/Examples/DemoApp/res/layout/touch_zoom_example.xml
new file mode 100644
index 0000000..71d0e33
--- /dev/null
+++ b/Examples/DemoApp/res/layout/touch_zoom_example.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              style="@style/sample_activity"

+              android:orientation="vertical">

+

+    <com.androidplot.xy.XYPlot

+            android:id="@+id/mySimpleXYPlot"

+            android:layout_width="fill_parent"

+            android:layout_height="fill_parent"

+            androidPlot.title="ScrollZoom Example"

+            androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"

+            androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"

+            androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"

+            androidPlot.graphWidget.marginTop="20dp"

+            androidPlot.graphWidget.marginLeft="15dp"

+            androidPlot.graphWidget.marginBottom="25dp"

+            androidPlot.graphWidget.marginRight="10dp"

+            androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"

+            androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"

+            androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+            androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+            androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"

+            androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"

+            androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"

+            androidPlot.legendWidget.widthMetric.value="1"

+            androidPlot.legendWidget.heightMetric.value="25dp"

+            androidPlot.legendWidget.positionMetrics.xPositionMetric.value="0"

+            androidPlot.legendWidget.positionMetrics.anchor="right_bottom"

+            android:layout_weight="1"/>

+

+    <Button android:id="@+id/resetButton"

+            android:layout_width="fill_parent"

+            android:layout_height="wrap_content"

+            android:text="Reset"/>

+</LinearLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/xy_plot_with_bq_img_example.xml b/Examples/DemoApp/res/layout/xy_plot_with_bq_img_example.xml
new file mode 100644
index 0000000..04e4315
--- /dev/null
+++ b/Examples/DemoApp/res/layout/xy_plot_with_bq_img_example.xml
@@ -0,0 +1,48 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context=".GraphMetricsActivity" >
+    
+    <com.androidplot.xy.XYPlot
+        android:id="@+id/graph_metrics"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_centerHorizontal="true"
+        androidPlot.title="BG Image Example"
+        androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"
+        androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"
+        androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"
+        androidPlot.graphWidget.marginTop="20dp"
+        androidPlot.graphWidget.marginLeft="15dp"
+        androidPlot.graphWidget.marginBottom="25dp"
+        androidPlot.graphWidget.marginRight="10dp"
+        androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"
+        androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"
+        androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+        androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"
+        androidPlot.legendWidget.textPaint.textSize="@dimen/legend_text_font_size"
+        androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"
+        androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"
+        androidPlot.legendWidget.heightMetric.value="25dp"
+        androidPlot.legendWidget.positionMetrics.xPositionMetric.value="0"
+        androidPlot.legendWidget.positionMetrics.anchor="right_bottom"
+        android:layout_centerVertical="true"/>
+    
+    <ToggleButton 
+        android:id="@+id/style_toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignTop="@id/graph_metrics"
+        android:layout_alignRight="@id/graph_metrics"
+        android:layout_marginTop="15dp"
+        android:layout_marginRight="15dp"
+        android:textOn="bg on"
+        android:textOff="bg off"
+        android:onClick="onGraphStyleToggle" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/layout/xyregion_example.xml b/Examples/DemoApp/res/layout/xyregion_example.xml
new file mode 100644
index 0000000..db0bbf8
--- /dev/null
+++ b/Examples/DemoApp/res/layout/xyregion_example.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>

+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

+              android:orientation="vertical"

+              android:layout_width="fill_parent"

+              android:layout_height="fill_parent"

+        >

+

+    <com.androidplot.xy.XYPlot

+            android:id="@+id/xyRegionExamplePlot"

+            android:layout_width="fill_parent"

+            android:layout_height="fill_parent"

+            androidPlot.title="Batting Practice Stats"

+            androidPlot.domainLabel="Pitch #"

+            androidPlot.rangeLabel="Distance"

+            androidPlot.titleWidget.labelPaint.textSize="@dimen/title_font_size"

+            androidPlot.domainLabelWidget.labelPaint.textSize="@dimen/domain_label_font_size"

+            androidPlot.rangeLabelWidget.labelPaint.textSize="@dimen/range_label_font_size"

+            androidPlot.graphWidget.marginTop="20dp"

+            androidPlot.graphWidget.marginLeft="35dp"

+            androidPlot.graphWidget.marginBottom="25dp"

+            androidPlot.graphWidget.marginRight="10dp"

+            androidPlot.graphWidget.rangeLabelPaint.textSize="@dimen/range_tick_label_font_size"

+            androidPlot.graphWidget.rangeOriginLabelPaint.textSize="@dimen/range_tick_label_font_size"

+            androidPlot.graphWidget.domainLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+            androidPlot.graphWidget.domainOriginLabelPaint.textSize="@dimen/domain_tick_label_font_size"

+            androidPlot.legendWidget.textPaint.textSize="12dp"

+            androidPlot.legendWidget.iconSizeMetrics.heightMetric.value="15dp"

+            androidPlot.legendWidget.iconSizeMetrics.widthMetric.value="15dp"

+            androidPlot.legendWidget.heightMetric.value="40dp"

+            androidPlot.legendWidget.widthMetric.value="40dp"

+            android:layout_weight="1"

+            />

+

+    <LinearLayout

+            android:layout_height="wrap_content"

+            android:layout_width="wrap_content"

+            android:layout_gravity="center"

+            android:orientation="horizontal">

+        <CheckBox android:id="@+id/s1CheckBox"

+                  android:text="Tim"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+        <CheckBox android:id="@+id/s2CheckBox"

+                  android:text="Nick"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+        <CheckBox android:id="@+id/s3CheckBox"

+                  android:text="Joe"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+        <CheckBox android:id="@+id/s4CheckBox"

+                  android:text="James"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+    </LinearLayout>

+

+    <LinearLayout

+            android:layout_height="wrap_content"

+            android:layout_width="wrap_content"

+            android:layout_gravity="center"

+            android:orientation="horizontal">

+        <CheckBox android:id="@+id/r1CheckBox"

+                  android:text="R1"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+        <CheckBox android:id="@+id/r2CheckBox"

+                  android:text="R2"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+        <CheckBox android:id="@+id/r3CheckBox"

+                  android:text="R3"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+        <CheckBox android:id="@+id/r4CheckBox"

+                  android:text="R4"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+        <CheckBox android:id="@+id/r5CheckBox"

+                  android:text="R5"

+                  android:checked="true"

+                  android:layout_height="wrap_content"

+                  android:layout_width="fill_parent"/>

+    </LinearLayout>

+

+</LinearLayout>

+

diff --git a/Examples/DemoApp/res/values-hdpi/dimens.xml b/Examples/DemoApp/res/values-hdpi/dimens.xml
new file mode 100644
index 0000000..79fac2c
--- /dev/null
+++ b/Examples/DemoApp/res/values-hdpi/dimens.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- HDPI DIMENSIONS -->
+<resources>
+    <dimen name="title_font_size">30dp</dimen>
+    <dimen name="pie_segment_label_font_size">20dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values-ldpi/dimens.xml b/Examples/DemoApp/res/values-ldpi/dimens.xml
new file mode 100644
index 0000000..81033f3
--- /dev/null
+++ b/Examples/DemoApp/res/values-ldpi/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- LDPI DIMENSIONS -->
+<resources>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values/attrs.xml b/Examples/DemoApp/res/values/attrs.xml
new file mode 100644
index 0000000..b88cddf
--- /dev/null
+++ b/Examples/DemoApp/res/values/attrs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <declare-styleable name="XYPlot">
+        <attr name="androidplot.title" format="string"/>
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values/dimens.xml b/Examples/DemoApp/res/values/dimens.xml
new file mode 100644
index 0000000..379e32b
--- /dev/null
+++ b/Examples/DemoApp/res/values/dimens.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- DEFAULT DIMENSIONS -->
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+    <dimen name="pie_segment_label_font_size">10dp</dimen>
+    <dimen name="title_font_size">20dp</dimen>
+    <dimen name="domain_label_font_size">13dp</dimen>
+    <dimen name="range_label_font_size">13dp</dimen>
+    <dimen name="range_tick_label_font_size">15dp</dimen>
+    <dimen name="domain_tick_label_font_size">15dp</dimen>
+    <dimen name="legend_text_font_size">20dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/values/strings.xml b/Examples/DemoApp/res/values/strings.xml
new file mode 100644
index 0000000..0bde0cd
--- /dev/null
+++ b/Examples/DemoApp/res/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>

+<resources>

+    <string name="app_name">AndroidPlot Demo</string>

+

+    <string name="sxy_title">A Simple XY Plot</string>

+

+    <string name="ts_title">Time Series</string>

+    <string name="ts_plot1_title">Yearly UFO Sightings</string>

+</resources>

diff --git a/Examples/DemoApp/res/values/style.xml b/Examples/DemoApp/res/values/style.xml
new file mode 100644
index 0000000..d465715
--- /dev/null
+++ b/Examples/DemoApp/res/values/style.xml
@@ -0,0 +1,34 @@
+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<resources xmlns="http://schemas.android.com/apk/lib/com.androidplot.xy">

+    <style name="sample_activity">

+        <item name="android:layout_width">fill_parent</item>

+        <item name="android:layout_height">fill_parent</item>

+    </style>

+

+    <style name="toc_button">

+        <item name="android:layout_width">fill_parent</item>

+        <item name="android:layout_height">wrap_content</item>

+    </style>

+

+    <style name="plotStyle1">

+        <!--<item name="androidplot.title">Default Title</item>

+        <item name="androidplot.domainLabel">my domain</item>-->

+        <!--<item name="XYPlot:androidplot.title">my domain</item>-->

+    </style>

+

+</resources>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/demo_app_widget_provider_info.xml b/Examples/DemoApp/res/xml/demo_app_widget_provider_info.xml
new file mode 100644
index 0000000..771c246
--- /dev/null
+++ b/Examples/DemoApp/res/xml/demo_app_widget_provider_info.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>

+

+<!--

+  ~ Copyright 2012 AndroidPlot.com

+  ~

+  ~    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.

+  -->

+

+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

+                    android:minWidth="294dp"

+                    android:minHeight="72dp"

+                    android:updatePeriodMillis="86400000"

+                    android:previewImage="@drawable/icon"

+                    android:initialLayout="@layout/demo_app_widget"

+                    android:resizeMode="horizontal|vertical">

+</appwidget-provider>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/line_point_formatter_with_plf1.xml b/Examples/DemoApp/res/xml/line_point_formatter_with_plf1.xml
new file mode 100644
index 0000000..47bf5f5
--- /dev/null
+++ b/Examples/DemoApp/res/xml/line_point_formatter_with_plf1.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+        linePaint.strokeWidth="3dp"
+        linePaint.color="#00AA00"
+        vertexPaint.color="#007700"
+        fillPaint.color="#00000000"
+        pointLabelFormatter.textPaint.color="#FFFFFF"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/line_point_formatter_with_plf2.xml b/Examples/DemoApp/res/xml/line_point_formatter_with_plf2.xml
new file mode 100644
index 0000000..31894cd
--- /dev/null
+++ b/Examples/DemoApp/res/xml/line_point_formatter_with_plf2.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+        linePaint.strokeWidth="3dp"
+        linePaint.color="#0000AA"
+        vertexPaint.color="#000077"
+        fillPaint.color="#00000000"
+        pointLabelFormatter.textPaint.color="#FFFFFF"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter1.xml b/Examples/DemoApp/res/xml/pie_segment_formatter1.xml
new file mode 100644
index 0000000..1b0c7e0
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter1.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+        fillPaint.color="#FF0000"
+        labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter2.xml b/Examples/DemoApp/res/xml/pie_segment_formatter2.xml
new file mode 100644
index 0000000..ad7a225
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter2.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+        fillPaint.color="#00FF00"
+        labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter3.xml b/Examples/DemoApp/res/xml/pie_segment_formatter3.xml
new file mode 100644
index 0000000..5e02a39
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter3.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+        fillPaint.color="#0000FF"
+        labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/res/xml/pie_segment_formatter4.xml b/Examples/DemoApp/res/xml/pie_segment_formatter4.xml
new file mode 100644
index 0000000..2cf1171
--- /dev/null
+++ b/Examples/DemoApp/res/xml/pie_segment_formatter4.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<config
+        fillPaint.color="#FF00FF"
+        labelPaint.textSize="@dimen/pie_segment_label_font_size"/>
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/BarPlotExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/BarPlotExampleActivity.java
new file mode 100644
index 0000000..4c79dd8
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/BarPlotExampleActivity.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.demos;
+
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.util.Pair;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+
+import com.androidplot.LineRegion;
+import com.androidplot.ui.AnchorPosition;
+import com.androidplot.ui.SeriesRenderer;
+import com.androidplot.ui.SizeLayoutType;
+import com.androidplot.ui.SizeMetrics;
+import com.androidplot.ui.TextOrientationType;
+import com.androidplot.ui.widget.TextLabelWidget;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.xy.*;
+import com.androidplot.ui.XLayoutStyle;
+import com.androidplot.ui.YLayoutStyle;
+
+/**
+ * The simplest possible example of using AndroidPlot to plot some data.
+ */
+public class BarPlotExampleActivity extends Activity
+{
+
+    private static final String NO_SELECTION_TXT = "Touch bar to select.";
+    private XYPlot plot;
+
+    private CheckBox series1CheckBox;
+    private CheckBox series2CheckBox;
+    private Spinner spRenderStyle, spWidthStyle, spSeriesSize;
+    private SeekBar sbFixedWidth, sbVariableWidth;
+    
+    private XYSeries series1;
+    private XYSeries series2;
+    private enum SeriesSize {
+        TEN,
+        TWENTY,
+        SIXTY
+    }
+
+    // Create a couple arrays of y-values to plot:
+    Number[] series1Numbers10 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5};
+    Number[] series2Numbers10 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4};
+    Number[] series1Numbers20 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3};
+    Number[] series2Numbers20 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9};
+    Number[] series1Numbers60 = {2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3, 2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3, 2, null, 5, 2, 7, 4, 3, 7, 4, 5, 7, 4, 5, 8, 5, 3, 6, 3, 9, 3};
+    Number[] series2Numbers60 = {4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9, 4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9, 4, 6, 3, null, 2, 0, 7, 4, 5, 4, 9, 6, 2, 8, 4, 0, 7, 4, 7, 9};
+    Number[] series1Numbers = series1Numbers10;
+    Number[] series2Numbers = series2Numbers10;
+
+    private MyBarFormatter formatter1 =
+            new MyBarFormatter(Color.argb(200, 100, 150, 100), Color.LTGRAY);
+
+    private MyBarFormatter formatter2 =
+            new MyBarFormatter(Color.argb(200, 100, 100, 150), Color.LTGRAY);
+
+    private MyBarFormatter selectionFormatter =
+            new MyBarFormatter(Color.YELLOW, Color.WHITE);
+
+    private TextLabelWidget selectionWidget;
+
+    private Pair<Integer, XYSeries> selection;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.bar_plot_example);
+
+        // initialize our XYPlot reference:
+        plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
+
+        selectionWidget = new TextLabelWidget(plot.getLayoutManager(), NO_SELECTION_TXT,
+                new SizeMetrics(
+                        PixelUtils.dpToPix(100), SizeLayoutType.ABSOLUTE,
+                        PixelUtils.dpToPix(100), SizeLayoutType.ABSOLUTE),
+                TextOrientationType.HORIZONTAL);
+
+        selectionWidget.getLabelPaint().setTextSize(PixelUtils.dpToPix(16));
+
+        // add a dark, semi-transparent background to the selection label widget:
+        Paint p = new Paint();
+        p.setARGB(100, 0, 0, 0);
+        selectionWidget.setBackgroundPaint(p);
+
+        selectionWidget.position(
+                0, XLayoutStyle.RELATIVE_TO_CENTER,
+                PixelUtils.dpToPix(45), YLayoutStyle.ABSOLUTE_FROM_TOP,
+                AnchorPosition.TOP_MIDDLE);
+        selectionWidget.pack();
+
+
+        // reduce the number of range labels
+        plot.setTicksPerRangeLabel(3);
+        plot.setRangeLowerBoundary(0, BoundaryMode.FIXED);
+        plot.getGraphWidget().setGridPadding(30, 10, 30, 0);
+
+        plot.setTicksPerDomainLabel(2);
+
+
+        // setup checkbox listers:
+        series1CheckBox = (CheckBox) findViewById(R.id.s1CheckBox);
+        series1CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+                onS1CheckBoxClicked(b);
+            }
+        });
+
+        series2CheckBox = (CheckBox) findViewById(R.id.s2CheckBox);
+        series2CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {onS2CheckBoxClicked(b);
+            }
+        });
+
+        plot.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View view, MotionEvent motionEvent) {
+                if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
+                    onPlotClicked(new PointF(motionEvent.getX(), motionEvent.getY()));
+                }
+                return true;
+            }
+        });
+        
+        spRenderStyle = (Spinner) findViewById(R.id.spRenderStyle);
+        ArrayAdapter <BarRenderer.BarRenderStyle> adapter = new ArrayAdapter <BarRenderer.BarRenderStyle> (this, android.R.layout.simple_spinner_item, BarRenderer.BarRenderStyle.values() );
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spRenderStyle.setAdapter(adapter);
+        spRenderStyle.setSelection(BarRenderer.BarRenderStyle.OVERLAID.ordinal());
+        spRenderStyle.setOnItemSelectedListener(new OnItemSelectedListener() {
+            public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
+                updatePlot();
+            }
+			@Override
+			public void onNothingSelected(AdapterView<?> arg0) {
+			}
+        });             
+        
+        spWidthStyle = (Spinner) findViewById(R.id.spWidthStyle);
+        ArrayAdapter <BarRenderer.BarWidthStyle> adapter1 = new ArrayAdapter <BarRenderer.BarWidthStyle> (this, android.R.layout.simple_spinner_item, BarRenderer.BarWidthStyle.values() );
+        adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spWidthStyle.setAdapter(adapter1);
+        spWidthStyle.setSelection(BarRenderer.BarWidthStyle.FIXED_WIDTH.ordinal());
+        spWidthStyle.setOnItemSelectedListener(new OnItemSelectedListener() {
+            public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
+            	if (BarRenderer.BarWidthStyle.FIXED_WIDTH.equals(spWidthStyle.getSelectedItem())) {
+            		sbFixedWidth.setVisibility(View.VISIBLE);
+            		sbVariableWidth.setVisibility(View.INVISIBLE);
+            	} else {
+            		sbFixedWidth.setVisibility(View.INVISIBLE);
+            		sbVariableWidth.setVisibility(View.VISIBLE);
+            	}
+                updatePlot();
+            }
+			@Override
+			public void onNothingSelected(AdapterView<?> arg0) {    
+			}
+        });             
+
+        spSeriesSize = (Spinner) findViewById(R.id.spSeriesSize);
+        ArrayAdapter <SeriesSize> adapter11 = new ArrayAdapter <SeriesSize> (this, android.R.layout.simple_spinner_item, SeriesSize.values() );
+        adapter11.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spSeriesSize.setAdapter(adapter11);
+        spSeriesSize.setSelection(SeriesSize.TEN.ordinal());
+        spSeriesSize.setOnItemSelectedListener(new OnItemSelectedListener() {
+            public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
+                switch ((SeriesSize)arg0.getSelectedItem()) {
+				case TEN:
+					series1Numbers = series1Numbers10;
+					series2Numbers = series2Numbers10;
+					break;
+				case TWENTY:
+					series1Numbers = series1Numbers20;
+					series2Numbers = series2Numbers20;
+					break;
+				case SIXTY:
+					series1Numbers = series1Numbers60;
+					series2Numbers = series2Numbers60;
+					break;
+				default:
+					break;
+                }
+                updatePlot();
+            }
+			@Override
+			public void onNothingSelected(AdapterView<?> arg0) {
+			}
+        });          
+        
+       
+        sbFixedWidth = (SeekBar) findViewById(R.id.sbFixed);
+        sbFixedWidth.setProgress(50);
+        sbFixedWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {updatePlot();}
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {}
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {}
+        });
+        
+        
+        sbVariableWidth = (SeekBar) findViewById(R.id.sbVariable);
+        sbVariableWidth.setProgress(1);
+        sbVariableWidth.setVisibility(View.INVISIBLE);
+        sbVariableWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {updatePlot();}
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {}
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {}
+        });
+
+        plot.setDomainValueFormat(new NumberFormat() {
+            @Override
+            public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) {
+                int year = (int) (value + 0.5d) / 12;
+                int month = (int) ((value + 0.5d) % 12);
+                return new StringBuffer(DateFormatSymbols.getInstance().getShortMonths()[month] + " '0" + year);
+            }
+
+            @Override
+            public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {
+                throw new UnsupportedOperationException("Not yet implemented.");
+            }
+
+            @Override
+            public Number parse(String string, ParsePosition position) {
+                throw new UnsupportedOperationException("Not yet implemented.");
+            }
+        });
+        updatePlot();
+
+    }
+
+    private void updatePlot() {
+    	
+    	// Remove all current series from each plot
+        Iterator<XYSeries> iterator1 = plot.getSeriesSet().iterator();
+        while(iterator1.hasNext()) { 
+        	XYSeries setElement = iterator1.next();
+        	plot.removeSeries(setElement);
+        }
+
+        // Setup our Series with the selected number of elements
+        series1 = new SimpleXYSeries(Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Us");
+        series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Them");
+
+        // add a new series' to the xyplot:
+        if (series1CheckBox.isChecked()) plot.addSeries(series1, formatter1);
+        if (series2CheckBox.isChecked()) plot.addSeries(series2, formatter2); 
+
+        // Setup the BarRenderer with our selected options
+        MyBarRenderer renderer = ((MyBarRenderer)plot.getRenderer(MyBarRenderer.class));
+        renderer.setBarRenderStyle((BarRenderer.BarRenderStyle)spRenderStyle.getSelectedItem());
+        renderer.setBarWidthStyle((BarRenderer.BarWidthStyle)spWidthStyle.getSelectedItem());
+        renderer.setBarWidth(sbFixedWidth.getProgress());
+        renderer.setBarGap(sbVariableWidth.getProgress());
+        
+        if (BarRenderer.BarRenderStyle.STACKED.equals(spRenderStyle.getSelectedItem())) {
+        	plot.setRangeTopMin(15);
+        } else {
+        	plot.setRangeTopMin(0);
+        }
+	        
+        plot.redraw();
+    	
+    }  
+    
+    private void onPlotClicked(PointF point) {
+
+        // make sure the point lies within the graph area.  we use gridrect
+        // because it accounts for margins and padding as well. 
+        if (plot.getGraphWidget().getGridRect().contains(point.x, point.y)) {
+            Number x = plot.getXVal(point);
+            Number y = plot.getYVal(point);
+
+
+            selection = null;
+            double xDistance = 0;
+            double yDistance = 0;
+
+            // find the closest value to the selection:
+            for (XYSeries series : plot.getSeriesSet()) {
+                for (int i = 0; i < series.size(); i++) {
+                    Number thisX = series.getX(i);
+                    Number thisY = series.getY(i);
+                    if (thisX != null && thisY != null) {
+                        double thisXDistance =
+                                LineRegion.measure(x, thisX).doubleValue();
+                        double thisYDistance =
+                                LineRegion.measure(y, thisY).doubleValue();
+                        if (selection == null) {
+                            selection = new Pair<Integer, XYSeries>(i, series);
+                            xDistance = thisXDistance;
+                            yDistance = thisYDistance;
+                        } else if (thisXDistance < xDistance) {
+                            selection = new Pair<Integer, XYSeries>(i, series);
+                            xDistance = thisXDistance;
+                            yDistance = thisYDistance;
+                        } else if (thisXDistance == xDistance &&
+                                thisYDistance < yDistance &&
+                                thisY.doubleValue() >= y.doubleValue()) {
+                            selection = new Pair<Integer, XYSeries>(i, series);
+                            xDistance = thisXDistance;
+                            yDistance = thisYDistance;
+                        }
+                    }
+                }
+            }
+
+        } else {
+            // if the press was outside the graph area, deselect:
+            selection = null;
+        }
+
+        if(selection == null) {
+            selectionWidget.setText(NO_SELECTION_TXT);
+        } else {
+            selectionWidget.setText("Selected: " + selection.second.getTitle() +
+                    " Value: " + selection.second.getY(selection.first));
+        }
+        plot.redraw();
+    }
+
+    private void onS1CheckBoxClicked(boolean checked) {
+        if (checked) {
+            plot.addSeries(series1, formatter1);
+        } else {
+            plot.removeSeries(series1);
+        }
+        plot.redraw();
+    }
+
+    private void onS2CheckBoxClicked(boolean checked) {
+        if (checked) {
+            plot.addSeries(series2, formatter2);  
+        } else {
+            plot.removeSeries(series2);
+        }
+        plot.redraw();
+    }
+
+    class MyBarFormatter extends BarFormatter {
+        public MyBarFormatter(int fillColor, int borderColor) {
+            super(fillColor, borderColor);
+        }
+
+        @Override
+        public Class<? extends SeriesRenderer> getRendererClass() {
+            return MyBarRenderer.class;
+        }
+
+        @Override
+        public SeriesRenderer getRendererInstance(XYPlot plot) {
+            return new MyBarRenderer(plot);
+        }
+    }
+
+    class MyBarRenderer extends BarRenderer<MyBarFormatter> {
+
+        public MyBarRenderer(XYPlot plot) {
+            super(plot);
+        }
+
+        /**
+         * Implementing this method to allow us to inject our
+         * special selection formatter.
+         * @param index index of the point being rendered.
+         * @param series XYSeries to which the point being rendered belongs.
+         * @return
+         */
+        //@Override
+        // TODO: figure out why using @Override screws up the Maven builds
+        protected MyBarFormatter getFormatter(int index, XYSeries series) { 
+            if(selection != null &&
+                    selection.second == series && 
+                    selection.first == index) {
+                return selectionFormatter;
+            } else {
+                return getFormatter(series);
+            }
+        }
+    }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/DualScaleXYPlotExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/DualScaleXYPlotExampleActivity.java
new file mode 100644
index 0000000..d06581e
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/DualScaleXYPlotExampleActivity.java
@@ -0,0 +1,200 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+import java.util.Arrays;

+import java.util.Iterator;

+

+import android.app.Activity;

+import android.graphics.Color;

+import android.os.Build;

+import android.os.Bundle;

+import android.view.View;

+import android.view.View.OnClickListener;

+import android.widget.Button;

+

+import com.androidplot.ui.AnchorPosition;

+import com.androidplot.ui.DynamicTableModel;

+import com.androidplot.ui.SizeLayoutType;

+import com.androidplot.ui.SizeMetrics;

+import com.androidplot.xy.LineAndPointFormatter;

+import com.androidplot.xy.PointLabelFormatter;

+import com.androidplot.xy.SimpleXYSeries;

+import com.androidplot.ui.XLayoutStyle;

+import com.androidplot.xy.XYGraphWidget;

+import com.androidplot.xy.XYLegendWidget;

+import com.androidplot.xy.XYPlot;

+import com.androidplot.xy.XYSeries;

+import com.androidplot.ui.YLayoutStyle;

+

+/**

+ * The simplest possible example of using AndroidPlot to plot some data.

+ */

+public class DualScaleXYPlotExampleActivity extends Activity implements OnClickListener

+{

+

+    private XYPlot myXYPlot_LEFT, myXYPlot_RIGHT; 

+    private Boolean series2_onRight = true;

+    private LineAndPointFormatter series1Format, series2Format;

+    private Button button;

+    

+    // Declare and enable buttons to toggle whether the 2nd series is on left or right.

+

+    // Create a couple arrays of y-values to plot:

+    private Number[] series1Numbers = {1, 8, 5, 2, 7, 4};

+    private Number[] series2Numbers = {444, 613, 353, 876, 924, 1004};

+    XYSeries series1, series2;

+    

+    @Override

+    public void onCreate(Bundle savedInstanceState)

+    {

+

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.dual_scale_xy_plot_example);

+

+        // Setup the LEFT Plot as normal

+        myXYPlot_LEFT = (XYPlot) findViewById(R.id.mySimpleXYPlot_L);

+        myXYPlot_RIGHT = (XYPlot) findViewById(R.id.mySimpleXYPlot_R);

+        

+        // Disable Hardware Acceleration on the xyPlot view object.

+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

+        	myXYPlot_LEFT.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

+        	myXYPlot_RIGHT.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

+        }

+

+        XYGraphWidget graphWidget_LEFT = myXYPlot_LEFT.getGraphWidget();

+        graphWidget_LEFT.setRangeAxisPosition(true, false, 4, "10");

+        graphWidget_LEFT.setMarginRight(0);

+        graphWidget_LEFT.setPaddingRight(30);

+        graphWidget_LEFT.setRangeLabelVerticalOffset(-3);

+        graphWidget_LEFT.setRangeLabelWidth(50);

+        

+        // Setup the second Plot with Right-hand Scale and otherwise invisible.

+        myXYPlot_RIGHT.getDomainLabelWidget().setVisible(false);

+        myXYPlot_RIGHT.getRangeLabelWidget().setVisible(false);

+        myXYPlot_RIGHT.getTitleWidget().setVisible(false);

+        myXYPlot_RIGHT.getBorderPaint().setAlpha(0);

+        myXYPlot_RIGHT.getBackgroundPaint().setAlpha(0);

+        XYGraphWidget graphWidget_RIGHT = myXYPlot_RIGHT.getGraphWidget();

+        graphWidget_RIGHT.getBackgroundPaint().setAlpha(0);

+        graphWidget_RIGHT.getDomainLabelPaint().setAlpha(0);

+        graphWidget_RIGHT.getGridBackgroundPaint().setAlpha(0);

+        graphWidget_RIGHT.getDomainOriginLabelPaint().setAlpha(0);

+        graphWidget_RIGHT.getRangeOriginLinePaint().setAlpha(0); 

+        graphWidget_RIGHT.getDomainGridLinePaint().setAlpha(0);

+        graphWidget_RIGHT.getRangeGridLinePaint().setAlpha(0);

+        graphWidget_RIGHT.setRangeAxisPosition(false, false, 4, "10");

+

+        // Copy where possible from the LEFT plot

+        graphWidget_RIGHT.setRangeLabelVerticalOffset(graphWidget_LEFT.getRangeLabelVerticalOffset());

+        graphWidget_RIGHT.setMarginRight(graphWidget_LEFT.getMarginRight());

+        graphWidget_RIGHT.setPaddingRight(graphWidget_LEFT.getPaddingRight());

+        graphWidget_RIGHT.setRangeLabelWidth(graphWidget_LEFT.getRangeLabelWidth());

+

+        // Position the Graphs

+        myXYPlot_LEFT.getGraphWidget().position(

+                0 ,XLayoutStyle.ABSOLUTE_FROM_LEFT,10,YLayoutStyle.ABSOLUTE_FROM_TOP,AnchorPosition.LEFT_TOP);

+        myXYPlot_RIGHT.getGraphWidget().position(

+                49,XLayoutStyle.ABSOLUTE_FROM_LEFT,10,YLayoutStyle.ABSOLUTE_FROM_TOP,AnchorPosition.LEFT_TOP);

+

+        // Setup and Position the LEFT Legend

+        XYLegendWidget legendWidget_LEFT = myXYPlot_LEFT.getLegendWidget();

+        legendWidget_LEFT.setTableModel(new DynamicTableModel(1, 3));

+        legendWidget_LEFT.getTextPaint().setTextSize(20);

+        legendWidget_LEFT.setSize(new SizeMetrics(100, SizeLayoutType.ABSOLUTE, 75, SizeLayoutType.FILL));

+        legendWidget_LEFT.setPadding(1, 1, 1, 1);

+        myXYPlot_LEFT.getGraphWidget().position(

+                55, XLayoutStyle.ABSOLUTE_FROM_LEFT, 15, YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.LEFT_TOP);

+        

+        // Setup and Position the RIGHT Legend

+        XYLegendWidget legendWidget_RIGHT = myXYPlot_RIGHT.getLegendWidget();

+        legendWidget_RIGHT.setTableModel(new DynamicTableModel(1, 3));

+        legendWidget_RIGHT.getTextPaint().setTextSize(20);

+        legendWidget_RIGHT.setSize(new SizeMetrics(100, SizeLayoutType.ABSOLUTE, 110, SizeLayoutType.ABSOLUTE));

+        legendWidget_RIGHT.setPadding(1, 1, 1, 1);

+        myXYPlot_RIGHT.getGraphWidget().position(

+                25, XLayoutStyle.ABSOLUTE_FROM_RIGHT, 15, YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.RIGHT_TOP);

+

+        // Setup the Series

+        series1 = new SimpleXYSeries(Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series1");                            

+        series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");

+

+        // Setup the formatters

+        series1Format = new LineAndPointFormatter(Color.rgb(0, 200, 0), Color.rgb(0, 100, 0), null, new PointLabelFormatter(Color.WHITE));

+        series2Format = new LineAndPointFormatter(Color.rgb(0, 0, 200), Color.rgb(0, 0, 100), null, new PointLabelFormatter(Color.WHITE));

+        

+        // Setup the Button

+        button = (Button)findViewById(R.id.toggleSeries2);

+        button.setOnClickListener(this);

+    }

+    

+    @Override    

+    protected void onResume() {        

+        super.onResume();

+

+        updateView();

+    	

+    }

+    

+    private void updateView() {

+

+    	// Remove all current series from each plot

+        Iterator<XYSeries> iterator1 = myXYPlot_LEFT.getSeriesSet().iterator();

+        while(iterator1.hasNext()) {

+        	XYSeries setElement = iterator1.next();

+        	myXYPlot_LEFT.removeSeries(setElement);

+        }

+        Iterator<XYSeries> iterator2 = myXYPlot_RIGHT.getSeriesSet().iterator();

+        while(iterator2.hasNext()) {

+        	XYSeries setElement = iterator2.next();

+        	myXYPlot_RIGHT.removeSeries(setElement);

+        }

+    	

+    	// Add series to each plot as needed.

+        myXYPlot_LEFT.addSeries(series1, series1Format);

+        if (series2_onRight) {

+        	myXYPlot_RIGHT.addSeries(series2, series2Format);

+        } else {

+        	myXYPlot_LEFT.addSeries(series2, series2Format);

+        }

+

+        // Finalise each Plot based on whether they have any series or not.

+    	if (! myXYPlot_RIGHT.getSeriesSet().isEmpty()) {

+    		myXYPlot_RIGHT.setVisibility(XYPlot.VISIBLE);

+    		myXYPlot_RIGHT.redraw();

+    	} else {

+    		myXYPlot_RIGHT.setVisibility(XYPlot.INVISIBLE);

+    	}

+    	

+    	if (! myXYPlot_LEFT.getSeriesSet().isEmpty()) {

+    		myXYPlot_LEFT.setVisibility(XYPlot.VISIBLE);

+    		myXYPlot_LEFT.redraw();

+    	} else {

+    		myXYPlot_LEFT.setVisibility(XYPlot.INVISIBLE);

+    	}

+    	

+    }

+

+	@Override

+	public void onClick(View v) {

+    	if (series2_onRight) {

+    		series2_onRight = false;

+    	} else {

+    		series2_onRight = true;

+    	}

+        updateView();

+	}

+}

diff --git a/Examples/DemoApp/src/com/androidplot/demos/DynamicXYPlotActivity.java b/Examples/DemoApp/src/com/androidplot/demos/DynamicXYPlotActivity.java
new file mode 100644
index 0000000..b4e6456
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/DynamicXYPlotActivity.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2013 AndroidPlot.com
+ *
+ *    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 com.androidplot.demos;
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.os.Bundle;
+import com.androidplot.Plot;
+import com.androidplot.util.PixelUtils;
+import com.androidplot.xy.XYSeries;
+import com.androidplot.xy.*;
+
+import java.text.DecimalFormat;
+import java.util.Observable;
+import java.util.Observer;
+
+public class DynamicXYPlotActivity extends Activity {
+
+    // redraws a plot whenever an update is received:
+    private class MyPlotUpdater implements Observer {
+        Plot plot;
+
+        public MyPlotUpdater(Plot plot) {
+            this.plot = plot;
+        }
+
+        @Override
+        public void update(Observable o, Object arg) {
+            plot.redraw();
+        }
+    }
+
+    private XYPlot dynamicPlot;
+    private MyPlotUpdater plotUpdater;
+    SampleDynamicXYDatasource data;
+    private Thread myThread;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+
+        // android boilerplate stuff
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.dynamicxyplot_example);
+
+        // get handles to our View defined in layout.xml:
+        dynamicPlot = (XYPlot) findViewById(R.id.dynamicXYPlot);
+
+        plotUpdater = new MyPlotUpdater(dynamicPlot);
+
+        // only display whole numbers in domain labels
+        dynamicPlot.getGraphWidget().setDomainValueFormat(new DecimalFormat("0"));
+
+        // getInstance and position datasets:
+        data = new SampleDynamicXYDatasource();
+        SampleDynamicSeries sine1Series = new SampleDynamicSeries(data, 0, "Sine 1");
+        SampleDynamicSeries sine2Series = new SampleDynamicSeries(data, 1, "Sine 2");
+
+        LineAndPointFormatter formatter1 = new LineAndPointFormatter(
+                                Color.rgb(0, 0, 0), null, null, null);
+        formatter1.getLinePaint().setStrokeJoin(Paint.Join.ROUND);
+        formatter1.getLinePaint().setStrokeWidth(10);
+        dynamicPlot.addSeries(sine1Series,
+                formatter1);
+
+        LineAndPointFormatter formatter2 =
+                new LineAndPointFormatter(Color.rgb(0, 0, 200), null, null, null);
+        formatter2.getLinePaint().setStrokeWidth(10);
+        formatter2.getLinePaint().setStrokeJoin(Paint.Join.ROUND);
+
+        //formatter2.getFillPaint().setAlpha(220);
+        dynamicPlot.addSeries(sine2Series, formatter2);
+
+        // hook up the plotUpdater to the data model:
+        data.addObserver(plotUpdater);
+
+        // thin out domain tick labels so they dont overlap each other:
+        dynamicPlot.setDomainStepMode(XYStepMode.INCREMENT_BY_VAL);
+        dynamicPlot.setDomainStepValue(5);
+
+        dynamicPlot.setRangeStepMode(XYStepMode.INCREMENT_BY_VAL);
+        dynamicPlot.setRangeStepValue(10);
+
+        dynamicPlot.setRangeValueFormat(new DecimalFormat("###.#"));
+
+        // uncomment this line to freeze the range boundaries:
+        dynamicPlot.setRangeBoundaries(-100, 100, BoundaryMode.FIXED);
+
+        // create a dash effect for domain and range grid lines:
+        DashPathEffect dashFx = new DashPathEffect(
+                new float[] {PixelUtils.dpToPix(3), PixelUtils.dpToPix(3)}, 0);
+        dynamicPlot.getGraphWidget().getDomainGridLinePaint().setPathEffect(dashFx);
+        dynamicPlot.getGraphWidget().getRangeGridLinePaint().setPathEffect(dashFx);
+
+
+    }
+
+    @Override
+    public void onResume() {
+        // kick off the data generating thread:
+        myThread = new Thread(data);
+        myThread.start();
+        super.onResume();
+    }
+
+    @Override
+    public void onPause() {
+        data.stopThread();
+        super.onPause();
+    }
+
+    class SampleDynamicXYDatasource implements Runnable {
+
+        // encapsulates management of the observers watching this datasource for update events:
+        class MyObservable extends Observable {
+            @Override
+            public void notifyObservers() {
+                setChanged();
+                super.notifyObservers();
+            }
+        }
+
+        private static final double FREQUENCY = 5; // larger is lower frequency
+        private static final int MAX_AMP_SEED = 100;
+        private static final int MIN_AMP_SEED = 10;
+        private static final int AMP_STEP = 1;
+        public static final int SINE1 = 0;
+        public static final int SINE2 = 1;
+        private static final int SAMPLE_SIZE = 30;
+        private int phase = 0;
+        private int sinAmp = 1;
+        private MyObservable notifier;
+        private boolean keepRunning = false;
+
+        {
+            notifier = new MyObservable();
+        }
+
+        public void stopThread() {
+            keepRunning = false;
+        }
+
+        //@Override
+        public void run() {
+            try {
+                keepRunning = true;
+                boolean isRising = true;
+                while (keepRunning) {
+
+                    Thread.sleep(10); // decrease or remove to speed up the refresh rate.
+                    phase++;
+                    if (sinAmp >= MAX_AMP_SEED) {
+                        isRising = false;
+                    } else if (sinAmp <= MIN_AMP_SEED) {
+                        isRising = true;
+                    }
+
+                    if (isRising) {
+                        sinAmp += AMP_STEP;
+                    } else {
+                        sinAmp -= AMP_STEP;
+                    }
+                    notifier.notifyObservers();
+                }
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
+        public int getItemCount(int series) {
+            return SAMPLE_SIZE;
+        }
+
+        public Number getX(int series, int index) {
+            if (index >= SAMPLE_SIZE) {
+                throw new IllegalArgumentException();
+            }
+            return index;
+        }
+
+        public Number getY(int series, int index) {
+            if (index >= SAMPLE_SIZE) {
+                throw new IllegalArgumentException();
+            }
+            double angle = (index + (phase))/FREQUENCY;
+            double amp = sinAmp * Math.sin(angle);
+            switch (series) {
+                case SINE1:
+                    return amp;
+                case SINE2:
+                    return -amp;
+                default:
+                    throw new IllegalArgumentException();
+            }
+        }
+
+        public void addObserver(Observer observer) {
+            notifier.addObserver(observer);
+        }
+
+        public void removeObserver(Observer observer) {
+            notifier.deleteObserver(observer);
+        }
+
+    }
+
+    class SampleDynamicSeries implements XYSeries {
+        private SampleDynamicXYDatasource datasource;
+        private int seriesIndex;
+        private String title;
+
+        public SampleDynamicSeries(SampleDynamicXYDatasource datasource, int seriesIndex, String title) {
+            this.datasource = datasource;
+            this.seriesIndex = seriesIndex;
+            this.title = title;
+        }
+
+        @Override
+        public String getTitle() {
+            return title;
+        }
+
+        @Override
+        public int size() {
+            return datasource.getItemCount(seriesIndex);
+        }
+
+        @Override
+        public Number getX(int index) {
+            return datasource.getX(seriesIndex, index);
+        }
+
+        @Override
+        public Number getY(int index) {
+            return datasource.getY(seriesIndex, index);
+        }
+    }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/GlobalDefs.java b/Examples/DemoApp/src/com/androidplot/demos/GlobalDefs.java
new file mode 100644
index 0000000..28b74d9
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/GlobalDefs.java
@@ -0,0 +1,24 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+public interface GlobalDefs {

+    static final float PLOT_TITLE_FONT_SIZE_DP = 15;

+    static final float PLOT_DOMAIN_LABEL_FONT_SIZE_DP = 10;

+    static final float PLOT_RANGE_LABEL_FONT_SIZE_DP = 10;

+    static final float PLOT_TICK_LABEL_FONT_SIZE_DP = 10;

+}

diff --git a/Examples/DemoApp/src/com/androidplot/demos/ListViewActivity.java b/Examples/DemoApp/src/com/androidplot/demos/ListViewActivity.java
new file mode 100644
index 0000000..d80ac50
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/ListViewActivity.java
@@ -0,0 +1,96 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+import android.app.Activity;

+import android.content.Context;

+import android.graphics.Color;

+import android.os.Bundle;

+import android.view.LayoutInflater;

+import android.view.View;

+import android.view.ViewGroup;

+import android.widget.ArrayAdapter;

+import android.widget.ListView;

+import com.androidplot.Plot;

+import com.androidplot.xy.XYSeries;

+import com.androidplot.xy.LineAndPointFormatter;

+import com.androidplot.xy.SimpleXYSeries;

+import com.androidplot.xy.XYPlot;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Random;

+

+public class ListViewActivity extends Activity {

+    private static final int NUM_PLOTS = 10;

+    private static final int NUM_POINTS_PER_SERIES = 10;

+    private static final int NUM_SERIES_PER_PLOT = 5;

+    private ListView lv;

+

+    public void onCreate(Bundle savedInstanceState) {

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.listview_example);

+        lv = (ListView) findViewById(R.id.listView1);

+        lv.setAdapter(new MyViewAdapter(getApplicationContext(), R.layout.listview_example_item, null));

+    }

+

+    class MyViewAdapter extends ArrayAdapter<View> {

+        public MyViewAdapter(Context context, int resId, List<View> views) {

+            super(context, resId, views);

+        }

+

+        @Override

+        public int getCount() {

+            return 5;

+        }

+

+        @Override

+        public View getView(int pos, View convertView, ViewGroup parent) {

+            LayoutInflater inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

+

+            View v = convertView;

+            if (v == null) {

+                v = inf.inflate(R.layout.listview_example_item, parent, false);

+            }

+

+            Plot p = (XYPlot) v.findViewById(R.id.xyplot);

+            Random generator = new Random();

+

+            for (int k = 0; k < NUM_SERIES_PER_PLOT; k++) {

+                ArrayList<Number> nums = new ArrayList<Number>();

+                for (int j = 0; j < NUM_POINTS_PER_SERIES; j++) {

+                    nums.add(generator.nextFloat());

+                }

+

+                double rl = Math.random();

+                double gl = Math.random();

+                double bl = Math.random();

+

+                double rp = Math.random();

+                double gp = Math.random();

+                double bp = Math.random();

+

+                XYSeries series = new SimpleXYSeries(nums, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "S" + k);

+                p.addSeries(series, new LineAndPointFormatter(

+                        Color.rgb(new Double(rl * 255).intValue(), new Double(gl * 255).intValue(), new Double(bl * 255).intValue()),

+                        Color.rgb(new Double(rp * 255).intValue(), new Double(gp * 255).intValue(), new Double(bp * 255).intValue()),

+                        null, null));

+            }

+            return v;

+        }

+    }

+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/MainActivity.java b/Examples/DemoApp/src/com/androidplot/demos/MainActivity.java
new file mode 100644
index 0000000..51c1441
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/MainActivity.java
@@ -0,0 +1,132 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+import android.app.Activity;

+import android.content.Intent;

+import android.os.Bundle;

+import android.view.View;

+import android.widget.Button;

+

+public class MainActivity extends Activity

+{

+    /** Called when the activity is first created. */

+    @Override

+    public void onCreate(Bundle savedInstanceState)

+    {

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.main);

+

+        Button startSimplePieExButton = (Button) findViewById(R.id.startSimplePieExButton);

+        startSimplePieExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, SimplePieChartActivity.class));

+            }

+        });

+

+        Button startDynamicXYExButton = (Button)findViewById(R.id.startDynamicXYExButton);

+        startDynamicXYExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, DynamicXYPlotActivity.class));

+            }

+        });

+

+        Button startSimpleXYExButton = (Button) findViewById(R.id.startSimpleXYExButton);

+        startSimpleXYExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, SimpleXYPlotActivity.class));

+            }

+        });

+

+        Button startBarPlotExButton = (Button) findViewById(R.id.startBarPlotExButton);

+        startBarPlotExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, BarPlotExampleActivity.class));

+            }

+        });

+

+        Button startOrSensorExButton = (Button) findViewById(R.id.startOrSensorExButton);

+        startOrSensorExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, OrientationSensorExampleActivity.class));

+            }

+        });

+

+        Button startTimeSeriesExButon = (Button)findViewById(R.id.startTimeSeriesExButton);

+        startTimeSeriesExButon.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, TimeSeriesActivity.class));

+            }

+        });

+

+        Button startStepChartExButton = (Button)findViewById(R.id.startStepChartExButton);

+        startStepChartExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, StepChartExampleActivity.class));

+            }

+        });

+

+        Button startScrollZoomExButton = (Button)findViewById(R.id.startScrollZoomButton);

+        startScrollZoomExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, TouchZoomExampleActivity.class));

+            }

+        });

+

+        Button startXyRegionExampleButton = (Button)findViewById(R.id.startXyRegionExampleButton);

+        startXyRegionExampleButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, XYRegionExampleActivity.class));

+            }

+        });

+

+

+        Button listViewExButton = (Button)findViewById(R.id.startXyListViewExButton);

+        listViewExButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, ListViewActivity.class));

+            }

+        });

+

+        Button startDualScaleExampleButton = (Button)findViewById(R.id.startDualScaleExampleButton);

+        startDualScaleExampleButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                startActivity(new Intent(MainActivity.this, DualScaleXYPlotExampleActivity.class));

+            }

+        });

+

+        Button startXYPlotWithBgImgExampleButton = (Button)findViewById(R.id.startXYPlotWithBgImgExample);

+        startXYPlotWithBgImgExampleButton.setOnClickListener(new View.OnClickListener() {

+                    @Override

+                    public void onClick(View view) {

+                        startActivity(new Intent(MainActivity.this, XYPlotWithBgImgActivity.class));

+                    }

+                });

+

+    }

+}

diff --git a/Examples/DemoApp/src/com/androidplot/demos/OrientationSensorExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/OrientationSensorExampleActivity.java
new file mode 100644
index 0000000..23012bd
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/OrientationSensorExampleActivity.java
@@ -0,0 +1,249 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+import android.app.Activity;

+import android.content.Context;

+import android.graphics.Color;

+import android.hardware.Sensor;

+import android.hardware.SensorEvent;

+import android.hardware.SensorEventListener;

+import android.hardware.SensorManager;

+import android.os.Bundle;

+import android.view.View;

+import android.widget.CheckBox;

+import android.widget.CompoundButton;

+import com.androidplot.Plot;

+import com.androidplot.util.PlotStatistics;

+import com.androidplot.util.Redrawer;

+import com.androidplot.xy.*;

+import java.text.DecimalFormat;

+import java.util.Arrays;

+

+// Monitor the phone's orientation sensor and plot the resulting azimuth pitch and roll values.

+// See: http://developer.android.com/reference/android/hardware/SensorEvent.html

+public class OrientationSensorExampleActivity extends Activity implements SensorEventListener

+{

+

+    private static final int HISTORY_SIZE = 300;            // number of points to plot in history

+    private SensorManager sensorMgr = null;

+    private Sensor orSensor = null;

+

+    private XYPlot aprLevelsPlot = null;

+    private XYPlot aprHistoryPlot = null;

+

+    private CheckBox hwAcceleratedCb;

+    private CheckBox showFpsCb;

+    //private SimpleXYSeries aprLevelsSeries = null;

+    private SimpleXYSeries aLvlSeries;

+    private SimpleXYSeries pLvlSeries;

+    private SimpleXYSeries rLvlSeries;

+    private SimpleXYSeries azimuthHistorySeries = null;

+    private SimpleXYSeries pitchHistorySeries = null;

+    private SimpleXYSeries rollHistorySeries = null;

+

+    private Redrawer redrawer;

+

+    /** Called when the activity is first created. */

+    @Override

+    public void onCreate(Bundle savedInstanceState) {

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.orientation_sensor_example);

+

+        // setup the APR Levels plot:

+        aprLevelsPlot = (XYPlot) findViewById(R.id.aprLevelsPlot);

+        aprLevelsPlot.setDomainBoundaries(-1, 1, BoundaryMode.FIXED);

+        aprLevelsPlot.getGraphWidget().getDomainLabelPaint().setColor(Color.TRANSPARENT);

+

+        aLvlSeries = new SimpleXYSeries("A");

+        pLvlSeries = new SimpleXYSeries("P");

+        rLvlSeries = new SimpleXYSeries("R");

+

+        aprLevelsPlot.addSeries(aLvlSeries,

+                        new BarFormatter(Color.rgb(0, 200, 0), Color.rgb(0, 80, 0)));

+        aprLevelsPlot.addSeries(pLvlSeries,

+                        new BarFormatter(Color.rgb(200, 0, 0), Color.rgb(0, 80, 0)));

+        aprLevelsPlot.addSeries(rLvlSeries,

+                        new BarFormatter(Color.rgb(0, 0, 200), Color.rgb(0, 80, 0)));

+

+        aprLevelsPlot.setDomainStepValue(3);

+        aprLevelsPlot.setTicksPerRangeLabel(3);

+

+        // per the android documentation, the minimum and maximum readings we can get from

+        // any of the orientation sensors is -180 and 359 respectively so we will fix our plot's

+        // boundaries to those values.  If we did not do this, the plot would auto-range which

+        // can be visually confusing in the case of dynamic plots.

+        aprLevelsPlot.setRangeBoundaries(-180, 359, BoundaryMode.FIXED);

+

+        // update our domain and range axis labels:

+        aprLevelsPlot.setDomainLabel("");

+        aprLevelsPlot.getDomainLabelWidget().pack();

+        aprLevelsPlot.setRangeLabel("Angle (Degs)");

+        aprLevelsPlot.getRangeLabelWidget().pack();

+        aprLevelsPlot.setGridPadding(15, 0, 15, 0);

+        aprLevelsPlot.setRangeValueFormat(new DecimalFormat("#"));

+

+        // setup the APR History plot:

+        aprHistoryPlot = (XYPlot) findViewById(R.id.aprHistoryPlot);

+

+        azimuthHistorySeries = new SimpleXYSeries("Az.");

+        azimuthHistorySeries.useImplicitXVals();

+        pitchHistorySeries = new SimpleXYSeries("Pitch");

+        pitchHistorySeries.useImplicitXVals();

+        rollHistorySeries = new SimpleXYSeries("Roll");

+        rollHistorySeries.useImplicitXVals();

+

+        aprHistoryPlot.setRangeBoundaries(-180, 359, BoundaryMode.FIXED);

+        aprHistoryPlot.setDomainBoundaries(0, HISTORY_SIZE, BoundaryMode.FIXED);

+        aprHistoryPlot.addSeries(azimuthHistorySeries,

+                new LineAndPointFormatter(

+                        Color.rgb(100, 100, 200), null, null, null));

+        aprHistoryPlot.addSeries(pitchHistorySeries,

+                new LineAndPointFormatter(

+                        Color.rgb(100, 200, 100), null, null, null));

+        aprHistoryPlot.addSeries(rollHistorySeries,

+                new LineAndPointFormatter(

+                        Color.rgb(200, 100, 100), null, null, null));

+        aprHistoryPlot.setDomainStepMode(XYStepMode.INCREMENT_BY_VAL);

+        aprHistoryPlot.setDomainStepValue(HISTORY_SIZE/10);

+        aprHistoryPlot.setTicksPerRangeLabel(3);

+        aprHistoryPlot.setDomainLabel("Sample Index");

+        aprHistoryPlot.getDomainLabelWidget().pack();

+        aprHistoryPlot.setRangeLabel("Angle (Degs)");

+        aprHistoryPlot.getRangeLabelWidget().pack();

+

+        aprHistoryPlot.setRangeValueFormat(new DecimalFormat("#"));

+        aprHistoryPlot.setDomainValueFormat(new DecimalFormat("#"));

+

+        // setup checkboxes:

+        hwAcceleratedCb = (CheckBox) findViewById(R.id.hwAccelerationCb);

+        final PlotStatistics levelStats = new PlotStatistics(1000, false);

+        final PlotStatistics histStats = new PlotStatistics(1000, false);

+

+        aprLevelsPlot.addListener(levelStats);

+        aprHistoryPlot.addListener(histStats);

+        hwAcceleratedCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                if(b) {

+                    aprLevelsPlot.setLayerType(View.LAYER_TYPE_NONE, null);

+                    aprHistoryPlot.setLayerType(View.LAYER_TYPE_NONE, null);

+                } else {

+                    aprLevelsPlot.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

+                    aprHistoryPlot.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

+                }

+            }

+        });

+

+        showFpsCb = (CheckBox) findViewById(R.id.showFpsCb);

+        showFpsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                levelStats.setAnnotatePlotEnabled(b);

+                histStats.setAnnotatePlotEnabled(b);

+            }

+        });

+

+        // get a ref to the BarRenderer so we can make some changes to it:

+        BarRenderer barRenderer = (BarRenderer) aprLevelsPlot.getRenderer(BarRenderer.class);

+        if(barRenderer != null) {

+            // make our bars a little thicker than the default so they can be seen better:

+            barRenderer.setBarWidth(25);

+        }

+

+        // register for orientation sensor events:

+        sensorMgr = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);

+        for (Sensor sensor : sensorMgr.getSensorList(Sensor.TYPE_ORIENTATION)) {

+            if (sensor.getType() == Sensor.TYPE_ORIENTATION) {

+                orSensor = sensor;

+            }

+        }

+

+        // if we can't access the orientation sensor then exit:

+        if (orSensor == null) {

+            System.out.println("Failed to attach to orSensor.");

+            cleanup();

+        }

+

+        sensorMgr.registerListener(this, orSensor, SensorManager.SENSOR_DELAY_UI);

+

+        redrawer = new Redrawer(

+                Arrays.asList(new Plot[]{aprHistoryPlot, aprLevelsPlot}),

+                100, false);

+    }

+

+    @Override

+    public void onResume() {

+        super.onResume();

+        redrawer.start();

+    }

+

+    @Override

+    public void onPause() {

+        redrawer.pause();

+        super.onPause();

+    }

+

+    @Override

+    public void onDestroy() {

+        redrawer.finish();

+        super.onDestroy();

+    }

+

+    private void cleanup() {

+        // aunregister with the orientation sensor before exiting:

+        sensorMgr.unregisterListener(this);

+        finish();

+    }

+

+

+    // Called whenever a new orSensor reading is taken.

+    @Override

+    public synchronized void onSensorChanged(SensorEvent sensorEvent) {

+

+        // update level data:

+        aLvlSeries.setModel(Arrays.asList(

+                new Number[]{sensorEvent.values[0]}),

+                SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+

+        pLvlSeries.setModel(Arrays.asList(

+                        new Number[]{sensorEvent.values[1]}),

+                        SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+

+        rLvlSeries.setModel(Arrays.asList(

+                        new Number[]{sensorEvent.values[2]}),

+                        SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);

+

+        // get rid the oldest sample in history:

+        if (rollHistorySeries.size() > HISTORY_SIZE) {

+            rollHistorySeries.removeFirst();

+            pitchHistorySeries.removeFirst();

+            azimuthHistorySeries.removeFirst();

+        }

+

+        // add the latest history sample:

+        azimuthHistorySeries.addLast(null, sensorEvent.values[0]);

+        pitchHistorySeries.addLast(null, sensorEvent.values[1]);

+        rollHistorySeries.addLast(null, sensorEvent.values[2]);

+    }

+

+

+    @Override

+    public void onAccuracyChanged(Sensor sensor, int i) {

+        // Not interested in this event

+    }

+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/SimplePieChartActivity.java b/Examples/DemoApp/src/com/androidplot/demos/SimplePieChartActivity.java
new file mode 100644
index 0000000..f90160b
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/SimplePieChartActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012 AndroidPlot.com
+ *
+ *    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 com.androidplot.demos;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import com.androidplot.pie.PieChart;
+import com.androidplot.pie.PieRenderer;
+import com.androidplot.pie.Segment;
+import com.androidplot.pie.SegmentFormatter;
+
+/**
+ * The simplest possible example of using AndroidPlot to plot some data.
+ */
+public class SimplePieChartActivity extends Activity
+{
+
+    private TextView donutSizeTextView;
+    private SeekBar donutSizeSeekBar;
+
+    private PieChart pie;
+
+    private Segment s1;
+    private Segment s2;
+    private Segment s3;
+    private Segment s4;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pie_chart);
+
+        // initialize our XYPlot reference:
+        pie = (PieChart) findViewById(R.id.mySimplePieChart);
+
+
+        donutSizeSeekBar = (SeekBar) findViewById(R.id.donutSizeSeekBar);
+
+        donutSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {}
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {}
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                pie.getRenderer(PieRenderer.class).setDonutSize(seekBar.getProgress()/100f,
+                        PieRenderer.DonutMode.PERCENT);
+                pie.redraw();
+                updateDonutText();
+            }
+        });
+
+        donutSizeTextView = (TextView) findViewById(R.id.donutSizeTextView);
+        updateDonutText();
+
+        s1 = new Segment("s1", 10);
+        s2 = new Segment("s2", 1);
+        s3 = new Segment("s3", 10);
+        s4 = new Segment("s4", 10);
+
+        EmbossMaskFilter emf = new EmbossMaskFilter(
+                new float[]{1, 1, 1}, 0.4f, 10, 8.2f);
+
+        SegmentFormatter sf1 = new SegmentFormatter();
+        sf1.configure(getApplicationContext(), R.xml.pie_segment_formatter1);
+
+        sf1.getFillPaint().setMaskFilter(emf);
+
+        SegmentFormatter sf2 = new SegmentFormatter();
+        sf2.configure(getApplicationContext(), R.xml.pie_segment_formatter2);
+
+        sf2.getFillPaint().setMaskFilter(emf);
+
+        SegmentFormatter sf3 = new SegmentFormatter();
+        sf3.configure(getApplicationContext(), R.xml.pie_segment_formatter3);
+
+        sf3.getFillPaint().setMaskFilter(emf);
+
+        SegmentFormatter sf4 = new SegmentFormatter();
+        sf4.configure(getApplicationContext(), R.xml.pie_segment_formatter4);
+
+        sf4.getFillPaint().setMaskFilter(emf);
+
+        pie.addSeries(s1, sf1);
+        pie.addSeries(s2, sf2);
+        pie.addSeries(s3, sf3);
+        pie.addSeries(s4, sf4);
+
+        pie.getBorderPaint().setColor(Color.TRANSPARENT);
+        pie.getBackgroundPaint().setColor(Color.TRANSPARENT);
+    }
+
+    protected void updateDonutText() {
+        donutSizeTextView.setText(donutSizeSeekBar.getProgress() + "%");
+    }
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/SimpleXYPlotActivity.java b/Examples/DemoApp/src/com/androidplot/demos/SimpleXYPlotActivity.java
new file mode 100644
index 0000000..985e5ff
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/SimpleXYPlotActivity.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+import android.app.Activity;

+import android.os.Bundle;

+import android.view.WindowManager;

+import com.androidplot.xy.SimpleXYSeries;

+import com.androidplot.xy.XYSeries;

+import com.androidplot.xy.*;

+import java.util.Arrays;

+

+/**

+ * A straightforward example of using AndroidPlot to plot some data.

+ */

+public class SimpleXYPlotActivity extends Activity

+{

+

+    private XYPlot plot;

+

+    @Override

+    public void onCreate(Bundle savedInstanceState)

+    {

+        super.onCreate(savedInstanceState);

+

+        // fun little snippet that prevents users from taking screenshots

+        // on ICS+ devices :-)

+        getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,

+                                 WindowManager.LayoutParams.FLAG_SECURE);

+        setContentView(R.layout.simple_xy_plot_example);

+

+        // initialize our XYPlot reference:

+        plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);

+

+        // Create a couple arrays of y-values to plot:

+        Number[] series1Numbers = {1, 8, 5, 2, 7, 4};

+        Number[] series2Numbers = {4, 6, 3, 8, 2, 10};

+

+        // Turn the above arrays into XYSeries':

+        XYSeries series1 = new SimpleXYSeries(

+                Arrays.asList(series1Numbers),          // SimpleXYSeries takes a List so turn our array into a List

+                SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, // Y_VALS_ONLY means use the element index as the x value

+                "Series1");                             // Set the display title of the series

+

+        // same as above

+        XYSeries series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");

+

+        // Create a formatter to use for drawing a series using LineAndPointRenderer

+        // and configure it from xml:

+        LineAndPointFormatter series1Format = new LineAndPointFormatter();

+        series1Format.setPointLabelFormatter(new PointLabelFormatter());

+        series1Format.configure(getApplicationContext(),

+                R.xml.line_point_formatter_with_plf1);

+

+        // add a new series' to the xyplot:

+        plot.addSeries(series1, series1Format);

+

+        // same as above:

+        LineAndPointFormatter series2Format = new LineAndPointFormatter();

+        series2Format.setPointLabelFormatter(new PointLabelFormatter());

+        series2Format.configure(getApplicationContext(),

+                R.xml.line_point_formatter_with_plf2);

+        plot.addSeries(series2, series2Format);

+

+        // reduce the number of range labels

+        plot.setTicksPerRangeLabel(3);

+        plot.getGraphWidget().setDomainLabelOrientation(-45);

+

+    }

+}

diff --git a/Examples/DemoApp/src/com/androidplot/demos/StepChartExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/StepChartExampleActivity.java
new file mode 100644
index 0000000..660d03a
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/StepChartExampleActivity.java
@@ -0,0 +1,140 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+

+import android.app.Activity;

+import android.graphics.*;

+import android.os.Bundle;

+import com.androidplot.xy.SimpleXYSeries;

+import com.androidplot.xy.XYSeries;

+import com.androidplot.xy.*;

+

+import java.text.DecimalFormat;

+import java.text.FieldPosition;

+import java.text.Format;

+import java.text.ParsePosition;

+import java.util.Arrays;

+

+public class StepChartExampleActivity extends Activity

+{

+

+    private XYPlot mySimpleXYPlot;

+

+    @Override

+    public void onCreate(Bundle savedInstanceState)

+    {

+

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.step_chart_example);

+

+        // initialize our XYPlot reference:

+        mySimpleXYPlot = (XYPlot) findViewById(R.id.stepChartExamplePlot);

+

+        // y-vals to plot:

+        Number[] series1Numbers = {1, 2, 3, 4, 2, 3, 4, 2, 2, 2, 3, 4, 2, 3, 2, 2};

+        // create our series from our array of nums:

+        XYSeries series2 = new SimpleXYSeries(

+                Arrays.asList(series1Numbers),

+                SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,

+                "Thread #1");

+

+        //mySimpleXYPlot.getLayoutManager().remove(mySimpleXYPlot.getLegendWidget());

+

+

+        mySimpleXYPlot.getGraphWidget().getGridBackgroundPaint().setColor(Color.WHITE);

+        mySimpleXYPlot.getGraphWidget().getDomainGridLinePaint().setColor(Color.BLACK);

+        mySimpleXYPlot.getGraphWidget().getDomainGridLinePaint().setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));

+        mySimpleXYPlot.getGraphWidget().getRangeGridLinePaint().setColor(Color.BLACK);

+        mySimpleXYPlot.getGraphWidget().getRangeGridLinePaint().setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));

+        mySimpleXYPlot.getGraphWidget().getDomainOriginLinePaint().setColor(Color.BLACK);

+        mySimpleXYPlot.getGraphWidget().getRangeOriginLinePaint().setColor(Color.BLACK);

+        mySimpleXYPlot.getGraphWidget().setMarginRight(5);

+

+        // Create a formatter to use for drawing a series using LineAndPointRenderer:

+        LineAndPointFormatter series1Format = new LineAndPointFormatter(

+                Color.rgb(0, 100, 0),                   // line color

+                Color.rgb(0, 100, 0),                   // point color

+                Color.rgb(100, 200, 0), null);                // fill color

+

+

+        // setup our line fill paint to be a slightly transparent gradient:

+        Paint lineFill = new Paint();

+        lineFill.setAlpha(200);

+        lineFill.setShader(new LinearGradient(0, 0, 0, 250, Color.WHITE, Color.BLUE, Shader.TileMode.MIRROR));

+

+        StepFormatter stepFormatter  = new StepFormatter(Color.rgb(0, 0,0), Color.BLUE);

+        stepFormatter.getLinePaint().setStrokeWidth(1);

+

+        stepFormatter.getLinePaint().setAntiAlias(false);

+        stepFormatter.setFillPaint(lineFill);

+        mySimpleXYPlot.addSeries(series2, stepFormatter);

+

+        // adjust the domain/range ticks to make more sense; label per tick for range and label per 5 ticks domain:

+        mySimpleXYPlot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 1);

+        mySimpleXYPlot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1);

+        mySimpleXYPlot.setTicksPerRangeLabel(1);

+        mySimpleXYPlot.setTicksPerDomainLabel(5);

+

+        // customize our domain/range labels

+        //mySimpleXYPlot.setDomainLabel("Time (Secs)");

+        //mySimpleXYPlot.setRangeLabel("Server State");

+

+        //mySimpleXYPlot.getGraphWidget().getGridLinePaint().setAlpha(0);

+      

+

+

+        // get rid of decimal points in our domain labels:

+        mySimpleXYPlot.setDomainValueFormat(new DecimalFormat("0"));

+

+        // create a custom formatter to draw our state names as range tick labels:

+        mySimpleXYPlot.setRangeValueFormat(new Format() {

+            @Override

+            public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {

+                Number num = (Number) obj;

+                switch(num.intValue()) {

+                    case 1:

+                        toAppendTo.append("Init");

+                        break;

+                    case 2:

+                        toAppendTo.append("Idle");

+                        break;

+                    case 3:

+                        toAppendTo.append("Recv");

+                        break;

+                    case 4:

+                        toAppendTo.append("Send");

+                        break;

+                    default:

+                        toAppendTo.append("Unknown");

+                        break;

+                }

+                return toAppendTo;

+            }

+

+            @Override

+            public Object parseObject(String source, ParsePosition pos) {

+                return null;

+            }

+        });

+

+        // by default, AndroidPlot displays developer guides to aid in laying out your plot.

+        // To get rid of them call disableAllMarkup():

+        //mySimpleXYPlot.disableAllMarkup();

+

+    }

+}

diff --git a/Examples/DemoApp/src/com/androidplot/demos/TimeSeriesActivity.java b/Examples/DemoApp/src/com/androidplot/demos/TimeSeriesActivity.java
new file mode 100644
index 0000000..0e7904c
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/TimeSeriesActivity.java
@@ -0,0 +1,128 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+import android.app.Activity;

+import android.graphics.*;

+import android.os.Bundle;

+import com.androidplot.xy.SimpleXYSeries;

+import com.androidplot.xy.XYSeries;

+import com.androidplot.xy.*;

+

+import java.text.*;

+import java.util.Arrays;

+import java.util.Date;

+

+public class TimeSeriesActivity extends Activity

+{

+

+    private XYPlot plot1;

+

+    @Override

+    public void onCreate(Bundle savedInstanceState)

+    {

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.time_series_example);

+

+        plot1 = (XYPlot) findViewById(R.id.plot1);

+        Number[] numSightings = {5, 8, 9, 2, 5};

+

+        // an array of years in milliseconds:

+        Number[] years = {

+                978307200,  // 2001

+                1009843200, // 2002

+                1041379200, // 2003

+                1072915200, // 2004

+                1104537600  // 2005

+        };

+        // create our series from our array of nums:

+        XYSeries series2 = new SimpleXYSeries(

+                Arrays.asList(years),

+                Arrays.asList(numSightings),

+                "Sightings in USA");

+

+        plot1.getGraphWidget().getGridBackgroundPaint().setColor(Color.WHITE);

+        plot1.getGraphWidget().getDomainGridLinePaint().setColor(Color.BLACK);

+        plot1.getGraphWidget().getDomainGridLinePaint().

+                setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));

+        plot1.getGraphWidget().getRangeGridLinePaint().setColor(Color.BLACK);

+        plot1.getGraphWidget().getRangeGridLinePaint().

+                setPathEffect(new DashPathEffect(new float[]{1, 1}, 1));

+        plot1.getGraphWidget().getDomainOriginLinePaint().setColor(Color.BLACK);

+        plot1.getGraphWidget().getRangeOriginLinePaint().setColor(Color.BLACK);

+

+        // Create a formatter to use for drawing a series using LineAndPointRenderer:

+        LineAndPointFormatter series1Format = new LineAndPointFormatter(

+                Color.rgb(0, 100, 0),                   // line color

+                Color.rgb(0, 100, 0),                   // point color

+                Color.rgb(100, 200, 0), null);                // fill color

+

+

+        // setup our line fill paint to be a slightly transparent gradient:

+        Paint lineFill = new Paint();

+        lineFill.setAlpha(200);

+

+        // ugly usage of LinearGradient. unfortunately there's no way to determine the actual size of

+        // a View from within onCreate.  one alternative is to specify a dimension in resources

+        // and use that accordingly.  at least then the values can be customized for the device type and orientation.

+        lineFill.setShader(new LinearGradient(0, 0, 200, 200, Color.WHITE, Color.GREEN, Shader.TileMode.CLAMP));

+

+        LineAndPointFormatter formatter  =

+                new LineAndPointFormatter(Color.rgb(0, 0,0), Color.BLUE, Color.RED, null);

+        formatter.setFillPaint(lineFill);

+        plot1.getGraphWidget().setPaddingRight(2);

+        plot1.addSeries(series2, formatter);

+

+        // draw a domain tick for each year:

+        plot1.setDomainStep(XYStepMode.SUBDIVIDE, years.length);

+

+        // customize our domain/range labels

+        plot1.setDomainLabel("Year");

+        plot1.setRangeLabel("# of Sightings");

+

+        // get rid of decimal points in our range labels:

+        plot1.setRangeValueFormat(new DecimalFormat("0"));

+

+        plot1.setDomainValueFormat(new Format() {

+

+            // create a simple date format that draws on the year portion of our timestamp.

+            // see http://download.oracle.com/javase/1.4.2/docs/api/java/text/SimpleDateFormat.html

+            // for a full description of SimpleDateFormat.

+            private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy");

+

+            @Override

+            public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {

+

+                // because our timestamps are in seconds and SimpleDateFormat expects milliseconds

+                // we multiply our timestamp by 1000:

+                long timestamp = ((Number) obj).longValue() * 1000;

+                Date date = new Date(timestamp);

+                return dateFormat.format(date, toAppendTo, pos);

+            }

+

+            @Override

+            public Object parseObject(String source, ParsePosition pos) {

+                return null;

+

+            }

+        });

+

+        // by default, AndroidPlot displays developer guides to aid in laying out your plot.

+        // To get rid of them call disableAllMarkup():

+        //plot1.disableAllMarkup();

+    }

+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/TouchZoomExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/TouchZoomExampleActivity.java
new file mode 100644
index 0000000..65c5c71
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/TouchZoomExampleActivity.java
@@ -0,0 +1,208 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+import java.text.DecimalFormat;

+import java.util.Random;

+

+import android.app.Activity;

+import android.graphics.Color;

+import android.graphics.Paint;

+import android.graphics.PointF;

+import android.os.Bundle;

+import android.util.FloatMath;

+import android.view.MotionEvent;

+import android.view.View;

+import android.view.View.OnTouchListener;

+import android.widget.Button;

+

+import com.androidplot.Plot;

+import com.androidplot.xy.BoundaryMode;

+import com.androidplot.xy.LineAndPointFormatter;

+import com.androidplot.xy.SimpleXYSeries;

+import com.androidplot.xy.XYPlot;

+

+/***********************************

+ * @author David Buezas (david.buezas at gmail.com)

+ * Feel free to copy, modify and use the source as it fits you.

+ * 09/27/2012 nfellows - updated for 0.5.1 and made a few simplifications

+ */

+public class TouchZoomExampleActivity extends Activity implements OnTouchListener {

+    private static final int SERIES_SIZE = 200;

+    private XYPlot mySimpleXYPlot;

+    private Button resetButton;

+    private SimpleXYSeries[] series = null;

+    private PointF minXY;

+    private PointF maxXY;

+

+    public void onCreate(Bundle savedInstanceState) {

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.touch_zoom_example);

+        resetButton = (Button) findViewById(R.id.resetButton);

+        resetButton.setOnClickListener(new View.OnClickListener() {

+            @Override

+            public void onClick(View view) {

+                minXY.x = series[0].getX(0).floatValue();

+                maxXY.x = series[3].getX(series[3].size() - 1).floatValue();

+                mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x, BoundaryMode.FIXED);

+

+                // pre 0.5.1 users should use postRedraw() instead.

+                mySimpleXYPlot.redraw();

+            }

+        });

+        mySimpleXYPlot = (XYPlot) findViewById(R.id.mySimpleXYPlot);

+        mySimpleXYPlot.setOnTouchListener(this);

+        mySimpleXYPlot.getGraphWidget().setTicksPerRangeLabel(2);

+        mySimpleXYPlot.getGraphWidget().setTicksPerDomainLabel(2);

+        mySimpleXYPlot.getGraphWidget().getBackgroundPaint().setColor(Color.TRANSPARENT);

+        mySimpleXYPlot.getGraphWidget().setRangeValueFormat(

+                new DecimalFormat("#####"));

+        mySimpleXYPlot.getGraphWidget().setDomainValueFormat(

+                new DecimalFormat("#####.#"));

+        mySimpleXYPlot.getGraphWidget().setRangeLabelWidth(25);

+        mySimpleXYPlot.setRangeLabel("");

+        mySimpleXYPlot.setDomainLabel("");

+

+        mySimpleXYPlot.setBorderStyle(Plot.BorderStyle.NONE, null, null);

+        //mySimpleXYPlot.disableAllMarkup();

+        series = new SimpleXYSeries[4];

+        int scale = 1;

+        for (int i = 0; i < 4; i++, scale *= 5) {

+            series[i] = new SimpleXYSeries("S" + i);

+            populateSeries(series[i], scale);

+        }

+        mySimpleXYPlot.addSeries(series[3],

+                new LineAndPointFormatter(Color.rgb(50, 0, 0), null,

+                        Color.rgb(100, 0, 0), null));

+        mySimpleXYPlot.addSeries(series[2],

+                new LineAndPointFormatter(Color.rgb(50, 50, 0), null,

+                        Color.rgb(100, 100, 0), null));

+        mySimpleXYPlot.addSeries(series[1],

+                new LineAndPointFormatter(Color.rgb(0, 50, 0), null,

+                        Color.rgb(0, 100, 0), null));

+        mySimpleXYPlot.addSeries(series[0],

+                new LineAndPointFormatter(Color.rgb(0, 0, 0), null,

+                        Color.rgb(0, 0, 150), null));

+        mySimpleXYPlot.redraw();

+        mySimpleXYPlot.calculateMinMaxVals();

+        minXY = new PointF(mySimpleXYPlot.getCalculatedMinX().floatValue(),

+                mySimpleXYPlot.getCalculatedMinY().floatValue());

+        maxXY = new PointF(mySimpleXYPlot.getCalculatedMaxX().floatValue(),

+                mySimpleXYPlot.getCalculatedMaxY().floatValue());

+    }

+

+    private void populateSeries(SimpleXYSeries series, int max) {

+        Random r = new Random();

+        for(int i = 0; i < SERIES_SIZE; i++) {

+            series.addLast(i, r.nextInt(max));

+        }

+    }

+

+    // Definition of the touch states

+    static final int NONE = 0;

+    static final int ONE_FINGER_DRAG = 1;

+    static final int TWO_FINGERS_DRAG = 2;

+    int mode = NONE;

+

+    PointF firstFinger;

+    float distBetweenFingers;

+    boolean stopThread = false;

+

+    @Override

+    public boolean onTouch(View arg0, MotionEvent event) {

+        switch (event.getAction() & MotionEvent.ACTION_MASK) {

+            case MotionEvent.ACTION_DOWN: // Start gesture

+                firstFinger = new PointF(event.getX(), event.getY());

+                mode = ONE_FINGER_DRAG;

+                stopThread = true;

+                break;

+            case MotionEvent.ACTION_UP:

+            case MotionEvent.ACTION_POINTER_UP:

+                mode = NONE;

+                break;

+            case MotionEvent.ACTION_POINTER_DOWN: // second finger

+                distBetweenFingers = spacing(event);

+                // the distance check is done to avoid false alarms

+                if (distBetweenFingers > 5f) {

+                    mode = TWO_FINGERS_DRAG;

+                }

+                break;

+            case MotionEvent.ACTION_MOVE:

+                if (mode == ONE_FINGER_DRAG) {

+                    PointF oldFirstFinger = firstFinger;

+                    firstFinger = new PointF(event.getX(), event.getY());

+                    scroll(oldFirstFinger.x - firstFinger.x);

+                    mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x,

+                            BoundaryMode.FIXED);

+                    mySimpleXYPlot.redraw();

+

+                } else if (mode == TWO_FINGERS_DRAG) {

+                    float oldDist = distBetweenFingers;

+                    distBetweenFingers = spacing(event);

+                    zoom(oldDist / distBetweenFingers);

+                    mySimpleXYPlot.setDomainBoundaries(minXY.x, maxXY.x,

+                            BoundaryMode.FIXED);

+                    mySimpleXYPlot.redraw();

+                }

+                break;

+        }

+        return true;

+    }

+

+    private void zoom(float scale) {

+        float domainSpan = maxXY.x - minXY.x;

+        float domainMidPoint = maxXY.x - domainSpan / 2.0f;

+        float offset = domainSpan * scale / 2.0f;

+

+        minXY.x = domainMidPoint - offset;

+        maxXY.x = domainMidPoint + offset;

+

+        minXY.x = Math.min(minXY.x, series[3].getX(series[3].size() - 3)

+                .floatValue());

+        maxXY.x = Math.max(maxXY.x, series[0].getX(1).floatValue());

+        clampToDomainBounds(domainSpan);

+    }

+

+    private void scroll(float pan) {

+        float domainSpan = maxXY.x - minXY.x;

+        float step = domainSpan / mySimpleXYPlot.getWidth();

+        float offset = pan * step;

+        minXY.x = minXY.x + offset;

+        maxXY.x = maxXY.x + offset;

+        clampToDomainBounds(domainSpan);

+    }

+

+    private void clampToDomainBounds(float domainSpan) {

+        float leftBoundary = series[0].getX(0).floatValue();

+        float rightBoundary = series[3].getX(series[3].size() - 1).floatValue();

+        // enforce left scroll boundary:

+        if (minXY.x < leftBoundary) {

+            minXY.x = leftBoundary;

+            maxXY.x = leftBoundary + domainSpan;

+        } else if (maxXY.x > series[3].getX(series[3].size() - 1).floatValue()) {

+            maxXY.x = rightBoundary;

+            minXY.x = rightBoundary - domainSpan;

+        }

+    }

+

+    private float spacing(MotionEvent event) {

+        float x = event.getX(0) - event.getX(1);

+        float y = event.getY(0) - event.getY(1);

+        return FloatMath.sqrt(x * x + y * y);

+    }

+}

+

diff --git a/Examples/DemoApp/src/com/androidplot/demos/XYPlotWithBgImgActivity.java b/Examples/DemoApp/src/com/androidplot/demos/XYPlotWithBgImgActivity.java
new file mode 100644
index 0000000..2f9de9c
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/XYPlotWithBgImgActivity.java
@@ -0,0 +1,131 @@
+package com.androidplot.demos;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ToggleButton;
+import com.androidplot.xy.*;
+
+import java.text.DecimalFormat;
+import java.util.Arrays;
+
+public class XYPlotWithBgImgActivity extends Activity {
+    private static final String TAG = XYPlotWithBgImgActivity.class.getName();
+
+	private int SERIES_LEN = 50;
+	private Shader WHITE_SHADER = new LinearGradient(1, 1, 1, 1, Color.WHITE, Color.WHITE, Shader.TileMode.REPEAT);
+
+	private XYPlot plot;
+	private SimpleXYSeries series;
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.xy_plot_with_bq_img_example);
+
+		plot = (XYPlot) findViewById(R.id.graph_metrics);
+
+		//For debugging.
+        //plot.setMarkupEnabled(true);
+
+        // Format Graph
+        plot.getGraphWidget().getBackgroundPaint().setColor(Color.TRANSPARENT);
+        plot.getGraphWidget().getGridBackgroundPaint().setShader(WHITE_SHADER);
+        plot.getGraphWidget().getDomainGridLinePaint().setColor(Color.BLACK);
+        plot.getGraphWidget().getDomainGridLinePaint().setPathEffect(new DashPathEffect(new float[]{3, 3}, 1));
+        plot.getGraphWidget().getRangeGridLinePaint().setColor(Color.BLACK);
+        plot.getGraphWidget().getRangeGridLinePaint().setPathEffect(new DashPathEffect(new float[]{3, 3}, 1));
+        plot.getGraphWidget().getDomainOriginLinePaint().setColor(Color.BLACK);
+        plot.getGraphWidget().getRangeOriginLinePaint().setColor(Color.BLACK);
+        //plot.getGraphWidget().setMarginTop(10);
+
+        // Customize domain and range labels.
+        plot.setDomainLabel("x-vals");
+        plot.setRangeLabel("y-vals");
+        plot.setRangeValueFormat(new DecimalFormat("0"));
+
+        // Make the domain and range step correctly
+        plot.setRangeBoundaries(40, 160, BoundaryMode.FIXED);
+        plot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 20);
+        plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 60);
+        plot.setTicksPerDomainLabel(2);
+
+        series = (SimpleXYSeries) getSeries();
+		LineAndPointFormatter lpFormat = new LineAndPointFormatter(
+				Color.BLACK,
+				Color.BLACK,
+				null, // No fill
+				new PointLabelFormatter(Color.TRANSPARENT) // Don't show text at points
+		);
+        plot.addSeries(series, lpFormat);
+        plot.redraw();
+	}
+
+	private SimpleXYSeries getSeries() {
+		Integer[] xVals = new Integer[SERIES_LEN];
+		Integer[] yVals = new Integer[SERIES_LEN];
+
+		xVals[0] = 0;
+		yVals[0] = 0;
+
+        for (int i = 1; i < SERIES_LEN; i += 1){
+        	xVals[i] = xVals[i-1] + (int)(Math.random() * i);
+        	yVals[i] = (int)(Math.random() * 140);
+        }
+
+        return new SimpleXYSeries(
+        		Arrays.asList(xVals),
+        		Arrays.asList(yVals),
+        		"Sample Series");
+	}
+
+	public void onGraphStyleToggle(View v) {
+		boolean styleOn = ((ToggleButton) v).isChecked();
+
+        /*RectF graphRect = plot.getGraphWidget().getGridRect();
+        float segmentSize = 1.0f/6.0f;
+        LinearGradient lg = new LinearGradient(
+                0,
+                graphRect.top,
+                0,
+                graphRect.bottom,
+                new int[]{
+                        Color.RED,
+                        Color.YELLOW,
+                        Color.GREEN,
+                        Color.WHITE},
+                new float[]{
+                        0,
+                        segmentSize*2,
+                        segmentSize*3,
+                        segmentSize*5
+                },
+                Shader.TileMode.REPEAT
+        );
+        plot.getGraphWidget().getGridBackgroundPaint().setShader(lg);*/
+
+        RectF rect = plot.getGraphWidget().getGridRect();
+        BitmapShader myShader = new BitmapShader(
+                Bitmap.createScaledBitmap(
+                        BitmapFactory.decodeResource(
+                                getResources(),
+                                R.drawable.graph_background),
+                        1,
+                        (int) rect.height(),
+                        false),
+                Shader.TileMode.REPEAT,
+                Shader.TileMode.REPEAT);
+        Matrix m = new Matrix();
+        m.setTranslate(rect.left, rect.top);
+        myShader.setLocalMatrix(m);
+        if (styleOn)
+	        plot.getGraphWidget().getGridBackgroundPaint().setShader(
+	        		myShader);
+		else
+			plot.getGraphWidget().getGridBackgroundPaint().setShader(WHITE_SHADER);
+
+        plot.redraw();
+
+	}
+}
diff --git a/Examples/DemoApp/src/com/androidplot/demos/XYRegionExampleActivity.java b/Examples/DemoApp/src/com/androidplot/demos/XYRegionExampleActivity.java
new file mode 100644
index 0000000..67e1b1b
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/XYRegionExampleActivity.java
@@ -0,0 +1,433 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos;

+

+import android.app.Activity;

+import android.graphics.Color;

+import android.graphics.DashPathEffect;

+import android.graphics.Paint;

+import android.os.Bundle;

+import android.widget.CheckBox;

+import android.widget.CompoundButton;

+import com.androidplot.util.PixelUtils;

+import com.androidplot.xy.XYSeries;

+import com.androidplot.ui.*;

+import com.androidplot.xy.*;

+

+import java.text.DecimalFormat;

+import java.text.FieldPosition;

+import java.text.NumberFormat;

+import java.text.ParsePosition;

+import java.util.Arrays;

+

+/**

+ * Demonstration of the usage of Marker and RectRegion.

+ */

+public class XYRegionExampleActivity extends Activity {

+

+    private static final float HOME_RUN_DIST = 325;

+    private static final int LINE_THICKNESS_DP = 2;

+    private static final int POINT_SIZE_DP = 6;

+    private XYPlot plot;

+    private final Number[] timHits = {105, 252, 220, 350, 12, 250, 353};

+    private final Number[] nickHits = {110, 191, 61, 371, 289, 101, 10};

+    private final Number[] joeHits = {25, 375, 364, 128, 178, 289, 346};

+    private final Number[] jamesHits = {250, 285, 295, 211, 311, 365, 241};

+    private LineAndPointFormatter timFormatter;

+    private LineAndPointFormatter nickFormatter;

+    private LineAndPointFormatter joeFormatter;

+    private LineAndPointFormatter jamesFormatter;

+

+    private XYSeries timSeries;

+    private XYSeries nickSeries;

+    private XYSeries joeSeries;

+    private XYSeries jamesSeries;

+

+    private RectRegion shortRegion;

+    private RectRegion warmupRegion;

+    private RectRegion homeRunRegion;

+

+    //private XYRegionFormatter rf1;

+    private XYRegionFormatter shortRegionFormatter;

+    private XYRegionFormatter warmupRegionFormatter;

+    private XYRegionFormatter homeRunRegionFormatter;

+    //private XYRegionFormatter rf5;

+

+    private CheckBox timCB;

+    private CheckBox nickCB;

+    private CheckBox joeCB;

+    private CheckBox jamesCB;

+

+    private CheckBox r2CheckBox;

+    private CheckBox r3CheckBox;

+    private CheckBox r4CheckBox;

+

+    public void onCreate(Bundle savedInstanceState) {

+        super.onCreate(savedInstanceState);

+        setContentView(R.layout.xyregion_example);

+        plot = (XYPlot) findViewById(R.id.xyRegionExamplePlot);

+        timCB = (CheckBox) findViewById(R.id.s1CheckBox);

+        timCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                onS1CheckBoxClicked();

+            }

+        });

+

+        nickCB = (CheckBox) findViewById(R.id.s2CheckBox);

+        nickCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                onS2CheckBoxClicked();

+            }

+        });

+

+        joeCB = (CheckBox) findViewById(R.id.s3CheckBox);

+        joeCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                onS3CheckBoxClicked();

+            }

+        });

+

+        jamesCB = (CheckBox) findViewById(R.id.s4CheckBox);

+        jamesCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                onS4CheckBoxClicked();

+            }

+        });

+

+

+

+        r2CheckBox = (CheckBox) findViewById(R.id.r2CheckBox);

+        r2CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                onCheckBoxClicked(r2CheckBox, timFormatter, shortRegionFormatter, shortRegion);

+            }

+        });

+

+        r3CheckBox = (CheckBox) findViewById(R.id.r3CheckBox);

+        r3CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                onCheckBoxClicked(r3CheckBox, nickFormatter, warmupRegionFormatter, warmupRegion);

+            }

+        });

+

+        r4CheckBox = (CheckBox) findViewById(R.id.r4CheckBox);

+        r4CheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

+            @Override

+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

+                onCheckBoxClicked(r4CheckBox, nickFormatter, homeRunRegionFormatter, homeRunRegion);

+            }

+        });

+

+        seriesSetup();

+        markerSetup();

+        axisLabelSetup();

+        regionSetup();

+        makePlotPretty();

+    }

+

+    private void onS1CheckBoxClicked() {

+        if(timCB.isChecked()) {

+            plot.addSeries(timSeries, timFormatter);

+            r2CheckBox.setEnabled(true);

+        } else {

+            plot.removeSeries(timSeries);

+            r2CheckBox.setEnabled(false);

+        }

+        plot.redraw();

+    }

+

+    private void onS2CheckBoxClicked() {

+        if(nickCB.isChecked()) {

+            plot.addSeries(nickSeries, nickFormatter);

+            r3CheckBox.setEnabled(true);

+            r4CheckBox.setEnabled(true);

+        } else {

+            plot.removeSeries(nickSeries);

+            r3CheckBox.setEnabled(false);

+            r4CheckBox.setEnabled(false);

+        }

+        plot.redraw();

+    }

+

+    private void onS3CheckBoxClicked() {

+        if(joeCB.isChecked()) {

+            plot.addSeries(joeSeries, joeFormatter);

+        } else {

+            plot.removeSeries(joeSeries);

+        }

+        plot.redraw();

+    }

+

+    private void onS4CheckBoxClicked() {

+        if(jamesCB.isChecked()) {

+            plot.addSeries(jamesSeries, jamesFormatter);

+        } else {

+            plot.removeSeries(jamesSeries);

+        }

+        plot.redraw();

+    }

+

+    /**

+     * Processes a check box event

+     * @param cb The checkbox event origin

+     * @param lpf LineAndPointFormatter with which rr and rf are to be added/removed

+     * @param rf The XYRegionFormatter with which rr should be rendered

+     * @param rr The RectRegion to add/remove

+     */

+    private void onCheckBoxClicked(CheckBox cb, LineAndPointFormatter lpf,

+                                   XYRegionFormatter rf, RectRegion rr) {

+        if(cb.isChecked()) {

+            lpf.removeRegion(rr);

+        } else {

+            lpf.addRegion(rr, rf);

+        }

+    }

+

+    /**

+     * Cleans up the plot's general layout and color scheme

+     */

+    private void makePlotPretty() {

+        // use a 2x5 grid with room for 10 items:

+        plot.getLegendWidget().setTableModel(new DynamicTableModel(4, 2));

+

+        // add a semi-transparent black background to the legend

+        // so it's easier to see overlaid on top of our plot:

+        Paint bgPaint = new Paint();

+        bgPaint.setColor(Color.BLACK);

+        bgPaint.setStyle(Paint.Style.FILL);

+        bgPaint.setAlpha(40);

+

+        plot.getLegendWidget().setBackgroundPaint(bgPaint);

+

+        // adjust the padding of the legend widget to look a little nicer:

+        plot.getLegendWidget().setPadding(5, 5, 5, 5);

+

+        plot.setRangeValueFormat(new NumberFormat() {

+            @Override

+            public StringBuffer format(double value, StringBuffer buffer, FieldPosition field) {

+                return new StringBuffer(value + "'");

+            }

+

+            @Override

+            public StringBuffer format(long value, StringBuffer buffer, FieldPosition field) {

+                throw new UnsupportedOperationException("Not yet implemented.");

+            }

+

+            @Override

+            public Number parse(String string, ParsePosition position) {

+                throw new UnsupportedOperationException("Not yet implemented.");

+            }

+        });

+

+        plot.setDomainValueFormat(new DecimalFormat("#"));

+

+        plot.getLegendWidget().setWidth(PixelUtils.dpToPix(100), SizeLayoutType.FILL);

+

+

+        // adjust the legend size so there is enough room

+        // to draw the new legend grid:

+        //plot.getLegendWidget().getHeightMetric().setLayoutType(SizeLayoutType.ABSOLUTE);

+        //plot.getLegendWidget().getWidthMetric().setLayoutType(SizeLayoutType.ABSOLUTE);

+        //plot.getLegendWidget().setSize(

+        //    new SizeMetrics(70, SizeLayoutType.ABSOLUTE, 80, SizeLayoutType.ABSOLUTE));

+

+        // reposition the grid so that it rests above the bottom-left

+        // edge of the graph widget:

+

+        plot.getLegendWidget().position(

+                125,

+                XLayoutStyle.ABSOLUTE_FROM_LEFT,

+                65,

+                YLayoutStyle.ABSOLUTE_FROM_TOP,

+                AnchorPosition.LEFT_TOP);

+

+        plot.getGraphWidget().setRangeLabelHorizontalOffset(-1);

+

+        // add enough space to ensure range value labels arent cut off on the left/right:

+        plot.getGraphWidget().setRangeLabelWidth(25);

+

+        // add enough space to make sure domain value labels arent cut off on the bottom:

+        plot.getGraphWidget().setDomainLabelWidth(15);

+

+        plot.getGraphWidget().setDomainLabelVerticalOffset(-6);

+

+        plot.setRangeBoundaries(0, BoundaryMode.FIXED, 500, BoundaryMode.FIXED);

+    }

+

+    /**

+     * Create 4 XYSeries from the values defined above add add them to the plot.

+     * Also add some arbitrary regions.

+     */

+    private void seriesSetup() {

+

+

+        // TIM

+        timFormatter = new LineAndPointFormatter(

+                Color.rgb(100, 25, 20),

+                Color.rgb(100, 25, 20),

+                null, null);

+        timFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));

+        timFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));

+

+        timSeries = new SimpleXYSeries(Arrays.asList(timHits),

+            SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Tim");

+

+        plot.addSeries(timSeries, timFormatter);

+

+        // SERIES #2:

+        nickFormatter = new LineAndPointFormatter(

+                Color.rgb(100, 25, 200),

+                Color.rgb(100, 25, 200),

+                null, null);

+        nickFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));

+        nickFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));

+

+

+

+        nickSeries = new SimpleXYSeries(Arrays.asList(nickHits),

+            SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Nick");

+

+        plot.addSeries(nickSeries, nickFormatter);

+

+        // SERIES #3:

+        joeFormatter = new LineAndPointFormatter(

+                Color.rgb(200, 25, 200),

+                Color.rgb(200, 25, 200),

+                null, null);

+        joeFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));

+        joeFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));

+

+        joeSeries = new SimpleXYSeries(Arrays.asList(joeHits),

+            SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Joe");

+

+        plot.addSeries(joeSeries, joeFormatter);

+

+        // SERIES #4:

+        jamesFormatter = new LineAndPointFormatter(

+                Color.rgb(220, 25, 20),

+                Color.rgb(220, 25, 20),

+                null, null);

+

+        jamesFormatter.getLinePaint().setStrokeWidth(PixelUtils.dpToPix(LINE_THICKNESS_DP));

+        jamesFormatter.getVertexPaint().setStrokeWidth(PixelUtils.dpToPix(POINT_SIZE_DP));

+

+        jamesSeries = new SimpleXYSeries(Arrays.asList(jamesHits),

+            SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,"James");

+        plot.addSeries(jamesSeries, jamesFormatter);

+

+        plot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 100);

+        //plot.setTicksPerRangeLabel(1);

+        plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1);

+    }

+

+    /**

+     * Add some color coded regions to our axis labels.

+     */

+    private void axisLabelSetup() {

+        // DOMAIN

+        plot.getGraphWidget().addDomainAxisValueLabelRegion(

+                Double.NEGATIVE_INFINITY, 2, new AxisValueLabelFormatter(Color.GRAY));

+        plot.getGraphWidget().addDomainAxisValueLabelRegion(

+                2, Double.POSITIVE_INFINITY, new AxisValueLabelFormatter(Color.WHITE));

+        // RANGE

+        plot.getGraphWidget().addRangeAxisValueLabelRegion(

+                Double.NEGATIVE_INFINITY, HOME_RUN_DIST, new AxisValueLabelFormatter(Color.RED));

+        plot.getGraphWidget().addRangeAxisValueLabelRegion(

+                HOME_RUN_DIST, Double.POSITIVE_INFINITY, new AxisValueLabelFormatter(Color.GREEN));

+    }

+

+    /**

+     * Add some markers to our plot.

+     */

+    private void markerSetup() {

+

+        YValueMarker fenwayLfMarker = new YValueMarker(

+                380,                                        // y-val to mark

+                "Fenway Park LF Wall",                      // marker label

+                new XPositionMetric(                        // object instance to set text positioning on the marker

+                        PixelUtils.dpToPix(5),              // 5dp offset

+                        XLayoutStyle.ABSOLUTE_FROM_RIGHT),  // offset origin

+                Color.BLUE,                                 // line paint color

+                Color.BLUE);                                // text paint color

+

+        YValueMarker attRfMarker = new YValueMarker(

+                        309,                                        // y-val to mark

+                        "ATT Park RF Wall",                         // marker label

+                        new XPositionMetric(                        // object instance to set text positioning on the marker

+                                PixelUtils.dpToPix(5),              // 5dp offset

+                                XLayoutStyle.ABSOLUTE_FROM_RIGHT),  // offset origin

+                        Color.CYAN,                                 // line paint color

+                        Color.CYAN);                                // text paint color

+

+

+        fenwayLfMarker.getTextPaint().setTextSize(PixelUtils.dpToPix(14));

+        attRfMarker.getTextPaint().setTextSize(PixelUtils.dpToPix(14));

+

+        DashPathEffect dpe = new DashPathEffect(

+                        new float[]{PixelUtils.dpToPix(2), PixelUtils.dpToPix(2)}, 0);

+

+        fenwayLfMarker.getLinePaint().setPathEffect(dpe);

+        attRfMarker.getLinePaint().setPathEffect(dpe);

+

+        plot.addMarker(fenwayLfMarker);

+        plot.addMarker(attRfMarker);

+    }

+

+    /**

+     * Add some fill regions to our series data

+     */

+    private void regionSetup() {

+

+

+        // and another region:

+        shortRegionFormatter = new XYRegionFormatter(Color.RED);

+        shortRegionFormatter.getPaint().setAlpha(75);

+        shortRegion = new RectRegion(2, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, HOME_RUN_DIST, "Short");

+        timFormatter.addRegion(shortRegion, shortRegionFormatter);

+        nickFormatter.addRegion(shortRegion, shortRegionFormatter);

+        joeFormatter.addRegion(shortRegion, shortRegionFormatter);

+        jamesFormatter.addRegion(shortRegion, shortRegionFormatter);

+

+        // the next three regions are horizontal regions with minY/maxY

+        // set to negative and positive infinity respectively.

+        warmupRegionFormatter = new XYRegionFormatter(Color.WHITE);

+        warmupRegionFormatter.getPaint().setAlpha(75);

+

+        warmupRegion = new RectRegion(0, 2, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, "Warmup");

+        timFormatter.addRegion(warmupRegion, warmupRegionFormatter);

+        nickFormatter.addRegion(warmupRegion, warmupRegionFormatter);

+        joeFormatter.addRegion(warmupRegion, warmupRegionFormatter);

+        jamesFormatter.addRegion(warmupRegion, warmupRegionFormatter);

+

+        homeRunRegionFormatter = new XYRegionFormatter(Color.GREEN);

+        homeRunRegionFormatter.getPaint().setAlpha(75);

+

+        homeRunRegion = new RectRegion(2, Double.POSITIVE_INFINITY, HOME_RUN_DIST, Double.POSITIVE_INFINITY, "H. Run");

+        timFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);

+        nickFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);

+        joeFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);

+        jamesFormatter.addRegion(homeRunRegion, homeRunRegionFormatter);

+

+        nickFormatter.setFillDirection(FillDirection.RANGE_ORIGIN);

+    }

+}
\ No newline at end of file
diff --git a/Examples/DemoApp/src/com/androidplot/demos/widget/DemoAppWidgetProvider.java b/Examples/DemoApp/src/com/androidplot/demos/widget/DemoAppWidgetProvider.java
new file mode 100644
index 0000000..f44a277
--- /dev/null
+++ b/Examples/DemoApp/src/com/androidplot/demos/widget/DemoAppWidgetProvider.java
@@ -0,0 +1,87 @@
+/*

+ * Copyright 2012 AndroidPlot.com

+ *

+ *    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 com.androidplot.demos.widget;

+

+import android.appwidget.AppWidgetManager;

+import android.appwidget.AppWidgetProvider;

+import android.content.Context;

+import android.graphics.Bitmap;

+import android.graphics.Color;

+import android.widget.RemoteViews;

+import com.androidplot.demos.R;

+import com.androidplot.xy.XYSeries;

+import com.androidplot.xy.LineAndPointFormatter;

+import com.androidplot.xy.SimpleXYSeries;

+import com.androidplot.xy.XYPlot;

+

+import java.util.Arrays;

+

+public class DemoAppWidgetProvider extends AppWidgetProvider {

+

+    @Override

+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

+        for (int widgetId : appWidgetIds) {

+            XYPlot plot = new XYPlot(context, "Widget Example");

+            //plot.getLayoutParams().height = 100;

+            //plot.getLayoutParams().width = 100;

+            plot.measure(150,150);

+            plot.layout(0,0,150,150);

+            plot.setDrawingCacheEnabled(true);

+

+            // Create a couple arrays of y-values to plot:

+            Number[] series1Numbers = {1, 8, 5, 2, 7, 4};

+            Number[] series2Numbers = {4, 6, 3, 8, 2, 10};

+

+            // Turn the above arrays into XYSeries':

+            XYSeries series1 = new SimpleXYSeries(

+                    Arrays.asList(series1Numbers),          // SimpleXYSeries takes a List so turn our array into a List

+                    SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, // Y_VALS_ONLY means use the element index as the x value

+                    "Series1");                             // Set the display title of the series

+

+            // same as above

+            XYSeries series2 = new SimpleXYSeries(Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");

+

+            // Create a formatter to use for drawing a series using LineAndPointRenderer:

+            LineAndPointFormatter series1Format = new LineAndPointFormatter(

+                    Color.rgb(0, 200, 0),                   // line color

+                    Color.rgb(0, 100, 0),                   // point color

+                    null, null);                                  // fill color (none)

+

+            // add a new series' to the xyplot:

+            plot.addSeries(series1, series1Format);

+

+            // same as above:

+            plot.addSeries(series2,

+                    new LineAndPointFormatter(

+                            Color.rgb(0, 0, 200), Color.rgb(0, 0, 100), null, null));

+

+

+            // reduce the number of range labels

+            plot.setTicksPerRangeLabel(3);

+

+            // by default, AndroidPlot displays developer guides to aid in laying out your plot.

+            // To get rid of them call disableAllMarkup():

+            //plot.disableAllMarkup();

+

+            Bitmap bmp = plot.getDrawingCache();

+

+            RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.demo_app_widget);

+            rv.setBitmap(R.id.imgView, "setImageBitmap", bmp);

+            appWidgetManager.updateAppWidget(widgetId, rv);

+        }

+    }

+}

diff --git a/Examples/pom.xml b/Examples/pom.xml
new file mode 100644
index 0000000..c706493
--- /dev/null
+++ b/Examples/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2012 AndroidPlot.com
+  ~
+  ~    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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>androidplot</artifactId>
+        <groupId>com.androidplot</groupId>
+        <version>0.6.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>androidplot-examples</artifactId>
+    <name>AndroidPlot-Examples</name>
+    <packaging>pom</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+        </dependency>
+        <!--<dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+            <version>2.1_r1</version>
+            <scope>provided</scope>
+        </dependency>-->
+        <!-- TODO: figure out how to use this dependency without breaking the clean target;
+        used as-is, the clean target can fail because the dependency is erased by maven.
+        <dependency>
+            <groupId>com.androidplot</groupId>
+            <artifactId>androidplot-core</artifactId>
+            <version>${applicationVersion}</version>
+        </dependency>
+        -->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <version>1.6</version>
+                <executions>
+                    <execution>
+                        <id>compile</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        
+                        <configuration>
+                            <target name="build-examples">
+                                <!--
+                                <property name="key.store" value="test"/>
+                                <property name="key.alias" value="test"/>
+                                -->
+                                <!--<property name="sdk.dir" value="C:/android-sdk"/>-->
+                                <property environment="env"/>
+                                <property name="sdk.dir" value="${env.ANDROID_HOME}"/>
+                                <!-- todo: include only the release jar, not everything inside
+                                           the artifacts dir.-->
+                                <!--<property name="jar.libs.dir" value="../../AndroidPlot-Core/target/obfuscated/"/>-->
+                                <property name="build.compiler" value="extJavac"/>
+                                <property environment="env"/>
+                                <property name="sdk.dir" value="${env.ANDROID_HOME}"/>
+                                <!--<property name="apkeystore" value="${env.AP_KEYSTORE}"/>-->
+                                
+                                <!-- DemoApp-->
+                                <ant antfile="build.xml" target="debug" dir="DemoApp"/>
+
+                                <!-- DynamicXYPlotExample-->
+                                <ant antfile="build.xml" target="debug" dir="DynamicXYPlotExample"/>
+
+                                <!-- HelperExample-->
+                                <ant antfile="build.xml" target="debug" dir="HelperExample"/>
+
+                                <!-- OrientationSensorExample-->
+                                <ant antfile="build.xml" target="debug" dir="OrientationSensorExample"/>
+
+                                <!-- SimpleXYPlotExample-->
+                                <ant antfile="build.xml" target="debug" dir="SimpleXYPlotExample"/>
+
+                                <!-- StepChartExample-->
+                                <ant antfile="build.xml" target="debug" dir="StepChartExample"/>
+
+                                <!-- TimedXYPlotExample
+                                <ant antfile="build.xml" target="debug" dir="TimedXYPlotExample"/>
+                                -->
+                            </target>
+
+                        </configuration>
+                        
+                    </execution>
+                    <execution>
+                        <id>clean</id>
+                        <phase>clean</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <tasks>
+                                <property environment="env"/>
+                                <property name="sdk.dir" value="${env.ANDROID_HOME}"/>
+
+                                <!-- DemoApp-->
+                                <ant antfile="build.xml" target="clean" dir="DemoApp"/>
+
+                                <!-- DynamicXYPlotExample-->
+                                <ant antfile="build.xml" target="clean" dir="DynamicXYPlotExample"/>
+
+                                <!-- HelperExample-->
+                                <ant antfile="build.xml" target="clean" dir="HelperExample"/>
+
+                                <!-- OrientationSensorExample-->
+                                <ant antfile="build.xml" target="clean" dir="OrientationSensorExample"/>
+
+                                <!-- SimpleXYPlotExample-->
+                                <ant antfile="build.xml" target="clean" dir="SimpleXYPlotExample"/>
+
+                                <!-- StepChartExample-->
+                                <ant antfile="build.xml" target="clean" dir="StepChartExample"/>
+                                
+                                <!-- TimedXYPlotExample
+                                <ant antfile="build.xml" target="clean" dir="TimedXYPlotExample"/>
+-->
+                            </tasks>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>
\ No newline at end of file
diff --git a/androidplot.iml b/androidplot.iml
new file mode 100644
index 0000000..9b79bdb
--- /dev/null
+++ b/androidplot.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">

+  <component name="NewModuleRootManager" inherit-compiler-output="false">

+    <output url="file://$MODULE_DIR$/target/classes" />

+    <output-test url="file://$MODULE_DIR$/target/test-classes" />

+    <content url="file://$MODULE_DIR$">

+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />

+      <excludeFolder url="file://$MODULE_DIR$/target" />

+    </content>

+    <orderEntry type="inheritedJdk" />

+    <orderEntry type="sourceFolder" forTests="false" />

+  </component>

+</module>

+

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..fd4c3de
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2012 AndroidPlot.com
+  ~
+  ~    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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.sonatype.oss</groupId>
+        <artifactId>oss-parent</artifactId>
+        <version>7</version>
+    </parent>
+
+    <groupId>com.androidplot</groupId>
+    <artifactId>androidplot</artifactId>
+    <version>0.6.0</version>
+    <name>AndroidPlot-Parent</name>
+    <description>A charting library for the Android platform</description>
+    <url>http://androidplot.com</url>
+    <inceptionYear>2010</inceptionYear>
+    <organization>
+        <name>androidplot.com</name>
+        <url>http://androidplot.com</url>
+    </organization>
+    <licenses>
+        <license>
+            <name>The Apache Software License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+            <distribution>repo</distribution>
+            <comments>A business-friendly OSS license</comments>
+          </license>
+    </licenses>
+    <developers>
+        <developer>
+          <id>Nick</id>
+          <name>Nick Fellows</name>
+          <email>[email protected]</email>
+          <organization>AndroidPlot</organization>
+          <organizationUrl>http://androidplot.com</organizationUrl>
+          <roles>
+            <role>architect</role>
+            <role>developer</role>
+          </roles>
+        </developer>
+        <developer>
+            <id>Tim</id>
+            <name>Tim Hepner</name>
+            <email>[email protected]</email>
+            <organization>AndroidPlot</organization>
+            <organizationUrl>http://androidplot.com</organizationUrl>
+            <roles>
+                <role>developer</role>
+            </roles>
+        </developer>
+    </developers>
+    <packaging>pom</packaging>
+
+    <properties>
+        <!--<applicationVersion>0.5.1-SNAPSHOT</applicationVersion>-->
+        <examplesDir>Examples</examplesDir>
+        <!--<obfuscatedJarPath>${project.build.directory}/obfuscated</obfuscatedJarPath>
+        <obfuscatedJarName>${project.build.finalName}.jar</obfuscatedJarName>-->
+    </properties>
+
+    <modules>
+        <module>AndroidPlot-Core</module>
+        <!--<module>Examples</module>-->
+        <module>Examples/DemoApp</module>
+    </modules>
+
+    <!--<repositories>
+        <repository>
+            <id>java.net2</id>
+            <url>http://download.java.net/maven/2/</url>
+        </repository>
+    </repositories>-->
+
+    <scm>
+        <connection>scm:git:https://bitbucket.org/androidplot/androidplot.git</connection>
+        <developerConnection>scm:git:https://bitbucket.org/androidplot/androidplot.git</developerConnection>
+        <url>https://bitbucket.org/androidplot/androidplot.git</url>
+      <tag>0.6.0</tag>
+  </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <tagNameFormat>v@{project.version}</tagNameFormat>
+                    <!-- Added to fix an issue with hidden gpg signing prompts
+                    described here: http://jira.codehaus.org/browse/MGPG-9-->
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <version>2.7</version>
+                <!--<configuration>
+                    <skip>true</skip>
+                </configuration>-->
+            </plugin>
+        </plugins>
+    </build>
+
+    <!-- dependencies to be inherited -->
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.google.android</groupId>
+                <artifactId>android</artifactId>
+                <version>4.1.1.4</version>
+                <scope>provided</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
\ No newline at end of file