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.
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:
Let'south identify a unproblematic and straightforward pattern, typical for the selected shape:
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:
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:
Describe an ellipse
Y'all have everything you lot demand already in the bounds
assortment.
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:
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:
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); }
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