├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── volokh │ │ └── danylo │ │ └── midpointcircle │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── volokh │ │ │ └── danylo │ │ │ └── midpointcircle │ │ │ ├── Diagram.java │ │ │ ├── MidPointCircleActivity.java │ │ │ ├── Point.java │ │ │ ├── circle_points_creator │ │ │ ├── CirclePointsCreator.java │ │ │ └── FirstQuadrantCirclePointsCreator.java │ │ │ └── mirror_helper │ │ │ ├── CircleMirrorHelper.java │ │ │ └── FirstQuadrantCircleMirrorHelper.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── volokh │ └── danylo │ └── midpointcircle │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MidPointCircleExplained 2 | This is a project that helps to understand how "Mid Point Circle" algorithm works and how it is modified for LondonEyeLayoutManager project 3 | 4 | Mid Point Circle Algorithm----------------|---- Mid Point Circle Algorithm modified fro LondonEyeLayoutManager project 5 | 6 | ![mid_point_circle](https://cloud.githubusercontent.com/assets/2686355/11886174/0ef8dff2-a530-11e5-8731-104608d7b8b9.gif) ![mid_point_circle_modified](https://cloud.githubusercontent.com/assets/2686355/11886193/4cb26f48-a530-11e5-8ef4-945f1462a1cc.gif) 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "21.1.2" 6 | 7 | defaultConfig { 8 | applicationId "com.volokh.danylo.midpointcircle" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.1.1' 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in d:\Projects\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/volokh/danylo/midpointcircle/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/volokh/danylo/midpointcircle/Diagram.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.util.AttributeSet; 8 | import android.util.Log; 9 | import android.view.View; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by danylo.volokh on 12/17/2015. 16 | */ 17 | public class Diagram extends View { 18 | 19 | private static final String TAG = Diagram.class.getSimpleName(); 20 | private List mPoints = new ArrayList(); 21 | 22 | private int mX0; 23 | private int mY0; 24 | 25 | private final Paint mPaintGreen = new Paint(); 26 | private final Paint mPaintRed = new Paint(); 27 | private final Paint mPaintBlue = new Paint(); 28 | private final Paint mPaintBlack = new Paint(); 29 | private final Paint mPaintYellow = new Paint(); 30 | private final Paint mPaintMagenta = new Paint(); 31 | private final Paint mPaintCyan = new Paint(); 32 | private final Paint mPaintDarkGrey = new Paint(); 33 | 34 | private int mVisiblePointsCount; 35 | 36 | { 37 | mPaintGreen.setColor(Color.GREEN); 38 | mPaintGreen.setStrokeWidth(2); 39 | 40 | mPaintRed.setColor(Color.RED); 41 | mPaintRed.setStrokeWidth(2); 42 | 43 | mPaintBlue.setColor(Color.BLUE); 44 | mPaintBlue.setStrokeWidth(2); 45 | 46 | mPaintBlack.setColor(Color.BLACK); 47 | mPaintBlack.setStrokeWidth(2); 48 | 49 | mPaintYellow.setColor(Color.YELLOW); 50 | mPaintYellow.setStrokeWidth(2); 51 | 52 | mPaintMagenta.setColor(Color.MAGENTA); 53 | mPaintMagenta.setStrokeWidth(2); 54 | 55 | mPaintCyan.setColor(Color.CYAN); 56 | mPaintCyan.setStrokeWidth(2); 57 | 58 | mPaintDarkGrey.setColor(Color.DKGRAY); 59 | mPaintDarkGrey.setStrokeWidth(2); 60 | } 61 | 62 | public void addPoint(Point point){ 63 | mPoints.add(point); 64 | } 65 | 66 | public void setOrigin(int x0, int y0){ 67 | mX0 = x0; 68 | mY0 = y0; 69 | } 70 | 71 | public Diagram(Context context) { 72 | super(context); 73 | } 74 | 75 | public Diagram(Context context, AttributeSet attrs) { 76 | super(context, attrs); 77 | } 78 | 79 | public Diagram(Context context, AttributeSet attrs, int defStyleAttr) { 80 | super(context, attrs, defStyleAttr); 81 | } 82 | 83 | @Override 84 | protected void onDraw(Canvas canvas) { 85 | super.onDraw(canvas); 86 | 87 | int index = 0; 88 | for(; index < mVisiblePointsCount; index++) { 89 | canvas.drawLine(mX0, mY0, mPoints.get(index).x, mPoints.get(index).y, mPoints.get(index).getPaint()); 90 | } 91 | } 92 | 93 | public void setVisiblePercent(int value) { 94 | mVisiblePointsCount = value; 95 | Log.v(TAG, "setVisiblePercent mVisiblePointsCount " + mVisiblePointsCount); 96 | } 97 | 98 | public int getPointsCount() { 99 | Log.v(TAG, "getPointsCount " + mPoints.size()); 100 | return mPoints.size(); 101 | } 102 | 103 | public List getPointsList() { 104 | return mPoints; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/com/volokh/danylo/midpointcircle/MidPointCircleActivity.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.graphics.Color; 5 | import android.graphics.Paint; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.util.Log; 9 | import android.view.View; 10 | import android.widget.Button; 11 | 12 | import com.volokh.danylo.midpointcircle.circle_points_creator.CirclePointsCreator; 13 | import com.volokh.danylo.midpointcircle.circle_points_creator.FirstQuadrantCirclePointsCreator; 14 | 15 | public class MidPointCircleActivity extends AppCompatActivity implements View.OnClickListener { 16 | 17 | private static final String TAG = MidPointCircleActivity.class.getSimpleName(); 18 | private static final int ANIMATION_DURATION = 5000; 19 | 20 | // paints to draw octants on the circle 21 | private final Paint mPaintGreen = new Paint(); 22 | private final Paint mPaintRed = new Paint(); 23 | private final Paint mPaintBlue = new Paint(); 24 | private final Paint mPaintBlack = new Paint(); 25 | private final Paint mPaintYellow = new Paint(); 26 | private final Paint mPaintMagenta = new Paint(); 27 | private final Paint mPaintCyan = new Paint(); 28 | private final Paint mPaintDarkGrey = new Paint(); 29 | 30 | { 31 | mPaintGreen.setColor(Color.GREEN); 32 | mPaintGreen.setStrokeWidth(2); 33 | 34 | mPaintRed.setColor(Color.RED); 35 | mPaintRed.setStrokeWidth(2); 36 | 37 | mPaintBlue.setColor(Color.BLUE); 38 | mPaintBlue.setStrokeWidth(2); 39 | 40 | mPaintBlack.setColor(Color.BLACK); 41 | mPaintBlack.setStrokeWidth(2); 42 | 43 | mPaintYellow.setColor(Color.YELLOW); 44 | mPaintYellow.setStrokeWidth(2); 45 | 46 | mPaintMagenta.setColor(Color.MAGENTA); 47 | mPaintMagenta.setStrokeWidth(2); 48 | 49 | mPaintCyan.setColor(Color.CYAN); 50 | mPaintCyan.setStrokeWidth(2); 51 | 52 | mPaintDarkGrey.setColor(Color.DKGRAY); 53 | mPaintDarkGrey.setStrokeWidth(2); 54 | } 55 | 56 | private int radius; 57 | 58 | private int mX0; 59 | private int mY0; 60 | 61 | private Diagram mMidPointCircleDiagram; 62 | private Diagram mMidPointCircleModifiedDiagram; 63 | 64 | private Button mReloadMidPointCircleButton; 65 | private Button mReloadMidPointCircleModifiedButton; 66 | 67 | private CirclePointsCreator mCirclePointsCreator; 68 | 69 | @Override 70 | protected void onCreate(Bundle savedInstanceState) { 71 | super.onCreate(savedInstanceState); 72 | 73 | setContentView(R.layout.activity_main); 74 | mMidPointCircleDiagram = (Diagram) findViewById(R.id.mid_point_circle); 75 | mMidPointCircleModifiedDiagram = (Diagram) findViewById(R.id.mid_point_circle_modified); 76 | mReloadMidPointCircleButton = (Button) findViewById(R.id.reload_mid_point_circle); 77 | mReloadMidPointCircleModifiedButton = (Button) findViewById(R.id.reload_mid_point_circle_modified); 78 | 79 | mReloadMidPointCircleButton.setOnClickListener(this); 80 | mReloadMidPointCircleModifiedButton.setOnClickListener(this); 81 | 82 | int screenWidth = getResources().getDisplayMetrics().widthPixels; 83 | int screenHeight = getResources().getDisplayMetrics().heightPixels; 84 | 85 | radius = screenWidth / 2; 86 | 87 | mX0 = screenWidth/2; 88 | mY0 = screenHeight/2; 89 | 90 | mMidPointCircleDiagram.setOrigin(mX0, mY0); 91 | mMidPointCircleModifiedDiagram.setOrigin(mX0, mY0); 92 | 93 | 94 | // create points for "mid point circle" 95 | createCirclePoints(mX0, mY0, radius); 96 | 97 | // create points for "mid point circle" modified 98 | mCirclePointsCreator = new FirstQuadrantCirclePointsCreator(radius, mX0, mY0); 99 | mCirclePointsCreator.fillCirclePoints(mMidPointCircleModifiedDiagram.getPointsList()); 100 | 101 | startAnimation(mMidPointCircleDiagram, mMidPointCircleModifiedDiagram.getPointsCount()); 102 | } 103 | 104 | private void startAnimation(final Diagram diagram, int pointsCount) { 105 | 106 | ValueAnimator valueAnimator = ValueAnimator.ofInt(0, pointsCount); 107 | valueAnimator.setDuration(ANIMATION_DURATION); 108 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 109 | @Override 110 | public void onAnimationUpdate(ValueAnimator animation) { 111 | int value = (int) animation.getAnimatedValue(); 112 | Log.v(TAG, "onAnimationUpdate value " + value); 113 | 114 | diagram.setVisiblePercent(value); 115 | diagram.invalidate(); 116 | } 117 | }); 118 | valueAnimator.start(); 119 | } 120 | 121 | void createCirclePoints(int x0, int y0, int radius) 122 | { 123 | int x = radius; 124 | int y = 0; 125 | int decisionOver2 = 1 - x; // Decision criterion divided by 2 evaluated at x=r, y=0 126 | 127 | while( y <= x ) 128 | { 129 | mMidPointCircleDiagram.addPoint(new Point(x + x0, y + y0, mPaintBlack)); // Octant 1 130 | 131 | mMidPointCircleDiagram.addPoint(new Point(y + x0, x + y0, mPaintBlue));// Octant 2 132 | 133 | mMidPointCircleDiagram.addPoint(new Point(-x + x0, y + y0, mPaintCyan));// Octant 4 134 | 135 | mMidPointCircleDiagram.addPoint(new Point(-y + x0, x + y0, mPaintDarkGrey));// Octant 3 136 | 137 | mMidPointCircleDiagram.addPoint(new Point(-x + x0, -y + y0, mPaintGreen));// Octant 5 138 | 139 | mMidPointCircleDiagram.addPoint(new Point(-y + x0, -x + y0, mPaintMagenta));// Octant 6 140 | 141 | mMidPointCircleDiagram.addPoint(new Point(x + x0, -y + y0, mPaintRed));// Octant 8 142 | 143 | mMidPointCircleDiagram.addPoint(new Point(y + x0, -x + y0, mPaintYellow));// Octant 7 144 | 145 | y++; 146 | if (decisionOver2<=0) 147 | { 148 | decisionOver2 += 2 * y + 1; // Change in decision criterion for y -> y+1 149 | } 150 | else 151 | { 152 | x--; 153 | decisionOver2 += 2 * (y - x) + 1; // Change for y -> y+1, x -> x-1 154 | } 155 | } 156 | } 157 | 158 | @Override 159 | public void onClick(View v) { 160 | switch (v.getId()){ 161 | case R.id.reload_mid_point_circle: 162 | 163 | mMidPointCircleModifiedDiagram.setVisibility(View.GONE); 164 | mMidPointCircleDiagram.setVisibility(View.VISIBLE); 165 | 166 | startAnimation(mMidPointCircleDiagram, mMidPointCircleDiagram.getPointsCount()); 167 | break; 168 | case R.id.reload_mid_point_circle_modified: 169 | 170 | mMidPointCircleModifiedDiagram.setVisibility(View.VISIBLE); 171 | mMidPointCircleDiagram.setVisibility(View.GONE); 172 | 173 | startAnimation(mMidPointCircleModifiedDiagram, mMidPointCircleModifiedDiagram.getPointsCount()); 174 | break; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /app/src/main/java/com/volokh/danylo/midpointcircle/Point.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle; 2 | 3 | 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * Created by danylo.volokh on 12/18/2015. 8 | */ 9 | public class Point extends android.graphics.Point{ 10 | 11 | private final Paint paint; 12 | 13 | public Point(int x, int y, Paint paint){ 14 | super(x, y); 15 | this.paint = paint; 16 | } 17 | 18 | public Paint getPaint(){ 19 | return paint; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/volokh/danylo/midpointcircle/circle_points_creator/CirclePointsCreator.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle.circle_points_creator; 2 | 3 | 4 | import android.graphics.Paint; 5 | 6 | import com.volokh.danylo.midpointcircle.Point; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by danylo.volokh on 12/4/2015. 12 | * 13 | * Implementation should be quadrant-specific and it should "know" in which order point should be created. 14 | * For example : if we starting to layout views from top to bottom in first quadrant then first point should be (R;0) 15 | * "R" - radius 16 | */ 17 | public interface CirclePointsCreator { 18 | 19 | void fillCirclePoints( 20 | List circlePoints); 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/volokh/danylo/midpointcircle/circle_points_creator/FirstQuadrantCirclePointsCreator.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle.circle_points_creator; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.Paint; 5 | import android.util.Log; 6 | 7 | import com.volokh.danylo.midpointcircle.Point; 8 | import com.volokh.danylo.midpointcircle.mirror_helper.CircleMirrorHelper; 9 | import com.volokh.danylo.midpointcircle.mirror_helper.FirstQuadrantCircleMirrorHelper; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * Created by danylo.volokh on 11/22/2015. 16 | * This class is a "Template method". It "knows" in which order point should be created. 17 | * 18 | * This class also uses CircleMirrorHelper to mirror created point. 19 | * Exactly as required by "Mid point circle algorithm" but a bit modified. 20 | * 21 | * "Midpoint circle algorithm" creates all 8 octant in parallel. 22 | * We are using "Midpoint circle algorithm" to create 1st octant and the mirror other points consecutively: 23 | * 1 octant, 2 octant, 2 quadrant, 2 semicircle 24 | */ 25 | public class FirstQuadrantCirclePointsCreator implements CirclePointsCreator { 26 | 27 | private static final String TAG = FirstQuadrantCirclePointsCreator.class.getSimpleName(); 28 | 29 | private final int mRadius; 30 | private final int mX0; 31 | private final int mY0; 32 | 33 | private final CircleMirrorHelper mCircleMirrorHelper; 34 | 35 | private Paint mPaintFor1stOctant = new Paint(); 36 | { 37 | mPaintFor1stOctant.setColor(Color.MAGENTA); 38 | mPaintFor1stOctant.setStrokeWidth(2); 39 | } 40 | 41 | public FirstQuadrantCirclePointsCreator(int radius, int x0, int y0){ 42 | mRadius = radius; 43 | mX0 = x0; 44 | mY0 = y0; 45 | mCircleMirrorHelper = new FirstQuadrantCircleMirrorHelper(x0, y0); 46 | } 47 | 48 | /** 49 | * This method is based on "Midpoint circle algorithm." 50 | * 51 | * We use three steps: 52 | * 53 | * 1. Create 1 octant of a circle. 54 | * 2. Mirror the created points for the 2nd octant 55 | * At this stage we have points for 1 quadrant of a circle 56 | * 57 | * 3. Mirror 2nd quadrant points using points from 1 quadrant 58 | * At this stage we have points for 1 semicircle 59 | * 60 | * 4. Mirror 2nd semicircle points using points from 1 semicircle 61 | * 62 | */ 63 | 64 | @Override 65 | public void fillCirclePoints( 66 | List circlePoints) 67 | { 68 | Log.v(TAG, ">> fillCirclePoints"); 69 | 70 | createFirstOctant(circlePoints, mPaintFor1stOctant); 71 | 72 | /** at this stage "circleIndexPoint" and "circlePointIndex" contains only the points from first octant*/ 73 | mCircleMirrorHelper.mirror_2nd_Octant( 74 | circlePoints); 75 | 76 | /** at this stage "circleIndexPoint" and "circlePointIndex" contains only the points from first quadrant*/ 77 | mCircleMirrorHelper.mirror_2nd_Quadrant( 78 | circlePoints 79 | ); 80 | 81 | /** at this stage "circleIndexPoint" and "circlePointIndex" contains only the points from first semicircle*/ 82 | mCircleMirrorHelper.mirror_2nd_Semicircle( 83 | circlePoints 84 | ); 85 | 86 | Log.v(TAG, "<< fillCirclePoints"); 87 | } 88 | 89 | /** 90 | * 91 | * This method is based on "Midpoint circle algorithm." 92 | * It creates a points that are situated in first octant. 93 | * 94 | * First point has an index of "0", next is "1" and so on. 95 | * First point is (radius;0) 96 | * ^ here is the last point of first octant. x == y 97 | * +y | | / 98 | * | | / 99 | * ______| V / 100 | * _-- | * 101 | * _/ | / \_ 1st Octant <-_ 102 | * _| | / |_ \ Points are created in this direction 103 | * | | / | | 104 | * | | / | | 105 | * ---------------|---------- * ---> V 106 | * | | ^ 107 | * |_ | | 108 | * |_ | | 109 | * \ | here is the first point of first octant (radius;0) 110 | * --______| 111 | * | 112 | * 113 | * 114 | * In our reverse coordinates system it's a bit different 115 | * 116 | * -y | 117 | * | 118 | * ______| 119 | * _-- | 120 | * _/ | 121 | * | | 122 | * | | 123 | * | | 124 | * ---------------|--------------------------------------> V 125 | * | | \ | * 126 | * | | \ | 1st Octant * 127 | * |_ | \ _| * 128 | * \_ | \_/ * 129 | * --_______| \ * 130 | * | \ * 131 | * | * 132 | * V * 133 | * +y * * 134 | * * Device's display * 135 | * * * 136 | * * * 137 | * * * 138 | * ************************* 139 | */ 140 | private void createFirstOctant( 141 | List circlePoints, 142 | Paint paint 143 | ) { 144 | 145 | int x = mRadius; 146 | int y = 0; 147 | int decisionOver2 = 1 - x; // Decision criterion divided by 2 evaluated at x=r, y=0 148 | while(y <= x){ 149 | 150 | createPoint(x + mX0, y + mY0, circlePoints, paint); 151 | 152 | y++; 153 | if (decisionOver2<=0){ 154 | decisionOver2 += 2 * y + 1; // Change in decision criterion for y -> y+1 155 | } else { 156 | x--; 157 | decisionOver2 += 2 * (y - x) + 1; // Change for y -> y+1, x -> x-1 158 | } 159 | } 160 | } 161 | 162 | private void createPoint( 163 | int x, 164 | int y, 165 | List circlePoints, 166 | Paint paint) { 167 | 168 | Point point = new Point(x, y, paint); 169 | 170 | circlePoints.add(point); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /app/src/main/java/com/volokh/danylo/midpointcircle/mirror_helper/CircleMirrorHelper.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle.mirror_helper; 2 | 3 | import com.volokh.danylo.midpointcircle.Point; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by danylo.volokh on 12/4/2015. 9 | * 10 | * This is generic interface for mirroring points related functionality. 11 | * 12 | * For layouting in each quadrant you should implement quadrant-specific classes. 13 | * For example: 14 | * {@link FirstQuadrantCircleMirrorHelper} 15 | * TODO: create other three 16 | */ 17 | public interface CircleMirrorHelper { 18 | 19 | /** 20 | * This method implementation should mirror second octant using input of already created points. 21 | * They might be any of 7 octant that left. Specific implementation should get correct input octant and mirror 2nd octant from it 22 | * Here the order of input and order of output does matter. 23 | */ 24 | void mirror_2nd_Octant( 25 | List circlePoints 26 | ); 27 | 28 | /** 29 | * This method implementation should mirror second quadrant using input of already created points. 30 | * They might be any of 3 quadrant that left. Specific implementation should get correct input quadrant and mirror 2nd quadrant from it 31 | * Here the order of input and order of output does matter. 32 | */ 33 | void mirror_2nd_Quadrant( 34 | List circlePoints 35 | ); 36 | 37 | /** 38 | * This method implementation should mirror second semicircle using input of already created points. 39 | * It should be other semicircle. Specific implementation should get correct input semicircle and mirror 2nd semicircle from it. 40 | * Here the order of input and order of output does matter. 41 | */ 42 | void mirror_2nd_Semicircle( 43 | List circlePoints 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/volokh/danylo/midpointcircle/mirror_helper/FirstQuadrantCircleMirrorHelper.java: -------------------------------------------------------------------------------- 1 | package com.volokh.danylo.midpointcircle.mirror_helper; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.Paint; 5 | import android.util.Log; 6 | 7 | import com.volokh.danylo.midpointcircle.Point; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * This class is a helper for FirstQuadrantCirclePointsCreator 13 | * 14 | * It can create a full circle points using points from 1st octant. It is mirroring existing points to the points in other circle sectors. 15 | * 16 | */ 17 | public class FirstQuadrantCircleMirrorHelper implements CircleMirrorHelper { 18 | 19 | private static final boolean SHOW_LOGS = false; 20 | private static final String TAG = FirstQuadrantCircleMirrorHelper.class.getSimpleName(); 21 | 22 | private final int mX0; 23 | private final int mY0; 24 | 25 | private final Paint _2_octant_paint = new Paint(); 26 | private final Paint _2_quadrant_paint = new Paint(); 27 | private final Paint _2_semicircle_paint = new Paint(); 28 | 29 | { 30 | _2_octant_paint.setColor(Color.RED); 31 | _2_octant_paint.setStrokeWidth(2); 32 | 33 | _2_quadrant_paint.setColor(Color.BLACK); 34 | _2_quadrant_paint.setStrokeWidth(2); 35 | 36 | _2_semicircle_paint.setColor(Color.BLUE); 37 | _2_semicircle_paint.setStrokeWidth(2); 38 | } 39 | 40 | 41 | public FirstQuadrantCircleMirrorHelper(int x0, int y0){ 42 | mX0 = x0; 43 | mY0 = y0; 44 | } 45 | 46 | enum Action{ 47 | MIRROR_2ND_OCTANT, 48 | MIRROR_2ND_QUADRANT, 49 | MIRROR_2ND_SEMICIRCLE 50 | } 51 | 52 | /** 53 | * This method takes the points from 1st octant and mirror them to the 2nd octant 54 | * 55 | * ^ / 56 | * +y | 2nd octant / 57 | * | / 58 | * |_____ / 59 | * | --_ / 60 | * | / *_ <-- this s the point from where we start 61 | * | / | \ 62 | * | / | 1st Octant | We are going through points in this direction 63 | * | / | V 64 | * ---------------|---------------> V 65 | * | | 66 | * | | 67 | * |_ | 68 | * \_ | 69 | * --_______| 70 | * | 71 | * | 72 | */ 73 | @Override 74 | public void mirror_2nd_Octant( 75 | List circlePoints 76 | ) { 77 | 78 | int countOfPointsIn_1st_octant = circlePoints.size(); 79 | if(SHOW_LOGS) Log.v(TAG, "mirror_2nd_Octant, countOfPointsIn_1st_octant " + countOfPointsIn_1st_octant); 80 | 81 | for(int pointIndex = countOfPointsIn_1st_octant - 1; 82 | pointIndex >= 0; 83 | pointIndex-- ){ 84 | 85 | createMirroredPoint(Action.MIRROR_2ND_OCTANT, pointIndex, circlePoints); 86 | } 87 | } 88 | 89 | @Override 90 | public void mirror_2nd_Quadrant( 91 | List circlePoints 92 | ) { 93 | 94 | int countOfPointsIn_1st_quadrant = circlePoints.size(); 95 | if(SHOW_LOGS) Log.v(TAG, "mirror_2nd_Quadrant, countOfPointsIn_1st_quadrant " + countOfPointsIn_1st_quadrant); 96 | 97 | for(int pointIndex = countOfPointsIn_1st_quadrant 98 | - 1 // last point 99 | - 1 // previous to the last because last point is already in the list (x0, radius + y0). It is a point on Y axis 100 | ; 101 | pointIndex >= 0; 102 | pointIndex-- ){ 103 | 104 | createMirroredPoint(Action.MIRROR_2ND_QUADRANT, pointIndex, circlePoints); 105 | } 106 | } 107 | 108 | @Override 109 | public void mirror_2nd_Semicircle( 110 | List circlePoints 111 | ) { 112 | 113 | int countOfPointsIn_1st_semicircle = circlePoints.size(); 114 | if(SHOW_LOGS) Log.v(TAG, "mirror_2nd_Semicircle, countOfPointsIn_1st_semicircle " + countOfPointsIn_1st_semicircle); 115 | 116 | for(int pointIndex = countOfPointsIn_1st_semicircle - 2; // don't count (-radius, 0) because it already in the list 117 | pointIndex > 0; // don't count (radius, 0) because it already in the list 118 | pointIndex-- ){ 119 | 120 | createMirroredPoint(Action.MIRROR_2ND_SEMICIRCLE, pointIndex, circlePoints); 121 | 122 | } 123 | 124 | } 125 | 126 | private void createMirroredPoint( 127 | Action action, 128 | int pointIndex, 129 | List circlePoints 130 | ) { 131 | 132 | Point pointAtIndex = circlePoints.get(pointIndex); 133 | 134 | Point mirroredPoint; 135 | switch (action) { 136 | case MIRROR_2ND_OCTANT: 137 | mirroredPoint = mirror_2nd_OctantPoint(pointAtIndex, _2_octant_paint); 138 | break; 139 | case MIRROR_2ND_QUADRANT: 140 | mirroredPoint = mirror_2nd_QuadrantPoint(pointAtIndex, _2_quadrant_paint); 141 | break; 142 | case MIRROR_2ND_SEMICIRCLE: 143 | mirroredPoint = mirror_2nd_SemicirclePoint(pointAtIndex, _2_semicircle_paint); 144 | break; 145 | default: 146 | throw new RuntimeException("Not handled action " + action); 147 | } 148 | 149 | if (mirroredPoint != null) { 150 | circlePoints.add(mirroredPoint); 151 | } else { 152 | if (SHOW_LOGS) 153 | Log.i(TAG, "createMirroredPoint, found a point that should not be mirrored, pointAtIndex " + pointAtIndex + ", action " + action); 154 | if (SHOW_LOGS) 155 | Log.i(TAG, "createMirroredPoint, this point is already created. Skip it"); 156 | 157 | } 158 | } 159 | 160 | /** 161 | * This method takes a single point from 1st octant and mirror it to the 2nd octant 162 | * 163 | * ^ 164 | * +y | 2nd octant 165 | * | / 166 | * | / 167 | * | / 168 | * | / 169 | * | / 170 | * | * (x1*, y1*) / 171 | * | * (x2*, y2*) 172 | * | / 173 | * | / * (x2, y2) 174 | * | / 175 | * | / * (x1, y1) 176 | * | / 177 | * | / 178 | * | / 1st octant 179 | * |-------------------------------------> 180 | * 181 | * How to get a mirrored point (x1*, y1*) when we have it's mirror (x1, y1)? 182 | * x1 = y1 183 | * y1 = x1 184 | ************ 185 | * How to get a mirrored point (x2*, y2*) when we have it's mirror (x2, y2)? 186 | * x2* = y2 187 | * y2* = x2 188 | * 189 | * Here is the explanation of the implementation. 190 | * This is how 1st and 2nd octant is drawn in "Mid point circle" algorithm 191 | * 192 | * DrawPixel( x + x0, y + y0); // Octant 1 193 | * DrawPixel( y + x0, x + y0); // Octant 2 194 | * 195 | * To mirror second point using "firstOctantPoint" we have to know original x and y; 196 | * 197 | * Get original x, y from "firstOctantPoint": 198 | * firstOctant_X = x + x0; -> x = firstOctant_X - x0; 199 | * firstOctant_Y = y + y0; -> y = firstOctant_Y - y0; 200 | * 201 | * Get "secondOctantPoint" from original x, y 202 | * secondOctant_X = y + x0; -> firstOctant_Y - y0 + x0; 203 | * secondOctant_Y = x + y0; -> firstOctant_X - x0 + y0; 204 | */ 205 | private Point mirror_2nd_OctantPoint(Point firstOctantPoint, Paint paint) { 206 | int correctedX = firstOctantPoint.x - mX0; 207 | int correctedY = firstOctantPoint.y - mY0; 208 | return correctedX != correctedY ? new Point(correctedY + mX0, correctedX + mY0, paint) 209 | : null; // null means that the mirror of this point is going to be the same. (24; 24) -> mirrored (24:24) 210 | } 211 | 212 | /** 213 | * This method takes a single point from 1st octant and mirror it to the 2nd octant 214 | * 215 | * ^ +y ^ 216 | * | 2nd Quadrant +y | 1st Quadrant 217 | * | | 218 | * | | 219 | * | (x3*; y3*) *<--|---* (x3; y3) 220 | * | (x2*; y2*) *<---------|----------*(x2; y2) 221 | * | | 222 | * | | 223 | * | | 224 | * | | 225 | * | | 226 | * | | 227 | * | | 228 | * | (x1*; y1*) *<--------------|---------------* (x1; y1) 229 | * | | 230 | * | | +x 231 | *--|-----------------------------------|-------------------------------------> 232 | * 233 | * How to get a mirrored point (x1*, y1*) when we have it's mirror (x1, y1)? 234 | * x1* = x0 - (x1 - x0) = 2*x0 - x1 235 | * y1* = y1 236 | ************ 237 | * How to get a mirrored point (x2*, y2*) when we have it's mirror (x2, y2)? 238 | * x2* = x0 - (x2 - x0) = 2*x0 - x2 239 | * y2* = y2 240 | ************ 241 | * How to get a mirrored point (x3*, y3*) when we have it's mirror (x3, y3)? 242 | * x3* = x0 - (x3 - x0) = 2*x0 - x3 243 | * y3* = y3 244 | */ 245 | private Point mirror_2nd_QuadrantPoint(Point secondQuadrantPoint, Paint paint) { 246 | return new Point(-secondQuadrantPoint.x + 2 * mX0, secondQuadrantPoint.y, paint); 247 | } 248 | 249 | /** 250 | * This method takes a single point from 1st octant and mirror it to the 2nd octant 251 | * 252 | * ^ 253 | * 2nd Quadrant +y | 1st Quadrant 254 | * | 255 | * | 256 | * (x3; y3) * | 257 | * (x4; y4) * | | 258 | * | | | 259 | * | | | 260 | * | | | 261 | * | | | 262 | * | | | * (x2; y2) 263 | * | | | | 264 | * | | | | 265 | * | | | | * (x1; y1) 266 | * | | | | | 267 | * | | | | | +x 268 | *-------------------------------------|-------------------------------------> 269 | * | | | | | 270 | * | | | | V 271 | * | | | | * (x1*; y1*) 272 | * | | | | 273 | * | | | V 274 | * | | | * (x2*; y2*) 275 | * | | | 276 | * | | | 277 | * | | | 278 | * V | | 279 | * (x4; y4) * V | 280 | * (x3*; y3*) * | 281 | * | 282 | * | 283 | * How to get a mirrored point (x1*, y1*) when we have it's mirror (x1, y1)? 284 | * x1* = x1 285 | * y1* = y0 - (y1 - y0) = 2 * y0 - y1 286 | ************ 287 | * How to get a mirrored point (x2*, y2*) when we have it's mirror (x2, y2)? 288 | * x2* = x2 289 | * y2* = y0 - (y2 - y0) = 2 * y0 - y2 290 | ************ 291 | * How to get a mirrored point (x3*, y3*) when we have it's mirror (x3, y3)? 292 | * x3* = x3 293 | * y3* = y0 - (y3 - y0) = 2 * y0 - y3 294 | ************ 295 | * How to get a mirrored point (x4*, y4*) when we have it's mirror (x4, y4)? 296 | * x4* = x4 297 | * y4* = y0 - (y3 - y0) = 2 * y0 - y3 298 | */ 299 | private Point mirror_2nd_SemicirclePoint(Point firstSemicirclePoint, Paint paint) { 300 | /** TODO: use the same logic as {@link #mirror_2nd_OctantPoint}*/ 301 | return new Point(firstSemicirclePoint.x, -firstSemicirclePoint.y + 2 * mY0, paint); 302 | } 303 | 304 | } 305 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 18 | 19 |