Can an Iphone Draw a Perfect Circle

Sometimes it is actually useful to spend some fourth dimension reinventing the wheel. As yous might take already noticed there are a lot of frameworks, just it is not that hard to implement a unproblematic, merely yet useful solution without introducing all that complexity. (Please don't go me wrong, for any serious purpose it is better to use some mature and proven to be stable framework).

I will present my results first and and so explicate the unproblematic and straightforward thought behind them.

enter image description here

You'll run into in my implementation there is no need to analyze every single point and practise complex computations. The thought is to spot some valuable meta information. I will use tangent every bit an example:

enter image description here

Let'south identify a unproblematic and straightforward pattern, typical for the selected shape:

enter image description here

And so it is not that hard to implement a circle detection mechanism based on that idea. Meet working demo below (Pitiful, I'm using Java as the fastest manner to provide this fast and a chip dirty example):

          import java.awt.BasicStroke; import java.awt.Colour; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.HeadlessException; import java.awt.Betoken; import coffee.awt.RenderingHints; import java.awt.issue.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.SwingUtilities;  public grade CircleGestureDemo extends JFrame implements MouseListener, MouseMotionListener {      enum Blazon {         RIGHT_DOWN,         LEFT_DOWN,         LEFT_UP,         RIGHT_UP,         UNDEFINED     }      private static final Type[] circleShape = {         Type.RIGHT_DOWN,         Type.LEFT_DOWN,         Type.LEFT_UP,         Type.RIGHT_UP};      individual boolean editing = false;     private Signal[] premises;     private Point last = new Point(0, 0);     private Listing<Point> points = new ArrayList<>();      public CircleGestureDemo() throws HeadlessException {         super("Discover Circle");          addMouseListener(this);         addMouseMotionListener(this);         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);          setPreferredSize(new Dimension(800, 600));         pack();     }      @Override     public void paint(Graphics graphics) {         Dimension d = getSize();         Graphics2D g = (Graphics2D) graphics;          super.paint(g);          RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);         qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);         chiliad.setRenderingHints(qualityHints);          g.setColor(Colour.Ruby-red);         if (cD == 0) {             Point b = nil;             for (Point e : points) {                 if (naught != b) {                     m.drawLine(b.x, b.y, east.x, e.y);                 }                 b = eastward;             }         }else if (cD > 0){             g.setColor(Colour.Blue);             g.setStroke(new BasicStroke(3));             g.drawOval(cX, cY, cD, cD);         }else{             g.drawString("Uknown",thirty,fifty);         }     }       private Blazon getType(int dx, int dy) {         Type result = Blazon.UNDEFINED;          if (dx > 0 && dy < 0) {             result = Type.RIGHT_DOWN;         } else if (dx < 0 && dy < 0) {             result = Blazon.LEFT_DOWN;         } else if (dx < 0 && dy > 0) {             result = Type.LEFT_UP;         } else if (dx > 0 && dy > 0) {             result = Type.RIGHT_UP;         }          render event;     }      individual boolean isCircle(List<Point> points) {         boolean event = false;         Blazon[] shape = circleShape;         Type[] detected = new Blazon[shape.length];         bounds = new Indicate[shape.length];          concluding int STEP = v;          int index = 0;                 Point current = points.go(0);         Type type = null;          for (int i = Footstep; i < points.size(); i += Step) {             Signal next = points.get(i);             int dx = next.x - electric current.x;             int dy = -(next.y - electric current.y);              if(dx == 0 || dy == 0) {                 continue;             }              Blazon newType = getType(dx, dy);             if(blazon == null || blazon != newType) {                 if(newType != shape[index]) {                     break;                 }                 premises[alphabetize] = current;                 detected[index++] = newType;             }             type = newType;                         current = adjacent;              if (index >= shape.length) {                 result = true;                 suspension;             }         }          render result;     }      @Override     public void mousePressed(MouseEvent e) {         cD = 0;         points.clear();         editing = true;     }      private int cX;     individual int cY;     private int cD;      @Override     public void mouseReleased(MouseEvent e) {         editing = imitation;         if(points.size() > 0) {             if(isCircle(points)) {                 cX = bounds[0].ten + Math.abs((bounds[ii].x - bounds[0].x)/2);                 cY = premises[0].y;                 cD = bounds[ii].y - premises[0].y;                 cX = cX - cD/two;                  System.out.println("circumvolve");             }else{                 cD = -i;                 System.out.println("unknown");             }             repaint();         }     }      @Override     public void mouseDragged(MouseEvent e) {         Point newPoint = e.getPoint();         if (editing && !terminal.equals(newPoint)) {             points.add together(newPoint);             last = newPoint;             repaint();         }     }      @Override     public void mouseMoved(MouseEvent e) {     }      @Override     public void mouseEntered(MouseEvent e) {     }      @Override     public void mouseExited(MouseEvent e) {     }      @Override     public void mouseClicked(MouseEvent e) {     }      public static void main(String[] args) {         SwingUtilities.invokeLater(new Runnable() {              @Override             public void run() {                 CircleGestureDemo t = new CircleGestureDemo();                 t.setVisible(truthful);             }         });     } }                  

It should non exist a problem to implement similar beliefs on iOS, since you just need several events and coordinates. Something like the following (meet example):

          - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {     UITouch* affect = [[event allTouches] anyObject]; }  - (void)handleTouch:(UIEvent *)result {     UITouch* touch = [[outcome allTouches] anyObject];     CGPoint location = [bear on locationInView:self];  }  - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)issue {     [cocky handleTouch: event]; }  - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)issue {     [self handleTouch: event];     }                  

There are several enhancements possible.

Start at whatsoever point

Current requirement is to kickoff drawing a circle from the top middle point due to the following simplification:

                      if(blazon == null || type != newType) {             if(newType != shape[alphabetize]) {                 pause;             }             premises[index] = current;             detected[index++] = newType;         }                  

Please notice the default value of index is used. A uncomplicated search through the bachelor "parts" of the shape will remove that limitation. Please annotation you'll need to use a circular buffer in order to detect a full shape:

enter image description here

Clockwise and counterclockwise

In guild to support both modes you will need to utilize the circular buffer from the previous enhancement and search in both directions:

enter image description here

Describe an ellipse

Y'all have everything you lot demand already in the bounds assortment.

enter image description here

Simply use that data:

          cWidth = bounds[two].y - premises[0].y; cHeight = bounds[3].y - bounds[1].y;                  

Other gestures (optional)

Finally, you only need to properly handle a state of affairs when dx (or dy) is equal to nothing in society to support other gestures:

enter image description here

Update

This small PoC got quite a loftier attention, and so I did update the code a scrap in order to go far work smoothly and provide some cartoon hints, highlight supporting points, etc:

enter image description here

Hither is the code:

          import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import coffee.awt.Dimension; import coffee.awt.Graphics; import java.awt.Graphics2D; import java.awt.HeadlessException; import java.awt.Indicate; import java.awt.RenderingHints; import java.awt.outcome.MouseEvent; import java.awt.event.MouseListener; import java.awt.outcome.MouseMotionListener; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities;  public class CircleGestureDemo extends JFrame {      enum Blazon {          RIGHT_DOWN,         LEFT_DOWN,         LEFT_UP,         RIGHT_UP,         UNDEFINED     }      private static concluding Type[] circleShape = {         Type.RIGHT_DOWN,         Type.LEFT_DOWN,         Type.LEFT_UP,         Type.RIGHT_UP};      public CircleGestureDemo() throws HeadlessException {         super("Circle gesture");         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);         setLayout(new BorderLayout());         add(BorderLayout.CENTER, new GesturePanel());         setPreferredSize(new Dimension(800, 600));         pack();     }      public static class GesturePanel extends JPanel implements MouseListener, MouseMotionListener {          private boolean editing = false;         private Betoken[] bounds;         individual Indicate last = new Betoken(0, 0);         private final Listing<Indicate> points = new ArrayList<>();          public GesturePanel() {             super(true);             addMouseListener(this);             addMouseMotionListener(this);         }          @Override         public void pigment(Graphics graphics) {             super.paint(graphics);              Dimension d = getSize();             Graphics2D g = (Graphics2D) graphics;              RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,                     RenderingHints.VALUE_ANTIALIAS_ON);             qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);              m.setRenderingHints(qualityHints);              if (!points.isEmpty() && cD == 0) {                 isCircle(points, g);                 1000.setColor(HINT_COLOR);                 if (bounds[2] != zippo) {                     int r = (bounds[2].y - bounds[0].y) / 2;                     k.setStroke(new BasicStroke(r / 3 + 1));                     1000.drawOval(bounds[0].x - r, bounds[0].y, two * r, 2 * r);                 } else if (bounds[i] != null) {                     int r = bounds[ane].10 - bounds[0].x;                     g.setStroke(new BasicStroke(r / iii + 1));                     g.drawOval(bounds[0].x - r, bounds[0].y, 2 * r, 2 * r);                 }             }              g.setStroke(new BasicStroke(two));             m.setColor(Color.RED);              if (cD == 0) {                 Bespeak b = cypher;                 for (Point due east : points) {                     if (null != b) {                         one thousand.drawLine(b.x, b.y, east.x, east.y);                     }                     b = eastward;                 }              } else if (cD > 0) {                 m.setColor(Color.Bluish);                 m.setStroke(new BasicStroke(3));                 1000.drawOval(cX, cY, cD, cD);             } else {                 g.drawString("Uknown", 30, 50);             }         }          private Blazon getType(int dx, int dy) {             Type outcome = Type.UNDEFINED;              if (dx > 0 && dy < 0) {                 effect = Type.RIGHT_DOWN;             } else if (dx < 0 && dy < 0) {                 result = Type.LEFT_DOWN;             } else if (dx < 0 && dy > 0) {                 result = Type.LEFT_UP;             } else if (dx > 0 && dy > 0) {                 result = Type.RIGHT_UP;             }              return effect;         }          private boolean isCircle(Listing<Point> points, Graphics2D g) {             boolean result = imitation;             Blazon[] shape = circleShape;             bounds = new Point[shape.length];              concluding int STEP = 5;             int index = 0;             int initial = 0;             Indicate electric current = points.get(0);             Type type = cipher;              for (int i = Stride; i < points.size(); i += Pace) {                 last Signal next = points.get(i);                 concluding int dx = next.x - electric current.x;                 final int dy = -(next.y - current.y);                  if (dx == 0 || dy == 0) {                     continue;                 }                  final int marker = 8;                 if (null != grand) {                     g.setColor(Colour.BLACK);                     chiliad.setStroke(new BasicStroke(2));                     yard.drawOval(electric current.x - marker/two,                                 current.y - marker/two,                                 marker, marker);                 }                  Type newType = getType(dx, dy);                 if (type == naught || type != newType) {                     if (newType != shape[index]) {                         break;                     }                     bounds[index++] = electric current;                 }                  type = newType;                 current = next;                 initial = i;                  if (alphabetize >= shape.length) {                     result = true;                     break;                 }             }             return result;         }          @Override         public void mousePressed(MouseEvent e) {             cD = 0;             points.articulate();             editing = true;         }          individual int cX;         individual int cY;         private int cD;          @Override         public void mouseReleased(MouseEvent e) {             editing = false;             if (points.size() > 0) {                 if (isCircle(points, null)) {                     int r = Math.abs((bounds[2].y - bounds[0].y) / 2);                     cX = bounds[0].x - r;                     cY = bounds[0].y;                     cD = two * r;                 } else {                     cD = -1;                 }                 repaint();             }         }          @Override         public void mouseDragged(MouseEvent e) {             Point newPoint = e.getPoint();             if (editing && !last.equals(newPoint)) {                 points.add(newPoint);                 last = newPoint;                 repaint();             }         }          @Override         public void mouseMoved(MouseEvent e) {         }          @Override         public void mouseEntered(MouseEvent due east) {         }          @Override         public void mouseExited(MouseEvent east) {         }          @Override         public void mouseClicked(MouseEvent e) {         }     }      public static void main(String[] args) {         SwingUtilities.invokeLater(new Runnable() {              @Override             public void run() {                 CircleGestureDemo t = new CircleGestureDemo();                 t.setVisible(true);             }         });     }      last static Colour HINT_COLOR = new Color(0x55888888, true); }                  

liningertheming.blogspot.com

Source: https://stackoverflow.com/questions/18934805/draw-a-perfect-circle-from-users-touch

0 Response to "Can an Iphone Draw a Perfect Circle"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel