How to Draw a Good Circle

Sometimes information technology is really useful to spend some time reinventing the bicycle. As you might have already noticed there are a lot of frameworks, just it is not that hard to implement a unproblematic, but yet useful solution without introducing all that complexity. (Please don't get me wrong, for whatsoever serious purpose information technology is meliorate to use some mature and proven to be stable framework).

I will nowadays my results kickoff and then explain the uncomplicated and straightforward thought behind them.

enter image description here

You'll see in my implementation there is no need to clarify every single point and do complex computations. The idea is to spot some valuable meta information. I will employ tangent every bit an instance:

enter image description here

Allow's place a uncomplicated and straightforward design, typical for the selected shape:

enter image description here

So information technology is not that hard to implement a circle detection machinery based on that idea. See working demo below (Sorry, I'yard using Java as the fastest way to provide this fast and a fleck dirty example):

          import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import coffee.awt.Graphics; import java.awt.Graphics2D; import coffee.awt.HeadlessException; import coffee.awt.Indicate; import coffee.awt.RenderingHints; import coffee.awt.event.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 class CircleGestureDemo extends JFrame implements MouseListener, MouseMotionListener {      enum Type {         RIGHT_DOWN,         LEFT_DOWN,         LEFT_UP,         RIGHT_UP,         UNDEFINED     }      private static final Type[] circleShape = {         Type.RIGHT_DOWN,         Blazon.LEFT_DOWN,         Type.LEFT_UP,         Type.RIGHT_UP};      private boolean editing = imitation;     private Point[] bounds;     private Point final = new Point(0, 0);     private List<Bespeak> points = new ArrayList<>();      public CircleGestureDemo() throws HeadlessException {         super("Observe Circumvolve");          addMouseListener(this);         addMouseMotionListener(this);         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);          setPreferredSize(new Dimension(800, 600));         pack();     }      @Override     public void pigment(Graphics graphics) {         Dimension d = getSize();         Graphics2D chiliad = (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);         grand.setRenderingHints(qualityHints);          g.setColor(Color.RED);         if (cD == 0) {             Bespeak b = aught;             for (Point e : points) {                 if (null != b) {                     g.drawLine(b.x, b.y, e.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{             yard.drawString("Uknown",30,l);         }     }       private Type getType(int dx, int dy) {         Type result = Type.UNDEFINED;          if (dx > 0 && dy < 0) {             upshot = 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) {             issue = Type.RIGHT_UP;         }          return result;     }      individual boolean isCircle(Listing<Point> points) {         boolean result = false;         Blazon[] shape = circleShape;         Type[] detected = new Type[shape.length];         bounds = new Point[shape.length];          concluding int STEP = 5;          int index = 0;                 Betoken current = points.get(0);         Blazon blazon = null;          for (int i = STEP; i < points.size(); i += Stride) {             Bespeak next = points.go(i);             int dx = adjacent.x - current.ten;             int dy = -(side by side.y - current.y);              if(dx == 0 || dy == 0) {                 continue;             }              Type newType = getType(dx, dy);             if(blazon == null || type != newType) {                 if(newType != shape[index]) {                     break;                 }                 premises[alphabetize] = current;                 detected[alphabetize++] = newType;             }             blazon = newType;                         current = next;              if (index >= shape.length) {                 effect = truthful;                 break;             }         }          return result;     }      @Override     public void mousePressed(MouseEvent east) {         cD = 0;         points.clear();         editing = truthful;     }      private int cX;     private int cY;     individual int cD;      @Override     public void mouseReleased(MouseEvent east) {         editing = false;         if(points.size() > 0) {             if(isCircle(points)) {                 cX = premises[0].x + Math.abs((bounds[2].x - bounds[0].x)/two);                 cY = premises[0].y;                 cD = premises[2].y - bounds[0].y;                 cX = cX - cD/two;                  Organisation.out.println("circumvolve");             }else{                 cD = -1;                 Organization.out.println("unknown");             }             repaint();         }     }      @Override     public void mouseDragged(MouseEvent e) {         Point newPoint = e.getPoint();         if (editing && !concluding.equals(newPoint)) {             points.add(newPoint);             final = newPoint;             repaint();         }     }      @Override     public void mouseMoved(MouseEvent e) {     }      @Override     public void mouseEntered(MouseEvent east) {     }      @Override     public void mouseExited(MouseEvent e) {     }      @Override     public void mouseClicked(MouseEvent e) {     }      public static void master(String[] args) {         SwingUtilities.invokeLater(new Runnable() {              @Override             public void run() {                 CircleGestureDemo t = new CircleGestureDemo();                 t.setVisible(true);             }         });     } }                  

It should not be a problem to implement similar beliefs on iOS, since yous only need several events and coordinates. Something like the post-obit (see example):

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

At that place are several enhancements possible.

Start at any point

Current requirement is to commencement cartoon a circle from the top centre point due to the post-obit simplification:

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

Delight discover the default value of index is used. A simple search through the available "parts" of the shape will remove that limitation. Delight note yous'll need to use a circular buffer in social club to detect a total shape:

enter image description here

Clockwise and counterclockwise

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

enter image description here

Draw an ellipse

Yous have everything you need already in the bounds array.

enter image description here

But utilise that data:

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

Other gestures (optional)

Finally, you just need to properly handle a situation when dx (or dy) is equal to zero in lodge to support other gestures:

enter image description here

Update

This modest PoC got quite a high attention, so I did update the code a bit in order to make it work smoothly and provide some drawing 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 java.awt.Graphics; import java.awt.Graphics2D; import java.awt.HeadlessException; import java.awt.Indicate; import java.awt.RenderingHints; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import coffee.awt.issue.MouseMotionListener; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities;  public form CircleGestureDemo extends JFrame {      enum Type {          RIGHT_DOWN,         LEFT_DOWN,         LEFT_UP,         RIGHT_UP,         UNDEFINED     }      private static final Type[] circleShape = {         Type.RIGHT_DOWN,         Type.LEFT_DOWN,         Type.LEFT_UP,         Blazon.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 grade GesturePanel extends JPanel implements MouseListener, MouseMotionListener {          private boolean editing = false;         individual Point[] bounds;         individual Point terminal = new Point(0, 0);         individual final List<Betoken> points = new ArrayList<>();          public GesturePanel() {             super(true);             addMouseListener(this);             addMouseMotionListener(this);         }          @Override         public void paint(Graphics graphics) {             super.paint(graphics);              Dimension d = getSize();             Graphics2D chiliad = (Graphics2D) graphics;              RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,                     RenderingHints.VALUE_ANTIALIAS_ON);             qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);              thousand.setRenderingHints(qualityHints);              if (!points.isEmpty() && cD == 0) {                 isCircle(points, g);                 yard.setColor(HINT_COLOR);                 if (bounds[2] != null) {                     int r = (bounds[2].y - bounds[0].y) / 2;                     1000.setStroke(new BasicStroke(r / three + 1));                     g.drawOval(bounds[0].ten - r, bounds[0].y, two * r, 2 * r);                 } else if (bounds[1] != null) {                     int r = premises[1].x - bounds[0].x;                     g.setStroke(new BasicStroke(r / iii + ane));                     thousand.drawOval(bounds[0].x - r, premises[0].y, 2 * r, 2 * r);                 }             }              g.setStroke(new BasicStroke(2));             1000.setColor(Color.Cherry);              if (cD == 0) {                 Bespeak b = null;                 for (Bespeak eastward : points) {                     if (null != b) {                         one thousand.drawLine(b.x, b.y, e.ten, east.y);                     }                     b = east;                 }              } else if (cD > 0) {                 g.setColor(Color.BLUE);                 thou.setStroke(new BasicStroke(iii));                 thou.drawOval(cX, cY, cD, cD);             } else {                 g.drawString("Uknown", 30, 50);             }         }          private Blazon getType(int dx, int dy) {             Type upshot = Type.UNDEFINED;              if (dx > 0 && dy < 0) {                 consequence = Type.RIGHT_DOWN;             } else if (dx < 0 && dy < 0) {                 result = Type.LEFT_DOWN;             } else if (dx < 0 && dy > 0) {                 upshot = Type.LEFT_UP;             } else if (dx > 0 && dy > 0) {                 consequence = Type.RIGHT_UP;             }              render result;         }          private boolean isCircle(List<Bespeak> points, Graphics2D one thousand) {             boolean issue = false;             Blazon[] shape = circleShape;             bounds = new Point[shape.length];              concluding int STEP = v;             int alphabetize = 0;             int initial = 0;             Point current = points.get(0);             Blazon type = null;              for (int i = STEP; i < points.size(); i += STEP) {                 final Point adjacent = points.get(i);                 last int dx = side by side.x - current.ten;                 concluding int dy = -(next.y - current.y);                  if (dx == 0 || dy == 0) {                     continue;                 }                  concluding int marker = 8;                 if (null != g) {                     g.setColor(Colour.BLACK);                     one thousand.setStroke(new BasicStroke(ii));                     yard.drawOval(current.ten - marker/2,                                 current.y - marker/2,                                 marker, mark);                 }                  Type newType = getType(dx, dy);                 if (type == null || blazon != newType) {                     if (newType != shape[index]) {                         break;                     }                     bounds[index++] = current;                 }                  blazon = newType;                 electric current = next;                 initial = i;                  if (alphabetize >= shape.length) {                     outcome = true;                     break;                 }             }             return result;         }          @Override         public void mousePressed(MouseEvent due east) {             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, nix)) {                     int r = Math.abs((bounds[2].y - bounds[0].y) / 2);                     cX = bounds[0].x - r;                     cY = bounds[0].y;                     cD = 2 * r;                 } else {                     cD = -one;                 }                 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 due east) {         }          @Override         public void mouseEntered(MouseEvent eastward) {         }          @Override         public void mouseExited(MouseEvent east) {         }          @Override         public void mouseClicked(MouseEvent east) {         }     }      public static void main(Cord[] args) {         SwingUtilities.invokeLater(new Runnable() {              @Override             public void run() {                 CircleGestureDemo t = new CircleGestureDemo();                 t.setVisible(true);             }         });     }      concluding static Colour HINT_COLOR = new Color(0x55888888, true); }                  

alexandercathery.blogspot.com

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

0 Response to "How to Draw a Good Circle"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel