├── .DS_Store ├── .gitattributes ├── .gitignore ├── AXGraphView ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── aghajari │ │ └── graphview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── aghajari │ │ └── graphview │ │ ├── AXGraphCanvas.java │ │ ├── AXGraphFormula.java │ │ ├── AXGraphMultiFormula.java │ │ ├── AXGraphOptions.java │ │ ├── AXGraphPointType.java │ │ ├── AXGraphUtils.java │ │ └── AXGraphView.java │ └── test │ └── java │ └── com │ └── aghajari │ └── graphview │ └── ExampleUnitTest.java ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── aghajari │ │ └── app │ │ └── graph │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── aghajari │ │ │ └── app │ │ │ └── graph │ │ │ ├── GraphActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── adapter │ │ │ ├── AdapterData.java │ │ │ ├── PaintedGraphFormula.java │ │ │ ├── PaintedGraphMultiFormula.java │ │ │ └── TrigonometryGraphOptions.java │ │ │ ├── graphs │ │ │ ├── AbsoluteValueGraphFormula.java │ │ │ ├── BracketGraphFormula.java │ │ │ ├── CircleGraphFormula.java │ │ │ ├── CubicRootGraphFormula.java │ │ │ ├── CubicRootX2GraphFormula.java │ │ │ └── CustomGraphFormula.java │ │ │ ├── multigraphs │ │ │ ├── BatmanGraphFormula.java │ │ │ ├── CaptainAmericaGraphFormula.java │ │ │ ├── FlashGraphFormula.java │ │ │ ├── HeartGraphFormula.java │ │ │ ├── SupermanGraphFormula.java │ │ │ └── WonderWomanGraphFormula.java │ │ │ └── parser │ │ │ ├── FormulaParser.java │ │ │ └── GraphFormulaParser.java │ └── res │ │ ├── drawable-v24 │ │ ├── graph_parent_bg.xml │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── graph_bg.xml │ │ └── ic_launcher_background.xml │ │ ├── layout-v21 │ │ ├── activity_graph.xml │ │ └── rv_item.xml │ │ ├── layout-v23 │ │ └── rv_item.xml │ │ ├── layout │ │ ├── activity_graph.xml │ │ ├── activity_main.xml │ │ └── rv_item.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-v21 │ │ └── styles.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── aghajari │ └── app │ └── graph │ └── ExampleUnitTest.java ├── build.gradle ├── deploy.settings ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── 1.jpg ├── 2.jpg ├── AXGraphView.gif ├── Header.jpg ├── axis.png ├── custom_draw.png ├── domain.png ├── graphs │ ├── 1.jpg │ ├── abs_custom.png │ ├── absolute_value.png │ ├── bracket.png │ ├── cos.png │ ├── cot.png │ ├── cubic.png │ ├── cubic_root.png │ ├── cubic_root_x2.png │ ├── exponential.png │ ├── homographic.png │ ├── logarithm.png │ ├── quadratic.png │ ├── quadratic_and_cubic.png │ ├── screen1.png │ ├── screen2.png │ ├── screen3.png │ ├── screen4.png │ ├── screen5.png │ ├── screen6.png │ ├── sin.png │ ├── sin_and_cos.png │ ├── square.png │ ├── tan.png │ └── tan_and_cot.png └── graphs2 │ ├── 2.jpg │ ├── batman.png │ ├── captain_america.png │ ├── flash.png │ ├── heart.png │ ├── screen1.png │ ├── screen2.png │ ├── screen3.png │ ├── screen4.png │ ├── screen5.png │ ├── screen_heart.png │ ├── superman.png │ └── wonder_woman.png └── settings.gradle /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /AXGraphView/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /AXGraphView/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 30 5 | 6 | defaultConfig { 7 | minSdkVersion 16 8 | targetSdkVersion 30 9 | versionCode 1 10 | versionName "1.0" 11 | 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | consumerProguardFiles "consumer-rules.pro" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(dir: "libs", include: ["*.jar"]) 26 | implementation 'androidx.appcompat:appcompat:1.3.1' 27 | testImplementation 'junit:junit:4.13.2' 28 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 29 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 30 | 31 | } 32 | 33 | 34 | // PUBLISH 35 | 36 | apply plugin: 'maven' 37 | apply plugin: 'maven-publish' 38 | apply plugin: 'signing' 39 | 40 | task sourcesJar(type: Jar) { 41 | from android.sourceSets.main.java.srcDirs 42 | classifier = 'sources' 43 | } 44 | 45 | task javadoc(type: Javadoc) { 46 | source = android.sourceSets.main.java.srcDirs 47 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 48 | } 49 | 50 | afterEvaluate { 51 | javadoc.classpath += files(android.libraryVariants.collect { variant -> 52 | variant.javaCompileProvider.get().classpath.files 53 | }) 54 | } 55 | 56 | task javadocJar(type: Jar, dependsOn: javadoc) { 57 | classifier = 'javadoc' 58 | from javadoc.destinationDir 59 | } 60 | 61 | artifacts { 62 | archives javadocJar 63 | archives sourcesJar 64 | } 65 | 66 | signing { 67 | useInMemoryPgpKeys( 68 | rootProject.ext["signing.keyId"], 69 | rootProject.ext["signing.key"], 70 | rootProject.ext["signing.password"], 71 | ) 72 | sign publishing.publications 73 | } 74 | 75 | tasks.withType(Javadoc) { 76 | options.addStringOption('Xdoclint:none', '-quiet') 77 | options.addStringOption('encoding', 'UTF-8') 78 | options.addStringOption('charSet', 'UTF-8') 79 | } 80 | 81 | def artifact = new Properties() 82 | artifact.load(new FileInputStream(rootProject.rootDir.getAbsolutePath() + "/deploy.settings")) 83 | 84 | version = artifact.version 85 | group = artifact.groupId 86 | archivesBaseName = artifact.id 87 | 88 | afterEvaluate { 89 | publishing { 90 | publications { 91 | release(MavenPublication) { 92 | groupId artifact.groupId 93 | artifactId artifact.id 94 | version artifact.version 95 | from components.release 96 | 97 | pom { 98 | name = artifact.id 99 | packaging = 'aar' 100 | description = 'AXGraphView creates zoomable and scrollable graphs based on function or custom draw.' 101 | url = artifact.siteUrl 102 | 103 | scm { 104 | connection = artifact.gitUrl 105 | developerConnection = artifact.gitUrl 106 | url = artifact.siteUrl 107 | } 108 | 109 | licenses { 110 | license { 111 | name = 'The Apache License, Version 2.0' 112 | url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 113 | } 114 | } 115 | 116 | developers { 117 | developer { 118 | id = 'Aghajari' 119 | name = 'AmirHossein Aghajari' 120 | email = 'amirhossein.aghajari.82@gmail.com' 121 | } 122 | } 123 | } 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /AXGraphView/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/AXGraphView/consumer-rules.pro -------------------------------------------------------------------------------- /AXGraphView/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /AXGraphView/src/androidTest/java/com/aghajari/graphview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.graphview; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.aghajari.graphview.test", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /AXGraphView/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | -------------------------------------------------------------------------------- /AXGraphView/src/main/java/com/aghajari/graphview/AXGraphCanvas.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.graphview; 20 | 21 | import android.graphics.Canvas; 22 | import android.graphics.Paint; 23 | import android.graphics.Rect; 24 | import android.graphics.RectF; 25 | import android.view.Gravity; 26 | 27 | public class AXGraphCanvas { 28 | 29 | private Canvas canvas; 30 | private AXGraphView graphView; 31 | private Paint defaultPaint; 32 | private boolean radiusFromAxis = false; 33 | private boolean applyTransform = false; 34 | private AXGraphFormula formula; 35 | 36 | AXGraphCanvas(AXGraphView graphView){ 37 | this.graphView = graphView; 38 | } 39 | 40 | void setCanvas (Canvas canvas,AXGraphFormula formula){ 41 | this.canvas = canvas; 42 | this.defaultPaint = formula.getGraphPaint(); 43 | this.formula = formula; 44 | this.applyTransform = false; 45 | } 46 | 47 | public AXGraphView getGraphView() { 48 | return graphView; 49 | } 50 | 51 | public Canvas getCanvas(){ 52 | return canvas; 53 | } 54 | 55 | public AXGraphOptions getGraphOptions() { 56 | return graphView.getGraphOptions(); 57 | } 58 | 59 | public void setRadiusFromAxis(boolean radiusFromAxis) { 60 | this.radiusFromAxis = radiusFromAxis; 61 | } 62 | 63 | public void setApplyFormulaTransform(boolean applyTransform) { 64 | this.applyTransform = applyTransform; 65 | } 66 | 67 | public float findFormulaX(float rx) { 68 | if (applyTransform) 69 | return formula.getTransformScaleX() * (graphView.findFormulaX(rx) + + formula.getTransformX()); 70 | else 71 | return graphView.findFormulaX(rx); 72 | } 73 | 74 | public float findFormulaY(float ry) { 75 | if (applyTransform) 76 | return formula.getTransformScaleY() * graphView.findFormulaY(ry) + formula.getTransformY(); 77 | else 78 | return graphView.findFormulaY(ry); 79 | } 80 | 81 | public float findGraphX(float fx) { 82 | if (applyTransform) 83 | return graphView.findGraphX(formula.getTransformScaleX() * (fx+formula.getTransformX())); 84 | else 85 | return graphView.findGraphX(fx); 86 | } 87 | 88 | public float findGraphY(float fy) { 89 | if (applyTransform) 90 | return graphView.findGraphY(formula.getTransformScaleY() * fy +formula.getTransformY()); 91 | else 92 | return graphView.findGraphY(fy); 93 | } 94 | 95 | public float findCanvasX(float fx) { 96 | if (applyTransform) 97 | return graphView.findCanvasX(formula.getTransformScaleX() * (fx+formula.getTransformX())); 98 | else 99 | return graphView.findCanvasX(fx); 100 | } 101 | 102 | public float findCanvasY(float fy) { 103 | if (applyTransform) 104 | return graphView.findCanvasY(formula.getTransformScaleY() * fy + formula.getTransformY()); 105 | else 106 | return graphView.findCanvasY(fy); 107 | } 108 | 109 | public float getGraphScale() { 110 | return graphView.getGraphScale(); 111 | } 112 | 113 | public float getGraphWidth() { 114 | return graphView.getGraphWidth(); 115 | } 116 | 117 | public float getGraphHeight() { 118 | return graphView.getGraphHeight(); 119 | } 120 | 121 | public void drawCircle(float cx, float cy, float radius){ 122 | drawCircle(cx,cy,radius,defaultPaint); 123 | } 124 | 125 | public void drawCircle(float cx, float cy, float radius, Paint paint){ 126 | canvas.drawCircle(findCanvasX(cx),findCanvasY(cy),radiusFromAxis?findGraphX(radius):radius,paint); 127 | } 128 | 129 | public void drawCircle(float cx, float cy, float radius, int circleColor, float borderWidth, int borderColor) { 130 | drawCircle(cx,cy,radius,circleColor,borderWidth,borderColor,defaultPaint); 131 | } 132 | 133 | public void drawCircle(float cx, float cy, float radius, int circleColor, float borderWidth, int borderColor, Paint p) { 134 | int saveColor = p.getColor(); 135 | p.setColor(circleColor); 136 | Paint.Style saveStyle = p.getStyle(); 137 | p.setStyle(Paint.Style.FILL); 138 | canvas.drawCircle(findCanvasX(cx), findCanvasY(cy), radiusFromAxis?findGraphX(radius):radius, p); 139 | if (borderWidth > 0) { 140 | p.setColor(borderColor); 141 | p.setStyle(Paint.Style.STROKE); 142 | float saveStrokeWidth = p.getStrokeWidth(); 143 | p.setStrokeWidth(borderWidth); 144 | canvas.drawCircle(findCanvasX(cx), findCanvasY(cy), (radiusFromAxis?findGraphX(radius):radius) - (borderWidth / 2), p); 145 | p.setStrokeWidth(saveStrokeWidth); 146 | } 147 | p.setColor(saveColor); 148 | p.setStyle(saveStyle); 149 | } 150 | 151 | public void drawBorderCircle(float cx, float cy, float radius, float borderWidth, Paint p) { 152 | Paint.Style saveStyle = p.getStyle(); 153 | if (borderWidth > 0) { 154 | p.setStyle(Paint.Style.STROKE); 155 | float saveStrokeWidth = p.getStrokeWidth(); 156 | p.setStrokeWidth(borderWidth); 157 | canvas.drawCircle(findCanvasX(cx), findCanvasY(cy), (radiusFromAxis?findGraphX(radius):radius) - (borderWidth / 2), p); 158 | p.setStrokeWidth(saveStrokeWidth); 159 | } 160 | p.setStyle(saveStyle); 161 | } 162 | 163 | public void drawLine(float startX, float startY, float stopX, float stopY) { 164 | drawLine(startX,startY,stopX,stopY,defaultPaint); 165 | } 166 | 167 | public void drawLine(float startX, float startY, float stopX, float stopY,Paint paint) { 168 | canvas.drawLine(findCanvasX(startX),findCanvasY(startY),findCanvasX(stopX),findCanvasY(stopY),paint); 169 | } 170 | 171 | public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter) { 172 | drawArc(new RectF(left,top,right,bottom),startAngle,sweepAngle,useCenter); 173 | } 174 | 175 | public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) { 176 | drawArc(new RectF(left,top,right,bottom),startAngle,sweepAngle,useCenter,paint); 177 | } 178 | 179 | public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter) { 180 | drawArc(oval,startAngle,sweepAngle,useCenter,defaultPaint); 181 | } 182 | 183 | public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint) { 184 | fixOval(oval); 185 | canvas.drawArc(oval,startAngle,sweepAngle,useCenter,paint); 186 | } 187 | 188 | public void drawPoint(float x, float y) { 189 | drawPoint(x,y,defaultPaint); 190 | } 191 | 192 | public void drawPoint(float x, float y,Paint paint) { 193 | canvas.drawPoint(findCanvasX(x),findCanvasY(y),paint); 194 | } 195 | 196 | public void drawText(String text, float x, float y) { 197 | drawText(text,x,y,getGraphOptions().textPaint); 198 | } 199 | 200 | public void drawText(String text, float x, float y, Paint paint) { 201 | canvas.drawText(text,findCanvasX(x),findCanvasY(y),paint); 202 | } 203 | 204 | public void drawText(String text, float x, float y,int gravity, Paint paint) { 205 | Rect rect = new Rect(); 206 | paint.getTextBounds(text,0,text.length(),rect); 207 | 208 | float left; 209 | float top; 210 | 211 | switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 212 | case Gravity.CENTER_HORIZONTAL: 213 | left = -rect.width()/2f; 214 | break; 215 | case Gravity.RIGHT: 216 | left = -rect.width(); 217 | break; 218 | case Gravity.LEFT: 219 | default: 220 | left = 0; 221 | } 222 | switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 223 | case Gravity.CENTER_VERTICAL: 224 | top = rect.height()/2f; 225 | break; 226 | case Gravity.TOP: 227 | top = rect.height(); 228 | break; 229 | case Gravity.BOTTOM: 230 | default: 231 | top = 0; 232 | } 233 | 234 | canvas.drawText(text,findCanvasX(x) + left,findCanvasY(y) + top,paint); 235 | } 236 | 237 | void fixOval(RectF oval){ 238 | oval.set(findCanvasX(0) + findGraphX(oval.left), 239 | findCanvasY(0) + findGraphY(oval.top), 240 | findCanvasX(0) + findGraphX(oval.right), 241 | findCanvasY(0) + findGraphY(oval.bottom)); 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /AXGraphView/src/main/java/com/aghajari/graphview/AXGraphFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.graphview; 20 | 21 | import android.graphics.Color; 22 | import android.graphics.Paint; 23 | import android.graphics.PointF; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | public abstract class AXGraphFormula { 29 | 30 | public AXGraphFormula(){ 31 | init(); 32 | } 33 | 34 | public abstract float function(float x); 35 | 36 | public boolean isInDomain(float x){ 37 | return true; 38 | } 39 | 40 | public AXGraphPointType getPointType(float x, float y){ 41 | return AXGraphPointType.CONTINUOUS; 42 | } 43 | 44 | protected void init(){ 45 | graphPaint = new Paint(); 46 | graphPaint.setColor(Color.BLUE); 47 | graphPaint.setStrokeWidth(strokeWidth); 48 | 49 | pointPaint = new Paint(); 50 | pointPaint.setColor(Color.BLUE); 51 | pointPaint.setStyle(Paint.Style.FILL); 52 | } 53 | 54 | private boolean enabled = true; 55 | private float strokeWidth = 6; 56 | private Paint graphPaint; 57 | private Paint pointPaint; 58 | private float pointCircleRadius = 10; 59 | protected int graphBackgroundColor = Color.WHITE; 60 | private List customPoints = null; 61 | protected AXGraphView graphView; 62 | 63 | protected void onAttachedToView(AXGraphView view){ 64 | this.graphView = view; 65 | } 66 | 67 | protected void onDetachedFromView(AXGraphView view){ 68 | this.graphView = null; 69 | } 70 | 71 | public void setCustomPoints(List customPoints) { 72 | this.customPoints = customPoints; 73 | } 74 | 75 | public void setCustomFunctionPoints(List customPoints) { 76 | this.customPoints = new ArrayList<>(); 77 | for (float x : customPoints){ 78 | addCustomFunctionPoint(x); 79 | } 80 | } 81 | 82 | public List getCustomPoints() { 83 | return customPoints; 84 | } 85 | 86 | public void addCustomPoint(float x,float y) { 87 | if (customPoints == null) customPoints = new ArrayList<>(); 88 | customPoints.add(new PointF(x,y)); 89 | } 90 | 91 | public void addCustomFunctionPoint(float x) { 92 | if (customPoints == null) customPoints = new ArrayList<>(); 93 | customPoints.add(new PointF(x,function(x))); 94 | } 95 | 96 | public boolean isEnabled() { 97 | return enabled; 98 | } 99 | 100 | public float getStrokeWidth() { 101 | return strokeWidth; 102 | } 103 | 104 | public Paint getGraphPaint() { 105 | return graphPaint; 106 | } 107 | 108 | public void setEnabled(boolean enabled) { 109 | this.enabled = enabled; 110 | } 111 | 112 | public void setGraphPaint(Paint graphPaint) { 113 | this.graphPaint = graphPaint; 114 | } 115 | 116 | public void setStrokeWidth(float strokeWidth) { 117 | this.strokeWidth = strokeWidth; 118 | } 119 | 120 | public float getPointCircleRadius() { 121 | return pointCircleRadius; 122 | } 123 | 124 | public void setPointCircleRadius(float pointCircleRadius) { 125 | this.pointCircleRadius = pointCircleRadius; 126 | } 127 | 128 | public Paint getPointPaint() { 129 | return pointPaint; 130 | } 131 | 132 | public void setPointPaint(Paint pointPaint) { 133 | this.pointPaint = pointPaint; 134 | } 135 | 136 | protected float sensitive(){ 137 | return AXGraphView.SENSITIVE; 138 | } 139 | 140 | protected void drawPoint(AXGraphCanvas canvas, float x, float y, AXGraphPointType type){ 141 | canvas.setRadiusFromAxis(false); 142 | 143 | switch (type){ 144 | case CONTINUOUS: 145 | break; 146 | case SPECIAL: 147 | case CUSTOM: 148 | case FILL: 149 | getPointPaint().setStyle(Paint.Style.FILL); 150 | canvas.drawCircle(x,y, getPointCircleRadius()/canvas.getGraphScale(), getPointPaint()); 151 | break; 152 | case EMPTY: 153 | canvas.drawCircle(x,y, getPointCircleRadius()/canvas.getGraphScale(),graphBackgroundColor, 154 | getPointCircleRadius()/2/canvas.getGraphScale(), getPointPaint().getColor(), getPointPaint()); 155 | } 156 | } 157 | 158 | protected boolean onDraw (AXGraphCanvas canvas){ 159 | return false; // continue drawing function by AXGraphView 160 | } 161 | 162 | float transformX = 0 ,transformY = 0; 163 | float transformScaleX = 1f,transformScaleY = 1f; 164 | 165 | public float getTransformScaleX() { 166 | return 1/transformScaleX; 167 | } 168 | 169 | public float getTransformScaleY() { 170 | return transformScaleY; 171 | } 172 | 173 | public float getTransformX() { 174 | return -transformX; 175 | } 176 | 177 | public float getTransformY() { 178 | return transformY; 179 | } 180 | 181 | public void applyTransformMove(float x,float y){ 182 | this.transformY = y; 183 | this.transformX = -x; 184 | } 185 | 186 | public void applyTransformScale (float x,float y){ 187 | this.transformScaleY = y; 188 | this.transformScaleX = 1/x; 189 | } 190 | 191 | protected float applyFunction (float x){ 192 | return transformScaleY * (function(transformScaleX * (x + transformX))) + transformY; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /AXGraphView/src/main/java/com/aghajari/graphview/AXGraphMultiFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.graphview; 20 | 21 | public abstract class AXGraphMultiFormula extends AXGraphFormula{ 22 | 23 | protected float[] applyMultiFunction (float x){ 24 | float[] y = multiFunction(transformScaleX * (x + transformX)); 25 | for (int i = 0; i1) { 121 | float scaledDividerInterval = xDividerIntervalInPx * scale; 122 | float c = 2; 123 | while (scaledDividerInterval / xDividerIntervalInPx >= c) { 124 | scaledDividerInterval -= xDividerIntervalInPx; 125 | s *= 2; 126 | c += 0.5f; 127 | } 128 | }else if (scale<1){ 129 | float scaledDividerInterval = xDividerIntervalInPx / scale; 130 | float c = 2; 131 | while (scaledDividerInterval / xDividerIntervalInPx >= c) { 132 | scaledDividerInterval -= xDividerIntervalInPx; 133 | s /= 2; 134 | c += 0.5f; 135 | } 136 | } 137 | s = Math.max(s,0); 138 | return 1f/s; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /AXGraphView/src/main/java/com/aghajari/graphview/AXGraphPointType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.graphview; 20 | 21 | public enum AXGraphPointType { 22 | CONTINUOUS, 23 | FILL, 24 | EMPTY, 25 | CUSTOM, 26 | SPECIAL 27 | } 28 | -------------------------------------------------------------------------------- /AXGraphView/src/main/java/com/aghajari/graphview/AXGraphUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.graphview; 20 | 21 | class AXGraphUtils { 22 | 23 | public static float round(float value, int places) { 24 | if (places < 0) throw new IllegalArgumentException(); 25 | 26 | long factor = (long) Math.pow(10, places); 27 | value = value * factor; 28 | long tmp = Math.round(value); 29 | return (float) tmp / factor; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /AXGraphView/src/main/java/com/aghajari/graphview/AXGraphView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.graphview; 20 | 21 | import android.animation.ValueAnimator; 22 | import android.content.Context; 23 | import android.graphics.Canvas; 24 | import android.graphics.Color; 25 | import android.graphics.Matrix; 26 | import android.graphics.PointF; 27 | import android.graphics.Rect; 28 | import android.graphics.RectF; 29 | import android.os.Build; 30 | import android.util.AttributeSet; 31 | import android.view.GestureDetector; 32 | import android.view.MotionEvent; 33 | import android.view.ScaleGestureDetector; 34 | import android.view.View; 35 | import android.view.animation.LinearInterpolator; 36 | 37 | import androidx.annotation.RequiresApi; 38 | import androidx.core.view.GestureDetectorCompat; 39 | 40 | import java.util.ArrayList; 41 | import java.util.Arrays; 42 | import java.util.List; 43 | 44 | import static com.aghajari.graphview.AXGraphOptions.DEFAULT; 45 | 46 | /** 47 | * @author Amir Hossein Aghajari 48 | * @version 1.1.0 49 | */ 50 | public class AXGraphView extends View { 51 | 52 | public static final float EPSILON = 1f; 53 | public static final float SENSITIVE = 1f; 54 | 55 | private List formulas = new ArrayList<>(); 56 | private AXGraphOptions options; 57 | protected float graphScale = 1; 58 | protected float[] paintsStrokeWidth = new float[4]; 59 | protected PointF axis; 60 | protected int mdc = 4; 61 | private int bg = Color.WHITE; 62 | private AXGraphCanvas graphCanvas; 63 | 64 | float[] matrixValues = new float[9]; 65 | float dy, dx; 66 | float xDIPx, xDI, yDIPx, yDI; 67 | private List drawPoints = new ArrayList<>(); 68 | private List mainPoints = new ArrayList<>(); 69 | float centerY, centerX; 70 | private RectF graphSize = new RectF(); 71 | 72 | private ScaleGestureDetector mScaleGestureDetector; 73 | private GestureDetectorCompat mGestureDetector; 74 | private float scale = 1f; 75 | private PointF start = new PointF(); 76 | private PointF mid = new PointF(); 77 | private float distanceX = 0f; 78 | private float distanceY = 0f; 79 | 80 | private RectF contentSize; 81 | private Matrix matrix = new Matrix(); 82 | private Matrix matrixInverse = new Matrix(); 83 | private Matrix savedMatrix = new Matrix(); 84 | private MatrixAnimation animation; 85 | private long animationDuration = 250; 86 | 87 | /** 88 | * The scale listener, used for handling multi-finger scale gestures. 89 | */ 90 | private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() { 91 | @Override 92 | public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { 93 | if (scaleGestureDetector.getCurrentSpan() > 10f) { 94 | savedMatrix.set(matrix); 95 | mid.set(scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY()); 96 | } 97 | return true; 98 | } 99 | 100 | @Override 101 | public boolean onScale(ScaleGestureDetector scaleGestureDetector) { 102 | scale = scaleGestureDetector.getScaleFactor(); 103 | return true; 104 | } 105 | }; 106 | 107 | /** 108 | * The gesture listener, used for handling simple gestures such as double touches, scrolls, 109 | * and flings. 110 | */ 111 | private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { 112 | @Override 113 | public boolean onDown(MotionEvent event) { 114 | savedMatrix.set(matrix); 115 | start.set(event.getX(), event.getY()); 116 | return true; 117 | } 118 | 119 | @Override 120 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float dX, float dY) { 121 | setupTranslation(dX, dY); 122 | matrix.postTranslate(distanceX, distanceY); 123 | return true; 124 | } 125 | 126 | @Override 127 | public boolean onDoubleTap(MotionEvent e) { 128 | return AXGraphView.this.onDoubleTap(e) || super.onDoubleTap(e); 129 | } 130 | 131 | @Override 132 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 133 | return true; 134 | } 135 | }; 136 | 137 | protected boolean onDoubleTap(MotionEvent e) { 138 | if (animationDuration < 0) 139 | return false; 140 | else if (graphScale != 1) 141 | return resetOnDoubleTap(e); 142 | else 143 | return zoomOnDoubleTap(e); 144 | } 145 | 146 | protected boolean resetOnDoubleTap(MotionEvent e) { 147 | postMatrixAnimation(createDefaultMatrix(), animationDuration); 148 | return true; 149 | } 150 | 151 | protected boolean zoomOnDoubleTap(MotionEvent e) { 152 | float scale = Math.min(2f, options != null ? options.maxZoom : 2f); 153 | setGraphScale(scale, e.getX(), e.getY()); 154 | 155 | return true; 156 | } 157 | 158 | public AXGraphView(Context context) { 159 | super(context); 160 | init(); 161 | } 162 | 163 | public AXGraphView(Context context, AttributeSet attrs) { 164 | super(context, attrs); 165 | init(); 166 | } 167 | 168 | public AXGraphView(Context context, AttributeSet attrs, int defStyleAttr) { 169 | super(context, attrs, defStyleAttr); 170 | init(); 171 | } 172 | 173 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 174 | public AXGraphView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 175 | super(context, attrs, defStyleAttr, defStyleRes); 176 | init(); 177 | } 178 | 179 | private void init() { 180 | graphCanvas = new AXGraphCanvas(this); 181 | mScaleGestureDetector = new ScaleGestureDetector(getContext(), mScaleGestureListener); 182 | mGestureDetector = new GestureDetectorCompat(getContext(), mGestureListener); 183 | } 184 | 185 | public void setGraphOptions(AXGraphOptions options) { 186 | this.options = options; 187 | xDIPx = options.xDividerIntervalInPx; 188 | xDI = options.xDividerInterval; 189 | paintsStrokeWidth[0] = options.axisPaint.getStrokeWidth(); 190 | paintsStrokeWidth[1] = options.dividerPaint.getStrokeWidth(); 191 | paintsStrokeWidth[2] = options.gridLinePaint.getStrokeWidth(); 192 | paintsStrokeWidth[3] = options.textPaint.getTextSize() / getContext().getResources().getDisplayMetrics().density; 193 | onSizeChanged(getWidth(),getHeight(),0,0); 194 | } 195 | 196 | public AXGraphOptions getGraphOptions() { 197 | return options; 198 | } 199 | 200 | public float findFormulaX(float rx) { 201 | return rx * xDI / xDIPx; 202 | } 203 | 204 | public float findFormulaY(float ry) { 205 | return ry * yDI / yDIPx; 206 | } 207 | 208 | public float findGraphX(float fx) { 209 | return fx / xDI * xDIPx; 210 | } 211 | 212 | public float findGraphY(float fy) { 213 | return fy * yDIPx / yDI; 214 | } 215 | 216 | public float findCanvasX(float fx) { 217 | return findGraphX(fx) + centerX; 218 | } 219 | 220 | public float findCanvasY(float fy) { 221 | return centerY - findGraphY(fy); 222 | } 223 | 224 | private void findAxisPosition() { 225 | //float x = options.axis == null || options.axis.x == DEFAULT ? getGraphWidth() / 2f : (getGraphWidth() / 2f) + options.axis.x; 226 | float x = options.axis == null || options.axis.x == DEFAULT ? (-graphSize.left) : (-graphSize.left) + options.axis.x; 227 | //float y = options.axis == null || options.axis.y == DEFAULT ? getGraphHeight() / 2f : (getGraphHeight() / 2f) + options.axis.y; 228 | float y = options.axis == null || options.axis.y == DEFAULT ? (-graphSize.top) : (-graphSize.top) + options.axis.y; 229 | axis = new PointF(x, y); 230 | 231 | centerY = axis.y; 232 | centerX = axis.x; 233 | } 234 | 235 | @Override 236 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 237 | super.onSizeChanged(w, h, oldw, oldh); 238 | if (options == null) return; 239 | //if (contentSize == null) { 240 | PointF pointF = findContentSize(w,h); 241 | setMaxScrollSize(w, h,pointF.x,pointF.y); 242 | //} 243 | findAxisPosition(); 244 | invalidate(); 245 | } 246 | 247 | private PointF findContentSize(int w,int h){ 248 | if (options==null) { 249 | graphSize.set(-w,-h,w,h); 250 | findAxisDiff(); 251 | return new PointF(w*2,h*2); 252 | }else if (!options.scrollEnabled) { 253 | graphSize.set(-w/2f,-h/2f,w/2f,h/2f); 254 | findAxisDiff(); 255 | return new PointF(w,h); 256 | } 257 | 258 | if (options.maxGraphX!=DEFAULT && options.minGraphX!=DEFAULT) { 259 | graphSize.left = findGraphX(options.minGraphX); 260 | graphSize.right = findGraphX(options.maxGraphX); 261 | } else { 262 | graphSize.left = -w; 263 | graphSize.right = w; 264 | } 265 | 266 | if (options.maxGraphY!=DEFAULT && options.minGraphY!=DEFAULT) { 267 | graphSize.top = findGraphY(-options.maxGraphY); 268 | graphSize.bottom = findGraphY(-options.minGraphY); 269 | } else { 270 | graphSize.top = -h; 271 | graphSize.bottom = h; 272 | } 273 | findAxisDiff(); 274 | 275 | return new PointF(graphSize.width(),graphSize.height()); 276 | } 277 | 278 | private void findAxisDiff(){ 279 | if (options.axis!=null && options.axis.x!=DEFAULT){ 280 | float x_dif = findGraphX(options.axis.x * options.xDividerInterval / options.xDividerIntervalInPx); 281 | graphSize.left += x_dif; 282 | graphSize.right += x_dif; 283 | } 284 | 285 | if (options.axis!=null && options.axis.y!=DEFAULT){ 286 | float y_dif = findGraphY(options.axis.y * options.yDividerInterval / options.yDividerIntervalInPx); 287 | graphSize.top += y_dif; 288 | graphSize.bottom += y_dif; 289 | } 290 | } 291 | 292 | @Override 293 | protected void onDraw(Canvas canvas) { 294 | if (options == null) return; 295 | matrix.getValues(matrixValues); 296 | 297 | if (options.scrollEnabled) { 298 | graphScale = matrixValues[Matrix.MSCALE_X]; 299 | dy = matrixValues[Matrix.MTRANS_Y]; 300 | dx = matrixValues[Matrix.MTRANS_X]; 301 | } else { 302 | dx = 0; 303 | dy = 0; 304 | graphScale = 1; 305 | } 306 | canvas.translate(dx, dy); 307 | canvas.scale(graphScale, graphScale); 308 | 309 | final float dx = -this.dx; 310 | 311 | if (axis == null) findAxisPosition(); 312 | xDIPx = options.getScaledDividerIntervalInPx(options.xDividerIntervalInPx, graphScale); 313 | yDIPx = options.getScaledDividerIntervalInPx(options.yDividerIntervalInPx, graphScale); 314 | xDI = options.getScaledDividerInterval(options.xDividerInterval, graphScale); 315 | yDI = options.getScaledDividerInterval(options.yDividerInterval, graphScale); 316 | 317 | float ls = options.dividerSize / 2; 318 | float text_ls = options.dividerSize / graphScale; 319 | updatePaints(); 320 | 321 | 322 | float min_display_x = findFormulaX((dx/graphScale) - centerX) - 0.5f; 323 | float max_display_x = findFormulaX(((dx + getWidth())/graphScale) - centerX) + 0.5f; 324 | float min_display_y = findFormulaY(((-dy)/graphScale) - centerY) - 0.5f; 325 | float max_display_y = findFormulaY((((-dy) + getHeight())/graphScale) - centerY) + 0.5f; 326 | 327 | mainPoints.clear(); 328 | if (options.drawAxisXDivider || options.drawGridXLines) { 329 | int xCount = (int) (centerX / xDIPx) * mdc; 330 | for (int i = 1; i <= xCount; i++) { 331 | if (min_display_x > -i*xDI || -i*xDI > max_display_x) continue; 332 | 333 | float xSelection = i * xDIPx; 334 | mainPoints.add(-i*xDI); 335 | 336 | if (options.drawGridXLines) 337 | canvas.drawLine(centerX - xSelection, Integer.MIN_VALUE, 338 | centerX - xSelection, Integer.MAX_VALUE, options.gridLinePaint); 339 | 340 | if (options.drawAxisXDivider) 341 | canvas.drawLine(centerX - xSelection, centerY - ls, 342 | centerX - xSelection, centerY + ls, options.dividerPaint); 343 | 344 | //if (options.drawXText) 345 | // drawTextX(canvas,centerX,centerY,text_ls,-xSelection,-i); 346 | } 347 | 348 | xCount = (int) ((getGraphWidth() - centerX) / xDIPx) * mdc; 349 | for (int i = 1; i <= xCount; i++) { 350 | if (min_display_x > i*xDI || i*xDI > max_display_x) continue; 351 | 352 | float xSelection = i * xDIPx; 353 | mainPoints.add(i*xDI); 354 | 355 | if (options.drawGridXLines) 356 | canvas.drawLine(centerX + xSelection, Integer.MIN_VALUE, 357 | centerX + xSelection, Integer.MAX_VALUE, options.gridLinePaint); 358 | 359 | if (options.drawAxisXDivider) 360 | canvas.drawLine(centerX + xSelection, centerY - ls, 361 | centerX + xSelection, centerY + ls, options.dividerPaint); 362 | 363 | //if (options.drawXText) 364 | // drawTextX(canvas,centerX,centerY,text_ls,xSelection,i); 365 | } 366 | } 367 | 368 | if (options.drawAxisYDivider || options.drawGridYLines) { 369 | int yCount = (int) (centerY / yDIPx) * mdc; 370 | for (int i = 1; i <= yCount; i++) { 371 | if (min_display_y > -i*yDI || -i*yDI > max_display_y) continue; 372 | 373 | float ySelection = i * yDIPx; 374 | if (options.drawGridYLines) 375 | canvas.drawLine(Integer.MIN_VALUE, centerY - ySelection, 376 | Integer.MAX_VALUE, centerY - ySelection, options.gridLinePaint); 377 | 378 | if (options.drawAxisYDivider) 379 | canvas.drawLine(centerX - ls, centerY - ySelection, 380 | centerX + ls, centerY - ySelection, options.dividerPaint); 381 | 382 | //if (options.drawYText) 383 | // drawTextY(canvas,centerX,centerY,text_ls,ySelection,i); 384 | } 385 | yCount = (int) ((getGraphHeight() - centerY) / yDIPx) * mdc; 386 | for (int i = 1; i <= yCount; i++) { 387 | if (min_display_y > i*yDI || i*yDI > max_display_y) continue; 388 | 389 | float ySelection = i * yDIPx; 390 | if (options.drawGridYLines) 391 | canvas.drawLine(Integer.MIN_VALUE, centerY + ySelection, 392 | Integer.MAX_VALUE, centerY + ySelection, options.gridLinePaint); 393 | 394 | if (options.drawAxisYDivider) 395 | canvas.drawLine(centerX - ls, centerY + ySelection, 396 | centerX + ls, centerY + ySelection, options.dividerPaint); 397 | 398 | //if (options.drawYText) 399 | // drawTextY(canvas,centerX,centerY,text_ls,-ySelection,-i); 400 | } 401 | } 402 | 403 | if (options.drawXText) { 404 | int xCount = (int) (centerX / xDIPx) * mdc; 405 | for (int i = 1; i <= xCount; i++) { 406 | if (min_display_x > -i*xDI || -i*xDI > max_display_x) continue; 407 | drawTextX(canvas, centerX, centerY, text_ls, -(i * xDIPx), -i); 408 | } 409 | xCount = (int) ((getGraphWidth() - centerX) / xDIPx) * mdc; 410 | for (int i = 1; i <= xCount; i++) { 411 | if (min_display_x > i*xDI || i*xDI > max_display_x) continue; 412 | drawTextX(canvas, centerX, centerY, text_ls, (i * xDIPx), i); 413 | } 414 | } 415 | if (options.drawYText) { 416 | int yCount = (int) (centerY / yDIPx) * mdc; 417 | for (int i = 1; i <= yCount; i++) { 418 | if (min_display_y > -i*yDI || -i*yDI > max_display_y) continue; 419 | drawTextY(canvas, centerX, centerY, text_ls, (i * yDIPx), i); 420 | } 421 | yCount = (int) ((getGraphHeight() - centerY) / yDIPx) * mdc; 422 | for (int i = 1; i <= yCount; i++) { 423 | if (min_display_y > i*yDI || i*yDI > max_display_y) continue; 424 | drawTextY(canvas, centerX, centerY, text_ls, -(i * yDIPx), -i); 425 | } 426 | } 427 | 428 | if (options.drawAxisY && min_display_x<0) 429 | canvas.drawLine(centerX, Integer.MIN_VALUE, centerX, Integer.MAX_VALUE, options.axisPaint); 430 | if (options.drawAxisX && min_display_y<0) 431 | canvas.drawLine(Integer.MIN_VALUE, centerY, Integer.MAX_VALUE, centerY, options.axisPaint); 432 | 433 | final float X_EPSILON = findFormulaX(EPSILON); 434 | for (AXGraphFormula formula : getFormulas()) { 435 | if (!formula.isEnabled()) continue; 436 | formula.getGraphPaint().setStrokeWidth(formula.getStrokeWidth() / graphScale); 437 | graphCanvas.setCanvas(canvas, formula); 438 | formula.onAttachedToView(this); 439 | 440 | if (!formula.onDraw(graphCanvas)) { 441 | graphCanvas.setApplyFormulaTransform(false); 442 | 443 | for (float rx = 0; rx <= getWidth() / graphScale; rx += formula.sensitive()) { 444 | final float x = (dx + (rx * graphScale)) / graphScale; 445 | final float xInAxis = x - centerX; 446 | final float fx = AXGraphUtils.round(findFormulaX(xInAxis), 3); 447 | 448 | if (!formula.isInDomain(fx)) { 449 | final float x1 = fx + X_EPSILON; 450 | final float x2 = fx - X_EPSILON; 451 | final float fy = formula.function(fx); 452 | 453 | if (fy == Float.POSITIVE_INFINITY || fy == Float.NEGATIVE_INFINITY) continue; 454 | if (formula.isInDomain(x1) && Math.abs(fy - formula.function(x1)) <= X_EPSILON) { 455 | drawPoints.add(new CirclePoints(fy, fx, AXGraphPointType.EMPTY)); 456 | } 457 | if (formula.isInDomain(x2) && Math.abs(fy - formula.function(x2)) <= X_EPSILON) { 458 | drawPoints.add(new CirclePoints(fy, fx, AXGraphPointType.EMPTY)); 459 | } 460 | continue; 461 | } 462 | 463 | drawFormula(formula,fx,X_EPSILON); 464 | } 465 | for (float mainFX : mainPoints){ 466 | if (formula.isInDomain(mainFX)) { 467 | drawFormula(formula,mainFX,X_EPSILON); 468 | } 469 | } 470 | } 471 | 472 | for (CirclePoints point : drawPoints) { 473 | formula.drawPoint(graphCanvas, point.fx, point.fy, point.type); 474 | } 475 | if (formula.getCustomPoints() != null) { 476 | for (PointF pointF : formula.getCustomPoints()) { 477 | formula.drawPoint(graphCanvas, pointF.x, pointF.y, AXGraphPointType.CUSTOM); 478 | } 479 | } 480 | drawPoints.clear(); 481 | } 482 | } 483 | 484 | protected void drawFormula (AXGraphFormula formula,float fx,float X_EPSILON){ 485 | //if (min_display_y > -fy || -fy > max_display_y) continue; 486 | if (formula instanceof AXGraphMultiFormula){ 487 | float[] fy1 = ((AXGraphMultiFormula) formula).applyMultiFunction(fx); 488 | float[] fy2 = ((AXGraphMultiFormula) formula).applyMultiFunction(fx + X_EPSILON); 489 | for (int fIndex = 0;fIndex getHeight()) continue; 493 | if (fy1[fIndex] == Float.POSITIVE_INFINITY || fy1[fIndex] == Float.NEGATIVE_INFINITY) continue; 494 | if (fy2[fIndex] == Float.POSITIVE_INFINITY || fy2[fIndex] == Float.NEGATIVE_INFINITY) continue; 495 | drawFunction(formula,fx,fy1[fIndex],fy2[fIndex],X_EPSILON); 496 | } 497 | } else { 498 | final float fy = formula.applyFunction(fx); 499 | 500 | if (fy == Float.POSITIVE_INFINITY || fy == Float.NEGATIVE_INFINITY) return; 501 | final float fy2 = formula.applyFunction(fx + X_EPSILON); 502 | if (fy2 == Float.POSITIVE_INFINITY || fy2 == Float.NEGATIVE_INFINITY) return; 503 | if (Math.abs(findCanvasY(fy) - findCanvasY(fy2)) > getHeight()) return; 504 | 505 | drawFunction(formula,fx,fy,fy2,X_EPSILON); 506 | } 507 | } 508 | 509 | protected void drawFunction(AXGraphFormula formula,float fx,float fy,float fy2,float X_EPSILON){ 510 | AXGraphPointType pointType = formula.getPointType(fx, fy); 511 | if (pointType == AXGraphPointType.CONTINUOUS) { 512 | graphCanvas.drawLine(fx, fy, fx + X_EPSILON, fy2); 513 | } else { 514 | drawPoints.add(new CirclePoints(fy, fx, pointType)); 515 | } 516 | } 517 | 518 | protected void drawTextX(Canvas canvas, float centerX, float centerY, float text_ls, float xSelection, int i) { 519 | String text = options.getTextForXAxis(findFormulaX(xSelection), i); 520 | Rect textBounds = new Rect(); 521 | options.textPaint.getTextBounds(text, 0, text.length(), textBounds); 522 | 523 | float left = centerX + xSelection - text_ls / 2 - textBounds.width() / 2f; 524 | float top = centerY + text_ls - textBounds.top - textBounds.height(); 525 | top = Math.max(top, -dy / graphScale + text_ls); 526 | top = Math.min(top, ((getHeight() - dy) / graphScale) - textBounds.height() - text_ls); 527 | float right = left + textBounds.width() + text_ls; 528 | float bottom = top + textBounds.height() + text_ls; 529 | if (options.textBackgroundPaint != null) { 530 | canvas.drawRect(left, top, right, bottom, options.textBackgroundPaint); 531 | } 532 | canvas.drawText(text, left + text_ls / 2, bottom - text_ls / 2, options.textPaint); 533 | } 534 | 535 | 536 | protected void drawTextY(Canvas canvas, float centerX, float centerY, float text_ls, float ySelection, int i) { 537 | String text = options.getTextForYAxis(findFormulaY(ySelection), i); 538 | Rect textBounds = new Rect(); 539 | options.textPaint.getTextBounds(text, 0, text.length(), textBounds); 540 | 541 | float left = centerX - textBounds.width() - text_ls; 542 | left = Math.max(left, -dx / graphScale); 543 | left = Math.min(left, ((getWidth() - dx) / graphScale) - textBounds.width() - text_ls); 544 | float top = centerY - ySelection - textBounds.height() / 2f - text_ls / 2; 545 | float right = left + textBounds.width() + text_ls - EPSILON; 546 | float bottom = top + textBounds.height() + text_ls; 547 | if (options.textBackgroundPaint != null) { 548 | canvas.drawRect(left, top, right, bottom, options.textBackgroundPaint); 549 | } 550 | canvas.drawText(text, left + text_ls / 4, bottom - text_ls / 2, options.textPaint); 551 | } 552 | 553 | protected void updatePaints() { 554 | options.axisPaint.setStrokeWidth(paintsStrokeWidth[0] / graphScale); 555 | options.dividerPaint.setStrokeWidth(paintsStrokeWidth[1] / graphScale); 556 | options.gridLinePaint.setStrokeWidth(paintsStrokeWidth[2] / graphScale); 557 | options.textPaint.setTextSize(paintsStrokeWidth[3] / graphScale * getContext().getResources().getDisplayMetrics().density); 558 | } 559 | 560 | public float getCenterX() { 561 | return centerX; 562 | } 563 | 564 | public float getCenterY() { 565 | return centerY; 566 | } 567 | 568 | public float getGraphWidth() { 569 | return options.scrollEnabled && contentSize != null ? contentSize.width() : getWidth(); 570 | } 571 | 572 | public float getGraphHeight() { 573 | return options.scrollEnabled && contentSize != null ? contentSize.height() : getHeight(); 574 | } 575 | 576 | @Override 577 | public boolean onTouchEvent(MotionEvent event) { 578 | if (options == null || !options.scrollEnabled) return super.onTouchEvent(event); 579 | 580 | matrix.set(savedMatrix); 581 | boolean gestureDetected = mGestureDetector.onTouchEvent(event); 582 | if (event.getPointerCount() > 1) { 583 | gestureDetected = mScaleGestureDetector.onTouchEvent(event) | gestureDetected; 584 | if (checkScaleBounds()) 585 | matrix.postScale(scale, scale, mid.x, mid.y); 586 | } 587 | matrix.invert(matrixInverse); 588 | savedMatrix.set(matrix); 589 | invalidate(); 590 | return gestureDetected; 591 | } 592 | 593 | private boolean checkScaleBounds() { 594 | float[] values = new float[9]; 595 | matrix.getValues(values); 596 | float sx = values[Matrix.MSCALE_X] * scale; 597 | float sy = values[Matrix.MSCALE_Y] * scale; 598 | return sx >= options.minZoom && sx <= options.maxZoom && sy >= options.minZoom && sy <= options.maxZoom; 599 | } 600 | 601 | private void setupTranslation(float dX, float dY) { 602 | distanceX = -1 * dX; 603 | distanceY = -1 * dY; 604 | 605 | if (contentSize != null) { 606 | float[] values = new float[9]; 607 | matrix.getValues(values); 608 | float totX = values[Matrix.MTRANS_X] + distanceX; 609 | float totY = values[Matrix.MTRANS_Y] + distanceY; 610 | float sx = values[Matrix.MSCALE_X]; 611 | 612 | Rect viewableRect = new Rect(); 613 | this.getDrawingRect(viewableRect); 614 | float offscreenWidth = contentSize.width() - (viewableRect.right - viewableRect.left); 615 | float offscreenHeight = contentSize.height() - (viewableRect.bottom - viewableRect.top); 616 | float maxDx = (contentSize.width() - (contentSize.width() / sx)) * sx; 617 | float maxDy = (contentSize.height() - (contentSize.height() / sx)) * sx; 618 | if (totX > 0 && distanceX > 0) distanceX = 0; 619 | if (totY > 0 && distanceY > 0) distanceY = 0; 620 | if (totX * -1 > offscreenWidth + maxDx && distanceX < 0) distanceX = 0; 621 | if (totY * -1 > offscreenHeight + maxDy && distanceY < 0) distanceY = 0; 622 | } 623 | } 624 | 625 | float graphWidth, graphHeight; 626 | 627 | protected void setMaxScrollSize(float graphWidth, float graphHeight, float maxWidth, float maxHeight) { 628 | this.contentSize = new RectF(0, 0, maxWidth, maxHeight); 629 | this.graphWidth = graphWidth; 630 | this.graphHeight = graphHeight; 631 | resetScroll(false); 632 | } 633 | 634 | @Override 635 | public void setBackgroundColor(int color) { 636 | super.setBackgroundColor(color); 637 | setGraphInnerPointColor(color); 638 | } 639 | 640 | public void setGraphInnerPointColor(int color) { 641 | bg = color; 642 | if (options != null) 643 | options.textBackgroundPaint.setColor(color); 644 | for (AXGraphFormula formula : formulas) 645 | formula.graphBackgroundColor = color; 646 | } 647 | 648 | public float getGraphScale() { 649 | return graphScale; 650 | } 651 | 652 | public Matrix getGraphMatrix() { 653 | return matrix; 654 | } 655 | 656 | public void setAnimationDuration(long animationDuration) { 657 | this.animationDuration = animationDuration; 658 | } 659 | 660 | public long getAnimationDuration() { 661 | return animationDuration; 662 | } 663 | 664 | public void setGraphScale(float scale, float x, float y) { 665 | matrix.getValues(matrixValues); 666 | 667 | Matrix matrix = new Matrix(); 668 | matrix.setTranslate(matrixValues[Matrix.MTRANS_X], matrixValues[Matrix.MTRANS_Y]); 669 | matrix.postScale(scale, scale, x, y); 670 | postMatrixAnimation(matrix, animationDuration); 671 | } 672 | 673 | public void postMatrixAnimation(Matrix matrix, long duration) { 674 | if (animation != null) animation.cancel(); 675 | 676 | if (duration <= 0) { 677 | this.matrix.set(matrix); 678 | this.savedMatrix.set(matrix); 679 | invalidate(); 680 | } else { 681 | animation = new MatrixAnimation(this.matrix, matrix, duration); 682 | } 683 | } 684 | 685 | public void addFormula(AXGraphFormula formula) { 686 | formulas.add(formula); 687 | formula.graphBackgroundColor = bg; 688 | formula.onAttachedToView(this); 689 | 690 | 691 | } 692 | 693 | public void addFormulas(AXGraphFormula... graphFormulas) { 694 | formulas.addAll(Arrays.asList(graphFormulas)); 695 | for (AXGraphFormula formula : graphFormulas) { 696 | formula.graphBackgroundColor = bg; 697 | formula.onAttachedToView(this); 698 | } 699 | } 700 | 701 | public void addFormulas(List graphFormulas) { 702 | formulas.addAll(graphFormulas); 703 | for (AXGraphFormula formula : graphFormulas) { 704 | formula.graphBackgroundColor = bg; 705 | formula.onAttachedToView(this); 706 | } 707 | } 708 | 709 | public void removeFormula(AXGraphFormula formula) { 710 | formulas.remove(formula); 711 | formula.onDetachedFromView(this); 712 | } 713 | 714 | public void resetScroll(boolean animation) { 715 | if (animation) { 716 | postMatrixAnimation(createDefaultMatrix(), animationDuration); 717 | } else { 718 | matrix.reset(); 719 | savedMatrix.reset(); 720 | matrix.set(createDefaultMatrix()); 721 | savedMatrix.set(matrix); 722 | mdc = (int) ((contentSize.width() / graphWidth) * 2); 723 | invalidate(); 724 | } 725 | } 726 | 727 | public Matrix createDefaultMatrix() { 728 | //float axisX = (contentSize.width()-graphWidth)/2 ,axisY = (contentSize.height()-graphHeight) /2; 729 | float axisX = graphSize.left + graphWidth/2f ,axisY = graphSize.top + graphHeight/2; 730 | if (options != null && options.axis!=null && options.axis.x!=DEFAULT) axisX += options.axis.x; 731 | if (options != null && options.axis!=null && options.axis.y!=DEFAULT) axisY += options.axis.y; 732 | 733 | Matrix matrix = new Matrix(); 734 | //matrix.postTranslate(-axisX, -axisY); 735 | matrix.postTranslate(axisX, axisY); 736 | matrix.postScale(1f, 1f); 737 | return matrix; 738 | } 739 | 740 | public void clearFormulas() { 741 | for (AXGraphFormula formula : formulas) 742 | formula.onDetachedFromView(this); 743 | formulas.clear(); 744 | } 745 | 746 | public List getFormulas() { 747 | return formulas; 748 | } 749 | 750 | @Override 751 | protected void onDetachedFromWindow() { 752 | resetScroll(false); 753 | super.onDetachedFromWindow(); 754 | } 755 | 756 | private static class CirclePoints { 757 | AXGraphPointType type; 758 | float fy, fx; 759 | 760 | public CirclePoints(float fy, float fx, AXGraphPointType type) { 761 | this.type = type; 762 | this.fy = fy; 763 | this.fx = fx; 764 | } 765 | } 766 | 767 | private class MatrixAnimation { 768 | private final PointF scaleStart; 769 | private final PointF scaleEnd; 770 | private final PointF translateStart; 771 | private final PointF translateEnd; 772 | private final ValueAnimator animator; 773 | 774 | public MatrixAnimation(Matrix startMatrix, Matrix endMatrix, long duration) { 775 | float[] a = new float[9]; 776 | float[] b = new float[9]; 777 | 778 | startMatrix.getValues(a); 779 | endMatrix.getValues(b); 780 | 781 | scaleStart = new PointF(a[Matrix.MSCALE_X], a[Matrix.MSCALE_Y]); 782 | scaleEnd = new PointF(b[Matrix.MSCALE_X], b[Matrix.MSCALE_Y]); 783 | translateStart = new PointF(a[Matrix.MTRANS_X], a[Matrix.MTRANS_Y]); 784 | translateEnd = new PointF(b[Matrix.MTRANS_X], b[Matrix.MTRANS_Y]); 785 | 786 | animator = ValueAnimator.ofFloat(0, 1f); 787 | animator.setInterpolator(new LinearInterpolator()); 788 | animator.setDuration(duration); 789 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 790 | @Override 791 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 792 | float interpolatedTime = valueAnimator.getAnimatedFraction(); 793 | 794 | PointF sFactor = new PointF( 795 | scaleEnd.x * interpolatedTime / scaleStart.x + 1 - interpolatedTime, 796 | scaleEnd.y * interpolatedTime / scaleStart.y + 1 - interpolatedTime); 797 | PointF tFactor = new PointF( 798 | (translateEnd.x - translateStart.x) * interpolatedTime, 799 | (translateEnd.y - translateStart.y) * interpolatedTime); 800 | 801 | Matrix matrix = new Matrix(); 802 | matrix.postScale(scaleStart.x, scaleStart.y, 0, 0); 803 | matrix.postScale(sFactor.x, sFactor.y, 0, 0); 804 | matrix.postTranslate(translateStart.x, translateStart.y); 805 | matrix.postTranslate(tFactor.x, tFactor.y); 806 | AXGraphView.this.matrix.set(matrix); 807 | AXGraphView.this.savedMatrix.set(matrix); 808 | AXGraphView.this.invalidate(); 809 | } 810 | }); 811 | animator.start(); 812 | } 813 | 814 | public void cancel() { 815 | if (animator != null && animator.isRunning()) animator.end(); 816 | } 817 | 818 | } 819 | } -------------------------------------------------------------------------------- /AXGraphView/src/test/java/com/aghajari/graphview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.graphview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AXGraphView 2 | ![AXGraphView](./images/Header.jpg) 3 | 4 | [![Platform](https://img.shields.io/badge/platform-android-green.svg)](http://developer.android.com/index.html) 5 | [![API](https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=16) 6 | [![Maven Central](https://img.shields.io/maven-central/v/io.github.aghajari/AXGraphView.svg?label=Maven%20Central)](https://search.maven.org/artifact/io.github.aghajari/AXGraphView/1.1.0/aar) 7 | [![Join the chat at https://gitter.im/Aghajari/community](https://badges.gitter.im/Aghajari/community.svg)](https://gitter.im/Aghajari/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 8 | 9 | **AXGraphView** creates zoomable and scrollable graphs. 10 | 11 | ## Screenshot 12 | 13 |
14 | 15 | 16 |
17 | 18 | ## Preview (GIF) 19 | 20 | 21 | ## Table of Contents 22 | - [Installation](#installation) 23 | - [Quick Start](#quick-start) 24 | - [FormulaParser](#formulaparser) 25 | - [Graph Axis](#graph-axis) 26 | - [Domain of Graph](#domain-of-graph) 27 | - [Custom Points](#custom-points) 28 | - [Transform](#transform) 29 | - [MultiFormula](#multiformula) 30 | - [Custom Draw](#custom-draw) 31 | - [Famous Graphs](#famous-graphs) 32 | - [Multi Formula Graphs](#multi-formula-graphs) 33 | - [Author](#author) 34 | - [License](#license) 35 | 36 | ## Installation 37 | AXGraphView is available in the `mavenCentral()`, so you just need to add it as a dependency (Module gradle) 38 | 39 | Gradle 40 | ```gradle 41 | implementation 'io.github.aghajari:AXGraphView:1.1.0' 42 | ``` 43 | 44 | Maven 45 | ```xml 46 | 47 | io.github.aghajari 48 | AXGraphView 49 | 1.1.0 50 | pom 51 | 52 | ``` 53 | 54 | # Quick Start 55 | 56 | 1. Add view to your layout : 57 | ```xml 58 | 63 | ``` 64 | 65 | 2. Set Graph Options 66 | ```java 67 | AXGraphView graphView = findViewById(R.id.graph_view); 68 | AXGraphOptions options = new AXGraphOptions(this); 69 | 70 | options.scrollEnabled = true; 71 | options.xDividerIntervalInPx = 100; 72 | options.xDividerInterval = 0.5f; 73 | options.yDividerIntervalInPx = 100; 74 | options.yDividerInterval = 0.5f; 75 | options.maxZoom = 6f; 76 | options.minZoom = 0.5f 77 | options.drawAxis = true; 78 | ... 79 | 80 | graphView.setGraphOptions(options); 81 | ``` 82 | 83 | 3. Add Graph Formula 84 | ```java 85 | graphView.addFormula(new AXGraphFormula() { // x^2 86 | @Override 87 | public float function(float x) { 88 | return (float) Math.pow(x,2); 89 | } 90 | }); 91 | 92 | graphView.addFormula(new AXGraphFormula() { 93 | @Override 94 | public float function(float x) { // x^3 95 | return (float) Math.pow(x,3); 96 | } 97 | }); 98 | ``` 99 | 100 | Or simply use Graph Formula Parser : 101 | ```java 102 | graphView.addFormula(new GraphFormulaParser(this,"(x)^2")); 103 | graphView.addFormula(new GraphFormulaParser(this,"(x)^3")); 104 | ``` 105 | 106 | ## FormulaParser 107 | Currently i'm using this [Eval Method](https://stackoverflow.com/a/26227947) for parsing a formula in sample of AXGraphView, you can also use other libraries to parse the formula and draw it in AXGraphView. 108 | 109 | ## Graph Axis 110 | 111 | - Change Axis Position : 112 | You can change the default axis position in options. 113 | 114 | ![Image](./images/axis.png) 115 | 116 | ```java 117 | options.axis = new PointF(-300,AXGraphOptions.DEFAULT); 118 | ``` 119 | 120 | - Disable drawing Axis and grid lines : 121 | ```java 122 | options.drawAxisX = false; 123 | options.drawAxisY = false; 124 | options.drawGridXLines = false; 125 | options.drawGridYLines = false; 126 | options.drawAxisXDivider = false; 127 | options.drawAxisYDivider = false; 128 | options.drawXText = false; 129 | options.drawYText = false; 130 | ``` 131 | 132 | - Change axis color : 133 | ```java 134 | options.axisPaint.setColor(Color.RED); 135 | options.textPaint.setColor(Color.RED); 136 | options.gridLinePaint.setColor(Color.GRAY); 137 | ``` 138 | 139 | ## Domain of Graph 140 | You can limit domain of the Graph. 141 | 142 | ![Image](./images/domain.png) 143 | 144 | ```java 145 | graphView.addFormula(new AXGraphFormula() { // x^2 146 | @Override 147 | public float function(float x) { 148 | return (float) Math.pow(x,2); 149 | } 150 | 151 | @Override 152 | public boolean isInDomain(float x) { 153 | return Math.abs(x) < 1; 154 | } 155 | }); 156 | ``` 157 | 158 | ## Custom Points 159 | You can customize an Specific Point in Graph. 160 | 161 | ```java 162 | graphView.addFormula(new AXGraphFormula() { // Sin(x) 163 | 164 | @Override 165 | protected void init() { 166 | super.init(); 167 | addCustomFunctionPoint(1); 168 | addCustomFunctionPoint(-1); 169 | addCustomPoint(0,3); 170 | } 171 | 172 | @Override 173 | public float function(float x) { 174 | return (float) Math.sin(x); 175 | } 176 | 177 | @Override 178 | public AXGraphPointType getPointType(float x, float y) { 179 | if (x==0) return AXGraphPointType.EMPTY; 180 | return super.getPointType(x, y); 181 | } 182 | }); 183 | ``` 184 | 185 | ## Transform 186 | You can move(x&y) or change scale(x&y) of the function on graph! 187 | 188 | ```java 189 | formula.applyTransformMove(3f,2.5f); 190 | formula.applyTransformScale(1.5f,1.5f); 191 | ``` 192 | 193 | ## MultiFormula 194 | You can also draw multi functions together. 195 | 196 | ```java 197 | public class HeartGraphFormula extends AXGraphMultiFormula { 198 | 199 | @Override 200 | public float[] multiFunction(float x) { 201 | return new float[] { 202 | function1(x), 203 | function2(x) 204 | }; 205 | } 206 | 207 | public float function1(float x) { 208 | return (float) Math.sqrt(1 - Math.pow(Math.abs(x) - 1,2)); 209 | } 210 | 211 | public float function2(float x) { 212 | return (float) (Math.acos(1 - Math.abs(x)) - Math.PI); 213 | } 214 | } 215 | ``` 216 | 217 | Output : 218 | 219 | 220 | 221 | ## Custom Draw 222 | You can draw custom shapes using AXGraphCanvas (by graph x,y) : 223 | 224 | ![Image](./images/custom_draw.png) 225 | 226 |
CircleGraphFormula (click to expand) 227 |

228 | 229 | ````java 230 | public class CircleGraphFormula extends PaintedGraphFormula { 231 | 232 | private float r; 233 | private int angle; 234 | Paint paint; 235 | 236 | public CircleGraphFormula(Context context,float r,int angle){ 237 | super(context); 238 | this.r = r; 239 | this.angle = angle; 240 | paint = new Paint(); 241 | 242 | getGraphPaint().setStyle(Paint.Style.STROKE); 243 | getPointPaint().setColor(getGraphPaint().getColor()); 244 | setPointCircleRadius(getPointCircleRadius()*1.2f); 245 | 246 | addCustomPoint(0,r); 247 | addCustomPoint(0,-r); 248 | addCustomPoint(r,0); 249 | addCustomPoint(-r,0); 250 | } 251 | 252 | @Override 253 | protected boolean onDraw(AXGraphCanvas canvas) { 254 | canvas.setRadiusFromAxis(true); 255 | canvas.drawCircle(0,0,r, getGraphPaint()); 256 | 257 | if (angle!=0) { 258 | float angleR = (float) Math.toRadians(angle); 259 | String text = angle+"°"; 260 | 261 | paint.setColor(Color.parseColor("#03DAC5")); 262 | paint.setStrokeWidth(getGraphPaint().getStrokeWidth()); 263 | final float x = (float) Math.cos(angleR) * r; 264 | final float y = (float) Math.sin(angleR) * r; 265 | 266 | float r2 = r/5; 267 | paint.setStyle(Paint.Style.STROKE); 268 | canvas.drawArc(-r2,-r2,r2,r2,-angle,angle,true,paint); 269 | 270 | paint.setStyle(Paint.Style.FILL); 271 | paint.setTextSize(canvas.findGraphX(r)/10); 272 | canvas.drawText(text,r2,r2/1.5f, Gravity.CENTER_VERTICAL|Gravity.LEFT,paint); 273 | 274 | canvas.drawLine(0,0,x,0,paint); 275 | paint.setPathEffect(new DashPathEffect(new float[] {20f/canvas.getGraphScale(),20f/canvas.getGraphScale()}, 0f)); 276 | canvas.drawLine(x,y,x,0,paint); 277 | canvas.drawLine(0,y,x,y,paint); 278 | paint.setPathEffect(null); 279 | 280 | paint.setColor(canvas.getGraphView().getContext().getResources().getColor(R.color.colorPrimary)); 281 | canvas.drawLine(0,0,x,y,paint); 282 | 283 | int savedColor = getPointPaint().getColor(); 284 | getPointPaint().setColor(paint.getColor()); 285 | drawPoint(canvas,x,y, AXGraphPointType.CUSTOM); 286 | getPointPaint().setColor(savedColor); 287 | } 288 | return true; //skip drawing function 289 | } 290 | 291 | @Override 292 | public float function(float x) { 293 | return Float.POSITIVE_INFINITY; //undefined 294 | } 295 | } 296 | ```` 297 |

298 | 299 | ## Famous Graphs 300 | 301 | |Name|Formula|AXGraphView| 302 | | :----: | :----: | :----: | 303 | |Quadratic|x^2|| 304 | |Cubic|x^3|| 305 | |Quadratic And Cubic|x^2 , x^3|| 306 | |Homographic|1/x|| 307 | |Square|√x|| 308 | |Cubic Root|3√x|| 309 | |Cubic Root|3√(x^2)|| 310 | |Bracket|[x]|| 311 | |Absolute Value|\|x\||| 312 | |\|x+1\| - \|x-1\||\|x+1\| - \|x-1\||| 313 | |Exponential|2^x|| 314 | |Logarithm|log(x)|| 315 | |Sine|sin(x)|| 316 | |Cosine|cos(x)|| 317 | |Sine And Cosine|sin(x) , cos(x)|| 318 | |Tangent|tan(x)|| 319 | |Cotangent|cot(x)|| 320 | |Tangent And Cotangent|tan(x) , cot(x)|| 321 | 322 | ## Multi Formula Graphs 323 | 324 | |Name|AXGraphView| 325 | | :----: | :----: | 326 | |Heart|| 327 | |Captain America|| 328 | |Superman|| 329 | |Batman|| 330 | |The Flash|| 331 | |Wonder Woman|| 332 | 333 | ## Author 334 | - **Amir Hossein Aghajari** 335 | 336 | License 337 | ======= 338 | 339 | Copyright 2021 Amir Hossein Aghajari 340 | Licensed under the Apache License, Version 2.0 (the "License"); 341 | you may not use this file except in compliance with the License. 342 | You may obtain a copy of the License at 343 | 344 | http://www.apache.org/licenses/LICENSE-2.0 345 | 346 | Unless required by applicable law or agreed to in writing, software 347 | distributed under the License is distributed on an "AS IS" BASIS, 348 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 349 | See the License for the specific language governing permissions and 350 | limitations under the License. 351 | 352 | 353 |

354 |
355 | LCoders | AmirHosseinAghajari 356 |
Amir Hossein AghajariEmailGitHub 357 |
358 | 359 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 30 5 | 6 | defaultConfig { 7 | applicationId "com.aghajari.app.graph" 8 | minSdkVersion 16 9 | targetSdkVersion 30 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(dir: "libs", include: ["*.jar"]) 26 | implementation 'androidx.appcompat:appcompat:1.3.1' 27 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0' 28 | testImplementation 'junit:junit:4.13.2' 29 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 30 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 31 | 32 | implementation 'androidx.recyclerview:recyclerview:1.2.1' 33 | implementation 'androidx.cardview:cardview:1.0.0' 34 | implementation project(':AXGraphView') 35 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/aghajari/app/graph/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.aghajari.app.simplegraph", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/GraphActivity.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.core.app.ActivityOptionsCompat; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.graphics.Color; 10 | import android.os.Build; 11 | import android.os.Bundle; 12 | import android.view.View; 13 | 14 | import com.aghajari.graphview.AXGraphView; 15 | import com.aghajari.app.graph.adapter.AdapterData; 16 | 17 | public class GraphActivity extends AppCompatActivity { 18 | AXGraphView view; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_graph); 24 | 25 | int graphIndex = getIntent().getIntExtra("graph", 0); 26 | AdapterData.GraphInfo info = AdapterData.list.get(graphIndex); 27 | getSupportActionBar().setTitle(info.getName()); 28 | 29 | view = findViewById(R.id.graph_view); 30 | view.addFormulas(info.getFormulas()); 31 | view.setGraphOptions(info.getGraphActivityOptions()); 32 | view.setBackgroundColor(Color.WHITE); 33 | } 34 | 35 | public static void start(Context context, View view, int index) { 36 | Intent starter = new Intent(context, GraphActivity.class); 37 | starter.putExtra("graph",index); 38 | 39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 40 | ActivityOptionsCompat options = ActivityOptionsCompat. 41 | makeSceneTransitionAnimation((Activity) context, view, "graph"); 42 | context.startActivity(starter, options.toBundle()); 43 | }else { 44 | context.startActivity(starter); 45 | } 46 | } 47 | 48 | @Override 49 | public void onBackPressed() { 50 | super.onBackPressed(); 51 | view.resetScroll(false); 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import androidx.appcompat.widget.AppCompatButton; 6 | import androidx.appcompat.widget.AppCompatEditText; 7 | import androidx.appcompat.widget.AppCompatTextView; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import android.graphics.Typeface; 11 | import android.os.Bundle; 12 | import android.view.Gravity; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.FrameLayout; 17 | import android.widget.TextView; 18 | 19 | import com.aghajari.graphview.AXGraphView; 20 | import com.aghajari.app.graph.adapter.AdapterData; 21 | 22 | public class MainActivity extends AppCompatActivity { 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | AdapterData.init(getApplicationContext()); 28 | setContentView(R.layout.activity_main); 29 | 30 | RecyclerView rv = findViewById(R.id.rv); 31 | rv.setAdapter(new Adapter()); 32 | } 33 | 34 | public class Adapter extends RecyclerView.Adapter{ 35 | 36 | @NonNull 37 | @Override 38 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 39 | if (viewType == -1) { 40 | AppCompatTextView textView = new AppCompatTextView(parent.getContext()); 41 | textView.setGravity(Gravity.CENTER); 42 | textView.setTypeface(Typeface.DEFAULT_BOLD); 43 | textView.setTextColor(getResources().getColor(R.color.colorPrimaryDark)); 44 | textView.setTextSize(20); 45 | textView.setText(R.string.author); 46 | textView.setLayoutParams(new FrameLayout.LayoutParams(-1, (int) (56 * getResources().getDisplayMetrics().density))); 47 | return new ViewHolder(textView); 48 | } else { 49 | return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_item, parent, false)); 50 | } 51 | } 52 | 53 | @Override 54 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 55 | holder.bind(position); 56 | } 57 | 58 | @Override 59 | public int getItemCount() { 60 | return AdapterData.list.size() + 1; 61 | } 62 | 63 | @Override 64 | public int getItemViewType(int position) { 65 | return position == (getItemCount()-1) ? -1 : 1; 66 | } 67 | 68 | public class ViewHolder extends RecyclerView.ViewHolder{ 69 | AXGraphView view; 70 | TextView name; 71 | 72 | public ViewHolder(@NonNull View itemView) { 73 | super(itemView); 74 | view = itemView.findViewById(R.id.graph_view); 75 | name = itemView.findViewById(R.id.graph_name); 76 | } 77 | 78 | public ViewHolder(@NonNull TextView itemView) { 79 | super(itemView); 80 | } 81 | 82 | public void bind(final int index){ 83 | if (view == null) return; 84 | AdapterData.GraphInfo info = AdapterData.list.get(index); 85 | name.setText(info.getName()); 86 | view.clearFormulas(); 87 | view.addFormulas(info.getFormulas()); 88 | view.setGraphOptions(info.getMainActivityOptions()); 89 | 90 | view.setOnClickListener(new View.OnClickListener() { 91 | @Override 92 | public void onClick(View view) { 93 | GraphActivity.start(MainActivity.this, (View) view.getParent().getParent(),index); 94 | } 95 | }); 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/adapter/AdapterData.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.adapter; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | 6 | import com.aghajari.app.graph.graphs.AbsoluteValueGraphFormula; 7 | import com.aghajari.app.graph.graphs.BracketGraphFormula; 8 | import com.aghajari.app.graph.graphs.CircleGraphFormula; 9 | import com.aghajari.app.graph.graphs.CubicRootGraphFormula; 10 | import com.aghajari.app.graph.graphs.CubicRootX2GraphFormula; 11 | import com.aghajari.app.graph.multigraphs.BatmanGraphFormula; 12 | import com.aghajari.app.graph.multigraphs.CaptainAmericaGraphFormula; 13 | import com.aghajari.app.graph.multigraphs.FlashGraphFormula; 14 | import com.aghajari.app.graph.multigraphs.HeartGraphFormula; 15 | import com.aghajari.app.graph.graphs.CustomGraphFormula; 16 | import com.aghajari.app.graph.multigraphs.SupermanGraphFormula; 17 | import com.aghajari.app.graph.multigraphs.WonderWomanGraphFormula; 18 | import com.aghajari.app.graph.parser.GraphFormulaParser; 19 | import com.aghajari.graphview.AXGraphFormula; 20 | import com.aghajari.graphview.AXGraphOptions; 21 | import com.aghajari.app.graph.R; 22 | 23 | import java.util.ArrayList; 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | public class AdapterData { 28 | public static List list; 29 | 30 | public static void init(Context context){ 31 | list = new ArrayList<>(); 32 | 33 | list.add(new GraphInfo("Quadratic (x^2)", 34 | createOptions(context,true), 35 | createOptions(context,false), 36 | new GraphFormulaParser(context,"(x)^2",0f))); 37 | 38 | list.add(new GraphInfo("Cubic (x^3)", 39 | createOptions(context,true), 40 | createOptions(context,false), 41 | new GraphFormulaParser(context,"(x)^3",0f))); 42 | 43 | list.add(new GraphInfo("x^3 And x^2", 44 | createOptions(context,true), 45 | createOptions(context,false), 46 | new GraphFormulaParser(context,"(x)^3"), 47 | new GraphFormulaParser(context,"(x)^2", R.color.colorPrimary,0f,1f))); 48 | 49 | list.add(new GraphInfo("x^2 And (x^2)^-1", 50 | createOptions(context,true), 51 | createOptions(context,false), 52 | new GraphFormulaParser(context,"(x)^2"), 53 | new GraphFormulaParser(context,"sqrt(x)", R.color.colorPrimary,0f,1f))); 54 | 55 | list.add(new GraphInfo("Homographic (1/x)", 56 | createCustomOptions(context,true), 57 | createCustomOptions(context,false), 58 | new GraphFormulaParser(context,"1/(x)"))); 59 | 60 | list.add(new GraphInfo("Square (√x)", 61 | createOptions(context,true), 62 | createOptions(context,false), 63 | new GraphFormulaParser(context,"sqrt(x)",0f))); 64 | 65 | list.add(new GraphInfo("Cubic Root (3√x)", 66 | createOptions(context,true), 67 | createOptions(context,false), 68 | new CubicRootGraphFormula(context))); 69 | 70 | list.add(new GraphInfo("Cubic Root x^2 (3√(x^2))", 71 | createOptions(context,true), 72 | createOptions(context,false), 73 | new CubicRootX2GraphFormula(context))); 74 | 75 | list.add(new GraphInfo("Bracket ([x])", 76 | createOptions(context,true), 77 | createOptions(context,false), 78 | new BracketGraphFormula(context))); 79 | 80 | list.add(new GraphInfo("Absolute value (|x|)", 81 | createOptions(context,true), 82 | createOptions(context,false), 83 | new AbsoluteValueGraphFormula(context))); 84 | 85 | list.add(new GraphInfo("Custom (|x+1| - |x-1|)", 86 | createCustomOptions(context,true), 87 | createCustomOptions(context,false), 88 | new CustomGraphFormula(context))); 89 | 90 | list.add(new GraphInfo("Exponential (2^x)", 91 | createOptions(context,true), 92 | createOptions(context,false), 93 | new GraphFormulaParser(context,"2^(x)"))); 94 | 95 | list.add(new GraphInfo("Logarithm (log(x))", 96 | createOptions(context,true), 97 | createOptions(context,false), 98 | new GraphFormulaParser(context,"log(x)"))); 99 | 100 | list.add(new GraphInfo("Sine (sin(x))", 101 | createTrigonometryOptions(context,true), 102 | createTrigonometryOptions(context,false), 103 | new GraphFormulaParser(context,"sin(x)"))); 104 | 105 | list.add(new GraphInfo("Cosine (cos(x))", 106 | createTrigonometryOptions(context,true), 107 | createTrigonometryOptions(context,false), 108 | new GraphFormulaParser(context,"cos(x)"))); 109 | 110 | list.add(new GraphInfo("sin(x) And cos(x)", 111 | createTrigonometryOptions(context,true), 112 | createTrigonometryOptions(context,false), 113 | new GraphFormulaParser(context,"sin(x)"), 114 | new GraphFormulaParser(context,"cos(x)", R.color.colorPrimary))); 115 | 116 | list.add(new GraphInfo("Tangent (tan(x))", 117 | createTrigonometryOptions(context,true), 118 | createTrigonometryOptions(context,false), 119 | new GraphFormulaParser(context,"tan(x)"))); 120 | 121 | list.add(new GraphInfo("Cotangent (cot(x))", 122 | createTrigonometryOptions(context,true), 123 | createTrigonometryOptions(context,false), 124 | new GraphFormulaParser(context,"cot(x)"))); 125 | 126 | list.add(new GraphInfo("tan(x) And cot(x)", 127 | createTrigonometryOptions(context,true), 128 | createTrigonometryOptions(context,false), 129 | new GraphFormulaParser(context,"tan(x)"), 130 | new GraphFormulaParser(context,"cot(x)",R.color.colorPrimary))); 131 | 132 | list.add(new GraphInfo("Custom Draw", 133 | createCircleOptions(context,true), 134 | createCircleOptions(context,false), 135 | new CircleGraphFormula(context,1, 60))); 136 | 137 | list.add(new GraphInfo("Heart", 138 | createMultiGraphOptions(context,true), 139 | createMultiGraphOptions(context,false), 140 | new HeartGraphFormula(context))); 141 | 142 | list.add(new GraphInfo("The Flash", 143 | createMultiGraphOptions(context,true), 144 | createMultiGraphOptions(context,false), 145 | new FlashGraphFormula(context))); 146 | 147 | list.add(new GraphInfo("Captain America", 148 | createMultiGraphOptions(context,true), 149 | createMultiGraphOptions(context,false), 150 | new CaptainAmericaGraphFormula(context))); 151 | 152 | list.add(new GraphInfo("Batman", 153 | createMultiGraphOptions(context,true), 154 | createMultiGraphOptions(context,false), 155 | new BatmanGraphFormula(context))); 156 | 157 | list.add(new GraphInfo("Wonder Woman", 158 | createMultiGraphOptions(context,true), 159 | createMultiGraphOptions(context,false), 160 | new WonderWomanGraphFormula(context))); 161 | 162 | list.add(new GraphInfo("Superman", 163 | createMultiGraphOptions(context,true), 164 | createMultiGraphOptions(context,false), 165 | new SupermanGraphFormula(context))); 166 | } 167 | 168 | private static AXGraphOptions createOptions(Context context, boolean mainActivity){ 169 | if (!mainActivity) return new AXGraphOptions(context); 170 | AXGraphOptions options = new AXGraphOptions(context); 171 | options.scrollEnabled = false; 172 | options.xDividerIntervalInPx = 150; 173 | options.yDividerIntervalInPx = 150; 174 | return options; 175 | } 176 | 177 | private static AXGraphOptions createCustomOptions(Context context, boolean mainActivity){ 178 | if (!mainActivity) return new AXGraphOptions(context); 179 | AXGraphOptions options = new AXGraphOptions(context); 180 | options.scrollEnabled = false; 181 | options.xDividerIntervalInPx = 80; 182 | options.yDividerIntervalInPx = 80; 183 | 184 | return options; 185 | } 186 | 187 | private static AXGraphOptions createCircleOptions(Context context, boolean mainActivity){ 188 | AXGraphOptions options = new AXGraphOptions(context); 189 | options.scrollEnabled = false; 190 | options.xDividerIntervalInPx = 150; 191 | options.yDividerIntervalInPx = 150; 192 | 193 | if (!mainActivity) { 194 | options.xDividerInterval = 0.5f; 195 | options.yDividerInterval = 0.5f; 196 | } 197 | return options; 198 | } 199 | 200 | private static AXGraphOptions createTrigonometryOptions(Context context, boolean mainActivity){ 201 | TrigonometryGraphOptions options = new TrigonometryGraphOptions(context); 202 | 203 | options.scrollEnabled = false; 204 | options.xDividerIntervalInPx = 100 * options.xDividerInterval; 205 | options.yDividerInterval = 1; 206 | options.yDividerIntervalInPx = 100; 207 | 208 | /**if (!mainActivity){ 209 | options.xLength *= 2; 210 | options.yLength *= 2; 211 | }*/ 212 | return options; 213 | } 214 | 215 | private static AXGraphOptions createMultiGraphOptions(Context context, boolean mainActivity){ 216 | AXGraphOptions options = new AXGraphOptions(context); 217 | options.scrollEnabled = false; 218 | options.xDividerIntervalInPx = 150; 219 | options.yDividerIntervalInPx = 150; 220 | options.maxZoom = 2.25f; 221 | options.minZoom = 0.25f; 222 | options.axisPaint.setColor(Color.GRAY); 223 | 224 | if (mainActivity) { 225 | options.xDividerInterval = 2f; 226 | options.yDividerInterval = 2f; 227 | } 228 | return options; 229 | } 230 | 231 | public static class GraphInfo { 232 | final List formulas; 233 | final AXGraphOptions options; 234 | final AXGraphOptions mainOptions; 235 | final String name; 236 | 237 | public GraphInfo(String name, AXGraphOptions options, AXGraphOptions mainOptions, AXGraphFormula... graphFormulas){ 238 | formulas = new ArrayList<>(Arrays.asList(graphFormulas)); 239 | this.name = name; 240 | this.options = options; 241 | this.mainOptions = mainOptions; 242 | } 243 | 244 | public List getFormulas() { 245 | return formulas; 246 | } 247 | 248 | public String getName() { 249 | return name; 250 | } 251 | 252 | public AXGraphOptions getMainActivityOptions() { 253 | options.scrollEnabled = false; 254 | return options; 255 | } 256 | 257 | public AXGraphOptions getGraphActivityOptions() { 258 | mainOptions.scrollEnabled = true; 259 | return mainOptions; 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/adapter/PaintedGraphFormula.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.adapter; 2 | 3 | import android.content.Context; 4 | 5 | import com.aghajari.graphview.AXGraphFormula; 6 | import com.aghajari.app.graph.R; 7 | 8 | public abstract class PaintedGraphFormula extends AXGraphFormula { 9 | 10 | public PaintedGraphFormula(Context context){ 11 | getGraphPaint().setColor(context.getResources().getColor(R.color.colorAccent)); 12 | getPointPaint().setColor(context.getResources().getColor(R.color.colorPrimary)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/adapter/PaintedGraphMultiFormula.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.adapter; 2 | 3 | import android.content.Context; 4 | 5 | import com.aghajari.app.graph.R; 6 | import com.aghajari.graphview.AXGraphFormula; 7 | import com.aghajari.graphview.AXGraphMultiFormula; 8 | 9 | public abstract class PaintedGraphMultiFormula extends AXGraphMultiFormula { 10 | 11 | public PaintedGraphMultiFormula(Context context){ 12 | super(); 13 | getGraphPaint().setColor(context.getResources().getColor(R.color.colorAccent)); 14 | getPointPaint().setColor(context.getResources().getColor(R.color.colorPrimary)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/adapter/TrigonometryGraphOptions.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.adapter; 2 | 3 | import android.content.Context; 4 | 5 | import com.aghajari.graphview.AXGraphOptions; 6 | 7 | public class TrigonometryGraphOptions extends AXGraphOptions { 8 | 9 | final static String pi = "π"; 10 | final static int pi_d = 2; // π/2 11 | 12 | public TrigonometryGraphOptions(Context context) { 13 | super(context); 14 | xDividerInterval = (float) Math.PI / pi_d; 15 | } 16 | 17 | @Override 18 | protected String getTextForXAxis(float x, int i) { 19 | float e = 1/calculateLastScaleEffect(); 20 | int a = Math.abs(i); 21 | int b = pi_d; 22 | if (e<1) a /= e; else b *= e; 23 | 24 | while (a % 2 == 0 && b % 2 == 0){ 25 | a /= 2; 26 | b /= 2; 27 | } 28 | 29 | String da = a!=1 ? String.valueOf(a) : ""; 30 | String db = b!=1 ? "/"+b : ""; 31 | String n = i<0 ? "-" : ""; 32 | return n + da + pi + db; 33 | } 34 | 35 | @Override 36 | protected String getTextForYAxis(float y, int index) { 37 | return super.getTextForYAxis(y, index); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/graphs/AbsoluteValueGraphFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.app.graph.graphs; 20 | 21 | import android.content.Context; 22 | 23 | import com.aghajari.app.graph.adapter.PaintedGraphFormula; 24 | 25 | public class AbsoluteValueGraphFormula extends PaintedGraphFormula { 26 | 27 | public AbsoluteValueGraphFormula(Context context){ 28 | super(context); 29 | } 30 | 31 | @Override 32 | public float function(float x) { 33 | return Math.abs(x); 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/graphs/BracketGraphFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.app.graph.graphs; 20 | 21 | import android.content.Context; 22 | 23 | import com.aghajari.app.graph.adapter.PaintedGraphFormula; 24 | import com.aghajari.graphview.AXGraphPointType; 25 | import com.aghajari.graphview.AXGraphView; 26 | 27 | public class BracketGraphFormula extends PaintedGraphFormula { 28 | 29 | public BracketGraphFormula(Context context){ 30 | super(context); 31 | getPointPaint().setColor(getGraphPaint().getColor()); 32 | } 33 | 34 | @Override 35 | public float function(float x) { 36 | return (float) Math.ceil(x); 37 | } 38 | 39 | @Override 40 | public AXGraphPointType getPointType(float x, float y) { 41 | float y1 = function(x - graphView.findFormulaX(AXGraphView.EPSILON)); 42 | float y2 = function(x + graphView.findFormulaX(AXGraphView.EPSILON)); 43 | 44 | if (y != y1) { 45 | return AXGraphPointType.EMPTY; 46 | } 47 | 48 | if (y != y2) { 49 | return AXGraphPointType.FILL; 50 | } 51 | return super.getPointType(x, y); 52 | } 53 | 54 | @Override 55 | protected float sensitive() { 56 | return 0.9f; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/graphs/CircleGraphFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.app.graph.graphs; 20 | 21 | import android.content.Context; 22 | import android.graphics.Color; 23 | import android.graphics.DashPathEffect; 24 | import android.graphics.Paint; 25 | import android.view.Gravity; 26 | 27 | import com.aghajari.app.graph.adapter.PaintedGraphFormula; 28 | import com.aghajari.graphview.AXGraphCanvas; 29 | import com.aghajari.graphview.AXGraphPointType; 30 | import com.aghajari.app.graph.R; 31 | 32 | public class CircleGraphFormula extends PaintedGraphFormula { 33 | 34 | private float r; 35 | private int angle; 36 | Paint paint; 37 | 38 | public CircleGraphFormula(Context context,float r,int angle){ 39 | super(context); 40 | this.r = r; 41 | this.angle = angle; 42 | paint = new Paint(); 43 | 44 | getGraphPaint().setStyle(Paint.Style.STROKE); 45 | getPointPaint().setColor(getGraphPaint().getColor()); 46 | setPointCircleRadius(getPointCircleRadius()*1.2f); 47 | 48 | addCustomPoint(0,r); 49 | addCustomPoint(0,-r); 50 | addCustomPoint(r,0); 51 | addCustomPoint(-r,0); 52 | } 53 | 54 | @Override 55 | protected boolean onDraw(AXGraphCanvas canvas) { 56 | canvas.setRadiusFromAxis(true); 57 | canvas.drawCircle(0,0,r, getGraphPaint()); 58 | 59 | if (angle!=0) { 60 | float angleR = (float) Math.toRadians(angle); 61 | String text = angle+"°"; 62 | 63 | paint.setColor(Color.parseColor("#03DAC5")); 64 | paint.setStrokeWidth(getGraphPaint().getStrokeWidth()); 65 | final float x = (float) Math.cos(angleR) * r; 66 | final float y = (float) Math.sin(angleR) * r; 67 | 68 | //paint.setStyle(Paint.Style.STROKE); 69 | float r2 = r/5; 70 | paint.setStyle(Paint.Style.STROKE); 71 | canvas.drawArc(-r2,-r2,r2,r2,-angle,angle,true,paint); 72 | 73 | paint.setStyle(Paint.Style.FILL); 74 | paint.setTextSize(canvas.findGraphX(r)/10); 75 | canvas.drawText(text,r2,r2/1.5f, Gravity.CENTER_VERTICAL|Gravity.LEFT,paint); 76 | 77 | canvas.drawLine(0,0,x,0,paint); 78 | paint.setPathEffect(new DashPathEffect(new float[] {20f/canvas.getGraphScale(),20f/canvas.getGraphScale()}, 0f)); 79 | canvas.drawLine(x,y,x,0,paint); 80 | canvas.drawLine(0,y,x,y,paint); 81 | paint.setPathEffect(null); 82 | 83 | paint.setColor(canvas.getGraphView().getContext().getResources().getColor(R.color.colorPrimary)); 84 | canvas.drawLine(0,0,x,y,paint); 85 | 86 | int savedColor = getPointPaint().getColor(); 87 | getPointPaint().setColor(paint.getColor()); 88 | drawPoint(canvas,x,y, AXGraphPointType.CUSTOM); 89 | getPointPaint().setColor(savedColor); 90 | } 91 | return true; //skip drawing function 92 | } 93 | 94 | @Override 95 | public float function(float x) { 96 | return Float.POSITIVE_INFINITY; //undefined 97 | } 98 | 99 | @Override 100 | public boolean isInDomain(float x) { 101 | return false; 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/graphs/CubicRootGraphFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.app.graph.graphs; 20 | 21 | import android.content.Context; 22 | 23 | import com.aghajari.app.graph.adapter.PaintedGraphFormula; 24 | 25 | public class CubicRootGraphFormula extends PaintedGraphFormula { 26 | 27 | public CubicRootGraphFormula(Context context){ 28 | super(context); 29 | } 30 | 31 | @Override 32 | public float function(float x) { 33 | return (float) Math.cbrt(x); 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/graphs/CubicRootX2GraphFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.app.graph.graphs; 20 | 21 | import android.content.Context; 22 | 23 | import com.aghajari.app.graph.adapter.PaintedGraphFormula; 24 | 25 | public class CubicRootX2GraphFormula extends PaintedGraphFormula { 26 | 27 | public CubicRootX2GraphFormula(Context context){ 28 | super(context); 29 | } 30 | 31 | @Override 32 | public float function(float x) { 33 | return (float) Math.cbrt(Math.pow(x,2)); 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/graphs/CustomGraphFormula.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 - Amir Hossein Aghajari 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.aghajari.app.graph.graphs; 20 | 21 | import android.content.Context; 22 | 23 | import com.aghajari.app.graph.adapter.PaintedGraphFormula; 24 | 25 | // |x+1|-|x-1| 26 | public class CustomGraphFormula extends PaintedGraphFormula { 27 | 28 | public CustomGraphFormula(Context context){ 29 | super(context); 30 | } 31 | 32 | @Override 33 | public float function(float x) { 34 | return Math.abs(x+1f) - Math.abs(x-1f) ; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/multigraphs/BatmanGraphFormula.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.multigraphs; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | 6 | import com.aghajari.app.graph.adapter.PaintedGraphMultiFormula; 7 | 8 | public class BatmanGraphFormula extends PaintedGraphMultiFormula { 9 | 10 | public BatmanGraphFormula(Context context){ 11 | super(context); 12 | applyTransformScale(0.5f,0.5f); 13 | getGraphPaint().setColor(Color.BLACK); 14 | } 15 | 16 | @Override 17 | public float[] multiFunction(float x) { 18 | return new float[] { 19 | function1(x), 20 | function2(x), 21 | function3(x), 22 | function4(x), 23 | function5(x), 24 | function6(x), 25 | function7(x) 26 | }; 27 | } 28 | 29 | public float function1(float x) { 30 | if (Math.abs(x) > 3) 31 | return (float) (3 * Math.sqrt(-Math.pow(x/7,2) + 1)); 32 | else 33 | return Float.POSITIVE_INFINITY; 34 | } 35 | 36 | public float function2(float x) { 37 | if (Math.abs(x) > 4) 38 | return (float) (-3 * Math.sqrt(-Math.pow(x/7,2) + 1)); 39 | else 40 | return Float.POSITIVE_INFINITY; 41 | } 42 | 43 | public float function3(float x) { 44 | return (float) (Math.abs(x/2) - Math.pow(x,2) * ((3*Math.sqrt(33) - 7)/112) 45 | + Math.sqrt(1 - Math.pow(Math.abs(Math.abs(x) - 2)-1,2)) - 3); 46 | } 47 | 48 | public float function4(float x) { 49 | if (Math.abs(x) > 0.75 && Math.abs(x) < 1) 50 | return (float) (9 - 8*Math.abs(x)); 51 | else 52 | return Float.POSITIVE_INFINITY; 53 | } 54 | 55 | public float function5(float x) { 56 | if (Math.abs(x) > 0.5 && Math.abs(x) < 0.75) 57 | return (float) (3*Math.abs(x) + 0.75); 58 | else 59 | return Float.POSITIVE_INFINITY; 60 | } 61 | 62 | public float function6(float x) { 63 | if (Math.abs(x) < 0.5) 64 | return 2.25f; 65 | else 66 | return Float.POSITIVE_INFINITY; 67 | } 68 | 69 | public float function7(float x) { 70 | if (Math.abs(x) > 1) 71 | return (float) (1.5 - 0.5 * Math.abs(x) - ((6*Math.sqrt(10))/14) 72 | * (Math.sqrt(3-Math.pow(x,2)+2*Math.abs(x))-2)); 73 | else 74 | return Float.POSITIVE_INFINITY; 75 | } 76 | 77 | @Override 78 | protected float sensitive() { 79 | return super.sensitive()/4f; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/multigraphs/CaptainAmericaGraphFormula.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.multigraphs; 2 | 3 | import android.content.Context; 4 | 5 | import com.aghajari.app.graph.adapter.PaintedGraphMultiFormula; 6 | import com.aghajari.graphview.AXGraphCanvas; 7 | 8 | public class CaptainAmericaGraphFormula extends PaintedGraphMultiFormula { 9 | 10 | public CaptainAmericaGraphFormula(Context context){ 11 | super(context); 12 | 13 | int color = getGraphPaint().getColor(); 14 | getGraphPaint().setColor(getPointPaint().getColor()); 15 | getPointPaint().setColor(color); 16 | 17 | applyTransformScale(0.2f,0.2f); 18 | } 19 | 20 | @Override 21 | public float[] multiFunction(float x) { 22 | return new float[] { 23 | function1(x), 24 | function12(x), 25 | function2(x), 26 | function3(x), 27 | function32(x) 28 | }; 29 | } 30 | 31 | public float function1(float x) { 32 | if (isInRange(0,x,1.67f) || isInRange(2.62f,x,4.20f)) 33 | return -3*x + 7; 34 | else 35 | return Float.POSITIVE_INFINITY; 36 | } 37 | 38 | public float function12(float x) { 39 | if (isInRange(-1.67f,x,0) || isInRange(-4.22f,x,-2.62f)) 40 | return 3*x + 7; 41 | else 42 | return Float.POSITIVE_INFINITY; 43 | } 44 | 45 | public float function2(float x) { 46 | if (isInRange(1.67f,Math.abs(x),6.72f)) 47 | return 2; 48 | else 49 | return Float.POSITIVE_INFINITY; 50 | } 51 | 52 | public float function3(float x) { 53 | if (isInRange(0,x,4.19f) || isInRange(-6.71f,x,-2.62f)) 54 | return -0.7f * x - 2.7f; 55 | else 56 | return Float.POSITIVE_INFINITY; 57 | } 58 | 59 | public float function32(float x) { 60 | if (isInRange(-4.21f,x,0) || isInRange(2.62f,x,6.71f)) 61 | return 0.7f * x - 2.7f; 62 | else 63 | return Float.POSITIVE_INFINITY; 64 | } 65 | 66 | @Override 67 | protected boolean onDraw(AXGraphCanvas canvas) { 68 | canvas.setApplyFormulaTransform(true); 69 | canvas.setRadiusFromAxis(true); 70 | 71 | canvas.drawBorderCircle(0,0,16,getPointCircleRadius()/2/canvas.getGraphScale(),getPointPaint()); 72 | canvas.drawBorderCircle(0,0,13,getPointCircleRadius()/2/canvas.getGraphScale(),getPointPaint()); 73 | canvas.drawBorderCircle(0,0,10,getPointCircleRadius()/2/canvas.getGraphScale(),getPointPaint()); 74 | 75 | return super.onDraw(canvas); 76 | } 77 | 78 | public boolean isInRange(float c, float x, float b){ 79 | return c Math.abs(x)) 52 | return 9f; 53 | else 54 | return Float.POSITIVE_INFINITY; 55 | } 56 | 57 | //y=|x|-15 {-18 Math.abs(x)) 60 | return Math.abs(x) - 15f; 61 | else 62 | return Float.POSITIVE_INFINITY; 63 | } 64 | 65 | //y=7 {-110) 172 | return (float) (Math.sqrt((-x+6)/6) -5); 173 | else 174 | return Float.POSITIVE_INFINITY; 175 | } 176 | 177 | //y=-sqrt{-(x-6)/6} -5 {00) 180 | return (float) (-Math.sqrt((-x+6)/6) -5); 181 | else 182 | return Float.POSITIVE_INFINITY; 183 | } 184 | 185 | //y= (-3/5 * sqrt{5(x+5)}) +5 {x<0} 186 | public float function19(float x) { 187 | if (x<0) 188 | return (float) ( -(Math.sqrt(5*x + 25) * 3)/5 + 5); 189 | else 190 | return Float.POSITIVE_INFINITY; 191 | } 192 | 193 | //y= ( 3/5 * sqrt{-2.27* (x-11)}) -1 {x>0} 194 | public float function20(float x) { 195 | if (x>0) 196 | return (float) ((Math.sqrt(2.27*(11-x))*3)/5 -1); 197 | else 198 | return Float.POSITIVE_INFINITY; 199 | } 200 | 201 | @Override 202 | protected boolean onDraw(AXGraphCanvas canvas) { 203 | canvas.setApplyFormulaTransform(true); 204 | //x=12 {3= 9.1f) 141 | return (float) (-2.1 * (Math.sqrt(-x+10)) + 5); 142 | else 143 | return Float.POSITIVE_INFINITY; 144 | } 145 | 146 | public float function16(float x) { 147 | if (x <= -8.1f) 148 | return (float) (-2.1 * (Math.sqrt(x+9)) + 3); 149 | else if (x >= 8.1f) 150 | return (float) (-2.1 * (Math.sqrt(-x+9)) + 3); 151 | else 152 | return Float.POSITIVE_INFINITY; 153 | } 154 | 155 | public float function17(float x) { 156 | if (x <= -7.1f) 157 | return (float) (-2.1 * (Math.sqrt(x+8)) + 1); 158 | else if (x >= 7.1f) 159 | return (float) (-2.1 * (Math.sqrt(-x+8)) + 1); 160 | else 161 | return Float.POSITIVE_INFINITY; 162 | } 163 | 164 | public boolean isInRange(float c, float x, float b){ 165 | return c<=x && x<=b; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/parser/FormulaParser.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.parser; 2 | 3 | public class FormulaParser { 4 | 5 | private String savedFormula; 6 | private String formula; 7 | private int pos = -1, ch; 8 | 9 | public FormulaParser(String formula){ 10 | this.savedFormula = formula; 11 | this.formula = formula; 12 | } 13 | 14 | public double parse(float x) { 15 | this.formula = this.savedFormula.replace("x",String.valueOf(x)); 16 | return parse(); 17 | } 18 | 19 | public double parse() { 20 | pos = -1; ch = 0; 21 | nextChar(); 22 | double x = parseExpression(); 23 | if (pos < formula.length()) throw new RuntimeException("Unexpected: " + (char)ch); 24 | return x; 25 | } 26 | 27 | 28 | private void nextChar() { 29 | ch = (++pos < formula.length()) ? formula.charAt(pos) : -1; 30 | } 31 | 32 | private boolean eat(int charToEat) { 33 | while (ch == ' ') nextChar(); 34 | if (ch == charToEat) { 35 | nextChar(); 36 | return true; 37 | } 38 | return false; 39 | } 40 | 41 | private double parseExpression() { 42 | double x = parseTerm(); 43 | for (;;) { 44 | if (eat('+')) { 45 | x += parseTerm(); // addition 46 | } else if (eat('-')){ 47 | x -= parseTerm(); // subtraction 48 | } else return x; 49 | } 50 | } 51 | 52 | private double parseTerm() { 53 | double x = parseFactor(); 54 | for (;;) { 55 | if (eat('*')) { 56 | x *= parseFactor(); // multiplication 57 | } else if (eat('/')) { 58 | x /= parseFactor(); // division 59 | } else return x; 60 | } 61 | } 62 | 63 | private double parseFactor() { 64 | if (eat('+')) return parseFactor(); // unary plus 65 | if (eat('-')) return -parseFactor(); // unary minus 66 | 67 | double x; 68 | int startPos = this.pos; 69 | if (eat('(')) { // parentheses 70 | x = parseExpression(); 71 | eat(')'); 72 | } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers 73 | while ((ch >= '0' && ch <= '9') || ch == '.') nextChar(); 74 | x = Double.parseDouble(formula.substring(startPos, this.pos)); 75 | } else if (ch >= 'a' && ch <= 'z') { // functions 76 | while (ch >= 'a' && ch <= 'z') nextChar(); 77 | String func = formula.substring(startPos, this.pos); 78 | x = parseFactor(); 79 | x = parseFunctions(func,x); 80 | } else { 81 | throw new RuntimeException("Unexpected: " + (char)ch); 82 | } 83 | 84 | if (eat('^')) 85 | x = Math.pow(x, parseFactor()); // exponentiation 86 | 87 | return x; 88 | } 89 | 90 | private double parseFunctions(String func,double x){ 91 | switch (func.toLowerCase()) { 92 | case "sqrt": 93 | x = Math.sqrt(x); 94 | break; 95 | case "sin": 96 | x = Math.sin(x); 97 | break; 98 | case "cos": 99 | x = Math.cos(x); 100 | break; 101 | case "tan": 102 | x = Math.tan(x); 103 | break; 104 | case "cot": 105 | x = 1/Math.tan(x); 106 | break; 107 | case "log": 108 | x = Math.log(x); 109 | break; 110 | default: 111 | throw new RuntimeException("Unknown function: " + func); 112 | } 113 | return x; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/com/aghajari/app/graph/parser/GraphFormulaParser.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph.parser; 2 | 3 | import android.content.Context; 4 | 5 | import com.aghajari.app.graph.adapter.PaintedGraphFormula; 6 | import com.aghajari.app.graph.R; 7 | 8 | import java.util.Arrays; 9 | 10 | public class GraphFormulaParser extends PaintedGraphFormula { 11 | FormulaParser parser; 12 | 13 | public GraphFormulaParser(Context context,String formula,Float... customPointsX){ 14 | super(context); 15 | parser = new FormulaParser(formula); 16 | if (customPointsX.length>0) 17 | setCustomFunctionPoints(Arrays.asList(customPointsX)); 18 | } 19 | 20 | public GraphFormulaParser(Context context,String formula,int colorId,Float... customPointsX){ 21 | super(context); 22 | getGraphPaint().setColor(context.getResources().getColor(colorId)); 23 | getPointPaint().setColor(context.getResources().getColor(R.color.colorPrimaryDark)); 24 | parser = new FormulaParser(formula); 25 | if (customPointsX.length>0) 26 | setCustomFunctionPoints(Arrays.asList(customPointsX)); 27 | } 28 | 29 | @Override 30 | public float function(float x) { 31 | try { 32 | return (float) parser.parse(x); 33 | }catch (Exception ignore){ 34 | return Float.POSITIVE_INFINITY; // undefined 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/graph_parent_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/graph_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v21/activity_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v21/rv_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 28 | 29 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v23/rv_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 28 | 29 | 35 | 36 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rv_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 27 | 28 | 34 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AXGraphView 3 | Amir Hossein Aghajari 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/aghajari/app/graph/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.aghajari.app.graph; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:4.2.2" 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | plugins { 16 | id("io.github.gradle-nexus.publish-plugin") version "1.1.0" 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | maven { url 'https://maven.google.com' } 22 | mavenCentral() 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | 30 | ext["signing.keyId"] = '' 31 | ext["signing.password"] = '' 32 | ext["signing.key"] = '' 33 | ext["sonatypeStagingProfileId"] = '' 34 | ext["ossrhUsername"] = '' 35 | ext["ossrhPassword"] = '' 36 | 37 | File secretPropsFile = project.rootProject.file('local.properties') 38 | Properties p = new Properties() 39 | new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) } 40 | p.each { name, value -> ext[name] = value } 41 | 42 | nexusPublishing { 43 | repositories { 44 | sonatype { 45 | stagingProfileId = rootProject.ext["sonatypeStagingProfileId"] 46 | username = rootProject.ext["ossrhUsername"] 47 | password = rootProject.ext["ossrhPassword"] 48 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) 49 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /deploy.settings: -------------------------------------------------------------------------------- 1 | siteUrl = https://github.com/Aghajari/AXGraphView 2 | gitUrl = https://github.com/Aghajari/AXGraphView.git 3 | version = 1.1.0 4 | groupId = io.github.aghajari 5 | licenses = ['Apache-2.0'] 6 | id = AXGraphView 7 | name = AXGraphView -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jan 07 18:18:01 IRST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/1.jpg -------------------------------------------------------------------------------- /images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/2.jpg -------------------------------------------------------------------------------- /images/AXGraphView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/AXGraphView.gif -------------------------------------------------------------------------------- /images/Header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/Header.jpg -------------------------------------------------------------------------------- /images/axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/axis.png -------------------------------------------------------------------------------- /images/custom_draw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/custom_draw.png -------------------------------------------------------------------------------- /images/domain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/domain.png -------------------------------------------------------------------------------- /images/graphs/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/1.jpg -------------------------------------------------------------------------------- /images/graphs/abs_custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/abs_custom.png -------------------------------------------------------------------------------- /images/graphs/absolute_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/absolute_value.png -------------------------------------------------------------------------------- /images/graphs/bracket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/bracket.png -------------------------------------------------------------------------------- /images/graphs/cos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/cos.png -------------------------------------------------------------------------------- /images/graphs/cot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/cot.png -------------------------------------------------------------------------------- /images/graphs/cubic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/cubic.png -------------------------------------------------------------------------------- /images/graphs/cubic_root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/cubic_root.png -------------------------------------------------------------------------------- /images/graphs/cubic_root_x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/cubic_root_x2.png -------------------------------------------------------------------------------- /images/graphs/exponential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/exponential.png -------------------------------------------------------------------------------- /images/graphs/homographic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/homographic.png -------------------------------------------------------------------------------- /images/graphs/logarithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/logarithm.png -------------------------------------------------------------------------------- /images/graphs/quadratic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/quadratic.png -------------------------------------------------------------------------------- /images/graphs/quadratic_and_cubic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/quadratic_and_cubic.png -------------------------------------------------------------------------------- /images/graphs/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/screen1.png -------------------------------------------------------------------------------- /images/graphs/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/screen2.png -------------------------------------------------------------------------------- /images/graphs/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/screen3.png -------------------------------------------------------------------------------- /images/graphs/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/screen4.png -------------------------------------------------------------------------------- /images/graphs/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/screen5.png -------------------------------------------------------------------------------- /images/graphs/screen6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/screen6.png -------------------------------------------------------------------------------- /images/graphs/sin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/sin.png -------------------------------------------------------------------------------- /images/graphs/sin_and_cos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/sin_and_cos.png -------------------------------------------------------------------------------- /images/graphs/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/square.png -------------------------------------------------------------------------------- /images/graphs/tan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/tan.png -------------------------------------------------------------------------------- /images/graphs/tan_and_cot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs/tan_and_cot.png -------------------------------------------------------------------------------- /images/graphs2/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/2.jpg -------------------------------------------------------------------------------- /images/graphs2/batman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/batman.png -------------------------------------------------------------------------------- /images/graphs2/captain_america.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/captain_america.png -------------------------------------------------------------------------------- /images/graphs2/flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/flash.png -------------------------------------------------------------------------------- /images/graphs2/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/heart.png -------------------------------------------------------------------------------- /images/graphs2/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/screen1.png -------------------------------------------------------------------------------- /images/graphs2/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/screen2.png -------------------------------------------------------------------------------- /images/graphs2/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/screen3.png -------------------------------------------------------------------------------- /images/graphs2/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/screen4.png -------------------------------------------------------------------------------- /images/graphs2/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/screen5.png -------------------------------------------------------------------------------- /images/graphs2/screen_heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/screen_heart.png -------------------------------------------------------------------------------- /images/graphs2/superman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/superman.png -------------------------------------------------------------------------------- /images/graphs2/wonder_woman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aghajari/AXGraphView/f687351e4ba82c91616429348542443761ff3c9a/images/graphs2/wonder_woman.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':AXGraphView' 2 | include ':app' 3 | rootProject.name = "SimpleGraph" --------------------------------------------------------------------------------